Skip to content

Commit fe173a1

Browse files
committed
Rework ReflectMapEntities to match MapEntities, reorder scene entity mapping
1 parent d70595b commit fe173a1

File tree

5 files changed

+101
-152
lines changed

5 files changed

+101
-152
lines changed

crates/bevy_ecs/src/entity/map_entities.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,15 @@ pub trait DynEntityMapper {
112112
fn dyn_mappings(&self) -> Vec<(Entity, Entity)>;
113113
}
114114

115+
impl<'a> EntityMapper for &'a mut dyn DynEntityMapper {
116+
fn map_entity(&mut self, entity: Entity) -> Entity {
117+
(*self).dyn_map_entity(entity)
118+
}
119+
fn mappings(&self) -> impl Iterator<Item = (Entity, Entity)> {
120+
(*self).dyn_mappings().into_iter()
121+
}
122+
}
123+
115124
impl<T: EntityMapper> DynEntityMapper for T {
116125
fn dyn_map_entity(&mut self, entity: Entity) -> Entity {
117126
<T as EntityMapper>::map_entity(self, entity)

crates/bevy_ecs/src/reflect/map_entities.rs

Lines changed: 19 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,41 @@
1-
use crate::{
2-
component::Component,
3-
entity::{Entity, EntityHashMap, MapEntities, SceneEntityMapper},
4-
world::World,
5-
};
6-
use bevy_reflect::FromType;
1+
use crate::entity::{DynEntityMapper, MapEntities};
2+
use bevy_reflect::{FromReflect, FromType, PartialReflect};
73

8-
/// For a specific type of component, this maps any fields with values of type [`Entity`] to a new world.
4+
/// For a specific type of value, this maps any fields with values of type [`Entity`] to a new world.
95
///
106
/// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization
117
/// any stored IDs need to be re-allocated in the destination world.
128
///
13-
/// See [`SceneEntityMapper`] and [`MapEntities`] for more information.
9+
/// See [`EntityMapper`] and [`MapEntities`] for more information.
10+
///
11+
/// [`Entity`]: crate::entity::Entity
12+
/// [`EntityMapper`]: crate::entity::EntityMapper
1413
#[derive(Clone)]
1514
pub struct ReflectMapEntities {
16-
map_all_entities: fn(&mut World, &mut SceneEntityMapper),
17-
map_entities: fn(&mut World, &mut SceneEntityMapper, &[Entity]),
15+
map_entities: fn(&mut dyn PartialReflect, &mut dyn DynEntityMapper),
1816
}
1917

2018
impl ReflectMapEntities {
21-
/// A general method for applying [`MapEntities`] behavior to all elements in an [`EntityHashMap<Entity>`].
22-
///
23-
/// Be mindful in its usage: Works best in situations where the entities in the [`EntityHashMap<Entity>`] are newly
24-
/// created, before systems have a chance to add new components. If some of the entities referred to
25-
/// by the [`EntityHashMap<Entity>`] might already contain valid entity references, you should use [`map_entities`](Self::map_entities).
26-
///
27-
/// An example of this: A scene can be loaded with `Parent` components, but then a `Parent` component can be added
28-
/// to these entities after they have been loaded. If you reload the scene using [`map_all_entities`](Self::map_all_entities), those `Parent`
29-
/// components with already valid entity references could be updated to point at something else entirely.
30-
pub fn map_all_entities(&self, world: &mut World, entity_map: &mut EntityHashMap<Entity>) {
31-
SceneEntityMapper::world_scope(entity_map, world, self.map_all_entities);
32-
}
33-
34-
/// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap<Entity>`]. Unlike
35-
/// [`map_all_entities`](Self::map_all_entities), this is applied to specific entities, not all values
36-
/// in the [`EntityHashMap<Entity>`].
19+
/// A general method for remapping entities in a reflected value via a [`DynEntityMapper`].
3720
///
38-
/// This is useful mostly for when you need to be careful not to update components that already contain valid entity
39-
/// values. See [`map_all_entities`](Self::map_all_entities) for more details.
21+
/// # Panics
22+
/// Panics if the the type of the reflected value doesn't match.
4023
pub fn map_entities(
4124
&self,
42-
world: &mut World,
43-
entity_map: &mut EntityHashMap<Entity>,
44-
entities: &[Entity],
25+
reflected: &mut dyn PartialReflect,
26+
mapper: &mut dyn DynEntityMapper,
4527
) {
46-
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
47-
(self.map_entities)(world, mapper, entities);
48-
});
28+
(self.map_entities)(reflected, mapper);
4929
}
5030
}
5131

