From bfd224f3dc1de5b278cd6954683e751ffd7b0c76 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 15 Apr 2026 19:24:55 -0700 Subject: [PATCH] Fix QueuedScenes not existing when spawning queued scenes --- crates/bevy_scene/src/lib.rs | 31 +++++++++++++++++++ crates/bevy_scene/src/spawn.rs | 56 +++++++++++++++++++--------------- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/crates/bevy_scene/src/lib.rs b/crates/bevy_scene/src/lib.rs index f0928a52514c8..558d27de8c157 100644 --- a/crates/bevy_scene/src/lib.rs +++ b/crates/bevy_scene/src/lib.rs @@ -541,6 +541,7 @@ pub struct ScenePlugin; impl Plugin for ScenePlugin { fn build(&self, app: &mut App) { app.init_resource::() + .init_resource::() .init_asset::() .init_asset::() .add_systems( @@ -560,7 +561,9 @@ mod tests { use crate::{self as bevy_scene, ScenePlugin}; use bevy_app::{App, TaskPoolPlugin}; use bevy_asset::{Asset, AssetApp, AssetPlugin, AssetServer, Handle}; + use bevy_ecs::lifecycle::HookContext; use bevy_ecs::prelude::*; + use bevy_ecs::world::DeferredWorld; use bevy_reflect::TypePath; fn test_app() -> App { @@ -1217,4 +1220,32 @@ mod tests { Name }; } + + #[test] + fn queue_spawn_scene_during_spawn() { + #[derive(Component, Default, Clone)] + #[component(on_insert)] + struct SpawnOnInsert; + + impl SpawnOnInsert { + fn on_insert(mut world: DeferredWorld, _context: HookContext) { + world.commands().queue_spawn_scene(scene2()); + } + } + + let mut app = test_app(); + + fn scene1() -> impl Scene { + bsn!(SpawnOnInsert) + } + + fn scene2() -> impl Scene { + bsn!(#Name) + } + + let world = app.world_mut(); + world.queue_spawn_scene(scene1()); + + app.update(); + } } diff --git a/crates/bevy_scene/src/spawn.rs b/crates/bevy_scene/src/spawn.rs index d7fd540fb7b01..12fe9888a213e 100644 --- a/crates/bevy_scene/src/spawn.rs +++ b/crates/bevy_scene/src/spawn.rs @@ -586,7 +586,7 @@ pub fn resolve_scene_patches( assets: Res, mut patches: ResMut>, mut list_patches: ResMut>, - mut queued: ResMut, + mut waiting: ResMut, ) { for event in events.read() { match *event { @@ -602,11 +602,11 @@ pub fn resolve_scene_patches( } } AssetEvent::Removed { id } => { - if let Some(waiting) = queued.waiting_scene_entities.remove(&id) - && !waiting.is_empty() + if let Some(waiting_entities) = waiting.scene_entities.remove(&id) + && !waiting_entities.is_empty() { error!( - "Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting:?}" + "Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting_entities:?}" ); } } @@ -626,18 +626,19 @@ pub fn resolve_scene_patches( } } AssetEvent::Removed { id } => { - if let Some(waiting) = queued.waiting_scene_list_spawns.remove(&id) - && waiting > 0 + if let Some(waiting_scene_lists) = waiting.scene_list_spawns.remove(&id) + && waiting_scene_lists > 0 { error!( - "Failed to spawn scene list {id:?} {waiting} times because it was removed." + "Failed to spawn scene list {id:?} {waiting_scene_lists} times because it was removed." ); } - if let Some(waiting) = queued.waiting_related_list_entities.remove(&id) - && !waiting.is_empty() + if let Some(waiting_related) = waiting.related_list_entities.remove(&id) + && !waiting_related.is_empty() { - let waiting_entities = waiting.iter().map(|r| r.entity).collect::>(); + let waiting_entities = + waiting_related.iter().map(|r| r.entity).collect::>(); error!( "Failed to spawn related entities for scene list {id:?} because it was removed: {waiting_entities:?}" ); @@ -654,9 +655,14 @@ pub struct QueuedScenes { new_scene_entities: Vec, related_scene_list_spawns: Vec<(RelatedSceneListSpawn, Handle)>, scene_list_spawns: Vec>, - waiting_scene_entities: HashMap, Vec>, - waiting_related_list_entities: HashMap, Vec>, - waiting_scene_list_spawns: HashMap, usize>, +} + +/// A [`Resource`] that tracks entities / scenes that are waiting for an asset to load +#[derive(Resource, Default)] +pub struct WaitingScenes { + scene_entities: HashMap, Vec>, + related_list_entities: HashMap, Vec>, + scene_list_spawns: HashMap, usize>, } pub(crate) struct RelatedSceneListSpawn { @@ -676,16 +682,18 @@ pub fn on_add_scene_patch_instance( pub fn spawn_queued( world: &mut World, scene_patch_instances: &mut QueryState<&ScenePatchInstance>, + mut queued: Local, mut reader: Local>>, mut list_reader: Local>>, ) { + core::mem::swap(&mut *world.resource_mut::(), &mut queued); world.resource_scope(|world, mut list_patches: Mut>| { - world.resource_scope(|world, mut queued: Mut| { + world.resource_scope(|world, mut waiting: Mut| { loop { if queued.is_empty() { break; } - queued.spawn_queued(world, scene_patch_instances, &list_patches); + queued.spawn_queued(world, &mut waiting, scene_patch_instances, &list_patches); } world.resource_scope(|world, events: Mut>>| { @@ -693,7 +701,7 @@ pub fn spawn_queued( let patches = world.resource::>(); if let AssetEvent::LoadedWithDependencies { id } = event && let Some(resolved) = patches.get(*id).and_then(|p| p.resolved.clone()) - && let Some(entities) = queued.waiting_scene_entities.remove(id) + && let Some(entities) = waiting.scene_entities.remove(id) { for entity in entities { if let Ok(mut entity_mut) = world.get_entity_mut(entity) @@ -715,7 +723,7 @@ pub fn spawn_queued( && let Some(list_patch) = list_patches.get_mut(*id) { if let Some(scene_list_spawns) = - queued.waiting_related_list_entities.remove(id) + waiting.related_list_entities.remove(id) { for scene_list_spawn in scene_list_spawns { let result = list_patch.spawn_with(world, |entity| { @@ -728,8 +736,7 @@ pub fn spawn_queued( } } - if let Some(waiting_list_spawns) = - queued.waiting_scene_list_spawns.remove(id) + if let Some(waiting_list_spawns) = waiting.scene_list_spawns.remove(id) { for _ in 0..waiting_list_spawns { let result = list_patch.spawn(world); @@ -756,6 +763,7 @@ impl QueuedScenes { fn spawn_queued( &mut self, world: &mut World, + waiting_scenes: &mut WaitingScenes, scene_patch_instances: &mut QueryState<&ScenePatchInstance>, list_patches: &Assets, ) { @@ -777,8 +785,8 @@ impl QueuedScenes { ); } } else { - let entities = self - .waiting_scene_entities + let entities = waiting_scenes + .scene_entities .entry(handle.clone()) .or_default(); entities.push(entity); @@ -800,8 +808,8 @@ impl QueuedScenes { ); } } else { - let entities = self - .waiting_related_list_entities + let entities = waiting_scenes + .related_list_entities .entry(handle) .or_default(); entities.push(scene_list_spawn); @@ -820,7 +828,7 @@ impl QueuedScenes { ); } } else { - let count = self.waiting_scene_list_spawns.entry(handle).or_default(); + let count = waiting_scenes.scene_list_spawns.entry(handle).or_default(); *count += 1; } }