summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerge Bazanski <q3k@q3k.org>2020-12-10 00:10:45 +0000
committerSerge Bazanski <q3k@q3k.org>2020-12-10 00:10:47 +0000
commit2bd393a536a4b175ac7e7e5c43563a7cf8406240 (patch)
tree57131448627875c2c80163195bec739f6f7c77ca
parent184a67d6c1b0a0c7eb59ded932ace36bfc121f0b (diff)
downloadabrasion-ecs.tar.gz
abrasion-ecs.tar.bz2
abrasion-ecs.tar.xz
abrasion-ecs.zip
ecs: implement mutable iterators and joinsecs
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaargh.
-rw-r--r--lib/ecs/BUILD1
-rw-r--r--lib/ecs/src/componentmap.rs127
-rw-r--r--lib/ecs/src/lib.rs1
-rw-r--r--lib/ecs/src/system.rs84
-rw-r--r--lib/ecs/src/world.rs102
5 files changed, 245 insertions, 70 deletions
diff --git a/lib/ecs/BUILD b/lib/ecs/BUILD
index f9170dfd..65baa5b9 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 00000000..6d006311
--- /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<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>,
+}
diff --git a/lib/ecs/src/lib.rs b/lib/ecs/src/lib.rs
index 32dd2330..dc2233a9 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 36a3b590..2ab47468 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<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>>());
}
}
diff --git a/lib/ecs/src/world.rs b/lib/ecs/src/world.rs
index 699e08cb..3a5996a7 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<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);