Observer trigger refactor (#19935)

# Objective

- The usage of ComponentId is quite confusing: events are not
components. By newtyping this, we can prevent stupid mistakes, avoid
leaking internal details and make the code clearer for users and engine
devs reading it.
- Adopts https://github.com/bevyengine/bevy/pull/19755

---------

Co-authored-by: oscar-benderstone <oscarbenderstone@gmail.com>
Co-authored-by: Oscar Bender-Stone <88625129+oscar-benderstone@users.noreply.github.com>
This commit is contained in:
Daniel Skates 2025-07-05 00:27:21 +08:00 committed by GitHub
parent 1380a3b7f2
commit 560429ebd9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 129 additions and 102 deletions

View File

@ -94,10 +94,10 @@ use core::{
note = "consider annotating `{Self}` with `#[derive(Event)]`" note = "consider annotating `{Self}` with `#[derive(Event)]`"
)] )]
pub trait Event: Send + Sync + 'static { pub trait Event: Send + Sync + 'static {
/// Generates the [`ComponentId`] for this event type. /// Generates the [`EventKey`] for this event type.
/// ///
/// If this type has already been registered, /// If this type has already been registered,
/// this will return the existing [`ComponentId`]. /// this will return the existing [`EventKey`].
/// ///
/// This is used by various dynamically typed observer APIs, /// This is used by various dynamically typed observer APIs,
/// such as [`World::trigger_targets_dynamic`]. /// such as [`World::trigger_targets_dynamic`].
@ -105,12 +105,12 @@ pub trait Event: Send + Sync + 'static {
/// # Warning /// # Warning
/// ///
/// This method should not be overridden by implementers, /// This method should not be overridden by implementers,
/// and should always correspond to the implementation of [`component_id`](Event::component_id). /// and should always correspond to the implementation of [`event_key`](Event::event_key).
fn register_component_id(world: &mut World) -> ComponentId { fn register_event_key(world: &mut World) -> EventKey {
world.register_component::<EventWrapperComponent<Self>>() EventKey(world.register_component::<EventWrapperComponent<Self>>())
} }
/// Fetches the [`ComponentId`] for this event type, /// Fetches the [`EventKey`] for this event type,
/// if it has already been generated. /// if it has already been generated.
/// ///
/// This is used by various dynamically typed observer APIs, /// This is used by various dynamically typed observer APIs,
@ -119,9 +119,12 @@ pub trait Event: Send + Sync + 'static {
/// # Warning /// # Warning
/// ///
/// This method should not be overridden by implementers, /// This method should not be overridden by implementers,
/// and should always correspond to the implementation of [`register_component_id`](Event::register_component_id). /// and should always correspond to the implementation of
fn component_id(world: &World) -> Option<ComponentId> { /// [`register_event_key`](Event::register_event_key).
world.component_id::<EventWrapperComponent<Self>>() fn event_key(world: &World) -> Option<EventKey> {
world
.component_id::<EventWrapperComponent<Self>>()
.map(EventKey)
} }
} }
@ -421,3 +424,19 @@ pub(crate) struct EventInstance<E: BufferedEvent> {
pub event_id: EventId<E>, pub event_id: EventId<E>,
pub event: E, pub event: E,
} }
/// A unique identifier for an [`Event`], used by [observers].
///
/// You can look up the key for your event by calling the [`Event::event_key`] method.
///
/// [observers]: crate::observer
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct EventKey(pub(crate) ComponentId);
impl EventKey {
/// Returns the internal [`ComponentId`].
#[inline]
pub(crate) fn component_id(&self) -> ComponentId {
self.0
}
}

View File

@ -11,7 +11,7 @@ mod update;
mod writer; mod writer;
pub(crate) use base::EventInstance; pub(crate) use base::EventInstance;
pub use base::{BufferedEvent, EntityEvent, Event, EventId}; pub use base::{BufferedEvent, EntityEvent, Event, EventId, EventKey};
pub use bevy_ecs_macros::{BufferedEvent, EntityEvent, Event}; pub use bevy_ecs_macros::{BufferedEvent, EntityEvent, Event};
pub use collections::{Events, SendBatchIds}; pub use collections::{Events, SendBatchIds};
pub use event_cursor::EventCursor; pub use event_cursor::EventCursor;

View File

