secs 0.1.0

Shit Entity Component System
Documentation
use elsa::FrozenVec;
use std::{
    any::{Any, TypeId, type_name},
    cell::{Ref, RefCell, RefMut},
    collections::HashMap,
};

use crate::world::Entity;

pub struct SparseSet<C> {
    pub sparse: HashMap<Entity, usize>,
    pub dense: Vec<C>,
    pub ids: Vec<Entity>,
}

impl<C> SparseSet<C> {
    pub fn new(entity: Entity, component: C) -> Self {
        let mut sparse = HashMap::new();
        sparse.insert(entity, 0);

        Self {
            sparse,
            dense: vec![component],
            ids: vec![entity],
        }
    }

    pub fn insert(&mut self, entity: Entity, component: C) {
        self.sparse.insert(entity, self.dense.len());
        self.dense.push(component);
        self.ids.push(entity);
    }

    pub fn remove(&mut self, entity: Entity) -> Option<C> {
        let idx = self.sparse.remove(&entity)?;
        let last = self.dense.len() - 1;

        if idx == last {
            self.ids.pop();
            self.dense.pop()
        } else {
            self.ids.swap_remove(idx);

            let _prev = self.sparse.insert(self.ids[idx], idx);
            debug_assert_eq!(_prev, Some(last));
            Some(self.dense.swap_remove(idx))
        }
    }

    pub fn get(&self, entity: Entity) -> Option<&C> {
        let &id = self.sparse.get(&entity)?;
        Some(&self.dense[id])
    }

    pub fn get_mut(&mut self, entity: Entity) -> Option<&mut C> {
        let &id = self.sparse.get(&entity)?;
        Some(&mut self.dense[id])
    }

    pub fn iter(&self) -> impl Iterator<Item = (Entity, &C)> {
        self.ids.iter().copied().zip(self.dense.iter())
    }

    pub fn iter_mut(&mut self) -> impl Iterator<Item = (Entity, &mut C)> {
        self.ids.iter().copied().zip(self.dense.iter_mut())
    }

    pub fn clear(&mut self) {
        self.sparse.clear();
        self.dense.clear();
        self.ids.clear();
    }
}

trait Set: Any {
    fn debug(&mut self, entity: Entity) -> Option<&'static str>;

    fn remove(&mut self, entity: Entity);
}

impl<C: Any> Set for SparseSet<C> {
    fn debug(&mut self, entity: Entity) -> Option<&'static str> {
        self.get(entity).map(|_| type_name::<C>())
    }

    fn remove(&mut self, entity: Entity) {
        self.remove(entity);
    }
}

#[derive(Default)]
pub struct SparseSets {
    set_access: RefCell<HashMap<TypeId, usize>>,
    sets: FrozenVec<Box<RefCell<dyn Set>>>,
}

impl SparseSets {
    pub fn insert<C: Any>(&self, entity: Entity, component: C) {
        let component = Box::new(RefCell::new(SparseSet::new(entity, component)));
        let n = self.sets.len();
        self.sets.push(component);
        assert_eq!(
            self.set_access.borrow_mut().insert(TypeId::of::<C>(), n),
            None
        );
    }

    #[track_caller]
    pub fn debug(&self, entity: Entity) -> String {
        #[cfg(any(debug_assertions, feature = "track_dead_entities"))]
        let mut component = String::new();

        for set in self.sets.iter() {
            let Ok(mut guard) = set.try_borrow_mut() else {
                panic!(
                    "Tried to access component mutably, but it is already being read or written to",
                )
            };

            #[cfg(any(debug_assertions, feature = "track_dead_entities"))]
            if let Some(c) = guard.debug(entity) {
                component.push_str(c);
                component.push_str(", ");
            }
            #[cfg(not(any(debug_assertions, feature = "track_dead_entities")))]
            guard.remove(entity);
        }
        #[cfg(any(debug_assertions, feature = "track_dead_entities"))]
        return component;
        #[cfg(not(any(debug_assertions, feature = "track_dead_entities")))]
        return String::new();
    }

    #[track_caller]
    pub fn remove(&self, entity: Entity) {
        for set in self.sets.iter() {
            let Ok(mut guard) = set.try_borrow_mut() else {
                panic!(
                    "Tried to access component mutably, but it is already being read or written to",
                )
            };

            guard.remove(entity);
        }
    }

    #[track_caller]
    pub fn get<C: 'static>(&self) -> Option<Ref<SparseSet<C>>> {
        let i = *self.set_access.borrow().get(&TypeId::of::<C>())?;
        let set = &self.sets.get(i).unwrap();
        let Ok(guard) = set.try_borrow() else {
            panic!(
                "Tried to access component `{}`, but it was already being written to",
                type_name::<C>()
            )
        };
        Some(Ref::map(guard, |dynbox| {
            (dynbox as &dyn Any).downcast_ref::<SparseSet<C>>().unwrap()
        }))
    }

    #[track_caller]
    pub fn get_mut<C: 'static>(&self) -> Option<RefMut<SparseSet<C>>> {
        let i = *self.set_access.borrow().get(&TypeId::of::<C>())?;
        let set = &self.sets.get(i).unwrap();
        let Ok(guard) = set.try_borrow_mut() else {
            panic!(
                "Tried to access component `{}` mutably, but it was already being written to or read from",
                type_name::<C>()
            )
        };
        Some(RefMut::map(guard, |dynbox| {
            (dynbox as &mut dyn Any)
                .downcast_mut::<SparseSet<C>>()
                .unwrap()
        }))
    }
}