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
18pub trait WorldWritable {
23 fn write(self: Arc<Self>, world: &mut World, cmd: &CommandBuffer);
25
26 fn write_components(&self) -> Vec<ComponentTypeId>;
29
30 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
196pub 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 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 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 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
279pub 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
322unsafe impl Send for CommandBuffer {}
325unsafe impl Sync for CommandBuffer {}
326
327impl CommandBuffer {
328 pub fn new_with_capacity(world: &World, capacity: usize) -> Self {
334 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 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 pub fn world(&self) -> WorldId { self.world_id }
371
372 #[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_list.len() - capacity..capacity).for_each(|_| {
390 allocator.delete_entity(free_list.pop().unwrap());
391 });
392 }
393 }
394
395 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 self.resize();
418 }
419
420 pub fn start_entity(&mut self) -> EntityBuilder<(), ()> {
422 EntityBuilder {
423 cmd: self,
424 tags: (),
425 components: (),
426 }
427 }
428
429 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 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 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 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 pub fn delete(&self, entity: Entity) { self.insert_writer(DeleteEntityCommand(entity)); }
498
499 pub fn add_component<C: Component>(&self, entity: Entity, component: C) {
502 self.insert_writer(AddComponentCommand { entity, component });
503 }
504
505 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 pub fn add_tag<T: Tag>(&self, entity: Entity, tag: T) {
517 self.insert_writer(AddTagCommand { entity, tag });
518 }
519
520 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 #[inline]
531 pub fn len(&self) -> usize { self.commands.get().len() }
532
533 #[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 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 let mut command = CommandBuffer::new(&world);
610 let _ = command.insert((), components);
611
612 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}