use crate::{ borrow::AtomicRefCell, cons::{ConsAppend, ConsFlatten}, entity::{Entity, EntityAllocator}, filter::{ChunksetFilterData, Filter}, storage::{Component, ComponentTypeId, Tag, TagTypeId}, world::{ComponentSource, ComponentTupleSet, IntoComponentSource, TagLayout, TagSet, World}, }; use derivative::Derivative; use smallvec::SmallVec; use std::{collections::VecDeque, iter::FromIterator, marker::PhantomData, sync::Arc}; /// This trait can be used to implement custom world writer types that can be directly /// inserted into the command buffer, for more custom and complex world operations. This is analogous /// to the `CommandBuffer::exec_mut` function type, but does not perform explicit any/any archetype /// access. pub trait WorldWritable { /// Destructs the writer and performs the write operations on the world. fn write(self: Arc, world: &mut World); /// Returns the list of `ComponentTypeId` which are written by this command buffer. This is leveraged /// to allow parralel command buffer flushing. fn write_components(&self) -> Vec; /// Returns the list of `TagTypeId` which are written by this command buffer. This is leveraged /// to allow parralel command buffer flushing. fn write_tags(&self) -> Vec; } #[derive(Derivative)] #[derivative(Debug(bound = ""))] struct InsertBufferedCommand { write_components: Vec, write_tags: Vec, #[derivative(Debug = "ignore")] tags: T, #[derivative(Debug = "ignore")] components: C, entities: Vec, } impl WorldWritable for InsertBufferedCommand where T: TagSet + TagLayout + for<'a> Filter>, C: ComponentSource, { fn write(self: Arc, world: &mut World) { let consumed = Arc::try_unwrap(self).unwrap(); world.insert_buffered(&consumed.entities, consumed.tags, consumed.components); } fn write_components(&self) -> Vec { self.write_components.clone() } fn write_tags(&self) -> Vec { self.write_tags.clone() } } #[derive(Derivative)] #[derivative(Debug(bound = ""))] struct InsertCommand { write_components: Vec, write_tags: Vec, #[derivative(Debug = "ignore")] tags: T, #[derivative(Debug = "ignore")] components: C, } impl WorldWritable for InsertCommand where T: TagSet + TagLayout + for<'a> Filter>, C: IntoComponentSource, { fn write(self: Arc, world: &mut World) { let consumed = Arc::try_unwrap(self).unwrap(); world.insert(consumed.tags, consumed.components); } fn write_components(&self) -> Vec { self.write_components.clone() } fn write_tags(&self) -> Vec { self.write_tags.clone() } } #[derive(Derivative)] #[derivative(Debug(bound = ""))] struct DeleteEntityCommand(Entity); impl WorldWritable for DeleteEntityCommand { fn write(self: Arc, world: &mut World) { world.delete(self.0); } fn write_components(&self) -> Vec { Vec::with_capacity(0) } fn write_tags(&self) -> Vec { Vec::with_capacity(0) } } #[derive(Derivative)] #[derivative(Debug(bound = ""))] struct AddTagCommand { entity: Entity, #[derivative(Debug = "ignore")] tag: T, } impl WorldWritable for AddTagCommand where T: Tag, { fn write(self: Arc, world: &mut World) { let consumed = Arc::try_unwrap(self).unwrap(); world.add_tag(consumed.entity, consumed.tag) } fn write_components(&self) -> Vec { Vec::with_capacity(0) } fn write_tags(&self) -> Vec { vec![TagTypeId::of::()] } } #[derive(Derivative)] #[derivative(Debug(bound = ""))] struct RemoveTagCommand { entity: Entity, _marker: PhantomData, } impl WorldWritable for RemoveTagCommand where T: Tag, { fn write(self: Arc, world: &mut World) { world.remove_tag::(self.entity) } fn write_components(&self) -> Vec { Vec::with_capacity(0) } fn write_tags(&self) -> Vec { vec![TagTypeId::of::()] } } #[derive(Derivative)] #[derivative(Debug(bound = ""))] struct AddComponentCommand { #[derivative(Debug = "ignore")] entity: Entity, #[derivative(Debug = "ignore")] component: C, } impl WorldWritable for AddComponentCommand where C: Component, { fn write(self: Arc, world: &mut World) { let consumed = Arc::try_unwrap(self).unwrap(); world .add_component::(consumed.entity, consumed.component) .unwrap(); } fn write_components(&self) -> Vec { vec![ComponentTypeId::of::()] } fn write_tags(&self) -> Vec { Vec::with_capacity(0) } } #[derive(Derivative)] #[derivative(Debug(bound = ""))] struct RemoveComponentCommand { entity: Entity, _marker: PhantomData, } impl WorldWritable for RemoveComponentCommand where C: Component, { fn write(self: Arc, world: &mut World) { world.remove_component::(self.entity) } fn write_components(&self) -> Vec { vec![ComponentTypeId::of::()] } fn write_tags(&self) -> Vec { Vec::with_capacity(0) } } #[allow(clippy::enum_variant_names)] enum EntityCommand { WriteWorld(Arc), ExecWorld(Arc), ExecMutWorld(Arc), } /// A builder type which can be retrieved from the command buffer. This is the ideal use case for /// inserted complex entities with multiple components and tags from a command buffer. Although /// `add_component` will perform a new move operation on every addition, this allows the construction /// of a single `insert` command for an entity, but without using the actual `insert` command /// provided by the `CommandBuffer` /// /// # Examples /// /// Inserting an entity using the `EntityBuilder`: /// /// ``` /// # use legion::prelude::*; /// # #[derive(Copy, Clone, Debug, PartialEq)] /// # struct Position(f32); /// # #[derive(Copy, Clone, Debug, PartialEq)] /// # struct Rotation(f32); /// # let universe = Universe::new(); /// # let mut world = universe.create_world(); /// let mut command_buffer = CommandBuffer::from_world(&mut world); /// command_buffer.build_entity().unwrap() /// .with_component(Position(123.0)) /// .with_component(Rotation(456.0)).build(&mut command_buffer); /// command_buffer.write(&mut world); /// ``` pub struct EntityBuilder { entity: Entity, tags: TS, components: CS, } impl EntityBuilder where TS: 'static + Send + ConsFlatten, CS: 'static + Send + ConsFlatten, { /// Adds a component to this builder, returning a new builder type containing that component type /// and its data. pub fn with_component( self, component: C, ) -> EntityBuilder>::Output> where CS: ConsAppend, >::Output: ConsFlatten, { EntityBuilder { components: ConsAppend::append(self.components, component), entity: self.entity, tags: self.tags, } } /// Adds a tag to this builder, returning a new builder type containing that component type /// and its data. pub fn with_tag(self, tag: T) -> EntityBuilder<>::Output, CS> where TS: ConsAppend, >::Output: ConsFlatten, { EntityBuilder { tags: ConsAppend::append(self.tags, tag), entity: self.entity, components: self.components, } } /// Finalizes this builder type and submits it to the `CommandBuffer` as a `WorldWritable` trait /// object. pub fn build(self, buffer: &mut CommandBuffer) where ::Output: TagSet + TagLayout + for<'a> Filter>, ComponentTupleSet< ::Output, std::iter::Once<::Output>, >: ComponentSource, { buffer .commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(InsertBufferedCommand { write_components: Vec::default(), write_tags: Vec::default(), tags: self.tags.flatten(), components: IntoComponentSource::into(std::iter::once(self.components.flatten())), entities: vec![self.entity], }))); } } /// Errors returned by the `CommandBuffer` #[derive(Debug)] pub enum CommandError { /// The command buffers entity cache has been exhausted. This is defaulted to 64 at `World::DEFAULT_COMMAND_BUFFER_SIZE`. /// This upper limit can be changed via `SystemBuilder::with_command_buffer_size` for specific systems, /// or globally via `World::set_command_buffer_size`. EntityBlockFull, } impl std::fmt::Display for CommandError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CommandError") } } impl std::error::Error for CommandError { fn cause(&self) -> Option<&dyn std::error::Error> { None } } /// A command buffer used to queue mutable changes to the world from a system. This buffer is automatically /// flushed and refreshed at the beginning of every frame by `Schedule`. If `Schedule` is not used, /// then the user needs to manually flush it by performing `CommandBuffer::write`. /// /// This buffer operates as follows: /// - All commands are queued as trait object of type `WorldWritable`, to be executed when `CommandBuffer:write` is called. /// - Entities are allocated at the time of `CommandBuffer:write` occuring, being directly allocated from the world /// and cached internally in the system. This upper cache size can be changed via `SystemBuilder::with_command_buffer_size` /// for specific systems, or globally via `World::set_command_buffer_size`. In the event the cached entity count is exceeded, /// the cache will be refilled on demand from the world `EntityAllocator`. /// /// This behavior exists because `EntityAllocator` is a shared lock within the world, so in order to reduce lock contention with many /// systems running and adding entities, the `CommandBuffer` will cache the configured number of entities - reducing contention. /// /// # Examples /// /// Inserting an entity using the `CommandBuffer`: /// /// ``` /// # use legion::prelude::*; /// # #[derive(Copy, Clone, Debug, PartialEq)] /// # struct Position(f32); /// # #[derive(Copy, Clone, Debug, PartialEq)] /// # struct Rotation(f32); /// # let universe = Universe::new(); /// # let mut world = universe.create_world(); /// let mut command_buffer = CommandBuffer::from_world(&mut world); /// let entity = command_buffer.create_entity().unwrap(); /// /// command_buffer.add_component(entity, Position(123.0)); /// command_buffer.delete(entity); /// /// command_buffer.write(&mut world); /// ``` #[derive(Default)] pub struct CommandBuffer { commands: AtomicRefCell>, entity_allocator: Option>, pub(crate) custom_capacity: Option, pub(crate) free_list: SmallVec<[Entity; 64]>, pub(crate) used_list: SmallVec<[Entity; 64]>, } // This is safe because only 1 system in 1 execution is only ever accessing a command buffer // and we garuntee the write operations of a command buffer occur in a safe manner unsafe impl Send for CommandBuffer {} unsafe impl Sync for CommandBuffer {} impl CommandBuffer { /// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame. /// Allocating a command buffer in this manner will overwrite `World::set_command_buffer_size` and /// this system will always allocate the custom provide capacity of entities every frame. /// /// # Notes /// This function does not perform any actual entity preallocation. `ComamandBuffer:resize` or `CommandBuffer:write` /// must be called before using the command buffer for the first time to make entities available. pub fn with_capacity(capacity: usize) -> Self { // Pull free entities from the world. Self { custom_capacity: Some(capacity), free_list: SmallVec::with_capacity(capacity), commands: Default::default(), used_list: SmallVec::with_capacity(capacity), entity_allocator: None, } } /// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame. /// Allocating a command buffer in this manner will overwrite `World::set_command_buffer_size` and /// this system will always allocate the custom provide capacity of entities every frame. /// /// This constructor will preallocate the first round of entities needed from the world. pub fn from_world_with_capacity(world: &mut World, capacity: usize) -> Self { // Pull free entities from the world. let free_list = SmallVec::from_iter((0..capacity).map(|_| world.entity_allocator.create_entity())); Self { free_list, custom_capacity: Some(capacity), commands: Default::default(), used_list: SmallVec::with_capacity(capacity), entity_allocator: Some(world.entity_allocator.clone()), } } /// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame. /// Allocating a command buffer in this manner will use the default `World::set_command_buffer_size` /// value. /// /// This constructor will preallocate the first round of entities needed from the world. pub fn from_world(world: &mut World) -> Self { // Pull free entities from the world. let free_list = SmallVec::from_iter( (0..world.command_buffer_size()).map(|_| world.entity_allocator.create_entity()), ); Self { free_list, custom_capacity: None, commands: Default::default(), used_list: SmallVec::with_capacity(world.command_buffer_size()), entity_allocator: Some(world.entity_allocator.clone()), } } /// Changes the cached capacity of this `CommandBuffer` to the specified capacity. This includes shrinking /// and growing the allocated entities, and possibly returning them to the entity allocator in the /// case of a shrink. /// /// This function does *NOT* set the `CommandBuffer::custom_capacity` override. #[allow(clippy::comparison_chain)] pub fn resize(&mut self, capacity: usize) { let allocator = &self.entity_allocator; let free_list = &mut self.free_list; if let Some(allocator) = allocator.as_ref() { if free_list.len() < capacity { (free_list.len()..capacity).for_each(|_| free_list.push(allocator.create_entity())); } else if free_list.len() > capacity { // Free the entities (free_list.len() - capacity..capacity).for_each(|_| { allocator.delete_entity(free_list.pop().unwrap()); }); } } else { panic!("Entity allocator not assigned to command buffer") } } /// Flushes this command buffer, draining all stored commands and writing them to the world. /// /// Command flushes are performed in a FIFO manner, allowing for reliable, linear commands being /// executed in the order they were provided. /// /// This function also calls `CommandBuffer:resize`, performing any appropriate entity preallocation, /// refilling the entity cache of any consumed entities. pub fn write(&mut self, world: &mut World) { tracing::trace!("Draining command buffer"); if self.entity_allocator.is_none() { self.entity_allocator = Some(world.entity_allocator.clone()); } let empty = Vec::from_iter((0..self.used_list.len()).map(|_| ())); world.insert_buffered( self.used_list.as_slice(), (), IntoComponentSource::into(empty), ); self.used_list.clear(); while let Some(command) = self.commands.get_mut().pop_back() { match command { EntityCommand::WriteWorld(ptr) => ptr.write(world), EntityCommand::ExecMutWorld(closure) => closure(world), EntityCommand::ExecWorld(closure) => closure(world), } } // Refill our entity buffer from the world if let Some(custom_capacity) = self.custom_capacity { self.resize(custom_capacity); } else { self.resize(world.command_buffer_size()); } } /// Consumed an internally cached entity, returning an `EntityBuilder` using that entity. pub fn build_entity(&mut self) -> Result, CommandError> { let entity = self.create_entity()?; Ok(EntityBuilder { entity, tags: (), components: (), }) } /// Consumed an internally cached entity, or returns `CommandError` pub fn create_entity(&mut self) -> Result { if self.free_list.is_empty() { self.resize( self.custom_capacity .unwrap_or(World::DEFAULT_COMMAND_BUFFER_SIZE), ); } let entity = self.free_list.pop().ok_or(CommandError::EntityBlockFull)?; self.used_list.push(entity); Ok(entity) } /// Executes an arbitrary closure against the mutable world, allowing for queued exclusive /// access to the world. pub fn exec_mut(&self, f: F) where F: 'static + Fn(&mut World), { self.commands .get_mut() .push_front(EntityCommand::ExecMutWorld(Arc::new(f))); } /// Inserts an arbitrary implementor of the `WorldWritable` trait into the command queue. /// This can be leveraged for creating custom `WorldWritable` trait implementors, and is used /// internally for the default writers. pub fn insert_writer(&self, writer: W) where W: 'static + WorldWritable, { self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(writer))); } /// Queues an *unbuffered* insertion into the world. This command follows the same syntax as /// the normal `World::insert`, except for one caviate - entities are NOT returned by this /// function, meaning that the internal entity cache and limits of this `CommandBuffer` are not /// applicable to this function. /// /// This function can be considered a "fire and forget" entity creation method which is not bound /// by the standard command buffer size limits of the other entity insertion functions. This allows /// for mass insertion of entities, exceeding the command buffer sizes, to occur in scenarios that /// the entities do not need to be retrieved. pub fn insert_unbuffered(&mut self, tags: T, components: C) where T: 'static + TagSet + TagLayout + for<'a> Filter>, C: 'static + IntoComponentSource, { self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(InsertCommand { write_components: Vec::default(), write_tags: Vec::default(), tags, components, }))); } /// Queues an insertion into the world. This command follows the same syntax as /// the normal `World::insert`, returning the entities created for this command. pub fn insert(&mut self, tags: T, components: C) -> Result, CommandError> where T: 'static + TagSet + TagLayout + for<'a> Filter>, C: 'static + IntoComponentSource, { let components = components.into(); if components.len() > self.free_list.len() { return Err(CommandError::EntityBlockFull); } let mut entities = Vec::with_capacity(components.len()); for _ in 0..components.len() { entities.push(self.free_list.pop().ok_or(CommandError::EntityBlockFull)?); } self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(InsertBufferedCommand { write_components: Vec::default(), write_tags: Vec::default(), tags, components, entities: entities.clone(), }))); Ok(entities) } /// Queues the deletion of an entity in the command buffer. This writer calls `World::delete` pub fn delete(&self, entity: Entity) { self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(DeleteEntityCommand( entity, )))); } /// Queues the addition of a component from an entity in the command buffer. /// This writer calls `World::add_component` pub fn add_component(&self, entity: Entity, component: C) { self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(AddComponentCommand { entity, component, }))); } /// Queues the removal of a component from an entity in the command buffer. /// This writer calls `World::remove_component` pub fn remove_component(&self, entity: Entity) { self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new( RemoveComponentCommand { entity, _marker: PhantomData::::default(), }, ))); } /// Queues the addition of a tag from an entity in the command buffer. /// This writer calls `World::add_tag` pub fn add_tag(&self, entity: Entity, tag: T) { self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(AddTagCommand { entity, tag, }))); } /// Queues the removal of a tag from an entity in the command buffer. /// This writer calls `World::remove_tag` pub fn remove_tag(&self, entity: Entity) { self.commands .get_mut() .push_front(EntityCommand::WriteWorld(Arc::new(RemoveTagCommand { entity, _marker: PhantomData::::default(), }))); } /// Returns the current number of commands already queued in this `CommandBuffer` instance. #[inline] pub fn len(&self) -> usize { self.commands.get().len() } /// Returns true if this `CommandBuffer` is currently empty and contains no writers. #[inline] pub fn is_empty(&self) -> bool { self.commands.get().len() == 0 } } #[cfg(test)] mod tests { use super::*; use crate::prelude::*; #[derive(Clone, Copy, Debug, PartialEq)] struct Pos(f32, f32, f32); #[derive(Clone, Copy, Debug, PartialEq)] struct Vel(f32, f32, f32); #[derive(Default)] struct TestResource(pub i32); #[test] fn create_entity_test() -> Result<(), CommandError> { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let components = vec![ (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)), ]; let components_len = components.len(); //world.entity_allocator.get_block() let mut command = CommandBuffer::from_world(&mut world); let entity1 = command.create_entity()?; let entity2 = command.create_entity()?; command.add_component(entity1, Pos(1., 2., 3.)); command.add_component(entity2, Pos(4., 5., 6.)); command.write(&mut world); let query = Read::::query(); let mut count = 0; for _ in query.iter_entities(&mut world) { count += 1; } assert_eq!(components_len, count); Ok(()) } #[test] fn simple_write_test() -> Result<(), CommandError> { let _ = tracing_subscriber::fmt::try_init(); let universe = Universe::new(); let mut world = universe.create_world(); let components = vec![ (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)), (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)), ]; let components_len = components.len(); //world.entity_allocator.get_block() let mut command = CommandBuffer::from_world(&mut world); let _ = command.insert((), components)?; // Assert writing checks // TODO: //assert_eq!( // vec![ComponentTypeId::of::(), ComponentTypeId::of::()], // command.write_components() //); command.write(&mut world); let query = Read::::query(); let mut count = 0; for _ in query.iter_entities_mut(&mut world) { count += 1; } assert_eq!(components_len, count); Ok(()) } }