Skip to content

Add ComponentId-taking functions to Entity{Ref,Mut}Except to mirror FilteredEntity{Ref,Mut} #17800

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

Merged
merged 2 commits into from
Feb 12, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3287,6 +3287,24 @@ impl<'a> From<&'a EntityWorldMut<'_>> for FilteredEntityRef<'a> {
}
}

impl<'a, B: Bundle> From<&'a EntityRefExcept<'_, B>> for FilteredEntityRef<'a> {
fn from(value: &'a EntityRefExcept<'_, B>) -> Self {
// SAFETY:
// - The FilteredEntityRef has the same component access as the given EntityRefExcept.
unsafe {
let mut access = Access::default();
access.read_all();
let components = value.entity.world().components();
B::get_component_ids(components, &mut |maybe_id| {
if let Some(id) = maybe_id {
access.remove_component_read(id);
}
});
FilteredEntityRef::new(value.entity, access)
}
}
}

impl PartialEq for FilteredEntityRef<'_> {
fn eq(&self, other: &Self) -> bool {
self.entity() == other.entity()
Expand Down Expand Up @@ -3612,6 +3630,24 @@ impl<'a> From<&'a mut EntityWorldMut<'_>> for FilteredEntityMut<'a> {
}
}

impl<'a, B: Bundle> From<&'a EntityMutExcept<'_, B>> for FilteredEntityMut<'a> {
fn from(value: &'a EntityMutExcept<'_, B>) -> Self {
// SAFETY:
// - The FilteredEntityMut has the same component access as the given EntityMutExcept.
unsafe {
let mut access = Access::default();
access.write_all();
let components = value.entity.world().components();
B::get_component_ids(components, &mut |maybe_id| {
if let Some(id) = maybe_id {
access.remove_component_read(id);
}
});
FilteredEntityMut::new(value.entity, access)
}
}
}

impl PartialEq for FilteredEntityMut<'_> {
fn eq(&self, other: &Self) -> bool {
self.entity() == other.entity()
Expand Down Expand Up @@ -3737,6 +3773,93 @@ where
pub fn spawned_by(&self) -> MaybeLocation {
self.entity.spawned_by()
}

/// Gets the component of the given [`ComponentId`] from the entity.
///
/// **You should prefer to use the typed API [`Self::get`] where possible and only
/// use this in cases where the actual component types are not known at
/// compile time.**
///
/// Unlike [`EntityRefExcept::get`], this returns a raw pointer to the component,
/// which is only valid while the [`EntityRefExcept`] is alive.
#[inline]
pub fn get_by_id(&self, component_id: ComponentId) -> Option<Ptr<'w>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be generalized using DynamicComponentFetch to match the other get_by_id() methods. But that should happen anyway when these get merged by the "Pointing Fingers: Deduplicating Entity APIs with EntityPtr" working group, so this is fine for now!

let components = self.entity.world().components();
(!bundle_contains_component::<B>(components, component_id))
.then(|| {
// SAFETY: We have read access for this component
unsafe { self.entity.get_by_id(component_id) }
})
.flatten()
}

/// Returns `true` if the current entity has a component of type `T`.
/// Otherwise, this returns `false`.
///
/// ## Notes
///
/// If you do not know the concrete type of a component, consider using
/// [`Self::contains_id`] or [`Self::contains_type_id`].
#[inline]
pub fn contains<T: Component>(&self) -> bool {
self.contains_type_id(TypeId::of::<T>())
}

/// Returns `true` if the current entity has a component identified by `component_id`.
/// Otherwise, this returns false.
///
/// ## Notes
///
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
/// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using
/// [`Self::contains_type_id`].
#[inline]
pub fn contains_id(&self, component_id: ComponentId) -> bool {
self.entity.contains_id(component_id)
}

/// Returns `true` if the current entity has a component with the type identified by `type_id`.
/// Otherwise, this returns false.
///
/// ## Notes
///
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
/// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`].
#[inline]
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
self.entity.contains_type_id(type_id)
}

