Improve module structure of observers code (#19779)

# Objective

While working on #17607, I found myself confused and frustrated by the
tangled web woven by the various modules inside of our observers code.

Rather than tackle that as part of a big rewrite PR, I've decided to do
the mature (if frustrating) thing where you split out your trivial but
noisy refactoring first.

There are a large number of moving parts, especially in terms of
storage, and these are strewn willy-nilly across the module with no
apparent ordering. To make matters worse, this was almost all just
dumped into a multi-thousand LOC mod.rs at the root.

## Solution

I've reshuffled the modules, attempting to:
- reduce the size of the mod.rs file
- organize structs so that smaller structs are found after the larger
structs that contain them
- group related functionality together
- document why modules exist, and their broad organization

No functional changes have been made here, although I've had to increase
the visibility of a few fields from private to pub(crate) or pub(super)
to keep things compiling.

During these changes, I've opted for the lazy private module, public
re-export strategy, to avoid causing any breakages, both within and
outside of `bevy` itself. I think we can do better, but I want to leave
that for a proper cleanup pass at the end. There's no sense maintaining
migration guides and forcing multiple breaking changes throughout the
cycle.

## Testing

No functional changes; relying on existing test suite and the Rust
compiler.
This commit is contained in:
Alice Cecile 2025-06-22 16:25:25 -07:00 committed by GitHub
parent 7645ce91ed
commit 61a5a37584
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 1079 additions and 1024 deletions

View File

@ -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<ComponentId, CachedObservers>,
}
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<T>(
mut world: DeferredWorld,
event_type: ComponentId,
current_target: Option<Entity>,
original_target: Option<Entity>,
components: impl Iterator<Item = ComponentId> + 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<ArchetypeFlags> {
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<ComponentId, CachedComponentObservers>,
// Observers listening for this trigger fired at a specific entity
pub(super) entity_observers: EntityHashMap<ObserverMap>,
}
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<ComponentId, CachedComponentObservers> {
&self.component_observers
}
/// Returns the observers listening for this trigger targeting entities.
pub fn entity_observers(&self) -> &HashMap<ComponentId, CachedComponentObservers> {
&self.component_observers
}
}
/// Map between an observer entity and its [`ObserverRunner`]
pub type ObserverMap = EntityHashMap<ObserverRunner>;
/// 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<ObserverMap>,
}
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<ObserverMap> {
&self.entity_component_observers
}
}

View File

