use std::{
any::type_name,
collections::VecDeque,
fmt,
iter::{Fuse, FusedIterator},
marker::PhantomData,
ops::Range,
sync::Arc,
};
use smallvec::SmallVec;
use crate::{
internals::{
entity::Entity,
insert::{
ArchetypeSource, ArchetypeWriter, ComponentSource, IntoComponentSource, KnownLength,
},
storage::{archetype::EntityLayout, component::Component},
systems::resources::Resources,
world::{World, WorldId},
},
world::Allocate,
};
pub trait WorldWritable: Send + Sync {
fn write(self: Arc<Self>, world: &mut World, cmd: &CommandBuffer);
}
struct InsertBufferedCommand<T> {
components: T,
entities: Range<usize>,
}
impl<T> fmt::Debug for InsertBufferedCommand<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"InsertBufferedCommand<{}>({:?})",
type_name::<T>(),
self.entities
))
}
}
impl<T> WorldWritable for InsertBufferedCommand<T>
where
T: ComponentSource + Send + Sync,
{
fn write(self: Arc<Self>, world: &mut World, cmd: &CommandBuffer) {
let consumed = Arc::try_unwrap(self).unwrap();
world.extend(PreallocComponentSource::new(
cmd.pending_insertion[consumed.entities].iter().copied(),
consumed.components,
));
}
}
struct PreallocComponentSource<I: Iterator<Item = Entity> + FusedIterator, C: ComponentSource> {
entities: I,
components: C,
}
impl<I: Iterator<Item = Entity> + FusedIterator, C: ComponentSource> IntoComponentSource
for PreallocComponentSource<I, C>
{
type Source = Self;
fn into(self) -> Self::Source {
self
}
}
impl<I: Iterator<Item = Entity>, C: ComponentSource> PreallocComponentSource<Fuse<I>, C> {
pub fn new(entities: I, components: C) -> Self {
Self {
entities: entities.fuse(),
components,
}
}
}
impl<I: Iterator<Item = Entity> + FusedIterator, C: ComponentSource> ArchetypeSource
for PreallocComponentSource<I, C>
{
type Filter = C::Filter;
fn filter(&self) -> Self::Filter {
self.components.filter()
}
fn layout(&mut self) -> EntityLayout {
self.components.layout()
}
}
impl<I: Iterator<Item = Entity> + FusedIterator, C: ComponentSource> ComponentSource
for PreallocComponentSource<I, C>
{
fn push_components<'a>(
&mut self,
writer: &mut ArchetypeWriter<'a>,
mut entities: impl Iterator<Item = Entity>,
) {
let iter = ConcatIter {
a: &mut self.entities,
b: &mut entities,
};
self.components.push_components(writer, iter)
}
}
struct ConcatIter<'a, T, A: Iterator<Item = T> + FusedIterator, B: Iterator<Item = T>> {
a: &'a mut A,
b: &'a mut B,
}
impl<'a, T, A: Iterator<Item = T> + FusedIterator, B: Iterator<Item = T>> Iterator
for ConcatIter<'a, T, A, B>
{
type Item = T;
fn next(&mut self) -> Option<T> {
self.a.next().or_else(|| self.b.next())
}
}
struct InsertCommand<T> {
components: T,
}
impl<T> fmt::Debug for InsertCommand<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("InsertCommand<{}>", type_name::<T>()))
}
}
impl<T> WorldWritable for InsertCommand<T>
where
T: IntoComponentSource + Send + Sync,
{
fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
let consumed = Arc::try_unwrap(self).unwrap();
world.extend(consumed.components);
}
}
struct DeleteEntityCommand(Entity);
impl fmt::Debug for DeleteEntityCommand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!("DeleteEntityCommand({:?})", self.0))
}
}
impl WorldWritable for DeleteEntityCommand {
fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
world.remove(self.0);
}
}
struct AddComponentCommand<C> {
entity: Entity,
component: C,
}
impl<T> fmt::Debug for AddComponentCommand<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"AddComponentCommand<{}>({:?})",
type_name::<T>(),
self.entity
))
}
}
impl<C> WorldWritable for AddComponentCommand<C>
where
C: Component,
{
fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
let consumed = Arc::try_unwrap(self).unwrap();
world
.entry(consumed.entity)
.expect("entity not found")
.add_component(consumed.component);
}
}
struct RemoveComponentCommand<C> {
entity: Entity,
_marker: PhantomData<C>,
}
impl<T> fmt::Debug for RemoveComponentCommand<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"RemoveComponentCommand<{}>({:?})",
type_name::<T>(),
self.entity
))
}
}
impl<C> WorldWritable for RemoveComponentCommand<C>
where
C: Component,
{
fn write(self: Arc<Self>, world: &mut World, _: &CommandBuffer) {
world
.entry(self.entity)
.expect("entity not found")
.remove_component::<C>();
}
}
#[allow(clippy::enum_variant_names)]
enum Command {
WriteWorld(Arc<dyn WorldWritable>),
ExecMutWorld(Arc<dyn Fn(&mut World, &mut Resources) + Send + Sync>),
}
pub struct CommandBuffer {
world_id: WorldId,
commands: VecDeque<Command>,
entity_allocator: Allocate,
pending_insertion: SmallVec<[Entity; 64]>,
}
impl CommandBuffer {
pub fn new(world: &World) -> Self {
Self {
world_id: world.id(),
commands: Default::default(),
pending_insertion: SmallVec::new(),
entity_allocator: Allocate::new(),
}
}
pub fn world(&self) -> WorldId {
self.world_id
}
pub fn flush(&mut self, world: &mut World, resources: &mut Resources) {
if self.world_id != world.id() {
panic!("command buffers may only write into their parent world");
}
while let Some(command) = self.commands.pop_back() {
match command {
Command::WriteWorld(ptr) => ptr.write(world, self),
Command::ExecMutWorld(closure) => closure(world, resources),
}
}
self.pending_insertion.clear();
}
pub fn exec_mut<F>(&mut self, f: F)
where
F: 'static + Fn(&mut World, &mut Resources) + Send + Sync,
{
self.commands.push_front(Command::ExecMutWorld(Arc::new(f)));
}
fn insert_writer<W>(&mut self, writer: W)
where
W: 'static + WorldWritable,
{
self.commands
.push_front(Command::WriteWorld(Arc::new(writer)));
}
pub fn push<T>(&mut self, components: T) -> Entity
where
Option<T>: 'static + IntoComponentSource,
<Option<T> as IntoComponentSource>::Source: KnownLength + Send + Sync,
{
self.extend(Some(components))[0]
}
pub fn extend<T>(&mut self, components: T) -> &[Entity]
where
T: 'static + IntoComponentSource,
<T as IntoComponentSource>::Source: KnownLength + Send + Sync,
{
let components = components.into();
let start = self.pending_insertion.len();
let count = components.len();
self.pending_insertion.reserve(count);
for _ in 0..count {
self.pending_insertion
.push(self.entity_allocator.next().unwrap());
}
let range = start..self.pending_insertion.len();
self.commands
.push_front(Command::WriteWorld(Arc::new(InsertBufferedCommand {
components,
entities: range.clone(),
})));
&self.pending_insertion[range]
}
pub fn remove(&mut self, entity: Entity) {
self.insert_writer(DeleteEntityCommand(entity));
}
pub fn add_component<C: Component>(&mut self, entity: Entity, component: C) {
self.insert_writer(AddComponentCommand { entity, component });
}
pub fn remove_component<C: Component>(&mut self, entity: Entity) {
self.insert_writer(RemoveComponentCommand {
entity,
_marker: PhantomData::<C>::default(),
});
}
#[inline]
pub fn len(&self) -> usize {
self.commands.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::internals::query::{view::read::Read, IntoQuery};
#[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 simple_write_test() {
let mut world = World::default();
let mut resources = Resources::default();
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();
let mut command = CommandBuffer::new(&world);
let _ = command.extend(components);
command.flush(&mut world, &mut resources);
let mut query = Read::<Pos>::query();
let mut count = 0;
for _ in query.iter(&world) {
count += 1;
}
assert_eq!(components_len, count);
}
}