Skip to content

Commit 40e88dc

Browse files
jrobsonchasealice-i-cecileShatur
authored
Change ReflectMapEntities to operate on components before insertion (#15422)
Previous PR #14549 was closed in error and couldn't be reopened since I had updated the branch :crying_cat_face: # Objective Fixes #14465 ## Solution `ReflectMapEntities` now works similarly to `MapEntities` in that it works on the reflected value itself rather than the component in the world after insertion. This makes it so that observers see the remapped entities on insertion rather than the entity IDs from the scene. `ReflectMapEntities` now works for both components and resources, so we only need the one. ## Testing * New unit test for `Observer`s + `DynamicScene`s * New unit test for `Observer`s + `Scene`s * Open to suggestions for other tests! --- ## Migration Guide - Consumers of `ReflectMapEntities` will need to call `map_entities` on values prior to inserting them into the world. - Implementors of `MapEntities` will need to remove the `mappings` method, which is no longer needed for `ReflectMapEntities` and has been removed from the trait. --------- Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: Hennadii Chernyshchyk <[email protected]>
1 parent 6465e3b commit 40e88dc

File tree

5 files changed

+104
-247
lines changed

5 files changed

+104
-247
lines changed

crates/bevy_ecs/src/entity/map_entities.rs

Lines changed: 10 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,16 @@ use super::{EntityHashMap, VisitEntitiesMut};
1515
/// (usually by using an [`EntityHashMap<Entity>`] between source entities and entities in the
1616
/// current world).
1717
///
18+
/// This trait is similar to [`VisitEntitiesMut`]. They differ in that [`VisitEntitiesMut`] operates
19+
/// on `&mut Entity` and allows for in-place modification, while this trait makes no assumption that
20+
/// such in-place modification is occurring, which is impossible for types such as [`HashSet<Entity>`]
21+
/// and [`EntityHashMap`] which must be rebuilt when their contained [`Entity`]s are remapped.
22+
///
1823
/// Implementing this trait correctly is required for properly loading components
1924
/// with entity references from scenes.
2025
///
26+
/// [`HashSet<Entity>`]: bevy_utils::HashSet
27+
///
2128
/// ## Example
2229
///
2330
/// ```
@@ -60,9 +67,6 @@ impl<T: VisitEntitiesMut> MapEntities for T {
6067
///
6168
/// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World).
6269
///
63-
/// Note that this trait is _not_ [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety).
64-
/// Please see [`DynEntityMapper`] for an object safe alternative.
65-
///
6670
/// ## Example
6771
///
6872
/// ```
@@ -79,64 +83,16 @@ impl<T: VisitEntitiesMut> MapEntities for T {
7983
/// fn map_entity(&mut self, entity: Entity) -> Entity {
8084
/// self.map.get(&entity).copied().unwrap_or(entity)
8185
/// }
82-
///
83-
/// fn mappings(&self) -> impl Iterator<Item = (Entity, Entity)> {
84-
/// self.map.iter().map(|(&source, &target)| (source, target))
85-
/// }
8686
/// }
8787
/// ```
8888
pub trait EntityMapper {
8989
/// Map an entity to another entity
9090
fn map_entity(&mut self, entity: Entity) -> Entity;
91-
92-
/// Iterate over all entity to entity mappings.
93-
///
94-
/// # Examples
95-
///
96-
/// ```rust
97-
/// # use bevy_ecs::entity::{Entity, EntityMapper};
98-
/// # fn example(mapper: impl EntityMapper) {
99-
/// for (source, target) in mapper.mappings() {
100-
/// println!("Will map from {source} to {target}");
101-
/// }
102-
/// # }
103-
/// ```
104-
fn mappings(&self) -> impl Iterator<Item = (Entity, Entity)>;
105-
}
106-
107-
/// An [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety) version
108-
/// of [`EntityMapper`]. This trait is automatically implemented for type that implements `EntityMapper`.
109-
pub trait DynEntityMapper {
110-
/// Map an entity to another entity.
111-
///
112-
/// This is an [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety)
113-
/// alternative to [`EntityMapper::map_entity`].
114-
fn dyn_map_entity(&mut self, entity: Entity) -> Entity;
115-
116-
/// Iterate over all entity to entity mappings.
117-
///
118-
/// This is an [object safe](https://doc.rust-lang.org/reference/items/traits.html#object-safety)
119-
/// alternative to [`EntityMapper::mappings`].
120-
fn dyn_mappings(&self) -> Vec<(Entity, Entity)>;
121-
}
122-
123-
impl<T: EntityMapper> DynEntityMapper for T {
124-
fn dyn_map_entity(&mut self, entity: Entity) -> Entity {
125-
<T as EntityMapper>::map_entity(self, entity)
126-
}
127-
128-
fn dyn_mappings(&self) -> Vec<(Entity, Entity)> {
129-
<T as EntityMapper>::mappings(self).collect()
130-
}
13191
}
13292

133-
impl<'a> EntityMapper for &'a mut dyn DynEntityMapper {
93+
impl EntityMapper for &mut dyn EntityMapper {
13494
fn map_entity(&mut self, entity: Entity) -> Entity {
135-
(*self).dyn_map_entity(entity)
136-
}
137-
138-
fn mappings(&self) -> impl Iterator<Item = (Entity, Entity)> {
139-
(*self).dyn_mappings().into_iter()
95+
(*self).map_entity(entity)
14096
}
14197
}
14298

@@ -160,10 +116,6 @@ impl EntityMapper for SceneEntityMapper<'_> {
160116

161117
new
162118
}
163-
164-
fn mappings(&self) -> impl Iterator<Item = (Entity, Entity)> {
165-
self.map.iter().map(|(&source, &target)| (source, target))
166-
}
167119
}
168120

169121
/// A wrapper for [`EntityHashMap<Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination
@@ -242,10 +194,9 @@ impl<'m> SceneEntityMapper<'m> {
242194
#[cfg(test)]
243195
mod tests {
244196
use crate::{
245-
entity::{DynEntityMapper, Entity, EntityHashMap, EntityMapper, SceneEntityMapper},
197+
entity::{Entity, EntityHashMap, EntityMapper, SceneEntityMapper},
246198
world::World,
247199
};
248-
use bevy_utils::assert_object_safe;
249200

250201
#[test]
251202
fn entity_mapper() {
@@ -292,26 +243,6 @@ mod tests {
292243
assert!(entity.generation() > dead_ref.generation());
293244
}
294245

295-
#[test]
296-
fn entity_mapper_iteration() {
297-
let mut old_world = World::new();
298-
let mut new_world = World::new();
299-
300-
let mut map = EntityHashMap::default();
301-
let mut mapper = SceneEntityMapper::new(&mut map, &mut new_world);
302-
303-
assert_eq!(mapper.mappings().collect::<Vec<_>>(), vec![]);
304-
305-
let old_entity = old_world.spawn_empty().id();
306-
307-
let new_entity = mapper.map_entity(old_entity);
308-
309-
assert_eq!(
310-
mapper.mappings().collect::<Vec<_>>(),
311-
vec![(old_entity, new_entity)]
312-
);
313-
}
314-
315246
#[test]
316247
fn entity_mapper_no_panic() {
317248
let mut world = World::new();
@@ -328,9 +259,4 @@ mod tests {
328259
// The SceneEntityMapper should leave `Entities` in a flushed state.
329260
assert!(!world.entities.needs_flush());
330261
}
331-
332-
#[test]
333-
fn dyn_entity_mapper_object_safe() {
334-
assert_object_safe::<dyn DynEntityMapper>();
335-
}
336262
}

crates/bevy_ecs/src/reflect/map_entities.rs

Lines changed: 18 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,37 @@
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::{EntityMapper, 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 EntityMapper),
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 an [`EntityMapper`].
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.
40-
pub fn map_entities(
41-
&self,
42-
world: &mut World,
43-
entity_map: &mut EntityHashMap<Entity>,
44-
entities: &[Entity],
45-
) {
46-
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
47-
(self.map_entities)(world, mapper, entities);
48-
});
21+
/// # Panics
22+
/// Panics if the the type of the reflected value doesn't match.
23+
pub fn map_entities(&self, reflected: &mut dyn PartialReflect, mapper: &mut dyn EntityMapper) {
24+
(self.map_entities)(reflected, mapper);
4925
}
5026
}
5127

52-
impl<C: Component + MapEntities> FromType<C> for ReflectMapEntities {
28+
impl<C: FromReflect + MapEntities> FromType<C> for ReflectMapEntities {
5329
fn from_type() -> Self {
5430
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-
}
31+
map_entities: |reflected, mut mapper| {
32+
let mut concrete = C::from_reflect(reflected).expect("reflected type should match");
33+
concrete.map_entities(&mut mapper);
34+
reflected.apply(&concrete);
10535
},
10636
}
10737
}

crates/bevy_ecs/src/reflect/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub use bundle::{ReflectBundle, ReflectBundleFns};
2424
pub use component::{ReflectComponent, ReflectComponentFns};
2525
pub use entity_commands::ReflectCommandExt;
2626
pub use from_world::{ReflectFromWorld, ReflectFromWorldFns};
27-
pub use map_entities::{ReflectMapEntities, ReflectMapEntitiesResource};
27+
pub use map_entities::ReflectMapEntities;
2828
pub use resource::{ReflectResource, ReflectResourceFns};
2929
pub use visit_entities::{ReflectVisitEntities, ReflectVisitEntitiesMut};
3030

0 commit comments

Comments
 (0)