From 2bd393a536a4b175ac7e7e5c43563a7cf8406240 Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Thu, 10 Dec 2020 00:10:45 +0000 Subject: [PATCH] ecs: implement mutable iterators and joins Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaargh. --- lib/ecs/BUILD | 1 + lib/ecs/src/componentmap.rs | 127 ++++++++++++++++++++++++++++++++++++ lib/ecs/src/lib.rs | 1 + lib/ecs/src/system.rs | 84 +++++++++++++++++++++--- lib/ecs/src/world.rs | 102 ++++++++++++----------------- 5 files changed, 245 insertions(+), 70 deletions(-) create mode 100644 lib/ecs/src/componentmap.rs diff --git a/lib/ecs/BUILD b/lib/ecs/BUILD index f9170df..65baa5b 100644 --- a/lib/ecs/BUILD +++ b/lib/ecs/BUILD @@ -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", diff --git a/lib/ecs/src/componentmap.rs b/lib/ecs/src/componentmap.rs new file mode 100644 index 0000000..6d00631 --- /dev/null +++ b/lib/ecs/src/componentmap.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, +} + +impl<'b> BorrowRef<'b> { + fn new(borrow: &'b Cell) -> Option> { + 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, +} + +impl<'b> BorrowRefMut<'b> { + fn new(borrow: &'b Cell) -> Option> { + 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>>, + borrow: Cell, +} + +#[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, 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, 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) -> 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>, + #[allow(dead_code)] + borrow: BorrowRef<'a>, +} + +pub struct ComponentMapIterMut<'a> { + pub iter: BTMIterMut<'a, entity::ID, Box>, + #[allow(dead_code)] + borrow: BorrowRefMut<'a>, +} diff --git a/lib/ecs/src/lib.rs b/lib/ecs/src/lib.rs index 32dd233..dc2233a 100644 --- a/lib/ecs/src/lib.rs +++ b/lib/ecs/src/lib.rs @@ -1,4 +1,5 @@ pub mod component; +pub mod componentmap; pub mod entity; pub mod system; pub mod world; diff --git a/lib/ecs/src/system.rs b/lib/ecs/src/system.rs index 36a3b59..2ab4746 100644 --- a/lib/ecs/src/system.rs +++ b/lib/ecs/src/system.rs @@ -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; 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 + 'static>(&mut self, system: T) { + pub fn add_system + '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; +} + +impl <'a, T: Access<'a>, U: Access<'a>> Join<'a> for (T, U) { + type Iterators = (Peekable, Peekable); + 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 { + 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 { + 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::(); assert_eq!(vec![3, 6], positions.iter().map(|(_, el)| el.z).collect::>()); p.run(); - assert_eq!(vec![4, 7], positions.iter().map(|(_, el)| el.z).collect::>()); + assert_eq!(vec![4, 8], positions.iter().map(|(_, el)| el.z).collect::>()); } } diff --git a/lib/ecs/src/world.rs b/lib/ecs/src/world.rs index 699e08c..3a5996a 100644 --- a/lib/ecs/src/world.rs +++ b/lib/ecs/src/world.rs @@ -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); - - pub struct ReadData<'a, T: component::Component> { - underlying: &'a RefCell>, + 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::()); 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>, phantom: PhantomData<&'a T>, + iter: Option>, } 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 { - 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>, + 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::()); 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>, phantom: PhantomData<&'a T>, + iter: Option>, } 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 { - 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, - components_by_id: BTreeMap>>, + components: BTreeMap, next_id: entity::ID, - - empty: RefCell>, } 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, 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 { - let underlying = match self.components_by_id.get(&component::id::()) { - 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 { - let underlying = match self.components_by_id.get(&component::id::()) { - 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::().iter(); let mut named2 = world.components::().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);