diff --git a/async-ecs/src/access/join.rs b/async-ecs/src/access/join.rs new file mode 100644 index 0000000..535a3e6 --- /dev/null +++ b/async-ecs/src/access/join.rs @@ -0,0 +1,238 @@ +use std::ops::{Deref, DerefMut}; + +use hibitset::{BitIter, BitSetAll, BitSetLike}; +use log::warn; + +use crate::{ + entity::{Entities, Entity, Index}, + misc::BitAnd, + resource::{Ref, RefMut, Resource}, +}; + +use super::{read::Read, write::Write}; + +pub trait Join { + type Type; + type Value; + type Mask: BitSetLike; + + fn open(self) -> (Self::Mask, Self::Value); + + fn get(value: &mut Self::Value, index: Index) -> Self::Type; + + fn join(self) -> JoinIter + where + Self: Sized, + { + JoinIter::new(self) + } + + fn maybe(self) -> MaybeJoin + where + Self: Sized, + { + MaybeJoin(self) + } + + #[inline] + fn is_unconstrained() -> bool { + false + } +} + +pub struct MaybeJoin(pub J); + +impl Join for MaybeJoin +where + T: Join, +{ + type Mask = BitSetAll; + type Type = Option<::Type>; + type Value = (::Mask, ::Value); + + fn open(self) -> (Self::Mask, Self::Value) { + let (mask, value) = self.0.open(); + + (BitSetAll, (mask, value)) + } + + fn get((mask, value): &mut Self::Value, index: Index) -> Self::Type { + if mask.contains(index) { + Some(::get(value, index)) + } else { + None + } + } + + fn is_unconstrained() -> bool { + true + } +} + +pub struct JoinIter { + keys: BitIter, + values: J::Value, +} + +impl JoinIter { + pub fn new(j: J) -> Self { + if ::is_unconstrained() { + warn!( + "`Join` possibly iterating through all indices, you might've made a join with all `MaybeJoin`s, which is unbounded in length." + ); + } + + let (keys, values) = j.open(); + + JoinIter { + keys: keys.iter(), + values, + } + } + + pub fn get(&mut self, entity: Entity, entities: &Entities) -> Option { + if self.keys.contains(entity.index()) && entities.is_alive(entity) { + Some(J::get(&mut self.values, entity.index())) + } else { + None + } + } +} + +impl std::iter::Iterator for JoinIter { + type Item = J::Type; + + fn next(&mut self) -> Option { + self.keys.next().map(|idx| J::get(&mut self.values, idx)) + } +} + +impl Clone for JoinIter +where + J::Mask: Clone, + J::Value: Clone, +{ + fn clone(&self) -> Self { + Self { + keys: self.keys.clone(), + values: self.values.clone(), + } + } +} + +macro_rules! define_tuple_join { + ($($from:ident),*) => { + impl<$($from,)*> Join for ($($from),*,) + where $($from: Join),*, + ($(<$from as Join>::Mask,)*): BitAnd, + { + type Type = ($($from::Type),*,); + type Value = ($($from::Value),*,); + type Mask = <($($from::Mask,)*) as BitAnd>::Value; + + #[allow(non_snake_case)] + fn open(self) -> (Self::Mask, Self::Value) { + let ($($from,)*) = self; + let ($($from,)*) = ($($from.open(),)*); + + ( + ($($from.0),*,).and(), + ($($from.1),*,) + ) + } + + #[allow(non_snake_case)] + fn get(v: &mut Self::Value, i: Index) -> Self::Type { + let &mut ($(ref mut $from,)*) = v; + + ($($from::get($from, i),)*) + } + + #[inline] + fn is_unconstrained() -> bool { + let mut unconstrained = true; + + $( unconstrained = unconstrained && $from::is_unconstrained(); )* + + unconstrained + } + } + } +} + +define_tuple_join! { A } +define_tuple_join! { A, B } +define_tuple_join! { A, B, C } +define_tuple_join! { A, B, C, D } +define_tuple_join! { A, B, C, D, E } +define_tuple_join! { A, B, C, D, E, F } +define_tuple_join! { A, B, C, D, E, F, G } +define_tuple_join! { A, B, C, D, E, F, G, H } +define_tuple_join! { A, B, C, D, E, F, G, H, I } +define_tuple_join! { A, B, C, D, E, F, G, H, I, J } +define_tuple_join! { A, B, C, D, E, F, G, H, I, J, K } +define_tuple_join! { A, B, C, D, E, F, G, H, I, J, K, L } +define_tuple_join! { A, B, C, D, E, F, G, H, I, J, K, L, M } +define_tuple_join! { A, B, C, D, E, F, G, H, I, J, K, L, M, N } +define_tuple_join! { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O } +define_tuple_join! { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P } + +macro_rules! define_mutable_join { + ($ty:ty) => { + impl<'a, 'b, T> Join for &'a mut $ty + where + &'a mut T: Join, + T: Resource, + { + type Type = <&'a mut T as Join>::Type; + type Value = <&'a mut T as Join>::Value; + type Mask = <&'a mut T as Join>::Mask; + + fn open(self) -> (Self::Mask, Self::Value) { + self.deref_mut().open() + } + + fn get(v: &mut Self::Value, i: Index) -> Self::Type { + <&'a mut T as Join>::get(v, i) + } + + #[inline] + fn is_unconstrained() -> bool { + <&'a mut T as Join>::is_unconstrained() + } + } + }; +} + +define_mutable_join!(Write<'b, T>); +define_mutable_join!(RefMut<'b, T>); + +macro_rules! define_immutable_join { + ($ty:ty) => { + impl<'a, 'b, T> Join for &'a $ty + where + &'a T: Join, + T: Resource, + { + type Type = <&'a T as Join>::Type; + type Value = <&'a T as Join>::Value; + type Mask = <&'a T as Join>::Mask; + + fn open(self) -> (Self::Mask, Self::Value) { + self.deref().open() + } + + fn get(v: &mut Self::Value, i: Index) -> Self::Type { + <&'a T as Join>::get(v, i) + } + + #[inline] + fn is_unconstrained() -> bool { + <&'a T as Join>::is_unconstrained() + } + } + }; +} + +define_immutable_join!(Read<'b, T>); +define_immutable_join!(Ref<'b, T>); diff --git a/async-ecs/src/access/mod.rs b/async-ecs/src/access/mod.rs index 1eb7463..65c5d6b 100644 --- a/async-ecs/src/access/mod.rs +++ b/async-ecs/src/access/mod.rs @@ -1,10 +1,12 @@ pub mod accessor; +pub mod join; pub mod read; pub mod read_storage; pub mod write; pub mod write_storage; pub use accessor::{Accessor, AccessorCow, AccessorType, StaticAccessor}; +pub use join::Join; pub use read::Read; pub use read_storage::ReadStorage; pub use write::Write; diff --git a/async-ecs/src/dispatcher/task.rs b/async-ecs/src/dispatcher/task.rs index 3761dd7..fd3fd66 100644 --- a/async-ecs/src/dispatcher/task.rs +++ b/async-ecs/src/dispatcher/task.rs @@ -11,13 +11,12 @@ pub async fn execute( ) { info!("System started: {}", &name); - run(&name, dispatchable, sender, receivers, world).await; + run(dispatchable, sender, receivers, world).await; info!("System finished: {}", &name); } async fn run( - name: &str, mut dispatchable: BoxedDispatchable, sender: Sender, mut receivers: Vec, @@ -31,8 +30,6 @@ async fn run( } } - info!("Run system: {}", &name); - let world = world.borrow(); let world = world.as_ref().unwrap(); diff --git a/async-ecs/src/lib.rs b/async-ecs/src/lib.rs index 6c088a9..4af94a8 100644 --- a/async-ecs/src/lib.rs +++ b/async-ecs/src/lib.rs @@ -11,7 +11,7 @@ pub mod storage; pub mod system; pub mod world; -pub use access::{ReadStorage, WriteStorage}; +pub use access::{Join, ReadStorage, WriteStorage}; pub use dispatcher::Dispatcher; pub use resource::Resources; pub use storage::VecStorage; diff --git a/async-ecs/src/main.rs b/async-ecs/src/main.rs index 023f935..986d8ba 100644 --- a/async-ecs/src/main.rs +++ b/async-ecs/src/main.rs @@ -2,7 +2,7 @@ use std::io::Error as IoError; use std::time::{Duration, Instant}; use async_ecs::{ - dispatcher::Error as DispatcherError, Dispatcher, ReadStorage, System, VecStorage, World, + dispatcher::Error as DispatcherError, Dispatcher, Join, ReadStorage, System, VecStorage, World, WriteStorage, }; use async_ecs_derive::Component; @@ -137,9 +137,11 @@ impl Acceleration { impl<'a> System<'a> for Move { type SystemData = (WriteStorage<'a, Position>, ReadStorage<'a, Velocity>); - fn run(&mut self, (position, velocity): Self::SystemData) { - let _position = position; - let _velocity = velocity; + fn run(&mut self, (mut position, velocity): Self::SystemData) { + for (position, velocity) in (&mut position, &velocity).join() { + position.x += velocity.x; + position.y += velocity.y; + } /* use specs::{prelude::ParallelIterator, ParJoin}; @@ -157,9 +159,11 @@ impl<'a> System<'a> for Move { impl<'a> System<'a> for Accelerate { type SystemData = (WriteStorage<'a, Velocity>, ReadStorage<'a, Acceleration>); - fn run(&mut self, (velocity, acceleration): Self::SystemData) { - let _velocity = velocity; - let _acceleration = acceleration; + fn run(&mut self, (mut velocity, acceleration): Self::SystemData) { + for (velocity, acceleration) in (&mut velocity, &acceleration).join() { + velocity.x += acceleration.x; + velocity.y += acceleration.y; + } /* use specs::{prelude::ParallelIterator, ParJoin}; diff --git a/async-ecs/src/misc/bit_and.rs b/async-ecs/src/misc/bit_and.rs new file mode 100644 index 0000000..1d20964 --- /dev/null +++ b/async-ecs/src/misc/bit_and.rs @@ -0,0 +1,55 @@ +use hibitset::{BitSetAnd, BitSetLike}; + +use crate::misc::Split; + +pub trait BitAnd { + type Value: BitSetLike; + + fn and(self) -> Self::Value; +} + +impl BitAnd for (A,) +where + A: BitSetLike, +{ + type Value = A; + + fn and(self) -> Self::Value { + self.0 + } +} + +macro_rules! bitset_and { + ($($from:ident),*) => { + impl<$($from),*> BitAnd for ($($from),*) + where $($from: BitSetLike),* + { + type Value = BitSetAnd< + <::Left as BitAnd>::Value, + <::Right as BitAnd>::Value + >; + + fn and(self) -> Self::Value { + let (l, r) = self.split(); + + BitSetAnd(l.and(), r.and()) + } + } + } +} + +bitset_and! { A, B } +bitset_and! { A, B, C } +bitset_and! { A, B, C, D } +bitset_and! { A, B, C, D, E } +bitset_and! { A, B, C, D, E, F } +bitset_and! { A, B, C, D, E, F, G } +bitset_and! { A, B, C, D, E, F, G, H } +bitset_and! { A, B, C, D, E, F, G, H, I } +bitset_and! { A, B, C, D, E, F, G, H, I, J } +bitset_and! { A, B, C, D, E, F, G, H, I, J, K } +bitset_and! { A, B, C, D, E, F, G, H, I, J, K, L } +bitset_and! { A, B, C, D, E, F, G, H, I, J, K, L, M } +bitset_and! { A, B, C, D, E, F, G, H, I, J, K, L, M, N } +bitset_and! { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O } +bitset_and! { A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P } diff --git a/async-ecs/src/misc/mod.rs b/async-ecs/src/misc/mod.rs index 5df1510..061f3c2 100644 --- a/async-ecs/src/misc/mod.rs +++ b/async-ecs/src/misc/mod.rs @@ -1,3 +1,7 @@ +pub mod bit_and; +pub mod split; pub mod try_default; +pub use bit_and::BitAnd; +pub use split::Split; pub use try_default::TryDefault; diff --git a/async-ecs/src/misc/split.rs b/async-ecs/src/misc/split.rs new file mode 100644 index 0000000..dc05a20 --- /dev/null +++ b/async-ecs/src/misc/split.rs @@ -0,0 +1,47 @@ +pub trait Split { + type Left; + type Right; + + fn split(self) -> (Self::Left, Self::Right); +} + +macro_rules! for_each_prefix ( + ($m:ident, [$(($acc:tt),)*], []) => { + $m!($($acc,)*); + }; + ($m:ident, [$(($acc:tt),)*], [($arg0:tt), $(($arg:tt),)*]) => { + $m!($($acc,)*); + for_each_prefix!($m, [$(($acc),)* ($arg0),], [$(($arg),)*]); + }; +); + +macro_rules! split_impl ( + ($(($a:ident, $b:ident),)*) => ( + impl<$($a,)* $($b,)*> Split for ($($a,)* $($b,)*) { + type Left = ($($a,)*); + type Right = ($($b,)*); + #[allow(non_snake_case)] + fn split(self) -> (Self::Left, Self::Right) { + match self { + ($($a,)* $($b,)*) => (($($a,)*), ($($b,)*)) + } + } + } + impl<$($a,)* $($b,)* TLast> Split for ($($a,)* $($b,)* TLast,) { + type Left = ($($a,)*); + type Right = ($($b,)* TLast,); + #[allow(non_snake_case)] + fn split(self) -> (Self::Left, Self::Right) { + match self { + ($($a,)* $($b,)* t_last,) => (($($a,)*), ($($b,)* t_last,)) + } + } + } + ); +); + +for_each_prefix! { + split_impl, + [], + [((T0, T1)), ((T2, T3)), ((T4, T5)), ((T6, T7)), ((T8, T9)), ((T10, T11)), ((T12, T13)), ((T14, T15)),] +} diff --git a/async-ecs/src/storage/masked_storage.rs b/async-ecs/src/storage/masked_storage.rs index f2df91e..2328aed 100644 --- a/async-ecs/src/storage/masked_storage.rs +++ b/async-ecs/src/storage/masked_storage.rs @@ -5,8 +5,8 @@ use hibitset::BitSet; use crate::{component::Component, entity::Entity, storage::Storage}; pub struct MaskedStorage { - mask: BitSet, - inner: T::Storage, + pub(crate) mask: BitSet, + pub(crate) inner: T::Storage, } impl MaskedStorage { @@ -17,6 +17,10 @@ impl MaskedStorage { } } + pub fn open_mut(&mut self) -> (&BitSet, &mut T::Storage) { + (&self.mask, &mut self.inner) + } + pub fn insert(&mut self, entity: Entity, mut component: T) -> Option { let index = entity.index(); diff --git a/async-ecs/src/storage/storage_wrapper.rs b/async-ecs/src/storage/storage_wrapper.rs index 322b18f..807121d 100644 --- a/async-ecs/src/storage/storage_wrapper.rs +++ b/async-ecs/src/storage/storage_wrapper.rs @@ -1,20 +1,27 @@ use std::marker::PhantomData; -use std::ops::DerefMut; +use std::ops::{Deref, DerefMut, Not}; + +use hibitset::{BitSet, BitSetNot}; use crate::{ + access::Join, component::Component, - entity::{Entities, Entity}, + entity::{Entities, Entity, Index}, error::Error, resource::Ref, storage::MaskedStorage, }; +use super::Storage; + pub struct StorageWrapper<'a, T, D> { data: D, entities: Ref<'a, Entities>, phantom: PhantomData, } +pub struct AntiStorage<'a>(pub &'a BitSet); + impl<'a, T, D> StorageWrapper<'a, T, D> { pub fn new(data: D, entities: Ref<'a, Entities>) -> Self { Self { @@ -37,4 +44,71 @@ where Ok(self.data.insert(entity, component)) } + + pub fn not(&self) -> AntiStorage<'_> { + AntiStorage(&self.data.mask) + } +} + +impl<'a, 'e, T, D> Not for &'a StorageWrapper<'e, T, D> +where + T: Component, + D: Deref>, +{ + type Output = AntiStorage<'a>; + + fn not(self) -> Self::Output { + AntiStorage(&self.data.mask) + } +} + +impl<'a, 'e, T, D> Join for &'a StorageWrapper<'e, T, D> +where + T: Component, + D: Deref>, +{ + type Mask = &'a BitSet; + type Type = &'a T; + type Value = &'a T::Storage; + + fn open(self) -> (Self::Mask, Self::Value) { + (&self.data.mask, &self.data.inner) + } + + fn get(v: &mut Self::Value, i: Index) -> &'a T { + v.get(i) + } +} + +impl<'a, 'e, T, D> Join for &'a mut StorageWrapper<'e, T, D> +where + T: Component, + D: DerefMut>, +{ + type Mask = &'a BitSet; + type Type = &'a mut T; + type Value = &'a mut T::Storage; + + fn open(self) -> (Self::Mask, Self::Value) { + self.data.open_mut() + } + + fn get(v: &mut Self::Value, i: Index) -> &'a mut T { + // HACK + let value: *mut Self::Value = v as *mut Self::Value; + + unsafe { (*value).get_mut(i) } + } +} + +impl<'a> Join for AntiStorage<'a> { + type Mask = BitSetNot<&'a BitSet>; + type Type = (); + type Value = (); + + fn open(self) -> (Self::Mask, ()) { + (BitSetNot(self.0), ()) + } + + fn get(_: &mut (), _: Index) {} }