@@ -26,6 +26,17 @@ pub(crate) struct BundleRemover<'w> {
2626 pub ( crate ) relationship_hook_mode : RelationshipHookMode ,
2727}
2828
29+ #[ inline]
30+ fn explicit_components_in_archetype < ' a > (
31+ explicit_components : & ' a [ ComponentId ] ,
32+ archetype : & ' a Archetype ,
33+ ) -> impl Iterator < Item = ComponentId > + ' a {
34+ explicit_components
35+ . iter ( )
36+ . copied ( )
37+ . filter ( move |component_id| archetype. contains ( * component_id) )
38+ }
39+
2940impl < ' w > BundleRemover < ' w > {
3041 /// Creates a new [`BundleRemover`], if such a remover would do anything.
3142 ///
@@ -136,195 +147,210 @@ impl<'w> BundleRemover<'w> {
136147 & [ ComponentId ] ,
137148 ) -> ( bool , T ) ,
138149 ) -> ( EntityLocation , T ) {
139- // Hooks
150+ let bundle_info = self . bundle_info . as_ref ( ) ;
151+ let explicit_components = bundle_info. explicit_components ( ) ;
152+ let old_archetype_id = location. archetype_id ;
153+ // Save flags before mutation invalidates archetype references.
154+ let has_after_remove_hook = self . old_archetype . as_ref ( ) . has_after_remove_hook ( ) ;
155+ let has_after_remove_observer = self . old_archetype . as_ref ( ) . has_after_remove_observer ( ) ;
156+
140157 // SAFETY: all bundle components exist in World
141158 unsafe {
142- // SAFETY: We only keep access to archetype/bundle data.
159+ let old_archetype = self . old_archetype . as_ref ( ) ;
160+ let new_archetype = self . new_archetype . as_ref ( ) ;
161+ let has_discard_observer = old_archetype. has_discard_observer ( ) ;
162+ let has_remove_observer = old_archetype. has_remove_observer ( ) ;
163+ let observer_components = ( has_discard_observer || has_remove_observer) . then ( || {
164+ explicit_components_in_archetype ( explicit_components, old_archetype)
165+ . collect :: < Vec < _ > > ( )
166+ } ) ;
167+
168+ // SAFETY: We only keep access to archetype metadata.
143169 let mut deferred_world = self . world . into_deferred ( ) ;
144- let bundle_components_in_archetype = || {
145- self . bundle_info
146- . as_ref ( )
147- . iter_explicit_components ( )
148- . filter ( |component_id| self . old_archetype . as_ref ( ) . contains ( * component_id) )
149- } ;
150- if self . old_archetype . as_ref ( ) . has_discard_observer ( ) {
151- let components = bundle_components_in_archetype ( ) . collect :: < Vec < _ > > ( ) ;
170+ if has_discard_observer {
171+ let components = observer_components. as_ref ( ) . unwrap ( ) ;
152172 // SAFETY: the DISCARD event_key corresponds to the Discard event's type
153173 deferred_world. trigger_raw (
154174 DISCARD ,
155175 & mut Discard { entity } ,
156176 & mut EntityComponentsTrigger {
157- components : & components ,
158- old_archetype : Some ( self . old_archetype . as_ref ( ) ) ,
159- new_archetype : Some ( self . new_archetype . as_ref ( ) ) ,
177+ components,
178+ old_archetype : Some ( old_archetype) ,
179+ new_archetype : Some ( new_archetype) ,
160180 } ,
161181 caller,
162182 ) ;
163183 }
164184 deferred_world. trigger_on_discard (
165- self . old_archetype . as_ref ( ) ,
185+ old_archetype,
166186 entity,
167- bundle_components_in_archetype ( ) ,
187+ explicit_components_in_archetype ( explicit_components , old_archetype ) ,
168188 caller,
169189 self . relationship_hook_mode ,
170190 ) ;
171- if self . old_archetype . as_ref ( ) . has_remove_observer ( ) {
172- let components = bundle_components_in_archetype ( ) . collect :: < Vec < _ > > ( ) ;
191+ if has_remove_observer {
192+ let components = observer_components . as_ref ( ) . unwrap ( ) ;
173193 // SAFETY: the REMOVE event_key corresponds to the Remove event's type
174194 deferred_world. trigger_raw (
175195 REMOVE ,
176196 & mut Remove { entity } ,
177197 & mut EntityComponentsTrigger {
178- components : & components ,
179- old_archetype : Some ( self . old_archetype . as_ref ( ) ) ,
180- new_archetype : Some ( self . new_archetype . as_ref ( ) ) ,
198+ components,
199+ old_archetype : Some ( old_archetype) ,
200+ new_archetype : Some ( new_archetype) ,
181201 } ,
182202 caller,
183203 ) ;
184204 }
185205 deferred_world. trigger_on_remove (
186- self . old_archetype . as_ref ( ) ,
206+ old_archetype,
187207 entity,
188- bundle_components_in_archetype ( ) ,
208+ explicit_components_in_archetype ( explicit_components , old_archetype ) ,
189209 caller,
190210 ) ;
191211 }
192212
193- // SAFETY: We still have the cell, so this is unique, it doesn't conflict with other references, and we drop it shortly.
194- let world = unsafe { self . world . world_mut ( ) } ;
195-
196- let ( needs_drop, pre_remove_result) = pre_remove (
197- & mut world. storages . sparse_sets ,
198- self . old_and_new_table
199- . as_ref ( )
200- // SAFETY: There is no conflicting access for this scope.
201- . map ( |( old, _) | unsafe { & mut * old. as_ptr ( ) } ) ,
202- & world. components ,
203- self . bundle_info . as_ref ( ) . explicit_components ( ) ,
204- ) ;
205-
206- // Handle sparse set removes
207- for component_id in self . bundle_info . as_ref ( ) . iter_explicit_components ( ) {
208- if self . old_archetype . as_ref ( ) . contains ( component_id) {
209- world. removed_components . write ( component_id, entity) ;
210-
211- // Make sure to drop components stored in sparse sets.
212- // Dense components are dropped later in `move_to_and_drop_missing_unchecked`.
213- if let Some ( StorageType :: SparseSet ) =
214- self . old_archetype . as_ref ( ) . get_storage_type ( component_id)
215- {
216- world
217- . storages
218- . sparse_sets
219- . get_mut ( component_id)
220- // Set exists because the component existed on the entity
221- . unwrap ( )
222- // If it was already forgotten, it would not be in the set.
223- . remove ( entity) ;
224- }
225- }
226- }
213+ let ( new_location, pre_remove_result) = {
214+ // SAFETY: We still have the cell, so this is unique, it doesn't conflict with other references, and we drop it shortly.
215+ let world = unsafe { self . world . world_mut ( ) } ;
227216
228- // Handle archetype change
229- let remove_result = self
230- . old_archetype
231- . as_mut ( )
232- . swap_remove ( location. archetype_row ) ;
233- // if an entity was moved into this entity's archetype row, update its archetype row
234- if let Some ( swapped_entity) = remove_result. swapped_entity {
235- let swapped_location = world. entities . get_spawned ( swapped_entity) . unwrap ( ) ;
236-
237- world. entities . update_existing_location (
238- swapped_entity. index ( ) ,
239- Some ( EntityLocation {
240- archetype_id : swapped_location. archetype_id ,
241- archetype_row : location. archetype_row ,
242- table_id : swapped_location. table_id ,
243- table_row : swapped_location. table_row ,
244- } ) ,
217+ let ( needs_drop, pre_remove_result) = pre_remove (
218+ & mut world. storages . sparse_sets ,
219+ self . old_and_new_table
220+ . as_ref ( )
221+ // SAFETY: There is no conflicting access for this scope.
222+ . map ( |( old, _) | unsafe { & mut * old. as_ptr ( ) } ) ,
223+ & world. components ,
224+ explicit_components,
245225 ) ;
246- }
247226
248- // Handle table change
249- let new_location = if let Some ( ( mut old_table, mut new_table) ) = self . old_and_new_table {
250- let move_result = if needs_drop {
251- // SAFETY: old_table_row exists
252- unsafe {
253- old_table
254- . as_mut ( )
255- . move_to_and_drop_missing_unchecked ( location. table_row , new_table. as_mut ( ) )
256- }
257- } else {
258- // SAFETY: old_table_row exists
259- unsafe {
260- old_table. as_mut ( ) . move_to_and_forget_missing_unchecked (
261- location. table_row ,
262- new_table. as_mut ( ) ,
263- )
227+ {
228+ let old_archetype = self . old_archetype . as_ref ( ) ;
229+ for & component_id in explicit_components {
230+ if !old_archetype. contains ( component_id) {
231+ continue ;
232+ }
233+ world. removed_components . write ( component_id, entity) ;
234+
235+ // Make sure to drop components stored in sparse sets.
236+ // Dense components are dropped later in `move_to_and_drop_missing_unchecked`.
237+ if let Some ( StorageType :: SparseSet ) =
238+ old_archetype. get_storage_type ( component_id)
239+ {
240+ world
241+ . storages
242+ . sparse_sets
243+ . get_mut ( component_id)
244+ // Set exists because the component existed on the entity
245+ . unwrap ( )
246+ // If it was already forgotten, it would not be in the set.
247+ . remove ( entity) ;
248+ }
264249 }
265- } ;
266-
267- // SAFETY: move_result.new_row is a valid position in new_archetype's table
268- let new_location = unsafe {
269- self . new_archetype
270- . as_mut ( )
271- . allocate ( entity, move_result. new_row )
272- } ;
250+ }
273251
274- // if an entity was moved into this entity's table row, update its table row
275- if let Some ( swapped_entity) = move_result. swapped_entity {
252+ let remove_result = self
253+ . old_archetype
254+ . as_mut ( )
255+ . swap_remove ( location. archetype_row ) ;
256+ if let Some ( swapped_entity) = remove_result. swapped_entity {
276257 let swapped_location = world. entities . get_spawned ( swapped_entity) . unwrap ( ) ;
277258
278259 world. entities . update_existing_location (
279260 swapped_entity. index ( ) ,
280261 Some ( EntityLocation {
281262 archetype_id : swapped_location. archetype_id ,
282- archetype_row : swapped_location . archetype_row ,
263+ archetype_row : location . archetype_row ,
283264 table_id : swapped_location. table_id ,
284- table_row : location . table_row ,
265+ table_row : swapped_location . table_row ,
285266 } ) ,
286267 ) ;
287- world. archetypes [ swapped_location. archetype_id ]
288- . set_entity_table_row ( swapped_location. archetype_row , location. table_row ) ;
289268 }
290269
291- new_location
292- } else {
293- // The tables are the same
294- self . new_archetype
295- . as_mut ( )
296- . allocate ( entity, location. table_row )
297- } ;
298-
299- // SAFETY: The entity is valid and has been moved to the new location already.
300- unsafe {
301- world
302- . entities
303- . update_existing_location ( entity. index ( ) , Some ( new_location) ) ;
304- }
270+ let new_location = if let Some ( ( mut old_table, mut new_table) ) = self . old_and_new_table
271+ {
272+ let move_result = if needs_drop {
273+ // SAFETY: old_table_row exists
274+ unsafe {
275+ old_table. as_mut ( ) . move_to_and_drop_missing_unchecked (
276+ location. table_row ,
277+ new_table. as_mut ( ) ,
278+ )
279+ }
280+ } else {
281+ // SAFETY: old_table_row exists
282+ unsafe {
283+ old_table. as_mut ( ) . move_to_and_forget_missing_unchecked (
284+ location. table_row ,
285+ new_table. as_mut ( ) ,
286+ )
287+ }
288+ } ;
305289
306- // Trigger AfterRemove for removed components.
307- // The entity is now in the new archetype; component data has been dropped.
308- // SAFETY: all bundle components exist in World
309- unsafe {
310- let old_archetype = self . old_archetype . as_ref ( ) ;
311- if old_archetype. has_after_remove_hook ( ) || old_archetype. has_after_remove_observer ( ) {
312- let mut deferred_world = self . world . into_deferred ( ) ;
313- let bundle_components_in_archetype = || {
314- self . bundle_info
315- . as_ref ( )
316- . iter_explicit_components ( )
317- . filter ( |component_id| old_archetype. contains ( * component_id) )
290+ // SAFETY: move_result.new_row is a valid position in new_archetype's table
291+ let allocated_location = unsafe {
292+ self . new_archetype
293+ . as_mut ( )
294+ . allocate ( entity, move_result. new_row )
318295 } ;
296+
297+ if let Some ( swapped_entity) = move_result. swapped_entity {
298+ let swapped_location = world. entities . get_spawned ( swapped_entity) . unwrap ( ) ;
299+
300+ world. entities . update_existing_location (
301+ swapped_entity. index ( ) ,
302+ Some ( EntityLocation {
303+ archetype_id : swapped_location. archetype_id ,
304+ archetype_row : swapped_location. archetype_row ,
305+ table_id : swapped_location. table_id ,
306+ table_row : location. table_row ,
307+ } ) ,
308+ ) ;
309+ world. archetypes [ swapped_location. archetype_id ]
310+ . set_entity_table_row ( swapped_location. archetype_row , location. table_row ) ;
311+ }
312+
313+ allocated_location
314+ } else {
315+ self . new_archetype
316+ . as_mut ( )
317+ . allocate ( entity, location. table_row )
318+ } ;
319+
320+ // SAFETY: The entity is valid and has been moved to the new location already.
321+ unsafe {
322+ world
323+ . entities
324+ . update_existing_location ( entity. index ( ) , Some ( new_location) ) ;
325+ }
326+
327+ ( new_location, pre_remove_result)
328+ } ;
329+
330+ // Trigger AfterRemove after the entity has moved and removed data is gone.
331+ // SAFETY:
332+ // - all bundle components exist in World
333+ // - only type metadata is read from archetypes during these hooks
334+ if has_after_remove_hook || has_after_remove_observer {
335+ // SAFETY:
336+ // - `old_archetype_id` and `new_location.archetype_id` are valid IDs
337+ // - only type metadata is read from archetypes while triggering hooks/observers
338+ unsafe {
339+ let world = self . world ;
340+ let archetypes = world. archetypes ( ) ;
341+ let old_archetype = & archetypes[ old_archetype_id] ;
342+ let mut deferred_world = world. into_deferred ( ) ;
319343 deferred_world. trigger_after_remove (
320344 old_archetype,
321345 entity,
322- bundle_components_in_archetype ( ) ,
346+ explicit_components_in_archetype ( explicit_components , old_archetype ) ,
323347 caller,
324348 ) ;
325- if old_archetype. has_after_remove_observer ( ) {
326- let removed_components = bundle_components_in_archetype ( ) . collect :: < Vec < _ > > ( ) ;
327- let new_archetype = self . new_archetype . as_ref ( ) ;
349+ if has_after_remove_observer {
350+ let new_archetype = & archetypes[ new_location. archetype_id ] ;
351+ let removed_components =
352+ explicit_components_in_archetype ( explicit_components, old_archetype)
353+ . collect :: < Vec < _ > > ( ) ;
328354 deferred_world. trigger_raw (
329355 AFTER_REMOVE ,
330356 & mut AfterRemove { entity } ,
0 commit comments