| @@ -0,0 +1,2 @@ | |||
| /target | |||
| Cargo.lock | |||
| @@ -0,0 +1,12 @@ | |||
| [package] | |||
| name = "async-ecs" | |||
| version = "0.1.0" | |||
| authors = ["Bergmann89 <info@bergmann89.de>"] | |||
| edition = "2018" | |||
| [dependencies] | |||
| env_logger = "0.8" | |||
| log = "0.4" | |||
| mopa = "0.2" | |||
| rand = "0.7" | |||
| thiserror = "1.0" | |||
| @@ -0,0 +1,4 @@ | |||
| pub mod resources; | |||
| pub mod system; | |||
| pub use resources::Resources; | |||
| @@ -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::<Position>(); | |||
| resources.register::<Velocity>(); | |||
| resources.register::<Acceleration>(); | |||
| 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::<f64>() - 0.5, | |||
| y: random::<f64>() - 0.5, | |||
| } | |||
| } | |||
| } | |||
| impl Velocity { | |||
| fn random() -> Self { | |||
| Self { | |||
| x: random::<f64>() - 0.5, | |||
| y: random::<f64>() - 0.5, | |||
| } | |||
| } | |||
| } | |||
| impl Acceleration { | |||
| fn random() -> Self { | |||
| Self { | |||
| x: random::<f64>() - 0.5, | |||
| y: random::<f64>() - 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; | |||
| }); | |||
| */ | |||
| } | |||
| } | |||
| */ | |||
| @@ -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<T> { | |||
| flag: AtomicUsize, | |||
| inner: UnsafeCell<T>, | |||
| } | |||
| #[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::<T>(), | |||
| $s, | |||
| ) | |||
| }}; | |||
| } | |||
| /* TrustCell */ | |||
| impl<T> TrustCell<T> { | |||
| 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<T> { | |||
| if !self.check_flag_read() { | |||
| borrow_panic!(" mutably"); | |||
| } | |||
| Ref { | |||
| flag: &self.flag, | |||
| value: unsafe { &*self.inner.get() }, | |||
| } | |||
| } | |||
| pub fn try_borrow(&self) -> Option<Ref<T>> { | |||
| if self.check_flag_read() { | |||
| Some(Ref { | |||
| flag: &self.flag, | |||
| value: unsafe { &*self.inner.get() }, | |||
| }) | |||
| } else { | |||
| None | |||
| } | |||
| } | |||
| pub fn borrow_mut(&self) -> RefMut<T> { | |||
| if !self.check_flag_write() { | |||
| borrow_panic!(""); | |||
| } | |||
| RefMut { | |||
| flag: &self.flag, | |||
| value: unsafe { &mut *self.inner.get() }, | |||
| } | |||
| } | |||
| pub fn try_borrow_mut(&self) -> Option<RefMut<T>> { | |||
| 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<U, F>(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<U, F>(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::<i32>().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::<i32>().unwrap(), &mut 2i32); | |||
| } | |||
| #[test] | |||
| fn ref_map_box() { | |||
| let cell = TrustCell::new(Box::new(10)); | |||
| let r: Ref<'_, Box<usize>> = 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<usize>> = 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<usize>> = 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<usize>> = 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); | |||
| } | |||
| } | |||
| @@ -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<ResourceId, TrustCell<Box<dyn Resource>>>, | |||
| } | |||
| #[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::<R>(), | |||
| ) | |||
| }}; | |||
| } | |||
| /* Resources */ | |||
| impl Resources { | |||
| pub fn insert<R>(&mut self, r: R) | |||
| where | |||
| R: Resource, | |||
| { | |||
| self.resources | |||
| .insert(ResourceId::new::<R>(), TrustCell::new(Box::new(r))); | |||
| } | |||
| pub fn remove<R>(&mut self) -> Option<R> | |||
| where | |||
| R: Resource, | |||
| { | |||
| self.resources | |||
| .remove(&ResourceId::new::<R>()) | |||
| .map(TrustCell::into_inner) | |||
| .map(|x: Box<dyn Resource>| x.downcast()) | |||
| .map(|x: Result<Box<R>, _>| x.ok().unwrap()) | |||
| .map(|x| *x) | |||
| } | |||
| pub fn contains<R>(&self) -> bool | |||
| where | |||
| R: Resource, | |||
| { | |||
| self.resources.contains_key(&ResourceId::new::<R>()) | |||
| } | |||
| pub fn borrow<R>(&self) -> Ref<R> | |||
| where | |||
| R: Resource, | |||
| { | |||
| self.try_borrow().unwrap_or_else(|| fetch_panic!()) | |||
| } | |||
| pub fn try_borrow<R>(&self) -> Option<Ref<R>> | |||
| where | |||
| R: Resource, | |||
| { | |||
| self.resources.get(&ResourceId::new::<R>()).map(|r| Ref { | |||
| inner: CellRef::map(r.borrow(), Box::as_ref), | |||
| phantom: PhantomData, | |||
| }) | |||
| } | |||
| pub fn borrow_mut<R>(&self) -> RefMut<R> | |||
| where | |||
| R: Resource, | |||
| { | |||
| self.try_borrow_mut().unwrap_or_else(|| fetch_panic!()) | |||
| } | |||
| pub fn try_borrow_mut<R>(&self) -> Option<RefMut<R>> | |||
| where | |||
| R: Resource, | |||
| { | |||
| self.resources.get(&ResourceId::new::<R>()).map(|r| RefMut { | |||
| inner: CellRefMut::map(r.borrow_mut(), Box::as_mut), | |||
| phantom: PhantomData, | |||
| }) | |||
| } | |||
| pub fn get_mut<R: Resource>(&mut self) -> Option<&mut R> { | |||
| self.get_resource_mut(ResourceId::new::<R>()) | |||
| .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<R>() -> Self | |||
| where | |||
| R: Resource, | |||
| { | |||
| Self(TypeId::of::<R>()) | |||
| } | |||
| } | |||
| /* Resource */ | |||
| impl<T> 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::<Res>()); | |||
| assert!(!resources.contains::<Foo>()); | |||
| } | |||
| #[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::<Res>(); | |||
| let _write = resources.borrow_mut::<Res>(); | |||
| } | |||
| #[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::<Res>(); | |||
| let _read = resources.borrow::<Res>(); | |||
| } | |||
| #[test] | |||
| fn remove_insert() { | |||
| let mut resources = Resources::default(); | |||
| resources.insert(Res); | |||
| assert!(resources.contains::<Res>()); | |||
| resources.remove::<Res>().unwrap(); | |||
| assert!(!resources.contains::<Res>()); | |||
| resources.insert(Res); | |||
| assert!(resources.contains::<Res>()); | |||
| } | |||
| } | |||
| @@ -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<Self>; | |||
| fn reads(&self) -> Vec<ResourceId>; | |||
| fn writes(&self) -> Vec<ResourceId>; | |||
| } | |||
| 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<T> { | |||
| marker: PhantomData<fn() -> T>, | |||
| } | |||
| pub type AccessorType<'a, T> = <<T as System<'a>>::SystemData as DynamicSystemData<'a>>::Accessor; | |||
| /* Accessor */ | |||
| impl Accessor for () { | |||
| fn try_new() -> Option<Self> { | |||
| None | |||
| } | |||
| fn reads(&self) -> Vec<ResourceId> { | |||
| Vec::new() | |||
| } | |||
| fn writes(&self) -> Vec<ResourceId> { | |||
| Vec::new() | |||
| } | |||
| } | |||
| impl<T> Accessor for PhantomData<T> | |||
| where | |||
| T: ?Sized, | |||
| { | |||
| fn try_new() -> Option<Self> { | |||
| None | |||
| } | |||
| fn reads(&self) -> Vec<ResourceId> { | |||
| Vec::new() | |||
| } | |||
| fn writes(&self) -> Vec<ResourceId> { | |||
| 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<T> | |||
| where | |||
| T: SystemData<'a>, | |||
| { | |||
| fn try_new() -> Option<Self> { | |||
| Some(StaticAccessor { | |||
| marker: PhantomData, | |||
| }) | |||
| } | |||
| fn reads(&self) -> Vec<ResourceId> { | |||
| T::reads() | |||
| } | |||
| fn writes(&self) -> Vec<ResourceId> { | |||
| T::writes() | |||
| } | |||
| } | |||
| @@ -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) { | |||
| <Self::SystemData as DynamicSystemData>::setup(&self.accessor(), resources) | |||
| } | |||
| fn dispose(self, resources: &mut Resources) | |||
| where | |||
| Self: Sized, | |||
| { | |||
| let _ = resources; | |||
| } | |||
| } | |||
| @@ -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<ResourceId>; | |||
| fn writes() -> Vec<ResourceId>; | |||
| } | |||
| 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<T> | |||
| where | |||
| T: ?Sized, | |||
| { | |||
| fn setup(_: &mut Resources) {} | |||
| fn fetch(_: &Resources) -> Self { | |||
| PhantomData | |||
| } | |||
| fn reads() -> Vec<ResourceId> { | |||
| vec![] | |||
| } | |||
| fn writes() -> Vec<ResourceId> { | |||
| vec![] | |||
| } | |||
| } | |||
| /* DynamicSystemData */ | |||
| impl<'a, T> DynamicSystemData<'a> for T | |||
| where | |||
| T: SystemData<'a>, | |||
| { | |||
| type Accessor = StaticAccessor<T>; | |||
| fn setup(_: &StaticAccessor<T>, resources: &mut Resources) { | |||
| T::setup(resources); | |||
| } | |||
| fn fetch(_: &StaticAccessor<T>, 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<ResourceId> { | |||
| #![allow(unused_mut)] | |||
| let mut r = Vec::new(); | |||
| $( { | |||
| let mut reads = <$ty as SystemData>::reads(); | |||
| r.append(&mut reads); | |||
| } )* | |||
| r | |||
| } | |||
| fn writes() -> Vec<ResourceId> { | |||
| #![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); | |||
| } | |||