Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/observer/runner.rs
9371 views
1
//! Logic for evaluating observers, and storing functions inside of observers.
2
3
use core::any::Any;
4
5
use crate::{
6
error::ErrorContext,
7
event::Event,
8
observer::TriggerContext,
9
prelude::*,
10
query::DebugCheckedUnwrap,
11
system::{ObserverSystem, RunSystemError},
12
world::DeferredWorld,
13
};
14
use bevy_ptr::PtrMut;
15
16
/// Type for function that is run when an observer is triggered.
17
///
18
/// Typically refers to the default runner that runs the system stored in the associated [`Observer`] component,
19
/// but can be overridden for custom behavior.
20
///
21
/// See `observer_system_runner` for safety considerations.
22
pub type ObserverRunner =
23
unsafe fn(DeferredWorld, observer: Entity, &TriggerContext, event: PtrMut, trigger: PtrMut);
24
25
/// # Safety
26
///
27
/// - `world` must be the [`DeferredWorld`] that the `entity` is defined in
28
/// - `event_ptr` must match the `E` [`Event`] type.
29
/// - `trigger_ptr` must match the [`Event::Trigger`] type for `E`.
30
/// - `trigger_context`'s [`TriggerContext::event_key`] must match the `E` event type.
31
///
32
// NOTE: The way `Trigger` and `On` interact in this implementation is _subtle_ and _easily invalidated_
33
// from a soundness perspective. Please read and understand the safety comments before making any changes,
34
// either here or in `On`.
35
pub(super) unsafe fn observer_system_runner<E: Event, B: Bundle, S: ObserverSystem<E, B>>(
36
mut world: DeferredWorld,
37
observer: Entity,
38
trigger_context: &TriggerContext,
39
event_ptr: PtrMut,
40
trigger_ptr: PtrMut,
41
) {
42
let world = world.as_unsafe_world_cell();
43
44
// SAFETY: Observer was triggered so must still exist in world
45
let observer_cell = unsafe { world.get_entity(observer).debug_checked_unwrap() };
46
// SAFETY: Observer was triggered so must have an `Observer`
47
let mut state = unsafe { observer_cell.get_mut::<Observer>().debug_checked_unwrap() };
48
49
// TODO: Move this check into the observer cache to avoid dynamic dispatch
50
let last_trigger = world.last_trigger_id();
51
if state.last_trigger_id == last_trigger {
52
return;
53
}
54
state.last_trigger_id = last_trigger;
55
56
// SAFETY:
57
// - Conditions are initialized during observer registration (hook_on_add)
58
// - Conditions are ReadOnlySystem (enforced by SystemCondition trait)
59
// - No aliasing: we hold &mut Observer, but conditions only read world state
60
let mut should_run = true;
61
for condition in state.conditions.iter_mut() {
62
// SAFETY: See the safety comment above.
63
should_run &= unsafe { condition.check(world) };
64
}
65
66
if !should_run {
67
return;
68
}
69
70
// SAFETY: Caller ensures `trigger_ptr` is castable to `&mut E::Trigger<'_>`
71
// The soundness story here is complicated: This casts to &'a mut E::Trigger<'a> which notably
72
// casts the _arbitrary lifetimes_ of the passed in `trigger_ptr` (&'w E::Trigger<'t>, which are
73
// 'w and 't on On<'w, 't>) as the _same_ lifetime 'a, which is _local to this function call_.
74
// This becomes On<'a, 'a> in practice. This is why `On<'w, 't>` has the strict constraint that
75
// the 'w lifetime can never be exposed. To do so would make it possible to introduce use-after-free bugs.
76
// See this thread for more details: <https://github.com/bevyengine/bevy/pull/20731#discussion_r2311907935>
77
let trigger: &mut E::Trigger<'_> = unsafe { trigger_ptr.deref_mut() };
78
79
let on: On<E, B> = On::new(
80
// SAFETY: Caller ensures `ptr` is castable to `&mut E`
81
unsafe { event_ptr.deref_mut() },
82
observer,
83
trigger,
84
trigger_context,
85
);
86
87
// SAFETY:
88
// - observer was triggered so must have an `Observer` component.
89
// - observer cannot be dropped or mutated until after the system pointer is already dropped.
90
let system: *mut dyn ObserverSystem<E, B> = unsafe {
91
let system: &mut dyn Any = state.system.as_mut();
92
let system = system.downcast_mut::<S>().debug_checked_unwrap();
93
&mut *system
94
};
95
96
// SAFETY:
97
// - there are no outstanding references to world except a private component
98
// - system is an `ObserverSystem` so won't mutate world beyond the access of a `DeferredWorld`
99
// and is never exclusive
100
// - system is the same type erased system from above
101
unsafe {
102
#[cfg(feature = "hotpatching")]
103
if world
104
.get_resource_ref::<crate::HotPatchChanges>()
105
.map(|r| {
106
r.last_changed()
107
.is_newer_than((*system).get_last_run(), world.change_tick())
108
})
109
.unwrap_or(true)
110
{
111
(*system).refresh_hotpatch();
112
};
113
114
if let Err(RunSystemError::Failed(err)) = (*system)
115
.validate_param_unsafe(world)
116
.map_err(From::from)
117
.and_then(|()| (*system).run_unsafe(on, world))
118
{
119
let handler = state
120
.error_handler
121
.unwrap_or_else(|| world.default_error_handler());
122
handler(
123
err,
124
ErrorContext::Observer {
125
name: (*system).name(),
126
last_run: (*system).get_last_run(),
127
},
128
);
129
};
130
(*system).queue_deferred(world.into_deferred());
131
}
132
}
133
134
#[cfg(test)]
135
mod tests {
136
use super::*;
137
use crate::{
138
error::{ignore, DefaultErrorHandler},
139
event::Event,
140
observer::On,
141
};
142
143
#[derive(Event)]
144
struct TriggerEvent;
145
146
#[test]
147
#[should_panic(expected = "I failed!")]
148
fn test_fallible_observer() {
149
fn system(_: On<TriggerEvent>) -> Result {
150
Err("I failed!".into())
151
}
152
153
let mut world = World::default();
154
world.add_observer(system);
155
Schedule::default().run(&mut world);
156
world.trigger(TriggerEvent);
157
}
158
159
#[test]
160
fn test_fallible_observer_ignored_errors() {
161
#[derive(Resource, Default)]
162
struct Ran(bool);
163
164
fn system(_: On<TriggerEvent>, mut ran: ResMut<Ran>) -> Result {
165
ran.0 = true;
166
Err("I failed!".into())
167
}
168
169
// Using observer error handler
170
let mut world = World::default();
171
world.init_resource::<Ran>();
172
world.spawn(Observer::new(system).with_error_handler(ignore));
173
world.trigger(TriggerEvent);
174
assert!(world.resource::<Ran>().0);
175
176
// Using world error handler
177
let mut world = World::default();
178
world.init_resource::<Ran>();
179
world.spawn(Observer::new(system));
180
// Test that the correct handler is used when the observer was added
181
// before the default handler
182
world.insert_resource(DefaultErrorHandler(ignore));
183
world.trigger(TriggerEvent);
184
assert!(world.resource::<Ran>().0);
185
}
186
187
#[test]
188
#[should_panic]
189
fn exclusive_system_cannot_be_observer() {
190
fn system(_: On<TriggerEvent>, _world: &mut World) {}
191
let mut world = World::default();
192
world.add_observer(system);
193
}
194
}
195
196