Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Forked from pythonesque/entity2.rs
Last active August 29, 2015 14:09
Show Gist options
  • Save fowlmouth/1f9b87a65d10b895b64a to your computer and use it in GitHub Desktop.
Save fowlmouth/1f9b87a65d10b895b64a to your computer and use it in GitHub Desktop.
#![feature(macro_rules)]
extern crate collections;
extern crate sync;
use std::sync::{Arc, Mutex, RWLock, RWLockReadGuard};
use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUint, Ordering, Relaxed, SeqCst};
use std::ptr;
use std::ptr::{RawMutPtr};
use std::slice::Items;
macro_rules! components {
($max:expr, $($var:ident ( $v:ident ) = $i:expr),*) => {
// I AM A TERRIBLE PERSON
#[deriving(Clone,FromPrimitive,Show)]
#[repr(uint)]
enum ComponentType {
$($var = $i),*
}
const MAX_COMPONENTS: uint = $max;
impl ComponentType {
// Wildly unsafe function that we use because we like to live dangerously.
// Just in case this isn't clear, this function transmutes an arbitrary pointer
// to an arbitrary component type. Basically, to avoid the overhead of an enum
// or trait object, we use preexisting knowledge to ensure type safety.
// Here be dragons.
unsafe fn drop_as(&self, component: &mut AtomicPtr<Arc<()>>) {
use std::mem::transmute;
// Swap in a null pointer
let ptr = component.swap(ptr::null_mut(), SeqCst);
// Sanity check for null
if ptr.is_null() { return }
// Drop based on the component type.
match *self {
$( $var => drop(transmute::<_, Box<Arc<$v>>>(ptr)) ),*
}
}
}
$(
impl Component for $v { fn component_type(_: Option<$v>) -> ComponentType { $var } }
)*
};
($max:expr, $($var:ident ( $v: ident ) = $i:expr),+, ) => (components!($max, $($var ($v) = $i),+))
}
trait Component: Sync + Send {
fn component_type(dummy: Option<Self>) -> ComponentType; // HACK HACK HACK
}
const COMPONENT_SET_BYTES: uint = (MAX_COMPONENTS + ::std::uint::BITS - 1) / ::std::uint::BITS;
struct ComponentBitvSet {
storage: [uint, .. COMPONENT_SET_BYTES],
}
impl ComponentBitvSet {
#[inline]
pub fn new(components: &[ComponentType]) -> ComponentBitvSet {
let mut set = ComponentBitvSet { storage: [0, .. COMPONENT_SET_BYTES] };
for &component in components.iter() {
set.set(component, true);
};
set
}
#[inline]
fn set(&mut self, component: ComponentType, x: bool) {
let i = component as uint;
let w = i / ::std::uint::BITS;
let b = i % ::std::uint::BITS;
let flag = 1 << b;
self.storage[w] = if x { self.storage[w] | flag }
else { self.storage[w] & !flag };
}
#[inline]
fn is_subset(&self, other: &AsyncBitvSet, order: Ordering) -> bool {
for i in range(0, COMPONENT_SET_BYTES) {
let storage = self.storage[i];
if other.storage[i].load(order) & storage != storage { return false }
}
true
}
}
struct AsyncBitvSet {
storage: [AtomicUint, .. COMPONENT_SET_BYTES]
}
impl AsyncBitvSet {
#[inline]
pub fn new() -> AsyncBitvSet {
AsyncBitvSet { storage: unsafe { ::std::mem::zeroed() } }
}
#[inline]
pub fn set(&self, component: ComponentType, x: bool, order: Ordering) {
let i = component as uint;
let w = i / ::std::uint::BITS;
let b = i % ::std::uint::BITS;
let flag = 1 << b;
let ref w = self.storage[w];
if x { w.fetch_or(flag, order) } else { w.fetch_and(!flag, order) };
}
}
struct Entity {
index: AsyncBitvSet,
components: [AtomicPtr<Arc<()>>, .. MAX_COMPONENTS]
}
impl Drop for Entity {
fn drop(&mut self) {
unsafe {
// Hilariously unsafe bit to make sure we drop components properly. This could be
// faster if we could trust the index, but by design we can't.
for (i, c) in self.components.iter_mut().enumerate() {
let component_type: ComponentType = ::std::mem::transmute(i);
component_type.drop_as(c);
}
}
}
}
impl Entity {
fn new() -> Entity {
Entity {
index: AsyncBitvSet::new(),
components: unsafe { ::std::mem::zeroed() },
}
}
fn set<T>(&self, data: Box<Arc<T>>) -> Result<(), Box<Arc<T>>> where T: Component {
// This is actually unsafe, if you make the Component trait accessible
// to someone malicious. Don't do that :)
let component_type = Component::component_type(None::<T>);
let ref component = self.components[component_type as uint];
unsafe {
let component: &AtomicPtr<Arc<T>> = ::std::mem::transmute(component);
let data: *mut Arc<T> = ::std::mem::transmute(data);
match component.compare_and_swap(ptr::null_mut(), data, SeqCst).as_mut() {
Some(_) => {
let data: Box<Arc<T>> = ::std::mem::transmute(data);
Err(data)
},
None => {
// Set the index last
self.index.set(component_type, true, SeqCst);
Ok(())
}
}
}
}
pub fn get<T>(&self) -> Option<&Arc<T>> where T: Component {
// This is actually unsafe, if you make the Component trait accessible
// to someone malicious. Don't do that :)
let component_type = Component::component_type(None::<T>);
let ref component = self.components[component_type as uint];
let component: Option<&Arc<T>> = {
unsafe {
let component: &AtomicPtr<Arc<T>> = ::std::mem::transmute(component);
component.load(SeqCst).as_ref()
}
};
component
}
}
type ComponentVec = Vec<Arc<Entity>>;
struct ComponentManager {
done: AtomicBool,
components: [RWLock<ComponentVec>, .. MAX_COMPONENTS],
}
struct MatchEntities<'a, 'b: 'a, I> {
vec_guard: RWLockReadGuard<'a, ComponentVec>,
set: ComponentBitvSet,
iter: Option<I>,
}
impl<'a, 'b> MatchEntities<'a, 'b, Items<'a, Arc<Entity>>>
{
fn next(&'b mut self) -> Option<&'a Entity> {
let iter = match self.iter {
Some(ref mut i) => i,
None => { self.iter = Some(self.vec_guard.iter()); self.iter.as_mut().unwrap() } // required to avoid borrow checker shenanigans
};
loop {
match iter.next() {
Some(e) => {
if self.set.is_subset(&e.index, Relaxed) {
return Some(e.deref());
}
},
None => {
return None
}
}
}
}
}
impl ComponentManager {
fn new() -> ComponentManager {
// We allocate the Vec rather than directly because we need to make sure that
// fail! cannot be called between the time we set up our new uninitialized
// array and the time we actually put stuff in it. Yay exception safety.
let components = unsafe {
let components_vec = Vec::from_fn(MAX_COMPONENTS, |_| RWLock::new(Vec::new()));
let mut components: [RWLock<ComponentVec>, .. MAX_COMPONENTS] = ::std::mem::uninitialized();
for (dst, src) in components.iter_mut().zip(components_vec.into_iter()) {
// Need to make sure we don't trigger the destructor on the old element.
ptr::write(dst as *mut _, src);
}
components
};
ComponentManager { components: components, done: AtomicBool::new(false) }
}
fn matches<'a, 'b>(&'a self, index: ComponentType, rest: &[ComponentType]) -> MatchEntities<'a, 'b, Items<'a, Arc<Entity>>> {
let set = ComponentBitvSet::new(rest);
let guard = self.components[index as uint].read(); // always inbounds
let matches = MatchEntities {
vec_guard: guard,
iter: None,
set: set,
};
matches
}
pub fn insert<T>(&self, entity: Arc<Entity>, data: Box<Arc<T>>) -> Result<(), Box<Arc<T>>> where T: Component {
// If already inserted, don't add to the list
try!(entity.set(data));
// This is actually unsafe, if you make the Component trait accessible
// to someone malicious. Don't do that :)
let component_type = Component::component_type(None::<T>);
let mut guard = self.components[component_type as uint].write(); // always inbounds
guard.push(entity);
Ok(())
}
pub fn done(&self) -> bool {
self.done.load(Relaxed)
}
pub fn finish(&self) {
self.done.store(true, Relaxed);
}
}
// Test stuff
components! {
3,
Transform(CTransform) = 0u,
Velocity(CVelocity) = 1u,
Sprite(CSprite) = 2u,
}
#[deriving(Show)]
struct CTransform_ {
x: int,
y: int
}
type CTransform = Mutex<CTransform_>;
#[deriving(Clone)]
struct CVelocity {
x: int,
y: int
}
impl Drop for CVelocity {
fn drop(&mut self) {
//println!("Dropped: {} {}", self.x, self.y);
}
}
struct CSprite {
filename: String
}
fn s_physics(cm: Arc<ComponentManager>) {
'a: loop {
let mut iter = cm.matches(Velocity, [Transform]);
loop {
match iter.next() {
Some(e) => {
let velocity = e.get::<CVelocity>().unwrap();
let transform = e.get::<CTransform>().unwrap();
let mut transform = transform.lock();
//println!(" was at {:d}, {:d}!", transform.x, transform.y);
transform.x += velocity.x;
transform.y += velocity.y;
if cm.done() {
println!(" now at {:d}, {:d}!", transform.x, transform.y);
break 'a;
}
},
None => break
}
}
}
}
fn s_graphics(cm: Arc<ComponentManager>) {
'a: loop {
let mut iter = cm.matches(Sprite, [Transform]);
loop {
match iter.next() {
Some(e) => {
let transform = e.get::<CTransform>().unwrap();
let sprite = e.get::<CSprite>().unwrap();
if cm.done() {
let transform = transform.lock();
println!("Rendering at {:d}, {:d} with {:s}!", transform.x, transform.y, sprite.filename);
break 'a;
}
},
None => break
}
}
}
}
fn main() {
// Create manager
let cm = Arc::new(ComponentManager::new());
// Create entities
let physical_entity = Arc::new(Entity::new());
let _ = cm.insert(physical_entity.clone(), box Arc::new(Mutex::new(CTransform_{x: 6, y: 10})));
let _ = cm.insert(physical_entity.clone(), box Arc::new(CVelocity{x: 1, y: 2}));
let _ = cm.insert(physical_entity, box Arc::new(CSprite{filename: "foobar.png".into_string()}));
let graphical_entity = Arc::new(Entity::new());
let _ = cm.insert(graphical_entity.clone(), box Arc::new(Mutex::new(CTransform_{x: 6, y: 10})));
let _ = cm.insert(graphical_entity, box Arc::new(CSprite{filename: "foobar.png".into_string()}));
let graphical_entity = Arc::new(Entity::new());
let _ = cm.insert(graphical_entity.clone(), box Arc::new(CVelocity{x: 1, y: 2}));
let _ = cm.insert(graphical_entity.clone(), box Arc::new(CSprite{filename: "foobar.png".into_string()}));
let _ = cm.insert(graphical_entity, box Arc::new(Mutex::new(CTransform_{x: 6, y: 10})));
// Run systems
let cm_ = cm.clone();
spawn(proc() { s_physics(cm_); println!("Done physics"); } );
let cm_ = cm.clone();
spawn(proc() { s_graphics(cm_); println!("Done graphics"); } );
::std::io::timer::sleep(::std::time::Duration::seconds(1));
cm.finish();
}
#![feature(macro_rules)]
extern crate collections;
use std::collections::BitvSet;
//use std::sync::{Arc, Mutex, MutexGuard, RefCell, RWLockReadGuard};
use std::cell::{Cell, RefCell, Ref, RefMut};
use std::rc::{Rc};
use std::sync::atomic::{AtomicBool, Relaxed};
use std::ptr;
use std::slice::Items;
// I AM A TERRIBLE PERSON
#[deriving(FromPrimitive)]
enum ComponentType {
Transform = 0,
Velocity = 1,
Sprite = 2,
}
trait Component {
fn component_type(dummy: Option<Self>) -> ComponentType; // HACK HACK HACK
}
const MAX_COMPONENTS: uint = 3;
struct Entity {
index: BitvSet,
components: [Option<Box<()>>, .. MAX_COMPONENTS]
}
impl Entity {
fn new() -> Entity {
let components = unsafe {
let components_vec = Vec::from_elem(MAX_COMPONENTS, None);
let mut components: [Option<Box<()>>, .. MAX_COMPONENTS] = ::std::mem::uninitialized();
for (dst, src) in components.iter_mut().zip(components_vec.into_iter()) {
// Need to make sure we don't trigger the destructor on the old element.
ptr::write(dst as *mut _, src);
}
components
};
Entity {
index: BitvSet::with_capacity(MAX_COMPONENTS),
components: components,
}
}
fn set<T>(&mut self, data: T) -> bool where T: Component {
// This is actually unsafe, if you make the Component trait accessible
// to someone malicious. Don't do that :)
let component_type = Component::component_type(None::<T>);
let component: &mut Option<Box<T>> = {
let ref mut component = self.components[component_type as uint];
unsafe { ::std::mem::transmute(component) }
};
match component {
&Some(_) => false, // Data already in here
&None => {
*component = Some(box data);
// Set the index last
self.index.insert(component_type as uint);
true
}
}
}
pub fn get_mut<T>(&mut self) -> Option<&mut Box<T>> where T: Component {
// This is actually unsafe, if you make the Component trait accessible
// to someone malicious. Don't do that :)
let component_type = Component::component_type(None::<T>);
let component: Option<&mut Box<T>> = {
let ref mut component = self.components[component_type as uint];
component.as_mut().map( |x| {
let x: &mut Box<T> = unsafe { ::std::mem::transmute(x) };
x
})
};
component
}
pub fn get<T>(&self) -> Option<&Box<T>> where T: Component {
// This is actually unsafe, if you make the Component trait accessible
// to someone malicious. Don't do that :)
let component_type = Component::component_type(None::<T>);
let component: Option<&Box<T>> = {
let ref component = self.components[component_type as uint];
component.as_ref().map( |x| {
let x: &Box<T> = unsafe { ::std::mem::transmute(x) };
x
})
};
component
}
}
type ComponentVec = Vec<Rc<RefCell<Entity>>>;
struct ComponentManager {
done: AtomicBool,
components: [RefCell<ComponentVec>, .. MAX_COMPONENTS],
}
struct MatchEntities<'a, 'b: 'a, I> {
vec_guard: Ref<'a, ComponentVec>,
set: BitvSet,
iter: Option<I>,
}
impl<'a, 'b> MatchEntities<'a, 'b, Items<'a, Rc<RefCell<Entity>>>>
{
fn next<'c: 'b>(&'c mut self) -> Option<RefMut<'a, Entity>> {
let iter = match self.iter {
Some(ref mut i) => i,
None => { self.iter = Some(self.vec_guard.iter()); self.iter.as_mut().unwrap() } // required to avoid borrow checker shenanigans
};
loop {
match iter.next() {
Some(mutex) => {
// Lock components in the same order each time--this is important to prevent deadlock!
let guard = mutex.borrow_mut();
if self.set.is_subset(&guard.index) {
return Some(guard);
}
// Lock goes out of scope otherwise
},
None => {
return None
}
}
}
}
}
impl ComponentManager {
fn new() -> ComponentManager {
// We allocate the Vec rather than directly because we need to make sure that
// fail! cannot be called between the time we set up our new uninitialized
// array and the time we actually put stuff in it. Yay exception safety.
let components = unsafe {
let components_vec = Vec::from_fn(MAX_COMPONENTS, |_| RefCell::new(Vec::new()));
let mut components: [RefCell<ComponentVec>, .. MAX_COMPONENTS] = ::std::mem::uninitialized();
for (dst, src) in components.iter_mut().zip(components_vec.into_iter()) {
// Need to make sure we don't trigger the destructor on the old element.
ptr::write(dst as *mut _, src);
}
components
};
ComponentManager { components: components, done: AtomicBool::new(false) }
}
fn matches<'a, 'b>(&'a self, index: ComponentType, rest: &[ComponentType]) -> MatchEntities<'a, 'b, Items<'a, Rc<RefCell<Entity>>>> {
let mut set = BitvSet::with_capacity(MAX_COMPONENTS);
for &i in rest.iter() {
set.insert(i as uint);
}
let guard = self.components[index as uint].borrow(); // always inbounds
let matches = MatchEntities {
vec_guard: guard,
iter: None,
set: set,
};
matches
}
pub fn insert<T>(&self, entity: Rc<RefCell<Entity>>, data: T) where T: Component {
{
// Lock scope
let mut guard = entity.borrow_mut();
if !guard.set(data) {
// Already inserted, don't add to the list
return
}
}
// This is actually unsafe, if you make the Component trait accessible
// to someone malicious. Don't do that :)
let component_type = Component::component_type(None::<T>);
let mut guard = self.components[component_type as uint].borrow_mut(); // always inbounds
guard.push(entity);
}
pub fn done(&self) -> bool {
self.done.load(Relaxed)
}
pub fn finish(&self) {
self.done.store(true, Relaxed);
}
}
#[deriving(Show)]
struct CTransform {
x: int,
y: int
}
impl Component for CTransform { fn component_type(_: Option<CTransform>) -> ComponentType { Transform } }
#[deriving(Clone)]
struct CVelocity {
x: int,
y: int
}
impl Component for CVelocity { fn component_type(_: Option<CVelocity>) -> ComponentType { Velocity } }
struct CSprite {
filename: String
}
impl Component for CSprite { fn component_type(_: Option<CSprite>) -> ComponentType { Sprite } }
/*struct ComponentManager {
next_id: i64,
ctransform: HashMap<i64, CTransform>,
cvelocity: HashMap<i64, CVelocity>,
csprite: HashMap<i64, CSprite>
}
impl Default for ComponentManager {
fn default () -> ComponentManager {
ComponentManager {
next_id : 0,
ctransform: HashMap::new(),
cvelocity: HashMap::new(),
csprite: HashMap::new(),
}
}
}
impl ComponentManager {
fn new_entity(&mut self) -> i64 {
self.next_id += 1;
return self.next_id - 1;
}
}*/
fn s_physics(cm: Rc<ComponentManager>) {
//let entities: HashSet<i64> = hashmap_key_overlap!(cm.ctransform, cm.cvelocity);
//for e in entities.iter() {
'a: loop {
let mut iter = cm.matches(Transform, [Velocity]);
loop {
match iter.next() {
Some(mut e) => {
let velocity = { e.get::<CVelocity>().unwrap().clone() };
let transform = e.get_mut::<CTransform>().unwrap();
println!(" was at {:d}, {:d}!", transform.x, transform.y);
transform.x += velocity.x;
transform.y += velocity.y;
//if cm.done() {
println!(" now at {:d}, {:d}!", transform.x, transform.y);
break 'a;
//}
},
None => break
}
}
}
}
fn s_graphics(cm: Rc<ComponentManager>) {
//let entities: HashSet<i64> = hashmap_key_overlap!(cm.ctransform, cm.csprite);
'a: loop {
let mut iter = cm.matches(Transform, [Sprite]);
loop {
match iter.next() {
Some(e) => {
let transform = e.get::<CTransform>().unwrap();
let sprite = e.get::<CSprite>().unwrap();
//if cm.done() {
println!("Rendering at {:d}, {:d} with {:s}!", transform.x, transform.y, sprite.filename);
break 'a;
//}
},
None => break
}
}
}
}
/*fn main() {
let mut cm: ComponentManager = Default::default();
let physical_entity: i64 = cm.new_entity();
cm.ctransform.insert(physical_entity, CTransform{x: 6, y: 10});
cm.cvelocity.insert(physical_entity, CVelocity{x: 1, y: 2});
let graphical_entity: i64 = cm.new_entity();
cm.ctransform.insert(graphical_entity, CTransform{x: 6, y: 10});
cm.csprite.insert(graphical_entity, CSprite{filename: "foobar.png".into_string()});
s_physics(&mut cm);
s_graphics(&mut cm);
}*/
fn main() {
// Create manager
let cm = Rc::new(ComponentManager::new());
// Create entities
let physical_entity = Rc::new(RefCell::new(Entity::new()));
cm.insert(physical_entity.clone(), CTransform{x: 6, y: 10});
cm.insert(physical_entity, CVelocity{x: 1, y: 2});
let graphical_entity = Rc::new(RefCell::new(Entity::new()));
cm.insert(graphical_entity.clone(), CTransform{x: 6, y: 10});
cm.insert(graphical_entity, CSprite{filename: "foobar.png".into_string()});
// Run systems
let cm_ = cm.clone();
s_physics(cm_);
//spawn(proc() { s_physics(cm_); println!("Done physics"); } );
let cm_ = cm.clone();
s_graphics(cm_);
//spawn(proc() { s_graphics(cm_); println!("Done graphics"); } );
::std::io::timer::sleep(::std::time::Duration::seconds(1));
cm.finish();
}
extern crate collections;
use std::collections::HashSet;
use std::default::Default;
#[deriving(Eq, Hash)]
enum Component {
Transform(CTransform),
Velocity(CVelocity),
Sprite(CSprite),
}
impl Component {
fn tag(&self) -> u32 {
match *self {
Transform(_) => 0,
Velocity(_) => 1,
Sprite(_) => 2,
}
}
}
impl PartialEq for Component {
fn eq(&self, other: &Component) -> bool {
self.tag() == other.tag()
}
}
struct ComponentManager {
entities: Vec<Entity>,
}
impl ComponentManager {
fn new() -> ComponentManager { ComponentManager { entities: Vec::new() } }
fn add(&mut self, entity: Entity) {
self.entities.push(entity);
}
fn match_entities(&mut self, tags_: &[Component]) -> HashSet<&mut Entity> {
let mut tags = HashSet::<u32>::new();
for mut tag in tags_.iter().map( |ref c: &Component| c.tag() ) {
tags.insert(tag);
}
self.entities.iter_mut().filter( |entry| {
let mut set = HashSet::<u32>::new();
for tag in entry.components.iter().map ( |c| c.tag() ) {
set.insert(tag);
}
tags.is_subset(&set)
}).collect()
}
}
#[deriving(Default, Eq, PartialEq, Hash)]
struct Entity {
components: Vec<Component>,
}
impl Entity {
fn new() -> Entity { Entity { components: Vec::new() } }
fn add(&mut self, component: Component) {
self.components.push(component);
}
}
#[deriving(Default, Show, Eq, PartialEq, Hash)]
struct CTransform {
x: int,
y: int
}
#[deriving(Default, Clone, Eq, PartialEq, Hash)]
struct CVelocity {
x: int,
y: int
}
#[deriving(Default, Clone, Eq, PartialEq, Hash)]
struct CSprite {
filename: String
}
fn s_physics(cm: &mut ComponentManager) {
let query = [Transform(Default::default()), Velocity(Default::default())];
let entities: HashSet<_> = cm.match_entities(&query);
for e in entities.iter() {
let mut transform = e.components.iter().filter_map ( |c| match c { &Transform(t) => Some(t), _ => None } ).next().unwrap();
let velocity = e.components.iter().filter_map ( |c| match c { &Velocity(t) => Some(t), _ => None } ).next().unwrap();
println!(" was at {:d}, {:d}!", transform.x, transform.y);
transform.x += velocity.x;
transform.y += velocity.y;
println!(" now at {:d}, {:d}!", transform.x, transform.y);
}
}
fn s_graphics(cm: &mut ComponentManager) {
let query = [Transform(Default::default()), Sprite(Default::default())];
let entities: HashSet<_> = cm.match_entities(&query);
for e in entities.iter() {
let transform = e.components.iter().filter_map ( |c| match c { &Transform(t) => Some(t), _ => None } ).next().unwrap();
let sprite = e.components.iter().filter_map ( |c| match c { &Sprite(ref t) => Some(t), _ => None } ).next().unwrap();
println!("Rendering at {:d}, {:d} with {:s}!", transform.x, transform.y, sprite.filename);
}
}
fn main() {
let mut cm: ComponentManager = ComponentManager::new();
let mut physical_entity = Entity::new();
physical_entity.add(Transform(CTransform{x: 6, y: 10}));
physical_entity.add(Velocity(CVelocity{x: 1, y: 2}));
cm.add(physical_entity);
let mut graphical_entity = Entity::new();
graphical_entity.add(Transform(CTransform{x: 6, y: 10}));
graphical_entity.add(Sprite(CSprite{filename: "foobar.png".into_string()}));
cm.add(graphical_entity);
s_physics(&mut cm);
s_graphics(&mut cm);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment