use crate::{
ApplySceneError, ResolveSceneError, ResolvedSceneListRoot, ResolvedSceneRoot, Scene,
SceneDependencies, SceneList,
};
use alloc::sync::Arc;
use bevy_asset::{Asset, AssetServer, Assets, Handle, LoadFromPath, UntypedHandle};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
bundle::BundleScratch,
component::Component,
entity::Entity,
template::FromTemplate,
world::{EntityWorldMut, World},
};
use bevy_reflect::TypePath;
use thiserror::Error;
#[derive(Asset, TypePath)]
pub struct ScenePatch {
pub scene: Option<Box<dyn Scene>>,
#[dependency]
pub dependencies: Vec<UntypedHandle>,
pub resolved: Option<Arc<ResolvedSceneRoot>>,
}
impl ScenePatch {
pub fn load<P: Scene>(mut assets: &AssetServer, scene: P) -> Self {
Self::load_with(&mut assets, scene)
}
pub fn load_with<P: Scene>(load_from_path: &mut impl LoadFromPath, scene: P) -> Self {
let mut dependencies = SceneDependencies::default();
scene.register_dependencies(&mut dependencies);
let dependencies = dependencies
.iter()
.map(|i| load_from_path.load_from_path_erased(i.type_id, i.path.clone()))
.collect::<Vec<_>>();
ScenePatch {
scene: Some(Box::new(scene)),
dependencies,
resolved: None,
}
}
pub fn resolve(
&mut self,
assets: &AssetServer,
patches: &Assets<ScenePatch>,
) -> Result<(), ResolveSceneError> {
let scene = self.scene.take().ok_or(ResolveSceneError::MissingScene)?;
self.resolved = Some(Arc::new(ResolvedSceneRoot::resolve(
scene, assets, patches,
)?));
Ok(())
}
pub fn spawn<'w>(&self, world: &'w mut World) -> Result<EntityWorldMut<'w>, SpawnSceneError> {
let resolved = self
.resolved
.as_deref()
.ok_or(SpawnSceneError::UnresolvedSceneError)?;
resolved
.spawn(world)
.map_err(SpawnSceneError::ApplySceneError)
}
pub fn apply<'w>(&self, entity: &'w mut EntityWorldMut) -> Result<(), SpawnSceneError> {
let resolved = self
.resolved
.as_deref()
.ok_or(SpawnSceneError::UnresolvedSceneError)?;
resolved
.apply(entity, &mut BundleScratch::default())
.map_err(SpawnSceneError::ApplySceneError)
}
}
#[derive(Error, Debug)]
pub enum SpawnSceneError {
#[error(transparent)]
ApplySceneError(#[from] ApplySceneError),
#[error(transparent)]
ResolveSceneError(#[from] ResolveSceneError),
#[error("This scene has not been resolved yet and cannot be spawned. It is likely waiting for dependencies to load")]
UnresolvedSceneError,
}
#[derive(Component, FromTemplate, Deref, DerefMut)]
pub struct ScenePatchInstance(pub Handle<ScenePatch>);
#[derive(Asset, TypePath)]
pub struct SceneListPatch {
pub scene_list: Option<Box<dyn SceneList>>,
#[dependency]
pub dependencies: Vec<UntypedHandle>,
pub resolved: Option<ResolvedSceneListRoot>,
}
impl SceneListPatch {
pub fn load<L: SceneList>(assets: &AssetServer, scene_list: L) -> Self {
let mut dependencies = SceneDependencies::default();
scene_list.register_dependencies(&mut dependencies);
let dependencies = dependencies
.iter()
.map(|dep| assets.load_builder().load_erased(dep.type_id, &dep.path))
.collect::<Vec<_>>();
SceneListPatch {
scene_list: Some(Box::new(scene_list)),
dependencies,
resolved: None,
}
}
pub fn resolve(
&mut self,
assets: &AssetServer,
patches: &Assets<ScenePatch>,
) -> Result<(), ResolveSceneError> {
let scene_list = self
.scene_list
.take()
.ok_or(ResolveSceneError::MissingScene)?;
self.resolved = Some(ResolvedSceneListRoot::resolve(scene_list, assets, patches)?);
Ok(())
}
pub fn spawn<'w>(&self, world: &'w mut World) -> Result<Vec<Entity>, SpawnSceneError> {
self.spawn_with(world, |_| {})
}
pub(crate) fn spawn_with<'w>(
&self,
world: &'w mut World,
func: impl Fn(&mut EntityWorldMut),
) -> Result<Vec<Entity>, SpawnSceneError> {
let resolved = self
.resolved
.as_ref()
.ok_or(SpawnSceneError::UnresolvedSceneError)?;
resolved
.spawn_with(world, func)
.map_err(SpawnSceneError::ApplySceneError)
}
}