use core::any::Any;
use crate::{
error::ErrorContext,
event::Event,
observer::TriggerContext,
prelude::*,
query::DebugCheckedUnwrap,
system::{ObserverSystem, RunSystemError},
world::DeferredWorld,
};
use bevy_ptr::PtrMut;
pub type ObserverRunner =
unsafe fn(DeferredWorld, observer: Entity, &TriggerContext, event: PtrMut, trigger: PtrMut);
pub(super) unsafe fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
mut world: DeferredWorld,
observer: Entity,
trigger_context: &TriggerContext,
event_ptr: PtrMut,
trigger_ptr: PtrMut,
) {
let world = world.as_unsafe_world_cell();
let observer_cell = unsafe { world.get_entity(observer).debug_checked_unwrap() };
let mut state = unsafe { observer_cell.get_mut::<Observer>().debug_checked_unwrap() };
let last_trigger = world.last_trigger_id();
if state.last_trigger_id == last_trigger {
return;
}
state.last_trigger_id = last_trigger;
let trigger: &mut E::Trigger<'_> = unsafe { trigger_ptr.deref_mut() };
let on: On<E, B> = On::new(
unsafe { event_ptr.deref_mut() },
observer,
trigger,
trigger_context,
);
let system: *mut dyn ObserverSystem<E, B> = unsafe {
let system: &mut dyn Any = state.system.as_mut();
let system = system.downcast_mut::<S>().debug_checked_unwrap();
&mut *system
};
unsafe {
#[cfg(feature = "hotpatching")]
if world
.get_resource_ref::<crate::HotPatchChanges>()
.map(|r| {
r.last_changed()
.is_newer_than((*system).get_last_run(), world.change_tick())
})
.unwrap_or(true)
{
(*system).refresh_hotpatch();
};
if let Err(RunSystemError::Failed(err)) = (*system)
.validate_param_unsafe(world)
.map_err(From::from)
.and_then(|()| (*system).run_unsafe(on, world))
{
let handler = state
.error_handler
.unwrap_or_else(|| world.default_error_handler());
handler(
err,
ErrorContext::Observer {
name: (*system).name(),
last_run: (*system).get_last_run(),
},
);
};
(*system).queue_deferred(world.into_deferred());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
error::{ignore, DefaultErrorHandler},
event::Event,
observer::On,
};
#[derive(Event)]
struct TriggerEvent;
#[test]
#[should_panic(expected = "I failed!")]
fn test_fallible_observer() {
fn system(_: On<TriggerEvent>) -> Result {
Err("I failed!".into())
}
let mut world = World::default();
world.add_observer(system);
Schedule::default().run(&mut world);
world.trigger(TriggerEvent);
}
#[test]
fn test_fallible_observer_ignored_errors() {
#[derive(Resource, Default)]
struct Ran(bool);
fn system(_: On<TriggerEvent>, mut ran: ResMut<Ran>) -> Result {
ran.0 = true;
Err("I failed!".into())
}
let mut world = World::default();
world.init_resource::<Ran>();
world.spawn(Observer::new(system).with_error_handler(ignore));
world.trigger(TriggerEvent);
assert!(world.resource::<Ran>().0);
let mut world = World::default();
world.init_resource::<Ran>();
world.spawn(Observer::new(system));
world.insert_resource(DefaultErrorHandler(ignore));
world.trigger(TriggerEvent);
assert!(world.resource::<Ran>().0);
}
#[test]
#[should_panic]
fn exclusive_system_cannot_be_observer() {
fn system(_: On<TriggerEvent>, _world: &mut World) {}
let mut world = World::default();
world.add_observer(system);
}
}