52-
impl<C: Component + MapEntities> FromType<C> for ReflectMapEntities {
32+
impl<C: FromReflect + MapEntities> FromType<C> for ReflectMapEntities {
5333
fn from_type() -> Self {
5434
ReflectMapEntities {
55-
map_entities: |world, entity_mapper, entities| {
56-
for &entity in entities {
57-
if let Some(mut component) = world.get_mut::<C>(entity) {
58-
component.map_entities(entity_mapper);
59-
}
60-
}
61-
},
62-
map_all_entities: |world, entity_mapper| {
63-
let entities = entity_mapper
64-
.get_map()
65-
.values()
66-
.copied()
67-
.collect::<Vec<Entity>>();
68-
for entity in &entities {
69-
if let Some(mut component) = world.get_mut::<C>(*entity) {
70-
component.map_entities(entity_mapper);
71-
}
72-
}
73-
},
74-
}
75-
}
76-
}
77-
78-
/// For a specific type of resource, this maps any fields with values of type [`Entity`] to a new world.
79-
///
80-
/// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization
81-
/// any stored IDs need to be re-allocated in the destination world.
82-
///
83-
/// See [`SceneEntityMapper`] and [`MapEntities`] for more information.
84-
#[derive(Clone)]
85-
pub struct ReflectMapEntitiesResource {
86-
map_entities: fn(&mut World, &mut SceneEntityMapper),
87-
}
88-
89-
impl ReflectMapEntitiesResource {
90-
/// A method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap<Entity>`].
91-
pub fn map_entities(&self, world: &mut World, entity_map: &mut EntityHashMap<Entity>) {
92-
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
93-
(self.map_entities)(world, mapper);
94-
});
95-
}
96-
}
97-
98-
impl<R: crate::system::Resource + MapEntities> FromType<R> for ReflectMapEntitiesResource {
99-
fn from_type() -> Self {
100-
ReflectMapEntitiesResource {
101-
map_entities: |world, entity_mapper| {
102-
if let Some(mut resource) = world.get_resource_mut::<R>() {
103-
resource.map_entities(entity_mapper);
104-
}
35+
map_entities: |reflected, mut mapper| {
36+
let mut concrete = C::from_reflect(reflected).expect("mismatched reflected type");
37+
concrete.map_entities(&mut mapper);
38+
reflected.apply(&concrete);
10539
},
10640
}
10741
}

crates/bevy_ecs/src/reflect/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use bundle::{ReflectBundle, ReflectBundleFns};
2323
pub use component::{ReflectComponent, ReflectComponentFns};
2424
pub use entity_commands::ReflectCommandExt;
2525
pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
26-
pub use map_entities::{ReflectMapEntities, ReflectMapEntitiesResource};
26+
pub use map_entities::ReflectMapEntities;
2727
pub use resource::{ReflectResource, ReflectResourceFns};
2828

2929
/// A [`Resource`] storing [`TypeRegistry`] for

crates/bevy_scene/src/dynamic_scene.rs

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
use crate::{ron, DynamicSceneBuilder, Scene, SceneSpawnError};
2+
use bevy_asset::Asset;
3+
use bevy_ecs::reflect::ReflectResource;
24
use bevy_ecs::{
3-
entity::{Entity, EntityHashMap},
5+
entity::{Entity, EntityHashMap, SceneEntityMapper},
46
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities},
57
world::World,
68
};
79
use bevy_reflect::{PartialReflect, TypePath, TypeRegistry};
8-
use bevy_utils::TypeIdMap;
910

1011
#[cfg(feature = "serialize")]
1112
use crate::serde::SceneSerializer;
12-
use bevy_asset::Asset;
13-
use bevy_ecs::reflect::{ReflectMapEntitiesResource, ReflectResource};
1413
#[cfg(feature = "serialize")]
1514
use serde::Serialize;
1615

@@ -70,23 +69,26 @@ impl DynamicScene {
7069
) -> Result<(), SceneSpawnError> {
7170
let type_registry = type_registry.read();
7271

73-
// For each component types that reference other entities, we keep track
74-
// of which entities in the scene use that component.
75-
// This is so we can update the scene-internal references to references
76-
// of the actual entities in the world.
77-
let mut scene_mappings: TypeIdMap<Vec<Entity>> = Default::default();
78-
72+
// First ensure that every entity in the scene has a corresponding world
73+
// entity in the entity map.
7974
for scene_entity in &self.entities {
8075
// Fetch the entity with the given entity id from the `entity_map`
8176
// or spawn a new entity with a transiently unique id if there is
8277
// no corresponding entry.
83-
let entity = *entity_map
78+
entity_map
8479
.entry(scene_entity.entity)
8580
.or_insert_with(|| world.spawn_empty().id());
86-
let entity_mut = &mut world.entity_mut(entity);
81+
}
82+
83+
for scene_entity in &self.entities {
84+
// Fetch the entity with the given entity id from the `entity_map`.
85+
let entity = *entity_map
86+
.get(&scene_entity.entity)
87+
.expect("should have previously spawned an empty entity");
8788

8889
// Apply/ add each component to the given entity.
8990
for component in &scene_entity.components {
91+
let mut component = component.clone_value();
9092
let type_info = component.get_represented_type_info().ok_or_else(|| {
9193
SceneSpawnError::NoRepresentedType {
9294
type_path: component.reflect_type_path().to_string(),
@@ -104,39 +106,26 @@ impl DynamicScene {
104106
}
105107
})?;
106108

107-
// If this component references entities in the scene, track it
108-
// so we can update it to the entity in the world.
109-
if registration.data::<ReflectMapEntities>().is_some() {
110-
scene_mappings
111-
.entry(registration.type_id())
112-
.or_default()
113-
.push(entity);
109+
// If this component references entities in the scene, update
110+
// them to the entities in the world.
111+
if let Some(map_entities) = registration.data::<ReflectMapEntities>() {
112+
SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
113+
map_entities.map_entities(component.as_partial_reflect_mut(), mapper);
114+
});
114115
}
115116

116-
// If the entity already has the given component attached,
117-
// just apply the (possibly) new value, otherwise add the
118-
// component to the entity.
119117
reflect_component.apply_or_insert(
120-
entity_mut,
118+
&mut world.entity_mut(entity),
121119
component.as_partial_reflect(),
122120
&type_registry,
123121
);
124122
}
125123
}
126124

127-
// Updates references to entities in the scene to entities in the world
128-
for (type_id, entities) in scene_mappings.into_iter() {
129-
let registration = type_registry.get(type_id).expect(
130-
"we should be getting TypeId from this TypeRegistration in the first place",
131-
);
132-
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
133-
map_entities_reflect.map_entities(world, entity_map, &entities);
134-
}
135-
}
136-
137125
// Insert resources after all entities have been added to the world.
138126
// This ensures the entities are available for the resources to reference during mapping.
139127
for resource in &self.resources {
128+
let mut resource = resource.clone_value();
140129
let type_info = resource.get_represented_type_info().ok_or_else(|| {
141130
SceneSpawnError::NoRepresentedType {
142131
type_path: resource.reflect_type_path().to_string(),
@@ -153,14 +142,17 @@ impl DynamicScene {
153142
}
154143
})?;
155144

145+
// If this component references entities in the scene, update
146+
// them to the entities in the world.
147+
if let Some(map_entities) = registration.data::<ReflectMapEntities>() {
148+
SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
149+
map_entities.map_entities(resource.as_partial_reflect_mut(), mapper);
150+
});
151+
}
152+
156153
// If the world already contains an instance of the given resource
157154
// just apply the (possibly) new value, otherwise insert the resource
158-
reflect_resource.apply_or_insert(world, &**resource, &type_registry);
159-
160-
// Map entities in the resource if it implements [`MapEntities`].
161-
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntitiesResource>() {
162-
map_entities_reflect.map_entities(world, entity_map);
163-
}
155+
reflect_resource.apply_or_insert(world, resource.as_partial_reflect(), &type_registry);
164156
}
165157

