Skip to content

Commit 9223201

Browse files
cBournhonesquecbournhonesque-scalice-i-cecileUkoeHB
authored
Make the MapEntities trait generic over Mappers, and add a simpler EntityMapper (#11428)
# Objective My motivation are to resolve some of the issues I describe in this [PR](#11415): - not being able to easily mapping entities because the current EntityMapper requires `&mut World` access - not being able to create my own `EntityMapper` because some components (`Parent` or `Children`) do not provide any public way of modifying the inner entities This PR makes the `MapEntities` trait accept a generic type that implements `Mapper` to perform the mapping. This means we don't need to use `EntityMapper` to perform our mapping, we can use any type that implements `Mapper`. Basically this change is very similar to what `serde` does. Instead of specifying directly how to map entities for a given type, we have 2 distinct steps: - the user implements `MapEntities` to define how the type will be traversed and which `Entity`s will be mapped - the `Mapper` defines how the mapping is actually done This is similar to the distinction between `Serialize` (`MapEntities`) and `Serializer` (`Mapper`). This allows networking library to map entities without having to use the existing `EntityMapper` (which requires `&mut World` access and the use of `world_scope()`) ## Migration Guide - The existing `EntityMapper` (notably used to replicate `Scenes` across different `World`s) has been renamed to `SceneEntityMapper` - The `MapEntities` trait now works with a generic `EntityMapper` instead of the specific struct `EntityMapper`. Calls to `fn map_entities(&mut self, entity_mapper: &mut EntityMapper)` need to be updated to `fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M)` - The new trait `EntityMapper` has been added to the prelude --------- Co-authored-by: Charles Bournhonesque <[email protected]> Co-authored-by: Alice Cecile <[email protected]> Co-authored-by: UkoeHB <[email protected]>
1 parent 3a2e00a commit 9223201

File tree

8 files changed

+79
-54
lines changed

8 files changed

+79
-54
lines changed

crates/bevy_ecs/src/entity/map_entities.rs

Lines changed: 62 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ use bevy_utils::EntityHashMap;
99
///
1010
/// As entity IDs are valid only for the [`World`] they're sourced from, using [`Entity`]
1111
/// as references in components copied from another world will be invalid. This trait
12-
/// allows defining custom mappings for these references via [`EntityHashMap<Entity, Entity>`]
12+
/// allows defining custom mappings for these references via [`EntityMappers`](EntityMapper), which
13+
/// inject the entity mapping strategy between your `MapEntities` type and the current world
14+
/// (usually by using an [`EntityHashMap<Entity, Entity>`] between source entities and entities in the
15+
/// current world).
1316
///
1417
/// Implementing this trait correctly is required for properly loading components
1518
/// with entity references from scenes.
@@ -18,7 +21,7 @@ use bevy_utils::EntityHashMap;
1821
///
1922
/// ```
2023
/// use bevy_ecs::prelude::*;
21-
/// use bevy_ecs::entity::{EntityMapper, MapEntities};
24+
/// use bevy_ecs::entity::MapEntities;
2225
///
2326
/// #[derive(Component)]
2427
/// struct Spring {
@@ -27,19 +30,52 @@ use bevy_utils::EntityHashMap;
2730
/// }
2831
///
2932
/// impl MapEntities for Spring {
30-
/// fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
31-
/// self.a = entity_mapper.get_or_reserve(self.a);
32-
/// self.b = entity_mapper.get_or_reserve(self.b);
33+
/// fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
34+
/// self.a = entity_mapper.map_entity(self.a);
35+
/// self.b = entity_mapper.map_entity(self.b);
3336
/// }
3437
/// }
3538
/// ```
3639
///
3740
pub trait MapEntities {
3841
/// Updates all [`Entity`] references stored inside using `entity_mapper`.
3942
///
40-
/// Implementors should look up any and all [`Entity`] values stored within and
43+
/// Implementors should look up any and all [`Entity`] values stored within `self` and
4144
/// update them to the mapped values via `entity_mapper`.
42-
fn map_entities(&mut self, entity_mapper: &mut EntityMapper);
45+
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M);
46+
}
47+
48+
/// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`].
49+
///
50+
/// Usually this is done by using an [`EntityHashMap<Entity, Entity>`] to map source entities
51+
/// (mapper inputs) to the current world's entities (mapper outputs).
52+
///
53+
/// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World).
54+
pub trait EntityMapper {
55+
/// Map an entity to another entity
56+
fn map_entity(&mut self, entity: Entity) -> Entity;
57+
}
58+
59+
impl EntityMapper for SceneEntityMapper<'_> {
60+
/// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent.
61+
fn map_entity(&mut self, entity: Entity) -> Entity {
62+
if let Some(&mapped) = self.map.get(&entity) {
63+
return mapped;
64+
}
65+
66+
// this new entity reference is specifically designed to never represent any living entity
67+
let new = Entity::from_raw_and_generation(
68+
self.dead_start.index(),
69+
IdentifierMask::inc_masked_high_by(self.dead_start.generation, self.generations),
70+
);
71+
72+
// Prevent generations counter from being a greater value than HIGH_MASK.
73+
self.generations = (self.generations + 1) & HIGH_MASK;
74+
75+
self.map.insert(entity, new);
76+
77+
new
78+
}
4379
}
4480

