//! Observers are a push-based tool for responding to [`Event`]s. //! //! ## Observer targeting //! //! Observers can be "global", listening for events that are both targeted at and not targeted at any specific entity, //! or they can be "entity-specific", listening for events that are targeted at specific entities. //! //! They can also be further refined by listening to events targeted at specific components //! (instead of using a generic event type), as is done with the [`Add`] family of lifecycle events. //! //! When entities are observed, they will receive an [`ObservedBy`] component, //! which will be updated to track the observers that are currently observing them. //! //! Currently, [observers cannot be retargeted after spawning](https://github.com/bevyengine/bevy/issues/19587): //! despawn and respawn an observer as a workaround. //! //! ## Writing observers //! //! Observers are systems which implement [`IntoObserverSystem`] that listen for [`Event`]s matching their //! type and target(s). //! To write observer systems, use [`On`] as the first parameter of your system. //! This parameter provides access to the specific event that triggered the observer, //! as well as the entity that the event was targeted at, if any. //! //! Observers can request other data from the world, such as via a [`Query`] or [`Res`]. //! Commonly, you might want to verify that the entity that the observable event is targeting //! has a specific component, or meets some other condition. [`Query::get`] or [`Query::contains`] //! on the [`On::target`] entity is a good way to do this. //! //! [`Commands`] can also be used inside of observers. //! This can be particularly useful for triggering other observers! //! //! ## Spawning observers //! //! Observers can be spawned via [`World::add_observer`], or the equivalent app method. //! This will cause an entity with the [`Observer`] component to be created, //! which will then run the observer system whenever the event it is watching is triggered. //! //! You can control the targets that an observer is watching by calling [`Observer::watch_entity`] //! once the entity is spawned, or by manually spawning an entity with the [`Observer`] component //! configured with the desired targets. //! //! Observers are fundamentally defined as "entities which have the [`Observer`] component" //! allowing you to add it manually to existing entities. //! At first, this seems convenient, but only one observer can be added to an entity at a time, //! regardless of the event it responds to: like always, components are unique. //! //! Instead, a better way to achieve a similar aim is to //! use the [`EntityWorldMut::observe`] / [`EntityCommands::observe`] method, //! which spawns a new observer, and configures it to watch the entity it is called on. //! Unfortunately, observers defined in this way //! [currently cannot be spawned as part of bundles](https://github.com/bevyengine/bevy/issues/14204). //! //! ## Triggering observers //! //! Observers are most commonly triggered by [`Commands`], //! via [`Commands::trigger`] (for untargeted [`Event`]s) or [`Commands::trigger_targets`] (for targeted [`EntityEvent`]s). //! Like usual, equivalent methods are available on [`World`], allowing you to reduce overhead when working with exclusive world access. //! //! If your observer is configured to watch for a specific component or set of components instead, //! you can pass in [`ComponentId`]s into [`Commands::trigger_targets`] by using the [`TriggerTargets`] trait. //! As discussed in the [`On`] documentation, this use case is rare, and is currently only used //! for [lifecycle](crate::lifecycle) events, which are automatically emitted. //! //! ## Observer bubbling //! //! When using an [`EntityEvent`] targeted at an entity, the event can optionally be propagated to other targets, //! typically up to parents in an entity hierarchy. //! //! This behavior is controlled via [`EntityEvent::Traversal`] and [`EntityEvent::AUTO_PROPAGATE`], //! with the details of the propagation path specified by the [`Traversal`](crate::traversal::Traversal) trait. //! //! When auto-propagation is enabled, propagation must be manually stopped to prevent the event from //! continuing to other targets. This can be done using the [`On::propagate`] method inside of your observer. //! //! ## Observer timing //! //! Observers are triggered via [`Commands`], which imply that they are evaluated at the next sync point in the ECS schedule. //! Accordingly, they have full access to the world, and are evaluated sequentially, in the order that the commands were sent. //! //! To control the relative ordering of observers sent from different systems, //! order the systems in the schedule relative to each other. //! //! Currently, Bevy does not provide [a way to specify the ordering of observers](https://github.com/bevyengine/bevy/issues/14890) //! listening to the same event relative to each other. //! //! Commands sent by observers are [currently not immediately applied](https://github.com/bevyengine/bevy/issues/19569). //! Instead, all queued observers will run, and then all of the commands from those observers will be applied. //! Careful use of [`Schedule::apply_deferred`] may help as a workaround. //! //! ## Lifecycle events and observers //! //! It is important to note that observers, just like [hooks](crate::lifecycle::ComponentHooks), //! can listen to and respond to [lifecycle](crate::lifecycle) events. //! Unlike hooks, observers are not treated as an "innate" part of component behavior: //! they can be added or removed at runtime, and multiple observers //! can be registered for the same lifecycle event for the same component. //! //! The ordering of hooks versus observers differs based on the lifecycle event in question: //! //! - when adding components, hooks are evaluated first, then observers //! - when removing components, observers are evaluated first, then hooks //! //! This allows hooks to act as constructors and destructors for components, //! as they always have the first and final say in the component's lifecycle. //! //! ## Cleaning up observers //! //! Currently, observer entities are never cleaned up, even if their target entity(s) are despawned. //! This won't cause any runtime overhead, but is a waste of memory and can result in memory leaks. //! //! If you run into this problem, you could manually scan the world for observer entities and despawn them, //! by checking if the entity in [`Observer::descriptor`] still exists. //! //! ## Observers vs buffered events //! //! By contrast, [`EventReader`] and [`EventWriter`] ("buffered events"), are pull-based. //! They require periodically polling the world to check for new events, typically in a system that runs as part of a schedule. //! //! This imposes a small overhead, making observers a better choice for extremely rare events, //! but buffered events can be more efficient for events that are expected to occur multiple times per frame, //! as it allows for batch processing of events. //! //! The difference in timing is also an important consideration: //! buffered events are evaluated at fixed points during schedules, //! while observers are evaluated as soon as possible after the event is triggered. //! //! This provides more control over the timing of buffered event evaluation, //! 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 centralized_storage; mod distributed_storage; mod entity_cloning; mod runner; mod system_param; mod trigger_targets; pub use centralized_storage::*; pub use distributed_storage::*; pub use runner::*; pub use system_param::*; pub use trigger_targets::*; use crate::{ change_detection::MaybeLocation, component::ComponentId, prelude::*, system::IntoObserverSystem, world::{DeferredWorld, *}, }; impl World { /// Spawns a "global" [`Observer`] which will watch for the given event. /// Returns its [`Entity`] as a [`EntityWorldMut`]. /// /// `system` can be any system whose first parameter is [`On`]. /// /// **Calling [`observe`](EntityWorldMut::observe) on the returned /// [`EntityWorldMut`] will observe the observer itself, which you very /// likely do not want.** /// /// # Example /// /// ``` /// # use bevy_ecs::prelude::*; /// #[derive(Component)] /// struct A; /// /// # let mut world = World::new(); /// world.add_observer(|_: On| { /// // ... /// }); /// world.add_observer(|_: On| { /// // ... /// }); /// ``` /// /// # Panics /// /// Panics if the given system is an exclusive system. pub fn add_observer( &mut self, system: impl IntoObserverSystem, ) -> EntityWorldMut { self.spawn(Observer::new(system)) } /// Triggers the given [`Event`], which will run any [`Observer`]s watching for it. /// /// While event types commonly implement [`Copy`], /// those that don't will be consumed and will no longer be accessible. /// If you need to use the event after triggering it, use [`World::trigger_ref`] instead. #[track_caller] pub fn trigger(&mut self, event: E) { self.trigger_with_caller(event, MaybeLocation::caller()); } pub(crate) fn trigger_with_caller(&mut self, mut event: E, caller: MaybeLocation) { let event_key = E::register_event_key(self); // SAFETY: We just registered `event_key` with the type of `event` unsafe { self.trigger_dynamic_ref_with_caller(event_key, &mut event, caller); } } /// Triggers the given [`Event`] as a mutable reference, which will run any [`Observer`]s watching for it. /// /// Compared to [`World::trigger`], this method is most useful when it's necessary to check /// or use the event after it has been modified by observers. #[track_caller] pub fn trigger_ref(&mut self, event: &mut E) { let event_key = E::register_event_key(self); // SAFETY: We just registered `event_key` with the type of `event` unsafe { self.trigger_dynamic_ref_with_caller(event_key, event, MaybeLocation::caller()) }; } unsafe fn trigger_dynamic_ref_with_caller( &mut self, event_key: EventKey, event_data: &mut E, caller: MaybeLocation, ) { let mut world = DeferredWorld::from(self); // SAFETY: `event_data` is accessible as the type represented by `event_key` unsafe { world.trigger_observers_with_data::<_, ()>( event_key, None, None, core::iter::empty::(), event_data, false, caller, ); }; } /// Triggers the given [`EntityEvent`] for the given `targets`, which will run any [`Observer`]s watching for it. /// /// While event types commonly implement [`Copy`], /// those that don't will be consumed and will no longer be accessible. /// If you need to use the event after triggering it, use [`World::trigger_targets_ref`] instead. #[track_caller] pub fn trigger_targets(&mut self, event: E, targets: impl TriggerTargets) { self.trigger_targets_with_caller(event, targets, MaybeLocation::caller()); } pub(crate) fn trigger_targets_with_caller( &mut self, mut event: E, targets: impl TriggerTargets, caller: MaybeLocation, ) { let event_key = E::register_event_key(self); // SAFETY: We just registered `event_key` with the type of `event` unsafe { self.trigger_targets_dynamic_ref_with_caller(event_key, &mut event, targets, caller); } } /// Triggers the given [`EntityEvent`] as a mutable reference for the given `targets`, /// which will run any [`Observer`]s watching for it. /// /// Compared to [`World::trigger_targets`], this method is most useful when it's necessary to check /// or use the event after it has been modified by observers. #[track_caller] pub fn trigger_targets_ref( &mut self, event: &mut E, targets: impl TriggerTargets, ) { let event_key = E::register_event_key(self); // SAFETY: We just registered `event_key` with the type of `event` 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. /// /// While event types commonly implement [`Copy`], /// those that don't will be consumed and will no longer be accessible. /// If you need to use the event after triggering it, use [`World::trigger_targets_dynamic_ref`] instead. /// /// # Safety /// /// Caller must ensure that `event_data` is accessible as the type represented by `event_key`. #[track_caller] pub unsafe fn trigger_targets_dynamic( &mut self, event_key: EventKey, mut event_data: E, targets: Targets, ) { // SAFETY: `event_data` is accessible as the type represented by `event_key` unsafe { self.trigger_targets_dynamic_ref(event_key, &mut event_data, targets); }; } /// Triggers the given [`EntityEvent`] as a mutable reference for the given `targets`, /// which will run any [`Observer`]s watching for it. /// /// Compared to [`World::trigger_targets_dynamic`], this method is most useful when it's necessary to check /// or use the event after it has been modified by observers. /// /// # Safety /// /// Caller must ensure that `event_data` is accessible as the type represented by `event_key`. #[track_caller] pub unsafe fn trigger_targets_dynamic_ref( &mut self, event_key: EventKey, event_data: &mut E, targets: Targets, ) { self.trigger_targets_dynamic_ref_with_caller( event_key, event_data, targets, MaybeLocation::caller(), ); } /// # Safety /// /// See `trigger_targets_dynamic_ref` unsafe fn trigger_targets_dynamic_ref_with_caller( &mut self, event_key: EventKey, event_data: &mut E, targets: Targets, caller: MaybeLocation, ) { let mut world = DeferredWorld::from(self); let mut entity_targets = targets.entities().peekable(); if entity_targets.peek().is_none() { // SAFETY: `event_data` is accessible as the type represented by `event_key` unsafe { world.trigger_observers_with_data::<_, E::Traversal>( event_key, None, None, targets.components(), event_data, false, caller, ); }; } else { for target_entity in entity_targets { // SAFETY: `event_data` is accessible as the type represented by `event_key` unsafe { world.trigger_observers_with_data::<_, E::Traversal>( event_key, Some(target_entity), Some(target_entity), targets.components(), event_data, E::AUTO_PROPAGATE, caller, ); }; } } } /// Register an observer to the cache, called when an observer is created pub(crate) fn register_observer(&mut self, observer_entity: Entity) { // SAFETY: References do not alias. let (observer_state, archetypes, observers) = unsafe { let observer_state: *const Observer = self.get::(observer_entity).unwrap(); // Populate ObservedBy for each observed entity. for watched_entity in (*observer_state).descriptor.entities.iter().copied() { let mut entity_mut = self.entity_mut(watched_entity); let mut observed_by = entity_mut.entry::().or_default().into_mut(); observed_by.0.push(observer_entity); } (&*observer_state, &mut self.archetypes, &mut self.observers) }; let descriptor = &observer_state.descriptor; for &event_key in &descriptor.events { let cache = observers.get_observers_mut(event_key); if descriptor.components.is_empty() && descriptor.entities.is_empty() { cache .global_observers .insert(observer_entity, observer_state.runner); } else if descriptor.components.is_empty() { // Observer is not targeting any components so register it as an entity observer for &watched_entity in &observer_state.descriptor.entities { let map = cache.entity_observers.entry(watched_entity).or_default(); map.insert(observer_entity, observer_state.runner); } } else { // Register observer for each watched component for &component in &descriptor.components { let observers = cache .component_observers .entry(component) .or_insert_with(|| { if let Some(flag) = Observers::is_archetype_cached(event_key) { archetypes.update_flags(component, flag, true); } CachedComponentObservers::default() }); if descriptor.entities.is_empty() { // Register for all triggers targeting the component observers .global_observers .insert(observer_entity, observer_state.runner); } else { // Register for each watched entity for &watched_entity in &descriptor.entities { let map = observers .entity_component_observers .entry(watched_entity) .or_default(); map.insert(observer_entity, observer_state.runner); } } } } } } /// Remove the observer from the cache, called when an observer gets despawned pub(crate) fn unregister_observer(&mut self, entity: Entity, descriptor: ObserverDescriptor) { let archetypes = &mut self.archetypes; let observers = &mut self.observers; for &event_key in &descriptor.events { let cache = observers.get_observers_mut(event_key); if descriptor.components.is_empty() && descriptor.entities.is_empty() { cache.global_observers.remove(&entity); } else if descriptor.components.is_empty() { for watched_entity in &descriptor.entities { // This check should be unnecessary since this observer hasn't been unregistered yet let Some(observers) = cache.entity_observers.get_mut(watched_entity) else { continue; }; observers.remove(&entity); if observers.is_empty() { cache.entity_observers.remove(watched_entity); } } } else { for component in &descriptor.components { let Some(observers) = cache.component_observers.get_mut(component) else { continue; }; if descriptor.entities.is_empty() { observers.global_observers.remove(&entity); } else { for watched_entity in &descriptor.entities { let Some(map) = observers.entity_component_observers.get_mut(watched_entity) else { continue; }; map.remove(&entity); if map.is_empty() { observers.entity_component_observers.remove(watched_entity); } } } if observers.global_observers.is_empty() && observers.entity_component_observers.is_empty() { cache.component_observers.remove(component); if let Some(flag) = Observers::is_archetype_cached(event_key) { if let Some(by_component) = archetypes.by_component.get(component) { for archetype in by_component.keys() { let archetype = &mut archetypes.archetypes[archetype.index()]; if archetype.contains(*component) { let no_longer_observed = archetype .components() .all(|id| !cache.component_observers.contains_key(&id)); if no_longer_observed { archetype.flags.set(flag, false); } } } } } } } } } } } #[cfg(test)] mod tests { use alloc::{vec, vec::Vec}; use bevy_platform::collections::HashMap; use bevy_ptr::OwningPtr; use crate::component::ComponentId; use crate::{ change_detection::MaybeLocation, observer::{Observer, Replace}, prelude::*, traversal::Traversal, }; #[derive(Component)] struct A; #[derive(Component)] struct B; #[derive(Component)] struct C; #[derive(Component)] #[component(storage = "SparseSet")] struct S; #[derive(Event, EntityEvent)] struct EventA; #[derive(Event, EntityEvent)] struct EventWithData { counter: usize, } #[derive(Resource, Default)] struct Order(Vec<&'static str>); impl Order { #[track_caller] fn observed(&mut self, name: &'static str) { self.0.push(name); } } #[derive(Component)] struct ChildOf(Entity); impl Traversal for &'_ ChildOf { fn traverse(item: Self::Item<'_, '_>, _: &D) -> Option { Some(item.0) } } #[derive(Component, Event, EntityEvent)] #[entity_event(traversal = &'static ChildOf, auto_propagate)] struct EventPropagating; #[test] fn observer_order_spawn_despawn() { let mut world = World::new(); world.init_resource::(); world.add_observer(|_: On, mut res: ResMut| res.observed("add")); world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); world.add_observer(|_: On, mut res: ResMut| { res.observed("replace"); }); world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); let entity = world.spawn(A).id(); world.despawn(entity); assert_eq!( vec!["add", "insert", "replace", "remove"], world.resource::().0 ); } #[test] fn observer_order_insert_remove() { let mut world = World::new(); world.init_resource::(); world.add_observer(|_: On, mut res: ResMut| res.observed("add")); world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); world.add_observer(|_: On, mut res: ResMut| { res.observed("replace"); }); world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); let mut entity = world.spawn_empty(); entity.insert(A); entity.remove::(); entity.flush(); assert_eq!( vec!["add", "insert", "replace", "remove"], world.resource::().0 ); } #[test] fn observer_order_insert_remove_sparse() { let mut world = World::new(); world.init_resource::(); world.add_observer(|_: On, mut res: ResMut| res.observed("add")); world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); world.add_observer(|_: On, mut res: ResMut| { res.observed("replace"); }); world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); let mut entity = world.spawn_empty(); entity.insert(S); entity.remove::(); entity.flush(); assert_eq!( vec!["add", "insert", "replace", "remove"], world.resource::().0 ); } #[test] fn observer_order_replace() { let mut world = World::new(); world.init_resource::(); let entity = world.spawn(A).id(); world.add_observer(|_: On, mut res: ResMut| res.observed("add")); world.add_observer(|_: On, mut res: ResMut| res.observed("insert")); world.add_observer(|_: On, mut res: ResMut| { res.observed("replace"); }); world.add_observer(|_: On, mut res: ResMut| res.observed("remove")); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); let mut entity = world.entity_mut(entity); entity.insert(A); entity.flush(); assert_eq!(vec!["replace", "insert"], world.resource::().0); } #[test] fn observer_order_recursive() { let mut world = World::new(); world.init_resource::(); world.add_observer( |obs: On, mut res: ResMut, mut commands: Commands| { res.observed("add_a"); commands.entity(obs.target()).insert(B); }, ); world.add_observer( |obs: On, mut res: ResMut, mut commands: Commands| { res.observed("remove_a"); commands.entity(obs.target()).remove::(); }, ); world.add_observer( |obs: On, mut res: ResMut, mut commands: Commands| { res.observed("add_b"); commands.entity(obs.target()).remove::(); }, ); world.add_observer(|_: On, mut res: ResMut| { res.observed("remove_b"); }); let entity = world.spawn(A).flush(); let entity = world.get_entity(entity).unwrap(); assert!(!entity.contains::()); assert!(!entity.contains::()); assert_eq!( vec!["add_a", "add_b", "remove_a", "remove_b"], world.resource::().0 ); } #[test] fn observer_trigger_ref() { let mut world = World::new(); world.add_observer(|mut trigger: On| trigger.event_mut().counter += 1); world.add_observer(|mut trigger: On| trigger.event_mut().counter += 2); world.add_observer(|mut trigger: On| trigger.event_mut().counter += 4); // This flush is required for the last observer to be called when triggering the event, // due to `World::add_observer` returning `WorldEntityMut`. world.flush(); let mut event = EventWithData { counter: 0 }; world.trigger_ref(&mut event); assert_eq!(7, event.counter); } #[test] fn observer_trigger_targets_ref() { let mut world = World::new(); world.add_observer(|mut trigger: On| { trigger.event_mut().counter += 1; }); world.add_observer(|mut trigger: On| { trigger.event_mut().counter += 2; }); world.add_observer(|mut trigger: On| { trigger.event_mut().counter += 4; }); // This flush is required for the last observer to be called when triggering the event, // due to `World::add_observer` returning `WorldEntityMut`. world.flush(); let mut event = EventWithData { counter: 0 }; let component_a = world.register_component::(); world.trigger_targets_ref(&mut event, component_a); assert_eq!(5, event.counter); } #[test] fn observer_multiple_listeners() { let mut world = World::new(); world.init_resource::(); world.add_observer(|_: On, mut res: ResMut| res.observed("add_1")); world.add_observer(|_: On, mut res: ResMut| res.observed("add_2")); world.spawn(A).flush(); assert_eq!(vec!["add_2", "add_1"], world.resource::().0); // Our A entity plus our two observers assert_eq!(world.entity_count(), 3); } #[test] fn observer_multiple_events() { let mut world = World::new(); world.init_resource::(); let on_remove = Remove::register_event_key(&mut world); world.spawn( // SAFETY: Add and Remove are both unit types, so this is safe unsafe { Observer::new(|_: On, mut res: ResMut| { res.observed("add/remove"); }) .with_event(on_remove) }, ); let entity = world.spawn(A).id(); world.despawn(entity); assert_eq!( vec!["add/remove", "add/remove"], world.resource::().0 ); } #[test] fn observer_multiple_components() { let mut world = World::new(); world.init_resource::(); world.register_component::(); world.register_component::(); world.add_observer(|_: On, mut res: ResMut| { res.observed("add_ab"); }); let entity = world.spawn(A).id(); world.entity_mut(entity).insert(B); world.flush(); assert_eq!(vec!["add_ab", "add_ab"], world.resource::().0); } #[test] fn observer_despawn() { let mut world = World::new(); let system: fn(On) = |_| { panic!("Observer triggered after being despawned."); }; let observer = world.add_observer(system).id(); world.despawn(observer); world.spawn(A).flush(); } // Regression test for https://github.com/bevyengine/bevy/issues/14961 #[test] fn observer_despawn_archetype_flags() { let mut world = World::new(); world.init_resource::(); let entity = world.spawn((A, B)).flush(); world.add_observer(|_: On, mut res: ResMut| { res.observed("remove_a"); }); let system: fn(On) = |_: On| { panic!("Observer triggered after being despawned."); }; let observer = world.add_observer(system).flush(); world.despawn(observer); world.despawn(entity); assert_eq!(vec!["remove_a"], world.resource::().0); } #[test] fn observer_multiple_matches() { let mut world = World::new(); world.init_resource::(); world.add_observer(|_: On, mut res: ResMut| { res.observed("add_ab"); }); world.spawn((A, B)).flush(); assert_eq!(vec!["add_ab"], world.resource::().0); } #[test] fn observer_no_target() { let mut world = World::new(); world.init_resource::(); let system: fn(On) = |_| { panic!("Trigger routed to non-targeted entity."); }; world.spawn_empty().observe(system); world.add_observer(move |obs: On, mut res: ResMut| { assert_eq!(obs.target(), Entity::PLACEHOLDER); res.observed("event_a"); }); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger(EventA); world.flush(); assert_eq!(vec!["event_a"], world.resource::().0); } #[test] fn observer_entity_routing() { let mut world = World::new(); world.init_resource::(); let system: fn(On) = |_| { panic!("Trigger routed to non-targeted entity."); }; world.spawn_empty().observe(system); let entity = world .spawn_empty() .observe(|_: On, mut res: ResMut| res.observed("a_1")) .id(); world.add_observer(move |obs: On, mut res: ResMut| { assert_eq!(obs.target(), entity); res.observed("a_2"); }); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventA, entity); world.flush(); assert_eq!(vec!["a_2", "a_1"], world.resource::().0); } #[test] fn observer_multiple_targets() { #[derive(Resource, Default)] struct R(i32); let mut world = World::new(); let component_a = world.register_component::(); let component_b = world.register_component::(); world.init_resource::(); // targets (entity_1, A) let entity_1 = world .spawn_empty() .observe(|_: On, mut res: ResMut| res.0 += 1) .id(); // targets (entity_2, B) let entity_2 = world .spawn_empty() .observe(|_: On, mut res: ResMut| res.0 += 10) .id(); // targets any entity or component world.add_observer(|_: On, mut res: ResMut| res.0 += 100); // targets any entity, and components A or B world.add_observer(|_: On, mut res: ResMut| res.0 += 1000); // test all tuples world.add_observer(|_: On, mut res: ResMut| res.0 += 10000); world.add_observer( |_: On, mut res: ResMut| { res.0 += 100000; }, ); world.add_observer( |_: On, mut res: ResMut| res.0 += 1000000, ); // WorldEntityMut does not automatically flush. world.flush(); // trigger for an entity and a component world.trigger_targets(EventA, (entity_1, component_a)); world.flush(); // only observer that doesn't trigger is the one only watching entity_2 assert_eq!(1111101, world.resource::().0); world.resource_mut::().0 = 0; // trigger for both entities, but no components: trigger once per entity target world.trigger_targets(EventA, (entity_1, entity_2)); world.flush(); // only the observer that doesn't require components triggers - once per entity assert_eq!(200, world.resource::().0); world.resource_mut::().0 = 0; // trigger for both components, but no entities: trigger once world.trigger_targets(EventA, (component_a, component_b)); world.flush(); // all component observers trigger, entities are not observed assert_eq!(1111100, world.resource::().0); world.resource_mut::().0 = 0; // trigger for both entities and both components: trigger once per entity target // we only get 2222211 because a given observer can trigger only once per entity target world.trigger_targets(EventA, ((component_a, component_b), (entity_1, entity_2))); world.flush(); assert_eq!(2222211, world.resource::().0); world.resource_mut::().0 = 0; // trigger to test complex tuples: (A, B, (A, B)) world.trigger_targets( EventA, (component_a, component_b, (component_a, component_b)), ); world.flush(); // the duplicate components in the tuple don't cause multiple triggers assert_eq!(1111100, world.resource::().0); world.resource_mut::().0 = 0; // trigger to test complex tuples: (A, B, (A, B), ((A, B), (A, B))) world.trigger_targets( EventA, ( component_a, component_b, (component_a, component_b), ((component_a, component_b), (component_a, component_b)), ), ); world.flush(); // the duplicate components in the tuple don't cause multiple triggers assert_eq!(1111100, world.resource::().0); world.resource_mut::().0 = 0; // trigger to test the most complex tuple: (A, B, (A, B), (B, A), (A, B, ((A, B), (B, A)))) world.trigger_targets( EventA, ( component_a, component_b, (component_a, component_b), (component_b, component_a), ( component_a, component_b, ((component_a, component_b), (component_b, component_a)), ), ), ); world.flush(); // the duplicate components in the tuple don't cause multiple triggers assert_eq!(1111100, world.resource::().0); world.resource_mut::().0 = 0; } #[test] fn observer_dynamic_component() { let mut world = World::new(); world.init_resource::(); let component_id = world.register_component::(); world.spawn( Observer::new(|_: On, mut res: ResMut| res.observed("event_a")) .with_component(component_id), ); let mut entity = world.spawn_empty(); OwningPtr::make(A, |ptr| { // SAFETY: we registered `component_id` above. unsafe { entity.insert_by_id(component_id, ptr) }; }); let entity = entity.flush(); world.trigger_targets(EventA, entity); world.flush(); assert_eq!(vec!["event_a"], world.resource::().0); } #[test] fn observer_dynamic_trigger() { let mut world = World::new(); world.init_resource::(); let event_a = Remove::register_event_key(&mut world); // SAFETY: we registered `event_a` above and it matches the type of EventA let observe = unsafe { Observer::with_dynamic_runner(|mut world, _trigger, _ptr, _propagate| { world.resource_mut::().observed("event_a"); }) .with_event(event_a) }; world.spawn(observe); world.commands().queue(move |world: &mut World| { // SAFETY: we registered `event_a` above and it matches the type of EventA unsafe { world.trigger_targets_dynamic(event_a, EventA, ()) }; }); world.flush(); assert_eq!(vec!["event_a"], world.resource::().0); } #[test] fn observer_propagating() { let mut world = World::new(); world.init_resource::(); let parent = world.spawn_empty().id(); let child = world.spawn(ChildOf(parent)).id(); world.entity_mut(parent).observe( move |trigger: On, mut res: ResMut| { res.observed("parent"); assert_eq!(trigger.target(), parent); assert_eq!(trigger.original_target(), child); }, ); world.entity_mut(child).observe( move |trigger: On, mut res: ResMut| { res.observed("child"); assert_eq!(trigger.target(), child); assert_eq!(trigger.original_target(), child); }, ); // TODO: ideally this flush is not necessary, but right now observe() returns EntityWorldMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, child); world.flush(); assert_eq!(vec!["child", "parent"], world.resource::().0); } #[test] fn observer_propagating_redundant_dispatch_same_entity() { let mut world = World::new(); world.init_resource::(); let parent = world .spawn_empty() .observe(|_: On, mut res: ResMut| { res.observed("parent"); }) .id(); let child = world .spawn(ChildOf(parent)) .observe(|_: On, mut res: ResMut| { res.observed("child"); }) .id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, [child, child]); world.flush(); assert_eq!( vec!["child", "parent", "child", "parent"], world.resource::().0 ); } #[test] fn observer_propagating_redundant_dispatch_parent_child() { let mut world = World::new(); world.init_resource::(); let parent = world .spawn_empty() .observe(|_: On, mut res: ResMut| { res.observed("parent"); }) .id(); let child = world .spawn(ChildOf(parent)) .observe(|_: On, mut res: ResMut| { res.observed("child"); }) .id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, [child, parent]); world.flush(); assert_eq!( vec!["child", "parent", "parent"], world.resource::().0 ); } #[test] fn observer_propagating_halt() { let mut world = World::new(); world.init_resource::(); let parent = world .spawn_empty() .observe(|_: On, mut res: ResMut| { res.observed("parent"); }) .id(); let child = world .spawn(ChildOf(parent)) .observe( |mut trigger: On, mut res: ResMut| { res.observed("child"); trigger.propagate(false); }, ) .id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, child); world.flush(); assert_eq!(vec!["child"], world.resource::().0); } #[test] fn observer_propagating_join() { let mut world = World::new(); world.init_resource::(); let parent = world .spawn_empty() .observe(|_: On, mut res: ResMut| { res.observed("parent"); }) .id(); let child_a = world .spawn(ChildOf(parent)) .observe(|_: On, mut res: ResMut| { res.observed("child_a"); }) .id(); let child_b = world .spawn(ChildOf(parent)) .observe(|_: On, mut res: ResMut| { res.observed("child_b"); }) .id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, [child_a, child_b]); world.flush(); assert_eq!( vec!["child_a", "parent", "child_b", "parent"], world.resource::().0 ); } #[test] fn observer_propagating_no_next() { let mut world = World::new(); world.init_resource::(); let entity = world .spawn_empty() .observe(|_: On, mut res: ResMut| { res.observed("event"); }) .id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, entity); world.flush(); assert_eq!(vec!["event"], world.resource::().0); } #[test] fn observer_propagating_parallel_propagation() { let mut world = World::new(); world.init_resource::(); let parent_a = world .spawn_empty() .observe(|_: On, mut res: ResMut| { res.observed("parent_a"); }) .id(); let child_a = world .spawn(ChildOf(parent_a)) .observe( |mut trigger: On, mut res: ResMut| { res.observed("child_a"); trigger.propagate(false); }, ) .id(); let parent_b = world .spawn_empty() .observe(|_: On, mut res: ResMut| { res.observed("parent_b"); }) .id(); let child_b = world .spawn(ChildOf(parent_b)) .observe(|_: On, mut res: ResMut| { res.observed("child_b"); }) .id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, [child_a, child_b]); world.flush(); assert_eq!( vec!["child_a", "child_b", "parent_b"], world.resource::().0 ); } #[test] fn observer_propagating_world() { let mut world = World::new(); world.init_resource::(); world.add_observer(|_: On, mut res: ResMut| { res.observed("event"); }); let grandparent = world.spawn_empty().id(); let parent = world.spawn(ChildOf(grandparent)).id(); let child = world.spawn(ChildOf(parent)).id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, child); world.flush(); assert_eq!(vec!["event", "event", "event"], world.resource::().0); } #[test] fn observer_propagating_world_skipping() { let mut world = World::new(); world.init_resource::(); world.add_observer( |trigger: On, query: Query<&A>, mut res: ResMut| { if query.get(trigger.target()).is_ok() { res.observed("event"); } }, ); let grandparent = world.spawn(A).id(); let parent = world.spawn(ChildOf(grandparent)).id(); let child = world.spawn((A, ChildOf(parent))).id(); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger_targets(EventPropagating, child); world.flush(); assert_eq!(vec!["event", "event"], world.resource::().0); } // Originally for https://github.com/bevyengine/bevy/issues/18452 #[test] fn observer_modifies_relationship() { fn on_add(trigger: On, mut commands: Commands) { commands .entity(trigger.target()) .with_related_entities::(|rsc| { rsc.spawn_empty(); }); } let mut world = World::new(); world.add_observer(on_add); world.spawn(A); world.flush(); } // Regression test for https://github.com/bevyengine/bevy/issues/14467 // Fails prior to https://github.com/bevyengine/bevy/pull/15398 #[test] fn observer_on_remove_during_despawn_spawn_empty() { let mut world = World::new(); // Observe the removal of A - this will run during despawn world.add_observer(|_: On, mut cmd: Commands| { // Spawn a new entity - this reserves a new ID and requires a flush // afterward before Entities::free can be called. cmd.spawn_empty(); }); let ent = world.spawn(A).id(); // Despawn our entity, which runs the Remove observer and allocates a // new Entity. // Should not panic - if it does, then Entities was not flushed properly // after the observer's spawn_empty. world.despawn(ent); } #[test] #[should_panic] fn observer_invalid_params() { #[derive(Resource)] struct ResA; #[derive(Resource)] struct ResB; let mut world = World::new(); // This fails because `ResA` is not present in the world world.add_observer(|_: On, _: Res, mut commands: Commands| { commands.insert_resource(ResB); }); world.trigger(EventA); } #[test] fn observer_apply_deferred_from_param_set() { #[derive(Resource)] struct ResA; let mut world = World::new(); world.add_observer( |_: On, mut params: ParamSet<(Query, Commands)>| { params.p1().insert_resource(ResA); }, ); // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // and therefore does not automatically flush. world.flush(); world.trigger(EventA); world.flush(); assert!(world.get_resource::().is_some()); } #[test] #[track_caller] fn observer_caller_location_event() { #[derive(Event)] struct EventA; let caller = MaybeLocation::caller(); let mut world = World::new(); world.add_observer(move |trigger: On| { assert_eq!(trigger.caller(), caller); }); world.trigger(EventA); } #[test] #[track_caller] fn observer_caller_location_command_archetype_move() { #[derive(Component)] struct Component; let caller = MaybeLocation::caller(); let mut world = World::new(); world.add_observer(move |trigger: On| { assert_eq!(trigger.caller(), caller); }); world.add_observer(move |trigger: On| { assert_eq!(trigger.caller(), caller); }); world.commands().spawn(Component).clear(); world.flush(); } #[test] fn observer_triggered_components() { #[derive(Resource, Default)] struct Counter(HashMap); let mut world = World::new(); world.init_resource::(); let a_id = world.register_component::(); let b_id = world.register_component::(); world.add_observer( |trigger: On, mut counter: ResMut| { for &component in trigger.components() { *counter.0.entry(component).or_default() += 1; } }, ); world.flush(); world.trigger_targets(EventA, [a_id, b_id]); world.trigger_targets(EventA, a_id); world.trigger_targets(EventA, b_id); world.trigger_targets(EventA, [a_id, b_id]); world.trigger_targets(EventA, a_id); world.flush(); let counter = world.resource::(); assert_eq!(4, *counter.0.get(&a_id).unwrap()); assert_eq!(3, *counter.0.get(&b_id).unwrap()); } }