//! Logic for evaluating observers, and storing functions inside of observers.12use core::any::Any;34use crate::{5error::ErrorContext,6event::Event,7observer::TriggerContext,8prelude::*,9query::DebugCheckedUnwrap,10system::{ObserverSystem, RunSystemError},11world::DeferredWorld,12};13use bevy_ptr::PtrMut;1415/// Type for function that is run when an observer is triggered.16///17/// Typically refers to the default runner that runs the system stored in the associated [`Observer`] component,18/// but can be overridden for custom behavior.19///20/// See `observer_system_runner` for safety considerations.21pub type ObserverRunner =22unsafe fn(DeferredWorld, observer: Entity, &TriggerContext, event: PtrMut, trigger: PtrMut);2324/// # Safety25///26/// - `world` must be the [`DeferredWorld`] that the `entity` is defined in27/// - `event_ptr` must match the `E` [`Event`] type.28/// - `trigger_ptr` must match the [`Event::Trigger`] type for `E`.29/// - `trigger_context`'s [`TriggerContext::event_key`] must match the `E` event type.30///31// NOTE: The way `Trigger` and `On` interact in this implementation is _subtle_ and _easily invalidated_32// from a soundness perspective. Please read and understand the safety comments before making any changes,33// either here or in `On`.34pub(super) unsafe fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(35mut world: DeferredWorld,36observer: Entity,37trigger_context: &TriggerContext,38event_ptr: PtrMut,39trigger_ptr: PtrMut,40) {41let world = world.as_unsafe_world_cell();4243// SAFETY: Observer was triggered so must still exist in world44let observer_cell = unsafe { world.get_entity(observer).debug_checked_unwrap() };45// SAFETY: Observer was triggered so must have an `Observer`46let mut state = unsafe { observer_cell.get_mut::<Observer>().debug_checked_unwrap() };4748// TODO: Move this check into the observer cache to avoid dynamic dispatch49let last_trigger = world.last_trigger_id();50if state.last_trigger_id == last_trigger {51return;52}53state.last_trigger_id = last_trigger;5455// SAFETY:56// - Conditions are initialized during observer registration (hook_on_add)57// - Conditions are ReadOnlySystem (enforced by SystemCondition trait)58// - No aliasing: we hold &mut Observer, but conditions only read world state59let mut should_run = true;60for condition in state.conditions.iter_mut() {61// SAFETY: See the safety comment above.62should_run &= unsafe { condition.check(world) };63}6465if !should_run {66return;67}6869// SAFETY: Caller ensures `trigger_ptr` is castable to `&mut E::Trigger<'_>`70// The soundness story here is complicated: This casts to &'a mut E::Trigger<'a> which notably71// casts the _arbitrary lifetimes_ of the passed in `trigger_ptr` (&'w E::Trigger<'t>, which are72// 'w and 't on On<'w, 't>) as the _same_ lifetime 'a, which is _local to this function call_.73// This becomes On<'a, 'a> in practice. This is why `On<'w, 't>` has the strict constraint that74// the 'w lifetime can never be exposed. To do so would make it possible to introduce use-after-free bugs.75// See this thread for more details: <https://github.com/bevyengine/bevy/pull/20731#discussion_r2311907935>76let trigger: &mut E::Trigger<'_> = unsafe { trigger_ptr.deref_mut() };7778let on: On<E, B> = On::new(79// SAFETY: Caller ensures `ptr` is castable to `&mut E`80unsafe { event_ptr.deref_mut() },81observer,82trigger,83trigger_context,84);8586// SAFETY:87// - observer was triggered so must have an `Observer` component.88// - observer cannot be dropped or mutated until after the system pointer is already dropped.89let system: *mut dyn ObserverSystem<E, B> = unsafe {90let system: &mut dyn Any = state.system.as_mut();91let system = system.downcast_mut::<S>().debug_checked_unwrap();92&mut *system93};9495// SAFETY:96// - there are no outstanding references to world except a private component97// - system is an `ObserverSystem` so won't mutate world beyond the access of a `DeferredWorld`98// and is never exclusive99// - system is the same type erased system from above100unsafe {101#[cfg(feature = "hotpatching")]102if world103.get_resource_ref::<crate::HotPatchChanges>()104.map(|r| {105r.last_changed()106.is_newer_than((*system).get_last_run(), world.change_tick())107})108.unwrap_or(true)109{110(*system).refresh_hotpatch();111};112113if let Err(RunSystemError::Failed(err)) = (*system)114.validate_param_unsafe(world)115.map_err(From::from)116.and_then(|()| (*system).run_unsafe(on, world))117{118let handler = state119.error_handler120.unwrap_or_else(|| world.default_error_handler());121handler(122err,123ErrorContext::Observer {124name: (*system).name(),125last_run: (*system).get_last_run(),126},127);128};129(*system).queue_deferred(world.into_deferred());130}131}132133#[cfg(test)]134mod tests {135use super::*;136use crate::{137error::{ignore, DefaultErrorHandler},138event::Event,139observer::On,140};141142#[derive(Event)]143struct TriggerEvent;144145#[test]146#[should_panic(expected = "I failed!")]147fn test_fallible_observer() {148fn system(_: On<TriggerEvent>) -> Result {149Err("I failed!".into())150}151152let mut world = World::default();153world.add_observer(system);154Schedule::default().run(&mut world);155world.trigger(TriggerEvent);156}157158#[test]159fn test_fallible_observer_ignored_errors() {160#[derive(Resource, Default)]161struct Ran(bool);162163fn system(_: On<TriggerEvent>, mut ran: ResMut<Ran>) -> Result {164ran.0 = true;165Err("I failed!".into())166}167168// Using observer error handler169let mut world = World::default();170world.init_resource::<Ran>();171world.spawn(Observer::new(system).with_error_handler(ignore));172world.trigger(TriggerEvent);173assert!(world.resource::<Ran>().0);174175// Using world error handler176let mut world = World::default();177world.init_resource::<Ran>();178world.spawn(Observer::new(system));179// Test that the correct handler is used when the observer was added180// before the default handler181world.insert_resource(DefaultErrorHandler(ignore));182world.trigger(TriggerEvent);183assert!(world.resource::<Ran>().0);184}185186#[test]187#[should_panic]188fn exclusive_system_cannot_be_observer() {189fn system(_: On<TriggerEvent>, _world: &mut World) {}190let mut world = World::default();191world.add_observer(system);192}193}194195196