114 lines
3.1 KiB
Rust
114 lines
3.1 KiB
Rust
|
use std::{cell::RefMut, collections::VecDeque, mem, ptr::{self, NonNull}};
|
||
|
|
||
|
use unique::Unique;
|
||
|
|
||
|
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World};
|
||
|
|
||
|
pub trait Command {
|
||
|
fn run(self, world: &mut World) -> anyhow::Result<()>;
|
||
|
}
|
||
|
|
||
|
impl<F> Command for F
|
||
|
where
|
||
|
F: FnOnce(&mut World) -> anyhow::Result<()>
|
||
|
{
|
||
|
fn run(self, world: &mut World) -> anyhow::Result<()> {
|
||
|
self(world)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type RunCommand = unsafe fn(cmd: Unique<()>, world: &mut World) -> anyhow::Result<()>;
|
||
|
|
||
|
#[derive(Default)]
|
||
|
pub struct CommandQueue(VecDeque<(RunCommand, Unique<()>)>);
|
||
|
|
||
|
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<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);
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
pub fn spawn<B: Bundle>(&mut self, mut 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(())
|
||
|
});
|
||
|
|
||
|
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<World>) -> Self::Arg<'a, 'state> {
|
||
|
let world = world_ptr.as_mut();
|
||
|
Commands::new(state, world)
|
||
|
}
|
||
|
|
||
|
fn create_state(_: NonNull<World>) -> Self::State {
|
||
|
CommandQueue::default()
|
||
|
}
|
||
|
|
||
|
fn apply_deferred<'a>(mut state: Self::State, mut world_ptr: NonNull<World>) {
|
||
|
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
|
||
|
let world = unsafe { world_ptr.as_mut() };
|
||
|
cmds.execute(world).unwrap()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn execute_deferred_commands(world: &mut World, mut commands: RefMut<Commands>) -> anyhow::Result<()> {
|
||
|
commands.execute(world)?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|