1
- use crate :: entity:: Entity ;
1
+ use crate :: { entity:: Entity , world :: World } ;
2
2
use bevy_utils:: { Entry , HashMap } ;
3
- use std:: fmt;
4
-
5
- /// The errors that might be returned while using [`MapEntities::map_entities`].
6
- #[ derive( Debug ) ]
7
- pub enum MapEntitiesError {
8
- EntityNotFound ( Entity ) ,
9
- }
10
-
11
- impl std:: error:: Error for MapEntitiesError { }
12
-
13
- impl fmt:: Display for MapEntitiesError {
14
- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
15
- match self {
16
- MapEntitiesError :: EntityNotFound ( _) => {
17
- write ! ( f, "the given entity does not exist in the map" )
18
- }
19
- }
20
- }
21
- }
22
3
23
4
/// Operation to map all contained [`Entity`] fields in a type to new values.
24
5
///
@@ -33,7 +14,7 @@ impl fmt::Display for MapEntitiesError {
33
14
///
34
15
/// ```rust
35
16
/// use bevy_ecs::prelude::*;
36
- /// use bevy_ecs::entity::{EntityMap , MapEntities, MapEntitiesError };
17
+ /// use bevy_ecs::entity::{EntityMapper , MapEntities};
37
18
///
38
19
/// #[derive(Component)]
39
20
/// struct Spring {
@@ -42,10 +23,9 @@ impl fmt::Display for MapEntitiesError {
42
23
/// }
43
24
///
44
25
/// impl MapEntities for Spring {
45
- /// fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> {
46
- /// self.a = entity_map.get(self.a)?;
47
- /// self.b = entity_map.get(self.b)?;
48
- /// Ok(())
26
+ /// fn map_entities(&mut self, entity_mapper: &mut EntityMapper) {
27
+ /// self.a = entity_mapper.get_or_reserve(self.a);
28
+ /// self.b = entity_mapper.get_or_reserve(self.b);
49
29
/// }
50
30
/// }
51
31
/// ```
@@ -55,16 +35,22 @@ pub trait MapEntities {
55
35
/// Updates all [`Entity`] references stored inside using `entity_map`.
56
36
///
57
37
/// Implementors should look up any and all [`Entity`] values stored within and
58
- /// update them to the mapped values via `entity_map `.
59
- fn map_entities ( & mut self , entity_map : & EntityMap ) -> Result < ( ) , MapEntitiesError > ;
38
+ /// update them to the mapped values via `entity_mapper `.
39
+ fn map_entities ( & mut self , entity_mapper : & mut EntityMapper ) ;
60
40
}
61
41
62
42
/// A mapping from one set of entities to another.
63
43
///
64
44
/// The API generally follows [`HashMap`], but each [`Entity`] is returned by value, as they are [`Copy`].
65
45
///
66
- /// This is typically used to coordinate data transfer between sets of entities, such as between a scene and the world or over the network.
67
- /// This is required as [`Entity`] identifiers are opaque; you cannot and do not want to reuse identifiers directly.
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()`].
68
54
#[ derive( Default , Debug ) ]
69
55
pub struct EntityMap {
70
56
map : HashMap < Entity , Entity > ,
@@ -91,11 +77,8 @@ impl EntityMap {
91
77
}
92
78
93
79
/// Returns the corresponding mapped entity.
94
- pub fn get ( & self , entity : Entity ) -> Result < Entity , MapEntitiesError > {
95
- self . map
96
- . get ( & entity)
97
- . cloned ( )
98
- . ok_or ( MapEntitiesError :: EntityNotFound ( entity) )
80
+ pub fn get ( & self , entity : Entity ) -> Option < Entity > {
81
+ self . map . get ( & entity) . copied ( )
99
82
}
100
83
101
84
/// An iterator visiting all keys in arbitrary order.
@@ -122,4 +105,138 @@ impl EntityMap {
122
105
pub fn iter ( & self ) -> impl Iterator < Item = ( Entity , Entity ) > + ' _ {
123
106
self . map . iter ( ) . map ( |( from, to) | ( * from, * to) )
124
107
}
108
+
109
+ /// Creates an [`EntityMapper`] from this [`EntityMap`] and scoped to the provided [`World`], then calls the
110
+ /// provided function with it. This allows one to allocate new entity references in the provided `World` that are
111
+ /// guaranteed to never point at a living entity now or in the future. This functionality is useful for safely
112
+ /// mapping entity identifiers that point at entities outside the source world. The passed function, `f`, is called
113
+ /// within the scope of the passed world. Its return value is then returned from `world_scope` as the generic type
114
+ /// parameter `R`.
115
+ pub fn world_scope < R > (
116
+ & mut self ,
117
+ world : & mut World ,
118
+ f : impl FnOnce ( & mut World , & mut EntityMapper ) -> R ,
119
+ ) -> R {
120
+ let mut mapper = EntityMapper :: new ( self , world) ;
121
+ let result = f ( world, & mut mapper) ;
122
+ mapper. finish ( world) ;
123
+ result
124
+ }
125
+ }
126
+
127
+ /// A wrapper for [`EntityMap`], augmenting it with the ability to allocate new [`Entity`] references in a destination
128
+ /// world. These newly allocated references are guaranteed to never point to any living entity in that world.
129
+ ///
130
+ /// References are allocated by returning increasing generations starting from an internally initialized base
131
+ /// [`Entity`]. After it is finished being used by [`MapEntities`] implementations, this entity is despawned and the
132
+ /// requisite number of generations reserved.
133
+ pub struct EntityMapper < ' m > {
134
+ /// The wrapped [`EntityMap`].
135
+ map : & ' m mut EntityMap ,
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
+
142
+ impl < ' m > EntityMapper < ' m > {
143
+ /// Returns the corresponding mapped entity or reserves a new dead entity ID if it is absent.
144
+ pub fn get_or_reserve ( & mut self , entity : Entity ) -> Entity {
145
+ if let Some ( mapped) = self . map . get ( entity) {
146
+ return mapped;
147
+ }
148
+
149
+ // this new entity reference is specifically designed to never represent any living entity
150
+ let new = Entity {
151
+ generation : self . dead_start . generation + self . generations ,
152
+ index : self . dead_start . index ,
153
+ } ;
154
+ self . generations += 1 ;
155
+
156
+ self . map . insert ( entity, new) ;
157
+
158
+ new
159
+ }
160
+
161
+ /// Gets a reference to the underlying [`EntityMap`].
162
+ pub fn get_map ( & ' m self ) -> & ' m EntityMap {
163
+ self . map
164
+ }
165
+
166
+ /// Gets a mutable reference to the underlying [`EntityMap`]
167
+ pub fn get_map_mut ( & ' m mut self ) -> & ' m mut EntityMap {
168
+ self . map
169
+ }
170
+
171
+ /// Creates a new [`EntityMapper`], spawning a temporary base [`Entity`] in the provided [`World`]
172
+ fn new ( map : & ' m mut EntityMap , world : & mut World ) -> Self {
173
+ Self {
174
+ map,
175
+ // SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`
176
+ dead_start : unsafe { world. entities_mut ( ) . alloc ( ) } ,
177
+ generations : 0 ,
178
+ }
179
+ }
180
+
181
+ /// Reserves the allocated references to dead entities within the world. This frees the temporary base
182
+ /// [`Entity`] while reserving extra generations via [`crate::entity::Entities::reserve_generations`]. Because this
183
+ /// renders the [`EntityMapper`] unable to safely allocate any more references, this method takes ownership of
184
+ /// `self` in order to render it unusable.
185
+ fn finish ( self , world : & mut World ) {
186
+ // SAFETY: Entities data is kept in a valid state via `EntityMap::world_scope`
187
+ let entities = unsafe { world. entities_mut ( ) } ;
188
+ assert ! ( entities. free( self . dead_start) . is_some( ) ) ;
189
+ assert ! ( entities. reserve_generations( self . dead_start. index, self . generations) ) ;
190
+ }
191
+ }
192
+
193
+ #[ cfg( test) ]
194
+ mod tests {
195
+ use super :: { EntityMap , EntityMapper } ;
196
+ use crate :: { entity:: Entity , world:: World } ;
197
+
198
+ #[ test]
199
+ fn entity_mapper ( ) {
200
+ const FIRST_IDX : u32 = 1 ;
201
+ const SECOND_IDX : u32 = 2 ;
202
+
203
+ let mut map = EntityMap :: default ( ) ;
204
+ let mut world = World :: new ( ) ;
205
+ let mut mapper = EntityMapper :: new ( & mut map, & mut world) ;
206
+
207
+ let mapped_ent = Entity :: new ( FIRST_IDX , 0 ) ;
208
+ let dead_ref = mapper. get_or_reserve ( mapped_ent) ;
209
+
210
+ assert_eq ! (
211
+ dead_ref,
212
+ mapper. get_or_reserve( mapped_ent) ,
213
+ "should persist the allocated mapping from the previous line"
214
+ ) ;
215
+ assert_eq ! (
216
+ mapper. get_or_reserve( Entity :: new( SECOND_IDX , 0 ) ) . index( ) ,
217
+ dead_ref. index( ) ,
218
+ "should re-use the same index for further dead refs"
219
+ ) ;
220
+
221
+ mapper. finish ( & mut world) ;
222
+ // Next allocated entity should be a further generation on the same index
223
+ let entity = world. spawn_empty ( ) . id ( ) ;
224
+ assert_eq ! ( entity. index( ) , dead_ref. index( ) ) ;
225
+ assert ! ( entity. generation( ) > dead_ref. generation( ) ) ;
226
+ }
227
+
228
+ #[ test]
229
+ fn world_scope_reserves_generations ( ) {
230
+ let mut map = EntityMap :: default ( ) ;
231
+ let mut world = World :: new ( ) ;
232
+
233
+ let dead_ref = map. world_scope ( & mut world, |_, mapper| {
234
+ mapper. get_or_reserve ( Entity :: new ( 0 , 0 ) )
235
+ } ) ;
236
+
237
+ // Next allocated entity should be a further generation on the same index
238
+ let entity = world. spawn_empty ( ) . id ( ) ;
239
+ assert_eq ! ( entity. index( ) , dead_ref. index( ) ) ;
240
+ assert ! ( entity. generation( ) > dead_ref. generation( ) ) ;
241
+ }
125
242
}
0 commit comments