shipyard 0.5.0

Entity Component System
Documentation
use crate::all_storages::{AllStorages, CustomStorageAccess};
use crate::entities::Entities;
use crate::entity_id::EntityId;
use crate::reserve::BulkEntityIter;
use crate::sparse_set::SparseSet;
use core::iter::IntoIterator;

pub trait BulkAddEntity {
    fn bulk_add_entity(self, all_storages: &mut AllStorages) -> BulkEntityIter<'_>;
}

impl<I: IntoIterator> BulkAddEntity for I
where
    I::Item: BulkInsert,
{
    fn bulk_add_entity(self, all_storages: &mut AllStorages) -> BulkEntityIter<'_> {
        <I::Item as BulkInsert>::bulk_insert(all_storages, self)
    }
}

pub trait BulkInsert {
    fn bulk_insert<I: IntoIterator<Item = Self>>(
        all_storages: &mut AllStorages,
        iter: I,
    ) -> BulkEntityIter<'_>
    where
        Self: Sized;
}

impl BulkInsert for () {
    fn bulk_insert<I: IntoIterator<Item = Self>>(
        all_storages: &mut AllStorages,
        iter: I,
    ) -> BulkEntityIter<'_>
    where
        Self: Sized,
    {
        let iter = iter.into_iter();
        let len = iter.size_hint().0;

        let entities = all_storages.exclusive_storage_mut::<Entities>().unwrap();
        let entities_len = entities.data.len();

        entities.bulk_generate(len);
        for _ in iter.skip(len) {
            entities.generate();
        }
        BulkEntityIter(entities.data[entities_len..].iter().copied())
    }
}

impl<T: 'static + Send + Sync> BulkInsert for (T,) {
    fn bulk_insert<I: IntoIterator<Item = Self>>(
        all_storages: &mut AllStorages,
        iter: I,
    ) -> BulkEntityIter<'_> {
        let iter = iter.into_iter();
        let len = iter.size_hint().0;

        let mut entities = all_storages.entities_mut().unwrap();
        let entities_len = entities.data.len();
        let new_entities = entities.bulk_generate(len);

        let mut sparse_set = all_storages
            .custom_storage_or_insert_mut(SparseSet::<T>::new)
            .unwrap();

        sparse_set.reserve(len);
        sparse_set.data.extend(iter.map(|(component,)| component));

        let old_len = sparse_set.dense.len();
        if sparse_set.metadata.track_insertion {
            sparse_set
                .dense
                .extend(new_entities.iter().copied().map(|mut id| {
                    id.set_inserted();
                    id
                }));
        } else {
            sparse_set.dense.extend_from_slice(new_entities);
        }

        let dense_len = sparse_set.dense.len();
        let data_len = sparse_set.data.len();

        if sparse_set.metadata.track_insertion {
            sparse_set.dense.extend((0..data_len - dense_len).map(|_| {
                let mut id = entities.generate();
                id.set_inserted();
                id
            }));
        } else {
            sparse_set
                .dense
                .extend((0..data_len - dense_len).map(|_| entities.generate()));
        }

        let SparseSet { sparse, dense, .. } = &mut *sparse_set;

        sparse.bulk_allocate(dense[old_len], dense[data_len - 1]);
        for (i, &entity) in dense[old_len..].iter().enumerate() {
            unsafe {
                *sparse.get_mut_unchecked(entity) =
                    EntityId::new_from_parts((old_len + i) as u64, 0, 0);
            }
        }

        drop((entities, sparse_set));

        BulkEntityIter(
            all_storages
                .exclusive_storage_mut::<Entities>()
                .unwrap()
                .data[entities_len..]
                .iter()
                .copied(),
        )
    }
}

