ecs: implement mutable iterators and joins
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaargh.ecs
parent
184a67d6c1
commit
2bd393a536
|
@ -5,6 +5,7 @@ rust_library(
|
|||
srcs = [
|
||||
"src/lib.rs",
|
||||
"src/component.rs",
|
||||
"src/componentmap.rs",
|
||||
"src/entity.rs",
|
||||
"src/system.rs",
|
||||
"src/world.rs",
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::btree_map::{
|
||||
Iter as BTMIter,
|
||||
IterMut as BTMIterMut,
|
||||
};
|
||||
|
||||
use crate::entity;
|
||||
use crate::component;
|
||||
|
||||
|
||||
type BorrowFlag = isize;
|
||||
const UNUSED: BorrowFlag = 0;
|
||||
|
||||
struct BorrowRef<'b> {
|
||||
borrow: &'b Cell<BorrowFlag>,
|
||||
}
|
||||
|
||||
impl<'b> BorrowRef<'b> {
|
||||
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRef<'b>> {
|
||||
let b = borrow.get().wrapping_add(1);
|
||||
if b <= 0 {
|
||||
None
|
||||
} else {
|
||||
borrow.set(b);
|
||||
Some(BorrowRef { borrow })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for BorrowRef<'a> {
|
||||
fn drop(&mut self) {
|
||||
let borrow = self.borrow.get();
|
||||
self.borrow.set(borrow - 1);
|
||||
}
|
||||
}
|
||||
|
||||
struct BorrowRefMut<'b> {
|
||||
borrow: &'b Cell<BorrowFlag>,
|
||||
}
|
||||
|
||||
impl<'b> BorrowRefMut<'b> {
|
||||
fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRefMut<'b>> {
|
||||
match borrow.get() {
|
||||
UNUSED => {
|
||||
borrow.set(UNUSED - 1);
|
||||
Some(BorrowRefMut { borrow })
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for BorrowRefMut<'a> {
|
||||
fn drop(&mut self) {
|
||||
let borrow = self.borrow.get();
|
||||
self.borrow.set(borrow + 1);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ComponentMap {
|
||||
value: UnsafeCell<BTreeMap<entity::ID, Box<dyn component::Component>>>,
|
||||
borrow: Cell<BorrowFlag>,
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct AccessError(String);
|
||||
|
||||
impl ComponentMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
value: UnsafeCell::new(BTreeMap::new()),
|
||||
borrow: Cell::new(UNUSED),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_iter<'a>(&'a self) -> Result<ComponentMapIter<'a>, AccessError> {
|
||||
match BorrowRef::new(&self.borrow) {
|
||||
None => Err(AccessError("already borrowed mutably".to_string())),
|
||||
Some(b) => Ok(ComponentMapIter {
|
||||
iter: unsafe {
|
||||
let map = &*self.value.get();
|
||||
map.iter()
|
||||
},
|
||||
borrow: b,
|
||||
}),
|
||||
}
|
||||
}
|
||||
pub fn try_iter_mut<'a>(&'a self) -> Result<ComponentMapIterMut<'a>, AccessError> {
|
||||
match BorrowRefMut::new(&self.borrow) {
|
||||
None => Err(AccessError("already borrowed mutable".to_string())),
|
||||
Some(b) => Ok(ComponentMapIterMut {
|
||||
iter: unsafe {
|
||||
let map = &mut *self.value.get();
|
||||
map.iter_mut()
|
||||
},
|
||||
borrow: b,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&self, e: entity::ID, c: Box<dyn component::Component>) -> Result<(), AccessError> {
|
||||
match BorrowRefMut::new(&self.borrow) {
|
||||
None => Err(AccessError("already borrow mutably".to_string())),
|
||||
Some(b) => {
|
||||
unsafe {
|
||||
let map = &mut *self.value.get();
|
||||
map.insert(e, c);
|
||||
}
|
||||
drop(b);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ComponentMapIter<'a> {
|
||||
pub iter: BTMIter<'a, entity::ID, Box<dyn component::Component>>,
|
||||
#[allow(dead_code)]
|
||||
borrow: BorrowRef<'a>,
|
||||
}
|
||||
|
||||
pub struct ComponentMapIterMut<'a> {
|
||||
pub iter: BTMIterMut<'a, entity::ID, Box<dyn component::Component>>,
|
||||
#[allow(dead_code)]
|
||||
borrow: BorrowRefMut<'a>,
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod component;
|
||||
pub mod componentmap;
|
||||
pub mod entity;
|
||||
pub mod system;
|
||||
pub mod world;
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::marker::PhantomData;
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::{
|
||||
component,
|
||||
world::{ReadData, ReadWriteData, World}
|
||||
world::{ReadData, ReadDataIter, ReadWriteData, ReadWriteDataIter, World}
|
||||
};
|
||||
|
||||
pub trait System<'a> {
|
||||
|
@ -24,21 +27,32 @@ where
|
|||
}
|
||||
|
||||
pub trait Access<'a> {
|
||||
type Component: component::Component;
|
||||
//type Component: component::Component;
|
||||
type Component;
|
||||
type Iterator: Iterator<Item = (u64, Self::Component)>;
|
||||
fn fetch(world: &'a World) -> Self;
|
||||
fn iter(&self) -> Self::Iterator;
|
||||
}
|
||||
|
||||
impl<'a, T: component::Component> Access<'a> for ReadData<'a, T> {
|
||||
type Component = T;
|
||||
type Component = &'a T;
|
||||
type Iterator = ReadDataIter<'a, T>;
|
||||
fn fetch(world: &'a World) -> Self {
|
||||
world.components()
|
||||
}
|
||||
fn iter(&self) -> ReadDataIter<'a, T> {
|
||||
Self::iter(self)
|
||||
}
|
||||
}
|
||||
impl<'a, T: component::Component> Access<'a> for ReadWriteData<'a, T> {
|
||||
type Component = T;
|
||||
type Component = &'a mut T;
|
||||
type Iterator = ReadWriteDataIter<'a, T>;
|
||||
fn fetch(world: &'a World) -> Self {
|
||||
world.components_mut()
|
||||
}
|
||||
fn iter(&self) -> ReadWriteDataIter<'a, T> {
|
||||
Self::iter_mut(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DynamicSystemData<'a> {
|
||||
|
@ -63,25 +77,74 @@ pub struct Processor<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Processor<'a> {
|
||||
fn add_system<T: System<'a> + 'static>(&mut self, system: T) {
|
||||
pub fn add_system<T: System<'a> + 'static>(&mut self, system: T) {
|
||||
self.runners.push(Box::new(system));
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
pub fn run(&mut self) {
|
||||
for runner in &mut self.runners {
|
||||
runner.run_world(self.world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Join<'a> {
|
||||
type Iterators;
|
||||
type Result;
|
||||
fn join_all(&'a self) -> JoinIter<'a, Self> where Self: Sized;
|
||||
fn next(iters: &mut Self::Iterators) -> Option<Self::Result>;
|
||||
}
|
||||
|
||||
impl <'a, T: Access<'a>, U: Access<'a>> Join<'a> for (T, U) {
|
||||
type Iterators = (Peekable<T::Iterator>, Peekable<U::Iterator>);
|
||||
type Result = (T::Component, U::Component);
|
||||
fn join_all(&'a self) -> JoinIter<'a, Self> {
|
||||
return JoinIter{
|
||||
iterators: (self.0.iter().peekable(), self.1.iter().peekable()),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
fn next(iters: &mut Self::Iterators) -> Option<Self::Result> {
|
||||
loop {
|
||||
let k1 = { let (k, _) = iters.0.peek()?; *k };
|
||||
let k2 = { let (k, _) = iters.1.peek()?; *k };
|
||||
if k1 == k2 {
|
||||
let (_, v1) = iters.0.next().unwrap();
|
||||
let (_, v2) = iters.1.next().unwrap();
|
||||
return Some((v1, v2));
|
||||
}
|
||||
if k1 < k2 {
|
||||
iters.0.next().unwrap();
|
||||
}
|
||||
if k2 > k1 {
|
||||
iters.1.next().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JoinIter<'a, J: Join<'a>> {
|
||||
iterators: J::Iterators,
|
||||
phantom: PhantomData<&'a J::Result>,
|
||||
}
|
||||
|
||||
impl <'a, J: Join<'a>> Iterator for JoinIter<'a, J> {
|
||||
type Item = J::Result;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
J::next(&mut self.iterators)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
component::Component,
|
||||
system,
|
||||
system::Join,
|
||||
world::{ReadData, ReadWriteData, World},
|
||||
};
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
struct Position {
|
||||
x: u32,
|
||||
y: u32,
|
||||
|
@ -89,6 +152,7 @@ mod test {
|
|||
}
|
||||
impl Component for Position {}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
struct Velocity {
|
||||
x: u32,
|
||||
y: u32,
|
||||
|
@ -100,8 +164,8 @@ mod test {
|
|||
impl<'a> system::System<'a> for Physics {
|
||||
type SystemData = (ReadWriteData<'a, Position>, ReadData<'a, Velocity>);
|
||||
|
||||
fn run(&mut self, (mut pos, vel): Self::SystemData) {
|
||||
for ((_, mut p), (_, v)) in pos.iter_mut().zip(vel.iter()) {
|
||||
fn run(&mut self, (pos, vel): Self::SystemData) {
|
||||
for (mut p, v) in (pos, vel).join_all() {
|
||||
p.x += v.x;
|
||||
p.y += v.y;
|
||||
p.z += v.z;
|
||||
|
@ -113,7 +177,7 @@ mod test {
|
|||
fn processor() {
|
||||
let mut world = World::new();
|
||||
world.new_entity().with(Velocity { x: 0, y: 0, z: 1 }).with(Position { x: 1, y: 2, z: 3 }).build();
|
||||
world.new_entity().with(Velocity { x: 0, y: 0, z: 1 }).with(Position { x: 4, y: 5, z: 6 }).build();
|
||||
world.new_entity().with(Velocity { x: 0, y: 0, z: 2 }).with(Position { x: 4, y: 5, z: 6 }).build();
|
||||
|
||||
let mut p = system::Processor {
|
||||
world: &world,
|
||||
|
@ -124,6 +188,6 @@ mod test {
|
|||
let positions = world.components::<Position>();
|
||||
assert_eq!(vec![3, 6], positions.iter().map(|(_, el)| el.z).collect::<Vec<u32>>());
|
||||
p.run();
|
||||
assert_eq!(vec![4, 7], positions.iter().map(|(_, el)| el.z).collect::<Vec<u32>>());
|
||||
assert_eq!(vec![4, 8], positions.iter().map(|(_, el)| el.z).collect::<Vec<u32>>());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,112 +1,103 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::marker::PhantomData;
|
||||
use std::iter::Iterator;
|
||||
|
||||
use crate::componentmap::{
|
||||
ComponentMap,
|
||||
ComponentMapIter,
|
||||
ComponentMapIterMut,
|
||||
};
|
||||
use crate::entity;
|
||||
use crate::component;
|
||||
|
||||
type EntityComponent = (entity::ID, Box<dyn component::Component>);
|
||||
|
||||
|
||||
pub struct ReadData<'a, T: component::Component> {
|
||||
underlying: &'a RefCell<Vec<EntityComponent>>,
|
||||
world: &'a World,
|
||||
phantom: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T: component::Component> ReadData<'a, T> {
|
||||
pub fn iter(&self) -> ReadDataIter<'a, T> {
|
||||
let cm = self.world.components.get(&component::id::<T>());
|
||||
ReadDataIter {
|
||||
underlying: Some(Ref::map(self.underlying.borrow(), |el| el.as_slice())),
|
||||
phantom: PhantomData,
|
||||
iter: cm.map(|e| e.try_iter().unwrap() ),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadDataIter<'a, T: component::Component> {
|
||||
underlying: Option<Ref<'a, [EntityComponent]>>,
|
||||
phantom: PhantomData<&'a T>,
|
||||
iter: Option<ComponentMapIter<'a>>,
|
||||
}
|
||||
|
||||
impl <'a, T: component::Component> Iterator for ReadDataIter<'a, T> {
|
||||
type Item = (entity::ID, Ref<'a, T>);
|
||||
type Item = (entity::ID, &'a T);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.underlying.as_ref().unwrap().len() == 0 {
|
||||
if self.iter.is_none() {
|
||||
return None;
|
||||
}
|
||||
let mut id: u64 = 0;
|
||||
let (head, tail) = Ref::map_split(self.underlying.take().unwrap(), |slice| {
|
||||
let (head, tail) = slice.split_first().unwrap();
|
||||
id = head.0;
|
||||
let ptr = head.1.as_ref();
|
||||
|
||||
let el = unsafe { & *(ptr as *const (dyn component::Component) as *const T) };
|
||||
return (el, tail);
|
||||
});
|
||||
self.underlying = Some(tail);
|
||||
Some((id, head))
|
||||
match self.iter.as_mut().unwrap().iter.next() {
|
||||
None => None,
|
||||
Some((eid, component)) => {
|
||||
let component = component.as_ref();
|
||||
let component = unsafe { & *(component as *const (dyn component::Component) as *const T) };
|
||||
Some((*eid, component))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadWriteData<'a, T: component::Component> {
|
||||
underlying: &'a RefCell<Vec<EntityComponent>>,
|
||||
world: &'a World,
|
||||
phantom: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T: component::Component> ReadWriteData<'a, T> {
|
||||
pub fn iter_mut(&mut self) -> ReadWriteDataIter<'a, T> {
|
||||
pub fn iter_mut(&self) -> ReadWriteDataIter<'a, T> {
|
||||
let cm = self.world.components.get(&component::id::<T>());
|
||||
ReadWriteDataIter {
|
||||
underlying: Some(RefMut::map(self.underlying.borrow_mut(), |el| el.as_mut_slice())),
|
||||
phantom: PhantomData,
|
||||
iter: cm.map(|e| e.try_iter_mut().unwrap() ),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReadWriteDataIter<'a, T: component::Component> {
|
||||
underlying: Option<RefMut<'a, [EntityComponent]>>,
|
||||
phantom: PhantomData<&'a T>,
|
||||
iter: Option<ComponentMapIterMut<'a>>,
|
||||
}
|
||||
|
||||
impl <'a, T: component::Component> Iterator for ReadWriteDataIter<'a, T> {
|
||||
type Item = (entity::ID, RefMut<'a, T>);
|
||||
type Item = (entity::ID, &'a mut T);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.underlying.as_ref().unwrap().len() == 0 {
|
||||
if self.iter.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut id: u64 = 0;
|
||||
let (head, tail) = RefMut::map_split(self.underlying.take().unwrap(), |slice| {
|
||||
let (head, tail) = slice.split_first_mut().unwrap();
|
||||
id = head.0;
|
||||
let ptr: &mut dyn component::Component = &mut (*head.1);
|
||||
|
||||
let el = unsafe {
|
||||
&mut *(ptr as *mut (dyn component::Component) as *mut T)
|
||||
};
|
||||
return (el, tail);
|
||||
});
|
||||
self.underlying = Some(tail);
|
||||
Some((id, head))
|
||||
match self.iter.as_mut().unwrap().iter.next() {
|
||||
None => None,
|
||||
Some((eid, component)) => {
|
||||
let component = component.as_mut();
|
||||
let component = unsafe { &mut *(component as *mut (dyn component::Component) as *mut T) };
|
||||
Some((*eid, component))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
entities: BTreeMap<entity::ID, entity::Entity>,
|
||||
components_by_id: BTreeMap<component::ID, RefCell<Vec<EntityComponent>>>,
|
||||
components: BTreeMap<component::ID, ComponentMap>,
|
||||
next_id: entity::ID,
|
||||
|
||||
empty: RefCell<Vec<EntityComponent>>,
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
entities: BTreeMap::new(),
|
||||
components_by_id: BTreeMap::new(),
|
||||
components: BTreeMap::new(),
|
||||
next_id: 1u64,
|
||||
empty: RefCell::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,32 +113,24 @@ impl World {
|
|||
c: Box<dyn component::Component>,
|
||||
e: entity::Entity
|
||||
) {
|
||||
let vec = self.components_by_id.entry(cid).or_insert(RefCell::new(vec!()));
|
||||
vec.borrow_mut().push((e.id(), c));
|
||||
let map = self.components.entry(cid).or_insert(ComponentMap::new());
|
||||
map.insert(e.id(), c).unwrap();
|
||||
}
|
||||
|
||||
pub fn commit(&mut self, ent: entity::Entity) {
|
||||
self.entities.insert(ent.id(), ent);
|
||||
}
|
||||
|
||||
pub fn components<'a, T: component::Component>(&'a self) -> ReadData<T> {
|
||||
let underlying = match self.components_by_id.get(&component::id::<T>()) {
|
||||
None => &self.empty,
|
||||
Some(r) => r,
|
||||
};
|
||||
pub fn components<'a, T: component::Component>(&'a self) -> ReadData<'a, T> {
|
||||
ReadData {
|
||||
underlying: underlying,
|
||||
world: self,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn components_mut<'a, T: component::Component>(&'a self) -> ReadWriteData<T> {
|
||||
let underlying = match self.components_by_id.get(&component::id::<T>()) {
|
||||
None => &self.empty,
|
||||
Some(r) => &r,
|
||||
};
|
||||
pub fn components_mut<'a, T: component::Component>(&'a self) -> ReadWriteData<'a, T> {
|
||||
ReadWriteData {
|
||||
underlying: underlying,
|
||||
world: self,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +166,6 @@ mod tests {
|
|||
|
||||
let mut named = world.components::<Name>().iter();
|
||||
let mut named2 = world.components::<Name>().iter();
|
||||
//assert_eq!(named.len(), 2);
|
||||
assert_eq!(String::from("foo"), (named.next().unwrap().1).0);
|
||||
assert_eq!(String::from("foo"), (named2.next().unwrap().1).0);
|
||||
assert_eq!(String::from("bar"), (named.next().unwrap().1).0);
|
||||
|
|
Loading…
Reference in New Issue