Skip to content

Commit 394e2b0

Browse files
Replaced EntityMap with HashMap (#9461)
# Objective - Fixes #9321 ## Solution - `EntityMap` has been replaced by a simple `HashMap<Entity, Entity>`. --- ## Changelog - `EntityMap::world_scope` has been replaced with `World::world_scope` to avoid creating a new trait. This is a public facing change to the call semantics, but has no effect on results or behaviour. - `EntityMap`, as a `HashMap`, now operates on `&Entity` rather than `Entity`. This changes many standard access functions (e.g, `.get`) in a public-facing way. ## Migration Guide - Calls to `EntityMap::world_scope` can be directly replaced with the following: `map.world_scope(&mut world)` -> `world.world_scope(&mut map)` - Calls to legacy `EntityMap` methods such as `EntityMap::get` must explicitly include de/reference symbols: `let entity = map.get(parent);` -> `let &entity = map.get(&parent);`
1 parent 36f29a9 commit 394e2b0

File tree

6 files changed

+90
-139
lines changed

6 files changed

+90
-139
lines changed

crates/bevy_ecs/src/entity/map_entities.rs

Lines changed: 46 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::{entity::Entity, world::World};
2-
use bevy_utils::{Entry, HashMap};
2+
use bevy_utils::HashMap;
33

44
/// Operation to map all contained [`Entity`] fields in a type to new values.
55
///
66
/// As entity IDs are valid only for the [`World`] they're sourced from, using [`Entity`]
77
/// as references in components copied from another world will be invalid. This trait
8-
/// allows defining custom mappings for these references via [`EntityMap`].
8+
/// allows defining custom mappings for these references via [`HashMap<Entity, Entity>`]
99
///
1010
/// Implementing this trait correctly is required for properly loading components
1111
/// with entity references from scenes.
@@ -32,112 +32,29 @@ use bevy_utils::{Entry, HashMap};
3232
///
3333
/// [`World`]: crate::world::World
3434
pub trait MapEntities {
35-
/// Updates all [`Entity`] references stored inside using `entity_map`.
35+
/// Updates all [`Entity`] references stored inside using `entity_mapper`.
3636
///
3737
/// Implementors should look up any and all [`Entity`] values stored within and
3838
/// update them to the mapped values via `entity_mapper`.
3939
fn map_entities(&mut self, entity_mapper: &mut EntityMapper);
4040
}
4141

42-
/// A mapping from one set of entities to another.
43-
///
44-
/// The API generally follows [`HashMap`], but each [`Entity`] is returned by value, as they are [`Copy`].
45-
///
46-
/// This is typically used to coordinate data transfer between sets of entities, such as between a scene and the world
47-
/// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse
48-
/// identifiers directly.
49-
///
50-
/// On its own, an `EntityMap` is not capable of allocating new entity identifiers, which is needed to map references
51-
/// to entities that lie outside the source entity set. To do this, an `EntityMap` can be wrapped in an
52-
/// [`EntityMapper`] which scopes it to a particular destination [`World`] and allows new identifiers to be allocated.
53-
/// This functionality can be accessed through [`Self::world_scope()`].
54-
#[derive(Default, Debug)]
55-
pub struct EntityMap {
56-
map: HashMap<Entity, Entity>,
57-
}
58-
59-
impl EntityMap {
60-
/// Inserts an entities pair into the map.
61-
///
62-
/// If the map did not have `from` present, [`None`] is returned.
63-
///
64-
/// If the map did have `from` present, the value is updated, and the old value is returned.
65-
pub fn insert(&mut self, from: Entity, to: Entity) -> Option<Entity> {
66-
self.map.insert(from, to)
67-
}
68-
69-
/// Removes an `entity` from the map, returning the mapped value of it if the `entity` was previously in the map.
70-
pub fn remove(&mut self, entity: Entity) -> Option<Entity> {
71-
self.map.remove(&entity)
72-
}
73-
74-
/// Gets the given entity's corresponding entry in the map for in-place manipulation.
75-
pub fn entry(&mut self, entity: Entity) -> Entry<'_, Entity, Entity> {
76-
self.map.entry(entity)
77-
}
78-
79-
/// Returns the corresponding mapped entity.
80-
pub fn get(&self, entity: Entity) -> Option<Entity> {
81-
self.map.get(&entity).copied()
82-
}
83-
84-
/// An iterator visiting all keys in arbitrary order.
85-
pub fn keys(&self) -> impl Iterator<Item = Entity> + '_ {
86-
self.map.keys().cloned()
87-
}
88-
89-
/// An iterator visiting all values in arbitrary order.
90-
pub fn values(&self) -> impl Iterator<Item = Entity> + '_ {
91-
self.map.values().cloned()
92-
}
93-
94-
/// Returns the number of elements in the map.
95-
pub fn len(&self) -> usize {
96-
self.map.len()
97-
}
98-
99-
/// Returns true if the map contains no elements.
100-
pub fn is_empty(&self) -> bool {
101-
self.map.is_empty()
102-
}
103-
104-
/// An iterator visiting all (key, value) pairs in arbitrary order.
105-
pub fn iter(&self) -> impl Iterator<Item = (Entity, Entity)> + '_ {
106-
self.map.iter().map(|(from, to)| (*from, *to))
107-
}
108-
109-
/// Clears the map, removing all entity pairs. Keeps the allocated memory for reuse.
110-
pub fn clear(&mut self) {
111-
self.map.clear();
112-
}
113-
114-
/// Creates an [`EntityMapper`] from this [`EntityMap`] and scoped to the provided [`World`], then calls the
115-
/// provided function with it. This allows one to allocate new entity references in the provided `World` that are
116-
/// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
117-
/// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
118-
/// within the scope of the passed world. Its return value is then returned from `world_scope` as the generic type
119-
/// parameter `R`.
120-
pub fn world_scope<R>(
121-
&mut self,
122-
world: &mut World,
123-
f: impl FnOnce(&mut World, &mut EntityMapper) -> R,
124-
) -> R {
125-
let mut mapper = EntityMapper::new(self, world);
126-
let result = f(world, &mut mapper);
127-
mapper.finish(world);
128-
result
129-
}
130-
}
131-
132-
/// A wrapper for [`EntityMap`], augmenting it with the ability to allocate new [`Entity`] references in a destination
42+
/// A wrapper for [`HashMap<Entity, Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination
13343
/// world. These newly allocated references are guaranteed to never point to any living entity in that world.
13444
///
13545
/// References are allocated by returning increasing generations starting from an internally initialized base
13646
/// [`Entity`]. After it is finished being used by [`MapEntities`] implementations, this entity is despawned and the
13747
/// requisite number of generations reserved.
13848
pub struct EntityMapper<'m> {
139-
/// The wrapped [`EntityMap`].
140-
map: &'m mut EntityMap,
49+
/// A mapping from one set of entities to another.
50+
///
51+
/// This is typically used to coordinate data transfer between sets of entities, such as between a scene and the world
52+
/// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse
53+
/// identifiers directly.
54+
///
55+
/// On its own, a [`HashMap<Entity, Entity>`] is not capable of allocating new entity identifiers, which is needed to map references
56+
/// to entities that lie outside the source entity set. This functionality can be accessed through [`EntityMapper::world_scope()`].
57+
map: &'m mut HashMap<Entity, Entity>,
14158
/// A base [`Entity`] used to allocate new references.
14259
dead_start: Entity,
14360
/// The number of generations this mapper has allocated thus far.
@@ -147,7 +64,7 @@ pub struct EntityMapper<'m> {
14764
impl<'m> EntityMapper<'m> {
14865
/// Returns the corresponding mapped entity or reserves a new dead entity ID if it is absent.
14966
pub fn get_or_reserve(&mut self, entity: Entity) -> Entity {
150-
if let Some(mapped) = self.map.get(entity) {
67+
if let Some(&mapped) = self.map.get(&entity) {
15168
return mapped;
15269
}
15370

@@ -163,21 +80,21 @@ impl<'m> EntityMapper<'m> {
16380
new
16481
}
16582

166-
/// Gets a reference to the underlying [`EntityMap`].
167-
pub fn get_map(&'m self) -> &'m EntityMap {
83+
/// Gets a reference to the underlying [`HashMap<Entity, Entity>`].
84+
pub fn get_map(&'m self) -> &'m HashMap<Entity, Entity> {
16885
self.map
16986
}
17087

171-
/// Gets a mutable reference to the underlying [`EntityMap`]
172-
pub fn get_map_mut(&'m mut self) -> &'m mut EntityMap {
88+
/// Gets a mutable reference to the underlying [`HashMap<Entity, Entity>`].
89+
pub fn get_map_mut(&'m mut self) -> &'m mut HashMap<Entity, Entity> {
17390
self.map
17491
}
17592

17693
/// Creates a new [`EntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
177-
fn new(map: &'m mut EntityMap, world: &mut World) -> Self {
94+
fn new(map: &'m mut HashMap<Entity, Entity>, world: &mut World) -> Self {
17895
Self {
17996
map,
180-
// SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`
97+
// SAFETY: Entities data is kept in a valid state via `EntityMapper::world_scope`
18198
dead_start: unsafe { world.entities_mut().alloc() },
18299
generations: 0,
183100
}
@@ -193,19 +110,40 @@ impl<'m> EntityMapper<'m> {
193110
assert!(entities.free(self.dead_start).is_some());
194111
assert!(entities.reserve_generations(self.dead_start.index, self.generations));
195112
}
113+
114+
/// Creates an [`EntityMapper`] from a provided [`World`] and [`HashMap<Entity, Entity>`], then calls the
115+
/// provided function with it. This allows one to allocate new entity references in this [`World`] that are
116+
/// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
117+
/// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
118+
/// within the scope of this world. Its return value is then returned from `world_scope` as the generic type
119+
/// parameter `R`.
120+
pub fn world_scope<R>(
121+
entity_map: &'m mut HashMap<Entity, Entity>,
122+
world: &mut World,
123+
f: impl FnOnce(&mut World, &mut Self) -> R,
124+
) -> R {
125+
let mut mapper = Self::new(entity_map, world);
126+
let result = f(world, &mut mapper);
127+
mapper.finish(world);
128+
result
129+
}
196130
}
197131

198132
#[cfg(test)]
199133
mod tests {
200-
use super::{EntityMap, EntityMapper};
201-
use crate::{entity::Entity, world::World};
134+
use bevy_utils::HashMap;
135+
136+
use crate::{
137+
entity::{Entity, EntityMapper},
138+
world::World,
139+
};
202140

203141
#[test]
204142
fn entity_mapper() {
205143
const FIRST_IDX: u32 = 1;
206144
const SECOND_IDX: u32 = 2;
207145

208-
let mut map = EntityMap::default();
146+
let mut map = HashMap::default();
209147
let mut world = World::new();
210148
let mut mapper = EntityMapper::new(&mut map, &mut world);
211149

@@ -232,10 +170,10 @@ mod tests {
232170

233171
#[test]
234172
fn world_scope_reserves_generations() {
235-
let mut map = EntityMap::default();
173+
let mut map = HashMap::default();
236174
let mut world = World::new();
237175

238-
let dead_ref = map.world_scope(&mut world, |_, mapper| {
176+
let dead_ref = EntityMapper::world_scope(&mut map, &mut world, |_, mapper| {
239177
mapper.get_or_reserve(Entity::new(0, 0))
240178
});
241179

crates/bevy_ecs/src/reflect/map_entities.rs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use crate::{
22
component::Component,
3-
entity::{Entity, EntityMap, EntityMapper, MapEntities},
3+
entity::{Entity, EntityMapper, MapEntities},
44
world::World,
55
};
66
use bevy_reflect::FromType;
7+
use bevy_utils::HashMap;
78

89
/// For a specific type of component, this maps any fields with values of type [`Entity`] to a new world.
910
/// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization
@@ -17,27 +18,32 @@ pub struct ReflectMapEntities {
1718
}
1819

1920
impl ReflectMapEntities {
20-
/// A general method for applying [`MapEntities`] behavior to all elements in an [`EntityMap`].
21+
/// A general method for applying [`MapEntities`] behavior to all elements in an [`HashMap<Entity, Entity>`].
2122
///
22-
/// Be mindful in its usage: Works best in situations where the entities in the [`EntityMap`] are newly
23+
/// Be mindful in its usage: Works best in situations where the entities in the [`HashMap<Entity, Entity>`] are newly
2324
/// created, before systems have a chance to add new components. If some of the entities referred to
24-
/// by the [`EntityMap`] might already contain valid entity references, you should use [`map_entities`](Self::map_entities).
25+
/// by the [`HashMap<Entity, Entity>`] might already contain valid entity references, you should use [`map_entities`](Self::map_entities).
2526
///
2627
/// An example of this: A scene can be loaded with `Parent` components, but then a `Parent` component can be added
2728
/// to these entities after they have been loaded. If you reload the scene using [`map_all_entities`](Self::map_all_entities), those `Parent`
2829
/// components with already valid entity references could be updated to point at something else entirely.
29-
pub fn map_all_entities(&self, world: &mut World, entity_map: &mut EntityMap) {
30-
entity_map.world_scope(world, self.map_all_entities);
30+
pub fn map_all_entities(&self, world: &mut World, entity_map: &mut HashMap<Entity, Entity>) {
31+
EntityMapper::world_scope(entity_map, world, self.map_all_entities);
3132
}
3233

33-
/// A general method for applying [`MapEntities`] behavior to elements in an [`EntityMap`]. Unlike
34+
/// A general method for applying [`MapEntities`] behavior to elements in an [`HashMap<Entity, Entity>`]. Unlike
3435
/// [`map_all_entities`](Self::map_all_entities), this is applied to specific entities, not all values
35-
/// in the [`EntityMap`].
36+
/// in the [`HashMap<Entity, Entity>`].
3637
///
3738
/// This is useful mostly for when you need to be careful not to update components that already contain valid entity
3839
/// values. See [`map_all_entities`](Self::map_all_entities) for more details.
39-
pub fn map_entities(&self, world: &mut World, entity_map: &mut EntityMap, entities: &[Entity]) {
40-
entity_map.world_scope(world, |world, mapper| {
40+
pub fn map_entities(
41+
&self,
42+
world: &mut World,
43+
entity_map: &mut HashMap<Entity, Entity>,
44+
entities: &[Entity],
45+
) {
46+
EntityMapper::world_scope(entity_map, world, |world, mapper| {
4147
(self.map_entities)(world, mapper, entities);
4248
});
4349
}
@@ -54,7 +60,11 @@ impl<C: Component + MapEntities> FromType<C> for ReflectMapEntities {
5460
}
5561
},
5662
map_all_entities: |world, entity_mapper| {
57-
let entities = entity_mapper.get_map().values().collect::<Vec<Entity>>();
63+
let entities = entity_mapper
64+
.get_map()
65+
.values()
66+
.copied()
67+
.collect::<Vec<Entity>>();
5868
for entity in &entities {
5969
if let Some(mut component) = world.get_mut::<C>(*entity) {
6070
component.map_entities(entity_mapper);

crates/bevy_scene/src/dynamic_scene.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::any::TypeId;
33
use crate::{DynamicSceneBuilder, Scene, SceneSpawnError};
44
use anyhow::Result;
55
use bevy_ecs::{
6-
entity::{Entity, EntityMap},
6+
entity::Entity,
77
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities},
88
world::World,
99
};
@@ -67,7 +67,7 @@ impl DynamicScene {
6767
pub fn write_to_world_with(
6868
&self,
6969
world: &mut World,
70-
entity_map: &mut EntityMap,
70+
entity_map: &mut HashMap<Entity, Entity>,
7171
type_registry: &AppTypeRegistry,
7272
) -> Result<(), SceneSpawnError> {
7373
let type_registry = type_registry.read();
@@ -155,7 +155,7 @@ impl DynamicScene {
155155
pub fn write_to_world(
156156
&self,
157157
world: &mut World,
158-
entity_map: &mut EntityMap,
158+
entity_map: &mut HashMap<Entity, Entity>,
159159
) -> Result<(), SceneSpawnError> {
160160
let registry = world.resource::<AppTypeRegistry>().clone();
161161
self.write_to_world_with(world, entity_map, &registry)
@@ -183,8 +183,9 @@ where
183183

184184
#[cfg(test)]
185185
mod tests {
186-
use bevy_ecs::{entity::EntityMap, reflect::AppTypeRegistry, system::Command, world::World};
186+
use bevy_ecs::{reflect::AppTypeRegistry, system::Command, world::World};
187187
use bevy_hierarchy::{AddChild, Parent};
188+
use bevy_utils::HashMap;
188189

189190
use crate::dynamic_scene_builder::DynamicSceneBuilder;
190191

@@ -213,11 +214,11 @@ mod tests {
213214
scene_builder.extract_entity(original_parent_entity);
214215
scene_builder.extract_entity(original_child_entity);
215216
let scene = scene_builder.build();
216-
let mut entity_map = EntityMap::default();
217+
let mut entity_map = HashMap::default();
217218
scene.write_to_world(&mut world, &mut entity_map).unwrap();
218219

219-
let from_scene_parent_entity = entity_map.get(original_parent_entity).unwrap();
220-
let from_scene_child_entity = entity_map.get(original_child_entity).unwrap();
220+
let &from_scene_parent_entity = entity_map.get(&original_parent_entity).unwrap();
221+
let &from_scene_child_entity = entity_map.get(&original_child_entity).unwrap();
221222

222223
// We then add the parent from the scene as a child of the original child
223224
// Hierarchy should look like:

crates/bevy_scene/src/scene.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use bevy_ecs::{
2-
entity::EntityMap,
32
reflect::{AppTypeRegistry, ReflectComponent, ReflectMapEntities, ReflectResource},
43
world::World,
54
};
65
use bevy_reflect::{TypePath, TypeUuid};
6+
use bevy_utils::HashMap;
77

88
use crate::{DynamicScene, InstanceInfo, SceneSpawnError};
99

@@ -30,7 +30,7 @@ impl Scene {
3030
type_registry: &AppTypeRegistry,
3131
) -> Result<Scene, SceneSpawnError> {
3232
let mut world = World::new();
33-
let mut entity_map = EntityMap::default();
33+
let mut entity_map = HashMap::default();
3434
dynamic_scene.write_to_world_with(&mut world, &mut entity_map, type_registry)?;
3535

3636
Ok(Self { world })
@@ -56,7 +56,7 @@ impl Scene {
5656
type_registry: &AppTypeRegistry,
5757
) -> Result<InstanceInfo, SceneSpawnError> {
5858
let mut instance_info = InstanceInfo {
59-
entity_map: EntityMap::default(),
59+
entity_map: HashMap::default(),
6060
};
6161

6262
let type_registry = type_registry.read();

0 commit comments

Comments
 (0)