Skip to content

Commit 39d7af6

Browse files
committed
Reworked removal detection as events
1 parent 2938792 commit 39d7af6

File tree

9 files changed

+282
-130
lines changed

9 files changed

+282
-130
lines changed

crates/bevy_ecs/src/event.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,9 @@ impl<E: Event> Default for ManualEventReader<E> {
337337
#[allow(clippy::len_without_is_empty)] // Check fails since the is_empty implementation has a signature other than `(&self) -> bool`
338338
impl<E: Event> ManualEventReader<E> {
339339
/// See [`EventReader::iter`]
340-
pub fn iter<'a>(
340+
pub fn iter<'a, 'b: 'a>(
341341
&'a mut self,
342-
events: &'a Events<E>,
342+
events: &'b Events<E>,
343343
) -> impl DoubleEndedIterator<Item = &'a E> + ExactSizeIterator<Item = &'a E> {
344344
self.iter_with_id(events).map(|(e, _)| e)
345345
}

crates/bevy_ecs/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub mod event;
1313
pub mod query;
1414
#[cfg(feature = "bevy_reflect")]
1515
pub mod reflect;
16+
pub mod removal_detection;
1617
pub mod schedule;
1718
pub mod storage;
1819
pub mod system;
@@ -29,6 +30,7 @@ pub mod prelude {
2930
pub use crate::{
3031
bundle::Bundle,
3132
change_detection::DetectChanges,
33+
removal_detection::RemovedComponents,
3234
component::Component,
3335
entity::Entity,
3436
event::{EventReader, EventWriter, Events},
@@ -41,7 +43,7 @@ pub mod prelude {
4143
adapter as system_adapter,
4244
adapter::{dbg, error, ignore, info, unwrap, warn},
4345
Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,
44-
ParamSet, Query, RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction,
46+
ParamSet, Query, Res, ResMut, Resource, System, SystemParamFunction,
4547
},
4648
world::{FromWorld, Mut, World},
4749
};
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
use crate::{
2+
self as bevy_ecs,
3+
archetype::{Archetype, Archetypes},
4+
bundle::Bundles,
5+
change_detection::Ticks,
6+
component::{Component, ComponentId, ComponentTicks, Components, Tick},
7+
entity::{Entities, Entity},
8+
event::{Events, ManualEventReader},
9+
prelude::{Local, Res},
10+
query::{
11+
Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQuery, WorldQuery,
12+
},
13+
storage::SparseSet,
14+
system::{CommandQueue, Commands, Query, Resource, SystemMeta, ToComponentId},
15+
world::{FromWorld, World},
16+
};
17+
use bevy_ecs_macros::SystemParam;
18+
use bevy_ecs_macros::{all_tuples, impl_param_set};
19+
use bevy_ptr::UnsafeCellDeref;
20+
use bevy_utils::synccell::SyncCell;
21+
use std::{
22+
borrow::Cow,
23+
fmt::Debug,
24+
marker::PhantomData,
25+
ops::{Deref, DerefMut},
26+
};
27+
28+
#[derive(Debug)]
29+
pub struct RemovedComponentReader<T>
30+
where
31+
T: Component,
32+
{
33+
reader: ManualEventReader<Entity>,
34+
marker: PhantomData<T>,
35+
}
36+
37+
impl<T: Component> Default for RemovedComponentReader<T> {
38+
fn default() -> Self {
39+
Self {
40+
reader: Default::default(),
41+
marker: PhantomData,
42+
}
43+
}
44+
}
45+
46+
impl<T: Component> Deref for RemovedComponentReader<T> {
47+
type Target = ManualEventReader<Entity>;
48+
fn deref(&self) -> &Self::Target {
49+
&self.reader
50+
}
51+
}
52+
53+
impl<T: Component> DerefMut for RemovedComponentReader<T> {
54+
fn deref_mut(&mut self) -> &mut Self::Target {
55+
&mut self.reader
56+
}
57+
}
58+
59+
#[derive(Default, Debug)]
60+
pub struct RemovedComponentEvents {
61+
event_sets: SparseSet<ComponentId, Events<Entity>>,
62+
}
63+
64+
impl RemovedComponentEvents {
65+
pub fn new() -> Self {
66+
Self::default()
67+
}
68+
69+
pub fn update(&mut self) {
70+
for (component_id, events) in self.event_sets.iter_mut() {
71+
events.update();
72+
}
73+
}
74+
75+
pub fn get(&self, component_id: impl Into<ComponentId>) -> Option<&Events<Entity>> {
76+
self.event_sets
77+
.get(component_id.into())
78+
}
79+
80+
pub fn expect(&self, component_id: impl Into<ComponentId>) -> &Events<Entity> {
81+
self.get(component_id)
82+
.expect("No removal events for component")
83+
}
84+
85+
pub fn send(&mut self, component_id: impl Into<ComponentId>, entity: Entity) {
86+
self.event_sets
87+
.get_or_insert_with(component_id.into(), Default::default)
88+
.send(entity);
89+
}
90+
}
91+
92+
#[derive(SystemParam)]
93+
/// A [`SystemParam`] that grants access to the entities that had their `T` [`Component`] removed.
94+
///
95+
/// Note that this does not allow you to see which data existed before removal.
96+
/// If you need this, you will need to track the component data value on your own,
97+
/// using a regularly scheduled system that requests `Query<(Entity, &T), Changed<T>>`
98+
/// and stores the data somewhere safe to later cross-reference.
99+
///
100+
/// If you are using `bevy_ecs` as a standalone crate,
101+
/// note that the `RemovedComponents` list will not be automatically cleared for you,
102+
/// and will need to be manually flushed using [`World::clear_trackers`]
103+
///
104+
/// For users of `bevy` and `bevy_app`, this is automatically done in `bevy_app::App::update`.
105+
/// For the main world, [`World::clear_trackers`] is run after the main schedule is run and after
106+
/// `SubApp`'s have run.
107+
///
108+
/// # Examples
109+
///
110+
/// Basic usage:
111+
///
112+
/// ```
113+
/// # use bevy_ecs::component::Component;
114+
/// # use bevy_ecs::system::IntoSystem;
115+
/// # use bevy_ecs::system::RemovedComponents;
116+
/// #
117+
/// # #[derive(Component)]
118+
/// # struct MyComponent;
119+
///
120+
/// fn react_on_removal(removed: RemovedComponents<MyComponent>) {
121+
/// removed.iter().for_each(|removed_entity| println!("{:?}", removed_entity));
122+
/// }
123+
///
124+
/// # bevy_ecs::system::assert_is_system(react_on_removal);
125+
/// ```
126+
pub struct RemovedComponents<'w, 's, T: Component> {
127+
component_id: ToComponentId<T>,
128+
reader: Local<'s, RemovedComponentReader<T>>,
129+
event_sets: &'w RemovedComponentEvents,
130+
}
131+
132+
impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> {
133+
pub fn iter(&mut self) -> Box<dyn Iterator<Item = Entity> + '_> {
134+
if let Some(events) = self.event_sets.get(self.component_id) {
135+
Box::new(self.reader.iter(events).cloned())
136+
} else {
137+
Box::new(std::iter::empty())
138+
}
139+
}
140+
}

crates/bevy_ecs/src/system/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,9 @@ mod tests {
117117
schedule::{Schedule, Stage, SystemStage},
118118
system::{
119119
Commands, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, QueryComponentError,
120-
RemovedComponents, Res, ResMut, Resource, System, SystemState,
120+
Res, ResMut, Resource, System, SystemState,
121121
},
122+
removal_detection::RemovedComponents,
122123
world::{FromWorld, World},
123124
};
124125

@@ -577,7 +578,7 @@ mod tests {
577578
world.entity_mut(spurious_entity).despawn();
578579

579580
fn validate_despawn(
580-
removed_i32: RemovedComponents<W<i32>>,
581+
mut removed_i32: RemovedComponents<W<i32>>,
581582
despawned: Res<Despawned>,
582583
mut n_systems: ResMut<NSystems>,
583584
) {
@@ -602,7 +603,7 @@ mod tests {
602603
world.entity_mut(entity_to_remove_w_from).remove::<W<i32>>();
603604

604605
fn validate_remove(
605-
removed_i32: RemovedComponents<W<i32>>,
606+
mut removed_i32: RemovedComponents<W<i32>>,
606607
removed: Res<Removed>,
607608
mut n_systems: ResMut<NSystems>,
608609
) {

0 commit comments

Comments
 (0)