impl EntityBorrow for more types (#16917)
# Objective Some types like `RenderEntity` and `MainEntity` are just wrappers around `Entity`, so they should be able to implement `EntityBorrow`/`TrustedEntityBorrow`. This allows using them with `EntitySet` functionality. The `EntityRef` family are more than direct wrappers around `Entity`, but can still benefit from being unique in a collection. ## Solution Implement `EntityBorrow` and `TrustedEntityBorrow` for simple `Entity` newtypes and `EntityRef` types. These impls are an explicit decision to have the `EntityRef` types compare like just `Entity`. `EntityWorldMut` is omitted from this impl, because it explicitly contains a `&mut World` as well, and we do not ever use more than one at a time. Add `EntityBorrow` to the `bevy_ecs` prelude. ## Migration Guide `NormalizedWindowRef::entity` has been replaced with an `EntityBorrow::entity` impl.
This commit is contained in:
parent
450b939c1f
commit
5b899dcc3a
@ -58,7 +58,7 @@ pub mod prelude {
|
||||
bundle::Bundle,
|
||||
change_detection::{DetectChanges, DetectChangesMut, Mut, Ref},
|
||||
component::{require, Component},
|
||||
entity::{Entity, EntityMapper},
|
||||
entity::{Entity, EntityBorrow, EntityMapper},
|
||||
event::{Event, EventMutator, EventReader, EventWriter, Events},
|
||||
name::{Name, NameOrEntity},
|
||||
observer::{CloneEntityWithObserversExt, Observer, Trigger},
|
||||
|
@ -1,11 +1,15 @@
|
||||
use super::{QueryData, QueryFilter, ReadOnlyQueryData};
|
||||
use crate::{
|
||||
archetype::{Archetype, ArchetypeEntity, Archetypes},
|
||||
bundle::Bundle,
|
||||
component::Tick,
|
||||
entity::{Entities, Entity, EntityBorrow, EntitySet, EntitySetIterator},
|
||||
query::{ArchetypeFilter, DebugCheckedUnwrap, QueryState, StorageId},
|
||||
storage::{Table, TableRow, Tables},
|
||||
world::unsafe_world_cell::UnsafeWorldCell,
|
||||
world::{
|
||||
unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept,
|
||||
FilteredEntityMut, FilteredEntityRef,
|
||||
},
|
||||
};
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
@ -1105,6 +1109,36 @@ impl<'w, 's, D: QueryData, F: QueryFilter> FusedIterator for QueryIter<'w, 's, D
|
||||
// SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once.
|
||||
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, Entity, F> {}
|
||||
|
||||
// SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once.
|
||||
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, EntityRef<'_>, F> {}
|
||||
|
||||
// SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once.
|
||||
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator for QueryIter<'w, 's, EntityMut<'_>, F> {}
|
||||
|
||||
// SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once.
|
||||
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator
|
||||
for QueryIter<'w, 's, FilteredEntityRef<'_>, F>
|
||||
{
|
||||
}
|
||||
|
||||
// SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once.
|
||||
unsafe impl<'w, 's, F: QueryFilter> EntitySetIterator
|
||||
for QueryIter<'w, 's, FilteredEntityMut<'_>, F>
|
||||
{
|
||||
}
|
||||
|
||||
// SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once.
|
||||
unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator
|
||||
for QueryIter<'w, 's, EntityRefExcept<'_, B>, F>
|
||||
{
|
||||
}
|
||||
|
||||
// SAFETY: [`QueryIter`] is guaranteed to return every matching entity once and only once.
|
||||
unsafe impl<'w, 's, F: QueryFilter, B: Bundle> EntitySetIterator
|
||||
for QueryIter<'w, 's, EntityMutExcept<'_, B>, F>
|
||||
{
|
||||
}
|
||||
|
||||
impl<'w, 's, D: QueryData, F: QueryFilter> Debug for QueryIter<'w, 's, D, F> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("QueryIter").finish()
|
||||
|
@ -3,7 +3,9 @@ use crate::{
|
||||
bundle::{Bundle, BundleId, BundleInfo, BundleInserter, DynamicBundle, InsertMode},
|
||||
change_detection::MutUntyped,
|
||||
component::{Component, ComponentId, ComponentTicks, Components, Mutable, StorageType},
|
||||
entity::{Entities, Entity, EntityCloneBuilder, EntityLocation},
|
||||
entity::{
|
||||
Entities, Entity, EntityBorrow, EntityCloneBuilder, EntityLocation, TrustedEntityBorrow,
|
||||
},
|
||||
event::Event,
|
||||
observer::Observer,
|
||||
query::{Access, ReadOnlyQueryData},
|
||||
@ -17,7 +19,13 @@ use bevy_ptr::{OwningPtr, Ptr};
|
||||
use bevy_utils::{HashMap, HashSet};
|
||||
#[cfg(feature = "track_change_detection")]
|
||||
use core::panic::Location;
|
||||
use core::{any::TypeId, marker::PhantomData, mem::MaybeUninit};
|
||||
use core::{
|
||||
any::TypeId,
|
||||
cmp::Ordering,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use super::{unsafe_world_cell::UnsafeEntityCell, Ref, ON_REMOVE, ON_REPLACE};
|
||||
@ -369,6 +377,44 @@ impl<'a> TryFrom<&'a FilteredEntityMut<'_>> for EntityRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for EntityRef<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.entity() == other.entity()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for EntityRef<'_> {}
|
||||
|
||||
#[expect(clippy::non_canonical_partial_ord_impl)]
|
||||
impl PartialOrd for EntityRef<'_> {
|
||||
/// [`EntityRef`]'s comparison trait implementations match the underlying [`Entity`],
|
||||
/// and cannot discern between different worlds.
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.entity().partial_cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for EntityRef<'_> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.entity().cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for EntityRef<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.entity().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityBorrow for EntityRef<'_> {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity.
|
||||
unsafe impl TrustedEntityBorrow for EntityRef<'_> {}
|
||||
|
||||
/// Provides mutable access to a single entity and all of its components.
|
||||
///
|
||||
/// Contrast with [`EntityWorldMut`], which allows adding and removing components,
|
||||
@ -869,6 +915,44 @@ impl<'a> TryFrom<&'a mut FilteredEntityMut<'_>> for EntityMut<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for EntityMut<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.entity() == other.entity()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for EntityMut<'_> {}
|
||||
|
||||
#[expect(clippy::non_canonical_partial_ord_impl)]
|
||||
impl PartialOrd for EntityMut<'_> {
|
||||
/// [`EntityMut`]'s comparison trait implementations match the underlying [`Entity`],
|
||||
/// and cannot discern between different worlds.
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.entity().partial_cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for EntityMut<'_> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.entity().cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for EntityMut<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.entity().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityBorrow for EntityMut<'_> {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity.
|
||||
unsafe impl TrustedEntityBorrow for EntityMut<'_> {}
|
||||
|
||||
/// A mutable reference to a particular [`Entity`], and the entire world.
|
||||
///
|
||||
/// This is essentially a performance-optimized `(Entity, &mut World)` tuple,
|
||||
@ -2969,6 +3053,44 @@ impl<'a> From<&'a EntityWorldMut<'_>> for FilteredEntityRef<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FilteredEntityRef<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.entity() == other.entity()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FilteredEntityRef<'_> {}
|
||||
|
||||
#[expect(clippy::non_canonical_partial_ord_impl)]
|
||||
impl PartialOrd for FilteredEntityRef<'_> {
|
||||
/// [`FilteredEntityRef`]'s comparison trait implementations match the underlying [`Entity`],
|
||||
/// and cannot discern between different worlds.
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.entity().partial_cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for FilteredEntityRef<'_> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.entity().cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for FilteredEntityRef<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.entity().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityBorrow for FilteredEntityRef<'_> {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity.
|
||||
unsafe impl TrustedEntityBorrow for FilteredEntityRef<'_> {}
|
||||
|
||||
/// Provides mutable access to a single entity and some of its components defined by the contained [`Access`].
|
||||
///
|
||||
/// To define the access when used as a [`QueryData`](crate::query::QueryData),
|
||||
@ -3258,6 +3380,44 @@ impl<'a> From<&'a mut EntityWorldMut<'_>> for FilteredEntityMut<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for FilteredEntityMut<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.entity() == other.entity()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for FilteredEntityMut<'_> {}
|
||||
|
||||
#[expect(clippy::non_canonical_partial_ord_impl)]
|
||||
impl PartialOrd for FilteredEntityMut<'_> {
|
||||
/// [`FilteredEntityMut`]'s comparison trait implementations match the underlying [`Entity`],
|
||||
/// and cannot discern between different worlds.
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.entity().partial_cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for FilteredEntityMut<'_> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.entity().cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for FilteredEntityMut<'_> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.entity().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityBorrow for FilteredEntityMut<'_> {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity.
|
||||
unsafe impl TrustedEntityBorrow for FilteredEntityMut<'_> {}
|
||||
|
||||
/// Error type returned by [`TryFrom`] conversions from filtered entity types
|
||||
/// ([`FilteredEntityRef`]/[`FilteredEntityMut`]) to full-access entity types
|
||||
/// ([`EntityRef`]/[`EntityMut`]).
|
||||
@ -3361,6 +3521,44 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> PartialEq for EntityRefExcept<'_, B> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.entity() == other.entity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> Eq for EntityRefExcept<'_, B> {}
|
||||
|
||||
#[expect(clippy::non_canonical_partial_ord_impl)]
|
||||
impl<B: Bundle> PartialOrd for EntityRefExcept<'_, B> {
|
||||
/// [`EntityRefExcept`]'s comparison trait implementations match the underlying [`Entity`],
|
||||
/// and cannot discern between different worlds.
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.entity().partial_cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> Ord for EntityRefExcept<'_, B> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.entity().cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> Hash for EntityRefExcept<'_, B> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.entity().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> EntityBorrow for EntityRefExcept<'_, B> {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity.
|
||||
unsafe impl<B: Bundle> TrustedEntityBorrow for EntityRefExcept<'_, B> {}
|
||||
|
||||
/// Provides mutable access to all components of an entity, with the exception
|
||||
/// of an explicit set.
|
||||
///
|
||||
@ -3464,6 +3662,44 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> PartialEq for EntityMutExcept<'_, B> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.entity() == other.entity()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> Eq for EntityMutExcept<'_, B> {}
|
||||
|
||||
#[expect(clippy::non_canonical_partial_ord_impl)]
|
||||
impl<B: Bundle> PartialOrd for EntityMutExcept<'_, B> {
|
||||
/// [`EntityMutExcept`]'s comparison trait implementations match the underlying [`Entity`],
|
||||
/// and cannot discern between different worlds.
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.entity().partial_cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> Ord for EntityMutExcept<'_, B> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.entity().cmp(&other.entity())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> Hash for EntityMutExcept<'_, B> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.entity().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Bundle> EntityBorrow for EntityMutExcept<'_, B> {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: This type represents one Entity. We implement the comparison traits based on that Entity.
|
||||
unsafe impl<B: Bundle> TrustedEntityBorrow for EntityMutExcept<'_, B> {}
|
||||
|
||||
fn bundle_contains_component<B>(components: &Components, query_id: ComponentId) -> bool
|
||||
where
|
||||
B: Bundle,
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
bundle::Bundles,
|
||||
change_detection::{MaybeUnsafeCellLocation, MutUntyped, Ticks, TicksMut},
|
||||
component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells},
|
||||
entity::{Entities, Entity, EntityLocation},
|
||||
entity::{Entities, Entity, EntityBorrow, EntityLocation},
|
||||
observer::Observers,
|
||||
prelude::Component,
|
||||
query::{DebugCheckedUnwrap, ReadOnlyQueryData},
|
||||
@ -1183,3 +1183,9 @@ unsafe fn get_ticks(
|
||||
StorageType::SparseSet => world.fetch_sparse_set(component_id)?.get_ticks(entity),
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityBorrow for UnsafeEntityCell<'_> {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{
|
||||
change_detection::DetectChanges,
|
||||
component::{Component, ComponentId, Mutable},
|
||||
entity::Entity,
|
||||
entity::{Entity, EntityBorrow},
|
||||
event::EventReader,
|
||||
prelude::{require, With},
|
||||
query::Has,
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
renderer::RenderContext,
|
||||
view::ExtractedWindows,
|
||||
};
|
||||
use bevy_ecs::{prelude::QueryState, world::World};
|
||||
use bevy_ecs::{entity::EntityBorrow, prelude::QueryState, world::World};
|
||||
use bevy_utils::HashSet;
|
||||
use wgpu::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor, StoreOp};
|
||||
|
||||
|
@ -3,7 +3,7 @@ use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::entity::EntityHash;
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
entity::{Entity, EntityBorrow, TrustedEntityBorrow},
|
||||
observer::Trigger,
|
||||
query::With,
|
||||
reflect::ReflectComponent,
|
||||
@ -140,6 +140,15 @@ impl From<Entity> for RenderEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityBorrow for RenderEntity {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: RenderEntity is a newtype around Entity that derives its comparison traits.
|
||||
unsafe impl TrustedEntityBorrow for RenderEntity {}
|
||||
|
||||
/// Component added on the render world entities to keep track of the corresponding main world entity.
|
||||
///
|
||||
/// Can also be used as a newtype wrapper for main world entities.
|
||||
@ -158,6 +167,15 @@ impl From<Entity> for MainEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl EntityBorrow for MainEntity {
|
||||
fn entity(&self) -> Entity {
|
||||
self.id()
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: RenderEntity is a newtype around Entity that derives its comparison traits.
|
||||
unsafe impl TrustedEntityBorrow for MainEntity {}
|
||||
|
||||
/// A [`HashMap`](hashbrown::HashMap) pre-configured to use [`EntityHash`] hashing with a [`MainEntity`].
|
||||
pub type MainEntityHashMap<V> = hashbrown::HashMap<MainEntity, V, EntityHash>;
|
||||
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
};
|
||||
use bevy_ecs::{
|
||||
change_detection::DetectChangesMut,
|
||||
entity::Entity,
|
||||
entity::{Entity, EntityBorrow},
|
||||
prelude::{Component, With},
|
||||
query::QueryData,
|
||||
reflect::ReflectComponent,
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
};
|
||||
use bevy_ecs::{
|
||||
change_detection::{DetectChanges, DetectChangesMut},
|
||||
entity::{Entity, EntityHashMap, EntityHashSet},
|
||||
entity::{Entity, EntityBorrow, EntityHashMap, EntityHashSet},
|
||||
event::EventReader,
|
||||
query::With,
|
||||
removal_detection::RemovedComponents,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use core::num::NonZero;
|
||||
|
||||
use bevy_ecs::{
|
||||
entity::{Entity, VisitEntities, VisitEntitiesMut},
|
||||
entity::{Entity, EntityBorrow, VisitEntities, VisitEntitiesMut},
|
||||
prelude::{Component, ReflectComponent},
|
||||
};
|
||||
use bevy_math::{CompassOctant, DVec2, IVec2, UVec2, Vec2};
|
||||
@ -88,9 +88,8 @@ impl VisitEntitiesMut for WindowRef {
|
||||
)]
|
||||
pub struct NormalizedWindowRef(Entity);
|
||||
|
||||
impl NormalizedWindowRef {
|
||||
/// Fetch the entity of this window reference
|
||||
pub fn entity(&self) -> Entity {
|
||||
impl EntityBorrow for NormalizedWindowRef {
|
||||
fn entity(&self) -> Entity {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user