add world-level destruction

This commit is contained in:
Elliott Pierce 2025-05-31 12:43:32 -04:00
parent 5a69ebfbc0
commit 019c154d07
2 changed files with 64 additions and 43 deletions

View File

@ -31,8 +31,8 @@ pub struct TryInsertBatchError {
/// An error that occurs when a specified [`Entity`] could not be despawned. /// An error that occurs when a specified [`Entity`] could not be despawned.
#[derive(thiserror::Error, Debug, Clone, Copy)] #[derive(thiserror::Error, Debug, Clone, Copy)]
#[error("Could not despawn entity: {0}")] #[error("Could not destruct entity: {0}")]
pub struct EntityDespawnError(#[from] pub EntityMutableFetchError); pub struct EntityDestructError(#[from] pub EntityMutableFetchError);
/// An error that occurs when dynamically retrieving components from an entity. /// An error that occurs when dynamically retrieving components from an entity.
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)] #[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]

View File

@ -60,7 +60,7 @@ use crate::{
world::{ world::{
command_queue::RawCommandQueue, command_queue::RawCommandQueue,
error::{ error::{
EntityDespawnError, EntityMutableFetchError, TryInsertBatchError, TryRunScheduleError, EntityDestructError, EntityMutableFetchError, TryInsertBatchError, TryRunScheduleError,
}, },
}, },
}; };
@ -1086,42 +1086,6 @@ impl World {
self.allocator.alloc() self.allocator.alloc()
} }
/// This releases the entity to be reused by the allocator.
/// It despawns the entity as needed.
///
/// This is useful when the entity may not be constructed.
///
/// # Example
///
/// ```
/// # use bevy_ecs::{prelude::*};
/// let mut world = World::new();
/// let entity = world.spawn_null();
/// // The entity was never constructed.
/// world.release(entity);
/// ```
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));
}
/// Constructs the bundle on the entity. /// Constructs the bundle on the entity.
/// If the entity can not be constructed for any reason, returns an error. /// If the entity can not be constructed for any reason, returns an error.
/// ///
@ -1532,7 +1496,7 @@ impl World {
/// Despawns the given `entity`, if it exists. This will also remove all of the entity's /// Despawns the given `entity`, if it exists. This will also remove all of the entity's
/// [`Components`](Component). /// [`Components`](Component).
/// ///
/// Returns an [`EntityDespawnError`] if the entity does not exist. /// Returns an [`EntityDestructError`] if the entity does not exist.
/// ///
/// # Note /// # Note
/// ///
@ -1540,7 +1504,7 @@ impl World {
/// to despawn descendants. For example, this will recursively despawn [`Children`](crate::hierarchy::Children). /// to despawn descendants. For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
#[track_caller] #[track_caller]
#[inline] #[inline]
pub fn try_despawn(&mut self, entity: Entity) -> Result<(), EntityDespawnError> { pub fn try_despawn(&mut self, entity: Entity) -> Result<(), EntityDestructError> {
self.despawn_with_caller(entity, MaybeLocation::caller()) self.despawn_with_caller(entity, MaybeLocation::caller())
} }
@ -1549,13 +1513,70 @@ impl World {
&mut self, &mut self,
entity: Entity, entity: Entity,
caller: MaybeLocation, caller: MaybeLocation,
) -> Result<(), EntityDespawnError> { ) -> Result<(), EntityDestructError> {
self.flush();
let entity = self.get_entity_mut(entity)?; let entity = self.get_entity_mut(entity)?;
entity.despawn_with_caller(caller); entity.despawn_with_caller(caller);
Ok(()) Ok(())
} }
/// Performs [`try_destruct`](Self::try_destruct), warning on errors.
#[track_caller]
#[inline]
pub fn destruct(&mut self, entity: Entity) -> Option<EntityWorldMut<'_>> {
match self.destruct_with_caller(entity, MaybeLocation::caller()) {
Ok(entity) => Some(entity),
Err(error) => {
warn!("{error}");
None
}
}
}
/// Destructs the given `entity`, if it exists. This will also remove all of the entity's
/// [`Components`](Component).
/// The *only* difference between destructing and despawning an entity is that destructing does not release the `entity` to be reused.
/// It is up to the caller to either re-construct or fully despawn the `entity`; otherwise, the [`EntityRow`](crate::entity::EntityRow) will not be able to be reused.
///
/// Returns an [`EntityDestructError`] if the entity does not exist.
///
/// # Note
///
/// This will also *despawn* the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget) that is configured
/// to despawn descendants. For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
#[track_caller]
#[inline]
pub fn try_destruct(
&mut self,
entity: Entity,
) -> Result<EntityWorldMut<'_>, EntityDestructError> {
self.destruct_with_caller(entity, MaybeLocation::caller())
}
#[inline]
pub(crate) fn destruct_with_caller(
&mut self,
entity: Entity,
caller: MaybeLocation,
) -> Result<EntityWorldMut<'_>, EntityDestructError> {
let mut entity = self.get_entity_mut(entity)?;
entity.destruct_with_caller(caller);
Ok(entity)
}
/// 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));
}
/// Clears the internal component tracker state. /// Clears the internal component tracker state.
/// ///
/// The world maintains some internal state about changed and removed components. This state /// The world maintains some internal state about changed and removed components. This state