4581
/// A wrapper for [`EntityHashMap<Entity, Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination
@@ -48,41 +84,30 @@ pub trait MapEntities {
4884
/// References are allocated by returning increasing generations starting from an internally initialized base
4985
/// [`Entity`]. After it is finished being used by [`MapEntities`] implementations, this entity is despawned and the
5086
/// requisite number of generations reserved.
51-
pub struct EntityMapper<'m> {
87+
pub struct SceneEntityMapper<'m> {
5288
/// A mapping from one set of entities to another.
5389
///
5490
/// This is typically used to coordinate data transfer between sets of entities, such as between a scene and the world
5591
/// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse
5692
/// identifiers directly.
5793
///
5894
/// On its own, a [`EntityHashMap<Entity, Entity>`] is not capable of allocating new entity identifiers, which is needed to map references
59-
/// to entities that lie outside the source entity set. This functionality can be accessed through [`EntityMapper::world_scope()`].
95+
/// to entities that lie outside the source entity set. This functionality can be accessed through [`SceneEntityMapper::world_scope()`].
6096
map: &'m mut EntityHashMap<Entity, Entity>,
6197
/// A base [`Entity`] used to allocate new references.
6298
dead_start: Entity,
6399
/// The number of generations this mapper has allocated thus far.
64100
generations: u32,
65101
}
66102

67-
impl<'m> EntityMapper<'m> {
68-
/// Returns the corresponding mapped entity or reserves a new dead entity ID if it is absent.
103+
impl<'m> SceneEntityMapper<'m> {
104+
#[deprecated(
105+
since = "0.13.0",
106+
note = "please use `EntityMapper::map_entity` instead"
107+
)]
108+
/// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent.
69109
pub fn get_or_reserve(&mut self, entity: Entity) -> Entity {
70-
if let Some(&mapped) = self.map.get(&entity) {
71-
return mapped;
72-
}
73-
74-
// this new entity reference is specifically designed to never represent any living entity
75-
let new = Entity::from_raw_and_generation(
76-
self.dead_start.index(),
77-
IdentifierMask::inc_masked_high_by(self.dead_start.generation, self.generations),
78-
);
79-
80-
// Prevent generations counter from being a greater value than HIGH_MASK.
81-
self.generations = (self.generations + 1) & HIGH_MASK;
82-
83-
self.map.insert(entity, new);
84-
85-
new
110+
self.map_entity(entity)
86111
}
87112