@ -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<Speak>| {
/// 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<Speak>| {});
/// world.spawn(Observer::new(|trigger: On<Speak>| {}));
/// ```
///
/// 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<PrintNames>, 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<SpawnThing>, 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<A>, 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<Explode>, 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<Explode>, mut commands: Commands| {
/// println!("Boom!");
/// commands.entity(trigger.target()).despawn();
/// });
///
/// world.entity_mut(e2).observe(|trigger: On<Explode>, 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<Explode>| {});
/// 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<ErrorHandler>,
pub(crate) system: Box<dyn AnyNamedSystem>,
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<E: Event, B: Bundle, M, I: IntoObserverSystem<E, B, M>>(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::<E, B, I::System>,
error_handler: None,
runner: observer_system_runner::<E, B, I::System>,
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::<Observer>(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<ComponentHook> {
Some(|world, context| {
let Some(observe) = world.get::<Self>(context.entity) else {
return;
};
let hook = observe.hook_on_add;
hook(world, context);
})
}
fn on_remove() -> Option<ComponentHook> {
Some(|mut world, HookContext { entity, .. }| {
let descriptor = core::mem::take(
&mut world
.entity_mut(entity)
.get_mut::<Self>()
.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<ComponentId>,
/// The components the observer is watching.
pub(super) components: Vec<ComponentId>,
/// The entities the observer is watching.
pub(super) entities: Vec<Entity>,
}
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<ComponentId>) -> Self {
self.events = events;
self
}
/// Add the given `components` to the descriptor.
pub fn with_components(mut self, components: Vec<ComponentId>) -> Self {
self.components = components;
self
}
/// Add the given `entities` to the descriptor.
pub fn with_entities(mut self, entities: Vec<Entity>) -> 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<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
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::<Observer>(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<E, B> = system.downcast_mut::<S>().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<Entity>);
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<ComponentHook> {
Some(|mut world, HookContext { entity, .. }| {
let observed_by = {
let mut component = world.get_mut::<ObservedBy>(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::<Observer>() 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<T: Any + System> AnyNamedSystem for T {
fn system_name(&self) -> DebugName {
self.name()
}
}

View File

@ -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<Entity>);
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<ComponentHook> {
Some(|mut world, HookContext { entity, .. }| {
let observed_by = {
let mut component = world.get_mut::<ObservedBy>(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::<Observer>() 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 {

View File

@ -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<B>,
}
/// 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<MyEvent>, 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<Item = ComponentId> + Clone + '_;
/// The entities the trigger should target.
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_;
}
impl<T: TriggerTargets + ?Sized> TriggerTargets for &T {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
(**self).components()
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
(**self).entities()
}
}
impl TriggerTargets for Entity {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
[].into_iter()
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
core::iter::once(*self)
}
}
impl TriggerTargets for ComponentId {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
core::iter::once(*self)
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
[].into_iter()
}
}
impl<T: TriggerTargets> TriggerTargets for Vec<T> {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.iter().flat_map(T::components)
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
self.iter().flat_map(T::entities)
}
}
impl<const N: usize, T: TriggerTargets> TriggerTargets for [T; N] {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.iter().flat_map(T::components)
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
self.iter().flat_map(T::entities)
}
}
impl<T: TriggerTargets> TriggerTargets for [T] {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.iter().flat_map(T::components)
}
fn entities(&self) -> impl Iterator<Item = Entity> + 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<Item = ComponentId> + Clone + '_ {
let iter = [].into_iter();
let ($($trigger_targets,)*) = self;
$(
let iter = iter.chain($trigger_targets.components());
)*
iter
}
fn entities(&self) -> impl Iterator<Item = Entity> + 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<ComponentId>,
/// The components the observer is watching.
components: Vec<ComponentId>,
/// The entities the observer is watching.
entities: Vec<Entity>,
}
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<ComponentId>) -> Self {
self.events = events;
self
}
/// Add the given `components` to the descriptor.
pub fn with_components(mut self, components: Vec<ComponentId>) -> Self {
self.components = components;
self
}
/// Add the given `entities` to the descriptor.
pub fn with_entities(mut self, entities: Vec<Entity>) -> 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<Entity>,
/// 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<Entity>,
/// 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<ObserverRunner>;
/// 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<ObserverMap>,
}
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<ObserverMap> {
&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<ComponentId, CachedComponentObservers>,
// Observers listening for this trigger fired at a specific entity
entity_observers: EntityHashMap<ObserverMap>,
}
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<ComponentId, CachedComponentObservers> {
&self.component_observers
}
/// Returns the observers listening for this trigger targeting entities.
pub fn entity_observers(&self) -> &HashMap<ComponentId, CachedComponentObservers> {
&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<ComponentId, CachedObservers>,
}
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<T>(
mut world: DeferredWorld,
event_type: ComponentId,
current_target: Option<Entity>,
original_target: Option<Entity>,
components: impl Iterator<Item = ComponentId> + 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<ArchetypeFlags> {
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.

View File

@ -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<Speak>| {
/// 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<Speak>| {});
/// world.spawn(Observer::new(|trigger: On<Speak>| {}));
/// ```
///
/// 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<PrintNames>, 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<SpawnThing>, 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<A>, 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<Explode>, 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<Explode>, mut commands: Commands| {
/// println!("Boom!");
/// commands.entity(trigger.target()).despawn();
/// });
///
/// world.entity_mut(e2).observe(|trigger: On<Explode>, 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<Explode>| {});
/// 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<ErrorHandler>,
system: Box<dyn AnyNamedSystem>,
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<E: Event, B: Bundle, M, I: IntoObserverSystem<E, B, M>>(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::<E, B, I::System>,
error_handler: None,
runner: observer_system_runner::<E, B, I::System>,
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::<Observer>(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<ComponentHook> {
Some(|world, context| {
let Some(observe) = world.get::<Self>(context.entity) else {
return;
};
let hook = observe.hook_on_add;
hook(world, context);
})
}
fn on_remove() -> Option<ComponentHook> {
Some(|mut world, HookContext { entity, .. }| {
let descriptor = core::mem::take(
&mut world
.entity_mut(entity)
.get_mut::<Self>()
.unwrap()
.as_mut()
.descriptor,
);
world.commands().queue(move |world: &mut World| {
world.unregister_observer(entity, descriptor);
});
})
}
}
fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
pub(super) fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
mut world: DeferredWorld,
observer_trigger: ObserverTrigger,
ptr: PtrMut,
@ -420,48 +98,6 @@ fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
}
}
trait AnyNamedSystem: Any + Send + Sync + 'static {
fn system_name(&self) -> DebugName;
}
impl<T: Any + System> 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<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
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::<Observer>(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<E, B> = system.downcast_mut::<S>().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::*;

View File

@ -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<B>,
}
/// 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<AssertEvent>) {
/// 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<Entity>,
/// 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<Entity>,
/// 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
}
}

View File

@ -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<Item = ComponentId> + Clone + '_;
/// The entities the trigger should target.
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_;
}
impl<T: TriggerTargets + ?Sized> TriggerTargets for &T {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
(**self).components()
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
(**self).entities()
}
}
impl TriggerTargets for Entity {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
[].into_iter()
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
core::iter::once(*self)
}
}
impl TriggerTargets for ComponentId {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
core::iter::once(*self)
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
[].into_iter()
}
}
impl<T: TriggerTargets> TriggerTargets for Vec<T> {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.iter().flat_map(T::components)
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
self.iter().flat_map(T::entities)
}
}
impl<const N: usize, T: TriggerTargets> TriggerTargets for [T; N] {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.iter().flat_map(T::components)
}
fn entities(&self) -> impl Iterator<Item = Entity> + Clone + '_ {
self.iter().flat_map(T::entities)
}
}
impl<T: TriggerTargets> TriggerTargets for [T] {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.iter().flat_map(T::components)
}
fn entities(&self) -> impl Iterator<Item = Entity> + 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<Item = ComponentId> + Clone + '_ {
let iter = [].into_iter();
let ($($trigger_targets,)*) = self;
$(
let iter = iter.chain($trigger_targets.components());
)*
iter
}
fn entities(&self) -> impl Iterator<Item = Entity> + 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
);