entity refs can have no location
This commit is contained in:
parent
58ee663ece
commit
85b0d03dec
@ -487,7 +487,10 @@ impl EntityCloner {
|
||||
#[cfg(not(feature = "bevy_reflect"))]
|
||||
let app_registry = Option::<()>::None;
|
||||
|
||||
let archetype = source_entity.archetype();
|
||||
let Some(archetype) = source_entity.archetype() else {
|
||||
// If the source has no archetype, there is nothing to clone.
|
||||
return target;
|
||||
};
|
||||
bundle_scratch = BundleScratch::with_capacity(archetype.component_count());
|
||||
|
||||
for component in archetype.components() {
|
||||
|
@ -782,20 +782,19 @@ impl Entities {
|
||||
}
|
||||
|
||||
/// Returns the [`EntityLocation`] of an [`Entity`].
|
||||
/// Note: for pending entities and entities not participating in the ECS (entities with a [`EntityIdLocation`] of `None`), returns `None`.
|
||||
/// Note: for non-constructed entities, returns `None`.
|
||||
#[inline]
|
||||
pub fn get(&self, entity: Entity) -> Option<EntityLocation> {
|
||||
self.get_id_location(entity).flatten()
|
||||
}
|
||||
|
||||
/// Returns the [`EntityIdLocation`] of an [`Entity`].
|
||||
/// Note: for pending entities, returns `None`.
|
||||
#[inline]
|
||||
pub fn get_id_location(&self, entity: Entity) -> Option<EntityIdLocation> {
|
||||
self.meta
|
||||
.get(entity.index() as usize)
|
||||
.filter(|meta| meta.generation == entity.generation)
|
||||
.map(|meta| meta.location)
|
||||
match self.meta.get(entity.index() as usize) {
|
||||
Some(meta) => (meta.generation == entity.generation).then_some(meta.location),
|
||||
None => (entity.generation() == EntityGeneration::FIRST).then_some(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the entity exists in the world *now*:
|
||||
|
@ -353,6 +353,36 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construct_and_destruct() {
|
||||
let mut world = World::new();
|
||||
let e1 = world.spawn_null();
|
||||
world.construct(e1, (TableStored("abc"), A(123))).unwrap();
|
||||
let e2 = world.spawn_null();
|
||||
assert!(world.destruct(e2).is_some());
|
||||
assert!(world.despawn(e2));
|
||||
let e3 = world.spawn_null();
|
||||
let mut e3 = world.entity_mut(e3);
|
||||
e3.destruct();
|
||||
e3.despawn();
|
||||
let e4 = world.spawn_null();
|
||||
world
|
||||
.entity_mut(e4)
|
||||
.construct((TableStored("junk"), A(0)))
|
||||
.unwrap()
|
||||
.destruct()
|
||||
.construct((TableStored("def"), A(456)))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(world.entities.count_active(), 2);
|
||||
assert!(world.despawn(e1));
|
||||
assert_eq!(world.entities.count_active(), 1);
|
||||
assert!(world.get::<TableStored>(e1).is_none());
|
||||
assert!(world.get::<A>(e1).is_none());
|
||||
assert_eq!(world.get::<TableStored>(e4).unwrap().0, "def");
|
||||
assert_eq!(world.get::<A>(e4).unwrap().0, 456);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn despawn_table_storage() {
|
||||
let mut world = World::new();
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
event::{Event, EventId, Events, SendBatchIds},
|
||||
observer::{Observers, TriggerTargets},
|
||||
prelude::{Component, QueryState},
|
||||
query::{QueryData, QueryFilter},
|
||||
query::{DebugCheckedUnwrap, QueryData, QueryFilter},
|
||||
relationship::RelationshipHookMode,
|
||||
resource::Resource,
|
||||
system::{Commands, Query},
|
||||
@ -144,7 +144,8 @@ impl<'w> DeferredWorld<'w> {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let archetype = &raw const *entity_cell.archetype();
|
||||
// SAFETY: If the archetype was none, it would not have the component on it.
|
||||
let archetype = unsafe { &raw const *entity_cell.archetype().debug_checked_unwrap() };
|
||||
|
||||
// SAFETY:
|
||||
// - DeferredWorld ensures archetype pointer will remain valid as no
|
||||
|
@ -215,12 +215,12 @@ unsafe impl WorldEntityFetch for Entity {
|
||||
) -> Result<Self::Mut<'_>, EntityMutableFetchError> {
|
||||
let location = cell
|
||||
.entities()
|
||||
.get(self)
|
||||
.get_id_location(self)
|
||||
.ok_or(EntityDoesNotExistError::new(self, cell.entities()))?;
|
||||
// SAFETY: caller ensures that the world cell has mutable access to the entity.
|
||||
let world = unsafe { cell.world_mut() };
|
||||
// SAFETY: location was fetched from the same world's `Entities`.
|
||||
Ok(unsafe { EntityWorldMut::new(world, self, Some(location)) })
|
||||
Ok(unsafe { EntityWorldMut::new(world, self, location) })
|
||||
}
|
||||
|
||||
unsafe fn fetch_deferred_mut(
|
||||
|
@ -78,13 +78,13 @@ impl<'w> EntityRef<'w> {
|
||||
|
||||
/// Gets metadata indicating the location where the current entity is stored.
|
||||
#[inline]
|
||||
pub fn location(&self) -> EntityLocation {
|
||||
pub fn location(&self) -> EntityIdLocation {
|
||||
self.cell.location()
|
||||
}
|
||||
|
||||
/// Returns the archetype that the current entity belongs to.
|
||||
#[inline]
|
||||
pub fn archetype(&self) -> &Archetype {
|
||||
pub fn archetype(&self) -> Option<&Archetype> {
|
||||
self.cell.archetype()
|
||||
}
|
||||
|
||||
@ -488,13 +488,13 @@ impl<'w> EntityMut<'w> {
|
||||
|
||||
/// Gets metadata indicating the location where the current entity is stored.
|
||||
#[inline]
|
||||
pub fn location(&self) -> EntityLocation {
|
||||
pub fn location(&self) -> EntityIdLocation {
|
||||
self.cell.location()
|
||||
}
|
||||
|
||||
/// Returns the archetype that the current entity belongs to.
|
||||
#[inline]
|
||||
pub fn archetype(&self) -> &Archetype {
|
||||
pub fn archetype(&self) -> Option<&Archetype> {
|
||||
self.cell.archetype()
|
||||
}
|
||||
|
||||
@ -1123,37 +1123,34 @@ impl<'w> EntityWorldMut<'w> {
|
||||
}
|
||||
|
||||
fn as_unsafe_entity_cell_readonly(&self) -> UnsafeEntityCell<'_> {
|
||||
let location = self.location();
|
||||
let last_change_tick = self.world.last_change_tick;
|
||||
let change_tick = self.world.read_change_tick();
|
||||
UnsafeEntityCell::new(
|
||||
self.world.as_unsafe_world_cell_readonly(),
|
||||
self.entity,
|
||||
location,
|
||||
self.location,
|
||||
last_change_tick,
|
||||
change_tick,
|
||||
)
|
||||
}
|
||||
fn as_unsafe_entity_cell(&mut self) -> UnsafeEntityCell<'_> {
|
||||
let location = self.location();
|
||||
let last_change_tick = self.world.last_change_tick;
|
||||
let change_tick = self.world.change_tick();
|
||||
UnsafeEntityCell::new(
|
||||
self.world.as_unsafe_world_cell(),
|
||||
self.entity,
|
||||
location,
|
||||
self.location,
|
||||
last_change_tick,
|
||||
change_tick,
|
||||
)
|
||||
}
|
||||
fn into_unsafe_entity_cell(self) -> UnsafeEntityCell<'w> {
|
||||
let location = self.location();
|
||||
let last_change_tick = self.world.last_change_tick;
|
||||
let change_tick = self.world.change_tick();
|
||||
UnsafeEntityCell::new(
|
||||
self.world.as_unsafe_world_cell(),
|
||||
self.entity,
|
||||
location,
|
||||
self.location,
|
||||
last_change_tick,
|
||||
change_tick,
|
||||
)
|
||||
@ -3275,13 +3272,13 @@ impl<'w> FilteredEntityRef<'w> {
|
||||
|
||||
/// Gets metadata indicating the location where the current entity is stored.
|
||||
#[inline]
|
||||
pub fn location(&self) -> EntityLocation {
|
||||
pub fn location(&self) -> EntityIdLocation {
|
||||
self.entity.location()
|
||||
}
|
||||
|
||||
/// Returns the archetype that the current entity belongs to.
|
||||
#[inline]
|
||||
pub fn archetype(&self) -> &Archetype {
|
||||
pub fn archetype(&self) -> Option<&Archetype> {
|
||||
self.entity.archetype()
|
||||
}
|
||||
|
||||
@ -3617,13 +3614,13 @@ impl<'w> FilteredEntityMut<'w> {
|
||||
|
||||
/// Gets metadata indicating the location where the current entity is stored.
|
||||
#[inline]
|
||||
pub fn location(&self) -> EntityLocation {
|
||||
pub fn location(&self) -> EntityIdLocation {
|
||||
self.entity.location()
|
||||
}
|
||||
|
||||
/// Returns the archetype that the current entity belongs to.
|
||||
#[inline]
|
||||
pub fn archetype(&self) -> &Archetype {
|
||||
pub fn archetype(&self) -> Option<&Archetype> {
|
||||
self.entity.archetype()
|
||||
}
|
||||
|
||||
@ -4928,7 +4925,10 @@ mod tests {
|
||||
let ent = world.spawn((Marker::<1>, Marker::<2>, Marker::<3>)).id();
|
||||
|
||||
world.entity_mut(ent).retain::<()>();
|
||||
assert_eq!(world.entity(ent).archetype().components().next(), None);
|
||||
assert_eq!(
|
||||
world.entity(ent).archetype().unwrap().components().next(),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
// Test removing some components with `retain`, including components not on the entity.
|
||||
@ -4948,6 +4948,7 @@ mod tests {
|
||||
world
|
||||
.entity(ent)
|
||||
.archetype()
|
||||
.unwrap()
|
||||
.components()
|
||||
.collect::<Vec<_>>()
|
||||
.len(),
|
||||
|
@ -977,7 +977,7 @@ impl World {
|
||||
let cell = UnsafeEntityCell::new(
|
||||
self.as_unsafe_world_cell_readonly(),
|
||||
entity,
|
||||
location,
|
||||
Some(location),
|
||||
self.last_change_tick,
|
||||
self.read_change_tick(),
|
||||
);
|
||||
@ -1000,7 +1000,7 @@ impl World {
|
||||
let cell = UnsafeEntityCell::new(
|
||||
world_cell,
|
||||
entity,
|
||||
location,
|
||||
Some(location),
|
||||
last_change_tick,
|
||||
change_tick,
|
||||
);
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
component::{ComponentId, ComponentTicks, Components, Mutable, StorageType, Tick, TickCells},
|
||||
entity::{
|
||||
ContainsEntity, Entities, EntitiesAllocator, Entity, EntityDoesNotExistError,
|
||||
EntityLocation,
|
||||
EntityIdLocation, EntityLocation,
|
||||
},
|
||||
error::{DefaultErrorHandler, ErrorHandler},
|
||||
observer::Observers,
|
||||
@ -375,7 +375,7 @@ impl<'w> UnsafeWorldCell<'w> {
|
||||
) -> Result<UnsafeEntityCell<'w>, EntityDoesNotExistError> {
|
||||
let location = self
|
||||
.entities()
|
||||
.get(entity)
|
||||
.get_id_location(entity)
|
||||
.ok_or(EntityDoesNotExistError::new(entity, self.entities()))?;
|
||||
Ok(UnsafeEntityCell::new(
|
||||
self,
|
||||
@ -397,7 +397,7 @@ impl<'w> UnsafeWorldCell<'w> {
|
||||
) -> Result<UnsafeEntityCell<'w>, EntityDoesNotExistError> {
|
||||
let location = self
|
||||
.entities()
|
||||
.get(entity)
|
||||
.get_id_location(entity)
|
||||
.ok_or(EntityDoesNotExistError::new(entity, self.entities()))?;
|
||||
Ok(UnsafeEntityCell::new(
|
||||
self, entity, location, last_run, this_run,
|
||||
@ -743,7 +743,7 @@ impl Debug for UnsafeWorldCell<'_> {
|
||||
pub struct UnsafeEntityCell<'w> {
|
||||
world: UnsafeWorldCell<'w>,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
location: EntityIdLocation,
|
||||
last_run: Tick,
|
||||
this_run: Tick,
|
||||
}
|
||||
@ -753,7 +753,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
pub(crate) fn new(
|
||||
world: UnsafeWorldCell<'w>,
|
||||
entity: Entity,
|
||||
location: EntityLocation,
|
||||
location: EntityIdLocation,
|
||||
last_run: Tick,
|
||||
this_run: Tick,
|
||||
) -> Self {
|
||||
@ -775,14 +775,15 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
|
||||
/// Gets metadata indicating the location where the current entity is stored.
|
||||
#[inline]
|
||||
pub fn location(self) -> EntityLocation {
|
||||
pub fn location(self) -> EntityIdLocation {
|
||||
self.location
|
||||
}
|
||||
|
||||
/// Returns the archetype that the current entity belongs to.
|
||||
#[inline]
|
||||
pub fn archetype(self) -> &'w Archetype {
|
||||
&self.world.archetypes()[self.location.archetype_id]
|
||||
pub fn archetype(self) -> Option<&'w Archetype> {
|
||||
self.location
|
||||
.map(|loc| &self.world.archetypes()[loc.archetype_id])
|
||||
}
|
||||
|
||||
/// Gets the world that the current entity belongs to.
|
||||
@ -813,7 +814,8 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
/// [`Self::contains_type_id`].
|
||||
#[inline]
|
||||
pub fn contains_id(self, component_id: ComponentId) -> bool {
|
||||
self.archetype().contains(component_id)
|
||||
self.archetype()
|
||||
.is_some_and(|archetype| archetype.contains(component_id))
|
||||
}
|
||||
|
||||
/// Returns `true` if the current entity has a component with the type identified by `type_id`.
|
||||
@ -848,7 +850,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
component_id,
|
||||
T::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
self.location?,
|
||||
)
|
||||
// SAFETY: returned component is of type T
|
||||
.map(|value| value.deref::<T>())
|
||||
@ -875,7 +877,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
component_id,
|
||||
T::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
self.location?,
|
||||
)
|
||||
.map(|(value, cells, caller)| Ref {
|
||||
// SAFETY: returned component is of type T
|
||||
@ -906,7 +908,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
component_id,
|
||||
T::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
self.location?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -938,7 +940,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
self.location,
|
||||
self.location?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -991,7 +993,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
component_id,
|
||||
T::STORAGE_TYPE,
|
||||
self.entity,
|
||||
self.location,
|
||||
self.location?,
|
||||
)
|
||||
.map(|(value, cells, caller)| Mut {
|
||||
// SAFETY: returned component is of type T
|
||||
@ -1015,7 +1017,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
let world = self.world().world();
|
||||
Q::get_state(world.components())?
|
||||
};
|
||||
let location = self.location();
|
||||
let location = self.location()?;
|
||||
// SAFETY: Location is guaranteed to exist
|
||||
let archetype = unsafe {
|
||||
self.world
|
||||
@ -1068,7 +1070,7 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
self.location,
|
||||
self.location?,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1103,20 +1105,23 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
|
||||
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||
unsafe {
|
||||
get_component_and_ticks(
|
||||
self.world,
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|(value, cells, caller)| MutUntyped {
|
||||
// SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime
|
||||
value: value.assert_unique(),
|
||||
ticks: TicksMut::from_tick_cells(cells, self.last_run, self.this_run),
|
||||
changed_by: caller.map(|caller| caller.deref_mut()),
|
||||
})
|
||||
.ok_or(GetEntityMutByIdError::ComponentNotFound)
|
||||
self.location
|
||||
.and_then(|location| {
|
||||
get_component_and_ticks(
|
||||
self.world,
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
location,
|
||||
)
|
||||
})
|
||||
.map(|(value, cells, caller)| MutUntyped {
|
||||
// SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime
|
||||
value: value.assert_unique(),
|
||||
ticks: TicksMut::from_tick_cells(cells, self.last_run, self.this_run),
|
||||
changed_by: caller.map(|caller| caller.deref_mut()),
|
||||
})
|
||||
.ok_or(GetEntityMutByIdError::ComponentNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1147,20 +1152,23 @@ impl<'w> UnsafeEntityCell<'w> {
|
||||
|
||||
// SAFETY: entity_location is valid, component_id is valid as checked by the line above
|
||||
unsafe {
|
||||
get_component_and_ticks(
|
||||
self.world,
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
self.location,
|
||||
)
|
||||
.map(|(value, cells, caller)| MutUntyped {
|
||||
// SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime
|
||||
value: value.assert_unique(),
|
||||
ticks: TicksMut::from_tick_cells(cells, self.last_run, self.this_run),
|
||||
changed_by: caller.map(|caller| caller.deref_mut()),
|
||||
})
|
||||
.ok_or(GetEntityMutByIdError::ComponentNotFound)
|
||||
self.location
|
||||
.and_then(|location| {
|
||||
get_component_and_ticks(
|
||||
self.world,
|
||||
component_id,
|
||||
info.storage_type(),
|
||||
self.entity,
|
||||
location,
|
||||
)
|
||||
})
|
||||
.map(|(value, cells, caller)| MutUntyped {
|
||||
// SAFETY: world access validated by caller and ties world lifetime to `MutUntyped` lifetime
|
||||
value: value.assert_unique(),
|
||||
ticks: TicksMut::from_tick_cells(cells, self.last_run, self.this_run),
|
||||
changed_by: caller.map(|caller| caller.deref_mut()),
|
||||
})
|
||||
.ok_or(GetEntityMutByIdError::ComponentNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1125,7 +1125,11 @@ pub fn process_remote_list_request(In(params): In<Option<Value>>, world: &World)
|
||||
// If `Some`, return all components of the provided entity.
|
||||
if let Some(BrpListParams { entity }) = params.map(parse).transpose()? {
|
||||
let entity = get_entity(world, entity)?;
|
||||
for component_id in entity.archetype().components() {
|
||||
for component_id in entity
|
||||
.archetype()
|
||||
.iter()
|
||||
.flat_map(|archetype| archetype.components())
|
||||
{
|
||||
let Some(component_info) = world.components().get_info(component_id) else {
|
||||
continue;
|
||||
};
|
||||
@ -1179,7 +1183,11 @@ pub fn process_remote_list_watching_request(
|
||||
let entity_ref = get_entity(world, entity)?;
|
||||
let mut response = BrpListWatchingResponse::default();
|
||||
|
||||
for component_id in entity_ref.archetype().components() {
|
||||
for component_id in entity_ref
|
||||
.archetype()
|
||||
.iter()
|
||||
.flat_map(|archetype| archetype.components())
|
||||
{
|
||||
let ticks = entity_ref
|
||||
.get_change_ticks_by_id(component_id)
|
||||
.ok_or(BrpError::internal("Failed to get ticks"))?;
|
||||
|
@ -283,7 +283,11 @@ impl<'w> DynamicSceneBuilder<'w> {
|
||||
};
|
||||
|
||||
let original_entity = self.original_world.entity(entity);
|
||||
for component_id in original_entity.archetype().components() {
|
||||
for component_id in original_entity
|
||||
.archetype()
|
||||
.iter()
|
||||
.flat_map(|archetype| archetype.components())
|
||||
{
|
||||
let mut extract_and_push = || {
|
||||
let type_id = self
|
||||
.original_world
|
||||
|
Loading…
Reference in New Issue
Block a user