use crate::{ resource::{ResourceSet, ResourceTypeId}, schedule::{ArchetypeAccess, Schedulable}, system_fn_types::{FuncSystem, FuncSystemFnWrapper}, Access, SystemAccess, SystemId, SystemQuery, }; use bit_set::BitSet; use fxhash::FxHashMap; use legion_core::{ borrow::AtomicRefCell, command::CommandBuffer, filter::{And, EntityFilter, EntityFilterTuple}, query::{DefaultFilter, IntoQuery, View, ViewElement}, storage::ComponentTypeId, }; use std::marker::PhantomData; // TODO: add params for component access // TODO: add subworld to function parameters // TODO: somehow support filters pub trait IntoSystem<'a, CommandBuffer, Resources, Components> { fn system_id(self, id: SystemId) -> Box; fn system_named(self, name: &'static str) -> Box; fn system(self) -> Box; } macro_rules! impl_system { (($($command_buffer:ident)*), ($(($resource:ident, $resource_var:ident)),*), ($(($view:ident, $filter:ident, $view_var:ident)),*)) => { impl<'a, Func, $($resource: ResourceSet + 'static + Clone,)* $($view: for<'b> View<'b> + DefaultFilter + ViewElement, $filter: EntityFilter + Sync + 'static),* > IntoSystem<'a, tuple!($($command_buffer)*), ($($resource,)*), ($($view,)*)> for Func where Func: FnMut($(&mut $command_buffer,)* $($resource,)* $($view),*) + Send + Sync + 'static, $(<$view as View<'a>>::Iter: Iterator),* { fn system_id(mut self, id: SystemId) -> Box { let resource_access: Access = resource_access!(($($resource),*)); let component_access: Access = component_access!(($($view),*)); let run_fn = function_wrapper!(self, ($($command_buffer)*), ($($resource, $resource_var),*), ($($view, $filter, $view_var),*)); Box::new(FuncSystem { name: id, queries: AtomicRefCell::new(query!($($view),*)), access: SystemAccess { resources: resource_access, components: component_access, tags: Access::default(), }, archetypes: ArchetypeAccess::Some(BitSet::default()), _resources: PhantomData::, command_buffer: FxHashMap::default(), run_fn: AtomicRefCell::new(run_fn), }) } fn system_named(self, name: &'static str) -> Box { self.system_id(name.into()) } fn system(self) -> Box { self.system_id(std::any::type_name::().to_string().into()) } } } } macro_rules! function_wrapper { ($me:ident, ($($command_buffer:ident)*), ($($resource:ident, $resource_var:ident),*), ($($view:ident, $filter:ident, $view_var:ident),*)) => { FuncSystemFnWrapper( move |_command_buffer, _world, _resources: tuple!($($resource),*), _query: &mut system_query!($($view, $filter),*) | { let tuple!($($resource_var),*) = _resources; run_function!($me, ($(_command_buffer, $command_buffer)*), ($($resource, $resource_var),*), _world, _query, ($($view, $filter, $view_var),*)) }, PhantomData, ) }; } macro_rules! run_function { ($me:ident, ($($command_buffer_var:ident, $command_buffer:ident)*), ($($resource:ident, $resource_var:ident),*), $world:ident, $query:ident, ()) => { $me($($command_buffer_var,)*$($resource_var),*); }; ($me:ident, ($($command_buffer_var:ident, $command_buffer:ident)*), ($($resource:ident, $resource_var:ident),*), $world:ident, $query:ident, ($($view:ident, $filter:ident, $view_var:ident),+)) => { for tuple!($($view_var),*) in $query.iter_mut($world) { $me($($command_buffer_var,)*$($resource_var.clone(),)* $($view_var),*); } } } macro_rules! tuple { () => { () }; // single value: v1 ($value:ident) => { $value }; // multiple values: (v1, v2, v3) ($($value:ident),+) => { ($($value),+) } } macro_rules! component_access { (()) => {Access::default()}; (($($view:ident),+)) => {{ let mut component_access: Access = Access::default(); component_access .reads .extend(::read_types().iter()); component_access .writes .extend(::write_types().iter()); component_access }} } macro_rules! resource_access { (()) => {Access::default()}; (($($resource:ident),+)) => {{ let mut component_access: Access = Access::default(); component_access .reads .extend(::read_types().iter()); component_access .writes .extend(::write_types().iter()); component_access }} } macro_rules! system_query { () => { () }; ($view:ident, $filter:ident) => { SystemQuery< $view, $filter > }; ($($view:ident, $filter:ident),+) => { SystemQuery< ($($view),+), EntityFilterTuple< And<( $(<$filter as EntityFilter>::ArchetypeFilter),+ )>, And<( $(<$filter as EntityFilter>::ChunksetFilter),+ )>, And<( $(<$filter as EntityFilter>::ChunkFilter),+ )>, > > } } macro_rules! query { () => { () }; ($($query:ident),+) => { ::query() } } macro_rules! impl_system_variants { ($(($resource:ident, $resource_var:ident)),*) => { #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ()]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1))]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2))]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3))]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4))]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5))]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6))]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7))]; #[rustfmt::skip] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11), (V12, V12F, v12))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ()]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7))]; #[rustfmt::skip] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11))]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system![(CommandBuffer), ($(($resource, $resource_var)),*), ((V1, V1F, v1), (V2, V2F, v2), (V3, V3F, v3), (V4, V4F, v4), (V5, V5F, v5), (V6, V6F, v6), (V7, V7F, v7), (V8, V8F, v8), (V9, V9F, v9), (V10, V10F, v10), (V11, V11F, v11), (V12, V12F, v12))]; } } #[rustfmt::skip] impl_system_variants![]; #[rustfmt::skip] impl_system_variants![(R1, r1)]; #[rustfmt::skip] impl_system_variants![(R1, r1), (R2, r2)]; #[rustfmt::skip] impl_system_variants![(R1, r1), (R2, r2), (R3, r3)]; #[rustfmt::skip] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4)]; #[rustfmt::skip] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5)]; #[rustfmt::skip] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6)]; #[rustfmt::skip] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7)]; #[rustfmt::skip] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8)]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9)]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9), (R10, r10)]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9), (R10, r10), (R11, r11)]; #[rustfmt::skip] #[cfg(feature = "more-system-fns")] impl_system_variants![(R1, r1), (R2, r2), (R3, r3), (R4, r4), (R5, r5), (R6, r6), (R7, r7), (R8, r8), (R9, r9), (R10, r10), (R11, r11), (R12, r12)]; #[cfg(test)] mod tests { use crate::{ resource::Resources, system_fn_types::{Resource, ResourceMut}, IntoSystem, }; use legion_core::{ borrow::{Ref, RefMut}, command::CommandBuffer, world::World, }; #[derive(Debug, Eq, PartialEq)] struct A(usize); #[derive(Debug, Eq, PartialEq)] struct B(usize); #[derive(Debug, Eq, PartialEq)] struct Y(usize); #[derive(Debug, Eq, PartialEq)] struct X(usize); #[test] fn test_into_system() { let mut world = World::new(); let mut resources = Resources::default(); resources.insert(A(0)); world.insert((), vec![(X(1), Y(1)), (X(2), Y(2))]); fn single_read_system(x: Ref) { println!("{}", x.0); } let mut system = single_read_system.system(); system.run(&mut world, &mut resources); fn read_write_system(x: Ref, y: Ref, mut z: RefMut) { z.0 += 1; println!("{} {} {}", x.0, y.0, z.0); } ({ |x: Resource, y: Ref, mut z: RefMut| { z.0 += 1; println!("{} {} {}", x.0, y.0, z.0); } }) .system(); let mut system = read_write_system.system(); system.run(&mut world, &mut resources); fn resource_system(a: Resource, x: Ref, y: Ref) { println!("{} {} {}", a.0, x.0, y.0); } let mut system = resource_system.system(); system.run(&mut world, &mut resources); fn empty_system_mut() { println!("hello world"); } let mut system = empty_system_mut.system(); system.run(&mut world, &mut resources); fn resource_system_mut(mut a: ResourceMut, x: Ref, y: Ref) { a.0 += 1; println!("{} {} {}", a.0, x.0, y.0); } let mut system = resource_system_mut.system(); system.run(&mut world, &mut resources); fn command_buffer_system(command_buffer: &mut CommandBuffer, mut a: ResourceMut) { a.0 += 1; command_buffer.insert((), vec![(X(1), Y(1)), (X(2), Y(2))]); println!("{}", a.0); } let mut system = command_buffer_system.system(); system.run(&mut world, &mut resources); } #[test] fn test_resource_system_fn() { fn my_system(mut a: ResourceMut, x: Ref, mut y: RefMut) { if a.0 == 0 { assert_eq!(*a, A(0)); assert_eq!(*x, X(2)); assert_eq!(*y, Y(3)); } else if a.0 == 1 { assert_eq!(*a, A(1)); assert_eq!(*x, X(4)); assert_eq!(*y, Y(5)); y.0 += 1; assert_eq!(*y, Y(6)); } else { panic!("unexpected value"); } a.0 += 1; } let mut world = World::new(); let mut resources = Resources::default(); resources.insert(A(0)); resources.insert(B(1)); world.insert((), vec![(X(2), Y(3)), (X(4), Y(5))]); let mut my_system = my_system.system(); my_system.run(&mut world, &mut resources); } }