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