From 56bdd5c3c1fe0cf4772afd0e4bd7bb585ee61c3f Mon Sep 17 00:00:00 2001 From: Manuel Brea Carreras Date: Fri, 30 May 2025 20:33:47 +0100 Subject: [PATCH] Fix #19219 by moving observer triggers out of resource_scope (#19221) # Objective Fixes #19219 ## Solution Instead of calling `world.commands().trigger` and `world.commands().trigger_targets` whenever each scene is spawned, save the `instance_id` and optional parent entity to perform all such calls at the end. This prevents the potential flush of the world command queue that can happen if `add_child` is called from causing the crash. ## Testing - Did you test these changes? If so, how? - Verified that I can no longer reproduce the bug with the instructions at #19219. - Ran `bevy_scene` tests - Visually verified that the following examples still run as expected `many_foxes`, `scene` . (should I test any more?) - Are there any parts that need more testing? - Pending to run `cargo test` at the root to test that all examples still build; I will update the PR when that's done - How can other people (reviewers) test your changes? Is there anything specific they need to know? - Run bevy as usual - If relevant, what platforms did you test these changes on, and are there any important ones you can't test? - N/a (tested on Linux/wayland but it shouldn't be relevant) --- --- crates/bevy_scene/src/scene_spawner.rs | 27 +++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index dce5ad971e..be9df93607 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -79,6 +79,7 @@ pub struct SceneSpawner { scenes_to_despawn: Vec>, instances_to_despawn: Vec, scenes_with_parent: Vec<(InstanceId, Entity)>, + instances_ready: Vec<(InstanceId, Option)>, } /// Errors that can occur when spawning a scene. @@ -337,8 +338,9 @@ impl SceneSpawner { // Scenes with parents need more setup before they are ready. // See `set_scene_instance_parent_sync()`. if parent.is_none() { - // Defer via commands otherwise SceneSpawner is not available in the observer. - world.commands().trigger(SceneInstanceReady { instance_id }); + // We trigger `SceneInstanceReady` events after processing all scenes + // SceneSpawner may not be available in the observer. + self.instances_ready.push((instance_id, None)); } } Err(SceneSpawnError::NonExistentScene { .. }) => { @@ -362,8 +364,9 @@ impl SceneSpawner { // Scenes with parents need more setup before they are ready. // See `set_scene_instance_parent_sync()`. if parent.is_none() { - // Defer via commands otherwise SceneSpawner is not available in the observer. - world.commands().trigger(SceneInstanceReady { instance_id }); + // We trigger `SceneInstanceReady` events after processing all scenes + // SceneSpawner may not be available in the observer. + self.instances_ready.push((instance_id, None)); } } Err(SceneSpawnError::NonExistentRealScene { .. }) => { @@ -398,12 +401,25 @@ impl SceneSpawner { } } + // We trigger `SceneInstanceReady` events after processing all scenes + // SceneSpawner may not be available in the observer. + self.instances_ready.push((instance_id, Some(parent))); + } else { + self.scenes_with_parent.push((instance_id, parent)); + } + } + } + + fn trigger_scene_ready_events(&mut self, world: &mut World) { + for (instance_id, parent) in self.instances_ready.drain(..) { + if let Some(parent) = parent { // Defer via commands otherwise SceneSpawner is not available in the observer. world .commands() .trigger_targets(SceneInstanceReady { instance_id }, parent); } else { - self.scenes_with_parent.push((instance_id, parent)); + // Defer via commands otherwise SceneSpawner is not available in the observer. + world.commands().trigger(SceneInstanceReady { instance_id }); } } } @@ -477,6 +493,7 @@ pub fn scene_spawner_system(world: &mut World) { .update_spawned_scenes(world, &updated_spawned_scenes) .unwrap(); scene_spawner.set_scene_instance_parent_sync(world); + scene_spawner.trigger_scene_ready_events(world); }); }