/// Retrieves the change ticks for the given component. This can be useful for implementing change
/// detection in custom runtimes.
#[inline]
pub fn get_change_ticks<T: Component>(&self) -> Option<ComponentTicks> {
let component_id = self.entity.world().components().get_id(TypeId::of::<T>())?;
let components = self.entity.world().components();
(!bundle_contains_component::<B>(components, component_id))
.then(|| {
// SAFETY: We have read access
unsafe { self.entity.get_change_ticks::<T>() }
})
.flatten()
}

/// Retrieves the change ticks for the given [`ComponentId`]. This can be useful for implementing change
/// detection in custom runtimes.
///
/// **You should prefer to use the typed API [`Self::get_change_ticks`] where possible and only
/// use this in cases where the actual component types are not known at
/// compile time.**
#[inline]
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
let components = self.entity.world().components();
(!bundle_contains_component::<B>(components, component_id))
.then(|| {
// SAFETY: We have read access
unsafe { self.entity.get_change_ticks_by_id(component_id) }
})
.flatten()
}
}

impl<'a, B> From<&'a EntityMutExcept<'_, B>> for EntityRefExcept<'a, B>
Expand Down Expand Up @@ -3894,6 +4017,78 @@ where
pub fn spawned_by(&self) -> MaybeLocation {
self.entity.spawned_by()
}

/// Returns `true` if the current entity has a component of type `T`.
/// Otherwise, this returns `false`.
///
/// ## Notes
///
/// If you do not know the concrete type of a component, consider using
/// [`Self::contains_id`] or [`Self::contains_type_id`].
#[inline]
pub fn contains<T: Component>(&self) -> bool {
self.contains_type_id(TypeId::of::<T>())
}

/// Returns `true` if the current entity has a component identified by `component_id`.
/// Otherwise, this returns false.
///
/// ## Notes
///
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
/// - If you know the component's [`TypeId`] but not its [`ComponentId`], consider using
/// [`Self::contains_type_id`].
#[inline]
pub fn contains_id(&self, component_id: ComponentId) -> bool {
self.entity.contains_id(component_id)
}

/// Returns `true` if the current entity has a component with the type identified by `type_id`.
/// Otherwise, this returns false.
///
/// ## Notes
///
/// - If you know the concrete type of the component, you should prefer [`Self::contains`].
/// - If you have a [`ComponentId`] instead of a [`TypeId`], consider using [`Self::contains_id`].
#[inline]
pub fn contains_type_id(&self, type_id: TypeId) -> bool {
self.entity.contains_type_id(type_id)
}

/// Gets the component of the given [`ComponentId`] from the entity.
///
/// **You should prefer to use the typed API [`Self::get`] where possible and only
/// use this in cases where the actual component types are not known at
/// compile time.**
///
/// Unlike [`EntityMutExcept::get`], this returns a raw pointer to the component,
/// which is only valid while the [`EntityMutExcept`] is alive.
#[inline]
pub fn get_by_id(&'w self, component_id: ComponentId) -> Option<Ptr<'w>> {
self.as_readonly().get_by_id(component_id)
}

/// Gets a [`MutUntyped`] of the component of the given [`ComponentId`] from the entity.
///
/// **You should prefer to use the typed API [`Self::get_mut`] where possible and only
/// use this in cases where the actual component types are not known at
/// compile time.**
///
/// Unlike [`EntityMutExcept::get_mut`], this returns a raw pointer to the component,
/// which is only valid while the [`EntityMutExcept`] is alive.
#[inline]
pub fn get_mut_by_id<F: DynamicComponentFetch>(
&mut self,
component_id: ComponentId,
) -> Option<MutUntyped<'_>> {
let components = self.entity.world().components();
(!bundle_contains_component::<B>(components, component_id))
.then(|| {
// SAFETY: We have write access
unsafe { self.entity.get_mut_by_id(component_id).ok() }
})
.flatten()
}
}

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