construct and destruct
This commit is contained in:
parent
65a7ea9f6a
commit
4c6f613a1d
@ -1737,7 +1737,7 @@ impl<'w> BundleSpawner<'w> {
|
||||
caller,
|
||||
);
|
||||
entities.set(entity.index(), Some(location));
|
||||
entities.mark_spawn_despawn(entity.index(), caller, self.change_tick);
|
||||
entities.mark_construct_or_destruct(entity.index(), caller, self.change_tick);
|
||||
(location, after_effect)
|
||||
};
|
||||
|
||||
|
@ -666,6 +666,13 @@ pub(crate) struct EntitiesAllocator {
|
||||
}
|
||||
|
||||
impl EntitiesAllocator {
|
||||
/// Restarts the allocator.
|
||||
pub(crate) fn restart(&mut self) {
|
||||
self.free.clear();
|
||||
*self.free_len.get_mut() = 0;
|
||||
*self.next_row.get_mut() = 0;
|
||||
}
|
||||
|
||||
pub(crate) fn free(&mut self, freed: Entity) {
|
||||
self.free.truncate(*self.free_len.get_mut() as usize);
|
||||
self.free.push(freed);
|
||||
@ -752,6 +759,11 @@ impl Entities {
|
||||
Self { meta: Vec::new() }
|
||||
}
|
||||
|
||||
/// Clears all entity information
|
||||
pub fn clear(&mut self) {
|
||||
self.meta.clear()
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
#[inline]
|
||||
@ -769,6 +781,25 @@ impl Entities {
|
||||
.map(|meta| meta.location)
|
||||
}
|
||||
|
||||
/// Provides information regarding if `entity` may be constructed.
|
||||
#[inline]
|
||||
pub fn validate_construction(&self, entity: Entity) -> Result<(), ConstructionError> {
|
||||
if self
|
||||
.resolve_from_id(entity.row())
|
||||
.is_some_and(|found| found.generation() != entity.generation())
|
||||
{
|
||||
Err(ConstructionError::InvalidId)
|
||||
} else if self
|
||||
.meta
|
||||
.get(entity.index() as usize)
|
||||
.is_some_and(|meta| meta.location.is_some())
|
||||
{
|
||||
Err(ConstructionError::AlreadyConstructed)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Updates the location of an [`EntityRow`].
|
||||
/// This must be called when moving the components of the existing entity around in storage.
|
||||
///
|
||||
@ -791,6 +822,15 @@ impl Entities {
|
||||
/// before handing control to unknown code.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn declare(&mut self, row: EntityRow, location: EntityIdLocation) {
|
||||
self.ensure_row(row);
|
||||
// SAFETY: We just did `ensure_row`
|
||||
let meta = unsafe { self.meta.get_unchecked_mut(row.index() as usize) };
|
||||
meta.location = location;
|
||||
}
|
||||
|
||||
/// Ensures row is valid.
|
||||
#[inline]
|
||||
fn ensure_row(&mut self, row: EntityRow) {
|
||||
#[cold] // to help with branch prediction
|
||||
fn expand(meta: &mut Vec<EntityMeta>, len: usize) {
|
||||
meta.resize(len, EntityMeta::EMPTY);
|
||||
@ -801,44 +841,34 @@ impl Entities {
|
||||
// TODO: hint unlikely once stable.
|
||||
expand(&mut self.meta, index + 1);
|
||||
}
|
||||
// SAFETY: We guarantee that `index` a valid entity index
|
||||
let meta = unsafe { self.meta.get_unchecked_mut(index) };
|
||||
meta.location = location;
|
||||
}
|
||||
|
||||
/// Marks the entity as free if it exists, returning its [`EntityIdLocation`] and the [`Entity`] to reuse that [`EntityRow`].
|
||||
pub(crate) fn mark_free(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
generations: u32,
|
||||
) -> Option<(EntityIdLocation, Entity)> {
|
||||
let meta = self.meta.get_mut(entity.index() as usize)?;
|
||||
if meta.generation != entity.generation {
|
||||
return None;
|
||||
}
|
||||
/// Marks the `row` as free, returning the [`Entity`] to reuse that [`EntityRow`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - `row` must its [`EntityIdLocation`] set to `None`.
|
||||
pub(crate) unsafe fn mark_free(&mut self, row: EntityRow, generations: u32) -> Entity {
|
||||
// We need to do this in case an entity is being freed that was never constructed.
|
||||
self.ensure_row(row);
|
||||
// SAFETY: We just did `ensure_row`
|
||||
let meta = unsafe { self.meta.get_unchecked_mut(row.index() as usize) };
|
||||
|
||||
let (new_generation, aliased) = meta.generation.after_versions_and_could_alias(generations);
|
||||
meta.generation = new_generation;
|
||||
if aliased {
|
||||
warn!(
|
||||
"Entity({}) generation wrapped on Entities::free, aliasing may occur",
|
||||
entity.row()
|
||||
);
|
||||
warn!("EntityRow({row}) generation wrapped on Entities::free, aliasing may occur",);
|
||||
}
|
||||
|
||||
let loc = meta.location.take();
|
||||
Some((
|
||||
loc,
|
||||
Entity::from_raw_and_generation(entity.row(), meta.generation),
|
||||
))
|
||||
Entity::from_raw_and_generation(row, meta.generation)
|
||||
}
|
||||
|
||||
/// Mark an [`EntityRow`] as spawned or despawned in the given tick.
|
||||
/// Mark an [`EntityRow`] as constructed or destructed in the given tick.
|
||||
///
|
||||
/// # Safety
|
||||
/// - `row` must have a [`EntityIdLocation`].
|
||||
/// - `row` must have either been constructed or destructed, ensuring its row is valid.
|
||||
#[inline]
|
||||
pub(crate) unsafe fn mark_spawn_despawn(
|
||||
pub(crate) unsafe fn mark_construct_or_destruct(
|
||||
&mut self,
|
||||
row: EntityRow,
|
||||
by: MaybeLocation,
|
||||
@ -948,6 +978,11 @@ impl Entities {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ConstructionError {
|
||||
InvalidId,
|
||||
AlreadyConstructed,
|
||||
}
|
||||
|
||||
/// An error that occurs when a specified [`Entity`] does not exist.
|
||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[error("The entity with ID {entity} {details}")]
|
||||
|
@ -316,7 +316,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
let tick = world.change_tick();
|
||||
// SAFETY: Entity has been flushed
|
||||
unsafe {
|
||||
world.entities_mut().mark_spawn_despawn(index, caller, tick);
|
||||
world
|
||||
.entities_mut()
|
||||
.mark_construct_or_destruct(index, caller, tick);
|
||||
}
|
||||
});
|
||||
entity_commands
|
||||
@ -382,7 +384,9 @@ impl<'w, 's> Commands<'w, 's> {
|
||||
let tick = world.change_tick();
|
||||
// SAFETY: Entity has been flushed
|
||||
unsafe {
|
||||
world.entities_mut().mark_spawn_despawn(index, caller, tick);
|
||||
world
|
||||
.entities_mut()
|
||||
.mark_construct_or_destruct(index, caller, tick);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -10,8 +10,8 @@ use crate::{
|
||||
StorageType, Tick,
|
||||
},
|
||||
entity::{
|
||||
ContainsEntity, Entity, EntityCloner, EntityClonerBuilder, EntityEquivalent,
|
||||
EntityIdLocation, EntityLocation,
|
||||
ConstructionError, ContainsEntity, Entity, EntityCloner, EntityClonerBuilder,
|
||||
EntityEquivalent, EntityIdLocation, EntityLocation,
|
||||
},
|
||||
event::Event,
|
||||
observer::Observer,
|
||||
@ -1169,9 +1169,8 @@ impl<'w> EntityWorldMut<'w> {
|
||||
pub(crate) unsafe fn new(
|
||||
world: &'w mut World,
|
||||
entity: Entity,
|
||||
location: Option<EntityLocation>,
|
||||
location: EntityIdLocation,
|
||||
) -> Self {
|
||||
debug_assert!(world.entities().contains(entity));
|
||||
debug_assert_eq!(world.entities().get(entity), location);
|
||||
|
||||
EntityWorldMut {
|
||||
@ -1223,6 +1222,12 @@ impl<'w> EntityWorldMut<'w> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether or not the entity is constructed.
|
||||
#[inline]
|
||||
pub fn is_constructed(&self) -> bool {
|
||||
self.location.is_some()
|
||||
}
|
||||
|
||||
/// Returns the archetype that the current entity belongs to.
|
||||
///
|
||||
/// # Panics
|
||||
@ -2328,35 +2333,39 @@ impl<'w> EntityWorldMut<'w> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Despawns the current entity.
|
||||
///
|
||||
/// See [`World::despawn`] for more details.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This will also despawn any [`Children`](crate::hierarchy::Children) entities, and any other [`RelationshipTarget`](crate::relationship::RelationshipTarget) that is configured
|
||||
/// to despawn descendants. This results in "recursive despawn" behavior.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
#[track_caller]
|
||||
pub fn despawn(self) {
|
||||
self.despawn_with_caller(MaybeLocation::caller());
|
||||
pub fn construct<B: Bundle>(&mut self, bundle: B) -> Result<&mut Self, ConstructionError> {
|
||||
let Self {
|
||||
world,
|
||||
entity,
|
||||
location,
|
||||
} = self;
|
||||
let found = world.construct(*entity, bundle)?;
|
||||
*location = found.location;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub(crate) fn despawn_with_caller(self, caller: MaybeLocation) {
|
||||
let location = self.location();
|
||||
let world = self.world;
|
||||
let archetype = &world.archetypes[location.archetype_id];
|
||||
#[track_caller]
|
||||
pub fn destruct(&mut self) -> &mut Self {
|
||||
self.destruct_with_caller(MaybeLocation::caller())
|
||||
}
|
||||
|
||||
pub(crate) fn destruct_with_caller(&mut self, caller: MaybeLocation) -> &mut Self {
|
||||
// setup
|
||||
let Some(location) = self.location else {
|
||||
// If there is no location, we are already destructed
|
||||
return self;
|
||||
};
|
||||
let archetype = &self.world.archetypes[location.archetype_id];
|
||||
|
||||
// SAFETY: Archetype cannot be mutably aliased by DeferredWorld
|
||||
let (archetype, mut deferred_world) = unsafe {
|
||||
let archetype: *const Archetype = archetype;
|
||||
let world = world.as_unsafe_world_cell();
|
||||
let world = self.world.as_unsafe_world_cell();
|
||||
(&*archetype, world.into_deferred())
|
||||
};
|
||||
|
||||
// Triggers
|
||||
// SAFETY: All components in the archetype exist in world
|
||||
unsafe {
|
||||
if archetype.has_despawn_observer() {
|
||||
@ -2404,33 +2413,33 @@ impl<'w> EntityWorldMut<'w> {
|
||||
);
|
||||
}
|
||||
|
||||
// do the destruct
|
||||
let change_tick = self.world.change_tick();
|
||||
for component_id in archetype.components() {
|
||||
world.removed_components.send(component_id, self.entity);
|
||||
self.world
|
||||
.removed_components
|
||||
.send(component_id, self.entity);
|
||||
}
|
||||
// SAFETY: Since we had a location, and it was valid, this is safe.
|
||||
unsafe {
|
||||
self.world.entities.update(self.entity.row(), None);
|
||||
self.world
|
||||
.entities
|
||||
.mark_construct_or_destruct(self.entity.row(), caller, change_tick);
|
||||
}
|
||||
|
||||
// Observers and on_remove hooks may reserve new entities, which
|
||||
// requires a flush before Entities::free may be called.
|
||||
world.flush_entities();
|
||||
|
||||
let location = world
|
||||
.entities
|
||||
.free(self.entity)
|
||||
.flatten()
|
||||
.expect("entity should exist at this point.");
|
||||
let table_row;
|
||||
let moved_entity;
|
||||
let change_tick = world.change_tick();
|
||||
|
||||
{
|
||||
let archetype = &mut world.archetypes[location.archetype_id];
|
||||
let archetype = &mut self.world.archetypes[location.archetype_id];
|
||||
let remove_result = archetype.swap_remove(location.archetype_row);
|
||||
if let Some(swapped_entity) = remove_result.swapped_entity {
|
||||
let swapped_location = world.entities.get(swapped_entity).unwrap();
|
||||
let swapped_location = self.world.entities.get(swapped_entity).unwrap();
|
||||
// SAFETY: swapped_entity is valid and the swapped entity's components are
|
||||
// moved to the new location immediately after.
|
||||
unsafe {
|
||||
world.entities.set(
|
||||
swapped_entity.index(),
|
||||
self.world.entities.update(
|
||||
swapped_entity.row(),
|
||||
Some(EntityLocation {
|
||||
archetype_id: swapped_location.archetype_id,
|
||||
archetype_row: location.archetype_row,
|
||||
@ -2438,31 +2447,34 @@ impl<'w> EntityWorldMut<'w> {
|
||||
table_row: swapped_location.table_row,
|
||||
}),
|
||||
);
|
||||
world
|
||||
.entities
|
||||
.mark_spawn_despawn(swapped_entity.index(), caller, change_tick);
|
||||
}
|
||||
}
|
||||
table_row = remove_result.table_row;
|
||||
|
||||
for component_id in archetype.sparse_set_components() {
|
||||
// set must have existed for the component to be added.
|
||||
let sparse_set = world.storages.sparse_sets.get_mut(component_id).unwrap();
|
||||
let sparse_set = self
|
||||
.world
|
||||
.storages
|
||||
.sparse_sets
|
||||
.get_mut(component_id)
|
||||
.unwrap();
|
||||
sparse_set.remove(self.entity);
|
||||
}
|
||||
// SAFETY: table rows stored in archetypes always exist
|
||||
moved_entity = unsafe {
|
||||
world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row)
|
||||
self.world.storages.tables[archetype.table_id()].swap_remove_unchecked(table_row)
|
||||
};
|
||||
};
|
||||
|
||||
// Handle displaced entity
|
||||
if let Some(moved_entity) = moved_entity {
|
||||
let moved_location = world.entities.get(moved_entity).unwrap();
|
||||
let moved_location = self.world.entities.get(moved_entity).unwrap();
|
||||
// SAFETY: `moved_entity` is valid and the provided `EntityLocation` accurately reflects
|
||||
// the current location of the entity and its component data.
|
||||
unsafe {
|
||||
world.entities.set(
|
||||
moved_entity.index(),
|
||||
self.world.entities.update(
|
||||
moved_entity.row(),
|
||||
Some(EntityLocation {
|
||||
archetype_id: moved_location.archetype_id,
|
||||
archetype_row: moved_location.archetype_row,
|
||||
@ -2470,14 +2482,40 @@ impl<'w> EntityWorldMut<'w> {
|
||||
table_row,
|
||||
}),
|
||||
);
|
||||
world
|
||||
.entities
|
||||
.mark_spawn_despawn(moved_entity.index(), caller, change_tick);
|
||||
}
|
||||
world.archetypes[moved_location.archetype_id]
|
||||
self.world.archetypes[moved_location.archetype_id]
|
||||
.set_entity_table_row(moved_location.archetype_row, table_row);
|
||||
}
|
||||
world.flush();
|
||||
|
||||
// finish
|
||||
self.world.flush();
|
||||
self
|
||||
}
|
||||
|
||||
/// Despawns the current entity.
|
||||
///
|
||||
/// See [`World::despawn`] for more details.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This will also despawn any [`Children`](crate::hierarchy::Children) entities, and any other [`RelationshipTarget`](crate::relationship::RelationshipTarget) that is configured
|
||||
/// to despawn descendants. This results in "recursive despawn" behavior.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the entity has been despawned while this `EntityWorldMut` is still alive.
|
||||
#[track_caller]
|
||||
pub fn despawn(self) {
|
||||
self.despawn_with_caller(MaybeLocation::caller());
|
||||
}
|
||||
|
||||
pub(crate) fn despawn_with_caller(mut self, caller: MaybeLocation) {
|
||||
self.destruct_with_caller(caller);
|
||||
// SAFETY: We just destructed.
|
||||
unsafe {
|
||||
self.world
|
||||
.release_generations_unchecked(self.entity.row(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures any commands triggered by the actions of Self are applied, equivalent to [`World::flush`]
|
||||
|
@ -14,11 +14,14 @@ pub mod unsafe_world_cell;
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
pub mod reflect;
|
||||
|
||||
use crate::error::{DefaultErrorHandler, ErrorHandler};
|
||||
pub use crate::{
|
||||
change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD},
|
||||
world::command_queue::CommandQueue,
|
||||
};
|
||||
use crate::{
|
||||
entity::{ConstructionError, EntitiesAllocator, EntityRow},
|
||||
error::{DefaultErrorHandler, ErrorHandler},
|
||||
};
|
||||
pub use bevy_ecs_macros::FromWorld;
|
||||
pub use component_constants::*;
|
||||
pub use deferred_world::DeferredWorld;
|
||||
@ -89,6 +92,7 @@ use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell};
|
||||
pub struct World {
|
||||
id: WorldId,
|
||||
pub(crate) entities: Entities,
|
||||
pub(crate) allocator: EntitiesAllocator,
|
||||
pub(crate) components: Components,
|
||||
pub(crate) component_ids: ComponentIds,
|
||||
pub(crate) archetypes: Archetypes,
|
||||
@ -108,6 +112,7 @@ impl Default for World {
|
||||
let mut world = Self {
|
||||
id: WorldId::new().expect("More `bevy` `World`s have been created than is supported"),
|
||||
entities: Entities::new(),
|
||||
allocator: EntitiesAllocator::default(),
|
||||
components: Default::default(),
|
||||
archetypes: Archetypes::new(),
|
||||
storages: Default::default(),
|
||||
@ -1046,6 +1051,82 @@ impl World {
|
||||
(fetcher, commands)
|
||||
}
|
||||
|
||||
/// Spawns an [`Entity`] that is void/null.
|
||||
/// The returned entity id is valid and unique, but it does not correspond to any conceptual entity.
|
||||
/// The conceptual entity does not exist, and using the id as if it did may produce errors.
|
||||
/// It can not be queried, and it has no [`EntityLocation`](crate::entity::EntityLocation).
|
||||
///
|
||||
/// This is different from empty entities, which do exist in the world;
|
||||
/// they just happen to have no components.
|
||||
pub fn spawn_null(&self) -> Entity {
|
||||
self.allocator.alloc()
|
||||
}
|
||||
|
||||
pub fn release(&mut self, entity: Entity) {
|
||||
let Ok(entity) = self.get_entity_mut(entity) else {
|
||||
return; // Already released then.
|
||||
};
|
||||
|
||||
entity.despawn();
|
||||
}
|
||||
|
||||
/// Releases `entity` to be reused.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// It must have been destructed.
|
||||
pub(crate) unsafe fn release_generations_unchecked(
|
||||
&mut self,
|
||||
entity: EntityRow,
|
||||
generations: u32,
|
||||
) {
|
||||
self.allocator
|
||||
.free(self.entities.mark_free(entity, generations));
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn construct<B: Bundle>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
bundle: B,
|
||||
) -> Result<EntityWorldMut<'_>, ConstructionError> {
|
||||
self.entities.validate_construction(entity)?;
|
||||
// SAFETY: We just ensured it was valid.
|
||||
Ok(unsafe { self.construct_unchecked(entity, bundle, MaybeLocation::caller()) })
|
||||
}
|
||||
|
||||
/// Constructs `bundle` on `entity`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `entity` must be valid and have no location.
|
||||
pub(crate) unsafe fn construct_unchecked<B: Bundle>(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
bundle: B,
|
||||
caller: MaybeLocation,
|
||||
) -> EntityWorldMut<'_> {
|
||||
self.flush();
|
||||
let change_tick = 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
|
||||
let (entity_location, after_effect) =
|
||||
unsafe { bundle_spawner.spawn_non_existent(entity, bundle, caller) };
|
||||
|
||||
let mut entity_location = Some(entity_location);
|
||||
|
||||
// SAFETY: command_queue is not referenced anywhere else
|
||||
if !unsafe { self.command_queue.is_empty() } {
|
||||
self.flush();
|
||||
entity_location = self.entities().get(entity);
|
||||
}
|
||||
|
||||
// SAFETY: entity and location are valid, as they were just created above
|
||||
let mut entity = unsafe { EntityWorldMut::new(self, entity, entity_location) };
|
||||
after_effect.apply(&mut entity);
|
||||
entity
|
||||
}
|
||||
|
||||
/// Spawns a new [`Entity`] and returns a corresponding [`EntityWorldMut`], which can be used
|
||||
/// to add components to the entity or retrieve its id.
|
||||
///
|
||||
@ -1074,11 +1155,32 @@ impl World {
|
||||
#[track_caller]
|
||||
pub fn spawn_empty(&mut self) -> EntityWorldMut {
|
||||
self.flush();
|
||||
let entity = self.entities.alloc();
|
||||
let entity = self.allocator.alloc();
|
||||
// SAFETY: entity was just allocated
|
||||
unsafe { self.spawn_at_empty_internal(entity, MaybeLocation::caller()) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// must be called on an entity that was just allocated
|
||||
unsafe fn spawn_at_empty_internal(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
caller: MaybeLocation,
|
||||
) -> EntityWorldMut {
|
||||
let archetype = self.archetypes.empty_mut();
|
||||
// PERF: consider avoiding allocating entities in the empty archetype unless needed
|
||||
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
|
||||
// SAFETY: no components are allocated by archetype.allocate() because the archetype is
|
||||
// empty
|
||||
let location = unsafe { archetype.allocate(entity, table_row) };
|
||||
let change_tick = self.change_tick();
|
||||
self.entities.declare(entity.row(), Some(location));
|
||||
self.entities
|
||||
.mark_construct_or_destruct(entity.row(), caller, change_tick);
|
||||
|
||||
EntityWorldMut::new(self, entity, Some(location))
|
||||
}
|
||||
|
||||
/// Spawns a new [`Entity`] with a given [`Bundle`] of [components](`Component`) and returns
|
||||
/// a corresponding [`EntityWorldMut`], which can be used to add components to the entity or
|
||||
/// retrieve its id. In case large batches of entities need to be spawned, consider using
|
||||
@ -1149,47 +1251,9 @@ impl World {
|
||||
bundle: B,
|
||||
caller: MaybeLocation,
|
||||
) -> EntityWorldMut {
|
||||
self.flush();
|
||||
let change_tick = self.change_tick();
|
||||
let entity = self.entities.alloc();
|
||||
let mut bundle_spawner = BundleSpawner::new::<B>(self, change_tick);
|
||||
// SAFETY: bundle's type matches `bundle_info`, entity is allocated but non-existent
|
||||
let (entity_location, after_effect) =
|
||||
unsafe { bundle_spawner.spawn_non_existent(entity, bundle, caller) };
|
||||
|
||||
let mut entity_location = Some(entity_location);
|
||||
|
||||
// SAFETY: command_queue is not referenced anywhere else
|
||||
if !unsafe { self.command_queue.is_empty() } {
|
||||
self.flush();
|
||||
entity_location = self.entities().get(entity);
|
||||
}
|
||||
|
||||
// SAFETY: entity and location are valid, as they were just created above
|
||||
let mut entity = unsafe { EntityWorldMut::new(self, entity, entity_location) };
|
||||
after_effect.apply(&mut entity);
|
||||
entity
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// must be called on an entity that was just allocated
|
||||
unsafe fn spawn_at_empty_internal(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
caller: MaybeLocation,
|
||||
) -> EntityWorldMut {
|
||||
let archetype = self.archetypes.empty_mut();
|
||||
// PERF: consider avoiding allocating entities in the empty archetype unless needed
|
||||
let table_row = self.storages.tables[archetype.table_id()].allocate(entity);
|
||||
// SAFETY: no components are allocated by archetype.allocate() because the archetype is
|
||||
// empty
|
||||
let location = unsafe { archetype.allocate(entity, table_row) };
|
||||
let change_tick = self.change_tick();
|
||||
self.entities.set(entity.index(), Some(location));
|
||||
self.entities
|
||||
.mark_spawn_despawn(entity.index(), caller, change_tick);
|
||||
|
||||
EntityWorldMut::new(self, entity, Some(location))
|
||||
let entity = self.spawn_null();
|
||||
// SAFETY: This was just spawned from null.
|
||||
unsafe { self.construct_unchecked(entity, bundle, caller) }
|
||||
}
|
||||
|
||||
/// Spawns a batch of entities with the same component [`Bundle`] type. Takes a given
|
||||
@ -2708,30 +2772,6 @@ impl World {
|
||||
.initialize_with(component_id, &self.components)
|
||||
}
|
||||
|
||||
/// Empties queued entities and adds them to the empty [`Archetype`](crate::archetype::Archetype).
|
||||
/// This should be called before doing operations that might operate on queued entities,
|
||||
/// such as inserting a [`Component`].
|
||||
#[track_caller]
|
||||
pub(crate) fn flush_entities(&mut self) {
|
||||
let by = MaybeLocation::caller();
|
||||
let at = self.change_tick();
|
||||
let empty_archetype = self.archetypes.empty_mut();
|
||||
let table = &mut self.storages.tables[empty_archetype.table_id()];
|
||||
// PERF: consider pre-allocating space for flushed entities
|
||||
// SAFETY: entity is set to a valid location
|
||||
unsafe {
|
||||
self.entities.flush(
|
||||
|entity, location| {
|
||||
// SAFETY: no components are allocated by archetype.allocate() because the archetype
|
||||
// is empty
|
||||
*location = Some(empty_archetype.allocate(entity, table.allocate(entity)));
|
||||
},
|
||||
by,
|
||||
at,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Applies any commands in the world's internal [`CommandQueue`].
|
||||
/// This does not apply commands from any systems, only those stored in the world.
|
||||
///
|
||||
@ -2765,7 +2805,6 @@ impl World {
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn flush(&mut self) {
|
||||
self.flush_entities();
|
||||
self.flush_components();
|
||||
self.flush_commands();
|
||||
}
|
||||
@ -2980,6 +3019,7 @@ impl World {
|
||||
self.storages.sparse_sets.clear_entities();
|
||||
self.archetypes.clear_entities();
|
||||
self.entities.clear();
|
||||
self.allocator.restart();
|
||||
}
|
||||
|
||||
/// Clears all resources in this [`World`].
|
||||
|
Loading…
Reference in New Issue
Block a user