Skip to content

Commit 8a164b7

Browse files
authored
Call world.flush() after mark_spawn_despawn() (#21397)
# Objective During despawn, commands are flushed before writing the `despawned_by` location. This means that commands run by observers will not see the updated value. It also means an entity spawned by one of those commands that re-uses the entity index may have its `spawned_by` overwritten by the `despawned_by` meant for the original entity. ## Solution Move the call to `world.flush()` after the call to `mark_spawn_despawn()`. ## Testing Added a unit test demonstrating the issue.
1 parent 6687279 commit 8a164b7

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

crates/bevy_ecs/src/world/entity_ref.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2703,14 +2703,15 @@ impl<'w> EntityWorldMut<'w> {
27032703
world.archetypes[moved_location.archetype_id]
27042704
.set_entity_table_row(moved_location.archetype_row, table_row);
27052705
}
2706-
world.flush();
27072706

27082707
// SAFETY: `self.entity` is a valid entity index
27092708
unsafe {
27102709
world
27112710
.entities
27122711
.mark_spawn_despawn(self.entity.index(), caller, change_tick);
27132712
}
2713+
2714+
world.flush();
27142715
}
27152716

27162717
/// Ensures any commands triggered by the actions of Self are applied, equivalent to [`World::flush`]
@@ -6765,4 +6766,34 @@ mod tests {
67656766
let spawn_after = world.entity(id3).spawned_by();
67666767
assert_eq!(spawn, spawn_after);
67676768
}
6769+
6770+
#[test]
6771+
fn spawned_by_set_before_flush() {
6772+
#[derive(Component)]
6773+
#[component(on_despawn = on_despawn)]
6774+
struct C;
6775+
6776+
fn on_despawn(mut world: DeferredWorld, context: HookContext) {
6777+
let spawned = world.entity(context.entity).spawned_by();
6778+
world.commands().queue(move |world: &mut World| {
6779+
// The entity has finished despawning...
6780+
assert!(world.get_entity(context.entity).is_err());
6781+
let despawned = world
6782+
.entities()
6783+
.entity_get_spawned_or_despawned_by(context.entity);
6784+
// These assertions are only possible if the `track_location` feature is enabled
6785+
if let (Some(spawned), Some(despawned)) =
6786+
(spawned.into_option(), despawned.into_option())
6787+
{
6788+
// ... so ensure that `despawned_by` has been written
6789+
assert!(despawned.is_some());
6790+
assert_ne!(Some(spawned), despawned);
6791+
}
6792+
});
6793+
}
6794+
6795+
let mut world = World::new();
6796+
let original = world.spawn(C).id();
6797+
world.despawn(original);
6798+
}
67686799
}

0 commit comments

Comments
 (0)