mod entity_observer;
mod runner;
mod trigger_event;
pub use runner::*;
pub use trigger_event::*;
use crate::observer::entity_observer::ObservedBy;
use crate::{archetype::ArchetypeFlags, system::IntoObserverSystem, world::*};
use crate::{component::ComponentId, prelude::*, world::DeferredWorld};
use bevy_ptr::Ptr;
use bevy_utils::{EntityHashMap, HashMap};
use std::marker::PhantomData;
pub struct Trigger<'w, E, B: Bundle = ()> {
event: &'w mut E,
trigger: ObserverTrigger,
_marker: PhantomData<B>,
}
impl<'w, E, B: Bundle> Trigger<'w, E, B> {
pub fn new(event: &'w mut E, trigger: ObserverTrigger) -> Self {
Self {
event,
trigger,
_marker: PhantomData,
}
}
pub fn event_type(&self) -> ComponentId {
self.trigger.event_type
}
pub fn event(&self) -> &E {
self.event
}
pub fn event_mut(&mut self) -> &mut E {
self.event
}
pub fn event_ptr(&self) -> Ptr {
Ptr::from(&self.event)
}
pub fn entity(&self) -> Entity {
self.trigger.entity
}
}
#[derive(Default, Clone)]
pub struct ObserverDescriptor {
events: Vec<ComponentId>,
components: Vec<ComponentId>,
entities: Vec<Entity>,
}
impl ObserverDescriptor {
pub unsafe fn with_events(mut self, events: Vec<ComponentId>) -> Self {
self.events = events;
self
}
pub fn with_components(mut self, components: Vec<ComponentId>) -> Self {
self.components = components;
self
}
pub fn with_entities(mut self, entities: Vec<Entity>) -> Self {
self.entities = entities;
self
}
pub(crate) fn merge(&mut self, descriptor: &ObserverDescriptor) {
self.events.extend(descriptor.events.iter().copied());
self.components
.extend(descriptor.components.iter().copied());
self.entities.extend(descriptor.entities.iter().copied());
}
}
#[derive(Debug)]
pub struct ObserverTrigger {
pub observer: Entity,
pub event_type: ComponentId,
pub entity: Entity,
}
type ObserverMap = EntityHashMap<Entity, ObserverRunner>;
#[derive(Default, Debug)]
pub struct CachedComponentObservers {
map: ObserverMap,
entity_map: EntityHashMap<Entity, ObserverMap>,
}
#[derive(Default, Debug)]
pub struct CachedObservers {
map: ObserverMap,
component_observers: HashMap<ComponentId, CachedComponentObservers>,
entity_observers: EntityHashMap<Entity, ObserverMap>,
}
#[derive(Default, Debug)]
pub struct Observers {
on_add: CachedObservers,
on_insert: CachedObservers,
on_remove: CachedObservers,
cache: HashMap<ComponentId, CachedObservers>,
}
impl Observers {
pub(crate) fn get_observers(&mut self, event_type: ComponentId) -> &mut CachedObservers {
match event_type {
ON_ADD => &mut self.on_add,
ON_INSERT => &mut self.on_insert,
ON_REMOVE => &mut self.on_remove,
_ => self.cache.entry(event_type).or_default(),
}
}
pub(crate) fn try_get_observers(&self, event_type: ComponentId) -> Option<&CachedObservers> {
match event_type {
ON_ADD => Some(&self.on_add),
ON_INSERT => Some(&self.on_insert),
ON_REMOVE => Some(&self.on_remove),
_ => self.cache.get(&event_type),
}
}
pub(crate) fn invoke<T>(
mut world: DeferredWorld,
event_type: ComponentId,
entity: Entity,
components: impl Iterator<Item = ComponentId>,
data: &mut T,
) {
let (mut world, observers) = unsafe {
let world = world.as_unsafe_world_cell();
world.increment_trigger_id();
let observers = world.observers();
let Some(observers) = observers.try_get_observers(event_type) else {
return;
};
(world.into_deferred(), observers)
};
let mut trigger_observer = |(&observer, runner): (&Entity, &ObserverRunner)| {
(runner)(
world.reborrow(),
ObserverTrigger {
observer,
event_type,
entity,
},
data.into(),
);
};
observers.map.iter().for_each(&mut trigger_observer);
if entity != Entity::PLACEHOLDER {
if let Some(map) = observers.entity_observers.get(&entity) {
map.iter().for_each(&mut trigger_observer);
}
}
components.for_each(|id| {
if let Some(component_observers) = observers.component_observers.get(&id) {
component_observers
.map
.iter()
.for_each(&mut trigger_observer);
if entity != Entity::PLACEHOLDER {
if let Some(map) = component_observers.entity_map.get(&entity) {
map.iter().for_each(&mut trigger_observer);
}
}
}
});
}
pub(crate) fn is_archetype_cached(event_type: ComponentId) -> Option<ArchetypeFlags> {
match event_type {
ON_ADD => Some(ArchetypeFlags::ON_ADD_OBSERVER),
ON_INSERT => Some(ArchetypeFlags::ON_INSERT_OBSERVER),
ON_REMOVE => Some(ArchetypeFlags::ON_REMOVE_OBSERVER),
_ => None,
}
}
pub(crate) fn update_archetype_flags(
&self,
component_id: ComponentId,
flags: &mut ArchetypeFlags,
) {
if self.on_add.component_observers.contains_key(&component_id) {
flags.insert(ArchetypeFlags::ON_ADD_OBSERVER);
}
if self
.on_insert
.component_observers
.contains_key(&component_id)
{
flags.insert(ArchetypeFlags::ON_INSERT_OBSERVER);
}
if self
.on_remove
.component_observers
.contains_key(&component_id)
{
flags.insert(ArchetypeFlags::ON_REMOVE_OBSERVER);
}
}
}
impl World {
pub fn observe<E: Event, B: Bundle, M>(
&mut self,
system: impl IntoObserverSystem<E, B, M>,
) -> EntityWorldMut {
self.spawn(Observer::new(system))
}
pub fn trigger(&mut self, event: impl Event) {
TriggerEvent { event, targets: () }.apply(self);
}
pub fn trigger_targets(&mut self, event: impl Event, targets: impl TriggerTargets) {
TriggerEvent { event, targets }.apply(self);
}
pub(crate) fn register_observer(&mut self, observer_entity: Entity) {
let (observer_state, archetypes, observers) = unsafe {
let observer_state: *const ObserverState =
self.get::<ObserverState>(observer_entity).unwrap();
for watched_entity in &(*observer_state).descriptor.entities {
let mut entity_mut = self.entity_mut(*watched_entity);
let mut observed_by = entity_mut.entry::<ObservedBy>().or_default();
observed_by.0.push(observer_entity);
}
(&*observer_state, &mut self.archetypes, &mut self.observers)
};
let descriptor = &observer_state.descriptor;
for &event_type in &descriptor.events {
let cache = observers.get_observers(event_type);
if descriptor.components.is_empty() && descriptor.entities.is_empty() {
cache.map.insert(observer_entity, observer_state.runner);
} else if descriptor.components.is_empty() {
for &watched_entity in &observer_state.descriptor.entities {
let map = cache.entity_observers.entry(watched_entity).or_default();
map.insert(observer_entity, observer_state.runner);
}
} else {
for &component in &descriptor.components {
let observers =
cache
.component_observers
.entry(component)
.or_insert_with(|| {
if let Some(flag) = Observers::is_archetype_cached(event_type) {
archetypes.update_flags(component, flag, true);
}
CachedComponentObservers::default()
});
if descriptor.entities.is_empty() {
observers.map.insert(observer_entity, observer_state.runner);
} else {
for &watched_entity in &descriptor.entities {
let map = observers.entity_map.entry(watched_entity).or_default();
map.insert(observer_entity, observer_state.runner);
}
}
}
}
}
}
pub(crate) fn unregister_observer(&mut self, entity: Entity, descriptor: ObserverDescriptor) {
let archetypes = &mut self.archetypes;
let observers = &mut self.observers;
for &event_type in &descriptor.events {
let cache = observers.get_observers(event_type);
if descriptor.components.is_empty() && descriptor.entities.is_empty() {
cache.map.remove(&entity);
} else if descriptor.components.is_empty() {
for watched_entity in &descriptor.entities {
let Some(observers) = cache.entity_observers.get_mut(watched_entity) else {
continue;
};
observers.remove(&entity);
if observers.is_empty() {
cache.entity_observers.remove(watched_entity);
}
}
} else {
for component in &descriptor.components {
let Some(observers) = cache.component_observers.get_mut(component) else {
continue;
};
if descriptor.entities.is_empty() {
observers.map.remove(&entity);
} else {
for watched_entity in &descriptor.entities {
let Some(map) = observers.entity_map.get_mut(watched_entity) else {
continue;
};
map.remove(&entity);
if map.is_empty() {
observers.entity_map.remove(watched_entity);
}
}
}
if observers.map.is_empty() && observers.entity_map.is_empty() {
cache.component_observers.remove(component);
if let Some(flag) = Observers::is_archetype_cached(event_type) {
archetypes.update_flags(*component, flag, false);
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use bevy_ptr::OwningPtr;
use crate as bevy_ecs;
use crate::observer::{EmitDynamicTrigger, Observer, ObserverDescriptor, ObserverState};
use crate::prelude::*;
#[derive(Component)]
struct A;
#[derive(Component)]
struct B;
#[derive(Component)]
struct C;
#[derive(Component)]
#[component(storage = "SparseSet")]
struct S;
#[derive(Event)]
struct EventA;
#[derive(Resource, Default)]
struct R(usize);
impl R {
#[track_caller]
fn assert_order(&mut self, count: usize) {
assert_eq!(count, self.0);
self.0 += 1;
}
}
#[test]
fn observer_order_spawn_despawn() {
let mut world = World::new();
world.init_resource::<R>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.assert_order(0));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<R>| res.assert_order(1));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<R>| res.assert_order(2));
let entity = world.spawn(A).id();
world.despawn(entity);
assert_eq!(3, world.resource::<R>().0);
}
#[test]
fn observer_order_insert_remove() {
let mut world = World::new();
world.init_resource::<R>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.assert_order(0));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<R>| res.assert_order(1));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<R>| res.assert_order(2));
let mut entity = world.spawn_empty();
entity.insert(A);
entity.remove::<A>();
entity.flush();
assert_eq!(3, world.resource::<R>().0);
}
#[test]
fn observer_order_insert_remove_sparse() {
let mut world = World::new();
world.init_resource::<R>();
world.observe(|_: Trigger<OnAdd, S>, mut res: ResMut<R>| res.assert_order(0));
world.observe(|_: Trigger<OnInsert, S>, mut res: ResMut<R>| res.assert_order(1));
world.observe(|_: Trigger<OnRemove, S>, mut res: ResMut<R>| res.assert_order(2));
let mut entity = world.spawn_empty();
entity.insert(S);
entity.remove::<S>();
entity.flush();
assert_eq!(3, world.resource::<R>().0);
}
#[test]
fn observer_order_recursive() {
let mut world = World::new();
world.init_resource::<R>();
world.observe(
|obs: Trigger<OnAdd, A>, mut res: ResMut<R>, mut commands: Commands| {
res.assert_order(0);
commands.entity(obs.entity()).insert(B);
},
);
world.observe(
|obs: Trigger<OnRemove, A>, mut res: ResMut<R>, mut commands: Commands| {
res.assert_order(2);
commands.entity(obs.entity()).remove::<B>();
},
);
world.observe(
|obs: Trigger<OnAdd, B>, mut res: ResMut<R>, mut commands: Commands| {
res.assert_order(1);
commands.entity(obs.entity()).remove::<A>();
},
);
world.observe(|_: Trigger<OnRemove, B>, mut res: ResMut<R>| {
res.assert_order(3);
});
let entity = world.spawn(A).flush();
let entity = world.get_entity(entity).unwrap();
assert!(!entity.contains::<A>());
assert!(!entity.contains::<B>());
assert_eq!(4, world.resource::<R>().0);
}
#[test]
fn observer_multiple_listeners() {
let mut world = World::new();
world.init_resource::<R>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.0 += 1);
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.0 += 1);
world.spawn(A).flush();
assert_eq!(2, world.resource::<R>().0);
assert_eq!(world.entities().len(), 3);
}
#[test]
fn observer_multiple_events() {
let mut world = World::new();
world.init_resource::<R>();
let on_remove = world.init_component::<OnRemove>();
world.spawn(
unsafe {
Observer::new(|_: Trigger<OnAdd, A>, mut res: ResMut<R>| res.0 += 1)
.with_event(on_remove)
},
);
let entity = world.spawn(A).id();
world.despawn(entity);
assert_eq!(2, world.resource::<R>().0);
}
#[test]
fn observer_multiple_components() {
let mut world = World::new();
world.init_resource::<R>();
world.init_component::<A>();
world.init_component::<B>();
world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<R>| res.0 += 1);
let entity = world.spawn(A).id();
world.entity_mut(entity).insert(B);
world.flush();
assert_eq!(2, world.resource::<R>().0);
}
#[test]
fn observer_despawn() {
let mut world = World::new();
world.init_resource::<R>();
let observer = world
.observe(|_: Trigger<OnAdd, A>| panic!("Observer triggered after being despawned."))
.id();
world.despawn(observer);
world.spawn(A).flush();
}
#[test]
fn observer_multiple_matches() {
let mut world = World::new();
world.init_resource::<R>();
world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<R>| res.0 += 1);
world.spawn((A, B)).flush();
assert_eq!(1, world.resource::<R>().0);
}
#[test]
fn observer_no_target() {
let mut world = World::new();
world.init_resource::<R>();
world
.spawn_empty()
.observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity."));
world.observe(move |obs: Trigger<EventA>, mut res: ResMut<R>| {
assert_eq!(obs.entity(), Entity::PLACEHOLDER);
res.0 += 1;
});
world.flush();
world.trigger(EventA);
world.flush();
assert_eq!(1, world.resource::<R>().0);
}
#[test]
fn observer_entity_routing() {
let mut world = World::new();
world.init_resource::<R>();
world
.spawn_empty()
.observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity."));
let entity = world
.spawn_empty()
.observe(|_: Trigger<EventA>, mut res: ResMut<R>| res.0 += 1)
.id();
world.observe(move |obs: Trigger<EventA>, mut res: ResMut<R>| {
assert_eq!(obs.entity(), entity);
res.0 += 1;
});
world.flush();
world.trigger_targets(EventA, entity);
world.flush();
assert_eq!(2, world.resource::<R>().0);
}
#[test]
fn observer_dynamic_component() {
let mut world = World::new();
world.init_resource::<R>();
let component_id = world.init_component::<A>();
world.spawn(
Observer::new(|_: Trigger<OnAdd>, mut res: ResMut<R>| res.0 += 1)
.with_component(component_id),
);
let mut entity = world.spawn_empty();
OwningPtr::make(A, |ptr| {
unsafe { entity.insert_by_id(component_id, ptr) };
});
let entity = entity.flush();
world.trigger_targets(EventA, entity);
world.flush();
assert_eq!(1, world.resource::<R>().0);
}
#[test]
fn observer_dynamic_trigger() {
let mut world = World::new();
world.init_resource::<R>();
let event_a = world.init_component::<EventA>();
world.spawn(ObserverState {
descriptor: unsafe { ObserverDescriptor::default().with_events(vec![event_a]) },
runner: |mut world, _trigger, _ptr| {
world.resource_mut::<R>().0 += 1;
},
..Default::default()
});
world.commands().add(
unsafe { EmitDynamicTrigger::new_with_id(event_a, EventA, ()) },
);
world.flush();
assert_eq!(1, world.resource::<R>().0);
}
}