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}