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:
parent
7645ce91ed
commit
61a5a37584
245
crates/bevy_ecs/src/observer/centralized_storage.rs
Normal file
245
crates/bevy_ecs/src/observer/centralized_storage.rs
Normal 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
|
||||
}
|
||||
}
|
492
crates/bevy_ecs/src/observer/distributed_storage.rs
Normal file
492
crates/bevy_ecs/src/observer/distributed_storage.rs
Normal 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()
|
||||
}
|
||||
}
|
@ -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 {
|
@ -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.
|
||||
|
@ -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::*;
|
||||
|
206
crates/bevy_ecs/src/observer/system_param.rs
Normal file
206
crates/bevy_ecs/src/observer/system_param.rs
Normal 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
|
||||
}
|
||||
}
|
117
crates/bevy_ecs/src/observer/trigger_targets.rs
Normal file
117
crates/bevy_ecs/src/observer/trigger_targets.rs
Normal 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
|
||||
);
|
Loading…
Reference in New Issue
Block a user