Skip to main content

bevy_ecs/event/
trigger.rs

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