use crate::{CachedSceneError, ResolvedScene, SceneList, ScenePatch};
use bevy_asset::{Asset, AssetPath, AssetServer, Assets};
use bevy_ecs::{
bundle::Bundle,
component::Component,
error::Result,
event::EntityEvent,
name::Name,
relationship::Relationship,
system::IntoObserverSystem,
template::{FnTemplate, FromTemplate, SceneEntityReference, Template, TemplateContext},
};
use core::{any::TypeId, marker::PhantomData};
use thiserror::Error;
use variadics_please::all_tuples;
pub trait Scene: SceneBox {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError>;
fn register_dependencies(&self, _dependencies: &mut SceneDependencies) {}
}
pub trait SceneBox: Send + Sync + 'static {
fn resolve_box(
self: Box<Self>,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError>;
fn register_dependencies_box(&self, _dependencies: &mut SceneDependencies);
}
impl<S: Scene + Sized> SceneBox for S {
#[inline]
fn resolve_box(
self: Box<Self>,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
(*self).resolve(context, scene)
}
#[inline]
fn register_dependencies_box(&self, dependencies: &mut SceneDependencies) {
self.register_dependencies(dependencies);
}
}
impl<T: ?Sized + SceneBox> Scene for Box<T> {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
self.resolve_box(context, scene)
}
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
(**self).register_dependencies_box(dependencies);
}
}
#[derive(Default)]
pub struct SceneDependencies(Vec<SceneDependency>);
impl SceneDependencies {
pub fn register_erased(&mut self, type_id: TypeId, path: AssetPath<'static>) {
self.0.push(SceneDependency { path, type_id });
}
pub fn register<A: Asset>(&mut self, path: AssetPath<'static>) {
self.register_erased(TypeId::of::<A>(), path);
}
pub fn iter(&self) -> impl Iterator<Item = &SceneDependency> {
self.0.iter()
}
}
pub struct SceneDependency {
pub path: AssetPath<'static>,
pub type_id: TypeId,
}
#[derive(Error, Debug)]
pub enum ResolveSceneError {
#[error("Cannot resolve scene because the asset dependency {0} is not present. This could be because it isn't loaded yet, or because the asset does not exist. Consider using `queue_spawn_scene()` if you would like to wait for scene dependencies before spawning.")]
MissingSceneDependency(AssetPath<'static>),
#[error(transparent)]
CachedSceneError(#[from] CachedSceneError),
#[error("The Scene/SceneList is not present on the scene asset. This is likely because the scene has already been resolved, which consumed the source scene")]
MissingScene,
}
pub struct ResolveContext<'a> {
pub assets: &'a AssetServer,
pub patches: &'a Assets<ScenePatch>,
pub cached: Option<&'a ScenePatch>,
}
macro_rules! scene_impl {
($($patch: ident),*) => {
impl<$($patch: Scene),*> Scene for ($($patch,)*) {
fn resolve(self, _context: &mut ResolveContext, _scene: &mut ResolvedScene) -> Result<(), ResolveSceneError> {
#[expect(
clippy::allow_attributes,
reason = "This is inside a macro, and as such, may not trigger in all cases."
)]
#[allow(
non_snake_case,
reason = "The names of these variables are provided by the caller, not by us."
)]
let ($($patch,)*) = self;
$($patch.resolve(_context, _scene)?;)*
Ok(())
}
fn register_dependencies(&self, _dependencies: &mut SceneDependencies) {
#[expect(
clippy::allow_attributes,
reason = "This is inside a macro, and as such, may not trigger in all cases."
)]
#[allow(
non_snake_case,
reason = "The names of these variables are provided by the caller, not by us."
)]
let ($($patch,)*) = self;
$($patch.register_dependencies(_dependencies);)*
}
}
}
}
all_tuples!(scene_impl, 0, 12, P);
impl<S> Scene for Option<S>
where
S: Scene,
{
#[inline]
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
self.map(|value| value.resolve(context, scene))
.unwrap_or(Ok(()))
}
#[inline]
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
if let Some(value) = &self {
value.register_dependencies(dependencies);
}
}
}
impl<L: SceneList> SceneList for Option<L>
where
L: SceneList,
{
#[inline]
fn resolve_list(
self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> std::prelude::v1::Result<(), ResolveSceneError> {
self.map(|scene_list| scene_list.resolve_list(context, scenes))
.unwrap_or(Ok(()))
}
#[inline]
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
if let Some(scene_list) = &self {
scene_list.register_dependencies(dependencies);
}
}
}
pub struct TemplatePatch<F: FnOnce(&mut T, &mut ResolveContext), T>(pub F, pub PhantomData<T>);
pub fn template_value<T: Template>(
value: T,
) -> TemplatePatch<impl FnOnce(&mut T, &mut ResolveContext), T> {
TemplatePatch(
move |input: &mut T, _context: &mut ResolveContext| {
*input = value;
},
PhantomData,
)
}
pub trait PatchFromTemplate {
type Template;
fn patch<F: FnOnce(&mut Self::Template, &mut ResolveContext)>(
func: F,
) -> TemplatePatch<F, Self::Template>;
}
impl<G: FromTemplate> PatchFromTemplate for G {
type Template = G::Template;
fn patch<F: FnOnce(&mut Self::Template, &mut ResolveContext)>(
func: F,
) -> TemplatePatch<F, Self::Template> {
TemplatePatch(func, PhantomData)
}
}
pub trait PatchTemplate: Sized {
fn patch_template<F: FnOnce(&mut Self, &mut ResolveContext)>(func: F)
-> TemplatePatch<F, Self>;
}
impl<T: Template> PatchTemplate for T {
fn patch_template<F: FnOnce(&mut Self, &mut ResolveContext)>(
func: F,
) -> TemplatePatch<F, Self> {
TemplatePatch(func, PhantomData)
}
}
impl<
F: FnOnce(&mut T, &mut ResolveContext) + Send + Sync + 'static,
T: Template<Output: Component> + Send + Sync + Default + 'static,
> Scene for TemplatePatch<F, T>
{
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
let template = scene.get_or_insert_template::<T>(context);
(self.0)(template, context);
Ok(())
}
}
pub struct RelatedScenes<R: Relationship, L: SceneList> {
pub related_template_list: L,
pub marker: PhantomData<R>,
}
impl<R: Relationship, L: SceneList> RelatedScenes<R, L> {
pub fn new(list: L) -> Self {
Self {
related_template_list: list,
marker: PhantomData,
}
}
}
impl<R: Relationship, L: SceneList> Scene for RelatedScenes<R, L> {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
let related = scene.get_or_insert_related_resolved_scenes::<R>();
self.related_template_list
.resolve_list(context, &mut related.scenes)
}
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
self.related_template_list
.register_dependencies(dependencies);
}
}
#[derive(Clone)]
pub struct CachedSceneAsset(
pub AssetPath<'static>,
);
impl<I: Into<AssetPath<'static>>> From<I> for CachedSceneAsset {
fn from(value: I) -> Self {
CachedSceneAsset(value.into())
}
}
impl Scene for CachedSceneAsset {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
if let Some(handle) = context.assets.get_handle::<ScenePatch>(&self.0)
&& let Some(scene_patch) = context.patches.get(&handle)
{
scene.include_cached(handle)?;
context.cached = Some(scene_patch);
Ok(())
} else {
Err(ResolveSceneError::MissingSceneDependency(self.0))
}
}
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
dependencies.register::<ScenePatch>(self.0.clone());
}
}
impl<F: (Fn(&mut TemplateContext) -> Result<O>) + Clone + Send + Sync + 'static, O: Component> Scene
for FnTemplate<F, O>
{
fn resolve(
self,
_context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
scene.push_template(FnTemplate(self.0));
Ok(())
}
}
pub struct NameEntityReference {
pub name: Name,
pub reference: SceneEntityReference,
}
impl NameEntityReference {
pub fn resolve_inline(self, context: &mut ResolveContext, scene: &mut ResolvedScene) {
scene.entity_references.push(self.reference);
let name = scene.get_or_insert_template::<Name>(context);
*name = self.name;
}
}
impl Scene for NameEntityReference {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
self.resolve_inline(context, scene);
Ok(())
}
}
#[must_use]
pub struct SceneScope<S: Scene>(pub S);
impl<S: Scene> Scene for SceneScope<S> {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
self.0.resolve(context, scene)
}
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
self.0.register_dependencies(dependencies);
}
}
#[must_use]
pub struct SceneListScope<L: SceneList>(pub L);
impl<L: SceneList> SceneList for SceneListScope<L> {
fn resolve_list(
self,
context: &mut ResolveContext,
scenes: &mut Vec<ResolvedScene>,
) -> Result<(), ResolveSceneError> {
self.0.resolve_list(context, scenes)
}
fn register_dependencies(&self, dependencies: &mut SceneDependencies) {
self.0.register_dependencies(dependencies);
}
}
pub struct OnTemplate<I, E, B, M>(pub I, pub PhantomData<fn() -> (E, B, M)>);
impl<I: IntoObserverSystem<E, B, M> + Clone, E: EntityEvent, B: Bundle, M: 'static> Template
for OnTemplate<I, E, B, M>
{
type Output = ();
fn build_template(&self, context: &mut TemplateContext) -> Result<Self::Output> {
context.entity.observe(self.0.clone());
Ok(())
}
fn clone_template(&self) -> Self {
Self(self.0.clone(), PhantomData)
}
}
impl<
I: IntoObserverSystem<E, B, M> + Clone + Send + Sync,
E: EntityEvent,
B: Bundle,
M: 'static,
> Scene for OnTemplate<I, E, B, M>
{
fn resolve(
self,
_context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
scene.push_bundle_template(OnTemplate(self.0, PhantomData));
Ok(())
}
}
pub fn on<I: IntoObserverSystem<E, B, M>, E: EntityEvent, B: Bundle, M: 'static>(
observer: I,
) -> OnTemplate<I, E, B, M> {
OnTemplate(observer, PhantomData)
}
impl<S: Scene> From<SceneScope<S>> for Box<dyn Scene> {
fn from(value: SceneScope<S>) -> Self {
Box::new(value)
}
}
impl<S: Scene> From<SceneScope<S>> for Option<Box<dyn Scene>> {
fn from(value: SceneScope<S>) -> Self {
Some(Box::new(value))
}
}
impl<S: SceneList> From<SceneListScope<S>> for Box<dyn SceneList> {
fn from(value: SceneListScope<S>) -> Self {
Box::new(value)
}
}
impl<S: Scene> From<SceneScope<S>> for Box<dyn SceneList> {
fn from(value: SceneScope<S>) -> Self {
Box::new(value)
}
}
impl<S: Scene> From<SceneScope<S>> for Option<Box<dyn SceneList>> {
fn from(value: SceneScope<S>) -> Self {
Some(Box::new(value))
}
}
impl<S: SceneList> From<SceneListScope<S>> for Option<Box<dyn SceneList>> {
fn from(value: SceneListScope<S>) -> Self {
Some(Box::new(value))
}
}
#[derive(Default)]
pub struct InitTemplate<T>(PhantomData<T>);
impl<T: Template<Output: Component> + Default + Send + Sync + 'static> Scene for InitTemplate<T> {
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
let _ = scene.get_or_insert_template::<T>(context);
Ok(())
}
}
pub struct SceneFunction<F: FnOnce(&mut ResolveContext, &mut ResolvedScene)>(pub F);
impl<F: FnOnce(&mut ResolveContext, &mut ResolvedScene) + Send + Sync + 'static> Scene
for SceneFunction<F>
{
fn resolve(
self,
context: &mut ResolveContext,
scene: &mut ResolvedScene,
) -> Result<(), ResolveSceneError> {
(self.0)(context, scene);
Ok(())
}
}