diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..69369904 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..b11e8b6f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "simplecs" +version = "0.1.0" +authors = ["Benjamin Saunders "] +edition = "2018" +license = "MIT OR Apache-2.0" + +[dependencies] +hibitset = "0.6.2" +fxhash = "0.2.1" +downcast-rs = "1.1.1" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..10310b07 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,130 @@ +mod storage; + +pub use storage::*; + +use std::any::TypeId; +use std::sync::Mutex; + +use fxhash::FxHashMap; +use hibitset::{BitSet, BitSetLike, BitSetNot}; + +#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub struct Entity { + generation: u32, + index: u32, +} + +pub struct World { + entities: BitSet, + generations: Vec, + storages: FxHashMap>>, +} + +impl World { + pub fn new() -> Self { + Self { + entities: BitSet::new(), + generations: Vec::new(), + storages: FxHashMap::default(), + } + } + + /// Add a new type of storage + pub fn register(&mut self) { + dbg!(std::any::type_name::()); + self.storages.insert( + TypeId::of::(), + Mutex::new(Box::new(Masked::new(S::default()))), + ); + } + + /// Discard a type of storage, destroying its contents + pub fn unregister(&mut self) { + self.storages.remove(&TypeId::of::()); + } + + /// Access a storage + pub fn get(&self) -> Option> { + let guard = self.storages.get(&TypeId::of::())?.lock().unwrap(); + Some(StorageRefMut::new(guard)) + } + + /// Create a new entity + pub fn spawn(&mut self) -> Entity { + let index = BitSetNot(&self.entities).iter().next().unwrap(); + self.entities.add(index); + if index as usize >= self.generations.len() { + self.generations.resize(index as usize + 1, 0); + } + let generation = self.generations[index as usize]; + Entity { generation, index } + } + + /// Whether `entity` currently exists + pub fn contains(&self, entity: Entity) -> bool { + self.entities.contains(entity.index) + && self.generations[entity.index as usize] == entity.generation + } + + /// Destroy an entity and all associated components + /// + /// Returns `false` iff the entity was previously destroyed + pub fn despawn(&mut self, entity: Entity) -> bool { + if !self.contains(entity) { + return false; + } + for storage in self.storages.values() { + let mut storage = storage.lock().unwrap(); + storage.free(entity.index); + } + self.generations[entity.index as usize] = + self.generations[entity.index as usize].wrapping_add(1); + true + } + + /// Associate `component` with `entity` + /// + /// Returns `Some` if there was a pre-existing component for this entity in this storage. + pub fn insert( + &self, + entity: Entity, + component: S::Component, + ) -> Option { + if !self.contains(entity) { + return None; + } + self.get::() + .expect("no such storage") + .insert(entity.index, component) + } + + /// Remove `component` from `entity` + /// + /// Returns `none` iff no such component exists. + pub fn remove(&self, entity: Entity) -> Option { + if !self.contains(entity) { + return None; + } + self.get::() + .expect("no such storage") + .remove(entity.index) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn smoke() { + let mut world = World::new(); + world.register::>(); + let entity = world.spawn(); + world.insert::>(entity, 42); + assert!(world.contains(entity)); + assert_eq!(world.remove::>(entity), Some(42)); + assert_eq!(world.remove::>(entity), None); + assert!(world.despawn(entity)); + assert!(!world.contains(entity)); + } +} diff --git a/src/storage/mod.rs b/src/storage/mod.rs new file mode 100644 index 00000000..b1950cb1 --- /dev/null +++ b/src/storage/mod.rs @@ -0,0 +1,276 @@ +mod vec; + +pub use vec::*; + +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::sync::MutexGuard; + +use downcast_rs::{impl_downcast, Downcast}; +use hibitset::{BitIter, BitSet, BitSetAnd, BitSetLike}; + +pub(crate) trait AbstractStorage: Downcast + Send + 'static { + fn free(&mut self, i: u32); +} +impl_downcast!(AbstractStorage); + +impl AbstractStorage for Masked { + fn free(&mut self, i: u32) { + self.remove(i); + } +} + +pub trait Storage: Default + Send + 'static { + type Component; + + unsafe fn insert(&mut self, i: u32, x: Self::Component); + unsafe fn remove(&mut self, i: u32) -> Self::Component; + unsafe fn get(&self, i: u32) -> &Self::Component; + unsafe fn get_mut(&mut self, i: u32) -> &mut Self::Component; +} + +pub struct Masked { + inner: S, + mask: BitSet, +} + +impl Masked { + pub(crate) fn new(x: S) -> Self { + Self { + inner: x, + mask: BitSet::new(), + } + } + + pub(crate) fn insert(&mut self, i: u32, x: S::Component) -> Option { + unsafe { + let old = match self.mask.add(i) { + true => Some(self.inner.remove(i)), + false => None, + }; + self.inner.insert(i, x); + old + } + } + + pub(crate) fn remove(&mut self, i: u32) -> Option { + unsafe { + match self.mask.remove(i) { + true => Some(self.inner.remove(i)), + false => None, + } + } + } +} + +impl Drop for Masked { + fn drop(&mut self) { + for i in (&self.mask).iter() { + unsafe { + self.inner.remove(i); + } + } + } +} + +pub struct StorageRefMut<'a, S> { + guard: MutexGuard<'a, Box>, + marker: PhantomData, +} + +impl<'a, S> StorageRefMut<'a, S> { + pub(crate) fn new(guard: MutexGuard<'a, Box>) -> Self { + Self { + guard, + marker: PhantomData, + } + } +} + +impl<'a, S: Storage> Deref for StorageRefMut<'a, S> { + type Target = Masked; + fn deref(&self) -> &Masked { + (**self.guard).downcast_ref::>().unwrap() + } +} + +impl<'a, S: Storage> DerefMut for StorageRefMut<'a, S> { + fn deref_mut(&mut self) -> &mut Masked { + self.guard.downcast_mut::>().unwrap() + } +} + +impl<'a, S: Storage> IntoIterator for &'a Masked { + type Item = &'a S::Component; + type IntoIter = SingleIter<'a, S>; + + fn into_iter(self) -> SingleIter<'a, S> { + SingleIter { + bits: (&self.mask).iter(), + storage: &self.inner, + } + } +} + +pub struct SingleIter<'a, S> { + bits: BitIter<&'a BitSet>, + storage: &'a S, +} + +impl<'a, S: Storage> Iterator for SingleIter<'a, S> { + type Item = &'a S::Component; + fn next(&mut self) -> Option { + let i = self.bits.next()?; + unsafe { Some(self.storage.get(i)) } + } +} + +#[doc(hidden)] +pub trait Get<'a>: 'a { + type Item: 'a; + unsafe fn get(&'a mut self, i: u32) -> Self::Item; +} + +impl<'a, S: Storage> Get<'a> for &'a S { + type Item = &'a S::Component; + unsafe fn get(&'a mut self, i: u32) -> &'a S::Component { + Storage::get(*self, i) + } +} + +impl<'a, S: Storage> Get<'a> for &'a mut S { + type Item = &'a mut S::Component; + unsafe fn get(&'a mut self, i: u32) -> &'a mut S::Component { + Storage::get_mut(*self, i) + } +} + +#[doc(hidden)] +pub trait Join<'a>: Sized { + type Bits: BitSetLike; + type Get: Get<'a>; + + fn into_parts(self) -> (Self::Bits, Self::Get); + + fn join(self) -> JoinIter<'a, Self> { + let (bits, get) = self.into_parts(); + JoinIter { + bits: bits.iter(), + get, + } + } +} + +impl<'a, T: Join<'a>> Join<'a> for (T,) { + type Bits = T::Bits; + type Get = T::Get; + fn into_parts(self) -> (Self::Bits, Self::Get) { + self.0.into_parts() + } +} + +impl<'a, S: Storage> Join<'a> for &'a Masked { + type Bits = &'a BitSet; + type Get = &'a S; + fn into_parts(self) -> (&'a BitSet, &'a S) { + (&self.mask, &self.inner) + } +} + +impl<'a, S: Storage> Join<'a> for &'a mut Masked { + type Bits = &'a BitSet; + type Get = &'a mut S; + fn into_parts(self) -> (&'a BitSet, &'a mut S) { + (&self.mask, &mut self.inner) + } +} + +pub struct JoinIter<'a, T: Join<'a>> { + bits: BitIter, + get: T::Get, +} + +impl<'a, T: Join<'a>> Iterator for JoinIter<'a, T> { + type Item = >::Item; + fn next(&mut self) -> Option { + let i = self.bits.next()?; + Some(unsafe { + Get::get( + // Sound because we never use the same `i` twice + std::mem::transmute::<_, &'a mut T::Get>(&mut self.get), + i, + ) + }) + } +} + +macro_rules! bit_and_ty { + ($name:ty) => { $name }; + ($first:ty, $($rest:ty),+) => { + BitSetAnd<$first, bit_and_ty!($($rest),+)> + } +} + +macro_rules! bit_and_expr { + ($name:expr) => { $name }; + ($first:expr, $($rest:expr),+) => { + BitSetAnd($first, bit_and_expr!($($rest),+)) + } +} + +macro_rules! tuple_impl { + ($($name: ident),*) => { + impl<'a, $($name: Join<'a>),*> Join<'a> for ($($name),*) { + type Bits = bit_and_ty!($($name::Bits),*); + type Get = ($($name::Get),*); + #[allow(non_snake_case)] + fn into_parts(self) -> (Self::Bits, Self::Get) { + let ($($name),*) = self; + let ($($name),*) = ($($name.into_parts()),*); + (bit_and_expr!($($name.0),*), ($($name.1),*)) + } + } + + impl<'a, $($name: Get<'a>),*> Get<'a> for ($($name),*) { + type Item = ($($name::Item),*); + unsafe fn get(&'a mut self, i: u32) -> Self::Item { + #[allow(non_snake_case)] + let ($(ref mut $name),*) = self; + ($($name.get(i)),*) + } + } + } +} + +tuple_impl!(A, B); +tuple_impl!(A, B, C); +tuple_impl!(A, B, C, D); +tuple_impl!(A, B, C, D, E); +tuple_impl!(A, B, C, D, E, F); +tuple_impl!(A, B, C, D, E, F, G); +tuple_impl!(A, B, C, D, E, F, G, H); +tuple_impl!(A, B, C, D, E, F, G, H, I); +tuple_impl!(A, B, C, D, E, F, G, H, I, J); +tuple_impl!(A, B, C, D, E, F, G, H, I, J, K); +tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L); +tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M); +tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF); +// tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, AB, AC, AD, AE, AF, AG); diff --git a/src/storage/vec.rs b/src/storage/vec.rs new file mode 100644 index 00000000..30d7ca1b --- /dev/null +++ b/src/storage/vec.rs @@ -0,0 +1,36 @@ +use std::mem::MaybeUninit; +use std::ptr; + +use super::Storage; + +pub struct VecStorage(Vec>); + +impl Default for VecStorage { + fn default() -> Self { + Self(Vec::new()) + } +} + +impl Storage for VecStorage { + type Component = T; + + unsafe fn insert(&mut self, i: u32, x: Self::Component) { + let i = i as usize; + if i >= self.0.len() { + self.0.resize_with(i + 1, || MaybeUninit::uninit()); + } + ptr::write(self.0[i].as_mut_ptr(), x); + } + + unsafe fn remove(&mut self, i: u32) -> Self::Component { + ptr::read(self.0[i as usize].as_ptr()) + } + + unsafe fn get(&self, i: u32) -> &Self::Component { + &*self.0[i as usize].as_ptr() + } + + unsafe fn get_mut(&mut self, i: u32) -> &mut Self::Component { + &mut *self.0[i as usize].as_mut_ptr() + } +}