diff --git a/crates/bevy_ecs/src/entity/map_entities.rs b/crates/bevy_ecs/src/entity/map_entities.rs index 8283e55e95204c..5241ea52798ff4 100644 --- a/crates/bevy_ecs/src/entity/map_entities.rs +++ b/crates/bevy_ecs/src/entity/map_entities.rs @@ -122,6 +122,19 @@ impl DynEntityMapper for T { } } +/// Helper to construct an [`EntityMapper`] from a [`DynEntityMapper`]. +pub struct UnDynEntityMapper<'a>(pub &'a mut dyn DynEntityMapper); + +impl<'a> EntityMapper for UnDynEntityMapper<'a> { + fn map_entity(&mut self, entity: Entity) -> Entity { + self.0.dyn_map_entity(entity) + } + + fn mappings(&self) -> impl Iterator { + self.0.dyn_mappings().into_iter() + } +} + impl EntityMapper for SceneEntityMapper<'_> { /// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent. fn map_entity(&mut self, entity: Entity) -> Entity { diff --git a/crates/bevy_ecs/src/reflect/map_entities.rs b/crates/bevy_ecs/src/reflect/map_entities.rs index 4bc70acc3e55ea..dc380f9c2fd023 100644 --- a/crates/bevy_ecs/src/reflect/map_entities.rs +++ b/crates/bevy_ecs/src/reflect/map_entities.rs @@ -1,9 +1,11 @@ use crate::{ component::Component, - entity::{Entity, EntityHashMap, MapEntities, SceneEntityMapper}, + entity::{ + DynEntityMapper, Entity, EntityHashMap, MapEntities, SceneEntityMapper, UnDynEntityMapper, + }, world::World, }; -use bevy_reflect::FromType; +use bevy_reflect::{FromReflect, FromType, Reflect}; /// For a specific type of component, this maps any fields with values of type [`Entity`] to a new world. /// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization @@ -12,63 +14,26 @@ use bevy_reflect::FromType; /// See [`SceneEntityMapper`] and [`MapEntities`] for more information. #[derive(Clone)] pub struct ReflectMapEntities { - map_all_entities: fn(&mut World, &mut SceneEntityMapper), - map_entities: fn(&mut World, &mut SceneEntityMapper, &[Entity]), + map_entities: fn(&mut dyn Reflect, &mut dyn DynEntityMapper), } impl ReflectMapEntities { - /// A general method for applying [`MapEntities`] behavior to all elements in an [`EntityHashMap`]. + /// A general method for applying [`MapEntities`] behavior to a reflected component. /// /// Be mindful in its usage: Works best in situations where the entities in the [`EntityHashMap`] are newly - /// created, before systems have a chance to add new components. If some of the entities referred to - /// by the [`EntityHashMap`] might already contain valid entity references, you should use [`map_entities`](Self::map_entities). - /// - /// An example of this: A scene can be loaded with `Parent` components, but then a `Parent` component can be added - /// to these entities after they have been loaded. If you reload the scene using [`map_all_entities`](Self::map_all_entities), those `Parent` - /// components with already valid entity references could be updated to point at something else entirely. - pub fn map_all_entities(&self, world: &mut World, entity_map: &mut EntityHashMap) { - SceneEntityMapper::world_scope(entity_map, world, self.map_all_entities); - } - - /// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap`]. Unlike - /// [`map_all_entities`](Self::map_all_entities), this is applied to specific entities, not all values - /// in the [`EntityHashMap`]. - /// - /// This is useful mostly for when you need to be careful not to update components that already contain valid entity - /// values. See [`map_all_entities`](Self::map_all_entities) for more details. - pub fn map_entities( - &self, - world: &mut World, - entity_map: &mut EntityHashMap, - entities: &[Entity], - ) { - SceneEntityMapper::world_scope(entity_map, world, |world, mapper| { - (self.map_entities)(world, mapper, entities); - }); + /// created, before systems have a chance to add new components. + pub fn map_entities(&self, component: &mut dyn Reflect, mapper: &mut dyn DynEntityMapper) { + (self.map_entities)(component, mapper); } } -impl FromType for ReflectMapEntities { +impl FromType for ReflectMapEntities { fn from_type() -> Self { ReflectMapEntities { - map_entities: |world, entity_mapper, entities| { - for &entity in entities { - if let Some(mut component) = world.get_mut::(entity) { - component.map_entities(entity_mapper); - } - } - }, - map_all_entities: |world, entity_mapper| { - let entities = entity_mapper - .get_map() - .values() - .copied() - .collect::>(); - for entity in &entities { - if let Some(mut component) = world.get_mut::(*entity) { - component.map_entities(entity_mapper); - } - } + map_entities: |component, entity_mapper| { + let mut concrete = C::from_reflect(&*component).unwrap(); + concrete.map_entities(&mut UnDynEntityMapper(entity_mapper)); + component.apply(&concrete as _); }, } } diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index bd9beee1a1a5cc..61b91f572f3da1 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -1,12 +1,11 @@ use crate::{ron, DynamicSceneBuilder, Scene, SceneSpawnError}; -use bevy_ecs::entity::EntityHashMap; +use bevy_ecs::entity::{EntityHashMap, SceneEntityMapper}; use bevy_ecs::{ entity::Entity, reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities}, world::World, }; use bevy_reflect::{Reflect, TypePath, TypeRegistry}; -use bevy_utils::TypeIdMap; #[cfg(feature = "serialize")] use crate::serde::SceneSerializer; @@ -71,23 +70,26 @@ impl DynamicScene { ) -> Result<(), SceneSpawnError> { let type_registry = type_registry.read(); - // For each component types that reference other entities, we keep track - // of which entities in the scene use that component. - // This is so we can update the scene-internal references to references - // of the actual entities in the world. - let mut scene_mappings: TypeIdMap> = Default::default(); - + // First ensure that every entity in the scene has a corresponding world + // entity in the entity map. for scene_entity in &self.entities { // Fetch the entity with the given entity id from the `entity_map` // or spawn a new entity with a transiently unique id if there is // no corresponding entry. - let entity = *entity_map + entity_map .entry(scene_entity.entity) .or_insert_with(|| world.spawn_empty().id()); - let entity_mut = &mut world.entity_mut(entity); + } + + for scene_entity in &self.entities { + // Fetch the entity with the given entity id from the `entity_map`. + let entity = *entity_map + .get(&scene_entity.entity) + .expect("should have previously spawned an empty entity"); // Apply/ add each component to the given entity. for component in &scene_entity.components { + let mut component = component.clone_value(); let type_info = component.get_represented_type_info().ok_or_else(|| { SceneSpawnError::NoRepresentedType { type_path: component.reflect_type_path().to_string(), @@ -105,29 +107,19 @@ impl DynamicScene { } })?; - // If this component references entities in the scene, track it - // so we can update it to the entity in the world. - if registration.data::().is_some() { - scene_mappings - .entry(registration.type_id()) - .or_default() - .push(entity); + // If this component references entities in the scene, update + // them to the entities in the world. + if let Some(map_entities) = registration.data::() { + SceneEntityMapper::world_scope(entity_map, world, |_, mapper| { + map_entities.map_entities(&mut *component, mapper); + }); } - // If the entity already has the given component attached, - // just apply the (possibly) new value, otherwise add the - // component to the entity. - reflect_component.apply_or_insert(entity_mut, &**component, &type_registry); - } - } - - // Updates references to entities in the scene to entities in the world - for (type_id, entities) in scene_mappings.into_iter() { - let registration = type_registry.get(type_id).expect( - "we should be getting TypeId from this TypeRegistration in the first place", - ); - if let Some(map_entities_reflect) = registration.data::() { - map_entities_reflect.map_entities(world, entity_map, &entities); + reflect_component.apply_or_insert( + &mut world.entity_mut(entity), + &*component, + &type_registry, + ); } } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index f852d356b390b5..08276c72f6b761 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,11 +1,11 @@ use crate::{DynamicScene, SceneSpawnError}; use bevy_asset::Asset; -use bevy_ecs::entity::{Entity, EntityHashMap}; +use bevy_ecs::entity::{Entity, EntityHashMap, SceneEntityMapper}; use bevy_ecs::{ reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; -use bevy_reflect::TypePath; +use bevy_reflect::{Reflect, TypePath}; /// To spawn a scene, you can use either: /// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn) @@ -92,7 +92,7 @@ impl Scene { for archetype in self.world.archetypes().iter() { for scene_entity in archetype.entities() { - let entity = entity_map + let entity = *entity_map .entry(scene_entity.id()) .or_insert_with(|| world.spawn_empty().id()); for component_id in archetype.components() { @@ -102,35 +102,42 @@ impl Scene { .get_info(component_id) .expect("component_ids in archetypes should have ComponentInfo"); - let reflect_component = type_registry + let registration = type_registry .get(component_info.type_id().unwrap()) .ok_or_else(|| SceneSpawnError::UnregisteredType { std_type_name: component_info.name().to_string(), - }) - .and_then(|registration| { - registration.data::().ok_or_else(|| { - SceneSpawnError::UnregisteredComponent { - type_path: registration.type_info().type_path().to_string(), - } - }) })?; - reflect_component.copy( - &self.world, - world, - scene_entity.id(), - *entity, + let reflect_component = + registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredComponent { + type_path: registration.type_info().type_path().to_string(), + } + })?; + + let Some(mut component) = reflect_component + .reflect(self.world.entity(scene_entity.id())) + .map(Reflect::clone_value) + else { + continue; + }; + + // If this component references entities in the scene, + // update them to the entities in the world. + if let Some(map_entities) = registration.data::() { + SceneEntityMapper::world_scope(entity_map, world, |_, mapper| { + map_entities.map_entities(&mut *component, mapper); + }); + } + + reflect_component.apply_or_insert( + &mut world.entity_mut(entity), + &*component, &type_registry, ); } } } - for registration in type_registry.iter() { - if let Some(map_entities_reflect) = registration.data::() { - map_entities_reflect.map_all_entities(world, entity_map); - } - } - Ok(()) } }