use crate::{1ResolvedSceneRoot, Scene, SceneList, SceneListPatch, ScenePatch, ScenePatchInstance,2SpawnSceneError,3};4use alloc::sync::Arc;5use bevy_asset::{AssetEvent, AssetServer, Assets, Handle};6use bevy_ecs::{7bundle::BundleScratch, message::MessageCursor, prelude::*, relationship::Relationship,8};9use bevy_platform::collections::HashMap;10use tracing::error;1112/// Adds scene spawning functionality to [`World`].13pub trait WorldSceneExt {14/// Spawns the given [`Scene`] immediately. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been15/// loaded yet), it will return a [`SpawnSceneError`]. If resolving the [`Scene`] is successful, the scene will be spawned.16///17/// If resolving and spawning is successful, it will return a new [`EntityWorldMut`] containing the full contents of the spawned scene.18///19/// See [`Scene`] for the features of the scene system (and how to use it).20///21/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene`].22/// Note that the `.bsn` file format is not yet released.23///24/// ```25/// # use bevy_app::App;26/// # use bevy_scene::{prelude::*, ScenePlugin};27/// # use bevy_ecs::prelude::*;28/// # use bevy_asset::AssetPlugin;29/// # use bevy_app::TaskPoolPlugin;30/// # let mut app = App::new();31/// # app.add_plugins((32/// # TaskPoolPlugin::default(),33/// # AssetPlugin::default(),34/// # ScenePlugin::default(),35/// # ));36/// # let world = app.world_mut();37/// #[derive(Component, Default, Clone)]38/// struct Score(usize);39///40/// #[derive(Component, Default, Clone)]41/// struct Sword;42///43/// #[derive(Component, Default, Clone)]44/// struct Shield;45///46/// world.spawn_scene(bsn! {47/// #Player48/// Score(0)49/// Children [50/// Sword,51/// Shield,52/// ]53/// }).unwrap();54/// ```55fn spawn_scene<S: Scene>(&mut self, scene: S) -> Result<EntityWorldMut<'_>, SpawnSceneError>;5657/// Queues the `scene` to be spawned. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned58/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.59///60/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.61///62/// See [`Scene`] for the features of the scene system (and how to use it).63///64/// ```65/// # use bevy_app::App;66/// # use bevy_scene::{prelude::*, ScenePlugin};67/// # use bevy_ecs::prelude::*;68/// # use bevy_asset::AssetPlugin;69/// # use bevy_app::TaskPoolPlugin;70/// # let mut app = App::new();71/// # app.add_plugins((72/// # TaskPoolPlugin::default(),73/// # AssetPlugin::default(),74/// # ScenePlugin::default(),75/// # ));76/// # let world = app.world_mut();77/// #[derive(Component, Default, Clone)]78/// struct Score(usize);79///80/// #[derive(Component, Default, Clone)]81/// struct Sword;82///83/// #[derive(Component, Default, Clone)]84/// struct Shield;85///86/// // This scene includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"87/// // is fully loaded.88/// world.queue_spawn_scene(bsn! {89/// :"player.bsn"90/// #Player91/// Score(0)92/// Children [93/// Sword,94/// Shield,95/// ]96/// });97/// ```98fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityWorldMut<'_>;99100/// Spawns the given [`SceneList`] immediately. This will resolve the scene list (using [`SceneList::resolve_list`]). If that fails (for example, if there are dependencies that have not been101/// loaded yet), it will return a [`SpawnSceneError`]. If resolving the [`SceneList`] is successful, the scene list will be spawned.102///103/// If resolving and spawning is successful, it will return a [`Vec<Entity>`] containing each entity described in the [`SceneList`].104///105/// See [`Scene`] for the features of the scene system (and how to use it).106///107/// If your scene list has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene_list`].108/// Note that the `.bsn` file format is not yet released.109///110/// ```111/// # use bevy_app::App;112/// # use bevy_scene::{prelude::*, ScenePlugin};113/// # use bevy_ecs::prelude::*;114/// # use bevy_asset::AssetPlugin;115/// # use bevy_app::TaskPoolPlugin;116/// # let mut app = App::new();117/// # app.add_plugins((118/// # TaskPoolPlugin::default(),119/// # AssetPlugin::default(),120/// # ScenePlugin::default(),121/// # ));122/// # let world = app.world_mut();123/// #[derive(Component, FromTemplate)]124/// enum Team {125/// #[default]126/// Red,127/// Blue,128/// }129///130/// world.spawn_scene_list(bsn_list! {131/// (132/// #Player1133/// Team::Red134/// ),135/// (136/// #Player2137/// Team::Blue138/// )139/// }).unwrap();140/// ```141// PERF: ideally this is an iterator142fn spawn_scene_list<L: SceneList>(&mut self, scenes: L)143-> Result<Vec<Entity>, SpawnSceneError>;144145/// Queues the `scene_list` to be spawned. This will evaluate the `scene_list`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved146/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.147///148/// If the dependencies are already loaded (or there are no dependencies), then the scene list will be spawned this frame.149/// ```150/// # use bevy_app::App;151/// # use bevy_scene::{prelude::*, ScenePlugin};152/// # use bevy_ecs::prelude::*;153/// # use bevy_asset::AssetPlugin;154/// # use bevy_app::TaskPoolPlugin;155/// # let mut app = App::new();156/// # app.add_plugins((157/// # TaskPoolPlugin::default(),158/// # AssetPlugin::default(),159/// # ScenePlugin::default(),160/// # ));161/// # let world = app.world_mut();162/// #[derive(Component, FromTemplate)]163/// enum Team {164/// #[default]165/// Red,166/// Blue,167/// }168/// // This scene list includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"169/// // is loaded.170/// world.queue_spawn_scene_list(bsn_list! [171/// (172/// :"player.bsn"173/// #Player1174/// Team::Red175/// ),176/// (177/// :"player.bsn"178/// #Player2179/// Team::Blue180/// )181/// ]);182/// ```183fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L);184}185186impl WorldSceneExt for World {187fn spawn_scene<S: Scene>(&mut self, scene: S) -> Result<EntityWorldMut<'_>, SpawnSceneError> {188let assets = self.resource::<AssetServer>();189let mut patch = ScenePatch::load(assets, scene);190patch.resolve(assets, self.resource::<Assets<ScenePatch>>())?;191patch.spawn(self)192}193194fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityWorldMut<'_> {195let assets = self.resource::<AssetServer>();196let patch = ScenePatch::load(assets, scene);197let handle = assets.add(patch);198self.spawn(ScenePatchInstance(handle))199}200201fn spawn_scene_list<L: SceneList>(202&mut self,203scenes: L,204) -> Result<Vec<Entity>, SpawnSceneError> {205let assets = self.resource::<AssetServer>();206let mut patch = SceneListPatch::load(assets, scenes);207patch.resolve(assets, self.resource::<Assets<ScenePatch>>())?;208patch.spawn(self)209}210211fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L) {212let assets = self.resource::<AssetServer>();213let patch = SceneListPatch::load(assets, scenes);214let handle = assets.add(patch);215self.resource_mut::<QueuedScenes>()216.scene_list_spawns217.push(handle);218}219}220221/// Adds scene spawning functionality to [`Commands`].222pub trait CommandsSceneExt {223/// Spawns the given [`Scene`] as soon as [`Commands`] are applied. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been224/// loaded yet), it will log a [`SpawnSceneError`] as an error. If resolving the [`Scene`] is successful, the scene will be spawned.225///226/// This is essentially a [`Command`] that runs [`World::spawn_scene`].227///228/// See [`Scene`] for the features of the scene system (and how to use it).229///230/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::queue_spawn_scene`].231/// Note that the `.bsn` file format is not yet released.232///233/// ```234/// # use bevy_scene::prelude::*;235/// # use bevy_ecs::prelude::*;236/// # let mut world = World::new();237/// # let mut commands = world.commands();238/// #[derive(Component, Default, Clone)]239/// struct Score(usize);240///241/// #[derive(Component, Default, Clone)]242/// struct Sword;243///244/// #[derive(Component, Default, Clone)]245/// struct Shield;246///247/// commands.spawn_scene(bsn! {248/// #Player249/// Score(0)250/// Children [251/// Sword,252/// Shield,253/// ]254/// });255/// ```256fn spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_>;257258/// Queues the `scene` to be spawned. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned259/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.260///261/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.262///263/// See [`Scene`] for the features of the scene system (and how to use it).264///265/// ```266/// # use bevy_scene::prelude::*;267/// # use bevy_ecs::prelude::*;268/// # let mut world = World::new();269/// # let mut commands = world.commands();270/// #[derive(Component, Default, Clone)]271/// struct Score(usize);272///273/// #[derive(Component, Default, Clone)]274/// struct Sword;275///276/// #[derive(Component, Default, Clone)]277/// struct Shield;278///279/// // This scene includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"280/// // is fully loaded.281/// commands.queue_spawn_scene(bsn! {282/// :"player.bsn"283/// #Player284/// Score(0)285/// Children [286/// Sword,287/// Shield,288/// ]289/// });290/// ```291fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_>;292293/// Spawns the given [`SceneList`] as soon as [`Commands`] are applied. This will resolve the scene list (using [`SceneList::resolve_list`]). If that fails (for example, if there are dependencies that have not been294/// loaded yet), it will log a [`SpawnSceneError`] as an error. If resolving the [`Scene`] is successful, the scene list will be spawned.295///296/// This is essentially a [`Command`] that performs [`World::spawn_scene_list`].297///298/// See [`Scene`] for the features of the scene system (and how to use it).299///300/// If your scene list has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::queue_spawn_scene_list`].301///302/// ```303/// # use bevy_scene::prelude::*;304/// # use bevy_ecs::prelude::*;305/// # let mut world = World::new();306/// # let mut commands = world.commands();307/// #[derive(Component, FromTemplate)]308/// enum Team {309/// #[default]310/// Red,311/// Blue,312/// }313///314/// // Note that the .bsn file format is not yet released.315/// commands.spawn_scene_list(bsn_list! {316/// (317/// :"player.bsn"318/// #Player1319/// Team::Red320/// ),321/// (322/// :"player.bsn"323/// #Player2324/// Team::Blue325/// )326/// });327/// ```328fn spawn_scene_list<L: SceneList>(&mut self, scenes: L);329330/// Queues the `scene_list` to be spawned. This will evaluate the `scene_list`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved331/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.332///333/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.334///335/// ```336/// # use bevy_scene::prelude::*;337/// # use bevy_ecs::prelude::*;338/// # let mut world = World::new();339/// # let mut commands = world.commands();340/// #[derive(Component, FromTemplate)]341/// enum Team {342/// #[default]343/// Red,344/// Blue,345/// }346///347/// // This scene list includes the "player.bsn" asset (note that the `.bsn` file format is not yet released). It will be spawned on the frame that "player.bsn"348/// // is loaded.349/// commands.queue_spawn_scene_list(bsn_list! [350/// (351/// :"player.bsn"352/// #Player1353/// Team::Red354/// ),355/// (356/// :"player.bsn"357/// #Player2358/// Team::Blue359/// )360/// ]);361/// ```362fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L);363}364365impl<'w, 's> CommandsSceneExt for Commands<'w, 's> {366fn spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_> {367let mut entity_commands = self.spawn_empty();368let id = entity_commands.id();369entity_commands.commands().queue(move |world: &mut World| {370if let Ok(mut entity) = world.get_entity_mut(id)371&& let Err(err) = entity.apply_scene(scene)372{373error!("{err}");374}375});376entity_commands377}378379fn queue_spawn_scene<S: Scene>(&mut self, scene: S) -> EntityCommands<'_> {380let mut entity_commands = self.spawn_empty();381let id = entity_commands.id();382entity_commands.commands().queue(move |world: &mut World| {383if let Ok(mut entity) = world.get_entity_mut(id) {384entity.queue_apply_scene(scene);385}386});387entity_commands388}389390fn spawn_scene_list<L: SceneList>(&mut self, scenes: L) {391self.queue(move |world: &mut World| {392if let Err(err) = world.spawn_scene_list(scenes) {393error!("{err}");394}395});396}397398fn queue_spawn_scene_list<L: SceneList>(&mut self, scenes: L) {399self.queue(move |world: &mut World| {400world.queue_spawn_scene_list(scenes);401});402}403}404405/// Adds scene functionality to [`EntityWorldMut`].406pub trait EntityWorldMutSceneExt {407/// Spawns a [`SceneList`], where each entity is related to the current entity using [`RelationshipTarget::Relationship`].408///409/// This will evaluate the `scene_list`'s dependencies (via [`SceneList::register_dependencies`]) and queue it to be resolved410/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.411///412/// If the dependencies are already loaded (or there are no dependencies), then the scene list will be spawned this frame.413///414/// ```415/// # use bevy_app::App;416/// # use bevy_scene::{prelude::*, ScenePlugin};417/// # use bevy_ecs::prelude::*;418/// # use bevy_asset::AssetPlugin;419/// # use bevy_app::TaskPoolPlugin;420/// # let mut app = App::new();421/// # app.add_plugins((422/// # TaskPoolPlugin::default(),423/// # AssetPlugin::default(),424/// # ScenePlugin::default(),425/// # ));426/// # let world = app.world_mut();427/// #[derive(Component, FromTemplate)]428/// enum Team {429/// #[default]430/// Red,431/// Blue,432/// }433///434/// world.spawn_empty().queue_spawn_related_scenes::<Children>(bsn_list! {435/// (436/// #Player1437/// Team::Red438/// ),439/// (440/// #Player2441/// Team::Blue442/// )443/// });444/// ```445fn queue_spawn_related_scenes<T: RelationshipTarget>(self, scenes: impl SceneList) -> Self;446447/// Applies the given [`Scene`] to the current entity immediately. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been448/// loaded yet), it will return a [`SpawnSceneError`]. If resolving the [`Scene`] is successful, the scene will be spawned.449///450/// If resolving and spawning is successful, the entity will contain the full contents of the spawned scene.451///452/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`World::spawn_scene`].453///454/// See [`Scene`] for the features of the scene system (and how to use it).455///456/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`World::queue_spawn_scene`].457/// Note that the .bsn file format is not yet released.458fn apply_scene<S: Scene>(&mut self, scene: S) -> Result<(), SpawnSceneError>;459460/// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned461/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.462///463/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.464/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`World::queue_spawn_scene`].465///466/// See [`Scene`] for the features of the scene system (and how to use it).467fn queue_apply_scene<S: Scene>(&mut self, scene: S);468}469470impl EntityWorldMutSceneExt for EntityWorldMut<'_> {471fn queue_spawn_related_scenes<T: RelationshipTarget>(mut self, scenes: impl SceneList) -> Self {472let assets = self.resource::<AssetServer>();473let patch = SceneListPatch::load(assets, scenes);474let handle = assets.add(patch);475let entity = self.id();476self.resource_mut::<QueuedScenes>()477.related_scene_list_spawns478.push((479RelatedSceneListSpawn {480entity,481insert: |entity, target| {482entity.insert(483<<T as RelationshipTarget>::Relationship as Relationship>::from(target),484);485},486},487handle,488));489self490}491492fn apply_scene<S: Scene>(&mut self, scene: S) -> Result<(), SpawnSceneError> {493let assets = self.resource::<AssetServer>();494let mut patch = ScenePatch::load(assets, scene);495patch.resolve(assets, self.resource::<Assets<ScenePatch>>())?;496patch.apply(self)497}498499fn queue_apply_scene<S: Scene>(&mut self, scene: S) {500let assets = self.resource::<AssetServer>();501let patch = ScenePatch::load(assets, scene);502let handle = assets.add(patch);503self.insert(ScenePatchInstance(handle));504}505}506507/// Adds scene functionality to [`EntityWorldMut`].508pub trait EntityCommandsSceneExt {509/// Spawns a [`SceneList`], where each entity is related to the current entity using [`RelationshipTarget::Relationship`].510///511/// This will evaluate the `scene_list`'s dependencies (via [`SceneList::register_dependencies`]) and queue it to be resolved512/// and spawned after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.513///514/// If the dependencies are already loaded (or there are no dependencies), then the scene list will be spawned this frame.515///516/// ```517/// # use bevy_app::App;518/// # use bevy_scene::prelude::*;519/// # use bevy_ecs::prelude::*;520/// # use bevy_asset::AssetPlugin;521/// # use bevy_app::TaskPoolPlugin;522/// # let mut app = App::new();523/// # let mut commands = app.world_mut().commands();524/// #[derive(Component, FromTemplate)]525/// enum Team {526/// #[default]527/// Red,528/// Blue,529/// }530///531/// commands.spawn_empty().queue_spawn_related_scenes::<Children>(bsn_list! {532/// (533/// #Player1534/// Team::Red535/// ),536/// (537/// #Player2538/// Team::Blue539/// )540/// });541/// ```542fn queue_spawn_related_scenes<T: RelationshipTarget>(543&mut self,544scenes: impl SceneList,545) -> &mut Self;546547/// Applies the given [`Scene`] to the current entity as soon as [`Commands`] are applied. This will resolve the Scene (using [`Scene::resolve`]). If that fails (for example, if there are dependencies that have not been548/// loaded yet), it will log a [`SpawnSceneError`] as an error. If resolving the [`Scene`] is successful, the scene will be spawned.549///550/// If resolving and spawning is successful, the entity will contain the full contents of the spawned scene.551///552/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`Commands::spawn_scene`].553///554/// See [`Scene`] for the features of the scene system (and how to use it).555///556/// If your scene has a dependency that might not be loaded yet (for example, it includes a `.bsn` asset file), consider using [`Commands::spawn_scene`].557/// Note that the .bsn file format is not yet released.558fn apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self;559560/// Queues the `scene` to be applied. This will evaluate the `scene`'s dependencies (via [`Scene::register_dependencies`]) and queue it to be resolved and spawned561/// after all of the dependencies have been loaded. If a [`SpawnSceneError`] occurs, it will be logged as an error.562///563/// If the dependencies are already loaded (or there are no dependencies), then the scene will be spawned this frame.564/// This will write directly on top of any existing components on the entity. [`Scene`] is generally used as a spawning mechanism, so for most things, prefer using [`Commands::queue_spawn_scene`].565///566/// See [`Scene`] for the features of the scene system (and how to use it).567fn queue_apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self;568}569570impl EntityCommandsSceneExt for EntityCommands<'_> {571fn queue_spawn_related_scenes<T: RelationshipTarget>(572&mut self,573scenes: impl SceneList,574) -> &mut Self {575self.queue(move |entity: EntityWorldMut| {576entity.queue_spawn_related_scenes::<T>(scenes);577});578self579}580581fn apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self {582self.queue(move |mut entity: EntityWorldMut| entity.apply_scene(scene));583self584}585586fn queue_apply_scene<S: Scene>(&mut self, scene: S) -> &mut Self {587self.queue(move |mut entity: EntityWorldMut| entity.queue_apply_scene(scene));588self589}590}591592/// A [`System`] that resolves [`ScenePatch`] and [`SceneListPatch`] assets whose dependencies have been fully loaded.593pub fn resolve_scene_patches(594mut events: MessageReader<AssetEvent<ScenePatch>>,595mut list_events: MessageReader<AssetEvent<SceneListPatch>>,596assets: Res<AssetServer>,597mut patches: ResMut<Assets<ScenePatch>>,598mut list_patches: ResMut<Assets<SceneListPatch>>,599mut waiting: ResMut<WaitingScenes>,600) {601for event in events.read() {602match *event {603AssetEvent::LoadedWithDependencies { id } => {604if let Some(scene) = patches.get_mut(id).and_then(|mut p| p.scene.take()) {605match ResolvedSceneRoot::resolve(scene, &assets, &patches) {606Ok(resolved) => {607let mut patch = patches.get_mut(id).unwrap();608patch.resolved = Some(Arc::new(resolved));609}610Err(err) => error!("Failed to resolve scene {id}: {err}"),611}612}613}614AssetEvent::Removed { id } => {615if let Some(waiting_entities) = waiting.scene_entities.remove(&id)616&& !waiting_entities.is_empty()617{618error!(619"Failed to spawn entities waiting for scene {id:?} because it was removed: {waiting_entities:?}"620);621}622}623_ => {}624}625}626for event in list_events.read() {627match *event {628AssetEvent::LoadedWithDependencies { id } => {629if let Some(mut list_patch) = list_patches.get_mut(id)630&& let Err(err) = list_patch.resolve(&assets, &patches)631{632error!("Failed to resolve scene list {id}: {err}");633}634}635AssetEvent::Removed { id } => {636if let Some(waiting_scene_lists) = waiting.scene_list_spawns.remove(&id)637&& waiting_scene_lists > 0638{639error!(640"Failed to spawn scene list {id:?} {waiting_scene_lists} times because it was removed."641);642}643644if let Some(waiting_related) = waiting.related_list_entities.remove(&id)645&& !waiting_related.is_empty()646{647let waiting_entities =648waiting_related.iter().map(|r| r.entity).collect::<Vec<_>>();649error!(650"Failed to spawn related entities for scene list {id:?} because it was removed: {waiting_entities:?}"651);652}653}654_ => {}655}656}657}658659/// A [`Resource`] that tracks entities / scenes that have been queued to spawn.660#[derive(Resource, Default)]661pub struct QueuedScenes {662new_scene_entities: Vec<Entity>,663related_scene_list_spawns: Vec<(RelatedSceneListSpawn, Handle<SceneListPatch>)>,664scene_list_spawns: Vec<Handle<SceneListPatch>>,665}666667/// A [`Resource`] that tracks entities / scenes that are waiting for an asset to load668#[derive(Resource, Default)]669pub struct WaitingScenes {670scene_entities: HashMap<Handle<ScenePatch>, Vec<Entity>>,671related_list_entities: HashMap<Handle<SceneListPatch>, Vec<RelatedSceneListSpawn>>,672scene_list_spawns: HashMap<Handle<SceneListPatch>, usize>,673}674675pub(crate) struct RelatedSceneListSpawn {676entity: Entity,677insert: fn(&mut EntityWorldMut, target: Entity),678}679680/// An [`Observer`] system that queues newly added [`ScenePatchInstance`] entities.681pub fn on_add_scene_patch_instance(682add: On<Add, ScenePatchInstance>,683mut queued_scenes: ResMut<QueuedScenes>,684) {685queued_scenes.new_scene_entities.push(add.entity);686}687688/// A system that spawns queued scenes when they are loaded.689pub fn spawn_queued(690world: &mut World,691scene_patch_instances: &mut QueryState<&ScenePatchInstance>,692mut queued: Local<QueuedScenes>,693mut bundle_scratch: Local<BundleScratch>,694mut reader: Local<MessageCursor<AssetEvent<ScenePatch>>>,695mut list_reader: Local<MessageCursor<AssetEvent<SceneListPatch>>>,696) {697core::mem::swap(&mut *world.resource_mut::<QueuedScenes>(), &mut queued);698world.resource_scope(|world, mut list_patches: Mut<Assets<SceneListPatch>>| {699world.resource_scope(|world, mut waiting: Mut<WaitingScenes>| {700loop {701if queued.is_empty() {702break;703}704queued.spawn_queued(705world,706&mut waiting,707scene_patch_instances,708&mut bundle_scratch,709&list_patches,710);711}712713world.resource_scope(|world, events: Mut<Messages<AssetEvent<ScenePatch>>>| {714for event in reader.read(&events) {715let patches = world.resource::<Assets<ScenePatch>>();716if let AssetEvent::LoadedWithDependencies { id } = event717&& let Some(resolved) = patches.get(*id).and_then(|p| p.resolved.clone())718&& let Some(entities) = waiting.scene_entities.remove(id)719{720for entity in entities {721if let Ok(mut entity_mut) = world.get_entity_mut(entity)722&& let Err(err) =723resolved.apply(&mut entity_mut, &mut bundle_scratch)724{725error!(726"Failed to apply scene (id: {}) to entity {entity}: {}",727id, err728);729}730}731}732}733});734world.resource_scope(735|world, list_events: Mut<Messages<AssetEvent<SceneListPatch>>>| {736for event in list_reader.read(&list_events) {737if let AssetEvent::LoadedWithDependencies { id } = event738&& let Some(list_patch) = list_patches.get_mut(*id)739{740if let Some(scene_list_spawns) =741waiting.related_list_entities.remove(id)742{743for scene_list_spawn in scene_list_spawns {744let result = list_patch.spawn_with(world, |entity| {745(scene_list_spawn.insert)(entity, scene_list_spawn.entity);746});747748if let Err(err) = result {749error!("Failed to spawn scene list (id: {}): {}", id, err);750}751}752}753754if let Some(waiting_list_spawns) = waiting.scene_list_spawns.remove(id)755{756for _ in 0..waiting_list_spawns {757let result = list_patch.spawn(world);758if let Err(err) = result {759error!("Failed to spawn scene list (id: {}): {}", id, err);760}761}762}763}764}765},766);767});768});769}770771impl QueuedScenes {772fn is_empty(&self) -> bool {773self.new_scene_entities.is_empty()774&& self.related_scene_list_spawns.is_empty()775&& self.scene_list_spawns.is_empty()776}777778fn spawn_queued(779&mut self,780world: &mut World,781waiting_scenes: &mut WaitingScenes,782scene_patch_instances: &mut QueryState<&ScenePatchInstance>,783bundle_scratch: &mut BundleScratch,784list_patches: &Assets<SceneListPatch>,785) {786for entity in core::mem::take(&mut self.new_scene_entities) {787let Ok(handle) = scene_patch_instances.get(world, entity).map(|h| &h.0) else {788continue;789};790let patches = world.resource::<Assets<ScenePatch>>();791if let Some(resolved) = patches.get(handle).and_then(|p| p.resolved.clone()) {792let mut entity_mut = world.get_entity_mut(entity).unwrap();793if let Err(err) = resolved.apply(&mut entity_mut, bundle_scratch) {794let scene_patch_instance = scene_patch_instances.get(world, entity).unwrap();795let handle = &scene_patch_instance.0;796let id = handle.id();797let path = handle.path();798error!(799"Failed to apply scene (id: {id}, path: {path:?}) to \800entity {entity}: {err}",801);802}803} else {804let entities = waiting_scenes805.scene_entities806.entry(handle.clone())807.or_default();808entities.push(entity);809}810}811812for (scene_list_spawn, handle) in core::mem::take(&mut self.related_scene_list_spawns) {813if let Some(list_patch) = list_patches.get(&handle) {814let result = list_patch.spawn_with(world, |entity| {815(scene_list_spawn.insert)(entity, scene_list_spawn.entity);816});817818if let Err(err) = result {819error!(820"Failed to spawn scene list (id: {}, path: {:?}): {}",821handle.id(),822handle.path(),823err824);825}826} else {827let entities = waiting_scenes828.related_list_entities829.entry(handle)830.or_default();831entities.push(scene_list_spawn);832}833}834835for handle in core::mem::take(&mut self.scene_list_spawns) {836if let Some(list_patch) = list_patches.get(&handle) {837let result = list_patch.spawn(world);838if let Err(err) = result {839error!(840"Failed to spawn scene list (id: {}, path: {:?}): {}",841handle.id(),842handle.path(),843err844);845}846} else {847let count = waiting_scenes.scene_list_spawns.entry(handle).or_default();848*count += 1;849}850}851}852}853854855