ecs: add commands test and fix Commands
This commit is contained in:
parent
1f43a9d4da
commit
5dfc073db5
|
@ -72,7 +72,7 @@
|
|||
"--no-run",
|
||||
"--lib",
|
||||
"--package=lyra-ecs",
|
||||
"world::tests::view_change_tracking",
|
||||
"command::tests::deferred_commands",
|
||||
"--",
|
||||
"--exact --nocapture"
|
||||
],
|
||||
|
|
|
@ -1456,7 +1456,6 @@ dependencies = [
|
|||
"paste",
|
||||
"rand",
|
||||
"thiserror",
|
||||
"unique",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1520,7 +1519,6 @@ dependencies = [
|
|||
"lyra-ecs",
|
||||
"lyra-math",
|
||||
"lyra-reflect-derive",
|
||||
"lyra-resource",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2688,12 +2686,6 @@ version = "0.2.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "unique"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d360722e1f3884f5b14d332185f02ff111f771f0c76a313268fe6af1409aba96"
|
||||
|
||||
[[package]]
|
||||
name = "urlencoding"
|
||||
version = "2.1.3"
|
||||
|
|
|
@ -13,7 +13,6 @@ lyra-ecs-derive = { path = "./lyra-ecs-derive" }
|
|||
lyra-math = { path = "../lyra-math", optional = true }
|
||||
anyhow = "1.0.75"
|
||||
thiserror = "1.0.50"
|
||||
unique = "0.9.1"
|
||||
paste = "1.0.14"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -1,26 +1,30 @@
|
|||
use std::{cell::RefMut, collections::VecDeque, mem, ptr::{self, NonNull}};
|
||||
|
||||
use unique::Unique;
|
||||
use std::{any::Any, cell::RefMut, collections::VecDeque, ptr::{self, NonNull}};
|
||||
|
||||
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<()>;
|
||||
}
|
||||
|
||||
impl<F> Command for F
|
||||
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<()> {
|
||||
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)]
|
||||
pub struct CommandQueue(VecDeque<(RunCommand, Unique<()>)>);
|
||||
pub struct CommandQueue(VecDeque<(RunCommand, Box<dyn Command>)>);
|
||||
|
||||
pub struct Commands<'a, 'b> {
|
||||
queue: &'b mut CommandQueue,
|
||||
|
@ -36,31 +40,26 @@ impl<'a, 'b> Commands<'a, 'b> {
|
|||
}
|
||||
|
||||
/// Add a command to the end of the command queue
|
||||
pub fn add<C: Command>(&mut self, mut cmd: C) {
|
||||
// get an owned pointer to the command, then forget it to ensure its destructor isn't ran
|
||||
let ptr = Unique::from(&mut cmd).cast::<()>();
|
||||
mem::forget(cmd);
|
||||
pub fn add<C: Command>(&mut self, cmd: C) {
|
||||
let cmd = Box::new(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)?;
|
||||
|
||||
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 bundle_ptr = Unique::from(&mut bundle);
|
||||
mem::forget(bundle);
|
||||
//let bundle_box = Box::new(bundle);
|
||||
|
||||
self.add(move |world: &mut World| {
|
||||
let bundle = unsafe { ptr::read(bundle_ptr.as_ptr()) };
|
||||
world.spawn_into(e, bundle);
|
||||
Ok(())
|
||||
});
|
||||
|
@ -101,7 +100,7 @@ impl FnArgFetcher for Commands<'_, '_> {
|
|||
let world = unsafe { world_ptr.as_mut() };
|
||||
|
||||
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() };
|
||||
cmds.execute(world).unwrap()
|
||||
}
|
||||
|
@ -112,3 +111,40 @@ pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut<Command
|
|||
|
||||
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
|
||||
}
|
||||
None => {
|
||||
println!("id is {}", self.next_id.0);
|
||||
let new_id = self.next_id;
|
||||
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
|
||||
pub(crate) fn entity_record(&self, entity: Entity) -> Option<Record> {
|
||||
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 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>;
|
||||
|
||||
//fn new() -> Self;
|
||||
fn create_state(world: NonNull<World>) -> Self::State;
|
||||
|
||||
/// Return the appropriate world access if this fetcher gets the world directly.
|
||||
|
@ -40,7 +38,7 @@ pub struct FnSystem<F, Args> {
|
|||
inner: F,
|
||||
//#[allow(dead_code)]
|
||||
//args: Args,
|
||||
arg_state: Option<Vec<Unique<()>>>,
|
||||
arg_state: Option<Vec<Box<dyn Any>>>,
|
||||
_marker: PhantomData<Args>,
|
||||
}
|
||||
|
||||
|
@ -60,10 +58,8 @@ macro_rules! impl_fn_system_tuple {
|
|||
paste! {
|
||||
$(
|
||||
// get the arg fetcher, create its state, and get the arg
|
||||
|
||||
let mut [<state_ $name:lower>]: $name::State = $name::create_state(world);
|
||||
let [<$name:lower>] = $name::get(&mut [<state_ $name:lower>], world);
|
||||
|
||||
)+
|
||||
|
||||
(self.inner)($( [<$name:lower>] ),+)?;
|
||||
|
@ -71,9 +67,8 @@ macro_rules! impl_fn_system_tuple {
|
|||
let mut state = Vec::new();
|
||||
$(
|
||||
// type erase the now modified state, and store it
|
||||
let [<state_ $name:lower _ptr>] = Unique::from(&mut [<state_ $name:lower>]);
|
||||
std::mem::forget([<state_ $name:lower>]);
|
||||
state.push([<state_ $name:lower _ptr>].cast::<()>());
|
||||
let boxed = Box::new([<state_ $name:lower>]) as Box<dyn Any>;
|
||||
state.push(boxed);
|
||||
)+
|
||||
|
||||
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<()> {
|
||||
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 s = unsafe { std::ptr::read(s.cast::<$name::State>().as_ptr()) };
|
||||
$name::apply_deferred(s, world);
|
||||
let arg_state_box = state.pop()
|
||||
.expect("Missing expected arg state");
|
||||
let arg_state = *arg_state_box.downcast::<$name::State>()
|
||||
.unwrap();
|
||||
$name::apply_deferred(arg_state, world);
|
||||
)+
|
||||
|
||||
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
|
||||
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::Arg<'a, '_>,)+) -> anyhow::Result<()>,
|
||||
{
|
||||
|
@ -126,7 +102,6 @@ macro_rules! impl_fn_system_tuple {
|
|||
|
||||
fn into_system(self) -> Self::System {
|
||||
FnSystem {
|
||||
//args: ($($name::Fetcher::new(),)+),
|
||||
inner: self,
|
||||
arg_state: None,
|
||||
_marker: PhantomData::<($($name,)+)>::default(),
|
||||
|
|
|
@ -93,7 +93,6 @@ impl World {
|
|||
let entity_arch_id = archetype.add_entity(entity, bundle, &tick);
|
||||
|
||||
// store archetype
|
||||
println!("About to store arch, cap is {}, len is {}", self.archetypes.capacity(), self.archetypes.len());
|
||||
self.archetypes.insert(new_arch_id, archetype);
|
||||
|
||||
// Create entity record and store it
|
||||
|
|
Loading…
Reference in New Issue