Skip to main content

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}