@ -1,15 +1,15 @@
use alloc::vec::Vec; use alloc::vec::Vec;
use bevy_ecs::{ use bevy_ecs::{
change_detection::{DetectChangesMut, MutUntyped}, change_detection::{DetectChangesMut, MutUntyped},
component::{ComponentId, Tick}, component::Tick,
event::{BufferedEvent, Events}, event::{BufferedEvent, EventKey, Events},
resource::Resource, resource::Resource,
world::World, world::World,
}; };
#[doc(hidden)] #[doc(hidden)]
struct RegisteredEvent { struct RegisteredEvent {
component_id: ComponentId, event_key: EventKey,
// Required to flush the secondary buffer and drop events even if left unchanged. // Required to flush the secondary buffer and drop events even if left unchanged.
previously_updated: bool, previously_updated: bool,
// SAFETY: The component ID and the function must be used to fetch the Events<T> resource // SAFETY: The component ID and the function must be used to fetch the Events<T> resource
@ -51,7 +51,7 @@ impl EventRegistry {
let component_id = world.init_resource::<Events<T>>(); let component_id = world.init_resource::<Events<T>>();
let mut registry = world.get_resource_or_init::<Self>(); let mut registry = world.get_resource_or_init::<Self>();
registry.event_updates.push(RegisteredEvent { registry.event_updates.push(RegisteredEvent {
component_id, event_key: EventKey(component_id),
previously_updated: false, previously_updated: false,
update: |ptr| { update: |ptr| {
// SAFETY: The resource was initialized with the type Events<T>. // SAFETY: The resource was initialized with the type Events<T>.
@ -66,7 +66,9 @@ impl EventRegistry {
pub fn run_updates(&mut self, world: &mut World, last_change_tick: Tick) { pub fn run_updates(&mut self, world: &mut World, last_change_tick: Tick) {
for registered_event in &mut self.event_updates { for registered_event in &mut self.event_updates {
// Bypass the type ID -> Component ID lookup with the cached component ID. // Bypass the type ID -> Component ID lookup with the cached component ID.
if let Some(events) = world.get_resource_mut_by_id(registered_event.component_id) { if let Some(events) =
world.get_resource_mut_by_id(registered_event.event_key.component_id())
{
let has_changed = events.has_changed_since(last_change_tick); let has_changed = events.has_changed_since(last_change_tick);
if registered_event.previously_updated || has_changed { if registered_event.previously_updated || has_changed {
// SAFETY: The update function pointer is called with the resource // SAFETY: The update function pointer is called with the resource
@ -87,7 +89,7 @@ impl EventRegistry {
let mut registry = world.get_resource_or_init::<Self>(); let mut registry = world.get_resource_or_init::<Self>();
registry registry
.event_updates .event_updates
.retain(|e| e.component_id != component_id); .retain(|e| e.event_key.component_id() != component_id);
world.remove_resource::<Events<T>>(); world.remove_resource::<Events<T>>();
} }
} }

View File

@ -79,7 +79,8 @@ pub mod prelude {
entity::{ContainsEntity, Entity, EntityMapper}, entity::{ContainsEntity, Entity, EntityMapper},
error::{BevyError, Result}, error::{BevyError, Result},
event::{ event::{
BufferedEvent, EntityEvent, Event, EventMutator, EventReader, EventWriter, Events, BufferedEvent, EntityEvent, Event, EventKey, EventMutator, EventReader, EventWriter,
Events,
}, },
hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children}, hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
lifecycle::{ lifecycle::{

View File

@ -55,7 +55,7 @@ use crate::{
entity::Entity, entity::Entity,
event::{ event::{
BufferedEvent, EntityEvent, Event, EventCursor, EventId, EventIterator, BufferedEvent, EntityEvent, Event, EventCursor, EventId, EventIterator,
EventIteratorWithId, Events, EventIteratorWithId, EventKey, Events,
}, },
query::FilteredAccessSet, query::FilteredAccessSet,
relationship::RelationshipHookMode, relationship::RelationshipHookMode,
@ -314,16 +314,16 @@ impl ComponentHooks {
} }
} }
/// [`ComponentId`] for [`Add`] /// [`EventKey`] for [`Add`]
pub const ADD: ComponentId = ComponentId::new(0); pub const ADD: EventKey = EventKey(ComponentId::new(0));
/// [`ComponentId`] for [`Insert`] /// [`EventKey`] for [`Insert`]
pub const INSERT: ComponentId = ComponentId::new(1); pub const INSERT: EventKey = EventKey(ComponentId::new(1));
/// [`ComponentId`] for [`Replace`] /// [`EventKey`] for [`Replace`]
pub const REPLACE: ComponentId = ComponentId::new(2); pub const REPLACE: EventKey = EventKey(ComponentId::new(2));
/// [`ComponentId`] for [`Remove`] /// [`EventKey`] for [`Remove`]
pub const REMOVE: ComponentId = ComponentId::new(3); pub const REMOVE: EventKey = EventKey(ComponentId::new(3));
/// [`ComponentId`] for [`Despawn`] /// [`EventKey`] for [`Despawn`]
pub const DESPAWN: ComponentId = ComponentId::new(4); pub const DESPAWN: EventKey = EventKey(ComponentId::new(4));
/// Trigger emitted when a component is inserted onto an entity that does not already have that /// Trigger emitted when a component is inserted onto an entity that does not already have that
/// component. Runs before `Insert`. /// component. Runs before `Insert`.

View File

@ -37,44 +37,44 @@ pub struct Observers {
remove: CachedObservers, remove: CachedObservers,
despawn: CachedObservers, despawn: CachedObservers,
// Map from trigger type to set of observers listening to that trigger // Map from trigger type to set of observers listening to that trigger
cache: HashMap<ComponentId, CachedObservers>, cache: HashMap<EventKey, CachedObservers>,
} }
impl Observers { impl Observers {
pub(crate) fn get_observers_mut(&mut self, event_type: ComponentId) -> &mut CachedObservers { pub(crate) fn get_observers_mut(&mut self, event_key: EventKey) -> &mut CachedObservers {
use crate::lifecycle::*; use crate::lifecycle::*;
match event_type { match event_key {
ADD => &mut self.add, ADD => &mut self.add,
INSERT => &mut self.insert, INSERT => &mut self.insert,
REPLACE => &mut self.replace, REPLACE => &mut self.replace,
REMOVE => &mut self.remove, REMOVE => &mut self.remove,
DESPAWN => &mut self.despawn, DESPAWN => &mut self.despawn,
_ => self.cache.entry(event_type).or_default(), _ => self.cache.entry(event_key).or_default(),
} }
} }
/// Attempts to get the observers for the given `event_type`. /// Attempts to get the observers for the given `event_key`.
/// ///
/// When accessing the observers for lifecycle events, such as [`Add`], [`Insert`], [`Replace`], [`Remove`], and [`Despawn`], /// When accessing the observers for lifecycle events, such as [`Add`], [`Insert`], [`Replace`], [`Remove`], and [`Despawn`],
/// use the [`ComponentId`] constants from the [`lifecycle`](crate::lifecycle) module. /// use the [`EventKey`] constants from the [`lifecycle`](crate::lifecycle) module.
pub fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> { pub fn try_get_observers(&self, event_key: EventKey) -> Option<&CachedObservers> {
use crate::lifecycle::*; use crate::lifecycle::*;
match event_type { match event_key {
ADD => Some(&self.add), ADD => Some(&self.add),
INSERT => Some(&self.insert), INSERT => Some(&self.insert),
REPLACE => Some(&self.replace), REPLACE => Some(&self.replace),
REMOVE => Some(&self.remove), REMOVE => Some(&self.remove),
DESPAWN => Some(&self.despawn), DESPAWN => Some(&self.despawn),
_ => self.cache.get(&event_type), _ => self.cache.get(&event_key),
} }
} }
/// This will run the observers of the given `event_type`, targeting the given `entity` and `components`. /// This will run the observers of the given `event_key`, targeting the given `entity` and `components`.
pub(crate) fn invoke<T>( pub(crate) fn invoke<T>(
mut world: DeferredWorld, mut world: DeferredWorld,
event_type: ComponentId, event_key: EventKey,
current_target: Option<Entity>, current_target: Option<Entity>,
original_target: Option<Entity>, original_target: Option<Entity>,
components: impl Iterator<Item = ComponentId> + Clone, components: impl Iterator<Item = ComponentId> + Clone,
@ -88,7 +88,7 @@ impl Observers {
// SAFETY: There are no outstanding world references // SAFETY: There are no outstanding world references
world.increment_trigger_id(); world.increment_trigger_id();
let observers = world.observers(); let observers = world.observers();
let Some(observers) = observers.try_get_observers(event_type) else { let Some(observers) = observers.try_get_observers(event_key) else {
return; return;
}; };
// SAFETY: The only outstanding reference to world is `observers` // SAFETY: The only outstanding reference to world is `observers`
@ -102,7 +102,7 @@ impl Observers {
world.reborrow(), world.reborrow(),
ObserverTrigger { ObserverTrigger {
observer, observer,
event_type, event_key,
components: components.clone().collect(), components: components.clone().collect(),
current_target, current_target,
original_target, original_target,
@ -145,10 +145,10 @@ impl Observers {
}); });
} }
pub(crate) fn is_archetype_cached(event_type: ComponentId) -> Option<ArchetypeFlags> { pub(crate) fn is_archetype_cached(event_key: EventKey) -> Option<ArchetypeFlags> {
use crate::lifecycle::*; use crate::lifecycle::*;
match event_type { match event_key {
ADD => Some(ArchetypeFlags::ON_ADD_OBSERVER), ADD => Some(ArchetypeFlags::ON_ADD_OBSERVER),
INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER), INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER),
REPLACE => Some(ArchetypeFlags::ON_REPLACE_OBSERVER), REPLACE => Some(ArchetypeFlags::ON_REPLACE_OBSERVER),

View File

@ -289,9 +289,9 @@ impl Observer {
/// Observe the given `event`. This will cause the [`Observer`] to run whenever an event with the given [`ComponentId`] /// Observe the given `event`. This will cause the [`Observer`] to run whenever an event with the given [`ComponentId`]
/// is triggered. /// is triggered.
/// # Safety /// # Safety
/// The type of the `event` [`ComponentId`] _must_ match the actual value /// The type of the `event` [`EventKey`] _must_ match the actual value
/// of the event passed into the observer system. /// of the event passed into the observer system.
pub unsafe fn with_event(mut self, event: ComponentId) -> Self { pub unsafe fn with_event(mut self, event: EventKey) -> Self {
self.descriptor.events.push(event); self.descriptor.events.push(event);
self self
} }
@ -350,7 +350,7 @@ impl Component for Observer {
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct ObserverDescriptor { pub struct ObserverDescriptor {
/// The events the observer is watching. /// The events the observer is watching.
pub(super) events: Vec<ComponentId>, pub(super) events: Vec<EventKey>,
/// The components the observer is watching. /// The components the observer is watching.
pub(super) components: Vec<ComponentId>, pub(super) components: Vec<ComponentId>,
@ -362,9 +362,9 @@ pub struct ObserverDescriptor {
impl ObserverDescriptor { impl ObserverDescriptor {
/// Add the given `events` to the descriptor. /// Add the given `events` to the descriptor.
/// # Safety /// # Safety
/// The type of each [`ComponentId`] in `events` _must_ match the actual value /// The type of each [`EventKey`] in `events` _must_ match the actual value
/// of the event passed into the observer. /// of the event passed into the observer.
pub unsafe fn with_events(mut self, events: Vec<ComponentId>) -> Self { pub unsafe fn with_events(mut self, events: Vec<EventKey>) -> Self {
self.events = events; self.events = events;
self self
} }
@ -382,7 +382,7 @@ impl ObserverDescriptor {
} }
/// Returns the `events` that the observer is watching. /// Returns the `events` that the observer is watching.
pub fn events(&self) -> &[ComponentId] { pub fn events(&self) -> &[EventKey] {
&self.events &self.events
} }
@ -410,13 +410,13 @@ fn hook_on_add<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
HookContext { entity, .. }: HookContext, HookContext { entity, .. }: HookContext,
) { ) {
world.commands().queue(move |world: &mut World| { world.commands().queue(move |world: &mut World| {
let event_id = E::register_component_id(world); let event_key = E::register_event_key(world);
let mut components = alloc::vec![]; let mut components = alloc::vec![];
B::component_ids(&mut world.components_registrator(), &mut |id| { B::component_ids(&mut world.components_registrator(), &mut |id| {
components.push(id); components.push(id);
}); });
if let Some(mut observer) = world.get_mut::<Observer>(entity) { if let Some(mut observer) = world.get_mut::<Observer>(entity) {
observer.descriptor.events.push(event_id); observer.descriptor.events.push(event_key);
observer.descriptor.components.extend(components); observer.descriptor.components.extend(components);
let system: &mut dyn Any = observer.system.as_mut(); let system: &mut dyn Any = observer.system.as_mut();

View File

@ -43,10 +43,10 @@ fn component_clone_observed_by(_source: &SourceComponent, ctx: &mut ComponentClo
.get_mut::<Observer>(observer_entity) .get_mut::<Observer>(observer_entity)
.expect("Source observer entity must have Observer"); .expect("Source observer entity must have Observer");
observer_state.descriptor.entities.push(target); observer_state.descriptor.entities.push(target);
let event_types = observer_state.descriptor.events.clone(); let event_keys = observer_state.descriptor.events.clone();
let components = observer_state.descriptor.components.clone(); let components = observer_state.descriptor.components.clone();
for event_type in event_types { for event_key in event_keys {
let observers = world.observers.get_observers_mut(event_type); let observers = world.observers.get_observers_mut(event_key);
if components.is_empty() { if components.is_empty() {
if let Some(map) = observers.entity_observers.get(&source).cloned() { if let Some(map) = observers.entity_observers.get(&source).cloned() {
observers.entity_observers.insert(target, map); observers.entity_observers.insert(target, map);

View File

@ -197,10 +197,10 @@ impl World {
} }
pub(crate) fn trigger_with_caller<E: Event>(&mut self, mut event: E, caller: MaybeLocation) { pub(crate) fn trigger_with_caller<E: Event>(&mut self, mut event: E, caller: MaybeLocation) {
let event_id = E::register_component_id(self); let event_key = E::register_event_key(self);
// SAFETY: We just registered `event_id` with the type of `event` // SAFETY: We just registered `event_key` with the type of `event`
unsafe { unsafe {
self.trigger_dynamic_ref_with_caller(event_id, &mut event, caller); self.trigger_dynamic_ref_with_caller(event_key, &mut event, caller);
} }
} }
@ -210,22 +210,22 @@ impl World {
/// or use the event after it has been modified by observers. /// or use the event after it has been modified by observers.
#[track_caller] #[track_caller]
pub fn trigger_ref<E: Event>(&mut self, event: &mut E) { pub fn trigger_ref<E: Event>(&mut self, event: &mut E) {
let event_id = E::register_component_id(self); let event_key = E::register_event_key(self);
// SAFETY: We just registered `event_id` with the type of `event` // SAFETY: We just registered `event_key` with the type of `event`
unsafe { self.trigger_dynamic_ref_with_caller(event_id, event, MaybeLocation::caller()) }; unsafe { self.trigger_dynamic_ref_with_caller(event_key, event, MaybeLocation::caller()) };
} }
unsafe fn trigger_dynamic_ref_with_caller<E: Event>( unsafe fn trigger_dynamic_ref_with_caller<E: Event>(
&mut self, &mut self,
event_id: ComponentId, event_key: EventKey,
event_data: &mut E, event_data: &mut E,
caller: MaybeLocation, caller: MaybeLocation,
) { ) {
let mut world = DeferredWorld::from(self); let mut world = DeferredWorld::from(self);
// SAFETY: `event_data` is accessible as the type represented by `event_id` // SAFETY: `event_data` is accessible as the type represented by `event_key`
unsafe { unsafe {
world.trigger_observers_with_data::<_, ()>( world.trigger_observers_with_data::<_, ()>(
event_id, event_key,
None, None,
None, None,
core::iter::empty::<ComponentId>(), core::iter::empty::<ComponentId>(),
@ -252,10 +252,10 @@ impl World {
targets: impl TriggerTargets, targets: impl TriggerTargets,
caller: MaybeLocation, caller: MaybeLocation,
) { ) {
let event_id = E::register_component_id(self); let event_key = E::register_event_key(self);
// SAFETY: We just registered `event_id` with the type of `event` // SAFETY: We just registered `event_key` with the type of `event`
unsafe { unsafe {
self.trigger_targets_dynamic_ref_with_caller(event_id, &mut event, targets, caller); self.trigger_targets_dynamic_ref_with_caller(event_key, &mut event, targets, caller);
} }
} }
@ -270,9 +270,9 @@ impl World {
event: &mut E, event: &mut E,
targets: impl TriggerTargets, targets: impl TriggerTargets,
) { ) {
let event_id = E::register_component_id(self); let event_key = E::register_event_key(self);
// SAFETY: We just registered `event_id` with the type of `event` // SAFETY: We just registered `event_key` with the type of `event`
unsafe { self.trigger_targets_dynamic_ref(event_id, event, targets) }; unsafe { self.trigger_targets_dynamic_ref(event_key, event, targets) };
} }
/// Triggers the given [`EntityEvent`] for the given `targets`, which will run any [`Observer`]s watching for it. /// Triggers the given [`EntityEvent`] for the given `targets`, which will run any [`Observer`]s watching for it.
@ -283,17 +283,17 @@ impl World {
/// ///
/// # Safety /// # Safety
/// ///
/// Caller must ensure that `event_data` is accessible as the type represented by `event_id`. /// Caller must ensure that `event_data` is accessible as the type represented by `event_key`.
#[track_caller] #[track_caller]
pub unsafe fn trigger_targets_dynamic<E: EntityEvent, Targets: TriggerTargets>( pub unsafe fn trigger_targets_dynamic<E: EntityEvent, Targets: TriggerTargets>(
&mut self, &mut self,
event_id: ComponentId, event_key: EventKey,
mut event_data: E, mut event_data: E,
targets: Targets, targets: Targets,
) { ) {
// SAFETY: `event_data` is accessible as the type represented by `event_id` // SAFETY: `event_data` is accessible as the type represented by `event_key`
unsafe { unsafe {
self.trigger_targets_dynamic_ref(event_id, &mut event_data, targets); self.trigger_targets_dynamic_ref(event_key, &mut event_data, targets);
}; };
} }
@ -305,16 +305,16 @@ impl World {
/// ///
/// # Safety /// # Safety
/// ///
/// Caller must ensure that `event_data` is accessible as the type represented by `event_id`. /// Caller must ensure that `event_data` is accessible as the type represented by `event_key`.
#[track_caller] #[track_caller]
pub unsafe fn trigger_targets_dynamic_ref<E: EntityEvent, Targets: TriggerTargets>( pub unsafe fn trigger_targets_dynamic_ref<E: EntityEvent, Targets: TriggerTargets>(
&mut self, &mut self,
event_id: ComponentId, event_key: EventKey,
event_data: &mut E, event_data: &mut E,
targets: Targets, targets: Targets,
) { ) {
self.trigger_targets_dynamic_ref_with_caller( self.trigger_targets_dynamic_ref_with_caller(
event_id, event_key,
event_data, event_data,
targets, targets,
MaybeLocation::caller(), MaybeLocation::caller(),
@ -326,7 +326,7 @@ impl World {
/// See `trigger_targets_dynamic_ref` /// See `trigger_targets_dynamic_ref`
unsafe fn trigger_targets_dynamic_ref_with_caller<E: EntityEvent, Targets: TriggerTargets>( unsafe fn trigger_targets_dynamic_ref_with_caller<E: EntityEvent, Targets: TriggerTargets>(
&mut self, &mut self,
event_id: ComponentId, event_key: EventKey,
event_data: &mut E, event_data: &mut E,
targets: Targets, targets: Targets,
caller: MaybeLocation, caller: MaybeLocation,
@ -334,10 +334,10 @@ impl World {
let mut world = DeferredWorld::from(self); let mut world = DeferredWorld::from(self);
let mut entity_targets = targets.entities().peekable(); let mut entity_targets = targets.entities().peekable();
if entity_targets.peek().is_none() { if entity_targets.peek().is_none() {
// SAFETY: `event_data` is accessible as the type represented by `event_id` // SAFETY: `event_data` is accessible as the type represented by `event_key`
unsafe { unsafe {
world.trigger_observers_with_data::<_, E::Traversal>( world.trigger_observers_with_data::<_, E::Traversal>(
event_id, event_key,
None, None,
None, None,
targets.components(), targets.components(),
@ -348,10 +348,10 @@ impl World {
}; };
} else { } else {
for target_entity in entity_targets { for target_entity in entity_targets {
// SAFETY: `event_data` is accessible as the type represented by `event_id` // SAFETY: `event_data` is accessible as the type represented by `event_key`
unsafe { unsafe {
world.trigger_observers_with_data::<_, E::Traversal>( world.trigger_observers_with_data::<_, E::Traversal>(
event_id, event_key,
Some(target_entity), Some(target_entity),
Some(target_entity), Some(target_entity),
targets.components(), targets.components(),
@ -379,8 +379,8 @@ impl World {
}; };
let descriptor = &observer_state.descriptor; let descriptor = &observer_state.descriptor;
for &event_type in &descriptor.events { for &event_key in &descriptor.events {
let cache = observers.get_observers_mut(event_type); let cache = observers.get_observers_mut(event_key);
if descriptor.components.is_empty() && descriptor.entities.is_empty() { if descriptor.components.is_empty() && descriptor.entities.is_empty() {
cache cache
@ -400,7 +400,7 @@ impl World {
.component_observers .component_observers
.entry(component) .entry(component)
.or_insert_with(|| { .or_insert_with(|| {
if let Some(flag) = Observers::is_archetype_cached(event_type) { if let Some(flag) = Observers::is_archetype_cached(event_key) {
archetypes.update_flags(component, flag, true); archetypes.update_flags(component, flag, true);
} }
CachedComponentObservers::default() CachedComponentObservers::default()
@ -430,8 +430,8 @@ impl World {
let archetypes = &mut self.archetypes; let archetypes = &mut self.archetypes;
let observers = &mut self.observers; let observers = &mut self.observers;
for &event_type in &descriptor.events { for &event_key in &descriptor.events {
let cache = observers.get_observers_mut(event_type); let cache = observers.get_observers_mut(event_key);
if descriptor.components.is_empty() && descriptor.entities.is_empty() { if descriptor.components.is_empty() && descriptor.entities.is_empty() {
cache.global_observers.remove(&entity); cache.global_observers.remove(&entity);
} else if descriptor.components.is_empty() { } else if descriptor.components.is_empty() {
@ -470,7 +470,7 @@ impl World {
&& observers.entity_component_observers.is_empty() && observers.entity_component_observers.is_empty()
{ {
cache.component_observers.remove(component); cache.component_observers.remove(component);
if let Some(flag) = Observers::is_archetype_cached(event_type) { if let Some(flag) = Observers::is_archetype_cached(event_key) {
if let Some(by_component) = archetypes.by_component.get(component) { if let Some(by_component) = archetypes.by_component.get(component) {
for archetype in by_component.keys() { for archetype in by_component.keys() {
let archetype = &mut archetypes.archetypes[archetype.index()]; let archetype = &mut archetypes.archetypes[archetype.index()];
@ -734,7 +734,7 @@ mod tests {
fn observer_multiple_events() { fn observer_multiple_events() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
let on_remove = Remove::register_component_id(&mut world); let on_remove = Remove::register_event_key(&mut world);
world.spawn( world.spawn(
// SAFETY: Add and Remove are both unit types, so this is safe // SAFETY: Add and Remove are both unit types, so this is safe
unsafe { unsafe {
@ -1008,7 +1008,7 @@ mod tests {
fn observer_dynamic_trigger() { fn observer_dynamic_trigger() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
let event_a = Remove::register_component_id(&mut world); let event_a = Remove::register_event_key(&mut world);
// SAFETY: we registered `event_a` above and it matches the type of EventA // SAFETY: we registered `event_a` above and it matches the type of EventA
let observe = unsafe { let observe = unsafe {

View File

@ -49,8 +49,8 @@ impl<'w, E, B: Bundle> On<'w, E, B> {
} }
/// Returns the event type of this [`On`] instance. /// Returns the event type of this [`On`] instance.
pub fn event_type(&self) -> ComponentId { pub fn event_key(&self) -> EventKey {
self.trigger.event_type self.trigger.event_key
} }
/// Returns a reference to the triggered event. /// Returns a reference to the triggered event.
@ -182,7 +182,7 @@ pub struct ObserverTrigger {
/// The [`Entity`] of the observer handling the trigger. /// The [`Entity`] of the observer handling the trigger.
pub observer: Entity, pub observer: Entity,
/// The [`Event`] the trigger targeted. /// The [`Event`] the trigger targeted.
pub event_type: ComponentId, pub event_key: EventKey,
/// The [`ComponentId`]s the trigger targeted. /// The [`ComponentId`]s the trigger targeted.
pub components: SmallVec<[ComponentId; 2]>, pub components: SmallVec<[ComponentId; 2]>,
/// The entity that the entity-event targeted, if any. /// The entity that the entity-event targeted, if any.

View File

@ -7,7 +7,7 @@ use crate::{
change_detection::{MaybeLocation, MutUntyped}, change_detection::{MaybeLocation, MutUntyped},
component::{ComponentId, Mutable}, component::{ComponentId, Mutable},
entity::Entity, entity::Entity,
event::{BufferedEvent, EntityEvent, Event, EventId, Events, SendBatchIds}, event::{BufferedEvent, EntityEvent, Event, EventId, EventKey, Events, SendBatchIds},
lifecycle::{HookContext, INSERT, REPLACE}, lifecycle::{HookContext, INSERT, REPLACE},
observer::{Observers, TriggerTargets}, observer::{Observers, TriggerTargets},
prelude::{Component, QueryState}, prelude::{Component, QueryState},
@ -749,7 +749,7 @@ impl<'w> DeferredWorld<'w> {
#[inline] #[inline]
pub(crate) unsafe fn trigger_observers( pub(crate) unsafe fn trigger_observers(
&mut self, &mut self,
event: ComponentId, event: EventKey,
target: Option<Entity>, target: Option<Entity>,
components: impl Iterator<Item = ComponentId> + Clone, components: impl Iterator<Item = ComponentId> + Clone,
caller: MaybeLocation, caller: MaybeLocation,
@ -773,7 +773,7 @@ impl<'w> DeferredWorld<'w> {
#[inline] #[inline]
pub(crate) unsafe fn trigger_observers_with_data<E, T>( pub(crate) unsafe fn trigger_observers_with_data<E, T>(
&mut self, &mut self,
event: ComponentId, event: EventKey,
current_target: Option<Entity>, current_target: Option<Entity>,
original_target: Option<Entity>, original_target: Option<Entity>,
components: impl Iterator<Item = ComponentId> + Clone, components: impl Iterator<Item = ComponentId> + Clone,

View File

@ -152,19 +152,19 @@ impl World {
#[inline] #[inline]
fn bootstrap(&mut self) { fn bootstrap(&mut self) {
// The order that we register these events is vital to ensure that the constants are correct! // The order that we register these events is vital to ensure that the constants are correct!
let on_add = Add::register_component_id(self); let on_add = Add::register_event_key(self);
assert_eq!(ADD, on_add); assert_eq!(ADD, on_add);
let on_insert = Insert::register_component_id(self); let on_insert = Insert::register_event_key(self);
assert_eq!(INSERT, on_insert); assert_eq!(INSERT, on_insert);
let on_replace = Replace::register_component_id(self); let on_replace = Replace::register_event_key(self);
assert_eq!(REPLACE, on_replace); assert_eq!(REPLACE, on_replace);
let on_remove = Remove::register_component_id(self); let on_remove = Remove::register_event_key(self);
assert_eq!(REMOVE, on_remove); assert_eq!(REMOVE, on_remove);
let on_despawn = Despawn::register_component_id(self); let on_despawn = Despawn::register_event_key(self);
assert_eq!(DESPAWN, on_despawn); assert_eq!(DESPAWN, on_despawn);
// This sets up `Disabled` as a disabling component, via the FromWorld impl // This sets up `Disabled` as a disabling component, via the FromWorld impl

View File

@ -1,7 +1,7 @@
--- ---
title: Observer Overhaul title: Observer Overhaul
authors: ["@Jondolf", "@alice-i-cecile", "@hukasu] authors: ["@Jondolf", "@alice-i-cecile", "@hukasu", "oscar-benderstone", "Zeophlite"]
pull_requests: [19596, 19663, 19611] pull_requests: [19596, 19663, 19611, 19935]
--- ---
## Rename `Trigger` to `On` ## Rename `Trigger` to `On`
@ -45,3 +45,8 @@ This was handy! We've enabled this functionality for all entity-events: simply c
The name of the Observer's system is now accessible through `Observer::system_name`, The name of the Observer's system is now accessible through `Observer::system_name`,
this opens up the possibility for the debug tools to show more meaningful names for observers. this opens up the possibility for the debug tools to show more meaningful names for observers.
## Use `EventKey` instead of `ComponentId`
Internally, each `Event` type would generate a `Component` type, allowing us to use the corresponding `ComponentId` to track the event.
We have newtyped this to `EventKey` to help separate these concerns.