2024-02-24 16:09:26 +00:00
|
|
|
use std::{any::Any, cell::RefMut, collections::VecDeque, ptr::{self, NonNull}};
|
2024-02-23 21:34:21 +00:00
|
|
|
|
|
|
|
use crate::{system::FnArgFetcher, Access, Bundle, Entities, Entity, World};
|
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
/// A Command be used to delay mutation of the world until after this system is ran.
|
2024-02-24 16:09:26 +00:00
|
|
|
pub trait Command: Any {
|
|
|
|
fn as_any_boxed(self: Box<Self>) -> Box<dyn Any>;
|
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
/// Executes the command
|
|
|
|
fn run(self, world: &mut World);
|
2024-02-23 21:34:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<F> Command for F
|
|
|
|
where
|
2024-03-03 02:20:19 +00:00
|
|
|
F: FnOnce(&mut World) + 'static
|
2024-02-23 21:34:21 +00:00
|
|
|
{
|
2024-02-24 16:09:26 +00:00
|
|
|
fn as_any_boxed(self: Box<Self>) -> Box<dyn Any> {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
fn run(self, world: &mut World) {
|
2024-02-23 21:34:21 +00:00
|
|
|
self(world)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
type RunCommand = unsafe fn(cmd: Box<dyn Command>, world: &mut World);
|
2024-02-23 21:34:21 +00:00
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
/// Stores a queue of commands that will get executed after the system is ran.
|
|
|
|
///
|
|
|
|
/// This struct can be inserted as a resource into the world, and the commands will be
|
|
|
|
/// executed by the [`GraphExecutor`](crate::system::GraphExecutor) after the system is executed.
|
2024-02-23 21:34:21 +00:00
|
|
|
#[derive(Default)]
|
2024-02-24 16:09:26 +00:00
|
|
|
pub struct CommandQueue(VecDeque<(RunCommand, Box<dyn Command>)>);
|
2024-02-23 21:34:21 +00:00
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
/// Used in a system to queue up commands that will run right after this system.
|
|
|
|
///
|
|
|
|
/// This can be used to delay the mutation of the world until after the system is ran. These
|
|
|
|
/// must be used if you're mutating the world inside a [`ViewState`](crate::query::ViewState).
|
|
|
|
///
|
|
|
|
/// ```nobuild
|
|
|
|
/// fn particle_spawner_system(
|
|
|
|
/// commands: Commands,
|
|
|
|
/// view: ViewState<(&Campfire, &Transform)>
|
|
|
|
/// ) -> anyhow::Result<()> {
|
|
|
|
/// for (campfire, pos) in view.iter() {
|
|
|
|
/// // If you do not use commands to spawn this, the next iteration
|
|
|
|
/// // of the view will cause a segfault.
|
|
|
|
/// commands.spawn((pos, Particle::new(/* ... */)));
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// Ok(())
|
|
|
|
/// }
|
|
|
|
/// ```
|
2024-02-23 21:34:21 +00:00
|
|
|
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
|
2024-02-24 16:09:26 +00:00
|
|
|
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();
|
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
cmd.run(world);
|
2024-02-23 21:34:21 +00:00
|
|
|
};
|
|
|
|
|
2024-02-24 16:09:26 +00:00
|
|
|
self.queue.0.push_back((run_fn, cmd));
|
2024-02-23 21:34:21 +00:00
|
|
|
}
|
|
|
|
|
2024-03-03 02:20:19 +00:00
|
|
|
/// Spawn an entity into the World. See [`World::spawn`]
|
2024-02-24 16:09:26 +00:00
|
|
|
pub fn spawn<B: Bundle + 'static>(&mut self, bundle: B) -> Entity {
|
2024-02-23 21:34:21 +00:00
|
|
|
let e = self.entities.reserve();
|
|
|
|
|
|
|
|
self.add(move |world: &mut World| {
|
|
|
|
world.spawn_into(e, bundle);
|
|
|
|
});
|
|
|
|
|
|
|
|
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 {
|
2024-03-03 02:20:19 +00:00
|
|
|
cmd_fn(cmd_ptr, world);
|
2024-02-23 21:34:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2024-02-24 16:09:26 +00:00
|
|
|
// safety: Commands has a mut borrow only to entities in the world
|
2024-02-23 21:34:21 +00:00
|
|
|
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(())
|
2024-02-24 16:09:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2024-03-03 01:20:38 +00:00
|
|
|
use std::{cell::Ref, ptr::NonNull};
|
2024-02-24 16:09:26 +00:00
|
|
|
|
|
|
|
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();
|
2024-03-03 01:20:38 +00:00
|
|
|
let vec2: Ref<Vec2> = unsafe { col.get(3) };
|
2024-02-24 16:09:26 +00:00
|
|
|
assert_eq!(vec2.clone(), spawned_vec);
|
|
|
|
}
|
2024-02-23 21:34:21 +00:00
|
|
|
}
|