From b47681075360058a6ef9c7f4bc6fba7d5a8edac3 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Tue, 20 Oct 2020 23:34:13 +0200 Subject: [PATCH] Initial Commit --- .gitignore | 2 + Cargo.toml | 12 + src/lib.rs | 4 + src/main.rs | 152 ++++++++++++ src/resources/cell.rs | 490 ++++++++++++++++++++++++++++++++++++++ src/resources/mod.rs | 246 +++++++++++++++++++ src/system/accessor.rs | 105 ++++++++ src/system/mod.rs | 32 +++ src/system/system_data.rs | 140 +++++++++++ 9 files changed, 1183 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/resources/cell.rs create mode 100644 src/resources/mod.rs create mode 100644 src/system/accessor.rs create mode 100644 src/system/mod.rs create mode 100644 src/system/system_data.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9cc00ef --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "async-ecs" +version = "0.1.0" +authors = ["Bergmann89 "] +edition = "2018" + +[dependencies] +env_logger = "0.8" +log = "0.4" +mopa = "0.2" +rand = "0.7" +thiserror = "1.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..3647440 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub mod resources; +pub mod system; + +pub use resources::Resources; diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0b4a5bf --- /dev/null +++ b/src/main.rs @@ -0,0 +1,152 @@ +use log::info; + +/* +use std::time::{Duration, Instant}; + +use async_ecs::Resources; +use rand::random; +*/ + +fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .format_timestamp_nanos() + .init(); + + info!("Application started!"); + + /* + let mut resources = Resources::new(); + resources.register::(); + resources.register::(); + resources.register::(); + + for _ in 0..ENTITY_COUNT { + resources + .create_entity() + .with(Velocity::random()) + .with(Position::random()) + .with(Acceleration::random()) + .build(); + } + + info!("World initialized!"); + + let mut dispatcher = DispatcherBuilder::new() + .with(Move, "move", &[]) + .with(Accelerate, "accelerate", &["move"]) + .build(); + dispatcher.setup(&mut world); + + info!("Setup done!"); + + let mut delta = Duration::from_secs(0); + for _ in 0..REPEAT_COUNT { + info!("Start iteration"); + + let start = Instant::now(); + + dispatcher.dispatch(&world); + + let end = Instant::now(); + + info!("End iteration"); + + delta += end - start; + } + + let delta = delta / REPEAT_COUNT; + + info!("Average time per dispatch: {:?}", delta); + */ +} + +/* +const ENTITY_COUNT: usize = 3_000_000; +const REPEAT_COUNT: u32 = 100; + +#[derive(Debug, Component)] +#[storage(VecStorage)] +struct Position { + x: f64, + y: f64, +} + +#[derive(Debug, Component)] +#[storage(VecStorage)] +struct Velocity { + x: f64, + y: f64, +} + +#[derive(Debug, Component)] +#[storage(VecStorage)] +struct Acceleration { + x: f64, + y: f64, +} + +struct Move; +struct Accelerate; + +impl Position { + fn random() -> Self { + Self { + x: random::() - 0.5, + y: random::() - 0.5, + } + } +} + +impl Velocity { + fn random() -> Self { + Self { + x: random::() - 0.5, + y: random::() - 0.5, + } + } +} + +impl Acceleration { + fn random() -> Self { + Self { + x: random::() - 0.5, + y: random::() - 0.5, + } + } +} + +impl<'a> System<'a> for Move { + type SystemData = (WriteStorage<'a, Position>, ReadStorage<'a, Velocity>); + + fn run(&mut self, (mut position, velocity): Self::SystemData) { + /* + use specs::{prelude::ParallelIterator, ParJoin}; + + (&mut position, &velocity) + .par_join() + .for_each(|(position, velocity)| { + position.x += velocity.x; + position.y += velocity.y; + }); + */ + } +} + +impl<'a> System<'a> for Accelerate { + type SystemData = (WriteStorage<'a, Velocity>, ReadStorage<'a, Acceleration>); + + fn run(&mut self, (mut velocity, acceleration): Self::SystemData) { + /* + use specs::{prelude::ParallelIterator, ParJoin}; + + (&mut velocity, &acceleration) + .par_join() + .for_each(|(velocity, acceleration)| { + velocity.x += acceleration.x; + velocity.y += acceleration.y; + }); + */ + } +} +*/ diff --git a/src/resources/cell.rs b/src/resources/cell.rs new file mode 100644 index 0000000..e2beab6 --- /dev/null +++ b/src/resources/cell.rs @@ -0,0 +1,490 @@ +use std::cell::UnsafeCell; +use std::mem::forget; +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +#[derive(Debug)] +pub struct TrustCell { + flag: AtomicUsize, + inner: UnsafeCell, +} + +#[derive(Debug)] +pub struct Ref<'a, T> +where + T: ?Sized + 'a, +{ + flag: &'a AtomicUsize, + value: &'a T, +} + +#[derive(Debug)] +pub struct RefMut<'a, T> +where + T: ?Sized + 'a, +{ + flag: &'a AtomicUsize, + value: &'a mut T, +} + +macro_rules! borrow_panic { + ($s:expr) => {{ + panic!( + "Tried to fetch data of type {:?}, but it was already borrowed{}.", + ::std::any::type_name::(), + $s, + ) + }}; +} + +/* TrustCell */ + +impl TrustCell { + pub fn new(inner: T) -> Self { + TrustCell { + flag: AtomicUsize::new(0), + inner: UnsafeCell::new(inner), + } + } + + pub fn into_inner(self) -> T { + self.inner.into_inner() + } + + pub fn borrow(&self) -> Ref { + if !self.check_flag_read() { + borrow_panic!(" mutably"); + } + + Ref { + flag: &self.flag, + value: unsafe { &*self.inner.get() }, + } + } + + pub fn try_borrow(&self) -> Option> { + if self.check_flag_read() { + Some(Ref { + flag: &self.flag, + value: unsafe { &*self.inner.get() }, + }) + } else { + None + } + } + + pub fn borrow_mut(&self) -> RefMut { + if !self.check_flag_write() { + borrow_panic!(""); + } + + RefMut { + flag: &self.flag, + value: unsafe { &mut *self.inner.get() }, + } + } + + pub fn try_borrow_mut(&self) -> Option> { + if self.check_flag_write() { + Some(RefMut { + flag: &self.flag, + value: unsafe { &mut *self.inner.get() }, + }) + } else { + None + } + } + + pub fn get_mut(&mut self) -> &mut T { + unsafe { &mut *self.inner.get() } + } + + fn check_flag_read(&self) -> bool { + loop { + let val = self.flag.load(Ordering::Acquire); + + if val == usize::MAX { + return false; + } + + if self.flag.compare_and_swap(val, val + 1, Ordering::AcqRel) == val { + return true; + } + } + } + + fn check_flag_write(&self) -> bool { + match self.flag.compare_and_swap(0, usize::MAX, Ordering::AcqRel) { + 0 => true, + _ => false, + } + } +} + +/* Ref */ + +impl<'a, T> Ref<'a, T> +where + T: ?Sized, +{ + pub fn map(self, f: F) -> Ref<'a, U> + where + F: FnOnce(&T) -> &U, + U: ?Sized, + { + let flag = unsafe { &*(self.flag as *const _) }; + let value = unsafe { &*(self.value as *const _) }; + + forget(self); + + Ref { + flag, + value: f(value), + } + } +} + +impl<'a, T> Deref for Ref<'a, T> +where + T: ?Sized, +{ + type Target = T; + + fn deref(&self) -> &T { + self.value + } +} + +impl<'a, T> Drop for Ref<'a, T> +where + T: ?Sized, +{ + fn drop(&mut self) { + self.flag.fetch_sub(1, Ordering::Release); + } +} + +impl<'a, T> Clone for Ref<'a, T> +where + T: ?Sized, +{ + fn clone(&self) -> Self { + self.flag.fetch_add(1, Ordering::Release); + + Ref { + flag: self.flag, + value: self.value, + } + } +} + +/* RefMut */ + +impl<'a, T> RefMut<'a, T> +where + T: ?Sized, +{ + pub fn map(self, f: F) -> RefMut<'a, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + let flag = unsafe { &*(self.flag as *const _) }; + let value = unsafe { &mut *(self.value as *mut _) }; + + forget(self); + + RefMut { + flag, + value: f(value), + } + } +} + +impl<'a, T> Deref for RefMut<'a, T> +where + T: ?Sized, +{ + type Target = T; + + fn deref(&self) -> &T { + self.value + } +} + +impl<'a, T> DerefMut for RefMut<'a, T> +where + T: ?Sized, +{ + fn deref_mut(&mut self) -> &mut T { + self.value + } +} + +impl<'a, T> Drop for RefMut<'a, T> +where + T: ?Sized, +{ + fn drop(&mut self) { + self.flag.store(0, Ordering::Release) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn allow_multiple_reads() { + let cell = TrustCell::new(5); + + let a = cell.borrow(); + let b = cell.borrow(); + + assert_eq!(10, *a + *b); + } + + #[test] + fn allow_clone_reads() { + let cell = TrustCell::new(5); + + let a = cell.borrow(); + let b = a.clone(); + + assert_eq!(10, *a + *b); + } + + #[test] + fn allow_single_write() { + let cell = TrustCell::new(5); + + { + let mut a = cell.borrow_mut(); + *a += 2; + *a += 3; + } + + assert_eq!(10, *cell.borrow()); + } + + #[test] + #[should_panic(expected = "but it was already borrowed mutably")] + fn panic_write_and_read() { + let cell = TrustCell::new(5); + + let mut a = cell.borrow_mut(); + *a = 7; + + assert_eq!(7, *cell.borrow()); + } + + #[test] + #[should_panic(expected = "but it was already borrowed")] + fn panic_write_and_write() { + let cell = TrustCell::new(5); + + let mut a = cell.borrow_mut(); + *a = 7; + + assert_eq!(7, *cell.borrow_mut()); + } + + #[test] + #[should_panic(expected = "but it was already borrowed")] + fn panic_read_and_write() { + let cell = TrustCell::new(5); + + let _a = cell.borrow(); + + assert_eq!(7, *cell.borrow_mut()); + } + + #[test] + fn try_write_and_read() { + let cell = TrustCell::new(5); + + let mut a = cell.try_borrow_mut().unwrap(); + *a = 7; + + assert!(cell.try_borrow().is_none()); + + *a = 8; + } + + #[test] + fn try_write_and_write() { + let cell = TrustCell::new(5); + + let mut a = cell.try_borrow_mut().unwrap(); + *a = 7; + + assert!(cell.try_borrow_mut().is_none()); + + *a = 8; + } + + #[test] + fn try_read_and_write() { + let cell = TrustCell::new(5); + + let _a = cell.try_borrow().unwrap(); + + assert!(cell.try_borrow_mut().is_none()); + } + + #[test] + fn cloned_borrow_does_not_allow_write() { + let cell = TrustCell::new(5); + + let a = cell.borrow(); + let b = a.clone(); + + drop(a); + + assert!(cell.try_borrow_mut().is_none()); + assert_eq!(5, *b); + } + + #[test] + fn ref_with_non_sized() { + let r: Ref<'_, [i32]> = Ref { + flag: &AtomicUsize::new(1), + value: &[2, 3, 4, 5][..], + }; + + assert_eq!(&*r, &[2, 3, 4, 5][..]); + } + + #[test] + fn ref_with_non_sized_clone() { + let r: Ref<'_, [i32]> = Ref { + flag: &AtomicUsize::new(1), + value: &[2, 3, 4, 5][..], + }; + let rr = r.clone(); + + assert_eq!(&*r, &[2, 3, 4, 5][..]); + assert_eq!(r.flag.load(Ordering::SeqCst), 2); + + assert_eq!(&*rr, &[2, 3, 4, 5][..]); + assert_eq!(rr.flag.load(Ordering::SeqCst), 2); + } + + #[test] + fn ref_with_trait_obj() { + let ra: Ref<'_, dyn std::any::Any> = Ref { + flag: &AtomicUsize::new(1), + value: &2i32, + }; + + assert_eq!(ra.downcast_ref::().unwrap(), &2i32); + } + + #[test] + fn ref_mut_with_non_sized() { + let mut r: RefMut<'_, [i32]> = RefMut { + flag: &AtomicUsize::new(1), + value: &mut [2, 3, 4, 5][..], + }; + + assert_eq!(&mut *r, &mut [2, 3, 4, 5][..]); + } + + #[test] + fn ref_mut_with_trait_obj() { + let mut ra: RefMut<'_, dyn std::any::Any> = RefMut { + flag: &AtomicUsize::new(1), + value: &mut 2i32, + }; + + assert_eq!(ra.downcast_mut::().unwrap(), &mut 2i32); + } + + #[test] + fn ref_map_box() { + let cell = TrustCell::new(Box::new(10)); + + let r: Ref<'_, Box> = cell.borrow(); + assert_eq!(&**r, &10); + + let rr: Ref<'_, usize> = cell.borrow().map(Box::as_ref); + assert_eq!(&*rr, &10); + } + + #[test] + fn ref_map_preserves_flag() { + let cell = TrustCell::new(Box::new(10)); + + let r: Ref<'_, Box> = cell.borrow(); + assert_eq!(cell.flag.load(Ordering::SeqCst), 1); + let _nr: Ref<'_, usize> = r.map(Box::as_ref); + assert_eq!(cell.flag.load(Ordering::SeqCst), 1); + } + + #[test] + fn ref_map_retains_borrow() { + let cell = TrustCell::new(Box::new(10)); + + let _r: Ref<'_, usize> = cell.borrow().map(Box::as_ref); + assert_eq!(cell.flag.load(Ordering::SeqCst), 1); + + let _rr: Ref<'_, usize> = cell.borrow().map(Box::as_ref); + assert_eq!(cell.flag.load(Ordering::SeqCst), 2); + } + + #[test] + fn ref_map_drops_borrow() { + let cell = TrustCell::new(Box::new(10)); + + let r: Ref<'_, usize> = cell.borrow().map(Box::as_ref); + + assert_eq!(cell.flag.load(Ordering::SeqCst), 1); + drop(r); + assert_eq!(cell.flag.load(Ordering::SeqCst), 0); + } + + #[test] + fn ref_mut_map_box() { + let cell = TrustCell::new(Box::new(10)); + + { + let mut r: RefMut<'_, Box> = cell.borrow_mut(); + assert_eq!(&mut **r, &mut 10); + } + { + let mut rr: RefMut<'_, usize> = cell.borrow_mut().map(Box::as_mut); + assert_eq!(&mut *rr, &mut 10); + } + } + + #[test] + fn ref_mut_map_preserves_flag() { + let cell = TrustCell::new(Box::new(10)); + + let r: RefMut<'_, Box> = cell.borrow_mut(); + assert_eq!(cell.flag.load(Ordering::SeqCst), std::usize::MAX); + let _nr: RefMut<'_, usize> = r.map(Box::as_mut); + assert_eq!(cell.flag.load(Ordering::SeqCst), std::usize::MAX); + } + + #[test] + #[should_panic(expected = "but it was already borrowed")] + fn ref_mut_map_retains_mut_borrow() { + let cell = TrustCell::new(Box::new(10)); + + let _rr: RefMut<'_, usize> = cell.borrow_mut().map(Box::as_mut); + + let _ = cell.borrow_mut(); + } + + #[test] + fn ref_mut_map_drops_borrow() { + let cell = TrustCell::new(Box::new(10)); + + let r: RefMut<'_, usize> = cell.borrow_mut().map(Box::as_mut); + + assert_eq!(cell.flag.load(Ordering::SeqCst), std::usize::MAX); + drop(r); + assert_eq!(cell.flag.load(Ordering::SeqCst), 0); + } +} diff --git a/src/resources/mod.rs b/src/resources/mod.rs new file mode 100644 index 0000000..dfde10c --- /dev/null +++ b/src/resources/mod.rs @@ -0,0 +1,246 @@ +pub mod cell; + +use std::any::TypeId; +use std::collections::HashMap; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; + +use mopa::Any; + +use cell::{Ref as CellRef, RefMut as CellRefMut, TrustCell}; + +#[derive(Default)] +pub struct Resources { + resources: HashMap>>, +} + +#[derive(Debug, Hash, Eq, PartialEq)] +pub struct ResourceId(TypeId); + +pub trait Resource: Any + Send + Sync + 'static {} + +pub struct Ref<'a, R: 'a> { + inner: CellRef<'a, dyn Resource>, + phantom: PhantomData<&'a R>, +} + +pub struct RefMut<'a, R: 'a> { + inner: CellRefMut<'a, dyn Resource>, + phantom: PhantomData<&'a R>, +} + +macro_rules! fetch_panic { + () => {{ + panic!( + "\ + Tried to fetch resource from the resources map, but the resource does not exist.\n\ +\n\ + Resource: `{resource_name_full}`\n\ +\n\ + You may ensure the resource exists!\ + ", + resource_name_full = std::any::type_name::(), + ) + }}; +} + +/* Resources */ + +impl Resources { + pub fn insert(&mut self, r: R) + where + R: Resource, + { + self.resources + .insert(ResourceId::new::(), TrustCell::new(Box::new(r))); + } + + pub fn remove(&mut self) -> Option + where + R: Resource, + { + self.resources + .remove(&ResourceId::new::()) + .map(TrustCell::into_inner) + .map(|x: Box| x.downcast()) + .map(|x: Result, _>| x.ok().unwrap()) + .map(|x| *x) + } + + pub fn contains(&self) -> bool + where + R: Resource, + { + self.resources.contains_key(&ResourceId::new::()) + } + + pub fn borrow(&self) -> Ref + where + R: Resource, + { + self.try_borrow().unwrap_or_else(|| fetch_panic!()) + } + + pub fn try_borrow(&self) -> Option> + where + R: Resource, + { + self.resources.get(&ResourceId::new::()).map(|r| Ref { + inner: CellRef::map(r.borrow(), Box::as_ref), + phantom: PhantomData, + }) + } + + pub fn borrow_mut(&self) -> RefMut + where + R: Resource, + { + self.try_borrow_mut().unwrap_or_else(|| fetch_panic!()) + } + + pub fn try_borrow_mut(&self) -> Option> + where + R: Resource, + { + self.resources.get(&ResourceId::new::()).map(|r| RefMut { + inner: CellRefMut::map(r.borrow_mut(), Box::as_mut), + phantom: PhantomData, + }) + } + + pub fn get_mut(&mut self) -> Option<&mut R> { + self.get_resource_mut(ResourceId::new::()) + .map(|res| res.downcast_mut().unwrap()) + } + + pub fn get_resource_mut(&mut self, id: ResourceId) -> Option<&mut dyn Resource> { + self.resources + .get_mut(&id) + .map(TrustCell::get_mut) + .map(Box::as_mut) + } +} + +/* ResourceId */ + +impl ResourceId { + pub fn new() -> Self + where + R: Resource, + { + Self(TypeId::of::()) + } +} + +/* Resource */ + +impl Resource for T where T: Any + Send + Sync {} + +mod __resource_mopafy_scope { + #![allow(clippy::all)] + + use mopa::mopafy; + + use super::Resource; + + mopafy!(Resource); +} + +/* Ref */ + +impl<'a, R> Deref for Ref<'a, R> +where + R: Resource, +{ + type Target = R; + + fn deref(&self) -> &R { + unsafe { self.inner.downcast_ref_unchecked() } + } +} + +impl<'a, R> Clone for Ref<'a, R> { + fn clone(&self) -> Self { + Ref { + inner: self.inner.clone(), + phantom: PhantomData, + } + } +} + +/* RefMut */ + +impl<'a, R> Deref for RefMut<'a, R> +where + R: Resource, +{ + type Target = R; + + fn deref(&self) -> &R { + unsafe { self.inner.downcast_ref_unchecked() } + } +} + +impl<'a, R> DerefMut for RefMut<'a, R> +where + R: Resource, +{ + fn deref_mut(&mut self) -> &mut R { + unsafe { self.inner.downcast_mut_unchecked() } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Default)] + struct Res; + + #[test] + fn insert() { + struct Foo; + + let mut resources = Resources::default(); + resources.insert(Res); + + assert!(resources.contains::()); + assert!(!resources.contains::()); + } + + #[test] + #[should_panic(expected = "but it was already borrowed")] + fn read_write_fails() { + let mut resources = Resources::default(); + resources.insert(Res); + + let _read = resources.borrow::(); + let _write = resources.borrow_mut::(); + } + + #[test] + #[should_panic(expected = "but it was already borrowed mutably")] + fn write_read_fails() { + let mut resources = Resources::default(); + resources.insert(Res); + + let _write = resources.borrow_mut::(); + let _read = resources.borrow::(); + } + + #[test] + fn remove_insert() { + let mut resources = Resources::default(); + resources.insert(Res); + + assert!(resources.contains::()); + + resources.remove::().unwrap(); + + assert!(!resources.contains::()); + + resources.insert(Res); + + assert!(resources.contains::()); + } +} diff --git a/src/system/accessor.rs b/src/system/accessor.rs new file mode 100644 index 0000000..cba76fd --- /dev/null +++ b/src/system/accessor.rs @@ -0,0 +1,105 @@ +use std::marker::PhantomData; +use std::ops::Deref; + +use crate::resources::ResourceId; + +use super::{DynamicSystemData, System, SystemData}; + +pub trait Accessor: Sized { + fn try_new() -> Option; + + fn reads(&self) -> Vec; + + fn writes(&self) -> Vec; +} + +pub enum AccessorCow<'a, 'b, T> +where + AccessorType<'a, T>: 'b, + T: System<'a> + ?Sized, + 'a: 'b, +{ + /// A reference to an accessor. + Ref(&'b AccessorType<'a, T>), + /// An owned accessor. + Owned(AccessorType<'a, T>), +} + +#[derive(Default)] +pub struct StaticAccessor { + marker: PhantomData T>, +} + +pub type AccessorType<'a, T> = <>::SystemData as DynamicSystemData<'a>>::Accessor; + +/* Accessor */ + +impl Accessor for () { + fn try_new() -> Option { + None + } + + fn reads(&self) -> Vec { + Vec::new() + } + + fn writes(&self) -> Vec { + Vec::new() + } +} + +impl Accessor for PhantomData +where + T: ?Sized, +{ + fn try_new() -> Option { + None + } + + fn reads(&self) -> Vec { + Vec::new() + } + + fn writes(&self) -> Vec { + Vec::new() + } +} + +/* AccessorCow */ + +impl<'a, 'b, T> Deref for AccessorCow<'a, 'b, T> +where + AccessorType<'a, T>: 'b, + T: System<'a> + ?Sized + 'b, + 'a: 'b, +{ + type Target = AccessorType<'a, T>; + + fn deref(&self) -> &AccessorType<'a, T> { + match *self { + AccessorCow::Ref(r) => &*r, + AccessorCow::Owned(ref o) => o, + } + } +} + +/* StaticAccessor */ + +impl<'a, T> Accessor for StaticAccessor +where + T: SystemData<'a>, +{ + fn try_new() -> Option { + Some(StaticAccessor { + marker: PhantomData, + }) + } + + fn reads(&self) -> Vec { + T::reads() + } + + fn writes(&self) -> Vec { + T::writes() + } +} diff --git a/src/system/mod.rs b/src/system/mod.rs new file mode 100644 index 0000000..f83de83 --- /dev/null +++ b/src/system/mod.rs @@ -0,0 +1,32 @@ +mod accessor; +mod system_data; + +pub use accessor::{Accessor, AccessorCow}; +pub use system_data::{DynamicSystemData, SystemData}; + +use crate::resources::Resources; + +use accessor::AccessorType; + +pub trait System<'a> { + type SystemData: DynamicSystemData<'a>; + + fn run(&mut self, data: Self::SystemData); + + fn accessor<'b>(&'b self) -> AccessorCow<'a, 'b, Self> { + AccessorCow::Owned( + AccessorType::<'a, Self>::try_new().expect("Missing implementation for `accessor`"), + ) + } + + fn setup(&mut self, resources: &mut Resources) { + ::setup(&self.accessor(), resources) + } + + fn dispose(self, resources: &mut Resources) + where + Self: Sized, + { + let _ = resources; + } +} diff --git a/src/system/system_data.rs b/src/system/system_data.rs new file mode 100644 index 0000000..a4f4c05 --- /dev/null +++ b/src/system/system_data.rs @@ -0,0 +1,140 @@ +use std::marker::PhantomData; + +use crate::resources::{ResourceId, Resources}; + +use super::accessor::{Accessor, StaticAccessor}; + +pub trait SystemData<'a> { + fn setup(world: &mut Resources); + + fn fetch(world: &'a Resources) -> Self; + + fn reads() -> Vec; + + fn writes() -> Vec; +} + +pub trait DynamicSystemData<'a> { + type Accessor: Accessor; + + fn setup(accessor: &Self::Accessor, resources: &mut Resources); + + fn fetch(access: &Self::Accessor, resources: &'a Resources) -> Self; +} + +/* SystemData */ + +impl<'a, T> SystemData<'a> for PhantomData +where + T: ?Sized, +{ + fn setup(_: &mut Resources) {} + + fn fetch(_: &Resources) -> Self { + PhantomData + } + + fn reads() -> Vec { + vec![] + } + + fn writes() -> Vec { + vec![] + } +} + +/* DynamicSystemData */ + +impl<'a, T> DynamicSystemData<'a> for T +where + T: SystemData<'a>, +{ + type Accessor = StaticAccessor; + + fn setup(_: &StaticAccessor, resources: &mut Resources) { + T::setup(resources); + } + + fn fetch(_: &StaticAccessor, resources: &'a Resources) -> Self { + T::fetch(resources) + } +} + +mod impl_system_data { + use super::*; + + macro_rules! impl_system_data { + ( $($ty:ident),* ) => { + impl<'a, $($ty),*> SystemData<'a> for ( $( $ty , )* ) + where $( $ty : SystemData<'a> ),* + { + fn setup(resources: &mut Resources) { + #![allow(unused_variables)] + + $( + <$ty as SystemData>::setup(&mut *resources); + )* + } + + fn fetch(resources: &'a Resources) -> Self { + #![allow(unused_variables)] + + ( $( <$ty as SystemData<'a>>::fetch(resources), )* ) + } + + fn reads() -> Vec { + #![allow(unused_mut)] + + let mut r = Vec::new(); + + $( { + let mut reads = <$ty as SystemData>::reads(); + r.append(&mut reads); + } )* + + r + } + + fn writes() -> Vec { + #![allow(unused_mut)] + + let mut r = Vec::new(); + + $( { + let mut writes = <$ty as SystemData>::writes(); + r.append(&mut writes); + } )* + + r + } + } + }; + } + + impl_system_data!(A); + impl_system_data!(A, B); + impl_system_data!(A, B, C); + impl_system_data!(A, B, C, D); + impl_system_data!(A, B, C, D, E); + impl_system_data!(A, B, C, D, E, F); + impl_system_data!(A, B, C, D, E, F, G); + impl_system_data!(A, B, C, D, E, F, G, H); + impl_system_data!(A, B, C, D, E, F, G, H, I); + impl_system_data!(A, B, C, D, E, F, G, H, I, J); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y); + impl_system_data!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); +}