Skip to content

Commit 267a0d0

Browse files
authored
Add ComponentId-taking functions to Entity{Ref,Mut}Except to mirror FilteredEntity{Ref,Mut} (#17800)
# Objective Related to #17784. The ticket is actually about just getting rid of `Entity{Ref,Mut}Except` in favor of `FilteredEntity{Ref,Mut}`, but I got told the unification of Entity types is a bigger endeavor that has been going on for a while now (as the "Pointing Fingers" working group) and I should just add the functions I actually need in the meantime. ## Solution This PR adds all of the functions necessary to access components by TypeId or ComponentId instead of static types. ## Testing > Did you test these changes? If so, how? Haven't tested it yet, but the changes are mostly copy/paste from other implementations in the same file, since there is a lot of duplicated functionality there. ## Not a Migration Guide There shouldn't be any breaking changes, it's just a few new functions on existing types. I had to shuffle around the lifetimes in `From<&EntityMutExcept<'a, B>> for EntityRefExcept<'a, B>` (originally it was `From<&'a EntityMutExcept<'_, B>> for EntityRefExcept<'_, B>`) to make the borrow checker happy, but I don't think that this should have an impact on user code (correct me if I'm wrong).
1 parent 2fd4cc4 commit 267a0d0

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3287,6 +3287,24 @@ impl<'a> From<&'a EntityWorldMut<'_>> for FilteredEntityRef<'a> {
32873287
}
32883288
}
32893289

3290+
impl<'a, B: Bundle> From<&'a EntityRefExcept<'_, B>> for FilteredEntityRef<'a> {
3291+
fn from(value: &'a EntityRefExcept<'_, B>) -> Self {
3292+
// SAFETY:
3293+
// - The FilteredEntityRef has the same component access as the given EntityRefExcept.
3294+
unsafe {
3295+
let mut access = Access::default();
3296+
access.read_all();
3297+
let components = value.entity.world().components();
3298+
B::get_component_ids(components, &mut |maybe_id| {
3299+
if let Some(id) = maybe_id {
3300+
access.remove_component_read(id);
3301+
}
3302+
});
3303+
FilteredEntityRef::new(value.entity, access)
3304+
}
3305+
}
3306+
}
3307+
32903308
impl PartialEq for FilteredEntityRef<'_> {
32913309
fn eq(&self, other: &Self) -> bool {
32923310
self.entity() == other.entity()
@@ -3612,6 +3630,24 @@ impl<'a> From<&'a mut EntityWorldMut<'_>> for FilteredEntityMut<'a> {
36123630
}
36133631
}
36143632

3633+
impl<'a, B: Bundle> From<&'a EntityMutExcept<'_, B>> for FilteredEntityMut<'a> {
3634+
fn from(value: &'a EntityMutExcept<'_, B>) -> Self {
3635+
// SAFETY:
3636+
// - The FilteredEntityMut has the same component access as the given EntityMutExcept.
3637+
unsafe {
3638+
let mut access = Access::default();
3639+
access.write_all();
3640+
let components = value.entity.world().components();
3641+
B::get_component_ids(components, &mut |maybe_id| {
3642+
if let Some(id) = maybe_id {
3643+
access.remove_component_read(id);
3644+
}
3645+
});
3646+
FilteredEntityMut::new(value.entity, access)
3647+
}
3648+
}
3649+
}
3650+
36153651
impl PartialEq for FilteredEntityMut<'_> {
36163652
fn eq(&self, other: &Self) -> bool {
36173653
self.entity() == other.entity()
@@ -3737,6 +3773,93 @@ where
37373773
pub fn spawned_by(&self) -> MaybeLocation {
37383774
self.entity.spawned_by()
37393775
}
3776+
3777+
/// Gets the component of the given [`ComponentId`] from the entity.
3778+
///
3779+
/// **You should prefer to use the typed API [`Self::get`] where possible and only
3780+
/// use this in cases where the actual component types are not known at
3781+
/// compile time.**
3782+
///
3783+
/// Unlike [`EntityRefExcept::get`], this returns a raw pointer to the component,
3784+
/// which is only valid while the [`EntityRefExcept`] is alive.
3785+
#[inline]
3786+
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
3787+
let components = self.entity.world().components();
3788+
(!bundle_contains_component::<B>(components, component_id))
3789+
.then(|| {
3790+
// SAFETY: We have read access for this component
3791+
unsafe { self.entity.get_by_id(component_id) }
3792+
})
3793+
.flatten()
3794+
}
3795+
3796+
/// Returns `true` if the current entity has a component of type `T`.
3797+
/// Otherwise, this returns `false`.
3798+
///
3799+
/// ## Notes
3800+
///
3801+
/// If you do not know the concrete type of a component, consider using
3802+
/// [`Self::contains_id`] or [`Self::contains_type_id`].
3803+
#[inline]
3804+
pub fn contains<T: Component>(&self) -> bool {
3805+
self.contains_type_id(TypeId::of::<T>())
3806+
}
3807+
3808+
/// Returns `true` if the current entity has a component identified by `component_id`.
3809+
/// Otherwise, this returns false.
3810+
///
3811+
/// ## Notes
3812+
///
3813+
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
3814+
/// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using
3815+
/// [`Self::contains_type_id`].
3816+
#[inline]
3817+
pub fn contains_id(&self, component_id: ComponentId) -> bool {
3818+
self.entity.contains_id(component_id)
3819+
}
3820+
3821+
/// Returns `true` if the current entity has a component with the type identified by `type_id`.
3822+
/// Otherwise, this returns false.
3823+
///
3824+
/// ## Notes
3825+
///
3826+
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
3827+
/// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`].
3828+
#[inline]
3829+
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
3830+
self.entity.contains_type_id(type_id)
3831+
}
3832+
3833+
/// Retrieves the change ticks for the given component. This can be useful for implementing change
3834+
/// detection in custom runtimes.
3835+
#[inline]
3836+
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
3837+
let component_id = self.entity.world().components().get_id(TypeId::of::<T>())?;
3838+
let components = self.entity.world().components();
3839+
(!bundle_contains_component::<B>(components, component_id))
3840+
.then(|| {
3841+
// SAFETY: We have read access
3842+
unsafe { self.entity.get_change_ticks::<T>() }
3843+
})
3844+
.flatten()
3845+
}
3846+
3847+
/// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change
3848+
/// detection in custom runtimes.
3849+
///
3850+
/// **You should prefer to use the typed API [`Self::get_change_ticks`] where possible and only
3851+
/// use this in cases where the actual component types are not known at
3852+
/// compile time.**
3853+
#[inline]
3854+
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
3855+
let components = self.entity.world().components();
3856+
(!bundle_contains_component::<B>(components, component_id))
3857+
.then(|| {
3858+
// SAFETY: We have read access
3859+
unsafe { self.entity.get_change_ticks_by_id(component_id) }
3860+
})
3861+
.flatten()
3862+
}
37403863
}
37413864

