use super::SystemId; use crate::resource::{Resource, Resources}; use bevy_hecs::{Bundle, Component, DynamicBundle, Entity, World}; use std::{ marker::PhantomData, sync::{Arc, Mutex}, }; /// A queued command to mutate the current [World] or [Resources] pub enum Command { WriteWorld(Box), WriteResources(Box), } /// A [World] mutation pub trait WorldWriter: Send + Sync { fn write(self: Box, world: &mut World); } pub(crate) struct Spawn where T: DynamicBundle + Send + Sync + 'static, { components: T, } impl WorldWriter for Spawn where T: DynamicBundle + Send + Sync + 'static, { fn write(self: Box, world: &mut World) { world.spawn(self.components); } } pub(crate) struct SpawnAsEntity where T: DynamicBundle + Send + Sync + 'static, { entity: Entity, components: T, } impl WorldWriter for SpawnAsEntity where T: DynamicBundle + Send + Sync + 'static, { fn write(self: Box, world: &mut World) { world.spawn_as_entity(self.entity, self.components); } } pub(crate) struct SpawnBatch where I: IntoIterator, I::Item: Bundle, { components_iter: I, } impl WorldWriter for SpawnBatch where I: IntoIterator + Send + Sync, I::Item: Bundle, { fn write(self: Box, world: &mut World) { world.spawn_batch(self.components_iter); } } pub(crate) struct Despawn { entity: Entity, } impl WorldWriter for Despawn { fn write(self: Box, world: &mut World) { world.despawn(self.entity).unwrap(); } } pub struct Insert where T: DynamicBundle + Send + Sync + 'static, { entity: Entity, components: T, } impl WorldWriter for Insert where T: DynamicBundle + Send + Sync + 'static, { fn write(self: Box, world: &mut World) { world.insert(self.entity, self.components).unwrap(); } } pub(crate) struct InsertOne where T: Component, { entity: Entity, component: T, } impl WorldWriter for InsertOne where T: Component, { fn write(self: Box, world: &mut World) { world.insert(self.entity, (self.component,)).unwrap(); } } pub(crate) struct RemoveOne where T: Component, { entity: Entity, phantom: PhantomData, } impl WorldWriter for RemoveOne where T: Component, { fn write(self: Box, world: &mut World) { if world.get::(self.entity).is_ok() { world.remove_one::(self.entity).unwrap(); } } } pub trait ResourcesWriter: Send + Sync { fn write(self: Box, resources: &mut Resources); } pub struct InsertResource { resource: T, } impl ResourcesWriter for InsertResource { fn write(self: Box, resources: &mut Resources) { resources.insert(self.resource); } } pub(crate) struct InsertLocalResource { resource: T, system_id: SystemId, } impl ResourcesWriter for InsertLocalResource { fn write(self: Box, resources: &mut Resources) { resources.insert_local(self.system_id, self.resource); } } #[derive(Default)] pub struct CommandsInternal { pub commands: Vec, pub current_entity: Option, } impl CommandsInternal { pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self { self.spawn_as_entity(Entity::new(), components) } pub fn spawn_as_entity( &mut self, entity: Entity, components: impl DynamicBundle + Send + Sync + 'static, ) -> &mut Self { self.current_entity = Some(entity); self.commands .push(Command::WriteWorld(Box::new(SpawnAsEntity { entity, components, }))); self } pub fn with_bundle( &mut self, components: impl DynamicBundle + Send + Sync + 'static, ) -> &mut Self { let current_entity = self.current_entity.expect("Cannot add components because the 'current entity' is not set. You should spawn an entity first."); self.commands.push(Command::WriteWorld(Box::new(Insert { entity: current_entity, components, }))); self } pub fn with(&mut self, component: impl Component) -> &mut Self { let current_entity = self.current_entity.expect("Cannot add component because the 'current entity' is not set. You should spawn an entity first."); self.commands.push(Command::WriteWorld(Box::new(InsertOne { entity: current_entity, component, }))); self } pub fn write_world(&mut self, world_writer: W) -> &mut Self { self.commands .push(Command::WriteWorld(Box::new(world_writer))); self } pub fn write_resources( &mut self, resources_writer: W, ) -> &mut Self { self.commands .push(Command::WriteResources(Box::new(resources_writer))); self } } /// A queue of [Command]s to run on the current [World] and [Resources] #[derive(Default, Clone)] pub struct Commands { pub commands: Arc>, } impl Commands { pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self { self.spawn_as_entity(Entity::new(), components) } pub fn spawn_as_entity( &mut self, entity: Entity, components: impl DynamicBundle + Send + Sync + 'static, ) -> &mut Self { { let mut commands = self.commands.lock().unwrap(); commands.spawn_as_entity(entity, components); } self } pub fn spawn_batch(&mut self, components_iter: I) -> &mut Self where I: IntoIterator + Send + Sync + 'static, I::Item: Bundle, { self.write_world(SpawnBatch { components_iter }) } /// Despawns only the specified entity, ignoring any other consideration. pub fn despawn(&mut self, entity: Entity) -> &mut Self { self.write_world(Despawn { entity }) } pub fn with(&mut self, component: impl Component) -> &mut Self { { let mut commands = self.commands.lock().unwrap(); commands.with(component); } self } pub fn with_bundle( &mut self, components: impl DynamicBundle + Send + Sync + 'static, ) -> &mut Self { { let mut commands = self.commands.lock().unwrap(); commands.with_bundle(components); } self } pub fn insert( &mut self, entity: Entity, components: impl DynamicBundle + Send + Sync + 'static, ) -> &mut Self { self.write_world(Insert { entity, components }) } pub fn insert_one(&mut self, entity: Entity, component: impl Component) -> &mut Self { self.write_world(InsertOne { entity, component }) } pub fn insert_resource(&mut self, resource: T) -> &mut Self { self.write_resources(InsertResource { resource }) } pub fn insert_local_resource( &mut self, system_id: SystemId, resource: T, ) -> &mut Self { self.write_resources(InsertLocalResource { system_id, resource, }) } pub fn write_world(&mut self, world_writer: W) -> &mut Self { self.commands.lock().unwrap().write_world(world_writer); self } pub fn write_resources( &mut self, resources_writer: W, ) -> &mut Self { self.commands .lock() .unwrap() .write_resources(resources_writer); self } pub fn apply(&self, world: &mut World, resources: &mut Resources) { let mut commands = self.commands.lock().unwrap(); for command in commands.commands.drain(..) { match command { Command::WriteWorld(writer) => { writer.write(world); } Command::WriteResources(writer) => writer.write(resources), } } } pub fn current_entity(&self) -> Option { let commands = self.commands.lock().unwrap(); commands.current_entity } pub fn for_current_entity(&mut self, mut f: impl FnMut(Entity)) -> &mut Self { { let commands = self.commands.lock().unwrap(); let current_entity = commands .current_entity .expect("The 'current entity' is not set. You should spawn an entity first."); f(current_entity); } self } pub fn remove_one(&mut self, entity: Entity) -> &mut Self where T: Component, { self.write_world(RemoveOne:: { entity, phantom: PhantomData, }) } } #[cfg(test)] mod tests { use super::Commands; use crate::resource::Resources; use bevy_hecs::World; #[test] fn command_buffer() { let mut world = World::default(); let mut resources = Resources::default(); let mut command_buffer = Commands::default(); command_buffer.spawn((1u32, 2u64)); command_buffer.insert_resource(3.14f32); command_buffer.apply(&mut world, &mut resources); let results = world .query::<(&u32, &u64)>() .iter() .map(|(a, b)| (*a, *b)) .collect::>(); assert_eq!(results, vec![(1u32, 2u64)]); assert_eq!(*resources.get::().unwrap(), 3.14f32); } }