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::{
|
use crate::{
|
||||||
component::{Component, ComponentCloneBehavior, Mutable, StorageType},
|
component::ComponentCloneBehavior,
|
||||||
entity::{ComponentCloneCtx, Entity, EntityClonerBuilder, EntityMapper, SourceComponent},
|
entity::{ComponentCloneCtx, EntityClonerBuilder, EntityMapper, SourceComponent},
|
||||||
lifecycle::{ComponentHook, HookContext},
|
observer::ObservedBy,
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
#[cfg(feature = "bevy_reflect")]
|
|
||||||
use crate::prelude::ReflectComponent;
|
|
||||||
|
|
||||||
use super::Observer;
|
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<'_> {
|
impl EntityClonerBuilder<'_> {
|
||||||
/// Sets the option to automatically add cloned entities to the observers targeting source entity.
|
/// 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 {
|
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,
|
//! but allows for a more ad hoc approach with observers,
|
||||||
//! and enables indefinite chaining of observers triggering other observers (for both better and worse!).
|
//! 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 runner;
|
||||||
|
mod system_param;
|
||||||
|
mod trigger_targets;
|
||||||
|
|
||||||
pub use entity_observer::ObservedBy;
|
pub use centralized_storage::*;
|
||||||
|
pub use distributed_storage::*;
|
||||||
pub use runner::*;
|
pub use runner::*;
|
||||||
use variadics_please::all_tuples;
|
pub use system_param::*;
|
||||||
|
pub use trigger_targets::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
archetype::ArchetypeFlags,
|
|
||||||
change_detection::MaybeLocation,
|
change_detection::MaybeLocation,
|
||||||
component::ComponentId,
|
component::ComponentId,
|
||||||
entity::EntityHashMap,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
system::IntoObserverSystem,
|
system::IntoObserverSystem,
|
||||||
world::{DeferredWorld, *},
|
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 {
|
impl World {
|
||||||
/// Spawns a "global" [`Observer`] which will watch for the given event.
|
/// Spawns a "global" [`Observer`] which will watch for the given event.
|
||||||
|
@ -1,16 +1,10 @@
|
|||||||
use alloc::{boxed::Box, vec};
|
//! Logic for evaluating observers, and storing functions inside of observers.
|
||||||
use bevy_utils::prelude::DebugName;
|
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
component::{ComponentId, Mutable, StorageType},
|
error::ErrorContext, observer::ObserverTrigger, prelude::*, query::DebugCheckedUnwrap,
|
||||||
error::{ErrorContext, ErrorHandler},
|
system::ObserverSystem, world::DeferredWorld,
|
||||||
lifecycle::{ComponentHook, HookContext},
|
|
||||||
observer::{ObserverDescriptor, ObserverTrigger},
|
|
||||||
prelude::*,
|
|
||||||
query::DebugCheckedUnwrap,
|
|
||||||
system::{IntoObserverSystem, ObserverSystem},
|
|
||||||
world::DeferredWorld,
|
|
||||||
};
|
};
|
||||||
use bevy_ptr::PtrMut;
|
use bevy_ptr::PtrMut;
|
||||||
|
|
||||||
@ -20,323 +14,7 @@ use bevy_ptr::PtrMut;
|
|||||||
/// but can be overridden for custom behavior.
|
/// but can be overridden for custom behavior.
|
||||||
pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: &mut bool);
|
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".
|
pub(super) fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
|
||||||
///
|
|
||||||
/// 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>>(
|
|
||||||
mut world: DeferredWorld,
|
mut world: DeferredWorld,
|
||||||
observer_trigger: ObserverTrigger,
|
observer_trigger: ObserverTrigger,
|
||||||
ptr: PtrMut,
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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