entity cloning and mapping

This commit is contained in:
Elliott Pierce 2025-05-30 22:30:48 -04:00
parent 8556b190f3
commit f4e06add26
3 changed files with 17 additions and 40 deletions

View File

@ -8,12 +8,14 @@ use crate::{
archetype::Archetype, archetype::Archetype,
bundle::Bundle, bundle::Bundle,
component::{Component, ComponentCloneBehavior, ComponentCloneFn, ComponentId, ComponentInfo}, component::{Component, ComponentCloneBehavior, ComponentCloneFn, ComponentId, ComponentInfo},
entity::{hash_map::EntityHashMap, Entities, Entity, EntityMapper}, entity::{hash_map::EntityHashMap, Entity, EntityMapper},
query::DebugCheckedUnwrap, query::DebugCheckedUnwrap,
relationship::RelationshipHookMode, relationship::RelationshipHookMode,
world::World, world::World,
}; };
use super::EntitiesAllocator;
/// Provides read access to the source component (the component being cloned) in a [`ComponentCloneFn`]. /// Provides read access to the source component (the component being cloned) in a [`ComponentCloneFn`].
pub struct SourceComponent<'a> { pub struct SourceComponent<'a> {
ptr: Ptr<'a>, ptr: Ptr<'a>,
@ -76,7 +78,7 @@ pub struct ComponentCloneCtx<'a, 'b> {
target_component_written: bool, target_component_written: bool,
bundle_scratch: &'a mut BundleScratch<'b>, bundle_scratch: &'a mut BundleScratch<'b>,
bundle_scratch_allocator: &'b Bump, bundle_scratch_allocator: &'b Bump,
entities: &'a Entities, allocator: &'a EntitiesAllocator,
source: Entity, source: Entity,
target: Entity, target: Entity,
component_info: &'a ComponentInfo, component_info: &'a ComponentInfo,
@ -102,7 +104,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
target: Entity, target: Entity,
bundle_scratch_allocator: &'b Bump, bundle_scratch_allocator: &'b Bump,
bundle_scratch: &'a mut BundleScratch<'b>, bundle_scratch: &'a mut BundleScratch<'b>,
entities: &'a Entities, allocator: &'a EntitiesAllocator,
component_info: &'a ComponentInfo, component_info: &'a ComponentInfo,
entity_cloner: &'a mut EntityCloner, entity_cloner: &'a mut EntityCloner,
mapper: &'a mut dyn EntityMapper, mapper: &'a mut dyn EntityMapper,
@ -116,7 +118,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
bundle_scratch, bundle_scratch,
target_component_written: false, target_component_written: false,
bundle_scratch_allocator, bundle_scratch_allocator,
entities, allocator,
mapper, mapper,
component_info, component_info,
entity_cloner, entity_cloner,
@ -268,7 +270,7 @@ impl<'a, 'b> ComponentCloneCtx<'a, 'b> {
/// Queues the `entity` to be cloned by the current [`EntityCloner`] /// Queues the `entity` to be cloned by the current [`EntityCloner`]
pub fn queue_entity_clone(&mut self, entity: Entity) { pub fn queue_entity_clone(&mut self, entity: Entity) {
let target = self.entities.reserve_entity(); let target = self.allocator.alloc();
self.mapper.set_mapped(entity, target); self.mapper.set_mapped(entity, target);
self.entity_cloner.clone_queue.push_back(entity); self.entity_cloner.clone_queue.push_back(entity);
} }
@ -521,7 +523,7 @@ impl EntityCloner {
target, target,
&bundle_scratch_allocator, &bundle_scratch_allocator,
&mut bundle_scratch, &mut bundle_scratch,
world.entities(), world.entities_allocator(),
info, info,
self, self,
mapper, mapper,
@ -539,10 +541,6 @@ impl EntityCloner {
(deferred)(world, mapper); (deferred)(world, mapper);
} }
if !world.entities.contains(target) {
panic!("Target entity does not exist");
}
if self.move_components { if self.move_components {
world world
.entity_mut(source) .entity_mut(source)

View File

@ -53,7 +53,7 @@ use super::EntityIndexSet;
pub trait MapEntities { pub trait MapEntities {
/// Updates all [`Entity`] references stored inside using `entity_mapper`. /// Updates all [`Entity`] references stored inside using `entity_mapper`.
/// ///
/// Implementors should look up any and all [`Entity`] values stored within `self` and /// Implementers should look up any and all [`Entity`] values stored within `self` and
/// update them to the mapped values via `entity_mapper`. /// update them to the mapped values via `entity_mapper`.
fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E); fn map_entities<E: EntityMapper>(&mut self, entity_mapper: &mut E);
} }
@ -152,7 +152,7 @@ impl<T: MapEntities, A: smallvec::Array<Item = T>> MapEntities for SmallVec<A> {
/// ///
/// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World). /// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World).
/// ///
/// This is used by [`MapEntities`] implementors. /// This is used by [`MapEntities`] implementers.
/// ///
/// ## Example /// ## Example
/// ///
@ -286,14 +286,10 @@ impl<'m> SceneEntityMapper<'m> {
} }
/// Creates a new [`SceneEntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`] /// Creates a new [`SceneEntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
pub fn new(map: &'m mut EntityHashMap<Entity>, world: &mut World) -> Self { pub fn new(map: &'m mut EntityHashMap<Entity>, world: &World) -> Self {
// We're going to be calling methods on `Entities` that require advance
// flushing, such as `alloc` and `free`.
world.flush_entities();
Self { Self {
map, map,
// SAFETY: Entities data is kept in a valid state via `EntityMapper::world_scope` dead_start: world.allocator.alloc(),
dead_start: unsafe { world.entities_mut().alloc() },
generations: 0, generations: 0,
} }
} }
@ -302,10 +298,10 @@ impl<'m> SceneEntityMapper<'m> {
/// [`Entity`] while reserving extra generations. Because this makes the [`SceneEntityMapper`] unable to /// [`Entity`] while reserving extra generations. Because this makes the [`SceneEntityMapper`] unable to
/// safely allocate any more references, this method takes ownership of `self` in order to render it unusable. /// safely allocate any more references, this method takes ownership of `self` in order to render it unusable.
pub fn finish(self, world: &mut World) { pub fn finish(self, world: &mut World) {
// SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope` // SAFETY: We never constructed the entity and never released it for something else to construct.
let entities = unsafe { world.entities_mut() }; unsafe {
assert!(entities.free(self.dead_start).is_some()); world.release_generations_unchecked(self.dead_start.row(), self.generations);
assert!(entities.reserve_generations(self.dead_start.index(), self.generations)); }
} }
/// Creates an [`SceneEntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity>`], then calls the /// Creates an [`SceneEntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity>`], then calls the
@ -375,21 +371,4 @@ mod tests {
assert_eq!(entity.index(), dead_ref.index()); assert_eq!(entity.index(), dead_ref.index());
assert!(entity.generation() > dead_ref.generation()); assert!(entity.generation() > dead_ref.generation());
} }
#[test]
fn entity_mapper_no_panic() {
let mut world = World::new();
// "Dirty" the `Entities`, requiring a flush afterward.
world.entities.reserve_entity();
assert!(world.entities.needs_flush());
// Create and exercise a SceneEntityMapper - should not panic because it flushes
// `Entities` first.
SceneEntityMapper::world_scope(&mut Default::default(), &mut world, |_, m| {
m.get_mapped(Entity::PLACEHOLDER);
});
// The SceneEntityMapper should leave `Entities` in a flushed state.
assert!(!world.entities.needs_flush());
}
} }

View File

@ -2784,7 +2784,7 @@ impl<'w> EntityWorldMut<'w> {
self.assert_not_despawned(); self.assert_not_despawned();
self.world.flush(); self.world.flush();
let entity_clone = self.world.allocator.alloc(); let entity_clone = self.world.spawn_empty().id();
let mut builder = EntityCloner::build(self.world); let mut builder = EntityCloner::build(self.world);
config(&mut builder); config(&mut builder);