Skip to content

Commit 05288ff

Browse files
authored
Generalize component reflection to operate on FilteredEntityRef and FilteredEntityMut, not EntityRef and EntityMut. (#13549)
Currently, either an `EntityRef` or `EntityMut` is required in order to reflect a component on an entity. This can, however, be generalized to `FilteredEntityRef` and `FilteredEntityMut`, which are versions of `EntityRef` and `EntityMut` that restrict the components that can be accessed. This is useful because dynamic queries yield `FilteredEntityRef` and `FilteredEntityMut` rows when iterated over. This commit changes `ReflectComponent::contains()`, `ReflectComponent::reflect()`, and `ReflectComponent::reflect_mut()` to take an `Into<FilteredEntityRef>` (in the case of `contains()` and `reflect()`) and `Into<FilteredEntityMut>` (in the case of `reflect_mut()`). Fortunately, `EntityRef` and `EntityMut` already implement the corresponding trait, so nothing else has to be done to the public API. Note that in order to implement `ReflectComponent::reflect_mut()` properly, an additional method `FilteredEntityMut::into_mut()` was required, to match the one on `EntityMut`. I ran into this when attempting to implement `QUERY` in the Bevy Remote Protocol when trying to iterate over rows of dynamic queries and fetch the associated components without unsafe code. There were other potential ways to work around this problem, but they required either reimplementing the query logic myself instead of using regular Bevy queries or storing entity IDs and then issuing another query to fetch the associated `EntityRef`. Both of these seemed worse than just improving the `reflect()` function. ## Migration Guide * `ReflectComponent::contains`, `ReflectComponent::reflect`, and `ReflectComponent::reflect_mut` now take `FilteredEntityRef` (in the case of `contains()` and `reflect()`) and `FilteredEntityMut` (in the case of `reflect_mut()`) parameters. `FilteredEntityRef` and `FilteredEntityMut` have very similar APIs to `EntityRef` and `EntityMut` respectively, but optionally restrict the components that can be accessed.
1 parent d98d6d8 commit 05288ff

File tree

2 files changed

+25
-9
lines changed

2 files changed

+25
-9
lines changed

crates/bevy_ecs/src/reflect/component.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,10 @@ use crate::{
6262
change_detection::Mut,
6363
component::Component,
6464
entity::Entity,
65-
world::{unsafe_world_cell::UnsafeEntityCell, EntityMut, EntityRef, EntityWorldMut, World},
65+
world::{
66+
unsafe_world_cell::UnsafeEntityCell, EntityMut, EntityWorldMut, FilteredEntityMut,
67+
FilteredEntityRef, World,
68+
},
6669
};
6770
use bevy_reflect::{FromReflect, FromType, Reflect, TypeRegistry};
6871

@@ -104,11 +107,11 @@ pub struct ReflectComponentFns {
104107
/// Function pointer implementing [`ReflectComponent::remove()`].
105108
pub remove: fn(&mut EntityWorldMut),
106109
/// Function pointer implementing [`ReflectComponent::contains()`].
107-
pub contains: fn(EntityRef) -> bool,
110+
pub contains: fn(FilteredEntityRef) -> bool,
108111
/// Function pointer implementing [`ReflectComponent::reflect()`].
109-
pub reflect: fn(EntityRef) -> Option<&dyn Reflect>,
112+
pub reflect: fn(FilteredEntityRef) -> Option<&dyn Reflect>,
110113
/// Function pointer implementing [`ReflectComponent::reflect_mut()`].
111-
pub reflect_mut: fn(EntityMut) -> Option<Mut<dyn Reflect>>,
114+
pub reflect_mut: fn(FilteredEntityMut) -> Option<Mut<dyn Reflect>>,
112115
/// Function pointer implementing [`ReflectComponent::reflect_unchecked_mut()`].
113116
///
114117
/// # Safety
@@ -165,19 +168,19 @@ impl ReflectComponent {
165168
}
166169

167170
/// Returns whether entity contains this [`Component`]
168-
pub fn contains(&self, entity: EntityRef) -> bool {
169-
(self.0.contains)(entity)
171+
pub fn contains<'a>(&self, entity: impl Into<FilteredEntityRef<'a>>) -> bool {
172+
(self.0.contains)(entity.into())
170173
}
171174

172175
/// Gets the value of this [`Component`] type from the entity as a reflected reference.
173-
pub fn reflect<'a>(&self, entity: EntityRef<'a>) -> Option<&'a dyn Reflect> {
174-
(self.0.reflect)(entity)
176+
pub fn reflect<'a>(&self, entity: impl Into<FilteredEntityRef<'a>>) -> Option<&'a dyn Reflect> {
177+
(self.0.reflect)(entity.into())
175178
}
176179

177180
/// Gets the value of this [`Component`] type from the entity as a mutable reflected reference.
178181
pub fn reflect_mut<'a>(
179182
&self,
180-
entity: impl Into<EntityMut<'a>>,
183+
entity: impl Into<FilteredEntityMut<'a>>,
181184
) -> Option<Mut<'a, dyn Reflect>> {
182185
(self.0.reflect_mut)(entity.into())
183186
}

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,6 +2102,19 @@ impl<'w> FilteredEntityMut<'w> {
21022102
.flatten()
21032103
}
21042104

2105+
/// Consumes self and gets mutable access to the component of type `T`
2106+
/// with the world `'w` lifetime for the current entity.
2107+
/// Returns `None` if the entity does not have a component of type `T`.
2108+
#[inline]
2109+
pub fn into_mut<T: Component>(self) -> Option<Mut<'w, T>> {
2110+
let id = self.entity.world().components().get_id(TypeId::of::<T>())?;
2111+
self.access
2112+
.has_write(id)
2113+
// SAFETY: We have write access
2114+
.then(|| unsafe { self.entity.get_mut() })
2115+
.flatten()
2116+
}
2117+
21052118
/// Retrieves the change ticks for the given component. This can be useful for implementing change
21062119
/// detection in custom runtimes.
21072120
#[inline]

0 commit comments

Comments
 (0)