Remove invalid entity locations (#19433)
# Objective This is the first step of #19430 and is a follow up for #19132. Now that `ArchetypeRow` has a niche, we can use `Option` instead of needing `INVALID` everywhere. This was especially concerning since `INVALID` *really was valid!* Using options here made the code clearer and more data-driven. ## Solution Replace all uses of `INVALID` entity locations (and archetype/table rows) with `None`. ## Testing CI --------- Co-authored-by: Chris Russell <8494645+chescock@users.noreply.github.com> Co-authored-by: François Mockers <francois.mockers@vleue.com> Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
43b8fbda93
commit
61bd3af7c7
@ -1193,16 +1193,16 @@ impl<'w> BundleInserter<'w> {
|
|||||||
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
|
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
|
||||||
entities.set(
|
entities.set(
|
||||||
swapped_entity.index(),
|
swapped_entity.index(),
|
||||||
EntityLocation {
|
Some(EntityLocation {
|
||||||
archetype_id: swapped_location.archetype_id,
|
archetype_id: swapped_location.archetype_id,
|
||||||
archetype_row: location.archetype_row,
|
archetype_row: location.archetype_row,
|
||||||
table_id: swapped_location.table_id,
|
table_id: swapped_location.table_id,
|
||||||
table_row: swapped_location.table_row,
|
table_row: swapped_location.table_row,
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let new_location = new_archetype.allocate(entity, result.table_row);
|
let new_location = new_archetype.allocate(entity, result.table_row);
|
||||||
entities.set(entity.index(), new_location);
|
entities.set(entity.index(), Some(new_location));
|
||||||
let after_effect = bundle_info.write_components(
|
let after_effect = bundle_info.write_components(
|
||||||
table,
|
table,
|
||||||
sparse_sets,
|
sparse_sets,
|
||||||
@ -1242,19 +1242,19 @@ impl<'w> BundleInserter<'w> {
|
|||||||
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
|
unsafe { entities.get(swapped_entity).debug_checked_unwrap() };
|
||||||
entities.set(
|
entities.set(
|
||||||
swapped_entity.index(),
|
swapped_entity.index(),
|
||||||
EntityLocation {
|
Some(EntityLocation {
|
||||||
archetype_id: swapped_location.archetype_id,
|
archetype_id: swapped_location.archetype_id,
|
||||||
archetype_row: location.archetype_row,
|
archetype_row: location.archetype_row,
|
||||||
table_id: swapped_location.table_id,
|
table_id: swapped_location.table_id,
|
||||||
table_row: swapped_location.table_row,
|
table_row: swapped_location.table_row,
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// PERF: store "non bundle" components in edge, then just move those to avoid
|
// PERF: store "non bundle" components in edge, then just move those to avoid
|
||||||
// redundant copies
|
// redundant copies
|
||||||
let move_result = table.move_to_superset_unchecked(result.table_row, new_table);
|
let move_result = table.move_to_superset_unchecked(result.table_row, new_table);
|
||||||
let new_location = new_archetype.allocate(entity, move_result.new_row);
|
let new_location = new_archetype.allocate(entity, move_result.new_row);
|
||||||
entities.set(entity.index(), new_location);
|
entities.set(entity.index(), Some(new_location));
|
||||||
|
|
||||||
// If an entity was moved into this entity's table spot, update its table row.
|
// If an entity was moved into this entity's table spot, update its table row.
|
||||||
if let Some(swapped_entity) = move_result.swapped_entity {
|
if let Some(swapped_entity) = move_result.swapped_entity {
|
||||||
@ -1264,12 +1264,12 @@ impl<'w> BundleInserter<'w> {
|
|||||||
|
|
||||||
entities.set(
|
entities.set(
|
||||||
swapped_entity.index(),
|
swapped_entity.index(),
|
||||||
EntityLocation {
|
Some(EntityLocation {
|
||||||
archetype_id: swapped_location.archetype_id,
|
archetype_id: swapped_location.archetype_id,
|
||||||
archetype_row: swapped_location.archetype_row,
|
archetype_row: swapped_location.archetype_row,
|
||||||
table_id: swapped_location.table_id,
|
table_id: swapped_location.table_id,
|
||||||
table_row: result.table_row,
|
table_row: result.table_row,
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if archetype.id() == swapped_location.archetype_id {
|
if archetype.id() == swapped_location.archetype_id {
|
||||||
@ -1573,12 +1573,12 @@ impl<'w> BundleRemover<'w> {
|
|||||||
|
|
||||||
world.entities.set(
|
world.entities.set(
|
||||||
swapped_entity.index(),
|
swapped_entity.index(),
|
||||||
EntityLocation {
|
Some(EntityLocation {
|
||||||
archetype_id: swapped_location.archetype_id,
|
archetype_id: swapped_location.archetype_id,
|
||||||
archetype_row: location.archetype_row,
|
archetype_row: location.archetype_row,
|
||||||
table_id: swapped_location.table_id,
|
table_id: swapped_location.table_id,
|
||||||
table_row: swapped_location.table_row,
|
table_row: swapped_location.table_row,
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1614,12 +1614,12 @@ impl<'w> BundleRemover<'w> {
|
|||||||
|
|
||||||
world.entities.set(
|
world.entities.set(
|
||||||
swapped_entity.index(),
|
swapped_entity.index(),
|
||||||
EntityLocation {
|
Some(EntityLocation {
|
||||||
archetype_id: swapped_location.archetype_id,
|
archetype_id: swapped_location.archetype_id,
|
||||||
archetype_row: swapped_location.archetype_row,
|
archetype_row: swapped_location.archetype_row,
|
||||||
table_id: swapped_location.table_id,
|
table_id: swapped_location.table_id,
|
||||||
table_row: location.table_row,
|
table_row: location.table_row,
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
world.archetypes[swapped_location.archetype_id]
|
world.archetypes[swapped_location.archetype_id]
|
||||||
.set_entity_table_row(swapped_location.archetype_row, location.table_row);
|
.set_entity_table_row(swapped_location.archetype_row, location.table_row);
|
||||||
@ -1635,7 +1635,7 @@ impl<'w> BundleRemover<'w> {
|
|||||||
|
|
||||||
// SAFETY: The entity is valid and has been moved to the new location already.
|
// SAFETY: The entity is valid and has been moved to the new location already.
|
||||||
unsafe {
|
unsafe {
|
||||||
world.entities.set(entity.index(), new_location);
|
world.entities.set(entity.index(), Some(new_location));
|
||||||
}
|
}
|
||||||
|
|
||||||
(new_location, pre_remove_result)
|
(new_location, pre_remove_result)
|
||||||
@ -1736,7 +1736,7 @@ impl<'w> BundleSpawner<'w> {
|
|||||||
InsertMode::Replace,
|
InsertMode::Replace,
|
||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
entities.set(entity.index(), location);
|
entities.set(entity.index(), Some(location));
|
||||||
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick);
|
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick);
|
||||||
(location, after_effect)
|
(location, after_effect)
|
||||||
};
|
};
|
||||||
|
@ -886,8 +886,10 @@ impl Entities {
|
|||||||
|
|
||||||
/// Destroy an entity, allowing it to be reused.
|
/// Destroy an entity, allowing it to be reused.
|
||||||
///
|
///
|
||||||
|
/// Returns the `Option<EntityLocation>` of the entity or `None` if the `entity` was not present.
|
||||||
|
///
|
||||||
/// Must not be called while reserved entities are awaiting `flush()`.
|
/// Must not be called while reserved entities are awaiting `flush()`.
|
||||||
pub fn free(&mut self, entity: Entity) -> Option<EntityLocation> {
|
pub fn free(&mut self, entity: Entity) -> Option<EntityIdLocation> {
|
||||||
self.verify_flushed();
|
self.verify_flushed();
|
||||||
|
|
||||||
let meta = &mut self.meta[entity.index() as usize];
|
let meta = &mut self.meta[entity.index() as usize];
|
||||||
@ -949,20 +951,21 @@ impl Entities {
|
|||||||
*self.free_cursor.get_mut() = 0;
|
*self.free_cursor.get_mut() = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the location of an [`Entity`].
|
/// Returns the [`EntityLocation`] of an [`Entity`].
|
||||||
/// Note: for pending entities, returns `None`.
|
/// Note: for pending entities and entities not participating in the ECS (entities with a [`EntityIdLocation`] of `None`), returns `None`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get(&self, entity: Entity) -> Option<EntityLocation> {
|
pub fn get(&self, entity: Entity) -> Option<EntityLocation> {
|
||||||
if let Some(meta) = self.meta.get(entity.index() as usize) {
|
self.get_id_location(entity).flatten()
|
||||||
if meta.generation != entity.generation
|
|
||||||
|| meta.location.archetype_id == ArchetypeId::INVALID
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(meta.location)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the location of an [`Entity`].
|
/// Updates the location of an [`Entity`].
|
||||||
@ -973,7 +976,7 @@ impl Entities {
|
|||||||
/// - `location` must be valid for the entity at `index` or immediately made valid afterwards
|
/// - `location` must be valid for the entity at `index` or immediately made valid afterwards
|
||||||
/// before handing control to unknown code.
|
/// before handing control to unknown code.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn set(&mut self, index: u32, location: EntityLocation) {
|
pub(crate) unsafe fn set(&mut self, index: u32, location: EntityIdLocation) {
|
||||||
// SAFETY: Caller guarantees that `index` a valid entity index
|
// SAFETY: Caller guarantees that `index` a valid entity index
|
||||||
let meta = unsafe { self.meta.get_unchecked_mut(index as usize) };
|
let meta = unsafe { self.meta.get_unchecked_mut(index as usize) };
|
||||||
meta.location = location;
|
meta.location = location;
|
||||||
@ -1001,7 +1004,7 @@ impl Entities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let meta = &mut self.meta[index as usize];
|
let meta = &mut self.meta[index as usize];
|
||||||
if meta.location.archetype_id == ArchetypeId::INVALID {
|
if meta.location.is_none() {
|
||||||
meta.generation = meta.generation.after_versions(generations);
|
meta.generation = meta.generation.after_versions(generations);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
@ -1036,6 +1039,8 @@ impl Entities {
|
|||||||
/// Allocates space for entities previously reserved with [`reserve_entity`](Entities::reserve_entity) or
|
/// Allocates space for entities previously reserved with [`reserve_entity`](Entities::reserve_entity) or
|
||||||
/// [`reserve_entities`](Entities::reserve_entities), then initializes each one using the supplied function.
|
/// [`reserve_entities`](Entities::reserve_entities), then initializes each one using the supplied function.
|
||||||
///
|
///
|
||||||
|
/// See [`EntityLocation`] for details on its meaning and how to set it.
|
||||||
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Flush _must_ set the entity location to the correct [`ArchetypeId`] for the given [`Entity`]
|
/// Flush _must_ set the entity location to the correct [`ArchetypeId`] for the given [`Entity`]
|
||||||
/// each time init is called. This _can_ be [`ArchetypeId::INVALID`], provided the [`Entity`]
|
/// each time init is called. This _can_ be [`ArchetypeId::INVALID`], provided the [`Entity`]
|
||||||
@ -1045,7 +1050,7 @@ impl Entities {
|
|||||||
/// to be initialized with the invalid archetype.
|
/// to be initialized with the invalid archetype.
|
||||||
pub unsafe fn flush(
|
pub unsafe fn flush(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut init: impl FnMut(Entity, &mut EntityLocation),
|
mut init: impl FnMut(Entity, &mut EntityIdLocation),
|
||||||
by: MaybeLocation,
|
by: MaybeLocation,
|
||||||
at: Tick,
|
at: Tick,
|
||||||
) {
|
) {
|
||||||
@ -1090,7 +1095,7 @@ impl Entities {
|
|||||||
unsafe {
|
unsafe {
|
||||||
self.flush(
|
self.flush(
|
||||||
|_entity, location| {
|
|_entity, location| {
|
||||||
location.archetype_id = ArchetypeId::INVALID;
|
*location = None;
|
||||||
},
|
},
|
||||||
by,
|
by,
|
||||||
at,
|
at,
|
||||||
@ -1178,7 +1183,7 @@ impl Entities {
|
|||||||
.filter(|meta|
|
.filter(|meta|
|
||||||
// Generation is incremented immediately upon despawn
|
// Generation is incremented immediately upon despawn
|
||||||
(meta.generation == entity.generation)
|
(meta.generation == entity.generation)
|
||||||
|| (meta.location.archetype_id == ArchetypeId::INVALID)
|
|| meta.location.is_none()
|
||||||
&& (meta.generation == entity.generation.after_versions(1)))
|
&& (meta.generation == entity.generation.after_versions(1)))
|
||||||
.map(|meta| meta.spawned_or_despawned)
|
.map(|meta| meta.spawned_or_despawned)
|
||||||
}
|
}
|
||||||
@ -1203,13 +1208,9 @@ impl Entities {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
|
pub(crate) fn check_change_ticks(&mut self, change_tick: Tick) {
|
||||||
for meta in &mut self.meta {
|
for meta in &mut self.meta {
|
||||||
if meta.generation != EntityGeneration::FIRST
|
|
||||||
|| meta.location.archetype_id != ArchetypeId::INVALID
|
|
||||||
{
|
|
||||||
meta.spawned_or_despawned.at.check_tick(change_tick);
|
meta.spawned_or_despawned.at.check_tick(change_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a message explaining why an entity does not exist, if known.
|
/// Constructs a message explaining why an entity does not exist, if known.
|
||||||
pub(crate) fn entity_does_not_exist_error_details(
|
pub(crate) fn entity_does_not_exist_error_details(
|
||||||
@ -1267,9 +1268,9 @@ impl fmt::Display for EntityDoesNotExistDetails {
|
|||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
struct EntityMeta {
|
struct EntityMeta {
|
||||||
/// The current [`EntityGeneration`] of the [`EntityRow`].
|
/// The current [`EntityGeneration`] of the [`EntityRow`].
|
||||||
pub generation: EntityGeneration,
|
generation: EntityGeneration,
|
||||||
/// The current location of the [`EntityRow`].
|
/// The current location of the [`EntityRow`].
|
||||||
pub location: EntityLocation,
|
location: EntityIdLocation,
|
||||||
/// Location and tick of the last spawn, despawn or flush of this entity.
|
/// Location and tick of the last spawn, despawn or flush of this entity.
|
||||||
spawned_or_despawned: SpawnedOrDespawned,
|
spawned_or_despawned: SpawnedOrDespawned,
|
||||||
}
|
}
|
||||||
@ -1284,7 +1285,7 @@ impl EntityMeta {
|
|||||||
/// meta for **pending entity**
|
/// meta for **pending entity**
|
||||||
const EMPTY: EntityMeta = EntityMeta {
|
const EMPTY: EntityMeta = EntityMeta {
|
||||||
generation: EntityGeneration::FIRST,
|
generation: EntityGeneration::FIRST,
|
||||||
location: EntityLocation::INVALID,
|
location: None,
|
||||||
spawned_or_despawned: SpawnedOrDespawned {
|
spawned_or_despawned: SpawnedOrDespawned {
|
||||||
by: MaybeLocation::caller(),
|
by: MaybeLocation::caller(),
|
||||||
at: Tick::new(0),
|
at: Tick::new(0),
|
||||||
@ -1316,15 +1317,15 @@ pub struct EntityLocation {
|
|||||||
pub table_row: TableRow,
|
pub table_row: TableRow,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EntityLocation {
|
/// An [`Entity`] id may or may not correspond to a valid conceptual entity.
|
||||||
/// location for **pending entity** and **invalid entity**
|
/// If it does, the conceptual entity may or may not have a location.
|
||||||
pub(crate) const INVALID: EntityLocation = EntityLocation {
|
/// If it has no location, the [`EntityLocation`] will be `None`.
|
||||||
archetype_id: ArchetypeId::INVALID,
|
/// An location of `None` means the entity effectively does not exist; it has an id, but is not participating in the ECS.
|
||||||
archetype_row: ArchetypeRow::INVALID,
|
/// This is different from a location in the empty archetype, which is participating (queryable, etc) but just happens to have no components.
|
||||||
table_id: TableId::INVALID,
|
///
|
||||||
table_row: TableRow::INVALID,
|
/// Setting a location to `None` is often helpful when you want to destruct an entity or yank it from the ECS without allowing another system to reuse the id for something else.
|
||||||
};
|
/// It is also useful for reserving an id; commands will often allocate an `Entity` but not provide it a location until the command is applied.
|
||||||
}
|
pub type EntityIdLocation = Option<EntityLocation>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -36,8 +36,6 @@ mod column;
|
|||||||
pub struct TableId(u32);
|
pub struct TableId(u32);
|
||||||
|
|
||||||
impl TableId {
|
impl TableId {
|
||||||
pub(crate) const INVALID: TableId = TableId(u32::MAX);
|
|
||||||
|
|
||||||
/// Creates a new [`TableId`].
|
/// Creates a new [`TableId`].
|
||||||
///
|
///
|
||||||
/// `index` *must* be retrieved from calling [`TableId::as_u32`] on a `TableId` you got
|
/// `index` *must* be retrieved from calling [`TableId::as_u32`] on a `TableId` you got
|
||||||
@ -105,9 +103,6 @@ impl TableId {
|
|||||||
pub struct TableRow(NonMaxU32);
|
pub struct TableRow(NonMaxU32);
|
||||||
|
|
||||||
impl TableRow {
|
impl TableRow {
|
||||||
// TODO: Deprecate in favor of options, since `INVALID` is, technically, valid.
|
|
||||||
pub(crate) const INVALID: TableRow = TableRow(NonMaxU32::MAX);
|
|
||||||
|
|
||||||
/// Creates a [`TableRow`].
|
/// Creates a [`TableRow`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new(index: NonMaxU32) -> Self {
|
pub const fn new(index: NonMaxU32) -> Self {
|
||||||
|
@ -220,7 +220,7 @@ unsafe impl WorldEntityFetch for Entity {
|
|||||||
// SAFETY: caller ensures that the world cell has mutable access to the entity.
|
// SAFETY: caller ensures that the world cell has mutable access to the entity.
|
||||||
let world = unsafe { cell.world_mut() };
|
let world = unsafe { cell.world_mut() };
|
||||||
// SAFETY: location was fetched from the same world's `Entities`.
|
// SAFETY: location was fetched from the same world's `Entities`.
|
||||||
Ok(unsafe { EntityWorldMut::new(world, self, location) })
|
Ok(unsafe { EntityWorldMut::new(world, self, Some(location)) })
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn fetch_deferred_mut(
|
unsafe fn fetch_deferred_mut(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
archetype::{Archetype, ArchetypeId},
|
archetype::Archetype,
|
||||||
bundle::{
|
bundle::{
|
||||||
Bundle, BundleEffect, BundleFromComponents, BundleInserter, BundleRemover, DynamicBundle,
|
Bundle, BundleEffect, BundleFromComponents, BundleInserter, BundleRemover, DynamicBundle,
|
||||||
InsertMode,
|
InsertMode,
|
||||||
@ -10,7 +10,8 @@ use crate::{
|
|||||||
StorageType, Tick,
|
StorageType, Tick,
|
||||||
},
|
},
|
||||||
entity::{
|
entity::{
|
||||||
ContainsEntity, Entity, EntityCloner, EntityClonerBuilder, EntityEquivalent, EntityLocation,
|
ContainsEntity, Entity, EntityCloner, EntityClonerBuilder, EntityEquivalent,
|
||||||
|
EntityIdLocation, EntityLocation,
|
||||||
},
|
},
|
||||||
event::Event,
|
event::Event,
|
||||||
observer::Observer,
|
observer::Observer,
|
||||||
@ -1096,7 +1097,7 @@ unsafe impl EntityEquivalent for EntityMut<'_> {}
|
|||||||
pub struct EntityWorldMut<'w> {
|
pub struct EntityWorldMut<'w> {
|
||||||
world: &'w mut World,
|
world: &'w mut World,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
location: EntityLocation,
|
location: EntityIdLocation,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'w> EntityWorldMut<'w> {
|
impl<'w> EntityWorldMut<'w> {
|
||||||
@ -1116,43 +1117,43 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn assert_not_despawned(&self) {
|
pub(crate) fn assert_not_despawned(&self) {
|
||||||
if self.location.archetype_id == ArchetypeId::INVALID {
|
if self.location.is_none() {
|
||||||
self.panic_despawned();
|
self.panic_despawned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_unsafe_entity_cell_readonly(&self) -> UnsafeEntityCell<'_> {
|
fn as_unsafe_entity_cell_readonly(&self) -> UnsafeEntityCell<'_> {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let last_change_tick = self.world.last_change_tick;
|
let last_change_tick = self.world.last_change_tick;
|
||||||
let change_tick = self.world.read_change_tick();
|
let change_tick = self.world.read_change_tick();
|
||||||
UnsafeEntityCell::new(
|
UnsafeEntityCell::new(
|
||||||
self.world.as_unsafe_world_cell_readonly(),
|
self.world.as_unsafe_world_cell_readonly(),
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
last_change_tick,
|
last_change_tick,
|
||||||
change_tick,
|
change_tick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn as_unsafe_entity_cell(&mut self) -> UnsafeEntityCell<'_> {
|
fn as_unsafe_entity_cell(&mut self) -> UnsafeEntityCell<'_> {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let last_change_tick = self.world.last_change_tick;
|
let last_change_tick = self.world.last_change_tick;
|
||||||
let change_tick = self.world.change_tick();
|
let change_tick = self.world.change_tick();
|
||||||
UnsafeEntityCell::new(
|
UnsafeEntityCell::new(
|
||||||
self.world.as_unsafe_world_cell(),
|
self.world.as_unsafe_world_cell(),
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
last_change_tick,
|
last_change_tick,
|
||||||
change_tick,
|
change_tick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn into_unsafe_entity_cell(self) -> UnsafeEntityCell<'w> {
|
fn into_unsafe_entity_cell(self) -> UnsafeEntityCell<'w> {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let last_change_tick = self.world.last_change_tick;
|
let last_change_tick = self.world.last_change_tick;
|
||||||
let change_tick = self.world.change_tick();
|
let change_tick = self.world.change_tick();
|
||||||
UnsafeEntityCell::new(
|
UnsafeEntityCell::new(
|
||||||
self.world.as_unsafe_world_cell(),
|
self.world.as_unsafe_world_cell(),
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
last_change_tick,
|
last_change_tick,
|
||||||
change_tick,
|
change_tick,
|
||||||
)
|
)
|
||||||
@ -1168,10 +1169,10 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
pub(crate) unsafe fn new(
|
pub(crate) unsafe fn new(
|
||||||
world: &'w mut World,
|
world: &'w mut World,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
location: EntityLocation,
|
location: Option<EntityLocation>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert!(world.entities().contains(entity));
|
debug_assert!(world.entities().contains(entity));
|
||||||
debug_assert_eq!(world.entities().get(entity), Some(location));
|
debug_assert_eq!(world.entities().get(entity), location);
|
||||||
|
|
||||||
EntityWorldMut {
|
EntityWorldMut {
|
||||||
world,
|
world,
|
||||||
@ -1216,8 +1217,10 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn location(&self) -> EntityLocation {
|
pub fn location(&self) -> EntityLocation {
|
||||||
self.assert_not_despawned();
|
match self.location {
|
||||||
self.location
|
Some(loc) => loc,
|
||||||
|
None => self.panic_despawned(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the archetype that the current entity belongs to.
|
/// Returns the archetype that the current entity belongs to.
|
||||||
@ -1227,8 +1230,8 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn archetype(&self) -> &Archetype {
|
pub fn archetype(&self) -> &Archetype {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
&self.world.archetypes[self.location.archetype_id]
|
&self.world.archetypes[location.archetype_id]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if the current entity has a component of type `T`.
|
/// Returns `true` if the current entity has a component of type `T`.
|
||||||
@ -1830,22 +1833,22 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
relationship_hook_mode: RelationshipHookMode,
|
relationship_hook_mode: RelationshipHookMode,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let change_tick = self.world.change_tick();
|
let change_tick = self.world.change_tick();
|
||||||
let mut bundle_inserter =
|
let mut bundle_inserter =
|
||||||
BundleInserter::new::<T>(self.world, self.location.archetype_id, change_tick);
|
BundleInserter::new::<T>(self.world, location.archetype_id, change_tick);
|
||||||
// SAFETY: location matches current entity. `T` matches `bundle_info`
|
// SAFETY: location matches current entity. `T` matches `bundle_info`
|
||||||
let (location, after_effect) = unsafe {
|
let (location, after_effect) = unsafe {
|
||||||
bundle_inserter.insert(
|
bundle_inserter.insert(
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
bundle,
|
bundle,
|
||||||
mode,
|
mode,
|
||||||
caller,
|
caller,
|
||||||
relationship_hook_mode,
|
relationship_hook_mode,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
self.location = location;
|
self.location = Some(location);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
after_effect.apply(self);
|
after_effect.apply(self);
|
||||||
@ -1894,7 +1897,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
relationship_hook_insert_mode: RelationshipHookMode,
|
relationship_hook_insert_mode: RelationshipHookMode,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let change_tick = self.world.change_tick();
|
let change_tick = self.world.change_tick();
|
||||||
let bundle_id = self.world.bundles.init_component_info(
|
let bundle_id = self.world.bundles.init_component_info(
|
||||||
&mut self.world.storages,
|
&mut self.world.storages,
|
||||||
@ -1903,23 +1906,19 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
);
|
);
|
||||||
let storage_type = self.world.bundles.get_storage_unchecked(bundle_id);
|
let storage_type = self.world.bundles.get_storage_unchecked(bundle_id);
|
||||||
|
|
||||||
let bundle_inserter = BundleInserter::new_with_id(
|
let bundle_inserter =
|
||||||
self.world,
|
BundleInserter::new_with_id(self.world, location.archetype_id, bundle_id, change_tick);
|
||||||
self.location.archetype_id,
|
|
||||||
bundle_id,
|
|
||||||
change_tick,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.location = insert_dynamic_bundle(
|
self.location = Some(insert_dynamic_bundle(
|
||||||
bundle_inserter,
|
bundle_inserter,
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
Some(component).into_iter(),
|
Some(component).into_iter(),
|
||||||
Some(storage_type).iter().cloned(),
|
Some(storage_type).iter().cloned(),
|
||||||
mode,
|
mode,
|
||||||
caller,
|
caller,
|
||||||
relationship_hook_insert_mode,
|
relationship_hook_insert_mode,
|
||||||
);
|
));
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
self
|
self
|
||||||
@ -1957,7 +1956,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
iter_components: I,
|
iter_components: I,
|
||||||
relationship_hook_insert_mode: RelationshipHookMode,
|
relationship_hook_insert_mode: RelationshipHookMode,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let change_tick = self.world.change_tick();
|
let change_tick = self.world.change_tick();
|
||||||
let bundle_id = self.world.bundles.init_dynamic_info(
|
let bundle_id = self.world.bundles.init_dynamic_info(
|
||||||
&mut self.world.storages,
|
&mut self.world.storages,
|
||||||
@ -1966,23 +1965,19 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
);
|
);
|
||||||
let mut storage_types =
|
let mut storage_types =
|
||||||
core::mem::take(self.world.bundles.get_storages_unchecked(bundle_id));
|
core::mem::take(self.world.bundles.get_storages_unchecked(bundle_id));
|
||||||
let bundle_inserter = BundleInserter::new_with_id(
|
let bundle_inserter =
|
||||||
self.world,
|
BundleInserter::new_with_id(self.world, location.archetype_id, bundle_id, change_tick);
|
||||||
self.location.archetype_id,
|
|
||||||
bundle_id,
|
|
||||||
change_tick,
|
|
||||||
);
|
|
||||||
|
|
||||||
self.location = insert_dynamic_bundle(
|
self.location = Some(insert_dynamic_bundle(
|
||||||
bundle_inserter,
|
bundle_inserter,
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
iter_components,
|
iter_components,
|
||||||
(*storage_types).iter().cloned(),
|
(*storage_types).iter().cloned(),
|
||||||
InsertMode::Replace,
|
InsertMode::Replace,
|
||||||
MaybeLocation::caller(),
|
MaybeLocation::caller(),
|
||||||
relationship_hook_insert_mode,
|
relationship_hook_insert_mode,
|
||||||
);
|
));
|
||||||
*self.world.bundles.get_storages_unchecked(bundle_id) = core::mem::take(&mut storage_types);
|
*self.world.bundles.get_storages_unchecked(bundle_id) = core::mem::take(&mut storage_types);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
@ -2000,13 +1995,12 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn take<T: Bundle + BundleFromComponents>(&mut self) -> Option<T> {
|
pub fn take<T: Bundle + BundleFromComponents>(&mut self) -> Option<T> {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let entity = self.entity;
|
let entity = self.entity;
|
||||||
let location = self.location;
|
|
||||||
|
|
||||||
let mut remover =
|
let mut remover =
|
||||||
// SAFETY: The archetype id must be valid since this entity is in it.
|
// SAFETY: The archetype id must be valid since this entity is in it.
|
||||||
unsafe { BundleRemover::new::<T>(self.world, self.location.archetype_id, true) }?;
|
unsafe { BundleRemover::new::<T>(self.world, location.archetype_id, true) }?;
|
||||||
// SAFETY: The passed location has the sane archetype as the remover, since they came from the same location.
|
// SAFETY: The passed location has the sane archetype as the remover, since they came from the same location.
|
||||||
let (new_location, result) = unsafe {
|
let (new_location, result) = unsafe {
|
||||||
remover.remove(
|
remover.remove(
|
||||||
@ -2041,7 +2035,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
self.location = new_location;
|
self.location = Some(new_location);
|
||||||
|
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
@ -2062,11 +2056,11 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn remove_with_caller<T: Bundle>(&mut self, caller: MaybeLocation) -> &mut Self {
|
pub(crate) fn remove_with_caller<T: Bundle>(&mut self, caller: MaybeLocation) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
|
|
||||||
let Some(mut remover) =
|
let Some(mut remover) =
|
||||||
// SAFETY: The archetype id must be valid since this entity is in it.
|
// SAFETY: The archetype id must be valid since this entity is in it.
|
||||||
(unsafe { BundleRemover::new::<T>(self.world, self.location.archetype_id, false) })
|
(unsafe { BundleRemover::new::<T>(self.world, location.archetype_id, false) })
|
||||||
else {
|
else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
@ -2074,14 +2068,14 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
let new_location = unsafe {
|
let new_location = unsafe {
|
||||||
remover.remove(
|
remover.remove(
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
caller,
|
caller,
|
||||||
BundleRemover::empty_pre_remove,
|
BundleRemover::empty_pre_remove,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
self.location = new_location;
|
self.location = Some(new_location);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
self
|
self
|
||||||
@ -2101,7 +2095,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let storages = &mut self.world.storages;
|
let storages = &mut self.world.storages;
|
||||||
let bundles = &mut self.world.bundles;
|
let bundles = &mut self.world.bundles;
|
||||||
// SAFETY: These come from the same world.
|
// SAFETY: These come from the same world.
|
||||||
@ -2112,7 +2106,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
||||||
let Some(mut remover) = (unsafe {
|
let Some(mut remover) = (unsafe {
|
||||||
BundleRemover::new_with_id(self.world, self.location.archetype_id, bundle_id, false)
|
BundleRemover::new_with_id(self.world, location.archetype_id, bundle_id, false)
|
||||||
}) else {
|
}) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
@ -2120,14 +2114,14 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
let new_location = unsafe {
|
let new_location = unsafe {
|
||||||
remover.remove(
|
remover.remove(
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
caller,
|
caller,
|
||||||
BundleRemover::empty_pre_remove,
|
BundleRemover::empty_pre_remove,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
self.location = new_location;
|
self.location = Some(new_location);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
self
|
self
|
||||||
@ -2147,7 +2141,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn retain_with_caller<T: Bundle>(&mut self, caller: MaybeLocation) -> &mut Self {
|
pub(crate) fn retain_with_caller<T: Bundle>(&mut self, caller: MaybeLocation) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let old_location = self.location();
|
||||||
let archetypes = &mut self.world.archetypes;
|
let archetypes = &mut self.world.archetypes;
|
||||||
let storages = &mut self.world.storages;
|
let storages = &mut self.world.storages;
|
||||||
// SAFETY: These come from the same world.
|
// SAFETY: These come from the same world.
|
||||||
@ -2161,7 +2155,6 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
.register_info::<T>(&mut registrator, storages);
|
.register_info::<T>(&mut registrator, storages);
|
||||||
// SAFETY: `retained_bundle` exists as we just initialized it.
|
// SAFETY: `retained_bundle` exists as we just initialized it.
|
||||||
let retained_bundle_info = unsafe { self.world.bundles.get_unchecked(retained_bundle) };
|
let retained_bundle_info = unsafe { self.world.bundles.get_unchecked(retained_bundle) };
|
||||||
let old_location = self.location;
|
|
||||||
let old_archetype = &mut archetypes[old_location.archetype_id];
|
let old_archetype = &mut archetypes[old_location.archetype_id];
|
||||||
|
|
||||||
// PERF: this could be stored in an Archetype Edge
|
// PERF: this could be stored in an Archetype Edge
|
||||||
@ -2176,7 +2169,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
||||||
let Some(mut remover) = (unsafe {
|
let Some(mut remover) = (unsafe {
|
||||||
BundleRemover::new_with_id(self.world, self.location.archetype_id, remove_bundle, false)
|
BundleRemover::new_with_id(self.world, old_location.archetype_id, remove_bundle, false)
|
||||||
}) else {
|
}) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
@ -2184,14 +2177,14 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
let new_location = unsafe {
|
let new_location = unsafe {
|
||||||
remover.remove(
|
remover.remove(
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
old_location,
|
||||||
caller,
|
caller,
|
||||||
BundleRemover::empty_pre_remove,
|
BundleRemover::empty_pre_remove,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
self.location = new_location;
|
self.location = Some(new_location);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
self
|
self
|
||||||
@ -2216,7 +2209,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
component_id: ComponentId,
|
component_id: ComponentId,
|
||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let components = &mut self.world.components;
|
let components = &mut self.world.components;
|
||||||
|
|
||||||
let bundle_id = self.world.bundles.init_component_info(
|
let bundle_id = self.world.bundles.init_component_info(
|
||||||
@ -2227,7 +2220,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
||||||
let Some(mut remover) = (unsafe {
|
let Some(mut remover) = (unsafe {
|
||||||
BundleRemover::new_with_id(self.world, self.location.archetype_id, bundle_id, false)
|
BundleRemover::new_with_id(self.world, location.archetype_id, bundle_id, false)
|
||||||
}) else {
|
}) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
@ -2235,14 +2228,14 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
let new_location = unsafe {
|
let new_location = unsafe {
|
||||||
remover.remove(
|
remover.remove(
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
caller,
|
caller,
|
||||||
BundleRemover::empty_pre_remove,
|
BundleRemover::empty_pre_remove,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
self.location = new_location;
|
self.location = Some(new_location);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
self
|
self
|
||||||
@ -2258,7 +2251,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
/// entity has been despawned while this `EntityWorldMut` is still alive.
|
/// entity has been despawned while this `EntityWorldMut` is still alive.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn remove_by_ids(&mut self, component_ids: &[ComponentId]) -> &mut Self {
|
pub fn remove_by_ids(&mut self, component_ids: &[ComponentId]) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let components = &mut self.world.components;
|
let components = &mut self.world.components;
|
||||||
|
|
||||||
let bundle_id = self.world.bundles.init_dynamic_info(
|
let bundle_id = self.world.bundles.init_dynamic_info(
|
||||||
@ -2269,7 +2262,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
||||||
let Some(mut remover) = (unsafe {
|
let Some(mut remover) = (unsafe {
|
||||||
BundleRemover::new_with_id(self.world, self.location.archetype_id, bundle_id, false)
|
BundleRemover::new_with_id(self.world, location.archetype_id, bundle_id, false)
|
||||||
}) else {
|
}) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
@ -2277,14 +2270,14 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
let new_location = unsafe {
|
let new_location = unsafe {
|
||||||
remover.remove(
|
remover.remove(
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
MaybeLocation::caller(),
|
MaybeLocation::caller(),
|
||||||
BundleRemover::empty_pre_remove,
|
BundleRemover::empty_pre_remove,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
self.location = new_location;
|
self.location = Some(new_location);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
self
|
self
|
||||||
@ -2302,7 +2295,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn clear_with_caller(&mut self, caller: MaybeLocation) -> &mut Self {
|
pub(crate) fn clear_with_caller(&mut self, caller: MaybeLocation) -> &mut Self {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let component_ids: Vec<ComponentId> = self.archetype().components().collect();
|
let component_ids: Vec<ComponentId> = self.archetype().components().collect();
|
||||||
let components = &mut self.world.components;
|
let components = &mut self.world.components;
|
||||||
|
|
||||||
@ -2314,7 +2307,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
|
|
||||||
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
// SAFETY: We just created the bundle, and the archetype is valid, since we are in it.
|
||||||
let Some(mut remover) = (unsafe {
|
let Some(mut remover) = (unsafe {
|
||||||
BundleRemover::new_with_id(self.world, self.location.archetype_id, bundle_id, false)
|
BundleRemover::new_with_id(self.world, location.archetype_id, bundle_id, false)
|
||||||
}) else {
|
}) else {
|
||||||
return self;
|
return self;
|
||||||
};
|
};
|
||||||
@ -2322,14 +2315,14 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
let new_location = unsafe {
|
let new_location = unsafe {
|
||||||
remover.remove(
|
remover.remove(
|
||||||
self.entity,
|
self.entity,
|
||||||
self.location,
|
location,
|
||||||
caller,
|
caller,
|
||||||
BundleRemover::empty_pre_remove,
|
BundleRemover::empty_pre_remove,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
self.location = new_location;
|
self.location = Some(new_location);
|
||||||
self.world.flush();
|
self.world.flush();
|
||||||
self.update_location();
|
self.update_location();
|
||||||
self
|
self
|
||||||
@ -2353,9 +2346,9 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn despawn_with_caller(self, caller: MaybeLocation) {
|
pub(crate) fn despawn_with_caller(self, caller: MaybeLocation) {
|
||||||
self.assert_not_despawned();
|
let location = self.location();
|
||||||
let world = self.world;
|
let world = self.world;
|
||||||
let archetype = &world.archetypes[self.location.archetype_id];
|
let archetype = &world.archetypes[location.archetype_id];
|
||||||
|
|
||||||
// SAFETY: Archetype cannot be mutably aliased by DeferredWorld
|
// SAFETY: Archetype cannot be mutably aliased by DeferredWorld
|
||||||
let (archetype, mut deferred_world) = unsafe {
|
let (archetype, mut deferred_world) = unsafe {
|
||||||
@ -2422,13 +2415,14 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
let location = world
|
let location = world
|
||||||
.entities
|
.entities
|
||||||
.free(self.entity)
|
.free(self.entity)
|
||||||
|
.flatten()
|
||||||
.expect("entity should exist at this point.");
|
.expect("entity should exist at this point.");
|
||||||
let table_row;
|
let table_row;
|
||||||
let moved_entity;
|
let moved_entity;
|
||||||
let change_tick = world.change_tick();
|
let change_tick = world.change_tick();
|
||||||
|
|
||||||
{
|
{
|
||||||
let archetype = &mut world.archetypes[self.location.archetype_id];
|
let archetype = &mut world.archetypes[location.archetype_id];
|
||||||
let remove_result = archetype.swap_remove(location.archetype_row);
|
let remove_result = archetype.swap_remove(location.archetype_row);
|
||||||
if let Some(swapped_entity) = remove_result.swapped_entity {
|
if let Some(swapped_entity) = remove_result.swapped_entity {
|
||||||
let swapped_location = world.entities.get(swapped_entity).unwrap();
|
let swapped_location = world.entities.get(swapped_entity).unwrap();
|
||||||
@ -2437,12 +2431,12 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
world.entities.set(
|
world.entities.set(
|
||||||
swapped_entity.index(),
|
swapped_entity.index(),
|
||||||
EntityLocation {
|
Some(EntityLocation {
|
||||||
archetype_id: swapped_location.archetype_id,
|
archetype_id: swapped_location.archetype_id,
|
||||||
archetype_row: location.archetype_row,
|
archetype_row: location.archetype_row,
|
||||||
table_id: swapped_location.table_id,
|
table_id: swapped_location.table_id,
|
||||||
table_row: swapped_location.table_row,
|
table_row: swapped_location.table_row,
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
world
|
world
|
||||||
.entities
|
.entities
|
||||||
@ -2469,12 +2463,12 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
unsafe {
|
unsafe {
|
||||||
world.entities.set(
|
world.entities.set(
|
||||||
moved_entity.index(),
|
moved_entity.index(),
|
||||||
EntityLocation {
|
Some(EntityLocation {
|
||||||
archetype_id: moved_location.archetype_id,
|
archetype_id: moved_location.archetype_id,
|
||||||
archetype_row: moved_location.archetype_row,
|
archetype_row: moved_location.archetype_row,
|
||||||
table_id: moved_location.table_id,
|
table_id: moved_location.table_id,
|
||||||
table_row,
|
table_row,
|
||||||
},
|
}),
|
||||||
);
|
);
|
||||||
world
|
world
|
||||||
.entities
|
.entities
|
||||||
@ -2566,11 +2560,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
/// This is *only* required when using the unsafe function [`EntityWorldMut::world_mut`],
|
/// This is *only* required when using the unsafe function [`EntityWorldMut::world_mut`],
|
||||||
/// which enables the location to change.
|
/// which enables the location to change.
|
||||||
pub fn update_location(&mut self) {
|
pub fn update_location(&mut self) {
|
||||||
self.location = self
|
self.location = self.world.entities().get(self.entity);
|
||||||
.world
|
|
||||||
.entities()
|
|
||||||
.get(self.entity)
|
|
||||||
.unwrap_or(EntityLocation::INVALID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if the entity has been despawned.
|
/// Returns if the entity has been despawned.
|
||||||
@ -2582,7 +2572,7 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
/// to avoid panicking when calling further methods.
|
/// to avoid panicking when calling further methods.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_despawned(&self) -> bool {
|
pub fn is_despawned(&self) -> bool {
|
||||||
self.location.archetype_id == ArchetypeId::INVALID
|
self.location.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an Entry into the world for this entity and component for in-place manipulation.
|
/// Gets an Entry into the world for this entity and component for in-place manipulation.
|
||||||
|
@ -43,7 +43,7 @@ use crate::{
|
|||||||
ComponentTicks, Components, ComponentsQueuedRegistrator, ComponentsRegistrator, Mutable,
|
ComponentTicks, Components, ComponentsQueuedRegistrator, ComponentsRegistrator, Mutable,
|
||||||
RequiredComponents, RequiredComponentsError, Tick,
|
RequiredComponents, RequiredComponentsError, Tick,
|
||||||
},
|
},
|
||||||
entity::{Entities, Entity, EntityDoesNotExistError, EntityLocation},
|
entity::{Entities, Entity, EntityDoesNotExistError},
|
||||||
entity_disabling::DefaultQueryFilters,
|
entity_disabling::DefaultQueryFilters,
|
||||||
event::{Event, EventId, Events, SendBatchIds},
|
event::{Event, EventId, Events, SendBatchIds},
|
||||||
observer::Observers,
|
observer::Observers,
|
||||||
@ -1154,16 +1154,15 @@ impl World {
|
|||||||
let entity = self.entities.alloc();
|
let entity = self.entities.alloc();
|
||||||
let mut bundle_spawner = BundleSpawner::new::<B>(self, change_tick);
|
let mut bundle_spawner = BundleSpawner::new::<B>(self, change_tick);
|
||||||
// SAFETY: bundle's type matches `bundle_info`, entity is allocated but non-existent
|
// SAFETY: bundle's type matches `bundle_info`, entity is allocated but non-existent
|
||||||
let (mut entity_location, after_effect) =
|
let (entity_location, after_effect) =
|
||||||
unsafe { bundle_spawner.spawn_non_existent(entity, bundle, caller) };
|
unsafe { bundle_spawner.spawn_non_existent(entity, bundle, caller) };
|
||||||
|
|
||||||
|
let mut entity_location = Some(entity_location);
|
||||||
|
|
||||||
// SAFETY: command_queue is not referenced anywhere else
|
// SAFETY: command_queue is not referenced anywhere else
|
||||||
if !unsafe { self.command_queue.is_empty() } {
|
if !unsafe { self.command_queue.is_empty() } {
|
||||||
self.flush();
|
self.flush();
|
||||||
entity_location = self
|
entity_location = self.entities().get(entity);
|
||||||
.entities()
|
|
||||||
.get(entity)
|
|
||||||
.unwrap_or(EntityLocation::INVALID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: entity and location are valid, as they were just created above
|
// SAFETY: entity and location are valid, as they were just created above
|
||||||
@ -1186,11 +1185,11 @@ impl World {
|
|||||||
// empty
|
// empty
|
||||||
let location = unsafe { archetype.allocate(entity, table_row) };
|
let location = unsafe { archetype.allocate(entity, table_row) };
|
||||||
let change_tick = self.change_tick();
|
let change_tick = self.change_tick();
|
||||||
self.entities.set(entity.index(), location);
|
self.entities.set(entity.index(), Some(location));
|
||||||
self.entities
|
self.entities
|
||||||
.mark_spawn_despawn(entity.index(), caller, change_tick);
|
.mark_spawn_despawn(entity.index(), caller, change_tick);
|
||||||
|
|
||||||
EntityWorldMut::new(self, entity, location)
|
EntityWorldMut::new(self, entity, Some(location))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns a batch of entities with the same component [`Bundle`] type. Takes a given
|
/// Spawns a batch of entities with the same component [`Bundle`] type. Takes a given
|
||||||
@ -2725,7 +2724,7 @@ impl World {
|
|||||||
|entity, location| {
|
|entity, location| {
|
||||||
// SAFETY: no components are allocated by archetype.allocate() because the archetype
|
// SAFETY: no components are allocated by archetype.allocate() because the archetype
|
||||||
// is empty
|
// is empty
|
||||||
*location = empty_archetype.allocate(entity, table.allocate(entity));
|
*location = Some(empty_archetype.allocate(entity, table.allocate(entity)));
|
||||||
},
|
},
|
||||||
by,
|
by,
|
||||||
at,
|
at,
|
||||||
|
17
release-content/migration-guides/entities_apis.md
Normal file
17
release-content/migration-guides/entities_apis.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
title: Entities APIs
|
||||||
|
pull_requests: [19350, 19433]
|
||||||
|
---
|
||||||
|
|
||||||
|
`Entities::flush` now also asks for metadata about the flush operation
|
||||||
|
that will be stored for the flushed entities. For the source location,
|
||||||
|
`MaybeLocation::caller()` can be used; the tick should be retrieved
|
||||||
|
from the world.
|
||||||
|
|
||||||
|
Additionally, flush now gives `&mut EntityIdLocation` instead of `&mut EntityLocation` access.
|
||||||
|
`EntityIdLocation` is an alias for `Option<EntityLocation>`.
|
||||||
|
This replaces invalid locations with `None`.
|
||||||
|
It is possible for an `Entity` id to be allocated/reserved but not yet have a location.
|
||||||
|
This is used in commands for example, and this reality is more transparent with an `Option`.
|
||||||
|
This extends to other interfaces: `Entities::free` now returns `Option<EntityIdLocation>` instead of `Option<EntityLocation>`.
|
||||||
|
`Entities::get` remains unchanged, but you can access an `Entity`'s `EntityIdLocation` through the new `Entities::get_id_location`.
|
@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
title: Flushing
|
|
||||||
pull_requests: [19350]
|
|
||||||
---
|
|
||||||
|
|
||||||
`Entities::flush` now also asks for metadata about the flush operation
|
|
||||||
that will be stored for the flushed entities. For the source location,
|
|
||||||
`MaybeLocation::caller()` can be used; the tick should be retrieved
|
|
||||||
from the world.
|
|
Loading…
Reference in New Issue
Block a user