-
-
Save fowlmouth/1f9b87a65d10b895b64a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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