Skip to main content

bevy_ecs/
lifecycle.rs

1//! This module contains various tools to allow you to react to component insertion or removal,
2//! as well as entity spawning and despawning.
3//!
4//! There are four main ways to react to these lifecycle events:
5//!
6//! 1. Using component hooks, which act as inherent constructors and destructors for components.
7//! 2. Using [observers], which are a user-extensible way to respond to events, including component lifecycle events.
8//! 3. Using the [`RemovedComponents`] system parameter, which offers an event-style interface.
9//! 4. Using the [`Added`] query filter, which checks each component to see if it has been added since the last time a system ran.
10//!
11//! [observers]: crate::observer
12//! [`Added`]: crate::query::Added
13//!
14//! # Types of lifecycle events
15//!
16//! There are five types of lifecycle events, split into two categories. First, we have lifecycle events that are triggered
17//! when a component is added to an entity:
18//!
19//! - [`Add`]: Triggered when a component is added to an entity that did not already have it.
20//! - [`Insert`]: Triggered when a component is added to an entity, regardless of whether it already had it.
21//!
22//! When both events occur, [`Add`] hooks are evaluated before [`Insert`].
23//!
24//! Next, we have lifecycle events that are triggered when a component is removed from an entity:
25//!
26//! - [`Discard`]: Triggered when a component is removed from an entity, regardless if it is then replaced with a new value.
27//! - [`Remove`]: Triggered when a component is removed from an entity and not replaced, before the component is removed.
28//! - [`Despawn`]: Triggered for each component on an entity when it is despawned.
29//!
30//! [`Discard`] hooks are evaluated before [`Remove`] hooks. When an entity is despawned,
31//! [`Despawn`] hooks are evaluated first, followed by [`Discard`] and then [`Remove`] hooks.
32//!
33//! [`Add`] and [`Remove`] are counterparts: they are only triggered when a component is added or removed
34//! from an entity in such a way as to cause a change in the component's presence on that entity.
35//! Similarly, [`Insert`] and [`Discard`] are counterparts: they are triggered when a component is added or overwritten
36//! on an entity, regardless of whether this results in a change in the component's presence on that entity.
37//!
38//! To reliably synchronize data structures using with component lifecycle events,
39//! you can combine [`Insert`] and [`Discard`] to fully capture any changes to the data.
40//! This is particularly useful in combination with immutable components,
41//! to avoid any lifecycle-bypassing mutations.
42//!
43//! ## Lifecycle events and component types
44//!
45//! Despite the absence of generics, each lifecycle event is associated with a specific component.
46//! When defining a component hook for a [`Component`] type, that component is used.
47//! When observers watch lifecycle events, the `B: Bundle` generic is used.
48//!
49//! Each of these lifecycle events also corresponds to a fixed [`ComponentId`],
50//! which are assigned during [`World`] initialization.
51//! For example, [`Add`] corresponds to [`ADD`].
52//! This is used to skip [`TypeId`](core::any::TypeId) lookups in hot paths.
53use crate::{
54    bundle::Bundle,
55    change_detection::{MaybeLocation, Tick},
56    component::{Component, ComponentId, ComponentIdFor},
57    entity::Entity,
58    event::{EntityComponentsTrigger, EntityEvent, EventKey, EventPattern},
59    message::{
60        Message, MessageCursor, MessageId, MessageIterator, MessageIteratorWithId, Messages,
61    },
62    relationship::RelationshipHookMode,
63    storage::SparseSet,
64    system::{
65        Local, ReadOnlySystemParam, SystemAccess, SystemMeta, SystemParam,
66        SystemParamValidationError,
67    },
68    world::{unsafe_world_cell::UnsafeWorldCell, DeferredWorld, World},
69};
70
71use derive_more::derive::Into;
72
73#[cfg(feature = "bevy_reflect")]
74use bevy_reflect::Reflect;
75use core::{
76    fmt::Debug,
77    iter,
78    marker::PhantomData,
79    ops::{Deref, DerefMut},
80    option,
81};
82
83/// The type used for [`Component`] lifecycle hooks such as `on_add`, `on_insert` or `on_remove`.
84pub type ComponentHook = for<'w> fn(DeferredWorld<'w>, HookContext);
85
86/// Context provided to a [`ComponentHook`].
87#[derive(Clone, Copy, Debug)]
88pub struct HookContext {
89    /// The [`Entity`] this hook was invoked for.
90    pub entity: Entity,
91    /// The [`ComponentId`] this hook was invoked for.
92    pub component_id: ComponentId,
93    /// The caller location is `Some` if the `track_caller` feature is enabled.
94    pub caller: MaybeLocation,
95    /// Configures how relationship hooks will run
96    pub relationship_hook_mode: RelationshipHookMode,
97}
98
99/// [`World`]-mutating functions that run as part of lifecycle events of a [`Component`].
100///
101/// Hooks are functions that run when a component is added, overwritten, or removed from an entity.
102/// These are intended to be used for structural side effects that need to happen when a component is added or removed,
103/// and are not intended for general-purpose logic.
104///
105/// For example, you might use a hook to update a cached index when a component is added,
106/// to clean up resources when a component is removed,
107/// or to keep hierarchical data structures across entities in sync.
108///
109/// This information is stored in the [`ComponentInfo`](crate::component::ComponentInfo) of the associated component.
110///
111/// There are two ways of configuring hooks for a component:
112/// 1. Defining the relevant hooks on the [`Component`] implementation
113/// 2. Using the [`World::register_component_hooks`] method
114///
115/// # Example
116///
117/// ```
118/// use bevy_ecs::prelude::*;
119/// use bevy_ecs::entity::EntityHashSet;
120///
121/// #[derive(Component)]
122/// struct MyTrackedComponent;
123///
124/// #[derive(Resource, Default)]
125/// struct TrackedEntities(EntityHashSet);
126///
127/// let mut world = World::new();
128/// world.init_resource::<TrackedEntities>();
129///
130/// // No entities with `MyTrackedComponent` have been added yet, so we can safely add component hooks
131/// let mut tracked_component_query = world.query::<&MyTrackedComponent>();
132/// assert!(tracked_component_query.iter(&world).next().is_none());
133///
134/// world.register_component_hooks::<MyTrackedComponent>().on_add(|mut world, context| {
135///    let mut tracked_entities = world.resource_mut::<TrackedEntities>();
136///   tracked_entities.0.insert(context.entity);
137/// });
138///
139/// world.register_component_hooks::<MyTrackedComponent>().on_remove(|mut world, context| {
140///   let mut tracked_entities = world.resource_mut::<TrackedEntities>();
141///   tracked_entities.0.remove(&context.entity);
142/// });
143///
144/// let entity = world.spawn(MyTrackedComponent).id();
145/// let tracked_entities = world.resource::<TrackedEntities>();
146/// assert!(tracked_entities.0.contains(&entity));
147///
148/// world.despawn(entity);
149/// let tracked_entities = world.resource::<TrackedEntities>();
150/// assert!(!tracked_entities.0.contains(&entity));
151/// ```
152#[derive(Debug, Clone, Default)]
153pub struct ComponentHooks {
154    pub(crate) on_add: Option<ComponentHook>,
155    pub(crate) on_insert: Option<ComponentHook>,
156    pub(crate) on_discard: Option<ComponentHook>,
157    pub(crate) on_remove: Option<ComponentHook>,
158    pub(crate) on_despawn: Option<ComponentHook>,
159}
160
161impl ComponentHooks {
162    pub(crate) fn update_from_component<C: Component + ?Sized>(&mut self) -> &mut Self {
163        if let Some(hook) = C::on_add() {
164            self.on_add(hook);
165        }
166        if let Some(hook) = C::on_insert() {
167            self.on_insert(hook);
168        }
169        if let Some(hook) = C::on_discard() {
170            self.on_discard(hook);
171        }
172        if let Some(hook) = C::on_remove() {
173            self.on_remove(hook);
174        }
175        if let Some(hook) = C::on_despawn() {
176            self.on_despawn(hook);
177        }
178
179        self
180    }
181
182    /// Register a [`ComponentHook`] that will be run when this component is added to an entity.
183    /// An `on_add` hook will always run before `on_insert` hooks. Spawning an entity counts as
184    /// adding all of its components.
185    ///
186    /// # Panics
187    ///
188    /// Will panic if the component already has an `on_add` hook
189    pub fn on_add(&mut self, hook: ComponentHook) -> &mut Self {
190        self.try_on_add(hook)
191            .expect("Component already has an on_add hook")
192    }
193
194    /// Register a [`ComponentHook`] that will be run when this component is added (with `.insert`)
195    /// or replaced.
196    ///
197    /// An `on_insert` hook always runs after any `on_add` hooks (if the entity didn't already have the component).
198    ///
199    /// # Warning
200    ///
201    /// The hook won't run if the component is already present and is only mutated, such as in a system via a query.
202    /// As a result, this needs to be combined with immutable components to serve as a mechanism for reliably updating indexes and other caches.
203    ///
204    /// # Panics
205    ///
206    /// Will panic if the component already has an `on_insert` hook
207    pub fn on_insert(&mut self, hook: ComponentHook) -> &mut Self {
208        self.try_on_insert(hook)
209            .expect("Component already has an on_insert hook")
210    }
211
212    /// Register a [`ComponentHook`] that will be run when this component is about to be dropped,
213    /// such as being replaced (with `.insert`) or removed.
214    ///
215    /// If this component is inserted onto an entity that already has it, this hook will run before the value is replaced,
216    /// allowing access to the previous data just before it is dropped.
217    /// This hook does *not* run if the entity did not already have this component.
218    ///
219    /// An `on_discard` hook always runs before any `on_remove` hooks (if the component is being removed from the entity).
220    ///
221    /// # Warning
222    ///
223    /// The hook won't run if the component is already present and is only mutated, such as in a system via a query.
224    /// As a result, this needs to be combined with immutable components to serve as a mechanism for reliably updating indexes and other caches.
225    ///
226    /// # Panics
227    ///
228    /// Will panic if the component already has an `on_discard` hook
229    pub fn on_discard(&mut self, hook: ComponentHook) -> &mut Self {
230        self.try_on_discard(hook)
231            .expect("Component already has an on_discard hook")
232    }
233
234    /// Register a [`ComponentHook`] that will be run when this component is removed from an entity.
235    /// Despawning an entity counts as removing all of its components.
236    ///
237    /// # Panics
238    ///
239    /// Will panic if the component already has an `on_remove` hook
240    pub fn on_remove(&mut self, hook: ComponentHook) -> &mut Self {
241        self.try_on_remove(hook)
242            .expect("Component already has an on_remove hook")
243    }
244
245    /// Register a [`ComponentHook`] that will be run for each component on an entity when it is despawned.
246    ///
247    /// # Panics
248    ///
249    /// Will panic if the component already has an `on_despawn` hook
250    pub fn on_despawn(&mut self, hook: ComponentHook) -> &mut Self {
251        self.try_on_despawn(hook)
252            .expect("Component already has an on_despawn hook")
253    }
254
255    /// Attempt to register a [`ComponentHook`] that will be run when this component is added to an entity.
256    ///
257    /// This is a fallible version of [`Self::on_add`].
258    ///
259    /// Returns `None` if the component already has an `on_add` hook.
260    pub fn try_on_add(&mut self, hook: ComponentHook) -> Option<&mut Self> {
261        if self.on_add.is_some() {
262            return None;
263        }
264        self.on_add = Some(hook);
265        Some(self)
266    }
267
268    /// Attempt to register a [`ComponentHook`] that will be run when this component is added (with `.insert`)
269    ///
270    /// This is a fallible version of [`Self::on_insert`].
271    ///
272    /// Returns `None` if the component already has an `on_insert` hook.
273    pub fn try_on_insert(&mut self, hook: ComponentHook) -> Option<&mut Self> {
274        if self.on_insert.is_some() {
275            return None;
276        }
277        self.on_insert = Some(hook);
278        Some(self)
279    }
280
281    /// Attempt to register a [`ComponentHook`] that will be run when this component is replaced (with `.insert`) or removed
282    ///
283    /// This is a fallible version of [`Self::on_discard`].
284    ///
285    /// Returns `None` if the component already has an `on_discard` hook.
286    pub fn try_on_discard(&mut self, hook: ComponentHook) -> Option<&mut Self> {
287        if self.on_discard.is_some() {
288            return None;
289        }
290        self.on_discard = Some(hook);
291        Some(self)
292    }
293
294    /// Attempt to register a [`ComponentHook`] that will be run when this component is removed from an entity.
295    ///
296    /// This is a fallible version of [`Self::on_remove`].
297    ///
298    /// Returns `None` if the component already has an `on_remove` hook.
299    pub fn try_on_remove(&mut self, hook: ComponentHook) -> Option<&mut Self> {
300        if self.on_remove.is_some() {
301            return None;
302        }
303        self.on_remove = Some(hook);
304        Some(self)
305    }
306
307    /// Attempt to register a [`ComponentHook`] that will be run for each component on an entity when it is despawned.
308    ///
309    /// This is a fallible version of [`Self::on_despawn`].
310    ///
311    /// Returns `None` if the component already has an `on_despawn` hook.
312    pub fn try_on_despawn(&mut self, hook: ComponentHook) -> Option<&mut Self> {
313        if self.on_despawn.is_some() {
314            return None;
315        }
316        self.on_despawn = Some(hook);
317        Some(self)
318    }
319}
320
321/// [`EventKey`] for [`Add`]
322pub const ADD: EventKey = EventKey(crate::component::ADD);
323/// [`EventKey`] for [`Insert`]
324pub const INSERT: EventKey = EventKey(crate::component::INSERT);
325/// [`EventKey`] for [`Discard`]
326pub const DISCARD: EventKey = EventKey(crate::component::DISCARD);
327/// [`EventKey`] for [`Remove`]
328pub const REMOVE: EventKey = EventKey(crate::component::REMOVE);
329/// [`EventKey`] for [`Despawn`]
330pub const DESPAWN: EventKey = EventKey(crate::component::DESPAWN);
331
332/// Trigger emitted when a component is inserted onto an entity that does not already have that
333/// component. Runs before `Insert`.
334/// See [`ComponentHooks::on_add`](`crate::lifecycle::ComponentHooks::on_add`) for more information.
335#[derive(Debug, Clone, EntityEvent)]
336#[entity_event(trigger = EntityComponentsTrigger<'a>)]
337#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
338#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
339pub struct AddEvent {
340    /// The entity this component was added to.
341    pub entity: Entity,
342}
343
344/// [`EventPattern`] for an [`AddEvent`] on a given bundle of components.
345///
346/// # Note
347///
348/// All components specified in the [`Bundle`] are treated as an `OR` filter
349/// **not** an `AND` filter. For example, `Add<(A, B)>` will trigger if either
350/// component `A` or component `B` is added to an entity.
351#[doc(alias = "OnAdd")]
352pub struct Add<B: Bundle>(PhantomData<B>);
353
354impl<B: Bundle> EventPattern for Add<B> {
355    type Event = AddEvent;
356    type Components = B;
357}
358
359/// Trigger emitted when a component is inserted, regardless of whether or not the entity already
360/// had that component. Runs after `Add`, if it ran.
361/// See [`ComponentHooks::on_insert`](`crate::lifecycle::ComponentHooks::on_insert`) for more information.
362#[derive(Debug, Clone, EntityEvent)]
363#[entity_event(trigger = EntityComponentsTrigger<'a>)]
364#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
365#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
366pub struct InsertEvent {
367    /// The entity this component was inserted into.
368    pub entity: Entity,
369}
370
371/// [`EventPattern`] for an [`InsertEvent`] on a given bundle of components.
372///
373/// # Note
374///
375/// All components specified in the [`Bundle`] are treated as an `OR` filter
376/// **not** an `AND` filter. For example, `Insert<(A, B)>` will trigger if
377/// either component `A` or component `B` is inserted into an entity.
378#[doc(alias = "OnInsert")]
379pub struct Insert<B: Bundle>(PhantomData<B>);
380
381impl<B: Bundle> EventPattern for Insert<B> {
382    type Event = InsertEvent;
383    type Components = B;
384}
385
386/// Trigger emitted when a component is removed from an entity, regardless
387/// of whether or not it is later replaced.
388///
389/// Runs before the value is replaced, so you can still access the original component data.
390/// See [`ComponentHooks::on_discard`](`crate::lifecycle::ComponentHooks::on_discard`) for more information.
391#[derive(Debug, Clone, EntityEvent)]
392#[entity_event(trigger = EntityComponentsTrigger<'a>)]
393#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
394#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
395pub struct DiscardEvent {
396    /// The entity that held this component before it was discarded.
397    pub entity: Entity,
398}
399
400/// [`EventPattern`] for a [`DiscardEvent`] on a given bundle of components.
401///
402/// # Note
403///
404/// All components specified in the [`Bundle`] are treated as an `OR` filter
405/// **not** an `AND` filter. For example, `Discard<(A, B)>` will trigger if
406/// either component `A` or component `B` are discarded from an entity.
407#[doc(alias = "OnDiscard")]
408#[doc(alias = "OnReplace")]
409#[doc(alias = "Replace")]
410pub struct Discard<B: Bundle>(PhantomData<B>);
411
412impl<B: Bundle> EventPattern for Discard<B> {
413    type Event = DiscardEvent;
414    type Components = B;
415}
416
417/// Trigger emitted when a component is removed from an entity, and runs before the component is
418/// removed, so you can still access the component data.
419/// See [`ComponentHooks::on_remove`](`crate::lifecycle::ComponentHooks::on_remove`) for more information.
420#[derive(Debug, Clone, EntityEvent)]
421#[entity_event(trigger = EntityComponentsTrigger<'a>)]
422#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
423#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
424pub struct RemoveEvent {
425    /// The entity this component was removed from.
426    pub entity: Entity,
427}
428
429/// [`EventPattern`] for a [`RemoveEvent`] on a given bundle of components.
430///
431/// # Note
432///
433/// All components specified in the [`Bundle`] are treated as an `OR` filter
434/// **not** an `AND` filter. For example, `Remove<(A, B)>` will trigger if
435/// either component `A` or component `B` are removed from an entity.
436#[doc(alias = "OnRemove")]
437pub struct Remove<B: Bundle>(PhantomData<B>);
438
439impl<B: Bundle> EventPattern for Remove<B> {
440    type Event = RemoveEvent;
441    type Components = B;
442}
443
444/// [`EntityEvent`] emitted for each component on an entity when it is despawned.
445/// See [`ComponentHooks::on_despawn`](`crate::lifecycle::ComponentHooks::on_despawn`) for more information.
446#[derive(Debug, Clone, EntityEvent)]
447#[entity_event(trigger = EntityComponentsTrigger<'a>)]
448#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
449#[cfg_attr(feature = "bevy_reflect", reflect(Debug))]
450pub struct DespawnEvent {
451    /// The entity that held this component before it was despawned.
452    pub entity: Entity,
453}
454
455/// [`EventPattern`] for a [`DespawnEvent`] on a given bundle of components.
456///
457/// # Note
458///
459/// All components specified in the [`Bundle`] are treated as an `OR` filter
460/// **not** an `AND` filter. For example, `Despawn<(A, B)>` will trigger if
461/// either component `A` or component `B` are present on an entity that is despawned.
462#[doc(alias = "OnDespawn")]
463pub struct Despawn<B: Bundle>(PhantomData<B>);
464
465impl<B: Bundle> EventPattern for Despawn<B> {
466    type Event = DespawnEvent;
467    type Components = B;
468}
469
470/// Wrapper around [`Entity`] for [`RemovedComponents`].
471/// Internally, `RemovedComponents` uses these as an [`Messages<RemovedComponentEntity>`].
472#[derive(Message, Debug, Clone, Into)]
473#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
474#[cfg_attr(feature = "bevy_reflect", reflect(Debug, Clone))]
475pub struct RemovedComponentEntity(Entity);
476
477/// Wrapper around a [`MessageCursor<RemovedComponentEntity>`] so that we
478/// can differentiate messages between components.
479#[derive(Debug)]
480pub struct RemovedComponentReader<T>
481where
482    T: Component,
483{
484    reader: MessageCursor<RemovedComponentEntity>,
485    marker: PhantomData<T>,
486}
487
488impl<T: Component> Default for RemovedComponentReader<T> {
489    fn default() -> Self {
490        Self {
491            reader: Default::default(),
492            marker: PhantomData,
493        }
494    }
495}
496
497impl<T: Component> Deref for RemovedComponentReader<T> {
498    type Target = MessageCursor<RemovedComponentEntity>;
499    fn deref(&self) -> &Self::Target {
500        &self.reader
501    }
502}
503
504impl<T: Component> DerefMut for RemovedComponentReader<T> {
505    fn deref_mut(&mut self) -> &mut Self::Target {
506        &mut self.reader
507    }
508}
509
510/// Stores the [`RemovedComponents`] event buffers for all types of component in a given [`World`].
511#[derive(Default, Debug)]
512pub struct RemovedComponentMessages {
513    event_sets: SparseSet<ComponentId, Messages<RemovedComponentEntity>>,
514}
515
516impl RemovedComponentMessages {
517    /// Creates an empty storage buffer for component removal messages.
518    pub fn new() -> Self {
519        Self::default()
520    }
521
522    /// For each type of component, swaps the event buffers and clears the oldest event buffer.
523    /// In general, this should be called once per frame/update.
524    pub fn update(&mut self) {
525        for (_component_id, messages) in self.event_sets.iter_mut() {
526            messages.update();
527        }
528    }
529
530    /// Returns an iterator over components and their entity messages.
531    pub fn iter(&self) -> impl Iterator<Item = (&ComponentId, &Messages<RemovedComponentEntity>)> {
532        self.event_sets.iter()
533    }
534
535    /// Gets the event storage for a given component.
536    pub fn get(
537        &self,
538        component_id: impl Into<ComponentId>,
539    ) -> Option<&Messages<RemovedComponentEntity>> {
540        self.event_sets.get(component_id.into())
541    }
542
543    /// Writes a removal message for the specified component.
544    pub fn write(&mut self, component_id: impl Into<ComponentId>, entity: Entity) {
545        self.event_sets
546            .get_or_insert_with(component_id.into(), Default::default)
547            .write(RemovedComponentEntity(entity));
548    }
549}
550
551/// A [`SystemParam`] that yields entities that had their `T` [`Component`]
552/// removed or have been despawned with it.
553///
554/// This acts effectively the same as a [`MessageReader`](crate::message::MessageReader).
555///
556/// Unlike hooks or observers (see the [lifecycle](crate) module docs),
557/// this does not allow you to see which data existed before removal.
558///
559/// If you are using `bevy_ecs` as a standalone crate,
560/// note that the [`RemovedComponents`] list will not be automatically cleared for you,
561/// and will need to be manually flushed using [`World::clear_trackers`](World::clear_trackers).
562///
563/// For users of `bevy` and `bevy_app`, [`World::clear_trackers`](World::clear_trackers) is
564/// automatically called by `bevy_app::App::update` and `bevy_app::SubApp::update`.
565/// For the main world, this is delayed until after all `SubApp`s have run.
566///
567/// # Examples
568///
569/// Basic usage:
570///
571/// ```
572/// # use bevy_ecs::component::Component;
573/// # use bevy_ecs::system::IntoSystem;
574/// # use bevy_ecs::lifecycle::RemovedComponents;
575/// #
576/// # #[derive(Component)]
577/// # struct MyComponent;
578/// fn react_on_removal(mut removed: RemovedComponents<MyComponent>) {
579///     removed.read().for_each(|removed_entity| println!("{}", removed_entity));
580/// }
581/// # bevy_ecs::system::assert_is_system(react_on_removal);
582/// ```
583#[derive(SystemParam)]
584pub struct RemovedComponents<'w, 's, T: Component> {
585    component_id: ComponentIdFor<'s, T>,
586    reader: Local<'s, RemovedComponentReader<T>>,
587    message_sets: &'w RemovedComponentMessages,
588}
589
590/// Iterator over entities that had a specific component removed.
591///
592/// See [`RemovedComponents`].
593pub type RemovedIter<'a> = iter::Map<
594    iter::Flatten<option::IntoIter<iter::Cloned<MessageIterator<'a, RemovedComponentEntity>>>>,
595    fn(RemovedComponentEntity) -> Entity,
596>;
597
598/// Iterator over entities that had a specific component removed.
599///
600/// See [`RemovedComponents`].
601pub type RemovedIterWithId<'a> = iter::Map<
602    iter::Flatten<option::IntoIter<MessageIteratorWithId<'a, RemovedComponentEntity>>>,
603    fn(
604        (&RemovedComponentEntity, MessageId<RemovedComponentEntity>),
605    ) -> (Entity, MessageId<RemovedComponentEntity>),
606>;
607
608fn map_id_messages(
609    (entity, id): (&RemovedComponentEntity, MessageId<RemovedComponentEntity>),
610) -> (Entity, MessageId<RemovedComponentEntity>) {
611    (entity.clone().into(), id)
612}
613
614// For all practical purposes, the api surface of `RemovedComponents<T>`
615// should be similar to `MessageReader<T>` to reduce confusion.
616impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> {
617    /// Fetch underlying [`MessageCursor`].
618    pub fn reader(&self) -> &MessageCursor<RemovedComponentEntity> {
619        &self.reader
620    }
621
622    /// Fetch underlying [`MessageCursor`] mutably.
623    pub fn reader_mut(&mut self) -> &mut MessageCursor<RemovedComponentEntity> {
624        &mut self.reader
625    }
626
627    /// Fetch underlying [`Messages`].
628    pub fn messages(&self) -> Option<&Messages<RemovedComponentEntity>> {
629        self.message_sets.get(self.component_id.get())
630    }
631
632    /// Destructures to get a mutable reference to the `MessageCursor`
633    /// and a reference to `Messages`.
634    ///
635    /// This is necessary since Rust can't detect destructuring through methods and most
636    /// usecases of the reader uses the `Messages` as well.
637    pub fn reader_mut_with_messages(
638        &mut self,
639    ) -> Option<(
640        &mut RemovedComponentReader<T>,
641        &Messages<RemovedComponentEntity>,
642    )> {
643        self.message_sets
644            .get(self.component_id.get())
645            .map(|messages| (&mut *self.reader, messages))
646    }
647
648    /// Iterates over the messages this [`RemovedComponents`] has not seen yet. This updates the
649    /// [`RemovedComponents`]'s message counter, which means subsequent message reads will not include messages
650    /// that happened before now.
651    pub fn read(&mut self) -> RemovedIter<'_> {
652        self.reader_mut_with_messages()
653            .map(|(reader, messages)| reader.read(messages).cloned())
654            .into_iter()
655            .flatten()
656            .map(RemovedComponentEntity::into)
657    }
658
659    /// Like [`read`](Self::read), except also returning the [`MessageId`] of the messages.
660    pub fn read_with_id(&mut self) -> RemovedIterWithId<'_> {
661        self.reader_mut_with_messages()
662            .map(|(reader, messages)| reader.read_with_id(messages))
663            .into_iter()
664            .flatten()
665            .map(map_id_messages)
666    }
667
668    /// Determines the number of removal messages available to be read from this [`RemovedComponents`] without consuming any.
669    pub fn len(&self) -> usize {
670        self.messages()
671            .map(|messages| self.reader.len(messages))
672            .unwrap_or(0)
673    }
674
675    /// Returns `true` if there are no messages available to read.
676    pub fn is_empty(&self) -> bool {
677        self.messages()
678            .is_none_or(|messages| self.reader.is_empty(messages))
679    }
680
681    /// Consumes all available messages.
682    ///
683    /// This means these messages will not appear in calls to [`RemovedComponents::read()`] or
684    /// [`RemovedComponents::read_with_id()`] and [`RemovedComponents::is_empty()`] will return `true`.
685    pub fn clear(&mut self) {
686        if let Some((reader, messages)) = self.reader_mut_with_messages() {
687            reader.clear(messages);
688        }
689    }
690}
691
692// SAFETY: Only reads World removed component messages
693unsafe impl<'a> ReadOnlySystemParam for &'a RemovedComponentMessages {}
694
695// SAFETY: no component value access.
696unsafe impl<'a> SystemParam for &'a RemovedComponentMessages {
697    type State = ();
698    type Item<'w, 's> = &'w RemovedComponentMessages;
699
700    fn init_state(_world: &mut World) -> Self::State {}
701
702    fn init_access(
703        _state: &Self::State,
704        system_meta: &mut SystemMeta,
705        system_access: &mut SystemAccess,
706        _world: &mut World,
707    ) {
708        system_access.require_shared_access::<Self>(system_meta);
709    }
710
711    #[inline]
712    unsafe fn get_param<'w, 's>(
713        _state: &'s mut Self::State,
714        _system_meta: &SystemMeta,
715        world: UnsafeWorldCell<'w>,
716        _change_tick: Tick,
717    ) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
718        Ok(world.removed_components())
719    }
720}