#![expect(unsafe_code, reason = "Unsafe code is used to improve performance.")]
pub mod prelude {
pub use crate::{
bsn, bsn_list, on, template_value, CommandsSceneExt, EntityCommandsSceneExt,
EntityWorldMutSceneExt, PatchFromTemplate, PatchTemplate, Scene, SceneComponent, SceneList,
ScenePatchInstance, SpawnListSystem, SpawnSystem, WorldSceneExt,
};
}
pub mod macro_utils;
extern crate alloc;
mod resolved_scene;
mod scene;
mod scene_component;
mod scene_list;
mod scene_patch;
mod spawn;
mod spawn_system;
pub use resolved_scene::*;
pub use scene::*;
pub use scene_component::*;
pub use scene_list::*;
pub use scene_patch::*;
pub use spawn::*;
pub use spawn_system::*;
use bevy_app::{App, Plugin, SceneSpawnerSystems, SpawnScene};
use bevy_asset::AssetApp;
use bevy_ecs::prelude::*;
#[doc(inline)]
pub use bevy_scene_macros::bsn;
#[doc(inline)]
pub use bevy_scene_macros::bsn_list;
pub use bevy_scene_macros::SceneComponent;
#[derive(Default)]
pub struct ScenePlugin;
impl Plugin for ScenePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<QueuedScenes>()
.init_resource::<WaitingScenes>()
.init_asset::<ScenePatch>()
.init_asset::<SceneListPatch>()
.add_systems(
SpawnScene,
(resolve_scene_patches, spawn_queued)
.chain()
.in_set(SceneSpawnerSystems::SceneSpawn)
.after(SceneSpawnerSystems::WorldInstanceSpawn),
)
.add_observer(on_add_scene_patch_instance);
}
}
#[cfg(test)]
mod tests {
use crate::{self as bevy_scene, ScenePlugin};
use crate::{prelude::*, ScenePatch};
use alloc::sync::Arc;
use bevy_app::{App, TaskPoolPlugin};
use bevy_asset::io::memory::{Dir, MemoryAssetReader};
use bevy_asset::io::{AssetSourceBuilder, AssetSourceId};
use bevy_asset::{Asset, AssetApp, AssetLoader, AssetPlugin, AssetServer, Assets, Handle};
use bevy_ecs::lifecycle::HookContext;
use bevy_ecs::name::Name;
use bevy_ecs::prelude::*;
use bevy_ecs::relationship::Relationship;
use bevy_ecs::system::{system_value, SystemHandle};
use bevy_ecs::world::DeferredWorld;
use bevy_reflect::TypePath;
use bevy_scene_macros::SceneComponent;
use std::path::Path;
use std::sync::Mutex;
fn test_app() -> App {
let mut app = App::new();
app.add_plugins((
TaskPoolPlugin::default(),
AssetPlugin::default(),
ScenePlugin,
));
app
}
#[test]
fn cached_patching() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, FromTemplate)]
struct Position {
x: f32,
y: f32,
z: f32,
}
fn b() -> impl Scene {
bsn! {
a()
Position { x: 1. }
Children [ #Y ]
}
}
fn a() -> impl Scene {
bsn! {
Position { y: 2. }
Children [ #X ]
}
}
let id = world.spawn_scene(b()).unwrap().id();
let root = world.entity(id);
let position = root.get::<Position>().unwrap();
assert_eq!(position.x, 1.);
assert_eq!(position.y, 2.);
assert_eq!(position.z, 0.);
let children = root.get::<Children>().unwrap();
assert_eq!(children.len(), 2);
let x = world.entity(children[0]);
let name = x.get::<Name>().unwrap();
assert_eq!(name.as_str(), "X");
let y = world.entity(children[1]);
let name = y.get::<Name>().unwrap();
assert_eq!(name.as_str(), "Y");
}
#[test]
fn cached_patching_order() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, FromTemplate)]
struct Position {
x: f32,
y: f32,
z: f32,
}
fn a() -> impl Scene {
bsn! {
Position { x: 2. }
}
}
fn b() -> impl Scene {
bsn! {
Position { x: 1., y: 1., z: 1. }
a()
}
}
let root = world.spawn_scene(b()).unwrap();
let position = root.get::<Position>().unwrap();
assert_eq!(position.x, 2.);
assert_eq!(position.y, 1.);
assert_eq!(position.z, 1.);
}
#[test]
fn loaded_asset_cached_patching() {
#[derive(Component, FromTemplate)]
struct Position {
x: f32,
y: f32,
z: f32,
}
fn b() -> impl Scene {
bsn! {
:"a.bsn"
Position { x: 1. }
Children [ #Y ]
}
}
fn a() -> impl Scene {
bsn! {
Position { y: 2. }
Children [ #X ]
}
}
#[derive(SceneComponent, Default, Clone)]
#[scene("a.bsn")]
struct AWidget {
value: usize,
}
let mut app = App::new();
let dir = Dir::default();
let dir_clone = dir.clone();
app.register_asset_source(
AssetSourceId::Default,
AssetSourceBuilder::new(move || {
Box::new(MemoryAssetReader {
root: dir_clone.clone(),
})
}),
);
app.add_plugins((
TaskPoolPlugin::default(),
AssetPlugin::default(),
ScenePlugin,
));
app.finish();
app.cleanup();
app.register_asset_loader(FakeSceneLoader);
#[derive(TypePath)]
struct FakeSceneLoader;
impl AssetLoader for FakeSceneLoader {
type Asset = ScenePatch;
type Error = std::io::Error;
type Settings = ();
async fn load(
&self,
_reader: &mut dyn bevy_asset::io::Reader,
_settings: &Self::Settings,
load_context: &mut bevy_asset::LoadContext<'_>,
) -> Result<Self::Asset, Self::Error> {
Ok(ScenePatch::load_with(load_context, a()))
}
}
dir.insert_asset_text(Path::new("a.bsn"), "");
let asset_server = app.world().resource::<AssetServer>().clone();
let handle = asset_server.load("a.bsn");
assert!(app.world().get_resource::<Assets<ScenePatch>>().is_some());
run_app_until(&mut app, || asset_server.is_loaded(&handle));
let patch = app
.world()
.resource::<Assets<ScenePatch>>()
.get(&handle)
.unwrap();
assert!(patch.resolved.is_some());
let world = app.world_mut();
let id = world.spawn_scene(b()).unwrap().id();
let root = world.entity(id);
let position = root.get::<Position>().unwrap();
assert_eq!(position.x, 1.);
assert_eq!(position.y, 2.);
assert_eq!(position.z, 0.);
let children = root.get::<Children>().unwrap();
assert_eq!(children.len(), 2);
let x = world.entity(children[0]);
let name = x.get::<Name>().unwrap();
assert_eq!(name.as_str(), "X");
let y = world.entity(children[1]);
let name = y.get::<Name>().unwrap();
assert_eq!(name.as_str(), "Y");
let id = world
.spawn_scene(bsn! {@AWidget { value: 2 }})
.unwrap()
.id();
let root = world.entity(id);
let a_widget = root.get::<AWidget>().unwrap();
assert_eq!(a_widget.value, 2);
let position = root.get::<Position>().unwrap();
assert_eq!(position.x, 0.);
assert_eq!(position.y, 2.);
assert_eq!(position.z, 0.);
let children = root.get::<Children>().unwrap();
assert_eq!(children.len(), 1);
let x = world.entity(children[0]);
let name = x.get::<Name>().unwrap();
assert_eq!(name.as_str(), "X");
}
#[test]
fn inline_scene_patching() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, FromTemplate)]
struct Position {
x: f32,
y: f32,
z: f32,
}
fn b() -> impl Scene {
bsn! {
a()
Position { x: 1. }
Children [ #Y ]
}
}
fn a() -> impl Scene {
bsn! {
Position { y: 2. }
Children [ #X ]
}
}
let id = world.spawn_scene(b()).unwrap().id();
let root = world.entity(id);
let position = root.get::<Position>().unwrap();
assert_eq!(position.x, 1.);
assert_eq!(position.y, 2.);
assert_eq!(position.z, 0.);
let children = root.get::<Children>().unwrap();
assert_eq!(children.len(), 2);
let x = world.entity(children[0]);
let name = x.get::<Name>().unwrap();
assert_eq!(name.as_str(), "X");
let y = world.entity(children[1]);
let name = y.get::<Name>().unwrap();
assert_eq!(name.as_str(), "Y");
}
#[test]
fn hierarchy() {
let mut app = test_app();
let world = app.world_mut();
fn scene() -> impl Scene {
bsn! {
#A
Children [
(
#B
Children [
#X
]
),
(
#C
Children [
#Y
]
)
]
}
}
let id = world.spawn_scene(scene()).unwrap().id();
let a = world.entity(id);
let name = a.get::<Name>().unwrap();
assert_eq!(name.as_str(), "A");
let children = a.get::<Children>().unwrap();
assert_eq!(children.len(), 2);
let b = world.entity(children[0]);
let c = world.entity(children[1]);
let name = b.get::<Name>().unwrap();
assert_eq!(name.as_str(), "B");
let name = c.get::<Name>().unwrap();
assert_eq!(name.as_str(), "C");
let children = b.get::<Children>().unwrap();
assert_eq!(children.len(), 1);
let x = world.entity(children[0]);
let name = x.get::<Name>().unwrap();
assert_eq!(name.as_str(), "X");
let children = c.get::<Children>().unwrap();
assert_eq!(children.len(), 1);
let y = world.entity(children[0]);
let name = y.get::<Name>().unwrap();
assert_eq!(name.as_str(), "Y");
}
#[test]
fn constant_values() {
let mut app = test_app();
let world = app.world_mut();
const X_AXIS: usize = 1;
const XAXIS: usize = 2;
#[derive(Component, FromTemplate)]
struct Value(usize);
fn x_axis() -> impl Scene {
bsn! {Value(X_AXIS)}
}
fn xaxis() -> impl Scene {
bsn! {Value(XAXIS)}
}
let entity = world.spawn_scene(x_axis()).unwrap();
assert_eq!(entity.get::<Value>().unwrap().0, 1);
let entity = world.spawn_scene(xaxis()).unwrap();
assert_eq!(entity.get::<Value>().unwrap().0, 2);
}
#[derive(Component, FromTemplate)]
struct Reference(Entity);
#[test]
fn bsn_name_references() {
let mut app = test_app();
let world = app.world_mut();
fn a() -> impl Scene {
bsn! {
#X
Children [
(b() Reference(#X))
]
}
}
fn b() -> impl Scene {
let inline = bsn! {#Y Reference(#Y) Children [ Reference(#Y)] };
bsn! {
#X
Children [
Reference(#X),
(inline Reference(#X)),
]
}
}
let id = world.spawn_scene(a()).unwrap().id();
let a = world.entity(id);
let name = a.get::<Name>().unwrap();
assert_eq!(name.as_str(), "X");
let children = a.get::<Children>().unwrap();
assert_eq!(children.len(), 1);
let b = world.entity(children[0]);
let reference = b.get::<Reference>().unwrap();
assert_eq!(reference.0, id);
let b_name = b.get::<Name>().unwrap();
assert_eq!(b_name.as_str(), "X");
let grandchildren = b.get::<Children>().unwrap();
assert_eq!(grandchildren.len(), 2);
let grandchild = world.entity(grandchildren[0]);
assert_eq!(grandchild.get::<Reference>().unwrap().0, b.id());
let grandchild = world.entity(grandchildren[1]);
assert_eq!(grandchild.get::<Reference>().unwrap().0, b.id());
assert_eq!(grandchild.get::<Name>().unwrap().as_str(), "Y");
assert_eq!(
grandchild.id(),
world
.entity(grandchild.get::<Children>().unwrap()[0])
.get::<Reference>()
.unwrap()
.0
);
}
#[test]
fn bsn_reverse_reference() {
let mut app = test_app();
let world = app.world_mut();
fn a() -> impl Scene {
bsn! {
Reference(#Last)
Children [
#First,
#Second,
#Last
]
}
}
let id = world.spawn_scene(a()).unwrap().id();
let ref_id = world.entity(id).get::<Reference>().unwrap();
let children = world.entity(id).get::<Children>().unwrap();
assert_eq!(children[2], ref_id.0);
let name = world.entity(ref_id.0).get::<Name>().unwrap();
assert_eq!(name.as_str(), "Last");
}
#[test]
fn bsn_list_name_references() {
let mut app = test_app();
let world = app.world_mut();
fn b() -> impl Scene {
bsn! {
#Z
Children [
Reference(#Z)
]
}
}
fn a() -> impl SceneList {
bsn_list![
(
#X
Reference(#Y)
Children [
(#Z Reference(#X))
]
),
(
#Y
Reference(#X)
Children [
Reference(#Y)
]
),
(b() #Z)
]
}
let ids = world.spawn_scene_list(a()).unwrap();
assert_eq!(ids.len(), 3);
let e0 = world.entity(ids[0]);
let name = e0.get::<Name>().unwrap();
assert_eq!(name.as_str(), "X");
let reference = e0.get::<Reference>().unwrap();
assert_eq!(reference.0, ids[1]);
let child0 = e0.get::<Children>().unwrap()[0];
let reference = world.entity(child0).get::<Reference>().unwrap();
assert_eq!(reference.0, ids[0]);
let e1 = world.entity(ids[1]);
let name = e1.get::<Name>().unwrap();
assert_eq!(name.as_str(), "Y");
let reference = e1.get::<Reference>().unwrap();
assert_eq!(reference.0, ids[0]);
let child0 = e1.get::<Children>().unwrap()[0];
let reference = world.entity(child0).get::<Reference>().unwrap();
assert_eq!(reference.0, ids[1]);
let e2 = world.entity(ids[2]);
let name = e2.get::<Name>().unwrap();
assert_eq!(name.as_str(), "Z");
let child0 = e2.get::<Children>().unwrap()[0];
let reference = world.entity(child0).get::<Reference>().unwrap();
assert_eq!(reference.0, ids[2]);
}
#[test]
fn on_template() {
#[derive(Resource)]
struct Exploded(Option<Entity>);
#[derive(EntityEvent)]
struct Explode(Entity);
let mut app = test_app();
let world = app.world_mut();
world.insert_resource(Exploded(None));
fn scene() -> impl Scene {
bsn! {
on(|explode: On<Explode>, mut exploded: ResMut<Exploded>|{
exploded.0 = Some(explode.0);
})
}
}
let id = world.spawn_scene(scene()).unwrap().id();
world.trigger(Explode(id));
let exploded = world.resource::<Exploded>();
assert_eq!(exploded.0, Some(id));
}
#[test]
fn primitive_literals() {
#![allow(dead_code, reason = "test")]
macro_rules! types_fields {
(def $name:ident, $($typ:ident),*) => {
#[derive(Component, FromTemplate)]
struct $name {
$(
$typ: $typ,
)*
}
};
($world:ident, $name:ident($a:expr, $b:expr, $c:expr, $d:expr, $e:expr), $($typ:ident),*) => {
types_fields!($world, $name($a, $b, $c, $d), $($typ),*);
types_fields!(val $world, $e, $name, $($typ),*);
};
($world:ident, $name:ident($a:expr, $b:expr, $c:expr, $d:expr), $($typ:ident),*) => {
types_fields!($world, $name($a, $b, $c), $($typ),*);
types_fields!(val $world, $d, $name, $($typ),*);
};
($world:ident, $name:ident($a:expr, $b:expr, $c:expr), $($typ:ident),*) => {
types_fields!($world, $name($a, $b), $($typ),*);
types_fields!(val $world, $c, $name, $($typ),*);
};
($world:ident, $name:ident($a:expr, $b:expr), $($typ:ident),*) => {
types_fields!($world, $name($a), $($typ),*);
types_fields!(val $world, $b, $name, $($typ),*);
};
($world:ident, $name:ident($a:expr), $($typ:ident),*) => {
types_fields!(def $name, $($typ),*);
types_fields!(val $world, $a, $name, $($typ),*);
};
(val $world:ident, $a:expr, $name:ident, $($typ:ident),*) => {
let v = bsn!{ $name {
$(
$typ: $a,
)*
}};
$world.spawn_scene(v).unwrap();
};
}
let mut app = test_app();
let world = app.world_mut();
types_fields!(world, Unsigned(0, 1), usize, u8, u16, u32, u64, u128);
types_fields!(world, Signed(-1, 0, 1), isize, i8, i16, i32, i64, i128);
types_fields!(
world,
Float(-1.0, 0.0, 1.0, core::f32::consts::PI, -1.),
f32,
f64
);
types_fields!(world, Bool(true, false), bool);
#[derive(Component, FromTemplate)]
struct Random {
str: &'static str,
string: String,
vec: Vec<u8>,
array: [u8; 4],
}
let scene = bsn! {
Random{
str: "test",
string: "test",
vec: {vec![0, 1]},
array: {[0, 1, 2, 3]}
}
};
world.spawn_scene(scene).unwrap();
}
#[test]
fn children_list_expr() {
fn container(items: impl SceneList) -> impl Scene {
bsn! {
#Root
Children [
#First,
{items},
#Last
]
}
}
let mut app = test_app();
let world = app.world_mut();
let items = bsn_list![
#Second,
#Third
];
let id = world.spawn_scene(container(items)).unwrap().id();
let children = world.entity(id).get::<Children>().unwrap();
let names: Vec<_> = children
.iter()
.map(|id| world.entity(id).get::<Name>().unwrap().as_str())
.collect();
assert_eq!(&names, &["First", "Second", "Third", "Last"]);
}
#[test]
fn children_single_expr() {
fn container(item: impl Scene) -> impl Scene {
bsn! {
#Root
Children [
#First,
({item}),
#Last
]
}
}
let mut app = test_app();
let world = app.world_mut();
let items = bsn![
#Second
];
let id = world.spawn_scene(container(items)).unwrap().id();
let children = world.entity(id).get::<Children>().unwrap();
let names: Vec<_> = children
.iter()
.map(|id| world.entity(id).get::<Name>().unwrap().as_str())
.collect();
assert_eq!(&names, &["First", "Second", "Last"]);
}
#[test]
fn conditional_scene() {
#[derive(Component, Clone, Default)]
struct Grunt;
#[derive(Component, Clone, Default)]
struct Boss;
#[derive(Component, Clone, Default, PartialEq, Eq)]
struct Level(u32);
fn unit(is_boss: bool, level: u32) -> impl Scene {
let scene: Box<dyn Scene> = if is_boss {
Box::new(bsn! {
Boss
Children [ unit(false, level - 1) #Grunt1, unit(false, level - 1) #Grunt2]
})
} else {
Box::new(bsn! { Grunt })
};
bsn! {
Level(level)
{scene}
}
}
let mut app = test_app();
let world = app.world_mut();
let id = world.spawn_scene(unit(true, 10)).unwrap().id();
let children = world.entity(id).get::<Children>().unwrap();
let names: Vec<_> = children
.iter()
.map(|id| world.entity(id).get::<Name>().unwrap().as_str())
.collect();
assert_eq!(&names, &["Grunt1", "Grunt2"]);
let names: Vec<_> = children
.iter()
.map(|id| world.entity(id).get::<Level>().unwrap().0)
.collect();
assert_eq!(&names, &[9, 9]);
}
#[test]
fn partial_tuple_struct() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, Default, Clone)]
struct TupleStruct(f32, u32);
fn a() -> impl Scene {
bsn! {
TupleStruct(0.1)
}
}
let id = world.spawn_scene(a()).unwrap().id();
let root = world.entity(id);
let foo = root.get::<TupleStruct>().unwrap();
assert_eq!(foo.0, 0.1);
assert_eq!(foo.1, 0);
}
#[test]
fn scene_expression_passing_pointless() {
#[derive(Component, Default, Clone)]
struct Health {
current: u8,
max: u8,
}
#[derive(Component, Default, Clone)]
struct Armor(u8);
fn unit_with_armor(unit_base: impl Scene) -> impl Scene {
bsn! {
{unit_base}
Armor(50)
}
}
fn armor() -> impl Scene {
bsn! {
Armor(50)
}
}
let mut app = test_app();
let world = app.world_mut();
let inner = bsn! { Health { current: 100, max: 100 } };
let ida = world.spawn_scene(unit_with_armor(inner)).unwrap().id();
let entity_b = bsn! {
armor()
Health { current: 100, max: 100 }
};
let idb = world.spawn_scene(entity_b).unwrap().id();
assert_eq!(
world.entity(ida).archetype().components(),
world.entity(idb).archetype().components()
);
}
#[test]
fn enum_patching() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, FromTemplate, PartialEq, Eq, Debug)]
enum Foo {
#[default]
Bar {
x: u32,
y: u32,
z: u32,
},
Baz(usize),
Qux,
}
fn a() -> impl Scene {
bsn! {
Foo::Baz(10)
}
}
fn b() -> impl Scene {
bsn! {
a()
Foo::Bar { x: 1 }
}
}
fn c() -> impl Scene {
bsn! {
b()
Foo::Bar { y: 2 }
}
}
fn d() -> impl Scene {
bsn! {
c()
Foo::Qux
}
}
let id = world.spawn_scene(c()).unwrap().id();
let root = world.entity(id);
let foo = root.get::<Foo>().unwrap();
assert_eq!(Foo::Bar { x: 1, y: 2, z: 0 }, *foo);
let id = world.spawn_scene(a()).unwrap().id();
let root = world.entity(id);
let foo = root.get::<Foo>().unwrap();
assert_eq!(Foo::Baz(10), *foo);
let id = world.spawn_scene(d()).unwrap().id();
let root = world.entity(id);
let foo = root.get::<Foo>().unwrap();
assert_eq!(Foo::Qux, *foo);
}
#[test]
fn struct_patching() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, FromTemplate, PartialEq, Eq, Debug)]
struct Foo {
x: u32,
y: u32,
z: u32,
nested: Bar,
}
#[derive(Component, FromTemplate, PartialEq, Eq, Debug)]
struct Bar(usize, usize, usize);
fn a() -> impl Scene {
bsn! {
Foo {
x: 1,
nested: Bar(1, 1),
}
}
}
fn b() -> impl Scene {
bsn! {
a()
Foo {
y: 2,
nested: Bar(2),
}
}
}
let id = world.spawn_scene(b()).unwrap().id();
let root = world.entity(id);
let foo = root.get::<Foo>().unwrap();
assert_eq!(
*foo,
Foo {
x: 1,
y: 2,
z: 0,
nested: Bar(2, 1, 0)
}
);
}
#[test]
fn field_patching_with_default() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, Clone, Default, PartialEq, Eq, Debug)]
struct Foo {
x: u32,
y: u32,
z: u32,
nested: Bar,
}
#[derive(Component, Clone, Default, PartialEq, Eq, Debug)]
struct Bar(usize, usize, usize);
fn a() -> impl Scene {
bsn! {
Foo {
x: 1,
nested: Bar(1, 1),
}
}
}
fn b() -> impl Scene {
bsn! {
a()
Foo {
y: 2,
nested: Bar(2),
}
}
}
let id = world.spawn_scene(b()).unwrap().id();
let root = world.entity(id);
let foo = root.get::<Foo>().unwrap();
assert_eq!(
*foo,
Foo {
x: 1,
y: 2,
z: 0,
nested: Bar(2, 1, 0)
}
);
}
#[test]
fn handle_template() {
let mut app = test_app();
app.init_asset::<Image>();
#[derive(Asset, TypePath)]
struct Image;
let handle = app.world().resource::<AssetServer>().load("image.png");
let world = app.world_mut();
#[derive(Component, FromTemplate, PartialEq, Eq, Debug)]
struct Sprite(Handle<Image>);
fn scene() -> impl Scene {
bsn! {
Sprite("image.png")
}
}
let id = world.spawn_scene(scene()).unwrap().id();
let root = world.entity(id);
let sprite = root.get::<Sprite>().unwrap();
assert_eq!(sprite.0, handle);
}
#[test]
fn child_of_template() {
let mut app = test_app();
let world = app.world_mut();
fn scene(root: Entity) -> impl SceneList {
bsn_list! {
( #Child1 ChildOf(root) ),
( #Child2 ChildOf(#Child1) ),
}
}
let root = world.spawn_empty().id();
let ids = world.spawn_scene_list(scene(root)).unwrap();
assert_eq!(ids.len(), 2);
let [a, b] = world.entity(&*ids)[..] else {
unreachable!()
};
assert_eq!(a.get::<Name>().unwrap().as_str(), "Child1");
assert_eq!(a.get::<ChildOf>().unwrap().get(), root);
assert_eq!(b.get::<Name>().unwrap().as_str(), "Child2");
assert_eq!(b.get::<ChildOf>().unwrap().get(), a.id());
}
#[test]
fn scene_list_children() {
let mut app = test_app();
let world = app.world_mut();
fn root(children: impl SceneList) -> impl Scene {
bsn! {
Children [
#A,
{children},
#D
]
}
}
let children = bsn_list! [
#B,
#C,
];
let id = world.spawn_scene(root(children)).unwrap().id();
let root = world.entity(id);
let children = root.get::<Children>().unwrap();
let a = world.entity(children[0]).get::<Name>().unwrap();
let b = world.entity(children[1]).get::<Name>().unwrap();
let c = world.entity(children[2]).get::<Name>().unwrap();
let d = world.entity(children[3]).get::<Name>().unwrap();
assert_eq!(a.as_str(), "A");
assert_eq!(b.as_str(), "B");
assert_eq!(c.as_str(), "C");
assert_eq!(d.as_str(), "D");
}
#[test]
fn generic_patching() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, FromTemplate, PartialEq, Eq, Debug)]
struct Foo<T: FromTemplate<Template: Default + Template<Output = T>>> {
value: T,
number: u32,
}
#[derive(Component, FromTemplate, PartialEq, Eq, Debug)]
struct Position {
x: u32,
y: u32,
z: u32,
}
fn a() -> impl Scene {
bsn! {
Foo::<Position> {
value: Position { x: 1 }
}
}
}
fn b() -> impl Scene {
bsn! {
a()
Foo::<Position> {
value: Position { y: 2 },
number: 10,
}
}
}
let id = world.spawn_scene(b()).unwrap().id();
let root = world.entity(id);
let foo = root.get::<Foo<Position>>().unwrap();
assert_eq!(
*foo,
Foo {
value: Position { x: 1, y: 2, z: 0 },
number: 10
}
);
}
#[test]
fn empty_scene_expressions() {
let mut app = test_app();
let world = app.world_mut();
fn a() -> impl Scene {
bsn! {
{}
}
}
world.spawn_scene(a()).unwrap();
}
#[test]
fn closures_in_bsn() {
#[derive(Resource, Default)]
struct TotalHealed(u32);
#[derive(EntityEvent)]
struct Heal(Entity);
let mut app = test_app();
let world = app.world_mut();
world.init_resource::<TotalHealed>();
fn non_move_scene() -> impl Scene {
bsn! {
on(|_: On<Heal>, mut healed: ResMut<TotalHealed>| {
healed.0 += 1;
})
}
}
let id = world.spawn_scene(non_move_scene()).unwrap().id();
world.trigger(Heal(id));
assert_eq!(world.resource::<TotalHealed>().0, 1);
world.resource_mut::<TotalHealed>().0 = 0;
fn on_heal(_: On<Heal>, mut healed: ResMut<TotalHealed>) {
healed.0 += 2;
}
fn function_scene() -> impl Scene {
bsn! {
on(on_heal)
}
}
let id = world.spawn_scene(function_scene()).unwrap().id();
world.trigger(Heal(id));
assert_eq!(world.resource::<TotalHealed>().0, 2);
world.resource_mut::<TotalHealed>().0 = 0;
fn move_scene(bonus: u32) -> impl Scene {
bsn! {
on(move |_: On<Heal>, mut healed: ResMut<TotalHealed>| {
healed.0 += bonus;
})
}
}
let id = world.spawn_scene(move_scene(42)).unwrap().id();
world.trigger(Heal(id));
assert_eq!(world.resource::<TotalHealed>().0, 42);
}
#[test]
fn comments_in_bsn() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, Clone, Default)]
struct Marker;
fn yappy() -> impl Scene {
bsn! {
#MyName
Marker
}
}
world.spawn_scene(yappy()).unwrap();
}
#[test]
fn bsn_entry_can_surpass_tuple_limit() {
let _ = bsn! {
Name
Name
Name
Name
Name
Name
Name
Name
Name
Name
Name
Name
Name
Name
};
}
#[derive(Component, Default)]
struct Fail;
impl FromTemplate for Fail {
type Template = Fail;
}
impl Template for Fail {
type Output = Fail;
fn build_template(
&self,
_context: &mut bevy_ecs::template::TemplateContext,
) -> Result<Self::Output> {
Err(BevyError::error("fail!"))
}
fn clone_template(&self) -> Self {
todo!()
}
}
#[test]
fn queue_spawn_scene_during_spawn() {
#[derive(Component, Default, Clone)]
#[component(on_insert)]
struct SpawnOnInsert;
impl SpawnOnInsert {
fn on_insert(mut world: DeferredWorld, _context: HookContext) {
world.commands().queue_spawn_scene(scene2());
}
}
fn scene1() -> impl Scene {
bsn!(SpawnOnInsert)
}
fn scene2() -> impl Scene {
bsn!(#Name)
}
let mut app = test_app();
let world = app.world_mut();
world.queue_spawn_scene(scene1());
app.update();
}
#[test]
fn component_scene() {
#[derive(SceneComponent, Default, Clone)]
struct Widget;
impl Widget {
fn scene() -> impl Scene {
bsn! {Name("widget")}
}
}
let mut app = test_app();
let world = app.world_mut();
let entity = world.spawn_scene(bsn! {@Widget}).unwrap();
assert_eq!(entity.get::<Name>().unwrap().as_str(), "widget");
assert!(entity.contains::<Widget>());
#[derive(SceneComponent, Default, Clone)]
#[scene(Widget::scene)]
struct OtherWidget;
let entity = world.spawn_scene(bsn! {@OtherWidget}).unwrap();
assert_eq!(entity.get::<Name>().unwrap().as_str(), "widget");
assert!(entity.contains::<OtherWidget>());
assert!(
!entity.contains::<Widget>(),
"This reuses the Widget::scene function, but that scene does not explicitly add Widget"
);
}
#[test]
fn component_scene_props() {
#[derive(SceneComponent, Default, Clone)]
#[scene(WidgetProps)]
struct Widget {
value: usize,
}
#[derive(Default)]
struct WidgetProps {
children: usize,
}
impl Widget {
fn scene(props: WidgetProps) -> impl Scene {
let children = (0..props.children)
.map(|i| {
bsn! {
Name({format!("Child{i}")})
}
})
.collect::<Vec<_>>();
bsn! {
Children [
{children}
]
}
}
}
let mut app = test_app();
let world = app.world_mut();
let entity = world
.spawn_scene(bsn! {@Widget {
@children: 2,
value: 10,
}})
.unwrap();
assert_eq!(entity.get::<Widget>().unwrap().value, 10);
assert_eq!(entity.get::<Children>().unwrap().len(), 2);
#[derive(SceneComponent, Default, Clone)]
#[scene(Widget::scene(WidgetProps))]
struct OtherWidget {
value: usize,
}
let entity = world
.spawn_scene(bsn! {@OtherWidget {
@children: 2,
value: 10,
}})
.unwrap();
assert_eq!(entity.get::<OtherWidget>().unwrap().value, 10);
assert_eq!(entity.get::<Children>().unwrap().len(), 2);
fn const_val<const N: usize>() -> impl Scene {
bsn! {
@Widget {
@children: N
}
}
}
#[derive(SceneComponent, Clone, Default)]
#[scene(const_val::<5>)]
struct SpecificWidget;
let entity = world.spawn_scene(bsn! { @SpecificWidget }).unwrap();
assert_eq!(entity.get::<Children>().unwrap().len(), 5);
}
#[test]
fn scene_without_explicit_component_still_spawns_component() {
#[derive(SceneComponent, Default, Clone)]
struct Widget;
impl Widget {
fn scene() -> impl Scene {
bsn! {}
}
}
let mut app = test_app();
let world = app.world_mut();
let entity = world.spawn_scene(bsn! {@Widget}).unwrap();
assert!(entity.contains::<Widget>());
}
#[test]
fn tuple_scene_component_name_reference() {
#[derive(SceneComponent, FromTemplate)]
struct Widget(pub Entity);
impl Widget {
fn scene() -> impl Scene {
bsn! {}
}
}
let scene = bsn! {
#Name
Children [
@Widget(#Name)
]
};
let mut app = test_app();
let world = app.world_mut();
let entity = world.spawn_scene(scene).unwrap().id();
let root = world.entity(entity);
let children = root.get::<Children>().unwrap();
let child_widget = world.entity(children[0]).get::<Widget>().unwrap();
assert_eq!(child_widget.0, entity);
}
#[test]
fn named_scene_component_name_reference() {
#[derive(SceneComponent, FromTemplate)]
struct Widget {
entity: Entity,
}
impl Widget {
fn scene() -> impl Scene {
bsn! {}
}
}
let scene = bsn! {
#Name
Children [
@Widget{
entity: #Name
}
]
};
let mut app = test_app();
let world = app.world_mut();
let entity = world.spawn_scene(scene).unwrap().id();
let root = world.entity(entity);
let children = root.get::<Children>().unwrap();
let child_widget = world.entity(children[0]).get::<Widget>().unwrap();
assert_eq!(child_widget.entity, entity);
}
#[test]
fn scene_function_name_reference() {
use bevy_ecs::template::EntityTemplate;
#[derive(Component, FromTemplate)]
struct Reference(Entity);
fn widget(entity: EntityTemplate) -> impl Scene {
bsn! {
Reference(entity)
}
}
let mut app = test_app();
let world = app.world_mut();
let pass_expr = bsn! {
#Name
Children [
widget(Entity::PLACEHOLDER.into())
]
};
let entity = world.spawn_scene(pass_expr).unwrap().id();
let root = world.entity(entity);
let children = root.get::<Children>().unwrap();
let child_widget = world.entity(children[0]).get::<Reference>().unwrap();
assert_eq!(child_widget.0, Entity::PLACEHOLDER);
let pass_name = bsn! {
#Name
Children [
widget(#Name)
]
};
let entity = world.spawn_scene(pass_name).unwrap().id();
let root = world.entity(entity);
let children = root.get::<Children>().unwrap();
let child_widget = world.entity(children[0]).get::<Reference>().unwrap();
assert_eq!(child_widget.0, entity);
let i = 5;
let pass_name_expr = bsn! {
#Root
Name({format!("Foo{i}")})
Children [
#Name
widget(#Root)
]
};
let entity = world.spawn_scene(pass_name_expr).unwrap().id();
let root = world.entity(entity);
let children = root.get::<Children>().unwrap();
let child_widget = world.entity(children[0]).get::<Reference>().unwrap();
assert_eq!(child_widget.0, entity);
let name = root.get::<Name>().unwrap();
assert_eq!(name.as_str(), "Foo5");
}
#[test]
fn scene_component_prop_name_reference() {
use bevy_ecs::template::EntityTemplate;
#[derive(Component, FromTemplate)]
struct Reference(Entity);
#[derive(SceneComponent, Clone, Default)]
#[scene(WidgetProps)]
struct Widget;
#[derive(Default)]
struct WidgetProps {
entity: EntityTemplate,
}
impl Widget {
fn scene(props: WidgetProps) -> impl Scene {
bsn! {
Reference({props.entity})
}
}
}
let mut app = test_app();
let world = app.world_mut();
let prop_expr = bsn! {
Children [
@Widget {
@entity: Entity::PLACEHOLDER
}
]
};
let entity = world.spawn_scene(prop_expr).unwrap().id();
let root = world.entity(entity);
let children = root.get::<Children>().unwrap();
let child_widget = world.entity(children[0]).get::<Reference>().unwrap();
assert_eq!(child_widget.0, Entity::PLACEHOLDER);
let scene_prop = bsn! {
#Name
Children [
@Widget {
@entity: #Name
}
]
};
let entity = world.spawn_scene(scene_prop).unwrap().id();
let root = world.entity(entity);
let children = root.get::<Children>().unwrap();
let child_widget = world.entity(children[0]).get::<Reference>().unwrap();
assert_eq!(child_widget.0, entity);
}
#[test]
fn repeated_call_entity_reference() {
let scenes = (0..6).map(|_: u32| bsn! { #Name }).collect::<Vec<_>>();
let scenes_len = scenes.len();
let mut app = test_app();
let world = app.world_mut();
world.spawn_scene_list(scenes).unwrap();
assert_eq!(world.query::<&Name>().query(world).count(), scenes_len);
}
#[test]
fn drop_is_called_for_uninserted_components() {
#[derive(Component, FromTemplate)]
struct DropTracker(Option<Arc<Mutex<usize>>>);
impl Drop for DropTracker {
fn drop(&mut self) {
if let Some(count) = &mut self.0 {
*count.lock().unwrap() += 1;
}
}
}
let mut app = test_app();
let world = app.world_mut();
let count_arc = Arc::new(Mutex::new(0));
let count = Some(count_arc.clone());
let scene = bsn! {
DropTracker({count.clone()})
Fail
};
let result = world.spawn_scene(scene);
assert!(result.is_err());
assert_eq!(1, *count_arc.lock().unwrap());
}
#[test]
fn despawn_on_failed_spawn() {
let mut app = test_app();
let world = app.world_mut();
let current_entities = world.entities().len();
let result = world.spawn_scene(bsn! {
Fail
});
assert!(result.is_err());
assert_eq!(current_entities, world.entities().len());
}
fn run_app_until(app: &mut App, mut predicate: impl FnMut() -> bool) {
const LARGE_ITERATION_COUNT: usize = 10000;
for _ in 0..LARGE_ITERATION_COUNT {
app.update();
if predicate() {
return;
}
}
panic!("Ran out of loops to return `Some` from `predicate`");
}
#[test]
fn scene_with_blocks() {
#[derive(Component, Clone, Default)]
struct Holder {
const_block: i32,
unsafe_block: i32,
}
fn func() -> impl Scene {
bsn! {
Holder {
const_block: const {0},
unsafe_block: unsafe {0},
}
}
}
func();
}
#[test]
fn macro_doc_test() {
#![allow(unused, reason = "test")]
#![allow(dead_code, reason = "test")]
fn some_scene() -> impl Scene {}
#[derive(Component, Default, Clone)]
struct ComponentA;
#[derive(Component, Default, Clone)]
struct ComponentB(f32, u8);
#[derive(Component, Default, Clone)]
struct Node {
height: f32,
width: f32,
}
fn px(v: f32) -> f32 {
v
}
#[derive(EntityEvent)]
struct MyEntityEvent {
entity: Entity,
value: f32,
}
fn other_scene() -> impl Scene {}
#[derive(Component, FromTemplate)]
struct Link(Entity);
#[derive(SceneComponent, FromTemplate)]
#[scene(scenecomponentscene(Props))]
struct MySceneComponent {
normal_field: u8,
}
#[derive(Default)]
struct Props {
some_prop: u8,
}
fn scenecomponentscene(props: Props) -> impl Scene {}
let some_var: f32 = 0.;
#[derive(SceneComponent, FromTemplate)]
#[scene(scenecomponentscene2(Props2))]
struct Container;
struct Props2 {
items: Box<dyn SceneList>,
}
impl Default for Props2 {
fn default() -> Self {
Self {
items: Box::new(bsn_list!()),
}
}
}
fn scenecomponentscene2(props: Props2) -> impl Scene {}
#[derive(Component, Default, Clone)]
struct SomeComponent;
let scene = bsn! {
some_scene() #SomeName ComponentA ComponentB(0.0) Node {
height: px(0.1) }
on(|evt: On<MyEntityEvent>, mut query: Query<&mut ComponentB>| { let mut b = query.get_mut(evt.entity).unwrap();
b.0 += evt.value;
})
Children [ #Child1 ComponentA , (other_scene() #Child3), Link(#SomeName), @MySceneComponent { @some_prop: 3, normal_field: 5 },
Node {
width: some_var }
ComponentB({some_var + 3.}) @Container {
@items: {
bsn_list![ #item1 SomeComponent, some_scene() #item2
]
}
}
]
};
let mut app = test_app();
let world = app.world_mut();
let entity = world.spawn_scene(scene).unwrap().id();
}
#[test]
fn scene_with_oneshot_system() {
#[derive(Component, FromTemplate)]
struct Callback {
callback: SystemHandle<(), ()>,
}
fn my_system() {}
let mut app = test_app();
let world = app.world_mut();
let direct = bsn! {
Callback {
callback: system_value(my_system)
}
};
let direct_ent = world.spawn_scene(direct).unwrap();
assert!(direct_ent.get::<Callback>().is_some());
let id = world.register_tracked_system(my_system);
let id2 = id.clone();
let indirect = bsn! {
Callback {
callback: id
}
};
let indirect_ent = world.spawn_scene(indirect).unwrap();
assert!(indirect_ent.get::<Callback>().unwrap().callback == id2);
}
#[test]
fn direct_macro_values_in_bsn() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, Default, Clone)]
struct Foo {
value: Vec<usize>,
}
let entity = world
.spawn_scene(bsn! {
Foo {
value: vec! [ 10 ],
}
})
.unwrap();
assert_eq!(entity.get::<Foo>().unwrap().value, vec![10]);
}
#[test]
fn enum_variant_field_values_use_implicit_into() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, Default, Clone)]
struct TextFont {
font_size: FontSize,
}
#[derive(Default, Clone, Debug, PartialEq, Eq)]
struct FontSize(u32);
enum TextSize {
Large,
}
impl From<TextSize> for FontSize {
fn from(value: TextSize) -> Self {
match value {
TextSize::Large => FontSize(24),
}
}
}
let entity = world
.spawn_scene(bsn! {
TextFont {
font_size: TextSize::Large,
}
})
.unwrap();
assert_eq!(entity.get::<TextFont>().unwrap().font_size, FontSize(24));
}
#[test]
fn field_name_shorthand() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, Default, Clone)]
struct Foo {
value: usize,
}
let value = 10usize;
let entity = world.spawn_scene(bsn! { Foo { value } }).unwrap();
assert_eq!(entity.get::<Foo>().unwrap().value, 10);
#[derive(SceneComponent, Default, Clone)]
#[scene(BarProps)]
struct Bar {
value: usize,
}
#[derive(Default)]
struct BarProps {
value: usize,
}
impl Bar {
fn scene(props: BarProps) -> impl Scene {
bsn! {Bar {
value: {props.value}
}}
}
}
let value = 10usize;
let entity = world.spawn_scene(bsn! { @Bar { @value } }).unwrap();
assert_eq!(entity.get::<Bar>().unwrap().value, 10);
#[derive(Component, Default, Clone)]
struct Baz {
value: X,
}
#[derive(Default, Clone)]
struct X;
#[derive(Default, Clone)]
struct Y;
impl From<Y> for X {
fn from(_: Y) -> Self {
X
}
}
let value = Y;
let _ = world.spawn_scene(bsn! { Baz { value } }).unwrap();
}
#[test]
fn scene_with_optional_components() {
let mut app = test_app();
let world = app.world_mut();
#[derive(Component, Default, Clone)]
struct Foo;
let optional_component = Some(bsn! {
Foo
});
let entity = world
.spawn_scene(bsn! {
#MaybeFoo
{optional_component}
})
.unwrap();
assert!(entity.get::<Foo>().is_some());
}
}