macro_rules! impl_bulk_insert {
    (($type1: ident, $sparse_set1: ident, $index1: tt) $(($type: ident, $sparse_set: ident, $index: tt))*) => {
        impl<$type1: 'static + Send + Sync, $($type: 'static + Send + Sync,)*> BulkInsert for ($type1, $($type,)*) {
            #[allow(non_snake_case)]
            fn bulk_insert<Source: IntoIterator<Item = Self>>(all_storages: &mut AllStorages, iter: Source) -> BulkEntityIter<'_> {
                let iter = iter.into_iter();
                let size_hint = iter.size_hint().0;

                let mut entities = all_storages.entities_mut().unwrap();
                let entities_len = entities.data.len();
                let new_entities = entities.bulk_generate(size_hint);

                let mut $sparse_set1 = all_storages.custom_storage_or_insert_mut(SparseSet::<$type1>::new).unwrap();
                $(
                    let mut $sparse_set = all_storages.custom_storage_or_insert_mut(SparseSet::<$type>::new).unwrap();
                )*

                $sparse_set1.reserve(size_hint);
                $(
                    $sparse_set.reserve(size_hint);
                )*

                for ($type1, $($type,)*) in iter {
                    $sparse_set1.data.push($type1);
                    $(
                        $sparse_set.data.push($type);
                    )*
                }

                let len = $sparse_set1.data.len() - $sparse_set1.dense.len();

                let old_len1 = $sparse_set1.dense.len();
                $sparse_set1.dense.extend_from_slice(new_entities);

                let dense_len = $sparse_set1.dense.len();
                let data_len = $sparse_set1.data.len();
                $sparse_set1.dense.extend((0..data_len - dense_len).map(|_| entities.generate()));

                $(
                    $sparse_set.dense.extend_from_slice(&$sparse_set1.dense[old_len1..]);
                )*

                let start_entity = $sparse_set1.dense[old_len1];
                let end_entity = *$sparse_set1.dense.last().unwrap();

                $sparse_set1.sparse.bulk_allocate(start_entity, end_entity);
                $(
                    $sparse_set.sparse.bulk_allocate(start_entity, end_entity);
                )*

                let SparseSet {
                    sparse: sparse1,
                    dense: dense1,
                    metadata: metadata1,
                    ..
                } = &mut *$sparse_set1;

                if metadata1.track_insertion {
                    for (i, &entity) in dense1[old_len1..].iter().enumerate() {
                        unsafe {
                            let mut e = EntityId::new((old_len1 + i) as u64);
                            e.set_inserted();
                            *sparse1.get_mut_unchecked(entity) = e;
                        }
                    }
                } else {
                    for (i, &entity) in dense1[old_len1..].iter().enumerate() {
                        unsafe {
                            *sparse1.get_mut_unchecked(entity) = EntityId::new((old_len1 + i) as u64);
                        }
                    }
                }

                $(
                    let old_len = $sparse_set.dense.len() - len;

                    if $sparse_set.metadata.track_insertion {
                        for (i, &entity) in dense1[old_len1..].iter().enumerate() {
                            unsafe {
                                let mut e = EntityId::new((old_len + i) as u64);
                                e.set_inserted();
                                *$sparse_set.sparse.get_mut_unchecked(entity) = e;
                            }
                        }
                    } else {
                        for (i, &entity) in dense1[old_len1..].iter().enumerate() {
                            unsafe {
                                *$sparse_set.sparse.get_mut_unchecked(entity) = EntityId::new((old_len + i) as u64);
                            }
                        }
                    }
                )*

                drop((entities, $sparse_set1, $($sparse_set),*));

                BulkEntityIter(all_storages.exclusive_storage_mut::<Entities>().unwrap().data[entities_len..].iter().copied())
            }
        }
    };
}

macro_rules! bulk_insert {
    ($(($type: ident, $sparse_set: ident, $index: tt))*;($type1: ident, $sparse_set1: ident, $index1: tt) $(($queue_type: ident, $queue_sparse_set: ident, $queue_index: tt))*) => {
        impl_bulk_insert![$(($type, $sparse_set, $index))*];
        bulk_insert![$(($type, $sparse_set, $index))* ($type1, $sparse_set1, $index1); $(($queue_type, $queue_sparse_set, $queue_index))*];
    };
    ($(($type: ident, $sparse_set: ident, $index: tt))*;) => {
        impl_bulk_insert![$(($type, $sparse_set, $index))*];
    }
}

bulk_insert![(A, sparse_set0, 0) (B, sparse_set1, 1); (C, sparse_set2, 2) (D, sparse_set3, 3) (E, sparse_set4, 4) (F, sparse_set5, 5) (G, sparse_set6, 6) (H, sparse_set7, 7) (I, sparse_set8, 8) (J, sparse_set9, 9)];