lyra-engine/lyra-ecs/src/command.rs

114 lines
3.1 KiB
Rust
Raw Normal View History

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(())
}