bevy_ecs: flush entities after running observers and hooks in despawn (#15398)
# Objective Fixes #14467 Observers and component lifecycle hooks are allowed to perform operations that subsequently require `Entities` to be flushed, such as reserving a new entity. If this occurs during an `on_remove` hook or an `OnRemove` event trigger during an `EntityWorldMut::despawn`, a panic will occur. ## Solution Call `world.flush_entities()` after running `on_remove` hooks/observers during `despawn` ## Testing Added a new test that fails before the fix and succeeds afterward.
This commit is contained in:
		
							parent
							
								
									1a41c736b3
								
							
						
					
					
						commit
						3d0f2409d5
					
				| @ -1160,4 +1160,26 @@ mod tests { | ||||
|         world.flush(); | ||||
|         assert_eq!(vec!["event", "event"], world.resource::<Order>().0); | ||||
|     } | ||||
| 
 | ||||
|     // Regression test for https://github.com/bevyengine/bevy/issues/14467
 | ||||
|     // Fails prior to https://github.com/bevyengine/bevy/pull/15398
 | ||||
|     #[test] | ||||
|     fn observer_on_remove_during_despawn_spawn_empty() { | ||||
|         let mut world = World::new(); | ||||
| 
 | ||||
|         // Observe the removal of A - this will run during despawn
 | ||||
|         world.observe(|_: Trigger<OnRemove, A>, mut cmd: Commands| { | ||||
|             // Spawn a new entity - this reserves a new ID and requires a flush
 | ||||
|             // afterward before Entities::free can be called.
 | ||||
|             cmd.spawn_empty(); | ||||
|         }); | ||||
| 
 | ||||
|         let ent = world.spawn(A).id(); | ||||
| 
 | ||||
|         // Despawn our entity, which runs the OnRemove observer and allocates a
 | ||||
|         // new Entity.
 | ||||
|         // Should not panic - if it does, then Entities was not flushed properly
 | ||||
|         // after the observer's spawn_empty.
 | ||||
|         world.despawn(ent); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1297,7 +1297,6 @@ impl<'w> EntityWorldMut<'w> { | ||||
|     /// See [`World::despawn`] for more details.
 | ||||
|     pub fn despawn(self) { | ||||
|         let world = self.world; | ||||
|         world.flush_entities(); | ||||
|         let archetype = &world.archetypes[self.location.archetype_id]; | ||||
| 
 | ||||
|         // SAFETY: Archetype cannot be mutably aliased by DeferredWorld
 | ||||
| @ -1323,6 +1322,10 @@ impl<'w> EntityWorldMut<'w> { | ||||
|             world.removed_components.send(component_id, self.entity); | ||||
|         } | ||||
| 
 | ||||
|         // Observers and on_remove hooks may reserve new entities, which
 | ||||
|         // requires a flush before Entities::free may be called.
 | ||||
|         world.flush_entities(); | ||||
| 
 | ||||
|         let location = world | ||||
|             .entities | ||||
|             .free(self.entity) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Josh Robson Chase
						Josh Robson Chase