Skip to content

Commit 79f6fcd

Browse files
ItsDootcartcBournhonesque
authored
EntityRef/Mut get_components (immutable variants only) (#15089)
# Objective Smaller scoped version of #13375 without the `_mut` variants which currently have unsoundness issues. ## Solution Same as #13375, but without the `_mut` variants. ## Testing - The same test from #13375 is reused. --- ## Migration Guide - Renamed `FilteredEntityRef::components` to `FilteredEntityRef::accessed_components` and `FilteredEntityMut::components` to `FilteredEntityMut::accessed_components`. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com> Co-authored-by: Periwink <charlesbour@gmail.com>
1 parent 245d03a commit 79f6fcd

File tree

3 files changed

+125
-4
lines changed

3 files changed

+125
-4
lines changed

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
entity::{Entities, Entity, EntityLocation},
77
event::Event,
88
observer::{Observer, Observers},
9-
query::Access,
9+
query::{Access, ReadOnlyQueryData},
1010
removal_detection::RemovedComponentEvents,
1111
storage::Storages,
1212
system::IntoObserverSystem,
@@ -156,6 +156,22 @@ impl<'w> EntityRef<'w> {
156156
// SAFETY: We have read-only access to all components of this entity.
157157
unsafe { self.0.get_by_id(component_id) }
158158
}
159+
160+
/// Returns read-only components for the current entity that match the query `Q`.
161+
///
162+
/// # Panics
163+
///
164+
/// If the entity does not have the components required by the query `Q`.
165+
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'w> {
166+
self.get_components::<Q>().expect(QUERY_MISMATCH_ERROR)
167+
}
168+
169+
/// Returns read-only components for the current entity that match the query `Q`,
170+
/// or `None` if the entity does not have the components required by the query `Q`.
171+
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'w>> {
172+
// SAFETY: We have read-only access to all components of this entity.
173+
unsafe { self.0.get_components::<Q>() }
174+
}
159175
}
160176

161177
impl<'w> From<EntityWorldMut<'w>> for EntityRef<'w> {
@@ -351,6 +367,22 @@ impl<'w> EntityMut<'w> {
351367
self.as_readonly().get()
352368
}
353369

370+
/// Returns read-only components for the current entity that match the query `Q`.
371+
///
372+
/// # Panics
373+
///
374+
/// If the entity does not have the components required by the query `Q`.
375+
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'_> {
376+
self.get_components::<Q>().expect(QUERY_MISMATCH_ERROR)
377+
}
378+
379+
/// Returns read-only components for the current entity that match the query `Q`,
380+
/// or `None` if the entity does not have the components required by the query `Q`.
381+
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'_>> {
382+
// SAFETY: We have read-only access to all components of this entity.
383+
unsafe { self.0.get_components::<Q>() }
384+
}
385+
354386
/// Consumes `self` and gets access to the component of type `T` with the
355387
/// world `'w` lifetime for the current entity.
356388
///
@@ -648,6 +680,23 @@ impl<'w> EntityWorldMut<'w> {
648680
EntityRef::from(self).get()
649681
}
650682

683+
/// Returns read-only components for the current entity that match the query `Q`.
684+
///
685+
/// # Panics
686+
///
687+
/// If the entity does not have the components required by the query `Q`.
688+
#[inline]
689+
pub fn components<Q: ReadOnlyQueryData>(&self) -> Q::Item<'_> {
690+
EntityRef::from(self).components::<Q>()
691+
}
692+
693+
/// Returns read-only components for the current entity that match the query `Q`,
694+
/// or `None` if the entity does not have the components required by the query `Q`.
695+
#[inline]
696+
pub fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'_>> {
697+
EntityRef::from(self).get_components::<Q>()
698+
}
699+
651700
/// Consumes `self` and gets access to the component of type `T` with
652701
/// the world `'w` lifetime for the current entity.
653702
/// Returns `None` if the entity does not have a component of type `T`.
@@ -1491,6 +1540,8 @@ unsafe fn trigger_on_replace_and_on_remove_hooks_and_observers(
14911540
}
14921541
}
14931542

1543+
const QUERY_MISMATCH_ERROR: &str = "Query does not match the current entity";
1544+
14941545
/// A view into a single entity and component in a world, which may either be vacant or occupied.
14951546
///
14961547
/// This `enum` can only be constructed from the [`entry`] method on [`EntityWorldMut`].
@@ -1878,7 +1929,7 @@ impl<'w> FilteredEntityRef<'w> {
18781929

18791930
/// Returns an iterator over the component ids that are accessed by self.
18801931
#[inline]
1881-
pub fn components(&self) -> impl Iterator<Item = ComponentId> + '_ {
1932+
pub fn accessed_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
18821933
self.access.component_reads_and_writes()
18831934
}
18841935

