use std::{any::Any, cell::RefMut, collections::VecDeque, ptr::{self, NonNull}}; use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World}; pub trait Command: Any { fn as_any_boxed(self: Box) -> Box; fn run(self, world: &mut World) -> anyhow::Result<()>; } impl Command for F where F: FnOnce(&mut World) -> anyhow::Result<()> + 'static { fn as_any_boxed(self: Box) -> Box { self } fn run(self, world: &mut World) -> anyhow::Result<()> { self(world) } } type RunCommand = unsafe fn(cmd: Box, world: &mut World) -> anyhow::Result<()>; #[derive(Default)] pub struct CommandQueue(VecDeque<(RunCommand, Box)>); pub struct Commands<'a, 'b> { queue: &'b mut CommandQueue, entities: &'a mut Entities, } impl<'a, 'b> Commands<'a, 'b> { pub fn new(queue: &'b mut CommandQueue, world: &'a mut World) -> Self { Self { queue, entities: &mut world.entities, } } /// Add a command to the end of the command queue pub fn add(&mut self, cmd: C) { let cmd = Box::new(cmd); let run_fn = |cmd: Box, world: &mut World| { let cmd = cmd.as_any_boxed() .downcast::() .unwrap(); cmd.run(world)?; Ok(()) }; self.queue.0.push_back((run_fn, cmd)); } pub fn spawn(&mut self, bundle: B) -> Entity { let e = self.entities.reserve(); self.add(move |world: &mut World| { world.spawn_into(e, bundle); Ok(()) }); e } /// Execute all commands in the queue, in order of insertion pub fn execute(&mut self, world: &mut World) -> anyhow::Result<()> { while let Some((cmd_fn, cmd_ptr)) = self.queue.0.pop_front() { unsafe { cmd_fn(cmd_ptr, world)?; } } Ok(()) } } impl FnArgFetcher for Commands<'_, '_> { type State = CommandQueue; type Arg<'a, 'state> = Commands<'a, 'state>; fn world_access(&self) -> Access { Access::Write } unsafe fn get<'a, 'state>(state: &'state mut Self::State, mut world_ptr: ptr::NonNull) -> Self::Arg<'a, 'state> { let world = world_ptr.as_mut(); Commands::new(state, world) } fn create_state(_: NonNull) -> Self::State { CommandQueue::default() } fn apply_deferred<'a>(mut state: Self::State, mut world_ptr: NonNull) { let world = unsafe { world_ptr.as_mut() }; let mut cmds = Commands::new(&mut state, world); // safety: Commands has a mut borrow only to entities in the world let world = unsafe { world_ptr.as_mut() }; cmds.execute(world).unwrap() } } pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut) -> anyhow::Result<()> { commands.execute(world)?; Ok(()) } #[cfg(test)] mod tests { use std::{cell::Ref, 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::()).unwrap(); let vec2: Ref = unsafe { col.get(3) }; assert_eq!(vec2.clone(), spawned_vec); } }