Skip to content

Commit

Permalink
Reworked removal detection as events
Browse files Browse the repository at this point in the history
  • Loading branch information
Aceeri committed Dec 22, 2022
1 parent 2938792 commit 39d7af6
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 130 deletions.
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,9 @@ impl<E: Event> Default for ManualEventReader<E> {
#[allow(clippy::len_without_is_empty)] // Check fails since the is_empty implementation has a signature other than `(&self) -> bool`
impl<E: Event> ManualEventReader<E> {
/// See [`EventReader::iter`]
pub fn iter<'a>(
pub fn iter<'a, 'b: 'a>(
&'a mut self,
events: &'a Events<E>,
events: &'b Events<E>,
) -> impl DoubleEndedIterator<Item = &'a E> + ExactSizeIterator<Item = &'a E> {
self.iter_with_id(events).map(|(e, _)| e)
}
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub mod event;
pub mod query;
#[cfg(feature = "bevy_reflect")]
pub mod reflect;
pub mod removal_detection;
pub mod schedule;
pub mod storage;
pub mod system;
Expand All @@ -29,6 +30,7 @@ pub mod prelude {
pub use crate::{
bundle::Bundle,
change_detection::DetectChanges,
removal_detection::RemovedComponents,
component::Component,
entity::Entity,
event::{EventReader, EventWriter, Events},
Expand All @@ -41,7 +43,7 @@ pub mod prelude {
adapter as system_adapter,
adapter::{dbg, error, ignore, info, unwrap, warn},
Commands, In, IntoPipeSystem, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,
ParamSet, Query, RemovedComponents, Res, ResMut, Resource, System, SystemParamFunction,
ParamSet, Query, Res, ResMut, Resource, System, SystemParamFunction,
},
world::{FromWorld, Mut, World},
};
Expand Down
140 changes: 140 additions & 0 deletions crates/bevy_ecs/src/removal_detection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use crate::{
self as bevy_ecs,
archetype::{Archetype, Archetypes},
bundle::Bundles,
change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, Tick},
entity::{Entities, Entity},
event::{Events, ManualEventReader},
prelude::{Local, Res},
query::{
Access, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyWorldQuery, WorldQuery,
},
storage::SparseSet,
system::{CommandQueue, Commands, Query, Resource, SystemMeta, ToComponentId},
world::{FromWorld, World},
};
use bevy_ecs_macros::SystemParam;
use bevy_ecs_macros::{all_tuples, impl_param_set};
use bevy_ptr::UnsafeCellDeref;
use bevy_utils::synccell::SyncCell;
use std::{
borrow::Cow,
fmt::Debug,
marker::PhantomData,
ops::{Deref, DerefMut},
};

#[derive(Debug)]
pub struct RemovedComponentReader<T>
where
T: Component,
{
reader: ManualEventReader<Entity>,
marker: PhantomData<T>,
}

impl<T: Component> Default for RemovedComponentReader<T> {
fn default() -> Self {
Self {
reader: Default::default(),
marker: PhantomData,
}
}
}

impl<T: Component> Deref for RemovedComponentReader<T> {
type Target = ManualEventReader<Entity>;
fn deref(&self) -> &Self::Target {
&self.reader
}
}

impl<T: Component> DerefMut for RemovedComponentReader<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.reader
}
}

#[derive(Default, Debug)]
pub struct RemovedComponentEvents {
event_sets: SparseSet<ComponentId, Events<Entity>>,
}

impl RemovedComponentEvents {
pub fn new() -> Self {
Self::default()
}

pub fn update(&mut self) {
for (component_id, events) in self.event_sets.iter_mut() {
events.update();
}
}

pub fn get(&self, component_id: impl Into<ComponentId>) -> Option<&Events<Entity>> {
self.event_sets
.get(component_id.into())
}

pub fn expect(&self, component_id: impl Into<ComponentId>) -> &Events<Entity> {
self.get(component_id)
.expect("No removal events for component")
}

pub fn send(&mut self, component_id: impl Into<ComponentId>, entity: Entity) {
self.event_sets
.get_or_insert_with(component_id.into(), Default::default)
.send(entity);
}
}

#[derive(SystemParam)]
/// A [`SystemParam`] that grants access to the entities that had their `T` [`Component`] removed.
///
/// Note that this does not allow you to see which data existed before removal.
/// If you need this, you will need to track the component data value on your own,
/// using a regularly scheduled system that requests `Query<(Entity, &T), Changed<T>>`
/// and stores the data somewhere safe to later cross-reference.
///
/// If you are using `bevy_ecs` as a standalone crate,
/// note that the `RemovedComponents` list will not be automatically cleared for you,
/// and will need to be manually flushed using [`World::clear_trackers`]
///
/// For users of `bevy` and `bevy_app`, this is automatically done in `bevy_app::App::update`.
/// For the main world, [`World::clear_trackers`] is run after the main schedule is run and after
/// `SubApp`'s have run.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use bevy_ecs::component::Component;
/// # use bevy_ecs::system::IntoSystem;
/// # use bevy_ecs::system::RemovedComponents;
/// #
/// # #[derive(Component)]
/// # struct MyComponent;
///
/// fn react_on_removal(removed: RemovedComponents<MyComponent>) {
/// removed.iter().for_each(|removed_entity| println!("{:?}", removed_entity));
/// }
///
/// # bevy_ecs::system::assert_is_system(react_on_removal);
/// ```
pub struct RemovedComponents<'w, 's, T: Component> {
component_id: ToComponentId<T>,
reader: Local<'s, RemovedComponentReader<T>>,
event_sets: &'w RemovedComponentEvents,
}

impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> {
pub fn iter(&mut self) -> Box<dyn Iterator<Item = Entity> + '_> {
if let Some(events) = self.event_sets.get(self.component_id) {
Box::new(self.reader.iter(events).cloned())
} else {
Box::new(std::iter::empty())
}
}
}
7 changes: 4 additions & 3 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ mod tests {
schedule::{Schedule, Stage, SystemStage},
system::{
Commands, IntoSystem, Local, NonSend, NonSendMut, ParamSet, Query, QueryComponentError,
RemovedComponents, Res, ResMut, Resource, System, SystemState,
Res, ResMut, Resource, System, SystemState,
},
removal_detection::RemovedComponents,
world::{FromWorld, World},
};

Expand Down Expand Up @@ -577,7 +578,7 @@ mod tests {
world.entity_mut(spurious_entity).despawn();

fn validate_despawn(
removed_i32: RemovedComponents<W<i32>>,
mut removed_i32: RemovedComponents<W<i32>>,
despawned: Res<Despawned>,
mut n_systems: ResMut<NSystems>,
) {
Expand All @@ -602,7 +603,7 @@ mod tests {
world.entity_mut(entity_to_remove_w_from).remove::<W<i32>>();

fn validate_remove(
removed_i32: RemovedComponents<W<i32>>,
mut removed_i32: RemovedComponents<W<i32>>,
removed: Res<Removed>,
mut n_systems: ResMut<NSystems>,
) {
Expand Down
Loading

0 comments on commit 39d7af6

Please sign in to comment.