@@ -2135,7 +2186,7 @@ impl<'w> FilteredEntityMut<'w> {
21352186

21362187
/// Returns an iterator over the component ids that are accessed by self.
21372188
#[inline]
2138-
pub fn components(&self) -> impl Iterator<Item = ComponentId> + '_ {
2189+
pub fn accessed_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
21392190
self.access.component_reads_and_writes()
21402191
}
21412192

@@ -3115,4 +3166,24 @@ mod tests {
31153166
assert!(e.get_mut_by_id(a_id).is_none());
31163167
assert!(e.get_change_ticks_by_id(a_id).is_none());
31173168
}
3169+
3170+
#[test]
3171+
fn get_components() {
3172+
#[derive(Component, PartialEq, Eq, Debug)]
3173+
struct X(usize);
3174+
3175+
#[derive(Component, PartialEq, Eq, Debug)]
3176+
struct Y(usize);
3177+
let mut world = World::default();
3178+
let e1 = world.spawn((X(7), Y(10))).id();
3179+
let e2 = world.spawn(X(8)).id();
3180+
let e3 = world.spawn_empty().id();
3181+
3182+
assert_eq!(
3183+
Some((&X(7), &Y(10))),
3184+
world.entity(e1).get_components::<(&X, &Y)>()
3185+
);
3186+
assert_eq!(None, world.entity(e2).get_components::<(&X, &Y)>());
3187+
assert_eq!(None, world.entity(e3).get_components::<(&X, &Y)>());
3188+
}
31183189
}

crates/bevy_ecs/src/world/unsafe_world_cell.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::{
1111
entity::{Entities, Entity, EntityLocation},
1212
observer::Observers,
1313
prelude::Component,
14+
query::{DebugCheckedUnwrap, ReadOnlyQueryData},
1415
removal_detection::RemovedComponentEvents,
1516
storage::{Column, ComponentSparseSet, Storages},
1617
system::{Res, Resource},
@@ -882,6 +883,55 @@ impl<'w> UnsafeEntityCell<'w> {
882883
})
883884
}
884885
}
886+
887+
/// Returns read-only components for the current entity that match the query `Q`,
888+
/// or `None` if the entity does not have the components required by the query `Q`.
889+
///
890+
/// # Safety
891+
/// It is the callers responsibility to ensure that
892+
/// - the [`UnsafeEntityCell`] has permission to access the queried data immutably
893+
/// - no mutable references to the queried data exist at the same time
894+
pub(crate) unsafe fn get_components<Q: ReadOnlyQueryData>(&self) -> Option<Q::Item<'w>> {
895+
// SAFETY: World is only used to access query data and initialize query state
896+
let state = unsafe {
897+
let world = self.world().world();
898+
Q::get_state(world.components())?
899+
};
900+
let location = self.location();
901+
// SAFETY: Location is guaranteed to exist
902+
let archetype = unsafe {
903+
self.world
904+
.archetypes()
905+
.get(location.archetype_id)
906+
.debug_checked_unwrap()
907+
};
908+
if Q::matches_component_set(&state, &|id| archetype.contains(id)) {
909+
// SAFETY: state was initialized above using the world passed into this function
910+
let mut fetch = unsafe {
911+
Q::init_fetch(
912+
self.world,
913+
&state,
914+
self.world.last_change_tick(),
915+
self.world.change_tick(),
916+
)
917+
};
918+
// SAFETY: Table is guaranteed to exist
919+
let table = unsafe {
920+
self.world
921+
.storages()
922+
.tables
923+
.get(location.table_id)
924+
.debug_checked_unwrap()
925+
};
926+
// SAFETY: Archetype and table are from the same world used to initialize state and fetch.
927+
// Table corresponds to archetype. State is the same state used to init fetch above.
928+
unsafe { Q::set_archetype(&mut fetch, &state, archetype, table) }
929+
// SAFETY: Called after set_archetype above. Entity and location are guaranteed to exist.
930+
unsafe { Some(Q::fetch(&mut fetch, self.id(), location.table_row)) }
931+
} else {
932+
None
933+
}
934+
}
885935
}
886936

887937
impl<'w> UnsafeEntityCell<'w> {

examples/ecs/dynamic.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ fn main() {
151151

152152
query.iter_mut(&mut world).for_each(|filtered_entity| {
153153
let terms = filtered_entity
154-
.components()
154+
.accessed_components()
155155
.map(|id| {
156156
let ptr = filtered_entity.get_by_id(id).unwrap();
157157
let info = component_info.get(&id).unwrap();

0 commit comments

Comments
 (0)