-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[Merged by Bors] - Replace RemovedComponents<T>
backing with Events<Entity>
#5680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
4d63994
Reworked removal detection as events
Aceeri 8350f8b
Full path for phantomdata
Aceeri 36469e1
Better docs for ComponentIdFor, another From impl for common usage of it
Aceeri 823e7c2
Publicize the removed iter type with docs, move SystemParam impl for …
Aceeri 2389515
Merge branch 'main' into removed-components-as-events
Aceeri 3081a3a
Add import for tests
Aceeri File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
//! Alerting events when a component is removed from an entity. | ||
|
||
use crate::{ | ||
self as bevy_ecs, | ||
component::{Component, ComponentId, ComponentIdFor}, | ||
entity::Entity, | ||
event::{Events, ManualEventIterator, ManualEventReader}, | ||
prelude::Local, | ||
storage::SparseSet, | ||
system::{ReadOnlySystemParam, SystemMeta, SystemParam}, | ||
world::World, | ||
}; | ||
|
||
use std::{ | ||
fmt::Debug, | ||
iter, | ||
marker::PhantomData, | ||
ops::{Deref, DerefMut}, | ||
option, | ||
}; | ||
|
||
/// Wrapper around a [`ManualEventReader<Entity>`] so that we | ||
/// can differentiate events between components. | ||
#[derive(Debug)] | ||
pub struct RemovedComponentReader<T> | ||
Aceeri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 | ||
} | ||
} | ||
|
||
/// Wrapper around a map of components to [`Events<Entity>`]. | ||
/// So that we can find the events without naming the type directly. | ||
#[derive(Default, Debug)] | ||
pub struct RemovedComponentEvents { | ||
Aceeri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 send(&mut self, component_id: impl Into<ComponentId>, entity: Entity) { | ||
self.event_sets | ||
.get_or_insert_with(component_id.into(), Default::default) | ||
.send(entity); | ||
} | ||
} | ||
|
||
/// 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`](crate::world::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`](crate::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::removal_detection::RemovedComponents; | ||
/// # | ||
/// # #[derive(Component)] | ||
/// # struct MyComponent; | ||
/// fn react_on_removal(mut removed: RemovedComponents<MyComponent>) { | ||
/// removed.iter().for_each(|removed_entity| println!("{:?}", removed_entity)); | ||
/// } | ||
/// # bevy_ecs::system::assert_is_system(react_on_removal); | ||
/// ``` | ||
#[derive(SystemParam)] | ||
pub struct RemovedComponents<'w, 's, T: Component> { | ||
component_id: Local<'s, ComponentIdFor<T>>, | ||
reader: Local<'s, RemovedComponentReader<T>>, | ||
event_sets: &'w RemovedComponentEvents, | ||
} | ||
|
||
/// Iterator over entities that had a specific component removed. | ||
/// | ||
/// See [`RemovedComponents`]. | ||
pub type RemovedIter<'a> = | ||
iter::Flatten<option::IntoIter<iter::Cloned<ManualEventIterator<'a, Entity>>>>; | ||
|
||
impl<'w, 's, T: Component> RemovedComponents<'w, 's, T> { | ||
pub fn iter(&mut self) -> RemovedIter<'_> { | ||
self.event_sets | ||
.get(**self.component_id) | ||
.map(|events| self.reader.iter(events).cloned()) | ||
.into_iter() | ||
.flatten() | ||
} | ||
} | ||
|
||
impl<'a, 'w, 's: 'a, T> IntoIterator for &'a mut RemovedComponents<'w, 's, T> | ||
where | ||
T: Component, | ||
{ | ||
type Item = Entity; | ||
type IntoIter = RemovedIter<'a>; | ||
fn into_iter(self) -> Self::IntoIter { | ||
self.iter() | ||
} | ||
} | ||
|
||
// SAFETY: Only reads World removed component events | ||
unsafe impl<'a> ReadOnlySystemParam for &'a RemovedComponentEvents {} | ||
|
||
// SAFETY: no component value access, removed component events can be read in parallel and are | ||
// never mutably borrowed during system execution | ||
unsafe impl<'a> SystemParam for &'a RemovedComponentEvents { | ||
type State = (); | ||
type Item<'w, 's> = &'w RemovedComponentEvents; | ||
|
||
fn init_state(_world: &mut World, _system_meta: &mut SystemMeta) -> Self::State {} | ||
|
||
#[inline] | ||
unsafe fn get_param<'w, 's>( | ||
_state: &'s mut Self::State, | ||
_system_meta: &SystemMeta, | ||
world: &'w World, | ||
_change_tick: u32, | ||
) -> Self::Item<'w, 's> { | ||
world.removed_components() | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.