From 68f09acf8240bbf9f571be8c702a0f4bd992c39f Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 28 Apr 2024 14:26:36 -0700 Subject: [PATCH 1/5] Entity::components / components_mut / get_components / get_components_mut --- crates/bevy_ecs/src/world/entity_ref.rs | 59 ++++++++++++++++++- .../bevy_ecs/src/world/unsafe_world_cell.rs | 47 +++++++++++++++ examples/ecs/dynamic.rs | 2 +- 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 1308198186c40..e1f96affdd44a 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -4,7 +4,7 @@ use crate::{ change_detection::MutUntyped, component::{Component, ComponentId, ComponentTicks, Components, StorageType}, entity::{Entities, Entity, EntityLocation}, - query::{Access, DebugCheckedUnwrap}, + query::{Access, DebugCheckedUnwrap, QueryData, ReadOnlyQueryData}, removal_detection::RemovedComponentEvents, storage::Storages, world::{Mut, World}, @@ -153,6 +153,17 @@ impl<'w> EntityRef<'w> { // SAFETY: We have read-only access to all components of this entity. unsafe { self.0.get_by_id(component_id) } } + + /// Returns read-only components for the current entity that match the query `Q`. + pub fn components(&self) -> Q::Item<'_> { + self.get_components::().unwrap() + } + + /// Returns read-only components for the current entity that match the query `Q`. + pub fn get_components(&self) -> Option> { + // SAFETY: &mut self implies exclusive access for duration of returned value + unsafe { self.0.get_components::() } + } } impl<'w> From> for EntityRef<'w> { @@ -348,6 +359,28 @@ impl<'w> EntityMut<'w> { self.as_readonly().get() } + /// Returns read-only components for the current entity that match the query `Q`. + pub fn components(&self) -> Q::Item<'_> { + self.get_components::().unwrap() + } + + /// Returns components for the current entity that match the query `Q`. + pub fn components_mut(&mut self) -> Q::Item<'_> { + self.get_components_mut::().unwrap() + } + + /// Returns read-only components for the current entity that match the query `Q`. + pub fn get_components(&self) -> Option> { + // SAFETY: &mut self implies exclusive access for duration of returned value + unsafe { self.0.get_components::() } + } + + /// Returns components for the current entity that match the query `Q`. + pub fn get_components_mut(&mut self) -> Option> { + // SAFETY: &mut self implies exclusive access for duration of returned value + unsafe { self.0.get_components::() } + } + /// Consumes `self` and gets access to the component of type `T` with the /// world `'w` lifetime for the current entity. /// @@ -1770,7 +1803,7 @@ impl<'w> FilteredEntityRef<'w> { /// Returns an iterator over the component ids that are accessed by self. #[inline] - pub fn components(&self) -> impl Iterator + '_ { + pub fn accessed_components(&self) -> impl Iterator + '_ { self.access.reads_and_writes() } @@ -2024,7 +2057,7 @@ impl<'w> FilteredEntityMut<'w> { /// Returns an iterator over the component ids that are accessed by self. #[inline] - pub fn components(&self) -> impl Iterator + '_ { + pub fn accessed_components(&self) -> impl Iterator + '_ { self.access.reads_and_writes() } @@ -2918,4 +2951,24 @@ mod tests { assert_is_system(incompatible_system); } + + #[test] + fn get_components() { + #[derive(Component, PartialEq, Eq, Debug)] + struct X(usize); + + #[derive(Component, PartialEq, Eq, Debug)] + struct Y(usize); + let mut world = World::default(); + let e1 = world.spawn((X(7), Y(10))).id(); + let e2 = world.spawn(X(8)).id(); + let e3 = world.spawn_empty().id(); + + assert_eq!( + Some((&X(7), &Y(10))), + world.entity(e1).get_components::<(&X, &Y)>() + ); + assert_eq!(None, world.entity(e2).get_components::<(&X, &Y)>()); + assert_eq!(None, world.entity(e3).get_components::<(&X, &Y)>()); + } } diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 429a9658fdf78..7400d8fc2a684 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -10,6 +10,7 @@ use crate::{ component::{ComponentId, ComponentTicks, Components, StorageType, Tick, TickCells}, entity::{Entities, Entity, EntityLocation}, prelude::Component, + query::{DebugCheckedUnwrap, QueryData}, removal_detection::RemovedComponentEvents, storage::{Column, ComponentSparseSet, Storages}, system::{Res, Resource}, @@ -818,6 +819,52 @@ impl<'w> UnsafeEntityCell<'w> { }) } } + + /// # Safety + /// It is the callers responsibility to ensure that + /// - the [`UnsafeEntityCell`] has permission to access the queried data mutably + /// - no other references to the queried data exist at the same time + pub(crate) unsafe fn get_components(&self) -> Option> { + // SAFETY: World is only used to access query data and initialize query state + let state = unsafe { + let world = self.world().world(); + Q::get_state(world)? + }; + let location = self.location(); + // SAFETY: Location is guaranteed to exist + let archetype = unsafe { + self.world + .archetypes() + .get(location.archetype_id) + .debug_checked_unwrap() + }; + if Q::matches_component_set(&state, &|id| archetype.contains(id)) { + // SAFETY: state was initialized above using the world passed into this function + let mut fetch = unsafe { + Q::init_fetch( + self.world, + &state, + self.world.last_change_tick(), + self.world.change_tick(), + ) + }; + // SAFETY: Table is guaranteed to exist + let table = unsafe { + self.world + .storages() + .tables + .get(location.table_id) + .debug_checked_unwrap() + }; + // SAFETY: Archetype and table are from the same world used to initialize state and fetch. + // Table corresponds to archetype. State is the same state used to init fetch above. + unsafe { Q::set_archetype(&mut fetch, &state, archetype, table) } + // SAFETY: Called after set_archetype above. Entity and location are guaranteed to exist. + unsafe { Some(Q::fetch(&mut fetch, self.id(), location.table_row)) } + } else { + None + } + } } impl<'w> UnsafeEntityCell<'w> { diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index c804062aefe91..b3cc6c2b2770d 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -151,7 +151,7 @@ fn main() { query.iter_mut(&mut world).for_each(|filtered_entity| { let terms = filtered_entity - .components() + .accessed_components() .map(|id| { let ptr = filtered_entity.get_by_id(id).unwrap(); let info = component_info.get(&id).unwrap(); From 7b5d7e1faec74bf5af59e1aabdbe08d15043ab9f Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 14 May 2024 16:54:57 -0700 Subject: [PATCH 2/5] Update get_components to use world().components() instead of world().world() --- crates/bevy_ecs/src/world/unsafe_world_cell.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ecs/src/world/unsafe_world_cell.rs b/crates/bevy_ecs/src/world/unsafe_world_cell.rs index 7400d8fc2a684..16f33a13affcb 100644 --- a/crates/bevy_ecs/src/world/unsafe_world_cell.rs +++ b/crates/bevy_ecs/src/world/unsafe_world_cell.rs @@ -824,12 +824,8 @@ impl<'w> UnsafeEntityCell<'w> { /// It is the callers responsibility to ensure that /// - the [`UnsafeEntityCell`] has permission to access the queried data mutably /// - no other references to the queried data exist at the same time - pub(crate) unsafe fn get_components(&self) -> Option> { - // SAFETY: World is only used to access query data and initialize query state - let state = unsafe { - let world = self.world().world(); - Q::get_state(world)? - }; + pub(crate) unsafe fn get_components(&self) -> Option> { + let state = Q::get_state(self.world().components())?; let location = self.location(); // SAFETY: Location is guaranteed to exist let archetype = unsafe { From af6bcc1a62fc76d7858c975d7a1ffe16871c93d8 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 14 May 2024 16:55:42 -0700 Subject: [PATCH 3/5] Add World lifetimes where possible, add EntityWorldMut variants, add panic message --- crates/bevy_ecs/src/world/entity_ref.rs | 37 +++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index e1f96affdd44a..f044a6c222d87 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -155,12 +155,12 @@ impl<'w> EntityRef<'w> { } /// Returns read-only components for the current entity that match the query `Q`. - pub fn components(&self) -> Q::Item<'_> { - self.get_components::().unwrap() + pub fn components(&self) -> Q::Item<'w> { + self.get_components::().expect(QUERY_MISMATCH_ERROR) } /// Returns read-only components for the current entity that match the query `Q`. - pub fn get_components(&self) -> Option> { + pub fn get_components(&self) -> Option> { // SAFETY: &mut self implies exclusive access for duration of returned value unsafe { self.0.get_components::() } } @@ -361,12 +361,12 @@ impl<'w> EntityMut<'w> { /// Returns read-only components for the current entity that match the query `Q`. pub fn components(&self) -> Q::Item<'_> { - self.get_components::().unwrap() + self.get_components::().expect(QUERY_MISMATCH_ERROR) } /// Returns components for the current entity that match the query `Q`. pub fn components_mut(&mut self) -> Q::Item<'_> { - self.get_components_mut::().unwrap() + self.get_components_mut::().expect(QUERY_MISMATCH_ERROR) } /// Returns read-only components for the current entity that match the query `Q`. @@ -678,6 +678,31 @@ impl<'w> EntityWorldMut<'w> { EntityRef::from(self).get() } + /// Returns read-only components for the current entity that match the query `Q`. + #[inline] + pub fn components(&self) -> Q::Item<'_> { + EntityRef::from(self).components::() + } + + /// Returns components for the current entity that match the query `Q`. + #[inline] + pub fn components_mut(&mut self) -> Q::Item<'_> { + self.get_components_mut::().expect(QUERY_MISMATCH_ERROR) + } + + /// Returns read-only components for the current entity that match the query `Q`. + #[inline] + pub fn get_components(&self) -> Option> { + EntityRef::from(self).get_components::() + } + + /// Returns components for the current entity that match the query `Q`. + #[inline] + pub fn get_components_mut(&mut self) -> Option> { + // SAFETY: &mut self implies exclusive access for duration of returned value + unsafe { self.as_unsafe_entity_cell().get_components::() } + } + /// Consumes `self` and gets access to the component of type `T` with /// the world `'w` lifetime for the current entity. /// Returns `None` if the entity does not have a component of type `T`. @@ -1416,6 +1441,8 @@ impl<'w> EntityWorldMut<'w> { } } +const QUERY_MISMATCH_ERROR: &str = "Query does not match the current entity"; + /// A view into a single entity and component in a world, which may either be vacant or occupied. /// /// This `enum` can only be constructed from the [`entry`] method on [`EntityWorldMut`]. From 5005365fe3cdfd4923bdc79e1603deb84eba1b86 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 14 May 2024 16:58:18 -0700 Subject: [PATCH 4/5] Update crates/bevy_ecs/src/world/entity_ref.rs Co-authored-by: Periwink --- crates/bevy_ecs/src/world/entity_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index f044a6c222d87..7ee689a63e2af 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -371,7 +371,7 @@ impl<'w> EntityMut<'w> { /// Returns read-only components for the current entity that match the query `Q`. pub fn get_components(&self) -> Option> { - // SAFETY: &mut self implies exclusive access for duration of returned value + // SAFETY: We have read-only access to all components of this entity. unsafe { self.0.get_components::() } } From 8b8a75b2b715e37dc6c28dbd25e1c004a1bba816 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 14 May 2024 16:58:27 -0700 Subject: [PATCH 5/5] Update crates/bevy_ecs/src/world/entity_ref.rs Co-authored-by: Periwink --- crates/bevy_ecs/src/world/entity_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 7ee689a63e2af..5a6c2f8df9339 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -161,7 +161,7 @@ impl<'w> EntityRef<'w> { /// Returns read-only components for the current entity that match the query `Q`. pub fn get_components(&self) -> Option> { - // SAFETY: &mut self implies exclusive access for duration of returned value + // SAFETY: We have read-only access to all components of this entity. unsafe { self.0.get_components::() } } }