Skip to content

Commit

Permalink
Proof of concept
Browse files Browse the repository at this point in the history
  • Loading branch information
Ralith committed Dec 7, 2019
0 parents commit f258709
Show file tree
Hide file tree
Showing 5 changed files with 456 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
**/*.rs.bk
Cargo.lock
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "simplecs"
version = "0.1.0"
authors = ["Benjamin Saunders <[email protected]>"]
edition = "2018"
license = "MIT OR Apache-2.0"

[dependencies]
hibitset = "0.6.2"
fxhash = "0.2.1"
downcast-rs = "1.1.1"
130 changes: 130 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<u32>,
storages: FxHashMap<TypeId, Mutex<Box<dyn AbstractStorage>>>,
}

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<S: Storage>(&mut self) {
dbg!(std::any::type_name::<S>());
self.storages.insert(
TypeId::of::<S>(),
Mutex::new(Box::new(Masked::new(S::default()))),
);
}

/// Discard a type of storage, destroying its contents
pub fn unregister<S: Storage>(&mut self) {
self.storages.remove(&TypeId::of::<S>());
}

/// Access a storage
pub fn get<S: Storage>(&self) -> Option<StorageRefMut<'_, S>> {
let guard = self.storages.get(&TypeId::of::<S>())?.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<S: Storage>(
&self,
entity: Entity,
component: S::Component,
) -> Option<S::Component> {
if !self.contains(entity) {
return None;
}
self.get::<S>()
.expect("no such storage")
.insert(entity.index, component)
}

/// Remove `component` from `entity`
///
/// Returns `none` iff no such component exists.
pub fn remove<S: Storage>(&self, entity: Entity) -> Option<S::Component> {
if !self.contains(entity) {
return None;
}
self.get::<S>()
.expect("no such storage")
.remove(entity.index)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn smoke() {
let mut world = World::new();
world.register::<VecStorage<u32>>();
let entity = world.spawn();
world.insert::<VecStorage<u32>>(entity, 42);
assert!(world.contains(entity));
assert_eq!(world.remove::<VecStorage<u32>>(entity), Some(42));
assert_eq!(world.remove::<VecStorage<u32>>(entity), None);
assert!(world.despawn(entity));
assert!(!world.contains(entity));
}
}
Loading

0 comments on commit f258709

Please sign in to comment.