37423865
impl<'a, B> From<&'a EntityMutExcept<'_, B>> for EntityRefExcept<'a, B>
@@ -3894,6 +4017,78 @@ where
38944017
pub fn spawned_by(&self) -> MaybeLocation {
38954018
self.entity.spawned_by()
38964019
}
4020+
4021+
/// Returns `true` if the current entity has a component of type `T`.
4022+
/// Otherwise, this returns `false`.
4023+
///
4024+
/// ## Notes
4025+
///
4026+
/// If you do not know the concrete type of a component, consider using
4027+
/// [`Self::contains_id`] or [`Self::contains_type_id`].
4028+
#[inline]
4029+
pub fn contains<T: Component>(&self) -> bool {
4030+
self.contains_type_id(TypeId::of::<T>())
4031+
}
4032+
4033+
/// Returns `true` if the current entity has a component identified by `component_id`.
4034+
/// Otherwise, this returns false.
4035+
///
4036+
/// ## Notes
4037+
///
4038+
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
4039+
/// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using
4040+
/// [`Self::contains_type_id`].
4041+
#[inline]
4042+
pub fn contains_id(&self, component_id: ComponentId) -> bool {
4043+
self.entity.contains_id(component_id)
4044+
}
4045+
4046+
/// Returns `true` if the current entity has a component with the type identified by `type_id`.
4047+
/// Otherwise, this returns false.
4048+
///
4049+
/// ## Notes
4050+
///
4051+
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
4052+
/// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`].
4053+
#[inline]
4054+
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
4055+
self.entity.contains_type_id(type_id)
4056+
}
4057+
4058+
/// Gets the component of the given [`ComponentId`] from the entity.
4059+
///
4060+
/// **You should prefer to use the typed API [`Self::get`] where possible and only
4061+
/// use this in cases where the actual component types are not known at
4062+
/// compile time.**
4063+
///
4064+
/// Unlike [`EntityMutExcept::get`], this returns a raw pointer to the component,
4065+
/// which is only valid while the [`EntityMutExcept`] is alive.
4066+
#[inline]
4067+
pub fn get_by_id(&'w self, component_id: ComponentId) -> Option<Ptr<'w>> {
4068+
self.as_readonly().get_by_id(component_id)
4069+
}
4070+
4071+
/// Gets a [`MutUntyped`] of the component of the given [`ComponentId`] from the entity.
4072+
///
4073+
/// **You should prefer to use the typed API [`Self::get_mut`] where possible and only
4074+
/// use this in cases where the actual component types are not known at
4075+
/// compile time.**
4076+
///
4077+
/// Unlike [`EntityMutExcept::get_mut`], this returns a raw pointer to the component,
4078+
/// which is only valid while the [`EntityMutExcept`] is alive.
4079+
#[inline]
4080+
pub fn get_mut_by_id<F: DynamicComponentFetch>(
4081+
&mut self,
4082+
component_id: ComponentId,
4083+
) -> Option<MutUntyped<'_>> {
4084+
let components = self.entity.world().components();
4085+
(!bundle_contains_component::<B>(components, component_id))
4086+
.then(|| {
4087+
// SAFETY: We have write access
4088+
unsafe { self.entity.get_mut_by_id(component_id).ok() }
4089+
})
4090+
.flatten()
4091+
}
38974092
}
38984093

38994094
impl<B: Bundle> PartialEq for EntityMutExcept<'_, B> {

0 commit comments

Comments
 (0)