legion_core/
command.rs

1use crate::{
2    borrow::AtomicRefCell,
3    cons::{ConsAppend, ConsFlatten},
4    entity::{Entity, EntityAllocator},
5    filter::{ChunksetFilterData, Filter},
6    storage::{Component, ComponentTypeId, Tag, TagTypeId},
7    world::{
8        ComponentSource, ComponentTupleSet, IntoComponentSource, PreallocComponentSource,
9        TagLayout, TagSet, World, WorldId,
10    },
11};
12use derivative::Derivative;
13use smallvec::SmallVec;
14use std::ops::Range;
15use std::{collections::VecDeque, iter::FromIterator, marker::PhantomData, sync::Arc};
16use tracing::{span, Level};
17
18/// This trait can be used to implement custom world writer types that can be directly
19/// inserted into the command buffer, for more custom and complex world operations. This is analogous
20/// to the `CommandBuffer::exec_mut` function type, but does not perform explicit any/any archetype
21/// access.
22pub trait WorldWritable {
23    /// Destructs the writer and performs the write operations on the world.
24    fn write(self: Arc<Self>, world: &mut World, cmd: &CommandBuffer);
25
26    /// Returns the list of `ComponentTypeId` which are written by this command buffer. This is leveraged
27    /// to allow parralel command buffer flushing.
28    fn write_components(&self) -> Vec<ComponentTypeId>;
29
30    /// Returns the list of `TagTypeId` which are written by this command buffer. This is leveraged
31    /// to allow parralel command buffer flushing.
32    fn write_tags(&self) -> Vec<TagTypeId>;
33}
34
35#[derive(Derivative)]
36#[derivative(Debug(bound = ""))]
37struct InsertBufferedCommand<T, C> {
38    write_components: Vec<ComponentTypeId>,
39    write_tags: Vec<TagTypeId>,
40
41    #[derivative(Debug = "ignore")]
42    tags: T,
43    #[derivative(Debug = "ignore")]
44    components: C,
45
46    entities: Range<usize>,
47}
48impl<T, C> WorldWritable for InsertBufferedCommand<T, C>
49where
50    T: TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
51    C: ComponentSource,
52{
53    fn write(self: Arc<Self>, world: &mut World, cmd: &CommandBuffer) {
54        let consumed = Arc::try_unwrap(self).unwrap();
55
56        world.insert(
57            consumed.tags,
58            PreallocComponentSource::new(
59                cmd.pending_insertion[consumed.entities].iter().copied(),
60                consumed.components,
61            ),
62        );
63    }
64
65    fn write_components(&self) -> Vec<ComponentTypeId> { self.write_components.clone() }
66    fn write_tags(&self) -> Vec<TagTypeId> { self.write_tags.clone() }
67}
68
69#[derive(Derivative)]
70#[derivative(Debug(bound = ""))]
71struct InsertCommand<T, C> {
72    write_components: Vec<ComponentTypeId>,
73    write_tags: Vec<TagTypeId>,
74
75    #[derivative(Debug = "ignore")]
76    tags: T,
77    #[derivative(Debug = "ignore")]
78    components: C,
79}
80impl<T, C> WorldWritable for InsertCommand<T, C>
81where
82    T: TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
83    C: IntoComponentSource,
84{
85    fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
86        let consumed = Arc::try_unwrap(self).unwrap();
87        world.insert(consumed.tags, consumed.components);
88    }
89
90    fn write_components(&self) -> Vec<ComponentTypeId> { self.write_components.clone() }
91    fn write_tags(&self) -> Vec<TagTypeId> { self.write_tags.clone() }
92}
93
94#[derive(Derivative)]
95#[derivative(Debug(bound = ""))]
96struct DeleteEntityCommand(Entity);
97impl WorldWritable for DeleteEntityCommand {
98    fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) { world.delete(self.0); }
99
100    fn write_components(&self) -> Vec<ComponentTypeId> { Vec::with_capacity(0) }
101    fn write_tags(&self) -> Vec<TagTypeId> { Vec::with_capacity(0) }
102}
103
104#[derive(Derivative)]
105#[derivative(Debug(bound = ""))]
106struct AddTagCommand<T> {
107    entity: Entity,
108    #[derivative(Debug = "ignore")]
109    tag: T,
110}
111impl<T> WorldWritable for AddTagCommand<T>
112where
113    T: Tag,
114{
115    fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
116        let consumed = Arc::try_unwrap(self).unwrap();
117        if let Err(err) = world.add_tag(consumed.entity, consumed.tag) {
118            tracing::error!(error = %err, "error adding tag");
119        }
120    }
121
122    fn write_components(&self) -> Vec<ComponentTypeId> { Vec::with_capacity(0) }
123    fn write_tags(&self) -> Vec<TagTypeId> { vec![TagTypeId::of::<T>()] }
124}
125
126#[derive(Derivative)]
127#[derivative(Debug(bound = ""))]
128struct RemoveTagCommand<T> {
129    entity: Entity,
130    _marker: PhantomData<T>,
131}
132impl<T> WorldWritable for RemoveTagCommand<T>
133where
134    T: Tag,
135{
136    fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
137        if let Err(err) = world.remove_tag::<T>(self.entity) {
138            tracing::error!(error = %err, "error removing tag");
139        }
140    }
141
142    fn write_components(&self) -> Vec<ComponentTypeId> { Vec::with_capacity(0) }
143    fn write_tags(&self) -> Vec<TagTypeId> { vec![TagTypeId::of::<T>()] }
144}
145
146#[derive(Derivative)]
147#[derivative(Debug(bound = ""))]
148struct AddComponentCommand<C> {
149    #[derivative(Debug = "ignore")]
150    entity: Entity,
151    #[derivative(Debug = "ignore")]
152    component: C,
153}
154impl<C> WorldWritable for AddComponentCommand<C>
155where
156    C: Component,
157{
158    fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
159        let consumed = Arc::try_unwrap(self).unwrap();
160        if let Err(err) = world.add_component::<C>(consumed.entity, consumed.component) {
161            tracing::error!(error = %err, "error adding component");
162        }
163    }
164
165    fn write_components(&self) -> Vec<ComponentTypeId> { vec![ComponentTypeId::of::<C>()] }
166    fn write_tags(&self) -> Vec<TagTypeId> { Vec::with_capacity(0) }
167}
168
169#[derive(Derivative)]
170#[derivative(Debug(bound = ""))]
171struct RemoveComponentCommand<C> {
172    entity: Entity,
173    _marker: PhantomData<C>,
174}
175impl<C> WorldWritable for RemoveComponentCommand<C>
176where
177    C: Component,
178{
179    fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
180        if let Err(err) = world.remove_component::<C>(self.entity) {
181            tracing::error!(error = %err, "error removing component");
182        }
183    }
184
185    fn write_components(&self) -> Vec<ComponentTypeId> { vec![ComponentTypeId::of::<C>()] }
186    fn write_tags(&self) -> Vec<TagTypeId> { Vec::with_capacity(0) }
187}
188
189#[allow(clippy::enum_variant_names)]
190enum EntityCommand {
191    WriteWorld(Arc<dyn WorldWritable>),
192    ExecWorld(Arc<dyn Fn(&World)>),
193    ExecMutWorld(Arc<dyn Fn(&mut World)>),
194}
195
196/// A builder type which can be retrieved from the command buffer. This is the ideal use case for
197/// inserted complex entities with multiple components and tags from a command buffer. Although
198/// `add_component` will perform a new move operation on every addition, this allows the construction
199/// of a single `insert` command for an entity, but without using the actual `insert` command
200/// provided by the `CommandBuffer`
201///
202/// # Examples
203///
204/// Inserting an entity using the `EntityBuilder`:
205///
206/// ```
207/// # use legion_core::prelude::*;
208/// # #[derive(Copy, Clone, Debug, PartialEq)]
209/// # struct Position(f32);
210/// # #[derive(Copy, Clone, Debug, PartialEq)]
211/// # struct Rotation(f32);
212/// # let universe = Universe::new();
213/// # let mut world = universe.create_world();
214/// let mut command_buffer = CommandBuffer::new(&world);
215/// command_buffer.start_entity()
216///     .with_component(Position(123.0))
217///     .with_component(Rotation(456.0))
218///     .build();
219/// command_buffer.write(&mut world);
220/// ```
221pub struct EntityBuilder<'a, TS = (), CS = ()> {
222    cmd: &'a mut CommandBuffer,
223    tags: TS,
224    components: CS,
225}
226
227impl<'a, TS, CS> EntityBuilder<'a, TS, CS>
228where
229    TS: 'static + Send + ConsFlatten,
230    CS: 'static + Send + ConsFlatten,
231{
232    /// Adds a component to this builder, returning a new builder type containing that component type
233    /// and its data.
234    pub fn with_component<C: Component>(
235        self,
236        component: C,
237    ) -> EntityBuilder<'a, TS, <CS as ConsAppend<C>>::Output>
238    where
239        CS: ConsAppend<C>,
240        <CS as ConsAppend<C>>::Output: ConsFlatten,
241    {
242        EntityBuilder {
243            cmd: self.cmd,
244            components: ConsAppend::append(self.components, component),
245            tags: self.tags,
246        }
247    }
248
249    /// Adds a tag to this builder, returning a new builder type containing that component type
250    /// and its data.
251    pub fn with_tag<T: Tag>(self, tag: T) -> EntityBuilder<'a, <TS as ConsAppend<T>>::Output, CS>
252    where
253        TS: ConsAppend<T>,
254        <TS as ConsAppend<T>>::Output: ConsFlatten,
255    {
256        EntityBuilder {
257            cmd: self.cmd,
258            tags: ConsAppend::append(self.tags, tag),
259            components: self.components,
260        }
261    }
262
263    /// Finalizes this builder type and submits it to the `CommandBuffer`.
264    pub fn build(self) -> Entity
265    where
266        <TS as ConsFlatten>::Output: TagSet + TagLayout + for<'b> Filter<ChunksetFilterData<'b>>,
267        ComponentTupleSet<
268            <CS as ConsFlatten>::Output,
269            std::iter::Once<<CS as ConsFlatten>::Output>,
270        >: ComponentSource,
271    {
272        self.cmd.insert(
273            self.tags.flatten(),
274            std::iter::once(self.components.flatten()),
275        )[0]
276    }
277}
278
279/// A command buffer used to queue mutable changes to the world from a system. This buffer is automatically
280/// flushed and refreshed at the beginning of every frame by `Schedule`. If `Schedule` is not used,
281/// then the user needs to manually flush it by performing `CommandBuffer::write`.
282///
283/// This buffer operates as follows:
284///     - All commands are queued as trait object of type `WorldWritable`, to be executed when `CommandBuffer:write` is called.
285///     - Entities are allocated at the time of `CommandBuffer:write` occuring, being directly allocated from the world
286///       and cached internally in the system. This upper cache size can be changed via `SystemBuilder::with_command_buffer_size`
287///       for specific systems, or globally via `World::set_command_buffer_size`. In the event the cached entity count is exceeded,
288///       the cache will be refilled on demand from the world `EntityAllocator`.
289///  
290/// This behavior exists because `EntityAllocator` is a shared lock within the world, so in order to reduce lock contention with many
291/// systems running and adding entities, the `CommandBuffer` will cache the configured number of entities - reducing contention.
292///
293/// # Examples
294///
295/// Inserting an entity using the `CommandBuffer`:
296///
297/// ```
298/// # use legion_core::prelude::*;
299/// # #[derive(Copy, Clone, Debug, PartialEq)]
300/// # struct Position(f32);
301/// # #[derive(Copy, Clone, Debug, PartialEq)]
302/// # struct Rotation(f32);
303/// # let universe = Universe::new();
304/// # let mut world = universe.create_world();
305/// let mut command_buffer = CommandBuffer::new(&world);
306/// let entity = command_buffer.start_entity().build();
307///
308/// command_buffer.add_component(entity, Position(123.0));
309/// command_buffer.delete(entity);
310///
311/// command_buffer.write(&mut world);
312/// ```
313pub struct CommandBuffer {
314    world_id: WorldId,
315    commands: AtomicRefCell<VecDeque<EntityCommand>>,
316    entity_allocator: Arc<EntityAllocator>,
317    preallocated_capacity: usize,
318    free_list: SmallVec<[Entity; 64]>,
319    pending_insertion: SmallVec<[Entity; 64]>,
320}
321
322// This is safe because only 1 system in 1 execution is only ever accessing a command buffer
323// and we gaurantee the write operations of a command buffer occur in a safe manner
324unsafe impl Send for CommandBuffer {}
325unsafe impl Sync for CommandBuffer {}
326
327impl CommandBuffer {
328    /// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame.
329    /// Allocating a command buffer in this manner will override `World::set_command_buffer_size` and
330    /// this system will always allocate the custom provide capacity of entities every frame.
331    ///
332    /// This constructor will preallocate the first round of entities needed from the world.
333    pub fn new_with_capacity(world: &World, capacity: usize) -> Self {
334        // Pull  free entities from the world.
335
336        let free_list =
337            SmallVec::from_iter((0..capacity).map(|_| world.entity_allocator.create_entity()));
338
339        Self {
340            world_id: world.id(),
341            free_list,
342            preallocated_capacity: capacity,
343            commands: Default::default(),
344            pending_insertion: SmallVec::new(),
345            entity_allocator: world.entity_allocator.clone(),
346        }
347    }
348
349    /// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame.
350    /// Allocating a command buffer in this manner will use the default `World::set_command_buffer_size`
351    /// value.
352    ///
353    /// This constructor will preallocate the first round of entities needed from the world.
354    pub fn new(world: &World) -> Self {
355        let free_list = SmallVec::from_iter(
356            (0..world.command_buffer_size()).map(|_| world.entity_allocator.create_entity()),
357        );
358
359        Self {
360            world_id: world.id(),
361            free_list,
362            preallocated_capacity: world.command_buffer_size(),
363            commands: Default::default(),
364            pending_insertion: SmallVec::new(),
365            entity_allocator: world.entity_allocator.clone(),
366        }
367    }
368
369    /// Gets the ID of the world this command buffer belongs to.
370    pub fn world(&self) -> WorldId { self.world_id }
371
372    /// Changes the cached capacity of this `CommandBuffer` to the specified capacity. This includes shrinking
373    /// and growing the allocated entities, and possibly returning them to the entity allocator in the
374    /// case of a shrink.
375    ///
376    /// This function does *NOT* set the `CommandBuffer::custom_capacity` override.
377    #[allow(clippy::comparison_chain)]
378    fn resize(&mut self) {
379        let allocator = &self.entity_allocator;
380        let free_list = &mut self.free_list;
381        let capacity = self.preallocated_capacity;
382
383        if free_list.len() < capacity {
384            for entity in allocator.create_entities().take(capacity - free_list.len()) {
385                free_list.push(entity);
386            }
387        } else if free_list.len() > capacity {
388            // Free the entities
389            (free_list.len() - capacity..capacity).for_each(|_| {
390                allocator.delete_entity(free_list.pop().unwrap());
391            });
392        }
393    }
394
395    /// Flushes this command buffer, draining all stored commands and writing them to the world.
396    ///
397    /// Command flushes are performed in a FIFO manner, allowing for reliable, linear commands being
398    /// executed in the order they were provided.
399    pub fn write(&mut self, world: &mut World) {
400        let span = span!(Level::TRACE, "Draining command buffer");
401        let _guard = span.enter();
402
403        if self.world_id != world.id() {
404            panic!("command buffers may only write into their parent world");
405        }
406
407        while let Some(command) = self.commands.get_mut().pop_back() {
408            match command {
409                EntityCommand::WriteWorld(ptr) => ptr.write(world, self),
410                EntityCommand::ExecMutWorld(closure) => closure(world),
411                EntityCommand::ExecWorld(closure) => closure(world),
412            }
413        }
414        self.pending_insertion.clear();
415
416        // Refill our entity buffer from the world
417        self.resize();
418    }
419
420    /// Creates an entity builder for constructing a new entity.
421    pub fn start_entity(&mut self) -> EntityBuilder<(), ()> {
422        EntityBuilder {
423            cmd: self,
424            tags: (),
425            components: (),
426        }
427    }
428
429    /// Allocates a new entity.
430    fn allocate_entity(&mut self) -> Entity {
431        if self.free_list.is_empty() {
432            self.resize();
433        }
434        let entity = self
435            .free_list
436            .pop()
437            .unwrap_or_else(|| self.entity_allocator.create_entity());
438        self.pending_insertion.push(entity);
439        entity
440    }
441
442    /// Executes an arbitrary closure against the mutable world, allowing for queued exclusive
443    /// access to the world.
444    pub fn exec_mut<F>(&self, f: F)
445    where
446        F: 'static + Fn(&mut World),
447    {
448        self.commands
449            .get_mut()
450            .push_front(EntityCommand::ExecMutWorld(Arc::new(f)));
451    }
452
453    /// Inserts an arbitrary implementor of the `WorldWritable` trait into the command queue.
454    /// This can be leveraged for creating custom `WorldWritable` trait implementors, and is used
455    /// internally for the default writers.
456    fn insert_writer<W>(&self, writer: W)
457    where
458        W: 'static + WorldWritable,
459    {
460        self.commands
461            .get_mut()
462            .push_front(EntityCommand::WriteWorld(Arc::new(writer)));
463    }
464
465    /// Queues an insertion into the world. This command follows the same syntax as
466    /// the normal `World::insert`, returning the entities created for this command.
467    pub fn insert<T, C>(&mut self, tags: T, components: C) -> &[Entity]
468    where
469        T: 'static + TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
470        C: 'static + IntoComponentSource,
471    {
472        let components = components.into();
473        let start = self.pending_insertion.len();
474        let count = components.len();
475
476        self.pending_insertion.reserve(count);
477        for _ in 0..count {
478            self.allocate_entity();
479        }
480
481        let range = start..self.pending_insertion.len();
482
483        self.commands
484            .get_mut()
485            .push_front(EntityCommand::WriteWorld(Arc::new(InsertBufferedCommand {
486                write_components: Vec::default(),
487                write_tags: Vec::default(),
488                tags,
489                components,
490                entities: range.clone(),
491            })));
492
493        &self.pending_insertion[range]
494    }
495
496    /// Queues the deletion of an entity in the command buffer. This writer calls `World::delete`
497    pub fn delete(&self, entity: Entity) { self.insert_writer(DeleteEntityCommand(entity)); }
498
499    /// Queues the addition of a component from an entity in the command buffer.
500    /// This writer calls `World::add_component`
501    pub fn add_component<C: Component>(&self, entity: Entity, component: C) {
502        self.insert_writer(AddComponentCommand { entity, component });
503    }
504
505    /// Queues the removal of a component from an entity in the command buffer.
506    /// This writer calls `World::remove_component`
507    pub fn remove_component<C: Component>(&self, entity: Entity) {
508        self.insert_writer(RemoveComponentCommand {
509            entity,
510            _marker: PhantomData::<C>::default(),
511        });
512    }
513
514    /// Queues the addition of a tag from an entity in the command buffer.
515    /// This writer calls `World::add_tag`
516    pub fn add_tag<T: Tag>(&self, entity: Entity, tag: T) {
517        self.insert_writer(AddTagCommand { entity, tag });
518    }
519
520    /// Queues the removal of a tag from an entity in the command buffer.
521    /// This writer calls `World::remove_tag`
522    pub fn remove_tag<T: Tag>(&self, entity: Entity) {
523        self.insert_writer(RemoveTagCommand {
524            entity,
525            _marker: PhantomData::<T>::default(),
526        });
527    }
528
529    /// Returns the current number of commands already queued in this `CommandBuffer` instance.
530    #[inline]
531    pub fn len(&self) -> usize { self.commands.get().len() }
532
533    /// Returns true if this `CommandBuffer` is currently empty and contains no writers.
534    #[inline]
535    pub fn is_empty(&self) -> bool { self.commands.get().len() == 0 }
536}
537
538impl Drop for CommandBuffer {
539    fn drop(&mut self) {
540        while let Some(entity) = self.free_list.pop() {
541            self.entity_allocator.delete_entity(entity);
542        }
543
544        while let Some(entity) = self.pending_insertion.pop() {
545            self.entity_allocator.delete_entity(entity);
546        }
547    }
548}
549
550#[cfg(test)]
551mod tests {
552    use super::*;
553    use crate::prelude::*;
554
555    #[derive(Clone, Copy, Debug, PartialEq)]
556    struct Pos(f32, f32, f32);
557    #[derive(Clone, Copy, Debug, PartialEq)]
558    struct Vel(f32, f32, f32);
559    #[derive(Default)]
560    struct TestResource(pub i32);
561
562    #[test]
563    fn create_entity_test() {
564        let _ = tracing_subscriber::fmt::try_init();
565
566        let universe = Universe::new();
567        let mut world = universe.create_world();
568
569        let components = vec![
570            (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
571            (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
572        ];
573        let components_len = components.len();
574
575        //world.entity_allocator.get_block()
576        let mut command = CommandBuffer::new(&world);
577        let entity1 = command.start_entity().build();
578        let entity2 = command.start_entity().build();
579
580        command.add_component(entity1, Pos(1., 2., 3.));
581        command.add_component(entity2, Pos(4., 5., 6.));
582
583        command.write(&mut world);
584
585        let query = Read::<Pos>::query();
586
587        let mut count = 0;
588        for _ in query.iter_entities(&world) {
589            count += 1;
590        }
591
592        assert_eq!(components_len, count);
593    }
594
595    #[test]
596    fn simple_write_test() {
597        let _ = tracing_subscriber::fmt::try_init();
598
599        let universe = Universe::new();
600        let mut world = universe.create_world();
601
602        let components = vec![
603            (Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
604            (Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
605        ];
606        let components_len = components.len();
607
608        //world.entity_allocator.get_block()
609        let mut command = CommandBuffer::new(&world);
610        let _ = command.insert((), components);
611
612        // Assert writing checks
613        // TODO:
614        //assert_eq!(
615        //    vec![ComponentTypeId::of::<Pos>(), ComponentTypeId::of::<Vel>()],
616        //    command.write_components()
617        //);
618
619        command.write(&mut world);
620
621        let query = Read::<Pos>::query();
622
623        let mut count = 0;
624        for _ in query.iter_entities_mut(&mut world) {
625            count += 1;
626        }
627
628        assert_eq!(components_len, count);
629    }
630}