88113
/// Gets a reference to the underlying [`EntityHashMap<Entity, Entity>`].
@@ -95,7 +120,7 @@ impl<'m> EntityMapper<'m> {
95120
self.map
96121
}
97122

98-
/// Creates a new [`EntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
123+
/// Creates a new [`SceneEntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
99124
fn new(map: &'m mut EntityHashMap<Entity, Entity>, world: &mut World) -> Self {
100125
Self {
101126
map,
@@ -107,7 +132,7 @@ impl<'m> EntityMapper<'m> {
107132

108133
/// Reserves the allocated references to dead entities within the world. This frees the temporary base
109134
/// [`Entity`] while reserving extra generations via [`crate::entity::Entities::reserve_generations`]. Because this
110-
/// renders the [`EntityMapper`] unable to safely allocate any more references, this method takes ownership of
135+
/// renders the [`SceneEntityMapper`] unable to safely allocate any more references, this method takes ownership of
111136
/// `self` in order to render it unusable.
112137
fn finish(self, world: &mut World) {
113138
// SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`
@@ -116,7 +141,7 @@ impl<'m> EntityMapper<'m> {
116141
assert!(entities.reserve_generations(self.dead_start.index(), self.generations));
117142
}
118143

119-
/// Creates an [`EntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity, Entity>`], then calls the
144+
/// Creates an [`SceneEntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity, Entity>`], then calls the
120145
/// provided function with it. This allows one to allocate new entity references in this [`World`] that are
121146
/// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
122147
/// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
@@ -139,7 +164,7 @@ mod tests {
139164
use bevy_utils::EntityHashMap;
140165

141166
use crate::{
142-
entity::{Entity, EntityMapper},
167+
entity::{Entity, EntityMapper, SceneEntityMapper},
143168
world::World,
144169
};
145170

@@ -150,18 +175,18 @@ mod tests {
150175

151176
let mut map = EntityHashMap::default();
152177
let mut world = World::new();
153-
let mut mapper = EntityMapper::new(&mut map, &mut world);
178+
let mut mapper = SceneEntityMapper::new(&mut map, &mut world);
154179

155180
let mapped_ent = Entity::from_raw(FIRST_IDX);
156-
let dead_ref = mapper.get_or_reserve(mapped_ent);
181+
let dead_ref = mapper.map_entity(mapped_ent);
157182

158183
assert_eq!(
159184
dead_ref,
160-
mapper.get_or_reserve(mapped_ent),
185+
mapper.map_entity(mapped_ent),
161186
"should persist the allocated mapping from the previous line"
162187
);
163188
assert_eq!(
164-
mapper.get_or_reserve(Entity::from_raw(SECOND_IDX)).index(),
189+
mapper.map_entity(Entity::from_raw(SECOND_IDX)).index(),
165190
dead_ref.index(),
166191
"should re-use the same index for further dead refs"
167192
);
@@ -178,8 +203,8 @@ mod tests {
178203
let mut map = EntityHashMap::default();
179204
let mut world = World::new();
180205

181-
let dead_ref = EntityMapper::world_scope(&mut map, &mut world, |_, mapper| {
182-
mapper.get_or_reserve(Entity::from_raw(0))
206+
let dead_ref = SceneEntityMapper::world_scope(&mut map, &mut world, |_, mapper| {
207+
mapper.map_entity(Entity::from_raw(0))
183208
});
184209

185210
// Next allocated entity should be a further generation on the same index

crates/bevy_ecs/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub mod prelude {
3636
bundle::Bundle,
3737
change_detection::{DetectChanges, DetectChangesMut, Mut, Ref},
3838
component::Component,
39-
entity::Entity,
39+
entity::{Entity, EntityMapper},
4040
event::{Event, EventReader, EventWriter, Events},
4141
query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
4242
removal_detection::RemovedComponents,

crates/bevy_ecs/src/reflect/map_entities.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
component::Component,
3-
entity::{Entity, EntityMapper, MapEntities},
3+
entity::{Entity, MapEntities, SceneEntityMapper},
44
world::World,
55
};
66
use bevy_reflect::FromType;
@@ -10,11 +10,11 @@ use bevy_utils::EntityHashMap;
1010
/// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization
1111
/// any stored IDs need to be re-allocated in the destination world.
1212
///
13-
/// See [`MapEntities`] for more information.
13+
/// See [`SceneEntityMapper`] and [`MapEntities`] for more information.
1414
#[derive(Clone)]
1515
pub struct ReflectMapEntities {
16-
map_all_entities: fn(&mut World, &mut EntityMapper),
17-
map_entities: fn(&mut World, &mut EntityMapper, &[Entity]),
16+
map_all_entities: fn(&mut World, &mut SceneEntityMapper),
17+
map_entities: fn(&mut World, &mut SceneEntityMapper, &[Entity]),
1818
}
1919

2020
impl ReflectMapEntities {
@@ -32,7 +32,7 @@ impl ReflectMapEntities {
3232
world: &mut World,
3333
entity_map: &mut EntityHashMap<Entity, Entity>,
3434
) {
35-
EntityMapper::world_scope(entity_map, world, self.map_all_entities);
35+
SceneEntityMapper::world_scope(entity_map, world, self.map_all_entities);
3636
}
3737

3838
/// A general method for applying [`MapEntities`] behavior to elements in an [`EntityHashMap<Entity, Entity>`]. Unlike
@@ -47,7 +47,7 @@ impl ReflectMapEntities {
4747
entity_map: &mut EntityHashMap<Entity, Entity>,
4848
entities: &[Entity],
4949
) {
50-
EntityMapper::world_scope(entity_map, world, |world, mapper| {
50+
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
5151
(self.map_entities)(world, mapper, entities);
5252
});
5353
}

crates/bevy_hierarchy/src/components/children.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ use std::ops::Deref;
2929
pub struct Children(pub(crate) SmallVec<[Entity; 8]>);
3030

3131
impl MapEntities for Children {
32-
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
32+
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
3333
for entity in &mut self.0 {
34-
*entity = entity_mapper.get_or_reserve(*entity);
34+
*entity = entity_mapper.map_entity(*entity);
3535
}
3636
}
3737
}

crates/bevy_hierarchy/src/components/parent.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ impl FromWorld for Parent {
5656
}
5757

5858
impl MapEntities for Parent {
59-
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
60-
self.0 = entity_mapper.get_or_reserve(self.0);
59+
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
60+
self.0 = entity_mapper.map_entity(self.0);
6161
}
6262
}
6363

crates/bevy_render/src/mesh/mesh/skinning.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ pub struct SkinnedMesh {
1717
}
1818

1919
impl MapEntities for SkinnedMesh {
20-
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
20+
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
2121
for joint in &mut self.joints {
22-
*joint = entity_mapper.get_or_reserve(*joint);
22+
*joint = entity_mapper.map_entity(*joint);
2323
}
2424
}
2525
}

crates/bevy_scene/src/serde.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,8 +550,8 @@ mod tests {
550550
struct MyEntityRef(Entity);
551551

552552
impl MapEntities for MyEntityRef {
553-
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
554-
self.0 = entity_mapper.get_or_reserve(self.0);
553+
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
554+
self.0 = entity_mapper.map_entity(self.0);
555555
}
556556
}
557557

crates/bevy_window/src/window.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ impl WindowRef {
5959
}
6060

6161
impl MapEntities for WindowRef {
62-
fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
62+
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
6363
match self {
6464
Self::Entity(entity) => {
65-
*entity = entity_mapper.get_or_reserve(*entity);
65+
*entity = entity_mapper.map_entity(*entity);
6666
}
6767
Self::Primary => {}
6868
};

0 commit comments

Comments
 (0)