Skip to main content

bevy_scene/
scene_patch.rs

1use crate::{
2    ApplySceneError, ResolveSceneError, ResolvedSceneListRoot, ResolvedSceneRoot, Scene,
3    SceneDependencies, SceneList,
4};
5use alloc::sync::Arc;
6use bevy_asset::{Asset, AssetServer, Assets, Handle, LoadFromPath, UntypedHandle};
7use bevy_derive::{Deref, DerefMut};
8use bevy_ecs::{
9    bundle::BundleScratch,
10    component::Component,
11    entity::Entity,
12    template::FromTemplate,
13    world::{EntityWorldMut, World},
14};
15use bevy_reflect::TypePath;
16use thiserror::Error;
17
18/// An [`Asset`] that holds a [`Scene`], tracks its dependencies, and holds the [`ResolvedSceneRoot`] (after the [`Scene`] has been loaded and resolved).
19#[derive(Asset, TypePath)]
20pub struct ScenePatch {
21    /// A [`Scene`].
22    pub scene: Option<Box<dyn Scene>>,
23    /// The dependencies of `scene` (populated using [`Scene::register_dependencies`]). These are "asset dependencies" and will affect the load state.
24    #[dependency]
25    pub dependencies: Vec<UntypedHandle>,
26    /// The [`ResolvedSceneRoot`], if exists. This is populated after the [`Scene`] has been loaded and resolved
27    // TODO: consider breaking this out to prevent mutating asset events when resolved. Assets as Entities will enable this!
28    // TODO: This Arc exists to allow nested ResolvedSceneRoot::apply when borrowing cached ScenePatch assets (see the ResolvedSceneRoot::apply implementation).
29    pub resolved: Option<Arc<ResolvedSceneRoot>>,
30}
31
32impl ScenePatch {
33    /// Kicks off a load of the `scene`. This enumerates the scene's dependencies using [`Scene::register_dependencies`], loads
34    /// them using the given [`AssetServer`], and assigns the resulting asset handles to [`ScenePatch::dependencies`].
35    pub fn load<P: Scene>(mut assets: &AssetServer, scene: P) -> Self {
36        Self::load_with(&mut assets, scene)
37    }
38
39    /// Same as [`Self::load`], but allows passing in any [`LoadFromPath`] impl for more general
40    /// loading cases.
41    pub fn load_with<P: Scene>(load_from_path: &mut impl LoadFromPath, scene: P) -> Self {
42        let mut dependencies = SceneDependencies::default();
43        scene.register_dependencies(&mut dependencies);
44        let dependencies = dependencies
45            .iter()
46            .map(|i| load_from_path.load_from_path_erased(i.type_id, i.path.clone()))
47            .collect::<Vec<_>>();
48        ScenePatch {
49            scene: Some(Box::new(scene)),
50            dependencies,
51            resolved: None,
52        }
53    }
54
55    /// Resolves the current `scene` (using [`Scene::resolve`]). This should only be called after every dependency has loaded from the `scene`'s
56    /// [`Scene::register_dependencies`]. If successful, it will store the resolved result in [`ScenePatch::resolved`].
57    pub fn resolve(
58        &mut self,
59        assets: &AssetServer,
60        patches: &Assets<ScenePatch>,
61    ) -> Result<(), ResolveSceneError> {
62        let scene = self.scene.take().ok_or(ResolveSceneError::MissingScene)?;
63        self.resolved = Some(Arc::new(ResolvedSceneRoot::resolve(
64            scene, assets, patches,
65        )?));
66        Ok(())
67    }
68
69    /// Spawns the scene in `world` as a new entity. This should only be called after [`ScenePatch::resolve`].
70    pub fn spawn<'w>(&self, world: &'w mut World) -> Result<EntityWorldMut<'w>, SpawnSceneError> {
71        let resolved = self
72            .resolved
73            .as_deref()
74            .ok_or(SpawnSceneError::UnresolvedSceneError)?;
75        resolved
76            .spawn(world)
77            .map_err(SpawnSceneError::ApplySceneError)
78    }
79
80    /// Applies the scene to the given `entity`. This should only be called after [`ScenePatch::resolve`]
81    pub fn apply<'w>(&self, entity: &'w mut EntityWorldMut) -> Result<(), SpawnSceneError> {
82        let resolved = self
83            .resolved
84            .as_deref()
85            .ok_or(SpawnSceneError::UnresolvedSceneError)?;
86        resolved
87            .apply(entity, &mut BundleScratch::default())
88            .map_err(SpawnSceneError::ApplySceneError)
89    }
90}
91
92/// An [`Error`] that occurs during scene spawning.
93#[derive(Error, Debug)]
94pub enum SpawnSceneError {
95    /// Failed to apply a [`ResolvedScene`].
96    ///
97    /// [`ResolvedScene`]: crate::ResolvedScene
98    #[error(transparent)]
99    ApplySceneError(#[from] ApplySceneError),
100    #[error(transparent)]
101    /// Calling [`Scene::resolve`] failed.
102    ResolveSceneError(#[from] ResolveSceneError),
103    /// Attempted to spawn a scene that has not been resolved yet.
104    #[error("This scene has not been resolved yet and cannot be spawned. It is likely waiting for dependencies to load")]
105    UnresolvedSceneError,
106}
107
108/// A component that, when added, will queue applying the given [`ScenePatch`] after the scene and its dependencies have been loaded and resolved.
109#[derive(Component, FromTemplate, Deref, DerefMut)]
110pub struct ScenePatchInstance(pub Handle<ScenePatch>);
111
112/// An [`Asset`] that holds a [`SceneList`], tracks its dependencies, and holds a [`ResolvedSceneListRoot`] (after the [`SceneList`] has been loaded and resolved)
113#[derive(Asset, TypePath)]
114pub struct SceneListPatch {
115    /// A [`SceneList`].
116    pub scene_list: Option<Box<dyn SceneList>>,
117
118    /// The dependencies of `scene_list` (populated using [`SceneList::register_dependencies`]). These are "asset dependencies" and will affect the load state.
119    #[dependency]
120    pub dependencies: Vec<UntypedHandle>,
121
122    /// The [`ResolvedSceneListRoot`], if exists. This is populated after the scene list and its dependencies have been loaded and resolved.
123    // TODO: consider breaking this out to prevent mutating asset events when resolved
124    pub resolved: Option<ResolvedSceneListRoot>,
125}
126
127impl SceneListPatch {
128    /// Kicks off a load of the `scene_list`. This enumerates the scene list's dependencies using [`SceneList::register_dependencies`], loads
129    /// them using the given [`AssetServer`], and assigns the resulting asset handles to [`SceneListPatch::dependencies`].
130    pub fn load<L: SceneList>(assets: &AssetServer, scene_list: L) -> Self {
131        let mut dependencies = SceneDependencies::default();
132        scene_list.register_dependencies(&mut dependencies);
133        let dependencies = dependencies
134            .iter()
135            .map(|dep| assets.load_builder().load_erased(dep.type_id, &dep.path))
136            .collect::<Vec<_>>();
137        SceneListPatch {
138            scene_list: Some(Box::new(scene_list)),
139            dependencies,
140            resolved: None,
141        }
142    }
143
144    /// Resolves the current `scene` (using [`SceneList::resolve_list`]). This should only be called after every dependency has loaded from the `scene_list`'s
145    /// [`SceneList::register_dependencies`].
146    pub fn resolve(
147        &mut self,
148        assets: &AssetServer,
149        patches: &Assets<ScenePatch>,
150    ) -> Result<(), ResolveSceneError> {
151        let scene_list = self
152            .scene_list
153            .take()
154            .ok_or(ResolveSceneError::MissingScene)?;
155        self.resolved = Some(ResolvedSceneListRoot::resolve(scene_list, assets, patches)?);
156        Ok(())
157    }
158
159    /// Spawns the scene list in `world` as new entities. This should only be called after [`SceneListPatch::resolve`].
160    pub fn spawn<'w>(&self, world: &'w mut World) -> Result<Vec<Entity>, SpawnSceneError> {
161        self.spawn_with(world, |_| {})
162    }
163
164    /// Spawns the scene list in `world` as new entities. This should only be called after [`SceneListPatch::resolve`].
165    pub(crate) fn spawn_with<'w>(
166        &self,
167        world: &'w mut World,
168        func: impl Fn(&mut EntityWorldMut),
169    ) -> Result<Vec<Entity>, SpawnSceneError> {
170        let resolved = self
171            .resolved
172            .as_ref()
173            .ok_or(SpawnSceneError::UnresolvedSceneError)?;
174        resolved
175            .spawn_with(world, func)
176            .map_err(SpawnSceneError::ApplySceneError)
177    }
178}