166158
Ok(())
@@ -211,10 +203,7 @@ mod tests {
211203
use bevy_ecs::{
212204
component::Component,
213205
entity::{Entity, EntityHashMap, EntityMapper, MapEntities},
214-
reflect::{
215-
AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectMapEntitiesResource,
216-
ReflectResource,
217-
},
206+
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
218207
system::Resource,
219208
world::{Command, World},
220209
};
@@ -225,7 +214,7 @@ mod tests {
225214
use crate::dynamic_scene_builder::DynamicSceneBuilder;
226215

227216
#[derive(Resource, Reflect, Debug)]
228-
#[reflect(Resource, MapEntitiesResource)]
217+
#[reflect(Resource, MapEntities)]
229218
struct TestResource {
230219
entity_a: Entity,
231220
entity_b: Entity,

crates/bevy_scene/src/scene.rs

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::{DynamicScene, SceneSpawnError};
22
use bevy_asset::Asset;
33
use bevy_ecs::{
4-
entity::{Entity, EntityHashMap},
4+
entity::{Entity, EntityHashMap, SceneEntityMapper},
55
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
66
world::World,
77
};
8-
use bevy_reflect::TypePath;
8+
use bevy_reflect::{PartialReflect, TypePath};
99

1010
/// To spawn a scene, you can use either:
1111
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
@@ -90,47 +90,64 @@ impl Scene {
9090
reflect_resource.copy(&self.world, world, &type_registry);
9191
}
9292

93+
// Ensure that all scene entities have been allocated in the destination
94+
// world before handling components that may contain references that need mapping.
9395
for archetype in self.world.archetypes().iter() {
9496
for scene_entity in archetype.entities() {
95-
let entity = entity_map
97+
entity_map
9698
.entry(scene_entity.id())
9799
.or_insert_with(|| world.spawn_empty().id());
100+
}
101+
}
102+
103+
for archetype in self.world.archetypes().iter() {
104+
for scene_entity in archetype.entities() {
105+
let entity = *entity_map
106+
.get(&scene_entity.id())
107+
.expect("should have previously spawned an entity");
108+
98109
for component_id in archetype.components() {
99110
let component_info = self
100111
.world
101112
.components()
102113
.get_info(component_id)
103114
.expect("component_ids in archetypes should have ComponentInfo");
104115

105-
let reflect_component = type_registry
116+
let registration = type_registry
106117
.get(component_info.type_id().unwrap())
107118
.ok_or_else(|| SceneSpawnError::UnregisteredType {
108119
std_type_name: component_info.name().to_string(),
109-
})
110-
.and_then(|registration| {
111-
registration.data::<ReflectComponent>().ok_or_else(|| {
112-
SceneSpawnError::UnregisteredComponent {
113-
type_path: registration.type_info().type_path().to_string(),
114-
}
115-
})
116120
})?;
117-
reflect_component.copy(
118-
&self.world,
119-
world,
120-
scene_entity.id(),
121-
*entity,
121+
let reflect_component =
122+
registration.data::<ReflectComponent>().ok_or_else(|| {
123+
SceneSpawnError::UnregisteredComponent {
124+
type_path: registration.type_info().type_path().to_string(),
125+
}
126+
})?;
127+
128+
let Some(mut component) = reflect_component
129+
.reflect(self.world.entity(scene_entity.id()))
130+
.map(PartialReflect::clone_value)
131+
else {
132+
continue;
133+
};
134+
135+
// If this component references entities in the scene,
136+
// update them to the entities in the world.
137+
if let Some(map_entities) = registration.data::<ReflectMapEntities>() {
138+
SceneEntityMapper::world_scope(entity_map, world, |_, mapper| {
139+
map_entities.map_entities(component.as_partial_reflect_mut(), mapper);
140+
});
141+
}
142+
reflect_component.apply_or_insert(
143+
&mut world.entity_mut(entity),
144+
component.as_partial_reflect(),
122145
&type_registry,
123146
);
124147
}
125148
}
126149
}
127150

128-
for registration in type_registry.iter() {
129-
if let Some(map_entities_reflect) = registration.data::<ReflectMapEntities>() {
130-
map_entities_reflect.map_all_entities(world, entity_map);
131-
}
132-
}
133-
134151
Ok(())
135152
}
136153
}

0 commit comments

Comments
 (0)