construct and destruct
This commit is contained in:
parent
65a7ea9f6a
commit
4c6f613a1d
@ -1737,7 +1737,7 @@ impl<'w> BundleSpawner<'w> {
|
|||||||
caller,
|
caller,
|
||||||
);
|
);
|
||||||
entities.set(entity.index(), Some(location));
|
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)
|
(location, after_effect)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -666,6 +666,13 @@ pub(crate) struct EntitiesAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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) {
|
pub(crate) fn free(&mut self, freed: Entity) {
|
||||||
self.free.truncate(*self.free_len.get_mut() as usize);
|
self.free.truncate(*self.free_len.get_mut() as usize);
|
||||||
self.free.push(freed);
|
self.free.push(freed);
|
||||||
@ -752,6 +759,11 @@ impl Entities {
|
|||||||
Self { meta: Vec::new() }
|
Self { meta: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clears all entity information
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.meta.clear()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the [`EntityLocation`] of an [`Entity`].
|
/// 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 pending entities and entities not participating in the ECS (entities with a [`EntityIdLocation`] of `None`), returns `None`.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -769,6 +781,25 @@ impl Entities {
|
|||||||
.map(|meta| meta.location)
|
.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`].
|
/// Updates the location of an [`EntityRow`].
|
||||||
/// This must be called when moving the components of the existing entity around in storage.
|
/// 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.
|
/// before handing control to unknown code.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn declare(&mut self, row: EntityRow, location: EntityIdLocation) {
|
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
|
#[cold] // to help with branch prediction
|
||||||
fn expand(meta: &mut Vec<EntityMeta>, len: usize) {
|
fn expand(meta: &mut Vec<EntityMeta>, len: usize) {
|
||||||
meta.resize(len, EntityMeta::EMPTY);
|
meta.resize(len, EntityMeta::EMPTY);
|
||||||
@ -801,44 +841,34 @@ impl Entities {
|
|||||||
// TODO: hint unlikely once stable.
|
// TODO: hint unlikely once stable.
|
||||||
expand(&mut self.meta, index + 1);
|
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`].
|
/// Marks the `row` as free, returning the [`Entity`] to reuse that [`EntityRow`].
|
||||||
pub(crate) fn mark_free(
|
///
|
||||||
&mut self,
|
/// # Safety
|
||||||
entity: Entity,
|
///
|
||||||
generations: u32,
|
/// - `row` must its [`EntityIdLocation`] set to `None`.
|
||||||
) -> Option<(EntityIdLocation, Entity)> {
|
pub(crate) unsafe fn mark_free(&mut self, row: EntityRow, generations: u32) -> Entity {
|
||||||
let meta = self.meta.get_mut(entity.index() as usize)?;
|
// We need to do this in case an entity is being freed that was never constructed.
|
||||||
if meta.generation != entity.generation {
|
self.ensure_row(row);
|
||||||
return None;
|
// 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);
|
let (new_generation, aliased) = meta.generation.after_versions_and_could_alias(generations);
|
||||||
meta.generation = new_generation;
|
meta.generation = new_generation;
|
||||||
if aliased {
|
if aliased {
|
||||||
warn!(
|
warn!("EntityRow({row}) generation wrapped on Entities::free, aliasing may occur",);
|
||||||
"Entity({}) generation wrapped on Entities::free, aliasing may occur",
|
|
||||||
entity.row()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let loc = meta.location.take();
|
Entity::from_raw_and_generation(row, meta.generation)
|
||||||
Some((
|
|
||||||
loc,
|
|
||||||
Entity::from_raw_and_generation(entity.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
|
/// # Safety
|
||||||
/// - `row` must have a [`EntityIdLocation`].
|
/// - `row` must have either been constructed or destructed, ensuring its row is valid.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) unsafe fn mark_spawn_despawn(
|
pub(crate) unsafe fn mark_construct_or_destruct(
|
||||||
&mut self,
|
&mut self,
|
||||||
row: EntityRow,
|
row: EntityRow,
|
||||||
by: MaybeLocation,
|
by: MaybeLocation,
|
||||||
@ -948,6 +978,11 @@ impl Entities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ConstructionError {
|
||||||
|
InvalidId,
|
||||||
|
AlreadyConstructed,
|
||||||
|
}
|
||||||
|
|
||||||
/// An error that occurs when a specified [`Entity`] does not exist.
|
/// An error that occurs when a specified [`Entity`] does not exist.
|
||||||
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[error("The entity with ID {entity} {details}")]
|
#[error("The entity with ID {entity} {details}")]
|
||||||
|
@ -316,7 +316,9 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
let tick = world.change_tick();
|
let tick = world.change_tick();
|
||||||
// SAFETY: Entity has been flushed
|
// SAFETY: Entity has been flushed
|
||||||
unsafe {
|
unsafe {
|
||||||
world.entities_mut().mark_spawn_despawn(index, caller, tick);
|
world
|
||||||
|
.entities_mut()
|
||||||
|
.mark_construct_or_destruct(index, caller, tick);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
entity_commands
|
entity_commands
|
||||||
@ -382,7 +384,9 @@ impl<'w, 's> Commands<'w, 's> {
|
|||||||
let tick = world.change_tick();
|
let tick = world.change_tick();
|
||||||
// SAFETY: Entity has been flushed
|
// SAFETY: Entity has been flushed
|
||||||
unsafe {
|
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,
|
StorageType, Tick,
|
||||||
},
|
},
|
||||||
entity::{
|
entity::{
|
||||||
ContainsEntity, Entity, EntityCloner, EntityClonerBuilder, EntityEquivalent,
|
ConstructionError, ContainsEntity, Entity, EntityCloner, EntityClonerBuilder,
|
||||||
EntityIdLocation, EntityLocation,
|
EntityEquivalent, EntityIdLocation, EntityLocation,
|
||||||
},
|
},
|
||||||
event::Event,
|
event::Event,
|
||||||
observer::Observer,
|
observer::Observer,
|
||||||
@ -1169,9 +1169,8 @@ 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: Option<EntityLocation>,
|
location: EntityIdLocation,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert!(world.entities().contains(entity));
|
|
||||||
debug_assert_eq!(world.entities().get(entity), location);
|
debug_assert_eq!(world.entities().get(entity), location);
|
||||||
|
|
||||||
EntityWorldMut {
|
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.
|
/// Returns the archetype that the current entity belongs to.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -2328,35 +2333,39 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
self
|
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]
|
#[track_caller]
|
||||||
pub fn despawn(self) {
|
pub fn construct<B: Bundle>(&mut self, bundle: B) -> Result<&mut Self, ConstructionError> {
|
||||||
self.despawn_with_caller(MaybeLocation::caller());
|
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) {
|
#[track_caller]
|
||||||
let location = self.location();
|
pub fn destruct(&mut self) -> &mut Self {
|
||||||
let world = self.world;
|
self.destruct_with_caller(MaybeLocation::caller())
|
||||||
let archetype = &world.archetypes[location.archetype_id];
|
}
|
||||||
|
|
||||||
|
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
|
// SAFETY: Archetype cannot be mutably aliased by DeferredWorld
|
||||||
let (archetype, mut deferred_world) = unsafe {
|
let (archetype, mut deferred_world) = unsafe {
|
||||||
let archetype: *const Archetype = archetype;
|
let archetype: *const Archetype = archetype;
|
||||||
let world = world.as_unsafe_world_cell();
|
let world = self.world.as_unsafe_world_cell();
|
||||||
(&*archetype, world.into_deferred())
|
(&*archetype, world.into_deferred())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Triggers
|
||||||
// SAFETY: All components in the archetype exist in world
|
// SAFETY: All components in the archetype exist in world
|
||||||
unsafe {
|
unsafe {
|
||||||
if archetype.has_despawn_observer() {
|
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() {
|
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 table_row;
|
||||||
let moved_entity;
|
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);
|
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 = self.world.entities.get(swapped_entity).unwrap();
|
||||||
// SAFETY: swapped_entity is valid and the swapped entity's components are
|
// SAFETY: swapped_entity is valid and the swapped entity's components are
|
||||||
// moved to the new location immediately after.
|
// moved to the new location immediately after.
|
||||||
unsafe {
|
unsafe {
|
||||||
world.entities.set(
|
self.world.entities.update(
|
||||||
swapped_entity.index(),
|
swapped_entity.row(),
|
||||||
Some(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,
|
||||||
@ -2438,31 +2447,34 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
table_row: swapped_location.table_row,
|
table_row: swapped_location.table_row,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
world
|
|
||||||
.entities
|
|
||||||
.mark_spawn_despawn(swapped_entity.index(), caller, change_tick);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
table_row = remove_result.table_row;
|
table_row = remove_result.table_row;
|
||||||
|
|
||||||
for component_id in archetype.sparse_set_components() {
|
for component_id in archetype.sparse_set_components() {
|
||||||
// set must have existed for the component to be added.
|
// 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);
|
sparse_set.remove(self.entity);
|
||||||
}
|
}
|
||||||
// SAFETY: table rows stored in archetypes always exist
|
// SAFETY: table rows stored in archetypes always exist
|
||||||
moved_entity = unsafe {
|
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 {
|
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
|
// SAFETY: `moved_entity` is valid and the provided `EntityLocation` accurately reflects
|
||||||
// the current location of the entity and its component data.
|
// the current location of the entity and its component data.
|
||||||
unsafe {
|
unsafe {
|
||||||
world.entities.set(
|
self.world.entities.update(
|
||||||
moved_entity.index(),
|
moved_entity.row(),
|
||||||
Some(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,
|
||||||
@ -2470,14 +2482,40 @@ impl<'w> EntityWorldMut<'w> {
|
|||||||
table_row,
|
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);
|
.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`]
|
/// 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")]
|
#[cfg(feature = "bevy_reflect")]
|
||||||
pub mod reflect;
|
pub mod reflect;
|
||||||
|
|
||||||
use crate::error::{DefaultErrorHandler, ErrorHandler};
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD},
|
change_detection::{Mut, Ref, CHECK_TICK_THRESHOLD},
|
||||||
world::command_queue::CommandQueue,
|
world::command_queue::CommandQueue,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
entity::{ConstructionError, EntitiesAllocator, EntityRow},
|
||||||
|
error::{DefaultErrorHandler, ErrorHandler},
|
||||||
|
};
|
||||||
pub use bevy_ecs_macros::FromWorld;
|
pub use bevy_ecs_macros::FromWorld;
|
||||||
pub use component_constants::*;
|
pub use component_constants::*;
|
||||||
pub use deferred_world::DeferredWorld;
|
pub use deferred_world::DeferredWorld;
|
||||||
@ -89,6 +92,7 @@ use unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell};
|
|||||||
pub struct World {
|
pub struct World {
|
||||||
id: WorldId,
|
id: WorldId,
|
||||||
pub(crate) entities: Entities,
|
pub(crate) entities: Entities,
|
||||||
|
pub(crate) allocator: EntitiesAllocator,
|
||||||
pub(crate) components: Components,
|
pub(crate) components: Components,
|
||||||
pub(crate) component_ids: ComponentIds,
|
pub(crate) component_ids: ComponentIds,
|
||||||
pub(crate) archetypes: Archetypes,
|
pub(crate) archetypes: Archetypes,
|
||||||
@ -108,6 +112,7 @@ impl Default for World {
|
|||||||
let mut world = Self {
|
let mut world = Self {
|
||||||
id: WorldId::new().expect("More `bevy` `World`s have been created than is supported"),
|
id: WorldId::new().expect("More `bevy` `World`s have been created than is supported"),
|
||||||
entities: Entities::new(),
|
entities: Entities::new(),
|
||||||
|
allocator: EntitiesAllocator::default(),
|
||||||
components: Default::default(),
|
components: Default::default(),
|
||||||
archetypes: Archetypes::new(),
|
archetypes: Archetypes::new(),
|
||||||
storages: Default::default(),
|
storages: Default::default(),
|
||||||
@ -1046,6 +1051,82 @@ impl World {
|
|||||||
(fetcher, commands)
|
(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
|
/// Spawns a new [`Entity`] and returns a corresponding [`EntityWorldMut`], which can be used
|
||||||
/// to add components to the entity or retrieve its id.
|
/// to add components to the entity or retrieve its id.
|
||||||
///
|
///
|
||||||
@ -1074,11 +1155,32 @@ impl World {
|
|||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn spawn_empty(&mut self) -> EntityWorldMut {
|
pub fn spawn_empty(&mut self) -> EntityWorldMut {
|
||||||
self.flush();
|
self.flush();
|
||||||
let entity = self.entities.alloc();
|
let entity = self.allocator.alloc();
|
||||||
// SAFETY: entity was just allocated
|
// SAFETY: entity was just allocated
|
||||||
unsafe { self.spawn_at_empty_internal(entity, MaybeLocation::caller()) }
|
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
|
/// 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
|
/// 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
|
/// retrieve its id. In case large batches of entities need to be spawned, consider using
|
||||||
@ -1149,47 +1251,9 @@ impl World {
|
|||||||
bundle: B,
|
bundle: B,
|
||||||
caller: MaybeLocation,
|
caller: MaybeLocation,
|
||||||
) -> EntityWorldMut {
|
) -> EntityWorldMut {
|
||||||
self.flush();
|
let entity = self.spawn_null();
|
||||||
let change_tick = self.change_tick();
|
// SAFETY: This was just spawned from null.
|
||||||
let entity = self.entities.alloc();
|
unsafe { self.construct_unchecked(entity, bundle, caller) }
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@ -2708,30 +2772,6 @@ impl World {
|
|||||||
.initialize_with(component_id, &self.components)
|
.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`].
|
/// Applies any commands in the world's internal [`CommandQueue`].
|
||||||
/// This does not apply commands from any systems, only those stored in the world.
|
/// This does not apply commands from any systems, only those stored in the world.
|
||||||
///
|
///
|
||||||
@ -2765,7 +2805,6 @@ impl World {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn flush(&mut self) {
|
pub fn flush(&mut self) {
|
||||||
self.flush_entities();
|
|
||||||
self.flush_components();
|
self.flush_components();
|
||||||
self.flush_commands();
|
self.flush_commands();
|
||||||
}
|
}
|
||||||
@ -2980,6 +3019,7 @@ impl World {
|
|||||||
self.storages.sparse_sets.clear_entities();
|
self.storages.sparse_sets.clear_entities();
|
||||||
self.archetypes.clear_entities();
|
self.archetypes.clear_entities();
|
||||||
self.entities.clear();
|
self.entities.clear();
|
||||||
|
self.allocator.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears all resources in this [`World`].
|
/// Clears all resources in this [`World`].
|
||||||
|
Loading…
Reference in New Issue
Block a user