use crate::{1ApplySceneError, ResolveSceneError, ResolvedSceneListRoot, ResolvedSceneRoot, Scene,2SceneDependencies, SceneList,3};4use alloc::sync::Arc;5use bevy_asset::{Asset, AssetServer, Assets, Handle, LoadFromPath, UntypedHandle};6use bevy_derive::{Deref, DerefMut};7use bevy_ecs::{8bundle::BundleScratch,9component::Component,10entity::Entity,11template::FromTemplate,12world::{EntityWorldMut, World},13};14use bevy_reflect::TypePath;15use thiserror::Error;1617/// An [`Asset`] that holds a [`Scene`], tracks its dependencies, and holds the [`ResolvedSceneRoot`] (after the [`Scene`] has been loaded and resolved).18#[derive(Asset, TypePath)]19pub struct ScenePatch {20/// A [`Scene`].21pub scene: Option<Box<dyn Scene>>,22/// The dependencies of `scene` (populated using [`Scene::register_dependencies`]). These are "asset dependencies" and will affect the load state.23#[dependency]24pub dependencies: Vec<UntypedHandle>,25/// The [`ResolvedSceneRoot`], if exists. This is populated after the [`Scene`] has been loaded and resolved26// TODO: consider breaking this out to prevent mutating asset events when resolved. Assets as Entities will enable this!27// TODO: This Arc exists to allow nested ResolvedSceneRoot::apply when borrowing cached ScenePatch assets (see the ResolvedSceneRoot::apply implementation).28pub resolved: Option<Arc<ResolvedSceneRoot>>,29}3031impl ScenePatch {32/// Kicks off a load of the `scene`. This enumerates the scene's dependencies using [`Scene::register_dependencies`], loads33/// them using the given [`AssetServer`], and assigns the resulting asset handles to [`ScenePatch::dependencies`].34pub fn load<P: Scene>(mut assets: &AssetServer, scene: P) -> Self {35Self::load_with(&mut assets, scene)36}3738/// Same as [`Self::load`], but allows passing in any [`LoadFromPath`] impl for more general39/// loading cases.40pub fn load_with<P: Scene>(load_from_path: &mut impl LoadFromPath, scene: P) -> Self {41let mut dependencies = SceneDependencies::default();42scene.register_dependencies(&mut dependencies);43let dependencies = dependencies44.iter()45.map(|i| load_from_path.load_from_path_erased(i.type_id, i.path.clone()))46.collect::<Vec<_>>();47ScenePatch {48scene: Some(Box::new(scene)),49dependencies,50resolved: None,51}52}5354/// Resolves the current `scene` (using [`Scene::resolve`]). This should only be called after every dependency has loaded from the `scene`'s55/// [`Scene::register_dependencies`]. If successful, it will store the resolved result in [`ScenePatch::resolved`].56pub fn resolve(57&mut self,58assets: &AssetServer,59patches: &Assets<ScenePatch>,60) -> Result<(), ResolveSceneError> {61let scene = self.scene.take().ok_or(ResolveSceneError::MissingScene)?;62self.resolved = Some(Arc::new(ResolvedSceneRoot::resolve(63scene, assets, patches,64)?));65Ok(())66}6768/// Spawns the scene in `world` as a new entity. This should only be called after [`ScenePatch::resolve`].69pub fn spawn<'w>(&self, world: &'w mut World) -> Result<EntityWorldMut<'w>, SpawnSceneError> {70let resolved = self71.resolved72.as_deref()73.ok_or(SpawnSceneError::UnresolvedSceneError)?;74resolved75.spawn(world)76.map_err(SpawnSceneError::ApplySceneError)77}7879/// Applies the scene to the given `entity`. This should only be called after [`ScenePatch::resolve`]80pub fn apply<'w>(&self, entity: &'w mut EntityWorldMut) -> Result<(), SpawnSceneError> {81let resolved = self82.resolved83.as_deref()84.ok_or(SpawnSceneError::UnresolvedSceneError)?;85resolved86.apply(entity, &mut BundleScratch::default())87.map_err(SpawnSceneError::ApplySceneError)88}89}9091/// An [`Error`] that occurs during scene spawning.92#[derive(Error, Debug)]93pub enum SpawnSceneError {94/// Failed to apply a [`ResolvedScene`].95///96/// [`ResolvedScene`]: crate::ResolvedScene97#[error(transparent)]98ApplySceneError(#[from] ApplySceneError),99#[error(transparent)]100/// Calling [`Scene::resolve`] failed.101ResolveSceneError(#[from] ResolveSceneError),102/// Attempted to spawn a scene that has not been resolved yet.103#[error("This scene has not been resolved yet and cannot be spawned. It is likely waiting for dependencies to load")]104UnresolvedSceneError,105}106107/// A component that, when added, will queue applying the given [`ScenePatch`] after the scene and its dependencies have been loaded and resolved.108#[derive(Component, FromTemplate, Deref, DerefMut)]109pub struct ScenePatchInstance(pub Handle<ScenePatch>);110111/// An [`Asset`] that holds a [`SceneList`], tracks its dependencies, and holds a [`ResolvedSceneListRoot`] (after the [`SceneList`] has been loaded and resolved)112#[derive(Asset, TypePath)]113pub struct SceneListPatch {114/// A [`SceneList`].115pub scene_list: Option<Box<dyn SceneList>>,116117/// The dependencies of `scene_list` (populated using [`SceneList::register_dependencies`]). These are "asset dependencies" and will affect the load state.118#[dependency]119pub dependencies: Vec<UntypedHandle>,120121/// The [`ResolvedSceneListRoot`], if exists. This is populated after the scene list and its dependencies have been loaded and resolved.122// TODO: consider breaking this out to prevent mutating asset events when resolved123pub resolved: Option<ResolvedSceneListRoot>,124}125126impl SceneListPatch {127/// Kicks off a load of the `scene_list`. This enumerates the scene list's dependencies using [`SceneList::register_dependencies`], loads128/// them using the given [`AssetServer`], and assigns the resulting asset handles to [`SceneListPatch::dependencies`].129pub fn load<L: SceneList>(assets: &AssetServer, scene_list: L) -> Self {130let mut dependencies = SceneDependencies::default();131scene_list.register_dependencies(&mut dependencies);132let dependencies = dependencies133.iter()134.map(|dep| assets.load_builder().load_erased(dep.type_id, &dep.path))135.collect::<Vec<_>>();136SceneListPatch {137scene_list: Some(Box::new(scene_list)),138dependencies,139resolved: None,140}141}142143/// Resolves the current `scene` (using [`SceneList::resolve_list`]). This should only be called after every dependency has loaded from the `scene_list`'s144/// [`SceneList::register_dependencies`].145pub fn resolve(146&mut self,147assets: &AssetServer,148patches: &Assets<ScenePatch>,149) -> Result<(), ResolveSceneError> {150let scene_list = self151.scene_list152.take()153.ok_or(ResolveSceneError::MissingScene)?;154self.resolved = Some(ResolvedSceneListRoot::resolve(scene_list, assets, patches)?);155Ok(())156}157158/// Spawns the scene list in `world` as new entities. This should only be called after [`SceneListPatch::resolve`].159pub fn spawn<'w>(&self, world: &'w mut World) -> Result<Vec<Entity>, SpawnSceneError> {160self.spawn_with(world, |_| {})161}162163/// Spawns the scene list in `world` as new entities. This should only be called after [`SceneListPatch::resolve`].164pub(crate) fn spawn_with<'w>(165&self,166world: &'w mut World,167func: impl Fn(&mut EntityWorldMut),168) -> Result<Vec<Entity>, SpawnSceneError> {169let resolved = self170.resolved171.as_ref()172.ok_or(SpawnSceneError::UnresolvedSceneError)?;173resolved174.spawn_with(world, func)175.map_err(SpawnSceneError::ApplySceneError)176}177}178179180