bevy_ecs/event/trigger.rs
1use crate::event::SetEntityEventTarget;
2use crate::{
3 archetype::Archetype,
4 component::ComponentId,
5 entity::Entity,
6 event::{EntityEvent, Event},
7 observer::{CachedObservers, TriggerContext},
8 traversal::Traversal,
9 world::DeferredWorld,
10};
11use bevy_ptr::PtrMut;
12use core::{fmt, marker::PhantomData};
13
14/// [`Trigger`] determines _how_ an [`Event`] is triggered when [`World::trigger`](crate::world::World::trigger) is called.
15/// This decides which [`Observer`](crate::observer::Observer)s will run, what data gets passed to them, and the order they will
16/// be executed in.
17///
18/// Implementing [`Trigger`] is "advanced-level" territory, and is generally unnecessary unless you are developing highly specialized
19/// [`Event`] trigger logic.
20///
21/// Bevy comes with a number of built-in [`Trigger`] implementations (see their documentation for more info):
22/// - [`GlobalTrigger`]: The [`Event`] derive defaults to using this
23/// - [`EntityTrigger`]: The [`EntityEvent`] derive defaults to using this
24/// - [`PropagateEntityTrigger`]: The [`EntityEvent`] derive uses this when propagation is enabled.
25/// - [`EntityComponentsTrigger`]: Used by Bevy's [component lifecycle events](crate::lifecycle).
26///
27/// # Safety
28///
29/// Implementing this properly is _advanced_ soundness territory! Implementers must abide by the following:
30///
31/// - The `E`' [`Event::Trigger`] must be constrained to the implemented [`Trigger`] type, as part of the implementation.
32/// This prevents other [`Trigger`] implementations from directly deferring to your implementation, which is a very easy
33/// soundness misstep, as most [`Trigger`] implementations will invoke observers that are developed _for their specific [`Trigger`] type_.
34/// Without this constraint, something like [`GlobalTrigger`] could be called for _any_ [`Event`] type, even one that expects a different
35/// [`Trigger`] type. This would result in an unsound cast of [`GlobalTrigger`] reference.
36/// This is not expressed as an explicit type constraint,, as the `for<'a> Event::Trigger<'a>` lifetime can mismatch explicit lifetimes in
37/// some impls.
38pub unsafe trait Trigger<E: Event> {
39 /// Trigger the given `event`, running every [`Observer`](crate::observer::Observer) that matches the `event`, as defined by this
40 /// [`Trigger`] and the state stored on `self`.
41 ///
42 /// # Safety
43 /// - The [`CachedObservers`] `observers` must come from the [`DeferredWorld`] `world`
44 /// - [`TriggerContext`] must contain an [`EventKey`](crate::event::EventKey) that matches the `E` [`Event`] type
45 /// - `observers` must correspond to observers compatible with the event type `E`
46 /// - Read and abide by the "Safety" section defined in the top-level [`Trigger`] docs. Calling this function is
47 /// unintuitively risky. _Do not use it directly unless you know what you are doing_. Importantly, this should only
48 /// be called for an `event` whose [`Event::Trigger`] matches this trigger.
49 unsafe fn trigger(
50 &mut self,
51 world: DeferredWorld,
52 observers: &CachedObservers,
53 trigger_context: &TriggerContext,
54 event: &mut E,
55 );
56}
57
58/// A [`Trigger`] that runs _every_ "global" [`Observer`](crate::observer::Observer) (ex: registered via [`World::add_observer`](crate::world::World::add_observer))
59/// that matches the given [`Event`].
60///
61/// The [`Event`] derive defaults to using this [`Trigger`], and it is usable for any [`Event`] type.
62#[derive(Default, Debug)]
63pub struct GlobalTrigger;
64
65// SAFETY:
66// - `E`'s [`Event::Trigger`] is constrained to [`GlobalTrigger`]
67// - The implementation abides by the other safety constraints defined in [`Trigger`]
68unsafe impl<E: for<'a> Event<Trigger<'a> = Self>> Trigger<E> for GlobalTrigger {
69 unsafe fn trigger(
70 &mut self,
71 world: DeferredWorld,
72 observers: &CachedObservers,
73 trigger_context: &TriggerContext,
74 event: &mut E,
75 ) {
76 // SAFETY:
77 // - The caller of `trigger` ensures that `observers` come from the `world`
78 // - The passed in event ptr comes from `event`, which is E: Event
79 // - E: Event::Trigger is constrained to GlobalTrigger
80 // - The caller of `trigger` ensures that `TriggerContext::event_key` matches `event`
81 unsafe {
82 self.trigger_internal(world, observers, trigger_context, event.into());
83 }
84 }
85}
86
87impl GlobalTrigger {
88 /// # Safety
89 /// - `observers` must come from the `world` [`DeferredWorld`], and correspond to observers that match the `event` type
90 /// - `event` must point to an [`Event`]
91 /// - The `event` [`Event::Trigger`] must be [`GlobalTrigger`]
92 /// - `trigger_context`'s [`TriggerContext::event_key`] must correspond to the `event` type.
93 unsafe fn trigger_internal(
94 &mut self,
95 mut world: DeferredWorld,
96 observers: &CachedObservers,
97 trigger_context: &TriggerContext,
98 mut event: PtrMut,
99 ) {
100 // SAFETY: `observers` is the only active reference to something in `world`
101 unsafe {
102 world.as_unsafe_world_cell().increment_trigger_id();
103 }
104 for (observer, runner) in observers.global_observers() {
105 // SAFETY:
106 // - `observers` come from `world` and match the `event` type, enforced by the call to `trigger_internal`
107 // - the passed in event pointer is an `Event`, enforced by the call to `trigger_internal`
108 // - `trigger` is a matching trigger type, as it comes from `self`, which is the Trigger for `event`, enforced by `trigger_internal`
109 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger_internal`
110 // - this abides by the nuances defined in the `Trigger` safety docs
111 unsafe {
112 (runner)(
113 world.reborrow(),
114 *observer,
115 trigger_context,
116 event.reborrow(),
117 self.into(),
118 );
119 }
120 }
121 }
122}
123
124/// An [`EntityEvent`] [`Trigger`] that does two things:
125/// - Runs all "global" [`Observer`] (ex: registered via [`World::add_observer`](crate::world::World::add_observer))
126/// that matches the given [`Event`]. This is the same behavior as [`GlobalTrigger`].
127/// - Runs every "entity scoped" [`Observer`] that watches the given [`EntityEvent::event_target`] entity.
128///
129/// The [`EntityEvent`] derive defaults to using this [`Trigger`], and it is usable for any [`EntityEvent`] type.
130///
131/// [`Observer`]: crate::observer::Observer
132#[derive(Default, Debug)]
133pub struct EntityTrigger;
134
135// SAFETY:
136// - `E`'s [`Event::Trigger`] is constrained to [`EntityTrigger`]
137// - The implementation abides by the other safety constraints defined in [`Trigger`]
138unsafe impl<E: EntityEvent + for<'a> Event<Trigger<'a> = Self>> Trigger<E> for EntityTrigger {
139 unsafe fn trigger(
140 &mut self,
141 world: DeferredWorld,
142 observers: &CachedObservers,
143 trigger_context: &TriggerContext,
144 event: &mut E,
145 ) {
146 let entity = event.event_target();
147 // SAFETY:
148 // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
149 // - the passed in event pointer comes from `event`, which is an `Event`
150 // - `trigger` is a matching trigger type, as it comes from `self`, which is the Trigger for `E`
151 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
152 unsafe {
153 trigger_entity_internal(
154 world,
155 observers,
156 event.into(),
157 self.into(),
158 entity,
159 trigger_context,
160 );
161 }
162 }
163}
164
165/// Trigger observers watching for the given entity event.
166/// The `target_entity` should match the [`EntityEvent::event_target`] on `event` for logical correctness.
167///
168/// # Safety
169/// - `observers` must come from the `world` [`DeferredWorld`], and correspond to observers that match the `event` type
170/// - `event` must point to an [`Event`]
171/// - `trigger` must correspond to the [`Event::Trigger`] type expected by the `event`
172/// - `trigger_context`'s [`TriggerContext::event_key`] must correspond to the `event` type.
173/// - Read, understand, and abide by the [`Trigger`] safety documentation
174// Note: this is not an EntityTrigger method because we want to reuse this logic for the entity propagation trigger
175#[inline(never)]
176pub unsafe fn trigger_entity_internal(
177 mut world: DeferredWorld,
178 observers: &CachedObservers,
179 mut event: PtrMut,
180 mut trigger: PtrMut,
181 target_entity: Entity,
182 trigger_context: &TriggerContext,
183) {
184 // SAFETY: there are no outstanding world references
185 unsafe {
186 world.as_unsafe_world_cell().increment_trigger_id();
187 }
188 for (observer, runner) in observers.global_observers() {
189 // SAFETY:
190 // - `observers` come from `world` and match the `event` type, enforced by the call to `trigger_entity_internal`
191 // - the passed in event pointer is an `Event`, enforced by the call to `trigger_entity_internal`
192 // - `trigger` is a matching trigger type, enforced by the call to `trigger_entity_internal`
193 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger_entity_internal`
194 unsafe {
195 (runner)(
196 world.reborrow(),
197 *observer,
198 trigger_context,
199 event.reborrow(),
200 trigger.reborrow(),
201 );
202 }
203 }
204
205 if let Some(map) = observers.entity_observers().get(&target_entity) {
206 for (observer, runner) in map {
207 // SAFETY:
208 // - `observers` come from `world` and match the `event` type, enforced by the call to `trigger_entity_internal`
209 // - the passed in event pointer is an `Event`, enforced by the call to `trigger_entity_internal`
210 // - `trigger` is a matching trigger type, enforced by the call to `trigger_entity_internal`
211 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger_entity_internal`
212 unsafe {
213 (runner)(
214 world.reborrow(),
215 *observer,
216 trigger_context,
217 event.reborrow(),
218 trigger.reborrow(),
219 );
220 }
221 }
222 }
223}
224
225/// An [`EntityEvent`] [`Trigger`] that behaves like [`EntityTrigger`], but "propagates" the event
226/// using an [`Entity`] [`Traversal`]. At each step in the propagation, the [`EntityTrigger`] logic will
227/// be run, until [`PropagateEntityTrigger::propagate`] is false, or there are no entities left to traverse.
228///
229/// This is used by the [`EntityEvent`] derive when `#[entity_event(propagate)]` is enabled. It is usable by every
230/// [`EntityEvent`] type.
231///
232/// If `AUTO_PROPAGATE` is `true`, [`PropagateEntityTrigger::propagate`] will default to `true`.
233pub struct PropagateEntityTrigger<const AUTO_PROPAGATE: bool, E: EntityEvent, T: Traversal<E>> {
234 /// The original [`Entity`] the [`Event`] was _first_ triggered for.
235 pub original_event_target: Entity,
236
237 /// Whether or not to continue propagating using the `T` [`Traversal`]. If this is false,
238 /// The [`Traversal`] will stop on the current entity.
239 pub propagate: bool,
240
241 _marker: PhantomData<(E, T)>,
242}
243
244impl<const AUTO_PROPAGATE: bool, E: EntityEvent, T: Traversal<E>> Default
245 for PropagateEntityTrigger<AUTO_PROPAGATE, E, T>
246{
247 fn default() -> Self {
248 Self {
249 original_event_target: Entity::PLACEHOLDER,
250 propagate: AUTO_PROPAGATE,
251 _marker: Default::default(),
252 }
253 }
254}
255
256impl<const AUTO_PROPAGATE: bool, E: EntityEvent, T: Traversal<E>> fmt::Debug
257 for PropagateEntityTrigger<AUTO_PROPAGATE, E, T>
258{
259 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260 f.debug_struct("PropagateEntityTrigger")
261 .field("original_event_target", &self.original_event_target)
262 .field("propagate", &self.propagate)
263 .field("_marker", &self._marker)
264 .finish()
265 }
266}
267
268// SAFETY:
269// - `E`'s [`Event::Trigger`] is constrained to [`PropagateEntityTrigger<E>`]
270unsafe impl<
271 const AUTO_PROPAGATE: bool,
272 E: EntityEvent + SetEntityEventTarget + for<'a> Event<Trigger<'a> = Self>,
273 T: Traversal<E>,
274 > Trigger<E> for PropagateEntityTrigger<AUTO_PROPAGATE, E, T>
275{
276 unsafe fn trigger(
277 &mut self,
278 mut world: DeferredWorld,
279 observers: &CachedObservers,
280 trigger_context: &TriggerContext,
281 event: &mut E,
282 ) {
283 let mut current_entity = event.event_target();
284 self.original_event_target = current_entity;
285 // SAFETY:
286 // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
287 // - the passed in event pointer comes from `event`, which is an `Event`
288 // - `trigger` is a matching trigger type, as it comes from `self`, which is the Trigger for `E`
289 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
290 unsafe {
291 trigger_entity_internal(
292 world.reborrow(),
293 observers,
294 event.into(),
295 self.into(),
296 current_entity,
297 trigger_context,
298 );
299 }
300
301 loop {
302 if !self.propagate {
303 return;
304 }
305 if let Ok(entity) = world.get_entity(current_entity)
306 && let Ok(item) = entity.get_components::<T>()
307 && let Some(traverse_to) = T::traverse(item, event)
308 {
309 current_entity = traverse_to;
310 } else {
311 break;
312 }
313
314 event.set_event_target(current_entity);
315 // SAFETY:
316 // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
317 // - the passed in event pointer comes from `event`, which is an `Event`
318 // - `trigger` is a matching trigger type, as it comes from `self`, which is the Trigger for `E`
319 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
320 unsafe {
321 trigger_entity_internal(
322 world.reborrow(),
323 observers,
324 event.into(),
325 self.into(),
326 current_entity,
327 trigger_context,
328 );
329 }
330 }
331 }
332}
333
334/// An [`EntityEvent`] [`Trigger`] that, in addition to behaving like a normal [`EntityTrigger`], _also_ runs observers
335/// that watch for components that match the slice of [`ComponentId`]s referenced in [`EntityComponentsTrigger`]. This includes
336/// both _global_ observers of those components and "entity scoped" observers that watch the [`EntityEvent::event_target`].
337///
338/// This is used by Bevy's built-in [lifecycle events](crate::lifecycle).
339#[derive(Default)]
340pub struct EntityComponentsTrigger<'a> {
341 /// All of the components whose observers were triggered together for the target entity. For example,
342 /// if components `A` and `B` are added together, producing the [`Add`](crate::lifecycle::Add) event, this will
343 /// contain the [`ComponentId`] for both `A` and `B`.
344 pub components: &'a [ComponentId],
345
346 /// The [`Archetype`] of the target entity before this change, or `None` if the entity was just spawned.
347 /// For observers that run before the change, like [`Discard`](crate::lifecycle::Discard) and [`Remove`](crate::lifecycle::Remove), this will be the current archetype.
348 ///
349 /// This can be useful in [`Insert`](crate::lifecycle::Insert) and [`Add`](crate::lifecycle::Add) observers,
350 /// since the old archetype will not include any other components added at the same time.
351 ///
352 /// Note that `None` should usually be treated the same as an archetype with no components,
353 /// since spawning an entity should be equivalent to spawning an empty entity and then inserting all components.
354 ///
355 /// # Example
356 /// ```
357 /// # use bevy_ecs::{
358 /// # component::ComponentIdFor, entity::EntityHashSet, entity_disabling::Disabled,
359 /// # prelude::*,
360 /// # };
361 /// # #[derive(Component)]
362 /// # struct A;
363 /// # #[derive(Resource)]
364 /// # struct EntitiesWithA(EntityHashSet);
365 /// #
366 /// # let mut world = World::new();
367 /// #
368 /// fn on_add_disable(
369 /// on: On<Add, Disabled>,
370 /// mut cache: ResMut<EntitiesWithA>,
371 /// a_component: ComponentIdFor<A>,
372 /// ) {
373 /// // The `A` component may have been added at the same time as `Disabled`,
374 /// // either due to an insert or spawn. Only try to remove this entity from
375 /// // our cache if the `A` component was in the old archetype.
376 /// if on.trigger().old_archetype.is_some_and(|a| a.contains(*a_component)) {
377 /// cache.0.remove(&on.entity);
378 /// }
379 /// }
380 /// #
381 /// # world.add_observer(on_add_disable);
382 /// ```
383 pub old_archetype: Option<&'a Archetype>,
384
385 /// The [`Archetype`] of the target entity after this change, or `None` if the entity will be despawned.
386 /// For observers that run after the change, like [`Insert`](crate::lifecycle::Insert) and [`Add`](crate::lifecycle::Add), this will be the current archetype.
387 ///
388 /// This can be useful in [`Discard`](crate::lifecycle::Discard) and [`Remove`](crate::lifecycle::Remove) observers,
389 /// since the new archetype will not include any other components removed at the same time.
390 ///
391 /// Note that `None` should usually be treated the same as an archetype with no components,
392 /// since despawning an entity should be equivalent to removing all its components and then despawning the empty entity.
393 ///
394 /// # Example
395 /// ```
396 /// # use bevy_ecs::{
397 /// # component::ComponentIdFor, entity::EntityHashSet, entity_disabling::Disabled,
398 /// # prelude::*,
399 /// # };
400 /// # #[derive(Component)]
401 /// # struct A;
402 /// # #[derive(Resource)]
403 /// # struct EntitiesWithA(EntityHashSet);
404 /// #
405 /// # let mut world = World::new();
406 /// #
407 /// fn on_remove_disable(
408 /// on: On<Remove, Disabled>,
409 /// mut cache: ResMut<EntitiesWithA>,
410 /// a_component: ComponentIdFor<A>,
411 /// ) {
412 /// // The `A` component may have been removed at the same time as `Disabled`,
413 /// // either due to a remove or despawn. Only try to add this entity to our
414 /// // cache if the `A` component is still in the new archetype.
415 /// if on.trigger().new_archetype.is_some_and(|a| a.contains(*a_component)) {
416 /// cache.0.insert(on.entity);
417 /// }
418 /// }
419 /// #
420 /// # world.add_observer(on_remove_disable);
421 /// ```
422 pub new_archetype: Option<&'a Archetype>,
423}
424
425// SAFETY:
426// - `E`'s [`Event::Trigger`] is constrained to [`EntityComponentsTrigger`]
427unsafe impl<'a, E: EntityEvent + Event<Trigger<'a> = EntityComponentsTrigger<'a>>> Trigger<E>
428 for EntityComponentsTrigger<'a>
429{
430 unsafe fn trigger(
431 &mut self,
432 world: DeferredWorld,
433 observers: &CachedObservers,
434 trigger_context: &TriggerContext,
435 event: &mut E,
436 ) {
437 let entity = event.event_target();
438 // SAFETY:
439 // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
440 // - the passed in event pointer comes from `event`, which is an `Event`
441 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
442 unsafe {
443 self.trigger_internal(world, observers, event.into(), entity, trigger_context);
444 }
445 }
446}
447
448impl<'a> EntityComponentsTrigger<'a> {
449 /// # Safety
450 /// - `observers` must come from the `world` [`DeferredWorld`]
451 /// - `event` must point to an [`Event`] whose [`Event::Trigger`] is [`EntityComponentsTrigger`]
452 /// - `trigger_context`'s [`TriggerContext::event_key`] must correspond to the `event` type.
453 #[inline(never)]
454 unsafe fn trigger_internal(
455 &mut self,
456 mut world: DeferredWorld,
457 observers: &CachedObservers,
458 mut event: PtrMut,
459 entity: Entity,
460 trigger_context: &TriggerContext,
461 ) {
462 // SAFETY:
463 // - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
464 // - the passed in event pointer comes from `event`, which is an `Event`
465 // - `trigger` is a matching trigger type, as it comes from `self`, which is the Trigger for `E`
466 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger`
467 unsafe {
468 trigger_entity_internal(
469 world.reborrow(),
470 observers,
471 event.reborrow(),
472 self.into(),
473 entity,
474 trigger_context,
475 );
476 }
477
478 // Trigger observers watching for a specific component
479 for id in self.components {
480 if let Some(component_observers) = observers.component_observers().get(id) {
481 for (observer, runner) in component_observers.global_observers() {
482 // SAFETY:
483 // - `observers` come from `world` and match the `event` type, enforced by the call to `trigger_internal`
484 // - the passed in event pointer is an `Event`, enforced by the call to `trigger_internal`
485 // - `trigger` is a matching trigger type, enforced by the call to `trigger_internal`
486 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger_internal`
487 unsafe {
488 (runner)(
489 world.reborrow(),
490 *observer,
491 trigger_context,
492 event.reborrow(),
493 self.into(),
494 );
495 }
496 }
497
498 if let Some(map) = component_observers
499 .entity_component_observers()
500 .get(&entity)
501 {
502 for (observer, runner) in map {
503 // SAFETY:
504 // - `observers` come from `world` and match the `event` type, enforced by the call to `trigger_internal`
505 // - the passed in event pointer is an `Event`, enforced by the call to `trigger_internal`
506 // - `trigger` is a matching trigger type, enforced by the call to `trigger_internal`
507 // - `trigger_context`'s event_key matches `E`, enforced by the call to `trigger_internal`
508 unsafe {
509 (runner)(
510 world.reborrow(),
511 *observer,
512 trigger_context,
513 event.reborrow(),
514 self.into(),
515 );
516 }
517 }
518 }
519 }
520 }
521 }
522}