Skip to content

Instantly share code, notes, and snippets.

@amaranth
Last active September 5, 2015 15:09
Show Gist options
  • Save amaranth/6b85616352272d9c397e to your computer and use it in GitHub Desktop.
Save amaranth/6b85616352272d9c397e to your computer and use it in GitHub Desktop.
// TODO: Add systems, figure out parallelization
extern crate fixedbitset;
use self::fixedbitset::FixedBitSet;
use std::collections::HashMap;
use std::{mem, ptr};
pub const MAX_COMPONENTS: usize = 256;
const CHUNK_SIZE: usize = 8192;
// Methods of this are generic on T instead of the struct itself so we can
// store them in a Vec<Pool> without trying to box a trait object and later
// downcast
pub struct Pool {
element_size: usize,
elements_per_chunk: usize,
chunks: Vec<Box<[u8; CHUNK_SIZE]>>,
}
impl Pool {
pub fn new<T>() -> Pool {
Pool {
element_size: mem::size_of::<T>(),
elements_per_chunk: CHUNK_SIZE / mem::size_of::<T>(),
chunks: Vec::new(),
}
}
pub fn with_capacity<T>(capacity: usize) -> Pool {
let mut pool = Pool::new::<T>();
pool.reserve(capacity);
pool
}
pub fn capacity(&self) -> usize {
self.chunks.len() * self.elements_per_chunk
}
pub fn reserve(&mut self, additional: usize) {
let mut elements = self.chunks.len() * self.elements_per_chunk;
let new_elements = elements + additional;
while elements < new_elements {
self.chunks.push(Box::new([0; CHUNK_SIZE]));
elements += self.elements_per_chunk;
}
}
pub unsafe fn get<T>(&self, index: usize) -> &T {
assert!(index < self.capacity());
let outer = index / self.elements_per_chunk;
let inner = (index % self.elements_per_chunk) * self.element_size;
let inner_end = inner + self.element_size;
let raw = self.chunks[outer][inner..(inner + inner_end)].as_ptr();
mem::transmute(raw)
}
pub unsafe fn get_mut<T>(&mut self, index: usize) -> &mut T {
assert!(index < self.capacity());
let outer = index / self.elements_per_chunk;
let inner = (index % self.elements_per_chunk) * self.element_size;
let inner_end = inner + self.element_size;
let raw = self.chunks[outer][inner..(inner + inner_end)].as_mut_ptr();
mem::transmute(raw)
}
pub unsafe fn put<T>(&mut self, index: usize, value: T) {
assert!(index < self.capacity());
let outer = index / self.elements_per_chunk;
let inner = (index % self.elements_per_chunk) * self.element_size;
let inner_end = inner + self.element_size;
let raw = self.chunks[outer][inner..(inner + inner_end)].as_mut_ptr();
ptr::copy_nonoverlapping(&value, mem::transmute(raw), 1);
}
pub unsafe fn clear<T>(&mut self, index: usize) {
let dummy: T = unsafe { mem::zeroed() };
self.put(index, dummy);
}
}
#[derive(PartialEq, Eq)]
pub struct Entity(u32, u32);
impl Entity {
fn new(id: u32, version: u32) -> Entity {
Entity(id, version)
}
fn id(&self) -> u32 {
self.0
}
fn version(&self) -> u32 {
self.1
}
}
pub trait Component : Copy + Default {
/// The id for this component type, used for storage. **Must** be a unique
/// value and less than MAX_COMPONENTS.
fn id() -> u16;
}
pub struct EntityManager {
entity_counter: u32,
entity_versions: Vec<u32>,
entity_freelist: Vec<u32>,
components: Vec<Option<Pool>>,
components_mask: Vec<FixedBitSet>,
}
impl EntityManager {
pub fn new() -> EntityManager {
EntityManager {
entity_counter: 0,
entity_versions: Vec::new(),
entity_freelist: Vec::new(),
components: Vec::new(),
components_mask: Vec::new(),
}
}
pub fn register_component<T: Component>(&mut self) -> Result<(), &str> {
let index = T::id() as usize;
if index >= MAX_COMPONENTS {
Err("tried to register component with id larger than MAX_COMPONENTS")
} else if self.components[index].is_some() {
Err("tried to register component that is already registered")
} else {
self.components[index] = Some(Pool::with_capacity::<Data<T>>(self.entity_counter as usize));
Ok(())
}
}
pub fn unregister_component<T: Component>(&mut self) {
let index = T::id() as usize;
if index < MAX_COMPONENTS {
self.components[index] = None;
}
}
pub fn create(&mut self) -> Entity {
if self.entity_freelist.is_empty() {
let index = self.entity_counter as usize;
self.entity_versions[index] = 0;
for entry in self.components.iter_mut() {
if let Some(ref mut pool) = *entry {
pool.reserve(1);
}
}
self.components_mask[index] = FixedBitSet::with_capacity(MAX_COMPONENTS);
let entity = Entity::new(self.entity_counter, 0);
self.entity_counter += 1;
entity
} else {
let id = self.entity_freelist.pop().unwrap();
Entity::new(id, self.entity_versions[id as usize])
}
}
pub fn destroy(&mut self, entity: Entity) -> Result<(), &str> {
// These checks are from validate_entity but the damn borrow checker
if entity.id() as usize >= self.entity_versions.len() {
Err("entity outside known range")
} else if entity.version() != self.entity_versions[entity.id() as usize] {
Err("tried to access destroyed entity")
} else {
let index = entity.id() as usize;
self.entity_versions[index] += 1;
self.entity_freelist.push(entity.id());
self.components_mask[index].clear();
Ok(())
}
}
pub fn add_component<T: Component>(&mut self, entity: &Entity) -> Result<&mut Data<T>, &str> {
// These checks are from validate_entity but the damn borrow checker
if entity.id() as usize >= self.entity_versions.len() {
Err("entity outside known range")
} else if entity.version() != self.entity_versions[entity.id() as usize] {
Err("tried to access destroyed entity")
} else {
let entity_index = entity.id() as usize;
let component_index = T::id() as usize;
if self.components_mask[entity_index].contains(component_index) {
Err("tried to add component to entity twice")
} else {
if let Some(ref mut pool) = self.components[component_index] {
let data = Data::<T>::new();
self.components_mask[entity_index].set(component_index, true);
unsafe {
pool.put::<Data<T>>(entity_index, data);
Ok(pool.get_mut::<Data<T>>(entity_index))
}
} else {
Err("tried to add component to entity that is not registered")
}
}
}
}
pub fn remove_component<T: Component>(&mut self, entity: &Entity) -> Result<(), &str> {
// These checks are from validate_entity but the damn borrow checker
if entity.id() as usize >= self.entity_versions.len() {
Err("entity outside known range")
} else if entity.version() != self.entity_versions[entity.id() as usize] {
Err("tried to access destroyed entity")
} else {
let entity_index = entity.id() as usize;
let component_index = T::id() as usize;
if self.components[component_index].is_none() {
Err("tried to remove component from entity that is not registered")
} else {
self.components_mask[entity_index].set(component_index, false);
Ok(())
}
}
}
pub fn get_component<T: Component>(&self, entity: &Entity) -> Result<Option<&Data<T>>, &str> {
try!(self.validate_entity(entity));
let entity_index = entity.id() as usize;
let component_index = T::id() as usize;
if let Some(ref pool) = self.components[component_index] {
if !self.components_mask[entity_index].contains(component_index) {
Ok(None)
} else {
unsafe {
Ok(Some(pool.get::<Data<T>>(entity_index)))
}
}
} else {
Err("tried to add component to entity that is not registered")
}
}
pub fn get_mut_component<T: Component>(&mut self, entity: &Entity) -> Result<Option<&mut Data<T>>, &str> {
// These checks are from validate_entity but the damn borrow checker
if entity.id() as usize >= self.entity_versions.len() {
Err("entity outside known range")
} else if entity.version() != self.entity_versions[entity.id() as usize] {
Err("tried to access destroyed entity")
} else {
let entity_index = entity.id() as usize;
let component_index = T::id() as usize;
if let Some(ref mut pool) = self.components[component_index] {
if !self.components_mask[entity_index].contains(component_index) {
Ok(None)
} else {
unsafe {
Ok(Some(pool.get_mut::<Data<T>>(entity_index)))
}
}
} else {
Err("tried to add component to entity that is not registered")
}
}
}
fn validate_entity(&self, entity: &Entity) -> Result<(), &str> {
if entity.id() as usize >= self.entity_versions.len() {
Err("entity outside known range")
} else if entity.version() != self.entity_versions[entity.id() as usize] {
Err("attempted to access destroyed entity")
} else {
Ok(())
}
}
}
pub struct Data<T: Component> {
versions: [T; 3],
index: usize,
}
impl <T: Component> Data<T> {
pub fn new() -> Data<T> {
Data {
versions: [Default::default(); 3],
index: 0,
}
}
pub fn previous(&self) -> &T {
&self.versions[self.index % 3]
}
pub fn current(&self) -> &T {
&self.versions[(self.index + 1) % 3]
}
// TODO: Maybe this should be multiple things, use system priority for merging them
pub fn mutable(&mut self) -> &mut T {
&mut self.versions[(self.index + 2) % 3]
}
fn swap(&mut self) {
// After updating the index previous will be mutable, current will be
// previous, and mutable will be current. We want mutable to start
// with the contents of current so we copy from "mutable" to
// "previous" before updating the index.
self.versions[self.index % 3] = self.versions[(self.index + 2) % 3];
self.index += 1;
}
}
let mut manager = EntityManager::new();
manager.register_component::<TestComponent>();
let entity = manager.create();
{
let mut data = manager.add_component::<TestComponent>(&entity);
}
manager.remove_component::<TestComponent>(&entity);
manager.unregister_component::<TestComponent>();
manager.destroy_entity(entity);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment