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.
#[derive(thiserror::Error, Debug, Clone, Copy)]
#[error("Could not despawn entity: {0}")]
pub struct EntityDespawnError(#[from] pub EntityMutableFetchError);
#[error("Could not destruct entity: {0}")]
pub struct EntityDestructError(#[from] pub EntityMutableFetchError);
/// An error that occurs when dynamically retrieving components from an entity.
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]

View File

@ -60,7 +60,7 @@ use crate::{
world::{
command_queue::RawCommandQueue,
error::{
EntityDespawnError, EntityMutableFetchError, TryInsertBatchError, TryRunScheduleError,
EntityDestructError, EntityMutableFetchError, TryInsertBatchError, TryRunScheduleError,
},
},
};
@ -1086,42 +1086,6 @@ impl World {
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.
/// 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
/// [`Components`](Component).
///
/// Returns an [`EntityDespawnError`] if the entity does not exist.
/// Returns an [`EntityDestructError`] if the entity does not exist.
///
/// # Note
///
@ -1540,7 +1504,7 @@ impl World {
/// to despawn descendants. For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
#[track_caller]
#[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())
}
@ -1549,13 +1513,70 @@ impl World {
&mut self,
entity: Entity,
caller: MaybeLocation,
) -> Result<(), EntityDespawnError> {
self.flush();
) -> Result<(), EntityDestructError> {
let entity = self.get_entity_mut(entity)?;
entity.despawn_with_caller(caller);
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.
///
/// The world maintains some internal state about changed and removed components. This state