use core::any::Any;
use core::marker::PhantomData;
use crate::{
component::{ComponentCloneBehavior, ComponentId, Mutable, StorageType},
error::{ErrorContext, ErrorHandler},
event::EventKey,
lifecycle::{ComponentHook, HookContext},
observer::{
condition::{ObserverCondition, ObserverWithCondition, ObserverWithConditionMarker},
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;
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,
pub(crate) conditions: Vec<ObserverCondition>,
}
impl Observer {
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,
conditions: Vec::new(),
}
}
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.fallback_error_handler();
world.commands().queue(move |world: &mut World| {
let entity = hook_context.entity;
let mut conditions = {
let Some(mut observe) = world.get_mut::<Observer>(entity) else {
return;
};
if observe.descriptor.event_keys.is_empty() {
return;
}
if observe.error_handler.is_none() {
observe.error_handler = Some(default_error_handler);
}
core::mem::take(&mut observe.conditions)
};
for condition in &mut conditions {
condition.initialize(world);
}
if let Some(mut observe) = world.get_mut::<Observer>(entity) {
observe.conditions = conditions;
}
world.register_observer(entity);
});
},
error_handler: None,
runner,
despawned_watched_entities: 0,
last_trigger_id: 0,
conditions: Vec::new(),
}
}
pub fn with_entity(mut self, entity: Entity) -> Self {
self.watch_entity(entity);
self
}
pub fn with_entities<I: IntoIterator<Item = Entity>>(mut self, entities: I) -> Self {
self.watch_entities(entities);
self
}
pub fn watch_entity(&mut self, entity: Entity) {
self.descriptor.entities.push(entity);
}
pub fn watch_entities<I: IntoIterator<Item = Entity>>(&mut self, entities: I) {
self.descriptor.entities.extend(entities);
}
pub fn with_component(mut self, component: ComponentId) -> Self {
self.descriptor.components.push(component);
self
}
pub fn with_components<I: IntoIterator<Item = ComponentId>>(mut self, components: I) -> Self {
self.descriptor.components.extend(components);
self
}
pub unsafe fn with_event_key(mut self, event_key: EventKey) -> Self {
self.descriptor.event_keys.push(event_key);
self
}
pub fn with_error_handler(mut self, error_handler: fn(BevyError, ErrorContext)) -> Self {
self.error_handler = Some(error_handler);
self
}
pub fn run_if<M>(mut self, condition: impl SystemCondition<M>) -> Self {
self.conditions.push(ObserverCondition::new(condition));
self
}
pub fn descriptor(&self) -> &ObserverDescriptor {
&self.descriptor
}
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);
});
})
}
}
#[derive(Default, Clone)]
pub struct ObserverDescriptor {
pub(super) event_keys: Vec<EventKey>,
pub(super) components: Vec<ComponentId>,
pub(super) entities: Vec<Entity>,
}
impl ObserverDescriptor {
pub unsafe fn with_event_keys(mut self, event_keys: Vec<EventKey>) -> Self {
self.event_keys = event_keys;
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 fn event_keys(&self) -> &[EventKey] {
&self.event_keys
}
pub fn components(&self) -> &[ComponentId] {
&self.components
}
pub fn entities(&self) -> &[Entity] {
&self.entities
}
}
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_key = world.register_event_key::<E>();
let components = B::component_ids(&mut world.components_registrator());
let system_ptr: *mut dyn ObserverSystem<E, B> = {
let Some(mut observer) = world.get_mut::<Observer>(entity) else {
return;
};
observer.descriptor.event_keys.push(event_key);
observer.descriptor.components.extend(components);
let system: &mut dyn Any = observer.system.as_mut();
core::ptr::from_mut(system.downcast_mut::<S>().unwrap())
};
unsafe {
(*system_ptr).initialize(world);
}
let mut conditions = {
let Some(mut observer) = world.get_mut::<Observer>(entity) else {
return;
};
core::mem::take(&mut observer.conditions)
};
for condition in &mut conditions {
condition.initialize(world);
}
if let Some(mut observer) = world.get_mut::<Observer>(entity) {
observer.conditions = conditions;
}
world.register_observer(entity);
});
}
#[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 {
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,
)
};
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()
}
}
pub trait IntoObserver<Marker>: Send + 'static {
fn into_observer(self) -> Observer;
}
impl IntoObserver<()> for Observer {
fn into_observer(self) -> Observer {
self
}
}
impl<E: Event, B: Bundle, M, T: IntoObserverSystem<E, B, M>> IntoObserver<(E, B, M)> for T {
fn into_observer(self) -> Observer {
Observer::new(self)
}
}
impl<E: Event, B: Bundle, M: 'static, S: IntoObserverSystem<E, B, M>>
IntoObserver<ObserverWithConditionMarker> for ObserverWithCondition<E, B, M, S>
{
fn into_observer(self) -> Observer {
let (system, conditions) = self.take_conditions();
let mut observer = Observer::new(system);
observer.conditions = conditions;
observer
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be used as an entity observer",
note = "entity observers require the event type to implement `EntityEvent`"
)]
pub trait IntoEntityObserver<Marker>: Send + 'static {
fn into_observer_for_entity(self, entity: Entity) -> Observer;
}
impl<E: EntityEvent, B: Bundle, M, T: IntoObserverSystem<E, B, M>> IntoEntityObserver<(E, B, M)>
for T
{
fn into_observer_for_entity(self, entity: Entity) -> Observer {
Observer::new(self).with_entity(entity)
}
}
impl<E: EntityEvent, B: Bundle, M: 'static, S: IntoObserverSystem<E, B, M>>
IntoEntityObserver<ObserverWithConditionMarker> for ObserverWithCondition<E, B, M, S>
{
fn into_observer_for_entity(self, entity: Entity) -> Observer {
let (system, conditions) = self.take_conditions();
let mut observer = Observer::new(system);
observer.conditions = conditions;
observer.with_entity(entity)
}
}
pub trait ObserverSystemExt<E: Event, B: Bundle, M>: IntoObserverSystem<E, B, M> + Sized {
fn run_if<C, CM>(self, condition: C) -> ObserverWithCondition<E, B, M, Self>
where
C: SystemCondition<CM>,
{
ObserverWithCondition {
system: self,
conditions: alloc::vec![Box::new(IntoSystem::into_system(condition))],
_marker: PhantomData,
}
}
}
impl<E: Event, B: Bundle, M, T: IntoObserverSystem<E, B, M>> ObserverSystemExt<E, B, M> for T {}