| @@ -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); | |||||
| } | |||||