bevy_ecs/entity/map_entities.rs
1use crate::{
2 entity::Entity,
3 identifier::masks::{IdentifierMask, HIGH_MASK},
4 world::World,
5};
6
7use super::{EntityHashMap, VisitEntitiesMut};
8
9/// Operation to map all contained [`Entity`] fields in a type to new values.
10///
11/// As entity IDs are valid only for the [`World`] they're sourced from, using [`Entity`]
12/// as references in components copied from another world will be invalid. This trait
13/// allows defining custom mappings for these references via [`EntityMappers`](EntityMapper), which
14/// inject the entity mapping strategy between your `MapEntities` type and the current world
15/// (usually by using an [`EntityHashMap<Entity>`] between source entities and entities in the
16/// current world).
17///
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///
23/// Implementing this trait correctly is required for properly loading components
24/// with entity references from scenes.
25///
26/// [`HashSet<Entity>`]: bevy_utils::HashSet
27///
28/// ## Example
29///
30/// ```
31/// use bevy_ecs::prelude::*;
32/// use bevy_ecs::entity::MapEntities;
33///
34/// #[derive(Component)]
35/// struct Spring {
36/// a: Entity,
37/// b: Entity,
38/// }
39///
40/// impl MapEntities for Spring {
41/// fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
42/// self.a = entity_mapper.map_entity(self.a);
43/// self.b = entity_mapper.map_entity(self.b);
44/// }
45/// }
46/// ```
47pub trait MapEntities {
48 /// Updates all [`Entity`] references stored inside using `entity_mapper`.
49 ///
50 /// Implementors should look up any and all [`Entity`] values stored within `self` and
51 /// update them to the mapped values via `entity_mapper`.
52 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M);
53}
54
55impl<T: VisitEntitiesMut> MapEntities for T {
56 fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
57 self.visit_entities_mut(|entity| {
58 *entity = entity_mapper.map_entity(*entity);
59 });
60 }
61}
62
63/// An implementor of this trait knows how to map an [`Entity`] into another [`Entity`].
64///
65/// Usually this is done by using an [`EntityHashMap<Entity>`] to map source entities
66/// (mapper inputs) to the current world's entities (mapper outputs).
67///
68/// More generally, this can be used to map [`Entity`] references between any two [`Worlds`](World).
69///
70/// ## Example
71///
72/// ```
73/// # use bevy_ecs::entity::{Entity, EntityMapper};
74/// # use bevy_ecs::entity::EntityHashMap;
75/// #
76/// pub struct SimpleEntityMapper {
77/// map: EntityHashMap<Entity>,
78/// }
79///
80/// // Example implementation of EntityMapper where we map an entity to another entity if it exists
81/// // in the underlying `EntityHashMap`, otherwise we just return the original entity.
82/// impl EntityMapper for SimpleEntityMapper {
83/// fn map_entity(&mut self, entity: Entity) -> Entity {
84/// self.map.get(&entity).copied().unwrap_or(entity)
85/// }
86/// }
87/// ```
88pub trait EntityMapper {
89 /// Map an entity to another entity
90 fn map_entity(&mut self, entity: Entity) -> Entity;
91}
92
93impl EntityMapper for &mut dyn EntityMapper {
94 fn map_entity(&mut self, entity: Entity) -> Entity {
95 (*self).map_entity(entity)
96 }
97}
98
99impl EntityMapper for SceneEntityMapper<'_> {
100 /// Returns the corresponding mapped entity or reserves a new dead entity ID in the current world if it is absent.
101 fn map_entity(&mut self, entity: Entity) -> Entity {
102 if let Some(&mapped) = self.map.get(&entity) {
103 return mapped;
104 }
105
106 // this new entity reference is specifically designed to never represent any living entity
107 let new = Entity::from_raw_and_generation(
108 self.dead_start.index(),
109 IdentifierMask::inc_masked_high_by(self.dead_start.generation, self.generations),
110 );
111
112 // Prevent generations counter from being a greater value than HIGH_MASK.
113 self.generations = (self.generations + 1) & HIGH_MASK;
114
115 self.map.insert(entity, new);
116
117 new
118 }
119}
120
121/// A wrapper for [`EntityHashMap<Entity>`], augmenting it with the ability to allocate new [`Entity`] references in a destination
122/// world. These newly allocated references are guaranteed to never point to any living entity in that world.
123///
124/// References are allocated by returning increasing generations starting from an internally initialized base
125/// [`Entity`]. After it is finished being used, this entity is despawned and the requisite number of generations reserved.
126pub struct SceneEntityMapper<'m> {
127 /// A mapping from one set of entities to another.
128 ///
129 /// This is typically used to coordinate data transfer between sets of entities, such as between a scene and the world
130 /// or over the network. This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse
131 /// identifiers directly.
132 ///
133 /// On its own, a [`EntityHashMap<Entity>`] is not capable of allocating new entity identifiers, which is needed to map references
134 /// to entities that lie outside the source entity set. This functionality can be accessed through [`SceneEntityMapper::world_scope()`].
135 map: &'m mut EntityHashMap<Entity>,
136 /// A base [`Entity`] used to allocate new references.
137 dead_start: Entity,
138 /// The number of generations this mapper has allocated thus far.
139 generations: u32,
140}
141
142impl<'m> SceneEntityMapper<'m> {
143 /// Gets a reference to the underlying [`EntityHashMap<Entity>`].
144 pub fn get_map(&'m self) -> &'m EntityHashMap<Entity> {
145 self.map
146 }
147
148 /// Gets a mutable reference to the underlying [`EntityHashMap<Entity>`].
149 pub fn get_map_mut(&'m mut self) -> &'m mut EntityHashMap<Entity> {
150 self.map
151 }
152
153 /// Creates a new [`SceneEntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
154 pub fn new(map: &'m mut EntityHashMap<Entity>, world: &mut World) -> Self {
155 // We're going to be calling methods on `Entities` that require advance
156 // flushing, such as `alloc` and `free`.
157 world.flush_entities();
158 Self {
159 map,
160 // SAFETY: Entities data is kept in a valid state via `EntityMapper::world_scope`
161 dead_start: unsafe { world.entities_mut().alloc() },
162 generations: 0,
163 }
164 }
165
166 /// Reserves the allocated references to dead entities within the world. This frees the temporary base
167 /// [`Entity`] while reserving extra generations. Because this makes the [`SceneEntityMapper`] unable to
168 /// safely allocate any more references, this method takes ownership of `self` in order to render it unusable.
169 pub fn finish(self, world: &mut World) {
170 // SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`
171 let entities = unsafe { world.entities_mut() };
172 assert!(entities.free(self.dead_start).is_some());
173 assert!(entities.reserve_generations(self.dead_start.index(), self.generations));
174 }
175
176 /// Creates an [`SceneEntityMapper`] from a provided [`World`] and [`EntityHashMap<Entity>`], then calls the
177 /// provided function with it. This allows one to allocate new entity references in this [`World`] that are
178 /// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
179 /// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
180 /// within the scope of this world. Its return value is then returned from `world_scope` as the generic type
181 /// parameter `R`.
182 pub fn world_scope<R>(
183 entity_map: &'m mut EntityHashMap<Entity>,
184 world: &mut World,
185 f: impl FnOnce(&mut World, &mut Self) -> R,
186 ) -> R {
187 let mut mapper = Self::new(entity_map, world);
188 let result = f(world, &mut mapper);
189 mapper.finish(world);
190 result
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use crate::{
197 entity::{Entity, EntityHashMap, EntityMapper, SceneEntityMapper},
198 world::World,
199 };
200
201 #[test]
202 fn entity_mapper() {
203 const FIRST_IDX: u32 = 1;
204 const SECOND_IDX: u32 = 2;
205
206 let mut map = EntityHashMap::default();
207 let mut world = World::new();
208 let mut mapper = SceneEntityMapper::new(&mut map, &mut world);
209
210 let mapped_ent = Entity::from_raw(FIRST_IDX);
211 let dead_ref = mapper.map_entity(mapped_ent);
212
213 assert_eq!(
214 dead_ref,
215 mapper.map_entity(mapped_ent),
216 "should persist the allocated mapping from the previous line"
217 );
218 assert_eq!(
219 mapper.map_entity(Entity::from_raw(SECOND_IDX)).index(),
220 dead_ref.index(),
221 "should re-use the same index for further dead refs"
222 );
223
224 mapper.finish(&mut world);
225 // Next allocated entity should be a further generation on the same index
226 let entity = world.spawn_empty().id();
227 assert_eq!(entity.index(), dead_ref.index());
228 assert!(entity.generation() > dead_ref.generation());
229 }
230
231 #[test]
232 fn world_scope_reserves_generations() {
233 let mut map = EntityHashMap::default();
234 let mut world = World::new();
235
236 let dead_ref = SceneEntityMapper::world_scope(&mut map, &mut world, |_, mapper| {
237 mapper.map_entity(Entity::from_raw(0))
238 });
239
240 // Next allocated entity should be a further generation on the same index
241 let entity = world.spawn_empty().id();
242 assert_eq!(entity.index(), dead_ref.index());
243 assert!(entity.generation() > dead_ref.generation());
244 }
245
246 #[test]
247 fn entity_mapper_no_panic() {
248 let mut world = World::new();
249 // "Dirty" the `Entities`, requiring a flush afterward.
250 world.entities.reserve_entity();
251 assert!(world.entities.needs_flush());
252
253 // Create and exercise a SceneEntityMapper - should not panic because it flushes
254 // `Entities` first.
255 SceneEntityMapper::world_scope(&mut Default::default(), &mut world, |_, m| {
256 m.map_entity(Entity::PLACEHOLDER);
257 });
258
259 // The SceneEntityMapper should leave `Entities` in a flushed state.
260 assert!(!world.entities.needs_flush());
261 }
262}