ecs: add commands test and fix Commands
This commit is contained in:
parent
1f43a9d4da
commit
5dfc073db5
|
@ -72,7 +72,7 @@
|
||||||
"--no-run",
|
"--no-run",
|
||||||
"--lib",
|
"--lib",
|
||||||
"--package=lyra-ecs",
|
"--package=lyra-ecs",
|
||||||
"world::tests::view_change_tracking",
|
"command::tests::deferred_commands",
|
||||||
"--",
|
"--",
|
||||||
"--exact --nocapture"
|
"--exact --nocapture"
|
||||||
],
|
],
|
||||||
|
|
|
@ -1456,7 +1456,6 @@ dependencies = [
|
||||||
"paste",
|
"paste",
|
||||||
"rand",
|
"rand",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"unique",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1520,7 +1519,6 @@ dependencies = [
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-math",
|
"lyra-math",
|
||||||
"lyra-reflect-derive",
|
"lyra-reflect-derive",
|
||||||
"lyra-resource",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2688,12 +2686,6 @@ version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unique"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d360722e1f3884f5b14d332185f02ff111f771f0c76a313268fe6af1409aba96"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urlencoding"
|
name = "urlencoding"
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
|
|
|
@ -13,7 +13,6 @@ lyra-ecs-derive = { path = "./lyra-ecs-derive" }
|
||||||
lyra-math = { path = "../lyra-math", optional = true }
|
lyra-math = { path = "../lyra-math", optional = true }
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
thiserror = "1.0.50"
|
thiserror = "1.0.50"
|
||||||
unique = "0.9.1"
|
|
||||||
paste = "1.0.14"
|
paste = "1.0.14"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -1,26 +1,30 @@
|
||||||
use std::{cell::RefMut, collections::VecDeque, mem, ptr::{self, NonNull}};
|
use std::{any::Any, cell::RefMut, collections::VecDeque, ptr::{self, NonNull}};
|
||||||
|
|
||||||
use unique::Unique;
|
|
||||||
|
|
||||||
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World};
|
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World};
|
||||||
|
|
||||||
pub trait Command {
|
pub trait Command: Any {
|
||||||
|
fn as_any_boxed(self: Box<Self>) -> Box<dyn Any>;
|
||||||
|
|
||||||
fn run(self, world: &mut World) -> anyhow::Result<()>;
|
fn run(self, world: &mut World) -> anyhow::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> Command for F
|
impl<F> Command for F
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut World) -> anyhow::Result<()>
|
F: FnOnce(&mut World) -> anyhow::Result<()> + 'static
|
||||||
{
|
{
|
||||||
|
fn as_any_boxed(self: Box<Self>) -> Box<dyn Any> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn run(self, world: &mut World) -> anyhow::Result<()> {
|
fn run(self, world: &mut World) -> anyhow::Result<()> {
|
||||||
self(world)
|
self(world)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RunCommand = unsafe fn(cmd: Unique<()>, world: &mut World) -> anyhow::Result<()>;
|
type RunCommand = unsafe fn(cmd: Box<dyn Command>, world: &mut World) -> anyhow::Result<()>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CommandQueue(VecDeque<(RunCommand, Unique<()>)>);
|
pub struct CommandQueue(VecDeque<(RunCommand, Box<dyn Command>)>);
|
||||||
|
|
||||||
pub struct Commands<'a, 'b> {
|
pub struct Commands<'a, 'b> {
|
||||||
queue: &'b mut CommandQueue,
|
queue: &'b mut CommandQueue,
|
||||||
|
@ -36,31 +40,26 @@ impl<'a, 'b> Commands<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a command to the end of the command queue
|
/// Add a command to the end of the command queue
|
||||||
pub fn add<C: Command>(&mut self, mut cmd: C) {
|
pub fn add<C: Command>(&mut self, cmd: C) {
|
||||||
// get an owned pointer to the command, then forget it to ensure its destructor isn't ran
|
let cmd = Box::new(cmd);
|
||||||
let ptr = Unique::from(&mut cmd).cast::<()>();
|
|
||||||
mem::forget(cmd);
|
let run_fn = |cmd: Box<dyn Command>, world: &mut World| {
|
||||||
|
let cmd = cmd.as_any_boxed()
|
||||||
|
.downcast::<C>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let run_fn = |cmd_ptr: Unique<()>, world: &mut World| unsafe {
|
|
||||||
let cmd = cmd_ptr.cast::<C>();
|
|
||||||
let cmd = ptr::read(cmd.as_ptr());
|
|
||||||
cmd.run(world)?;
|
cmd.run(world)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
self.queue.0.push_back((run_fn, ptr));
|
self.queue.0.push_back((run_fn, cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn<B: Bundle>(&mut self, mut bundle: B) -> Entity {
|
pub fn spawn<B: Bundle + 'static>(&mut self, bundle: B) -> Entity {
|
||||||
let e = self.entities.reserve();
|
let e = self.entities.reserve();
|
||||||
|
|
||||||
let bundle_ptr = Unique::from(&mut bundle);
|
|
||||||
mem::forget(bundle);
|
|
||||||
//let bundle_box = Box::new(bundle);
|
|
||||||
|
|
||||||
self.add(move |world: &mut World| {
|
self.add(move |world: &mut World| {
|
||||||
let bundle = unsafe { ptr::read(bundle_ptr.as_ptr()) };
|
|
||||||
world.spawn_into(e, bundle);
|
world.spawn_into(e, bundle);
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
});
|
||||||
|
@ -101,7 +100,7 @@ impl FnArgFetcher for Commands<'_, '_> {
|
||||||
let world = unsafe { world_ptr.as_mut() };
|
let world = unsafe { world_ptr.as_mut() };
|
||||||
|
|
||||||
let mut cmds = Commands::new(&mut state, world);
|
let mut cmds = Commands::new(&mut state, world);
|
||||||
// safety: Commands has a mut borrow to entities in the world
|
// safety: Commands has a mut borrow only to entities in the world
|
||||||
let world = unsafe { world_ptr.as_mut() };
|
let world = unsafe { world_ptr.as_mut() };
|
||||||
cmds.execute(world).unwrap()
|
cmds.execute(world).unwrap()
|
||||||
}
|
}
|
||||||
|
@ -112,3 +111,40 @@ pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut<Command
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::ptr::NonNull;
|
||||||
|
|
||||||
|
use crate::{system::{GraphExecutor, IntoSystem}, tests::Vec2, Commands, DynTypeId, World};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deferred_commands() {
|
||||||
|
let mut world = World::new();
|
||||||
|
let vecs = vec![Vec2::rand(), Vec2::rand(), Vec2::rand()];
|
||||||
|
world.spawn((vecs[0],));
|
||||||
|
world.spawn((vecs[1],));
|
||||||
|
world.spawn((vecs[2],));
|
||||||
|
|
||||||
|
let spawned_vec = Vec2::rand();
|
||||||
|
|
||||||
|
let spawned_vec_cl = spawned_vec.clone();
|
||||||
|
let test_sys = move |mut commands: Commands| -> anyhow::Result<()> {
|
||||||
|
commands.spawn((spawned_vec_cl.clone(),));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut graph_exec = GraphExecutor::new();
|
||||||
|
graph_exec.insert_system("test", test_sys.into_system(), &[]);
|
||||||
|
graph_exec.execute(NonNull::from(&world), true).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(world.entities.len(), 4);
|
||||||
|
|
||||||
|
// there's only one archetype
|
||||||
|
let arch = world.archetypes.values().next().unwrap();
|
||||||
|
let col = arch.get_column(DynTypeId::of::<Vec2>()).unwrap();
|
||||||
|
let vec2: &Vec2 = unsafe { col.get(3) };
|
||||||
|
assert_eq!(vec2.clone(), spawned_vec);
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,7 +35,6 @@ impl Entities {
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
println!("id is {}", self.next_id.0);
|
|
||||||
let new_id = self.next_id;
|
let new_id = self.next_id;
|
||||||
self.next_id.0 += 1;
|
self.next_id.0 += 1;
|
||||||
|
|
||||||
|
@ -47,6 +46,11 @@ impl Entities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of spawned entities
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.next_id.0 as usize - self.dead.len()
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves the Archetype Record for the entity
|
/// Retrieves the Archetype Record for the entity
|
||||||
pub(crate) fn entity_record(&self, entity: Entity) -> Option<Record> {
|
pub(crate) fn entity_record(&self, entity: Entity) -> Option<Record> {
|
||||||
self.arch_index.get(&entity.id).cloned()
|
self.arch_index.get(&entity.id).cloned()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{ptr::NonNull, marker::PhantomData};
|
use std::{any::Any, marker::PhantomData, ptr::NonNull};
|
||||||
|
|
||||||
use unique::Unique;
|
|
||||||
use paste::paste;
|
use paste::paste;
|
||||||
use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}};
|
use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery, ResMut, Res}};
|
||||||
|
|
||||||
|
@ -12,7 +11,6 @@ pub trait FnArgFetcher {
|
||||||
|
|
||||||
type Arg<'a, 'state>: FnArgFetcher<State = Self::State>;
|
type Arg<'a, 'state>: FnArgFetcher<State = Self::State>;
|
||||||
|
|
||||||
//fn new() -> Self;
|
|
||||||
fn create_state(world: NonNull<World>) -> Self::State;
|
fn create_state(world: NonNull<World>) -> Self::State;
|
||||||
|
|
||||||
/// Return the appropriate world access if this fetcher gets the world directly.
|
/// Return the appropriate world access if this fetcher gets the world directly.
|
||||||
|
@ -40,7 +38,7 @@ pub struct FnSystem<F, Args> {
|
||||||
inner: F,
|
inner: F,
|
||||||
//#[allow(dead_code)]
|
//#[allow(dead_code)]
|
||||||
//args: Args,
|
//args: Args,
|
||||||
arg_state: Option<Vec<Unique<()>>>,
|
arg_state: Option<Vec<Box<dyn Any>>>,
|
||||||
_marker: PhantomData<Args>,
|
_marker: PhantomData<Args>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,10 +58,8 @@ macro_rules! impl_fn_system_tuple {
|
||||||
paste! {
|
paste! {
|
||||||
$(
|
$(
|
||||||
// get the arg fetcher, create its state, and get the arg
|
// get the arg fetcher, create its state, and get the arg
|
||||||
|
|
||||||
let mut [<state_ $name:lower>]: $name::State = $name::create_state(world);
|
let mut [<state_ $name:lower>]: $name::State = $name::create_state(world);
|
||||||
let [<$name:lower>] = $name::get(&mut [<state_ $name:lower>], world);
|
let [<$name:lower>] = $name::get(&mut [<state_ $name:lower>], world);
|
||||||
|
|
||||||
)+
|
)+
|
||||||
|
|
||||||
(self.inner)($( [<$name:lower>] ),+)?;
|
(self.inner)($( [<$name:lower>] ),+)?;
|
||||||
|
@ -71,9 +67,8 @@ macro_rules! impl_fn_system_tuple {
|
||||||
let mut state = Vec::new();
|
let mut state = Vec::new();
|
||||||
$(
|
$(
|
||||||
// type erase the now modified state, and store it
|
// type erase the now modified state, and store it
|
||||||
let [<state_ $name:lower _ptr>] = Unique::from(&mut [<state_ $name:lower>]);
|
let boxed = Box::new([<state_ $name:lower>]) as Box<dyn Any>;
|
||||||
std::mem::forget([<state_ $name:lower>]);
|
state.push(boxed);
|
||||||
state.push([<state_ $name:lower _ptr>].cast::<()>());
|
|
||||||
)+
|
)+
|
||||||
|
|
||||||
self.arg_state = Some(state);
|
self.arg_state = Some(state);
|
||||||
|
@ -84,41 +79,22 @@ macro_rules! impl_fn_system_tuple {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_deferred(&mut self, world: NonNull<World>) -> anyhow::Result<()> {
|
fn execute_deferred(&mut self, world: NonNull<World>) -> anyhow::Result<()> {
|
||||||
|
let state = self.arg_state.as_mut().expect("Somehow there was no state");
|
||||||
|
|
||||||
$(
|
$(
|
||||||
let s = self.arg_state.as_mut().expect("Somehow there was no state").pop().unwrap();
|
let arg_state_box = state.pop()
|
||||||
let s = unsafe { std::ptr::read(s.cast::<$name::State>().as_ptr()) };
|
.expect("Missing expected arg state");
|
||||||
$name::apply_deferred(s, world);
|
let arg_state = *arg_state_box.downcast::<$name::State>()
|
||||||
|
.unwrap();
|
||||||
|
$name::apply_deferred(arg_state, world);
|
||||||
)+
|
)+
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* impl<F, $($name: FnArg,)+> IntoSystem<($($name,)+)> for F
|
|
||||||
where
|
|
||||||
/* for <'a> &'a mut F:
|
|
||||||
FnMut($($name,)+) -> anyhow::Result<()>,
|
|
||||||
FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */
|
|
||||||
F: FnMut($($name,)+) -> anyhow::Result<()>,
|
|
||||||
F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>,
|
|
||||||
{
|
|
||||||
type System = FnSystem<F, ($($name::Fetcher,)+)>;
|
|
||||||
|
|
||||||
fn into_system(self) -> Self::System {
|
|
||||||
FnSystem {
|
|
||||||
//args: ($($name::Fetcher::new(),)+),
|
|
||||||
inner: self,
|
|
||||||
arg_state: None,
|
|
||||||
_marker: PhantomData::<($($name::Fetcher,)+)>::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
impl<F, $($name: FnArgFetcher,)+> IntoSystem<($($name,)+)> for F
|
impl<F, $($name: FnArgFetcher,)+> IntoSystem<($($name,)+)> for F
|
||||||
where
|
where
|
||||||
/* for <'a> &'a mut F:
|
|
||||||
FnMut($($name,)+) -> anyhow::Result<()>,
|
|
||||||
FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a, '_>,)+) -> anyhow::Result<()>, */
|
|
||||||
F: FnMut($($name,)+) -> anyhow::Result<()>,
|
F: FnMut($($name,)+) -> anyhow::Result<()>,
|
||||||
F: for<'a> FnMut($($name::Arg<'a, '_>,)+) -> anyhow::Result<()>,
|
F: for<'a> FnMut($($name::Arg<'a, '_>,)+) -> anyhow::Result<()>,
|
||||||
{
|
{
|
||||||
|
@ -126,7 +102,6 @@ macro_rules! impl_fn_system_tuple {
|
||||||
|
|
||||||
fn into_system(self) -> Self::System {
|
fn into_system(self) -> Self::System {
|
||||||
FnSystem {
|
FnSystem {
|
||||||
//args: ($($name::Fetcher::new(),)+),
|
|
||||||
inner: self,
|
inner: self,
|
||||||
arg_state: None,
|
arg_state: None,
|
||||||
_marker: PhantomData::<($($name,)+)>::default(),
|
_marker: PhantomData::<($($name,)+)>::default(),
|
||||||
|
|
|
@ -93,7 +93,6 @@ impl World {
|
||||||
let entity_arch_id = archetype.add_entity(entity, bundle, &tick);
|
let entity_arch_id = archetype.add_entity(entity, bundle, &tick);
|
||||||
|
|
||||||
// store archetype
|
// store archetype
|
||||||
println!("About to store arch, cap is {}, len is {}", self.archetypes.capacity(), self.archetypes.len());
|
|
||||||
self.archetypes.insert(new_arch_id, archetype);
|
self.archetypes.insert(new_arch_id, archetype);
|
||||||
|
|
||||||
// Create entity record and store it
|
// Create entity record and store it
|
||||||
|
|
Loading…
Reference in New Issue