diff --git a/crates/bevy_ecs/src/observer/centralized_storage.rs b/crates/bevy_ecs/src/observer/centralized_storage.rs new file mode 100644 index 0000000000..e3fa6c530a --- /dev/null +++ b/crates/bevy_ecs/src/observer/centralized_storage.rs @@ -0,0 +1,245 @@ +//! Centralized storage for observers, allowing for efficient look-ups. +//! +//! This has multiple levels: +//! - [`World::observers`] provides access to [`Observers`], which is a central storage for all observers. +//! - [`Observers`] contains multiple distinct caches in the form of [`CachedObservers`]. +//! - Most observers are looked up by the [`ComponentId`] of the event they are observing +//! - Lifecycle observers have their own fields to save lookups. +//! - [`CachedObservers`] contains maps of [`ObserverRunner`]s, which are the actual functions that will be run when the observer is triggered. +//! - These are split by target type, in order to allow for different lookup strategies. +//! - [`CachedComponentObservers`] is one of these maps, which contains observers that are specifically targeted at a component. + +use bevy_platform::collections::HashMap; + +use crate::{ + archetype::ArchetypeFlags, + change_detection::MaybeLocation, + component::ComponentId, + entity::EntityHashMap, + observer::{ObserverRunner, ObserverTrigger}, + prelude::*, + world::DeferredWorld, +}; + +/// An internal lookup table tracking all of the observers in the world. +/// +/// Stores a cache mapping trigger ids to the registered observers. +/// Some observer kinds (like [lifecycle](crate::lifecycle) observers) have a dedicated field, +/// saving lookups for the most common triggers. +/// +/// This can be accessed via [`World::observers`]. +#[derive(Default, Debug)] +pub struct Observers { + // Cached ECS observers to save a lookup most common triggers. + add: CachedObservers, + insert: CachedObservers, + replace: CachedObservers, + remove: CachedObservers, + despawn: CachedObservers, + // Map from trigger type to set of observers listening to that trigger + cache: HashMap, +} + +impl Observers { + pub(crate) fn get_observers_mut(&mut self, event_type: ComponentId) -> &mut CachedObservers { + use crate::lifecycle::*; + + match event_type { + ADD => &mut self.add, + INSERT => &mut self.insert, + REPLACE => &mut self.replace, + REMOVE => &mut self.remove, + DESPAWN => &mut self.despawn, + _ => self.cache.entry(event_type).or_default(), + } + } + + /// Attempts to get the observers for the given `event_type`. + /// + /// 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. + pub fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> { + use crate::lifecycle::*; + + match event_type { + ADD => Some(&self.add), + INSERT => Some(&self.insert), + REPLACE => Some(&self.replace), + REMOVE => Some(&self.remove), + DESPAWN => Some(&self.despawn), + _ => self.cache.get(&event_type), + } + } + + /// This will run the observers of the given `event_type`, targeting the given `entity` and `components`. + pub(crate) fn invoke( + mut world: DeferredWorld, + event_type: ComponentId, + current_target: Option, + original_target: Option, + components: impl Iterator + Clone, + data: &mut T, + propagate: &mut bool, + caller: MaybeLocation, + ) { + // SAFETY: You cannot get a mutable reference to `observers` from `DeferredWorld` + let (mut world, observers) = unsafe { + let world = world.as_unsafe_world_cell(); + // SAFETY: There are no outstanding world references + world.increment_trigger_id(); + let observers = world.observers(); + let Some(observers) = observers.try_get_observers(event_type) else { + return; + }; + // SAFETY: The only outstanding reference to world is `observers` + (world.into_deferred(), observers) + }; + + let trigger_for_components = components.clone(); + + let mut trigger_observer = |(&observer, runner): (&Entity, &ObserverRunner)| { + (runner)( + world.reborrow(), + ObserverTrigger { + observer, + event_type, + components: components.clone().collect(), + current_target, + original_target, + caller, + }, + data.into(), + propagate, + ); + }; + // Trigger observers listening for any kind of this trigger + observers + .global_observers + .iter() + .for_each(&mut trigger_observer); + + // Trigger entity observers listening for this kind of trigger + if let Some(target_entity) = current_target { + if let Some(map) = observers.entity_observers.get(&target_entity) { + map.iter().for_each(&mut trigger_observer); + } + } + + // Trigger observers listening to this trigger targeting a specific component + trigger_for_components.for_each(|id| { + if let Some(component_observers) = observers.component_observers.get(&id) { + component_observers + .global_observers + .iter() + .for_each(&mut trigger_observer); + + if let Some(target_entity) = current_target { + if let Some(map) = component_observers + .entity_component_observers + .get(&target_entity) + { + map.iter().for_each(&mut trigger_observer); + } + } + } + }); + } + + pub(crate) fn is_archetype_cached(event_type: ComponentId) -> Option { + use crate::lifecycle::*; + + match event_type { + ADD => Some(ArchetypeFlags::ON_ADD_OBSERVER), + INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER), + REPLACE => Some(ArchetypeFlags::ON_REPLACE_OBSERVER), + REMOVE => Some(ArchetypeFlags::ON_REMOVE_OBSERVER), + DESPAWN => Some(ArchetypeFlags::ON_DESPAWN_OBSERVER), + _ => None, + } + } + + pub(crate) fn update_archetype_flags( + &self, + component_id: ComponentId, + flags: &mut ArchetypeFlags, + ) { + if self.add.component_observers.contains_key(&component_id) { + flags.insert(ArchetypeFlags::ON_ADD_OBSERVER); + } + + if self.insert.component_observers.contains_key(&component_id) { + flags.insert(ArchetypeFlags::ON_INSERT_OBSERVER); + } + + if self.replace.component_observers.contains_key(&component_id) { + flags.insert(ArchetypeFlags::ON_REPLACE_OBSERVER); + } + + if self.remove.component_observers.contains_key(&component_id) { + flags.insert(ArchetypeFlags::ON_REMOVE_OBSERVER); + } + + if self.despawn.component_observers.contains_key(&component_id) { + flags.insert(ArchetypeFlags::ON_DESPAWN_OBSERVER); + } + } +} + +/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event. +/// +/// This is stored inside of [`Observers`], specialized for each kind of observer. +#[derive(Default, Debug)] +pub struct CachedObservers { + // Observers listening for any time this event is fired, regardless of target + // This will also respond to events targeting specific components or entities + pub(super) global_observers: ObserverMap, + // Observers listening for this trigger fired at a specific component + pub(super) component_observers: HashMap, + // Observers listening for this trigger fired at a specific entity + pub(super) entity_observers: EntityHashMap, +} + +impl CachedObservers { + /// Returns the observers listening for this trigger, regardless of target. + /// These observers will also respond to events targeting specific components or entities. + pub fn global_observers(&self) -> &ObserverMap { + &self.global_observers + } + + /// Returns the observers listening for this trigger targeting components. + pub fn get_component_observers(&self) -> &HashMap { + &self.component_observers + } + + /// Returns the observers listening for this trigger targeting entities. + pub fn entity_observers(&self) -> &HashMap { + &self.component_observers + } +} + +/// Map between an observer entity and its [`ObserverRunner`] +pub type ObserverMap = EntityHashMap; + +/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event targeted at a specific component. +/// +/// This is stored inside of [`CachedObservers`]. +#[derive(Default, Debug)] +pub struct CachedComponentObservers { + // Observers listening to events targeting this component, but not a specific entity + pub(super) global_observers: ObserverMap, + // Observers listening to events targeting this component on a specific entity + pub(super) entity_component_observers: EntityHashMap, +} + +impl CachedComponentObservers { + /// Returns the observers listening for this trigger, regardless of target. + /// These observers will also respond to events targeting specific entities. + pub fn global_observers(&self) -> &ObserverMap { + &self.global_observers + } + + /// Returns the observers listening for this trigger targeting this component on a specific entity. + pub fn entity_component_observers(&self) -> &EntityHashMap { + &self.entity_component_observers + } +} diff --git a/crates/bevy_ecs/src/observer/distributed_storage.rs b/crates/bevy_ecs/src/observer/distributed_storage.rs new file mode 100644 index 0000000000..a9a3645121 --- /dev/null +++ b/crates/bevy_ecs/src/observer/distributed_storage.rs @@ -0,0 +1,492 @@ +//! Information about observers that is stored on the entities themselves. +//! +//! This allows for easier cleanup, better inspection, and more flexible querying. +//! +//! Each observer is associated with an entity, defined by the [`Observer`] component. +//! The [`Observer`] component contains the system that will be run when the observer is triggered, +//! and the [`ObserverDescriptor`] which contains information about what the observer is observing. +//! +//! When we watch entities, we add the [`ObservedBy`] component to those entities, +//! which links back to the observer entity. + +use core::any::Any; + +use crate::{ + component::{ComponentCloneBehavior, ComponentId, Mutable, StorageType}, + entity::Entity, + error::{ErrorContext, ErrorHandler}, + lifecycle::{ComponentHook, HookContext}, + observer::{observer_system_runner, ObserverRunner}, + prelude::*, + system::{IntoObserverSystem, ObserverSystem}, + world::DeferredWorld, +}; +use alloc::boxed::Box; +use alloc::vec::Vec; +use bevy_utils::prelude::DebugName; + +#[cfg(feature = "bevy_reflect")] +use crate::prelude::ReflectComponent; + +/// An [`Observer`] system. Add this [`Component`] to an [`Entity`] to turn it into an "observer". +/// +/// Observers listen for a "trigger" of a specific [`Event`]. An event can be triggered on the [`World`] +/// by calling [`World::trigger`], or if the event is an [`EntityEvent`], it can also be triggered for specific +/// entity targets using [`World::trigger_targets`]. +/// +/// Note that [`BufferedEvent`]s sent using [`EventReader`] and [`EventWriter`] are _not_ automatically triggered. +/// They must be triggered at a specific point in the schedule. +/// +/// # Usage +/// +/// The simplest usage of the observer pattern looks like this: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// #[derive(Event)] +/// struct Speak { +/// message: String, +/// } +/// +/// world.add_observer(|trigger: On| { +/// println!("{}", trigger.event().message); +/// }); +/// +/// // Observers currently require a flush() to be registered. In the context of schedules, +/// // this will generally be done for you. +/// world.flush(); +/// +/// world.trigger(Speak { +/// message: "Hello!".into(), +/// }); +/// ``` +/// +/// Notice that we used [`World::add_observer`]. This is just a shorthand for spawning an [`Observer`] manually: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// # #[derive(Event)] +/// # struct Speak; +/// // These are functionally the same: +/// world.add_observer(|trigger: On| {}); +/// world.spawn(Observer::new(|trigger: On| {})); +/// ``` +/// +/// Observers are systems. They can access arbitrary [`World`] data by adding [`SystemParam`]s: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// # #[derive(Event)] +/// # struct PrintNames; +/// # #[derive(Component, Debug)] +/// # struct Name; +/// world.add_observer(|trigger: On, names: Query<&Name>| { +/// for name in &names { +/// println!("{name:?}"); +/// } +/// }); +/// ``` +/// +/// Note that [`On`] must always be the first parameter. +/// +/// You can also add [`Commands`], which means you can spawn new entities, insert new components, etc: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// # #[derive(Event)] +/// # struct SpawnThing; +/// # #[derive(Component, Debug)] +/// # struct Thing; +/// world.add_observer(|trigger: On, mut commands: Commands| { +/// commands.spawn(Thing); +/// }); +/// ``` +/// +/// Observers can also trigger new events: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// # #[derive(Event)] +/// # struct A; +/// # #[derive(Event)] +/// # struct B; +/// world.add_observer(|trigger: On, mut commands: Commands| { +/// commands.trigger(B); +/// }); +/// ``` +/// +/// When the commands are flushed (including these "nested triggers") they will be +/// recursively evaluated until there are no commands left, meaning nested triggers all +/// evaluate at the same time! +/// +/// If the event is an [`EntityEvent`], it can be triggered for specific entities, +/// which will be passed to the [`Observer`]: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// # let entity = world.spawn_empty().id(); +/// #[derive(Event, EntityEvent)] +/// struct Explode; +/// +/// world.add_observer(|trigger: On, mut commands: Commands| { +/// println!("Entity {} goes BOOM!", trigger.target()); +/// commands.entity(trigger.target()).despawn(); +/// }); +/// +/// world.flush(); +/// +/// world.trigger_targets(Explode, entity); +/// ``` +/// +/// You can trigger multiple entities at once: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// # let e1 = world.spawn_empty().id(); +/// # let e2 = world.spawn_empty().id(); +/// # #[derive(Event, EntityEvent)] +/// # struct Explode; +/// world.trigger_targets(Explode, [e1, e2]); +/// ``` +/// +/// Observers can also watch _specific_ entities, which enables you to assign entity-specific logic: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # #[derive(Component, Debug)] +/// # struct Name(String); +/// # let mut world = World::default(); +/// # let e1 = world.spawn_empty().id(); +/// # let e2 = world.spawn_empty().id(); +/// # #[derive(Event, EntityEvent)] +/// # struct Explode; +/// world.entity_mut(e1).observe(|trigger: On, mut commands: Commands| { +/// println!("Boom!"); +/// commands.entity(trigger.target()).despawn(); +/// }); +/// +/// world.entity_mut(e2).observe(|trigger: On, mut commands: Commands| { +/// println!("The explosion fizzles! This entity is immune!"); +/// }); +/// ``` +/// +/// If all entities watched by a given [`Observer`] are despawned, the [`Observer`] entity will also be despawned. +/// This protects against observer "garbage" building up over time. +/// +/// The examples above calling [`EntityWorldMut::observe`] to add entity-specific observer logic are (once again) +/// just shorthand for spawning an [`Observer`] directly: +/// +/// ``` +/// # use bevy_ecs::prelude::*; +/// # let mut world = World::default(); +/// # let entity = world.spawn_empty().id(); +/// # #[derive(Event, EntityEvent)] +/// # struct Explode; +/// let mut observer = Observer::new(|trigger: On| {}); +/// observer.watch_entity(entity); +/// world.spawn(observer); +/// ``` +/// +/// Note that the [`Observer`] component is not added to the entity it is observing. Observers should always be their own entities! +/// +/// You can call [`Observer::watch_entity`] more than once, which allows you to watch multiple entities with the same [`Observer`]. +/// serves as the "source of truth" of the observer. +/// +/// [`SystemParam`]: crate::system::SystemParam +pub struct Observer { + hook_on_add: ComponentHook, + pub(crate) error_handler: Option, + pub(crate) system: Box, + pub(crate) descriptor: ObserverDescriptor, + pub(crate) last_trigger_id: u32, + pub(crate) despawned_watched_entities: u32, + pub(crate) runner: ObserverRunner, +} + +impl Observer { + /// Creates a new [`Observer`], which defaults to a "global" observer. This means it will run whenever the event `E` is triggered + /// for _any_ entity (or no entity). + /// + /// # Panics + /// + /// Panics if the given system is an exclusive system. + pub fn new>(system: I) -> Self { + let system = Box::new(IntoObserverSystem::into_system(system)); + assert!( + !system.is_exclusive(), + concat!( + "Exclusive system `{}` may not be used as observer.\n", + "Instead of `&mut World`, use either `DeferredWorld` if you do not need structural changes, or `Commands` if you do." + ), + system.name() + ); + Self { + system, + descriptor: Default::default(), + hook_on_add: hook_on_add::, + error_handler: None, + runner: observer_system_runner::, + despawned_watched_entities: 0, + last_trigger_id: 0, + } + } + + /// Creates a new [`Observer`] with custom runner, this is mostly used for dynamic event observer + pub fn with_dynamic_runner(runner: ObserverRunner) -> Self { + Self { + system: Box::new(IntoSystem::into_system(|| {})), + descriptor: Default::default(), + hook_on_add: |mut world, hook_context| { + let default_error_handler = world.default_error_handler(); + world.commands().queue(move |world: &mut World| { + let entity = hook_context.entity; + if let Some(mut observe) = world.get_mut::(entity) { + if observe.descriptor.events.is_empty() { + return; + } + if observe.error_handler.is_none() { + observe.error_handler = Some(default_error_handler); + } + world.register_observer(entity); + } + }); + }, + error_handler: None, + runner, + despawned_watched_entities: 0, + last_trigger_id: 0, + } + } + + /// Observe the given `entity`. This will cause the [`Observer`] to run whenever the [`Event`] is triggered + /// for the `entity`. + pub fn with_entity(mut self, entity: Entity) -> Self { + self.descriptor.entities.push(entity); + self + } + + /// Observe the given `entity`. This will cause the [`Observer`] to run whenever the [`Event`] is triggered + /// for the `entity`. + /// Note that if this is called _after_ an [`Observer`] is spawned, it will produce no effects. + pub fn watch_entity(&mut self, entity: Entity) { + self.descriptor.entities.push(entity); + } + + /// Observe the given `component`. This will cause the [`Observer`] to run whenever the [`Event`] is triggered + /// with the given component target. + pub fn with_component(mut self, component: ComponentId) -> Self { + self.descriptor.components.push(component); + self + } + + /// Observe the given `event`. This will cause the [`Observer`] to run whenever an event with the given [`ComponentId`] + /// is triggered. + /// # Safety + /// The type of the `event` [`ComponentId`] _must_ match the actual value + /// of the event passed into the observer system. + pub unsafe fn with_event(mut self, event: ComponentId) -> Self { + self.descriptor.events.push(event); + self + } + + /// Set the error handler to use for this observer. + /// + /// See the [`error` module-level documentation](crate::error) for more information. + pub fn with_error_handler(mut self, error_handler: fn(BevyError, ErrorContext)) -> Self { + self.error_handler = Some(error_handler); + self + } + + /// Returns the [`ObserverDescriptor`] for this [`Observer`]. + pub fn descriptor(&self) -> &ObserverDescriptor { + &self.descriptor + } + + /// Returns the name of the [`Observer`]'s system . + pub fn system_name(&self) -> DebugName { + self.system.system_name() + } +} + +impl Component for Observer { + const STORAGE_TYPE: StorageType = StorageType::SparseSet; + type Mutability = Mutable; + fn on_add() -> Option { + Some(|world, context| { + let Some(observe) = world.get::(context.entity) else { + return; + }; + let hook = observe.hook_on_add; + hook(world, context); + }) + } + fn on_remove() -> Option { + Some(|mut world, HookContext { entity, .. }| { + let descriptor = core::mem::take( + &mut world + .entity_mut(entity) + .get_mut::() + .unwrap() + .as_mut() + .descriptor, + ); + world.commands().queue(move |world: &mut World| { + world.unregister_observer(entity, descriptor); + }); + }) + } +} + +/// Store information about what an [`Observer`] observes. +/// +/// This information is stored inside of the [`Observer`] component, +#[derive(Default, Clone)] +pub struct ObserverDescriptor { + /// The events the observer is watching. + pub(super) events: Vec, + + /// The components the observer is watching. + pub(super) components: Vec, + + /// The entities the observer is watching. + pub(super) entities: Vec, +} + +impl ObserverDescriptor { + /// Add the given `events` to the descriptor. + /// # Safety + /// The type of each [`ComponentId`] in `events` _must_ match the actual value + /// of the event passed into the observer. + pub unsafe fn with_events(mut self, events: Vec) -> Self { + self.events = events; + self + } + + /// Add the given `components` to the descriptor. + pub fn with_components(mut self, components: Vec) -> Self { + self.components = components; + self + } + + /// Add the given `entities` to the descriptor. + pub fn with_entities(mut self, entities: Vec) -> Self { + self.entities = entities; + self + } + + /// Returns the `events` that the observer is watching. + pub fn events(&self) -> &[ComponentId] { + &self.events + } + + /// Returns the `components` that the observer is watching. + pub fn components(&self) -> &[ComponentId] { + &self.components + } + + /// Returns the `entities` that the observer is watching. + pub fn entities(&self) -> &[Entity] { + &self.entities + } +} + +/// A [`ComponentHook`] used by [`Observer`] to handle its [`on-add`](`crate::lifecycle::ComponentHooks::on_add`). +/// +/// This function exists separate from [`Observer`] to allow [`Observer`] to have its type parameters +/// erased. +/// +/// The type parameters of this function _must_ match those used to create the [`Observer`]. +/// As such, it is recommended to only use this function within the [`Observer::new`] method to +/// ensure type parameters match. +fn hook_on_add>( + mut world: DeferredWorld<'_>, + HookContext { entity, .. }: HookContext, +) { + world.commands().queue(move |world: &mut World| { + let event_id = E::register_component_id(world); + let mut components = alloc::vec![]; + B::component_ids(&mut world.components_registrator(), &mut |id| { + components.push(id); + }); + if let Some(mut observer) = world.get_mut::(entity) { + observer.descriptor.events.push(event_id); + observer.descriptor.components.extend(components); + + let system: &mut dyn Any = observer.system.as_mut(); + let system: *mut dyn ObserverSystem = system.downcast_mut::().unwrap(); + // SAFETY: World reference is exclusive and initialize does not touch system, so references do not alias + unsafe { + (*system).initialize(world); + } + world.register_observer(entity); + } + }); +} + +/// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to. +#[derive(Default, Debug)] +#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] +#[cfg_attr(feature = "bevy_reflect", reflect(Component, Debug))] +pub struct ObservedBy(pub(crate) Vec); + +impl ObservedBy { + /// Provides a read-only reference to the list of entities observing this entity. + pub fn get(&self) -> &[Entity] { + &self.0 + } +} + +impl Component for ObservedBy { + const STORAGE_TYPE: StorageType = StorageType::SparseSet; + type Mutability = Mutable; + + fn on_remove() -> Option { + Some(|mut world, HookContext { entity, .. }| { + let observed_by = { + let mut component = world.get_mut::(entity).unwrap(); + core::mem::take(&mut component.0) + }; + for e in observed_by { + let (total_entities, despawned_watched_entities) = { + let Ok(mut entity_mut) = world.get_entity_mut(e) else { + continue; + }; + let Some(mut state) = entity_mut.get_mut::() else { + continue; + }; + state.despawned_watched_entities += 1; + ( + state.descriptor.entities.len(), + state.despawned_watched_entities as usize, + ) + }; + + // Despawn Observer if it has no more active sources. + if total_entities == despawned_watched_entities { + world.commands().entity(e).despawn(); + } + } + }) + } + + fn clone_behavior() -> ComponentCloneBehavior { + ComponentCloneBehavior::Ignore + } +} + +pub(crate) trait AnyNamedSystem: Any + Send + Sync + 'static { + fn system_name(&self) -> DebugName; +} + +impl AnyNamedSystem for T { + fn system_name(&self) -> DebugName { + self.name() + } +} diff --git a/crates/bevy_ecs/src/observer/entity_observer.rs b/crates/bevy_ecs/src/observer/entity_cloning.rs similarity index 61% rename from crates/bevy_ecs/src/observer/entity_observer.rs rename to crates/bevy_ecs/src/observer/entity_cloning.rs index 23be0e9672..ee37300e64 100644 --- a/crates/bevy_ecs/src/observer/entity_observer.rs +++ b/crates/bevy_ecs/src/observer/entity_cloning.rs @@ -1,67 +1,14 @@ +//! Logic to track observers when cloning entities. + use crate::{ - component::{Component, ComponentCloneBehavior, Mutable, StorageType}, - entity::{ComponentCloneCtx, Entity, EntityClonerBuilder, EntityMapper, SourceComponent}, - lifecycle::{ComponentHook, HookContext}, + component::ComponentCloneBehavior, + entity::{ComponentCloneCtx, EntityClonerBuilder, EntityMapper, SourceComponent}, + observer::ObservedBy, world::World, }; -use alloc::vec::Vec; - -#[cfg(feature = "bevy_reflect")] -use crate::prelude::ReflectComponent; use super::Observer; -/// Tracks a list of entity observers for the [`Entity`] [`ObservedBy`] is added to. -#[derive(Default, Debug)] -#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))] -#[cfg_attr(feature = "bevy_reflect", reflect(Component, Debug))] -pub struct ObservedBy(pub(crate) Vec); - -impl ObservedBy { - /// Provides a read-only reference to the list of entities observing this entity. - pub fn get(&self) -> &[Entity] { - &self.0 - } -} - -impl Component for ObservedBy { - const STORAGE_TYPE: StorageType = StorageType::SparseSet; - type Mutability = Mutable; - - fn on_remove() -> Option { - Some(|mut world, HookContext { entity, .. }| { - let observed_by = { - let mut component = world.get_mut::(entity).unwrap(); - core::mem::take(&mut component.0) - }; - for e in observed_by { - let (total_entities, despawned_watched_entities) = { - let Ok(mut entity_mut) = world.get_entity_mut(e) else { - continue; - }; - let Some(mut state) = entity_mut.get_mut::() else { - continue; - }; - state.despawned_watched_entities += 1; - ( - state.descriptor.entities.len(), - state.despawned_watched_entities as usize, - ) - }; - - // Despawn Observer if it has no more active sources. - if total_entities == despawned_watched_entities { - world.commands().entity(e).despawn(); - } - } - }) - } - - fn clone_behavior() -> ComponentCloneBehavior { - ComponentCloneBehavior::Ignore - } -} - impl EntityClonerBuilder<'_> { /// Sets the option to automatically add cloned entities to the observers targeting source entity. pub fn add_observers(&mut self, add_observers: bool) -> &mut Self { diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index ad0956f6d9..be61ccc243 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -129,614 +129,26 @@ //! but allows for a more ad hoc approach with observers, //! and enables indefinite chaining of observers triggering other observers (for both better and worse!). -mod entity_observer; +mod centralized_storage; +mod distributed_storage; +mod entity_cloning; mod runner; +mod system_param; +mod trigger_targets; -pub use entity_observer::ObservedBy; +pub use centralized_storage::*; +pub use distributed_storage::*; pub use runner::*; -use variadics_please::all_tuples; +pub use system_param::*; +pub use trigger_targets::*; use crate::{ - archetype::ArchetypeFlags, change_detection::MaybeLocation, component::ComponentId, - entity::EntityHashMap, prelude::*, system::IntoObserverSystem, world::{DeferredWorld, *}, }; -use alloc::vec::Vec; -use bevy_platform::collections::HashMap; -use bevy_ptr::Ptr; -use core::{ - fmt::Debug, - marker::PhantomData, - ops::{Deref, DerefMut}, -}; -use smallvec::SmallVec; - -/// Type containing triggered [`Event`] information for a given run of an [`Observer`]. This contains the -/// [`Event`] data itself. If it was triggered for a specific [`Entity`], it includes that as well. It also -/// contains event propagation information. See [`On::propagate`] for more information. -/// -/// The generic `B: Bundle` is used to modify the further specialize the events that this observer is interested in. -/// The entity involved *does not* have to have these components, but the observer will only be -/// triggered if the event matches the components in `B`. -/// -/// This is used to to avoid providing a generic argument in your event, as is done for [`Add`] -/// and the other lifecycle events. -/// -/// Providing multiple components in this bundle will cause this event to be triggered by any -/// matching component in the bundle, -/// [rather than requiring all of them to be present](https://github.com/bevyengine/bevy/issues/15325). -pub struct On<'w, E, B: Bundle = ()> { - event: &'w mut E, - propagate: &'w mut bool, - trigger: ObserverTrigger, - _marker: PhantomData, -} - -/// Deprecated in favor of [`On`]. -#[deprecated(since = "0.17.0", note = "Renamed to `On`.")] -pub type Trigger<'w, E, B = ()> = On<'w, E, B>; - -impl<'w, E, B: Bundle> On<'w, E, B> { - /// Creates a new instance of [`On`] for the given event and observer information. - pub fn new(event: &'w mut E, propagate: &'w mut bool, trigger: ObserverTrigger) -> Self { - Self { - event, - propagate, - trigger, - _marker: PhantomData, - } - } - - /// Returns the event type of this [`On`] instance. - pub fn event_type(&self) -> ComponentId { - self.trigger.event_type - } - - /// Returns a reference to the triggered event. - pub fn event(&self) -> &E { - self.event - } - - /// Returns a mutable reference to the triggered event. - pub fn event_mut(&mut self) -> &mut E { - self.event - } - - /// Returns a pointer to the triggered event. - pub fn event_ptr(&self) -> Ptr { - Ptr::from(&self.event) - } - - /// Returns the components that triggered the observer, out of the - /// components defined in `B`. Does not necessarily include all of them as - /// `B` acts like an `OR` filter rather than an `AND` filter. - pub fn components(&self) -> &[ComponentId] { - &self.trigger.components - } - - /// Returns the [`Entity`] that observed the triggered event. - /// This allows you to despawn the observer, ceasing observation. - /// - /// # Examples - /// - /// ```rust - /// # use bevy_ecs::prelude::{Commands, On}; - /// # - /// # struct MyEvent { - /// # done: bool, - /// # } - /// # - /// /// Handle `MyEvent` and if it is done, stop observation. - /// fn my_observer(trigger: On, mut commands: Commands) { - /// if trigger.event().done { - /// commands.entity(trigger.observer()).despawn(); - /// return; - /// } - /// - /// // ... - /// } - /// ``` - pub fn observer(&self) -> Entity { - self.trigger.observer - } - - /// Returns the source code location that triggered this observer. - pub fn caller(&self) -> MaybeLocation { - self.trigger.caller - } -} - -impl<'w, E: EntityEvent, B: Bundle> On<'w, E, B> { - /// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. - /// - /// Note that if event propagation is enabled, this may not be the same as the original target of the event, - /// which can be accessed via [`On::original_target`]. - /// - /// If the event was not targeted at a specific entity, this will return [`Entity::PLACEHOLDER`]. - pub fn target(&self) -> Entity { - self.trigger.current_target.unwrap_or(Entity::PLACEHOLDER) - } - - /// Returns the original [`Entity`] that the `event` was targeted at when it was first triggered. - /// - /// If event propagation is not enabled, this will always return the same value as [`On::target`]. - /// - /// If the event was not targeted at a specific entity, this will return [`Entity::PLACEHOLDER`]. - pub fn original_target(&self) -> Entity { - self.trigger.original_target.unwrap_or(Entity::PLACEHOLDER) - } - - /// Enables or disables event propagation, allowing the same event to trigger observers on a chain of different entities. - /// - /// The path an event will propagate along is specified by its associated [`Traversal`] component. By default, events - /// use `()` which ends the path immediately and prevents propagation. - /// - /// To enable propagation, you must: - /// + Set [`EntityEvent::Traversal`] to the component you want to propagate along. - /// + Either call `propagate(true)` in the first observer or set [`EntityEvent::AUTO_PROPAGATE`] to `true`. - /// - /// You can prevent an event from propagating further using `propagate(false)`. - /// - /// [`Traversal`]: crate::traversal::Traversal - pub fn propagate(&mut self, should_propagate: bool) { - *self.propagate = should_propagate; - } - - /// Returns the value of the flag that controls event propagation. See [`propagate`] for more information. - /// - /// [`propagate`]: On::propagate - pub fn get_propagate(&self) -> bool { - *self.propagate - } -} - -impl<'w, E: Debug, B: Bundle> Debug for On<'w, E, B> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("On") - .field("event", &self.event) - .field("propagate", &self.propagate) - .field("trigger", &self.trigger) - .field("_marker", &self._marker) - .finish() - } -} - -impl<'w, E, B: Bundle> Deref for On<'w, E, B> { - type Target = E; - - fn deref(&self) -> &Self::Target { - self.event - } -} - -impl<'w, E, B: Bundle> DerefMut for On<'w, E, B> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.event - } -} - -/// Represents a collection of targets for a specific [`On`] instance of an [`Event`]. -/// -/// When an event is triggered with [`TriggerTargets`], any [`Observer`] that watches for that specific -/// event-target combination will run. -/// -/// This trait is implemented for both [`Entity`] and [`ComponentId`], allowing you to target specific entities or components. -/// It is also implemented for various collections of these types, such as [`Vec`], arrays, and tuples, -/// allowing you to trigger events for multiple targets at once. -pub trait TriggerTargets { - /// The components the trigger should target. - fn components(&self) -> impl Iterator + Clone + '_; - - /// The entities the trigger should target. - fn entities(&self) -> impl Iterator + Clone + '_; -} - -impl TriggerTargets for &T { - fn components(&self) -> impl Iterator + Clone + '_ { - (**self).components() - } - - fn entities(&self) -> impl Iterator + Clone + '_ { - (**self).entities() - } -} - -impl TriggerTargets for Entity { - fn components(&self) -> impl Iterator + Clone + '_ { - [].into_iter() - } - - fn entities(&self) -> impl Iterator + Clone + '_ { - core::iter::once(*self) - } -} - -impl TriggerTargets for ComponentId { - fn components(&self) -> impl Iterator + Clone + '_ { - core::iter::once(*self) - } - - fn entities(&self) -> impl Iterator + Clone + '_ { - [].into_iter() - } -} - -impl TriggerTargets for Vec { - fn components(&self) -> impl Iterator + Clone + '_ { - self.iter().flat_map(T::components) - } - - fn entities(&self) -> impl Iterator + Clone + '_ { - self.iter().flat_map(T::entities) - } -} - -impl TriggerTargets for [T; N] { - fn components(&self) -> impl Iterator + Clone + '_ { - self.iter().flat_map(T::components) - } - - fn entities(&self) -> impl Iterator + Clone + '_ { - self.iter().flat_map(T::entities) - } -} - -impl TriggerTargets for [T] { - fn components(&self) -> impl Iterator + Clone + '_ { - self.iter().flat_map(T::components) - } - - fn entities(&self) -> impl Iterator + Clone + '_ { - self.iter().flat_map(T::entities) - } -} - -macro_rules! impl_trigger_targets_tuples { - ($(#[$meta:meta])* $($trigger_targets: ident),*) => { - #[expect(clippy::allow_attributes, reason = "can't guarantee violation of non_snake_case")] - #[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")] - $(#[$meta])* - impl<$($trigger_targets: TriggerTargets),*> TriggerTargets for ($($trigger_targets,)*) - { - fn components(&self) -> impl Iterator + Clone + '_ { - let iter = [].into_iter(); - let ($($trigger_targets,)*) = self; - $( - let iter = iter.chain($trigger_targets.components()); - )* - iter - } - - fn entities(&self) -> impl Iterator + Clone + '_ { - let iter = [].into_iter(); - let ($($trigger_targets,)*) = self; - $( - let iter = iter.chain($trigger_targets.entities()); - )* - iter - } - } - } -} - -all_tuples!( - #[doc(fake_variadic)] - impl_trigger_targets_tuples, - 0, - 15, - T -); - -/// Store information about what an [`Observer`] observes. -/// -/// This information is stored inside of the [`Observer`] component, -#[derive(Default, Clone)] -pub struct ObserverDescriptor { - /// The events the observer is watching. - events: Vec, - - /// The components the observer is watching. - components: Vec, - - /// The entities the observer is watching. - entities: Vec, -} - -impl ObserverDescriptor { - /// Add the given `events` to the descriptor. - /// # Safety - /// The type of each [`ComponentId`] in `events` _must_ match the actual value - /// of the event passed into the observer. - pub unsafe fn with_events(mut self, events: Vec) -> Self { - self.events = events; - self - } - - /// Add the given `components` to the descriptor. - pub fn with_components(mut self, components: Vec) -> Self { - self.components = components; - self - } - - /// Add the given `entities` to the descriptor. - pub fn with_entities(mut self, entities: Vec) -> Self { - self.entities = entities; - self - } - - /// Returns the `events` that the observer is watching. - pub fn events(&self) -> &[ComponentId] { - &self.events - } - - /// Returns the `components` that the observer is watching. - pub fn components(&self) -> &[ComponentId] { - &self.components - } - - /// Returns the `entities` that the observer is watching. - pub fn entities(&self) -> &[Entity] { - &self.entities - } -} - -/// Metadata about a specific [`Event`] that triggered an observer. -/// -/// This information is exposed via methods on [`On`]. -#[derive(Debug)] -pub struct ObserverTrigger { - /// The [`Entity`] of the observer handling the trigger. - pub observer: Entity, - /// The [`Event`] the trigger targeted. - pub event_type: ComponentId, - /// The [`ComponentId`]s the trigger targeted. - components: SmallVec<[ComponentId; 2]>, - /// The entity that the entity-event targeted, if any. - /// - /// Note that if event propagation is enabled, this may not be the same as [`ObserverTrigger::original_target`]. - pub current_target: Option, - /// The entity that the entity-event was originally targeted at, if any. - /// - /// If event propagation is enabled, this will be the first entity that the event was targeted at, - /// even if the event was propagated to other entities. - pub original_target: Option, - /// The location of the source code that triggered the observer. - pub caller: MaybeLocation, -} - -impl ObserverTrigger { - /// Returns the components that the trigger targeted. - pub fn components(&self) -> &[ComponentId] { - &self.components - } -} - -/// Map between an observer entity and its [`ObserverRunner`] -pub type ObserverMap = EntityHashMap; - -/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event targeted at a specific component. -/// -/// This is stored inside of [`CachedObservers`]. -#[derive(Default, Debug)] -pub struct CachedComponentObservers { - // Observers listening to events targeting this component, but not a specific entity - global_observers: ObserverMap, - // Observers listening to events targeting this component on a specific entity - entity_component_observers: EntityHashMap, -} - -impl CachedComponentObservers { - /// Returns the observers listening for this trigger, regardless of target. - /// These observers will also respond to events targeting specific entities. - pub fn global_observers(&self) -> &ObserverMap { - &self.global_observers - } - - /// Returns the observers listening for this trigger targeting this component on a specific entity. - pub fn entity_component_observers(&self) -> &EntityHashMap { - &self.entity_component_observers - } -} - -/// Collection of [`ObserverRunner`] for [`Observer`] registered to a particular event. -/// -/// This is stored inside of [`Observers`], specialized for each kind of observer. -#[derive(Default, Debug)] -pub struct CachedObservers { - // Observers listening for any time this event is fired, regardless of target - // This will also respond to events targeting specific components or entities - global_observers: ObserverMap, - // Observers listening for this trigger fired at a specific component - component_observers: HashMap, - // Observers listening for this trigger fired at a specific entity - entity_observers: EntityHashMap, -} - -impl CachedObservers { - /// Returns the observers listening for this trigger, regardless of target. - /// These observers will also respond to events targeting specific components or entities. - pub fn global_observers(&self) -> &ObserverMap { - &self.global_observers - } - - /// Returns the observers listening for this trigger targeting components. - pub fn get_component_observers(&self) -> &HashMap { - &self.component_observers - } - - /// Returns the observers listening for this trigger targeting entities. - pub fn entity_observers(&self) -> &HashMap { - &self.component_observers - } -} - -/// An internal lookup table tracking all of the observers in the world. -/// -/// Stores a cache mapping trigger ids to the registered observers. -/// Some observer kinds (like [lifecycle](crate::lifecycle) observers) have a dedicated field, -/// saving lookups for the most common triggers. -/// -/// This can be accessed via [`World::observers`]. -#[derive(Default, Debug)] -pub struct Observers { - // Cached ECS observers to save a lookup most common triggers. - add: CachedObservers, - insert: CachedObservers, - replace: CachedObservers, - remove: CachedObservers, - despawn: CachedObservers, - // Map from trigger type to set of observers listening to that trigger - cache: HashMap, -} - -impl Observers { - pub(crate) fn get_observers_mut(&mut self, event_type: ComponentId) -> &mut CachedObservers { - use crate::lifecycle::*; - - match event_type { - ADD => &mut self.add, - INSERT => &mut self.insert, - REPLACE => &mut self.replace, - REMOVE => &mut self.remove, - DESPAWN => &mut self.despawn, - _ => self.cache.entry(event_type).or_default(), - } - } - - /// Attempts to get the observers for the given `event_type`. - /// - /// 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. - pub fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> { - use crate::lifecycle::*; - - match event_type { - ADD => Some(&self.add), - INSERT => Some(&self.insert), - REPLACE => Some(&self.replace), - REMOVE => Some(&self.remove), - DESPAWN => Some(&self.despawn), - _ => self.cache.get(&event_type), - } - } - - /// This will run the observers of the given `event_type`, targeting the given `entity` and `components`. - pub(crate) fn invoke( - mut world: DeferredWorld, - event_type: ComponentId, - current_target: Option, - original_target: Option, - components: impl Iterator + Clone, - data: &mut T, - propagate: &mut bool, - caller: MaybeLocation, - ) { - // SAFETY: You cannot get a mutable reference to `observers` from `DeferredWorld` - let (mut world, observers) = unsafe { - let world = world.as_unsafe_world_cell(); - // SAFETY: There are no outstanding world references - world.increment_trigger_id(); - let observers = world.observers(); - let Some(observers) = observers.try_get_observers(event_type) else { - return; - }; - // SAFETY: The only outstanding reference to world is `observers` - (world.into_deferred(), observers) - }; - - let trigger_for_components = components.clone(); - - let mut trigger_observer = |(&observer, runner): (&Entity, &ObserverRunner)| { - (runner)( - world.reborrow(), - ObserverTrigger { - observer, - event_type, - components: components.clone().collect(), - current_target, - original_target, - caller, - }, - data.into(), - propagate, - ); - }; - // Trigger observers listening for any kind of this trigger - observers - .global_observers - .iter() - .for_each(&mut trigger_observer); - - // Trigger entity observers listening for this kind of trigger - if let Some(target_entity) = current_target { - if let Some(map) = observers.entity_observers.get(&target_entity) { - map.iter().for_each(&mut trigger_observer); - } - } - - // Trigger observers listening to this trigger targeting a specific component - trigger_for_components.for_each(|id| { - if let Some(component_observers) = observers.component_observers.get(&id) { - component_observers - .global_observers - .iter() - .for_each(&mut trigger_observer); - - if let Some(target_entity) = current_target { - if let Some(map) = component_observers - .entity_component_observers - .get(&target_entity) - { - map.iter().for_each(&mut trigger_observer); - } - } - } - }); - } - - pub(crate) fn is_archetype_cached(event_type: ComponentId) -> Option { - use crate::lifecycle::*; - - match event_type { - ADD => Some(ArchetypeFlags::ON_ADD_OBSERVER), - INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER), - REPLACE => Some(ArchetypeFlags::ON_REPLACE_OBSERVER), - REMOVE => Some(ArchetypeFlags::ON_REMOVE_OBSERVER), - DESPAWN => Some(ArchetypeFlags::ON_DESPAWN_OBSERVER), - _ => None, - } - } - - pub(crate) fn update_archetype_flags( - &self, - component_id: ComponentId, - flags: &mut ArchetypeFlags, - ) { - if self.add.component_observers.contains_key(&component_id) { - flags.insert(ArchetypeFlags::ON_ADD_OBSERVER); - } - - if self.insert.component_observers.contains_key(&component_id) { - flags.insert(ArchetypeFlags::ON_INSERT_OBSERVER); - } - - if self.replace.component_observers.contains_key(&component_id) { - flags.insert(ArchetypeFlags::ON_REPLACE_OBSERVER); - } - - if self.remove.component_observers.contains_key(&component_id) { - flags.insert(ArchetypeFlags::ON_REMOVE_OBSERVER); - } - - if self.despawn.component_observers.contains_key(&component_id) { - flags.insert(ArchetypeFlags::ON_DESPAWN_OBSERVER); - } - } -} impl World { /// Spawns a "global" [`Observer`] which will watch for the given event. diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index d055164cc2..acc2830a7d 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -1,16 +1,10 @@ -use alloc::{boxed::Box, vec}; -use bevy_utils::prelude::DebugName; +//! Logic for evaluating observers, and storing functions inside of observers. + use core::any::Any; use crate::{ - component::{ComponentId, Mutable, StorageType}, - error::{ErrorContext, ErrorHandler}, - lifecycle::{ComponentHook, HookContext}, - observer::{ObserverDescriptor, ObserverTrigger}, - prelude::*, - query::DebugCheckedUnwrap, - system::{IntoObserverSystem, ObserverSystem}, - world::DeferredWorld, + error::ErrorContext, observer::ObserverTrigger, prelude::*, query::DebugCheckedUnwrap, + system::ObserverSystem, world::DeferredWorld, }; use bevy_ptr::PtrMut; @@ -20,323 +14,7 @@ use bevy_ptr::PtrMut; /// but can be overridden for custom behavior. pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: &mut bool); -/// An [`Observer`] system. Add this [`Component`] to an [`Entity`] to turn it into an "observer". -/// -/// Observers listen for a "trigger" of a specific [`Event`]. An event can be triggered on the [`World`] -/// by calling [`World::trigger`], or if the event is an [`EntityEvent`], it can also be triggered for specific -/// entity targets using [`World::trigger_targets`]. -/// -/// Note that [`BufferedEvent`]s sent using [`EventReader`] and [`EventWriter`] are _not_ automatically triggered. -/// They must be triggered at a specific point in the schedule. -/// -/// # Usage -/// -/// The simplest usage of the observer pattern looks like this: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// #[derive(Event)] -/// struct Speak { -/// message: String, -/// } -/// -/// world.add_observer(|trigger: On| { -/// println!("{}", trigger.event().message); -/// }); -/// -/// // Observers currently require a flush() to be registered. In the context of schedules, -/// // this will generally be done for you. -/// world.flush(); -/// -/// world.trigger(Speak { -/// message: "Hello!".into(), -/// }); -/// ``` -/// -/// Notice that we used [`World::add_observer`]. This is just a shorthand for spawning an [`Observer`] manually: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// # #[derive(Event)] -/// # struct Speak; -/// // These are functionally the same: -/// world.add_observer(|trigger: On| {}); -/// world.spawn(Observer::new(|trigger: On| {})); -/// ``` -/// -/// Observers are systems. They can access arbitrary [`World`] data by adding [`SystemParam`]s: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// # #[derive(Event)] -/// # struct PrintNames; -/// # #[derive(Component, Debug)] -/// # struct Name; -/// world.add_observer(|trigger: On, names: Query<&Name>| { -/// for name in &names { -/// println!("{name:?}"); -/// } -/// }); -/// ``` -/// -/// Note that [`On`] must always be the first parameter. -/// -/// You can also add [`Commands`], which means you can spawn new entities, insert new components, etc: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// # #[derive(Event)] -/// # struct SpawnThing; -/// # #[derive(Component, Debug)] -/// # struct Thing; -/// world.add_observer(|trigger: On, mut commands: Commands| { -/// commands.spawn(Thing); -/// }); -/// ``` -/// -/// Observers can also trigger new events: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// # #[derive(Event)] -/// # struct A; -/// # #[derive(Event)] -/// # struct B; -/// world.add_observer(|trigger: On, mut commands: Commands| { -/// commands.trigger(B); -/// }); -/// ``` -/// -/// When the commands are flushed (including these "nested triggers") they will be -/// recursively evaluated until there are no commands left, meaning nested triggers all -/// evaluate at the same time! -/// -/// If the event is an [`EntityEvent`], it can be triggered for specific entities, -/// which will be passed to the [`Observer`]: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// # let entity = world.spawn_empty().id(); -/// #[derive(Event, EntityEvent)] -/// struct Explode; -/// -/// world.add_observer(|trigger: On, mut commands: Commands| { -/// println!("Entity {} goes BOOM!", trigger.target()); -/// commands.entity(trigger.target()).despawn(); -/// }); -/// -/// world.flush(); -/// -/// world.trigger_targets(Explode, entity); -/// ``` -/// -/// You can trigger multiple entities at once: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// # let e1 = world.spawn_empty().id(); -/// # let e2 = world.spawn_empty().id(); -/// # #[derive(Event, EntityEvent)] -/// # struct Explode; -/// world.trigger_targets(Explode, [e1, e2]); -/// ``` -/// -/// Observers can also watch _specific_ entities, which enables you to assign entity-specific logic: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # #[derive(Component, Debug)] -/// # struct Name(String); -/// # let mut world = World::default(); -/// # let e1 = world.spawn_empty().id(); -/// # let e2 = world.spawn_empty().id(); -/// # #[derive(Event, EntityEvent)] -/// # struct Explode; -/// world.entity_mut(e1).observe(|trigger: On, mut commands: Commands| { -/// println!("Boom!"); -/// commands.entity(trigger.target()).despawn(); -/// }); -/// -/// world.entity_mut(e2).observe(|trigger: On, mut commands: Commands| { -/// println!("The explosion fizzles! This entity is immune!"); -/// }); -/// ``` -/// -/// If all entities watched by a given [`Observer`] are despawned, the [`Observer`] entity will also be despawned. -/// This protects against observer "garbage" building up over time. -/// -/// The examples above calling [`EntityWorldMut::observe`] to add entity-specific observer logic are (once again) -/// just shorthand for spawning an [`Observer`] directly: -/// -/// ``` -/// # use bevy_ecs::prelude::*; -/// # let mut world = World::default(); -/// # let entity = world.spawn_empty().id(); -/// # #[derive(Event, EntityEvent)] -/// # struct Explode; -/// let mut observer = Observer::new(|trigger: On| {}); -/// observer.watch_entity(entity); -/// world.spawn(observer); -/// ``` -/// -/// Note that the [`Observer`] component is not added to the entity it is observing. Observers should always be their own entities! -/// -/// You can call [`Observer::watch_entity`] more than once, which allows you to watch multiple entities with the same [`Observer`]. -/// serves as the "source of truth" of the observer. -/// -/// [`SystemParam`]: crate::system::SystemParam -pub struct Observer { - hook_on_add: ComponentHook, - error_handler: Option, - system: Box, - pub(crate) descriptor: ObserverDescriptor, - pub(crate) last_trigger_id: u32, - pub(crate) despawned_watched_entities: u32, - pub(crate) runner: ObserverRunner, -} - -impl Observer { - /// Creates a new [`Observer`], which defaults to a "global" observer. This means it will run whenever the event `E` is triggered - /// for _any_ entity (or no entity). - /// - /// # Panics - /// - /// Panics if the given system is an exclusive system. - pub fn new>(system: I) -> Self { - let system = Box::new(IntoObserverSystem::into_system(system)); - assert!( - !system.is_exclusive(), - concat!( - "Exclusive system `{}` may not be used as observer.\n", - "Instead of `&mut World`, use either `DeferredWorld` if you do not need structural changes, or `Commands` if you do." - ), - system.name() - ); - Self { - system, - descriptor: Default::default(), - hook_on_add: hook_on_add::, - error_handler: None, - runner: observer_system_runner::, - despawned_watched_entities: 0, - last_trigger_id: 0, - } - } - - /// Creates a new [`Observer`] with custom runner, this is mostly used for dynamic event observer - pub fn with_dynamic_runner(runner: ObserverRunner) -> Self { - Self { - system: Box::new(IntoSystem::into_system(|| {})), - descriptor: Default::default(), - hook_on_add: |mut world, hook_context| { - let default_error_handler = world.default_error_handler(); - world.commands().queue(move |world: &mut World| { - let entity = hook_context.entity; - if let Some(mut observe) = world.get_mut::(entity) { - if observe.descriptor.events.is_empty() { - return; - } - if observe.error_handler.is_none() { - observe.error_handler = Some(default_error_handler); - } - world.register_observer(entity); - } - }); - }, - error_handler: None, - runner, - despawned_watched_entities: 0, - last_trigger_id: 0, - } - } - - /// Observe the given `entity`. This will cause the [`Observer`] to run whenever the [`Event`] is triggered - /// for the `entity`. - pub fn with_entity(mut self, entity: Entity) -> Self { - self.descriptor.entities.push(entity); - self - } - - /// Observe the given `entity`. This will cause the [`Observer`] to run whenever the [`Event`] is triggered - /// for the `entity`. - /// Note that if this is called _after_ an [`Observer`] is spawned, it will produce no effects. - pub fn watch_entity(&mut self, entity: Entity) { - self.descriptor.entities.push(entity); - } - - /// Observe the given `component`. This will cause the [`Observer`] to run whenever the [`Event`] is triggered - /// with the given component target. - pub fn with_component(mut self, component: ComponentId) -> Self { - self.descriptor.components.push(component); - self - } - - /// Observe the given `event`. This will cause the [`Observer`] to run whenever an event with the given [`ComponentId`] - /// is triggered. - /// # Safety - /// The type of the `event` [`ComponentId`] _must_ match the actual value - /// of the event passed into the observer system. - pub unsafe fn with_event(mut self, event: ComponentId) -> Self { - self.descriptor.events.push(event); - self - } - - /// Set the error handler to use for this observer. - /// - /// See the [`error` module-level documentation](crate::error) for more information. - pub fn with_error_handler(mut self, error_handler: fn(BevyError, ErrorContext)) -> Self { - self.error_handler = Some(error_handler); - self - } - - /// Returns the [`ObserverDescriptor`] for this [`Observer`]. - pub fn descriptor(&self) -> &ObserverDescriptor { - &self.descriptor - } - - /// Returns the name of the [`Observer`]'s system . - pub fn system_name(&self) -> DebugName { - self.system.system_name() - } -} - -impl Component for Observer { - const STORAGE_TYPE: StorageType = StorageType::SparseSet; - type Mutability = Mutable; - fn on_add() -> Option { - Some(|world, context| { - let Some(observe) = world.get::(context.entity) else { - return; - }; - let hook = observe.hook_on_add; - hook(world, context); - }) - } - fn on_remove() -> Option { - Some(|mut world, HookContext { entity, .. }| { - let descriptor = core::mem::take( - &mut world - .entity_mut(entity) - .get_mut::() - .unwrap() - .as_mut() - .descriptor, - ); - world.commands().queue(move |world: &mut World| { - world.unregister_observer(entity, descriptor); - }); - }) - } -} - -fn observer_system_runner>( +pub(super) fn observer_system_runner>( mut world: DeferredWorld, observer_trigger: ObserverTrigger, ptr: PtrMut, @@ -420,48 +98,6 @@ fn observer_system_runner>( } } -trait AnyNamedSystem: Any + Send + Sync + 'static { - fn system_name(&self) -> DebugName; -} - -impl AnyNamedSystem for T { - fn system_name(&self) -> DebugName { - self.name() - } -} - -/// A [`ComponentHook`] used by [`Observer`] to handle its [`on-add`](`crate::lifecycle::ComponentHooks::on_add`). -/// -/// This function exists separate from [`Observer`] to allow [`Observer`] to have its type parameters -/// erased. -/// -/// The type parameters of this function _must_ match those used to create the [`Observer`]. -/// As such, it is recommended to only use this function within the [`Observer::new`] method to -/// ensure type parameters match. -fn hook_on_add>( - mut world: DeferredWorld<'_>, - HookContext { entity, .. }: HookContext, -) { - world.commands().queue(move |world: &mut World| { - let event_id = E::register_component_id(world); - let mut components = vec![]; - B::component_ids(&mut world.components_registrator(), &mut |id| { - components.push(id); - }); - if let Some(mut observer) = world.get_mut::(entity) { - observer.descriptor.events.push(event_id); - observer.descriptor.components.extend(components); - - let system: &mut dyn Any = observer.system.as_mut(); - let system: *mut dyn ObserverSystem = system.downcast_mut::().unwrap(); - // SAFETY: World reference is exclusive and initialize does not touch system, so references do not alias - unsafe { - (*system).initialize(world); - } - world.register_observer(entity); - } - }); -} #[cfg(test)] mod tests { use super::*; diff --git a/crates/bevy_ecs/src/observer/system_param.rs b/crates/bevy_ecs/src/observer/system_param.rs new file mode 100644 index 0000000000..27d6fef5b3 --- /dev/null +++ b/crates/bevy_ecs/src/observer/system_param.rs @@ -0,0 +1,206 @@ +//! System parameters for working with observers. + +use core::marker::PhantomData; +use core::ops::DerefMut; +use core::{fmt::Debug, ops::Deref}; + +use bevy_ptr::Ptr; +use smallvec::SmallVec; + +use crate::{ + bundle::Bundle, change_detection::MaybeLocation, component::ComponentId, event::EntityEvent, + prelude::*, +}; + +/// Type containing triggered [`Event`] information for a given run of an [`Observer`]. This contains the +/// [`Event`] data itself. If it was triggered for a specific [`Entity`], it includes that as well. It also +/// contains event propagation information. See [`On::propagate`] for more information. +/// +/// The generic `B: Bundle` is used to modify the further specialize the events that this observer is interested in. +/// The entity involved *does not* have to have these components, but the observer will only be +/// triggered if the event matches the components in `B`. +/// +/// This is used to to avoid providing a generic argument in your event, as is done for [`Add`] +/// and the other lifecycle events. +/// +/// Providing multiple components in this bundle will cause this event to be triggered by any +/// matching component in the bundle, +/// [rather than requiring all of them to be present](https://github.com/bevyengine/bevy/issues/15325). +pub struct On<'w, E, B: Bundle = ()> { + event: &'w mut E, + propagate: &'w mut bool, + trigger: ObserverTrigger, + _marker: PhantomData, +} + +/// Deprecated in favor of [`On`]. +#[deprecated(since = "0.17.0", note = "Renamed to `On`.")] +pub type Trigger<'w, E, B = ()> = On<'w, E, B>; + +impl<'w, E, B: Bundle> On<'w, E, B> { + /// Creates a new instance of [`On`] for the given event and observer information. + pub fn new(event: &'w mut E, propagate: &'w mut bool, trigger: ObserverTrigger) -> Self { + Self { + event, + propagate, + trigger, + _marker: PhantomData, + } + } + + /// Returns the event type of this [`On`] instance. + pub fn event_type(&self) -> ComponentId { + self.trigger.event_type + } + + /// Returns a reference to the triggered event. + pub fn event(&self) -> &E { + self.event + } + + /// Returns a mutable reference to the triggered event. + pub fn event_mut(&mut self) -> &mut E { + self.event + } + + /// Returns a pointer to the triggered event. + pub fn event_ptr(&self) -> Ptr { + Ptr::from(&self.event) + } + + /// Returns the components that triggered the observer, out of the + /// components defined in `B`. Does not necessarily include all of them as + /// `B` acts like an `OR` filter rather than an `AND` filter. + pub fn components(&self) -> &[ComponentId] { + &self.trigger.components + } + + /// Returns the [`Entity`] that observed the triggered event. + /// This allows you to despawn the observer, ceasing observation. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_ecs::prelude::*; + /// + /// #[derive(Event, EntityEvent)] + /// struct AssertEvent; + /// + /// fn assert_observer(trigger: On) { + /// assert_eq!(trigger.observer(), trigger.target()); + /// } + /// + /// let mut world = World::new(); + /// let observer = world.spawn(Observer::new(assert_observer)).id(); + /// + /// world.trigger_targets(AssertEvent, observer); + /// ``` + pub fn observer(&self) -> Entity { + self.trigger.observer + } + + /// Returns the source code location that triggered this observer. + pub fn caller(&self) -> MaybeLocation { + self.trigger.caller + } +} + +impl<'w, E: EntityEvent, B: Bundle> On<'w, E, B> { + /// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. + /// + /// Note that if event propagation is enabled, this may not be the same as the original target of the event, + /// which can be accessed via [`On::original_target`]. + /// + /// If the event was not targeted at a specific entity, this will return [`Entity::PLACEHOLDER`]. + pub fn target(&self) -> Entity { + self.trigger.current_target.unwrap_or(Entity::PLACEHOLDER) + } + + /// Returns the original [`Entity`] that the `event` was targeted at when it was first triggered. + /// + /// If event propagation is not enabled, this will always return the same value as [`On::target`]. + /// + /// If the event was not targeted at a specific entity, this will return [`Entity::PLACEHOLDER`]. + pub fn original_target(&self) -> Entity { + self.trigger.original_target.unwrap_or(Entity::PLACEHOLDER) + } + + /// Enables or disables event propagation, allowing the same event to trigger observers on a chain of different entities. + /// + /// The path an event will propagate along is specified by its associated [`Traversal`] component. By default, events + /// use `()` which ends the path immediately and prevents propagation. + /// + /// To enable propagation, you must: + /// + Set [`EntityEvent::Traversal`] to the component you want to propagate along. + /// + Either call `propagate(true)` in the first observer or set [`EntityEvent::AUTO_PROPAGATE`] to `true`. + /// + /// You can prevent an event from propagating further using `propagate(false)`. + /// + /// [`Traversal`]: crate::traversal::Traversal + pub fn propagate(&mut self, should_propagate: bool) { + *self.propagate = should_propagate; + } + + /// Returns the value of the flag that controls event propagation. See [`propagate`] for more information. + /// + /// [`propagate`]: On::propagate + pub fn get_propagate(&self) -> bool { + *self.propagate + } +} + +impl<'w, E: Debug, B: Bundle> Debug for On<'w, E, B> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("On") + .field("event", &self.event) + .field("propagate", &self.propagate) + .field("trigger", &self.trigger) + .field("_marker", &self._marker) + .finish() + } +} + +impl<'w, E, B: Bundle> Deref for On<'w, E, B> { + type Target = E; + + fn deref(&self) -> &Self::Target { + self.event + } +} + +impl<'w, E, B: Bundle> DerefMut for On<'w, E, B> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.event + } +} + +/// Metadata about a specific [`Event`] that triggered an observer. +/// +/// This information is exposed via methods on [`On`]. +#[derive(Debug)] +pub struct ObserverTrigger { + /// The [`Entity`] of the observer handling the trigger. + pub observer: Entity, + /// The [`Event`] the trigger targeted. + pub event_type: ComponentId, + /// The [`ComponentId`]s the trigger targeted. + pub components: SmallVec<[ComponentId; 2]>, + /// The entity that the entity-event targeted, if any. + /// + /// Note that if event propagation is enabled, this may not be the same as [`ObserverTrigger::original_target`]. + pub current_target: Option, + /// The entity that the entity-event was originally targeted at, if any. + /// + /// If event propagation is enabled, this will be the first entity that the event was targeted at, + /// even if the event was propagated to other entities. + pub original_target: Option, + /// The location of the source code that triggered the observer. + pub caller: MaybeLocation, +} + +impl ObserverTrigger { + /// Returns the components that the trigger targeted. + pub fn components(&self) -> &[ComponentId] { + &self.components + } +} diff --git a/crates/bevy_ecs/src/observer/trigger_targets.rs b/crates/bevy_ecs/src/observer/trigger_targets.rs new file mode 100644 index 0000000000..77728e4acd --- /dev/null +++ b/crates/bevy_ecs/src/observer/trigger_targets.rs @@ -0,0 +1,117 @@ +//! Stores the [`TriggerTargets`] trait. + +use crate::{component::ComponentId, prelude::*}; +use alloc::vec::Vec; +use variadics_please::all_tuples; + +/// Represents a collection of targets for a specific [`On`] instance of an [`Event`]. +/// +/// When an event is triggered with [`TriggerTargets`], any [`Observer`] that watches for that specific +/// event-target combination will run. +/// +/// This trait is implemented for both [`Entity`] and [`ComponentId`], allowing you to target specific entities or components. +/// It is also implemented for various collections of these types, such as [`Vec`], arrays, and tuples, +/// allowing you to trigger events for multiple targets at once. +pub trait TriggerTargets { + /// The components the trigger should target. + fn components(&self) -> impl Iterator + Clone + '_; + + /// The entities the trigger should target. + fn entities(&self) -> impl Iterator + Clone + '_; +} + +impl TriggerTargets for &T { + fn components(&self) -> impl Iterator + Clone + '_ { + (**self).components() + } + + fn entities(&self) -> impl Iterator + Clone + '_ { + (**self).entities() + } +} + +impl TriggerTargets for Entity { + fn components(&self) -> impl Iterator + Clone + '_ { + [].into_iter() + } + + fn entities(&self) -> impl Iterator + Clone + '_ { + core::iter::once(*self) + } +} + +impl TriggerTargets for ComponentId { + fn components(&self) -> impl Iterator + Clone + '_ { + core::iter::once(*self) + } + + fn entities(&self) -> impl Iterator + Clone + '_ { + [].into_iter() + } +} + +impl TriggerTargets for Vec { + fn components(&self) -> impl Iterator + Clone + '_ { + self.iter().flat_map(T::components) + } + + fn entities(&self) -> impl Iterator + Clone + '_ { + self.iter().flat_map(T::entities) + } +} + +impl TriggerTargets for [T; N] { + fn components(&self) -> impl Iterator + Clone + '_ { + self.iter().flat_map(T::components) + } + + fn entities(&self) -> impl Iterator + Clone + '_ { + self.iter().flat_map(T::entities) + } +} + +impl TriggerTargets for [T] { + fn components(&self) -> impl Iterator + Clone + '_ { + self.iter().flat_map(T::components) + } + + fn entities(&self) -> impl Iterator + Clone + '_ { + self.iter().flat_map(T::entities) + } +} + +macro_rules! impl_trigger_targets_tuples { + ($(#[$meta:meta])* $($trigger_targets: ident),*) => { + #[expect(clippy::allow_attributes, reason = "can't guarantee violation of non_snake_case")] + #[allow(non_snake_case, reason = "`all_tuples!()` generates non-snake-case variable names.")] + $(#[$meta])* + impl<$($trigger_targets: TriggerTargets),*> TriggerTargets for ($($trigger_targets,)*) + { + fn components(&self) -> impl Iterator + Clone + '_ { + let iter = [].into_iter(); + let ($($trigger_targets,)*) = self; + $( + let iter = iter.chain($trigger_targets.components()); + )* + iter + } + + fn entities(&self) -> impl Iterator + Clone + '_ { + let iter = [].into_iter(); + let ($($trigger_targets,)*) = self; + $( + let iter = iter.chain($trigger_targets.entities()); + )* + iter + } + } + } +} + +all_tuples!( + #[doc(fake_variadic)] + impl_trigger_targets_tuples, + 0, + 15, + T +);