diff options
| author | HampusM <hampus@hampusmat.com> | 2024-02-25 23:25:03 +0100 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2024-02-26 19:54:00 +0100 | 
| commit | 1019924a29527eba2c8ec8bd976ece6ed76075b0 (patch) | |
| tree | b26d8bd872684a0c87802e057d8952c42be31f7a /ecs/src/system.rs | |
| parent | 00055d6af92d59a86eb00f110c77c699a562d33e (diff) | |
feat(ecs): add support for multiple system queries & local components
Diffstat (limited to 'ecs/src/system.rs')
| -rw-r--r-- | ecs/src/system.rs | 226 | 
1 files changed, 193 insertions, 33 deletions
| diff --git a/ecs/src/system.rs b/ecs/src/system.rs index 22e2215..d90e0a2 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -1,16 +1,21 @@ -use std::any::Any; +use std::any::{Any, TypeId};  use std::convert::Infallible;  use std::fmt::Debug; +use std::mem::{transmute_copy, ManuallyDrop}; +use std::ptr::addr_of_mut; -use crate::component::Sequence as ComponentSequence; -use crate::{ComponentStorage, Query}; +use seq_macro::seq; + +use crate::component::Component; +use crate::system::util::check_params_are_compatible; +use crate::ComponentStorage;  pub mod stateful; +mod util; +  pub trait System<Impl>: 'static  { -    type Query<'a>; -      type Input;      #[must_use] @@ -19,39 +24,90 @@ pub trait System<Impl>: 'static      fn run(&mut self, component_storage: &mut ComponentStorage);      fn into_type_erased(self) -> TypeErased; -} -impl<Func, Comps> System<fn(Query<Comps>)> for Func -where -    Func: Fn(Query<Comps>) + 'static, -    Comps: ComponentSequence, -{ -    type Input = Infallible; -    type Query<'a> = Query<'a, Comps>; +    fn get_local_component_mut<LocalComponent: Component>( +        &mut self, +    ) -> Option<&mut LocalComponent>; -    fn initialize(self, _input: Self::Input) -> Self -    { -        self -    } - -    fn run(&mut self, component_storage: &mut ComponentStorage) -    { -        self(Query::new(component_storage)); -    } +    fn set_local_component<LocalComponent: Component>( +        &mut self, +        local_component: LocalComponent, +    ); +} -    fn into_type_erased(self) -> TypeErased -    { -        TypeErased { -            data: Box::new(self), -            func: Box::new(|data, component_storage| { -                let me = data.downcast_mut::<Func>().unwrap(); - -                me.run(component_storage); -            }), -        } -    } +macro_rules! impl_system { +    ($c: tt) => { +        seq!(I in 0..=$c { +            impl<'world, Func, #(TParam~I,)*> System<fn(#(TParam~I,)*)> +                for Func +            where +                Func: Fn(#(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world, Flags = NoInitParamFlag>,)* +            { +                type Input = Infallible; + +                fn initialize(self, _input: Self::Input) -> Self +                { +                    self +                } + +                fn run(&mut self, component_storage: &mut ComponentStorage) +                { +                    #( +                        check_params_are_compatible!(I, TParam~I, $c); +                    )* + +                    let func = *self; + +                    func(#({ +                        // SAFETY: All parameters are compatible so this is fine +                        let this = unsafe { +                            &mut *addr_of_mut!(*self) +                        }; + +                        // SAFETY: All parameters are compatible so this is fine +                        let component_storage = unsafe { +                            &mut *addr_of_mut!(*component_storage) +                        }; + +                        TParam~I::new(this, component_storage) +                    },)*); +                } + +                fn into_type_erased(self) -> TypeErased +                { +                    TypeErased { +                        data: Box::new(self), +                        func: Box::new(|data, component_storage| { +                            let me = data.downcast_mut::<Func>().unwrap(); + +                            me.run(component_storage); +                        }), +                    } +                } + +                fn get_local_component_mut<LocalComponent: Component>( +                    &mut self, +                ) -> Option<&mut LocalComponent> +                { +                    panic!("System does not have any local components"); +                } + +                fn set_local_component<LocalComponent: Component>( +                    &mut self, +                    _local_component: LocalComponent, +                ) { +                    panic!("System does not have any local components"); +                } +            } +        }); +    };  } +seq!(C in 0..=4 { +    impl_system!(C); +}); +  pub trait Into<Impl>  {      type System; @@ -83,3 +139,107 @@ impl Debug for TypeErased  /// Function in [`TypeErased`] used to run the system.  type TypeErasedFunc = dyn Fn(&mut dyn Any, &mut ComponentStorage); + +/// A parameter to a [`System`]. +/// +/// # Safety +/// The `is_compatible` function is used for safety so it must be implemented properly. +pub unsafe trait Param<'world> +{ +    type Input; +    type Flags; + +    fn initialize<SystemImpl>(system: &mut impl System<SystemImpl>, input: Self::Input); + +    fn new<SystemImpl>( +        system: &'world mut impl System<SystemImpl>, +        component_storage: &'world mut ComponentStorage, +    ) -> Self; + +    fn is_compatible<Other: Param<'world>>() -> bool; + +    fn get_comparable() -> Box<dyn Any>; +} + +pub struct NoInitParamFlag {} + +/// A type which can be used as input to a [`System`]. +pub trait Input: 'static {} + +pub trait InputFilter +{ +    type Filtered: FilteredInputs; +} + +pub trait FilteredInputs +{ +    type InOptions: OptionInputs; + +    fn into_in_options(self) -> Self::InOptions; +} + +macro_rules! impl_filtered_inputs { +    ($cnt: tt) => { +        seq!(I in 0..$cnt { +            impl<#(Input~I: Input,)*> FilteredInputs for (#(Input~I,)*) { +                type InOptions = (#(Option<Input~I>,)*); + +                fn into_in_options(self) -> Self::InOptions { +                    #![allow(clippy::unused_unit)] +                    (#(Some(self.I),)*) +                } +            } +        }); +    }; +} + +seq!(N in 0..4 { +    impl_filtered_inputs!(N); +}); + +pub trait OptionInputs +{ +    fn take<Input: 'static>(&mut self) -> TakeOptionInputResult<Input>; +} + +macro_rules! impl_option_inputs { +    ($cnt: tt) => { +        seq!(I in 0..$cnt { +            impl<#(Input~I: 'static,)*> OptionInputs for (#(Option<Input~I>,)*) { +                fn take<Input: 'static>(&mut self) -> TakeOptionInputResult<Input> { +                    #( +                        if TypeId::of::<Input~I>() == TypeId::of::<Input>() { +                            let input = match self.I.take() { +                                Some(input) => ManuallyDrop::new(input), +                                None => { +                                    return TakeOptionInputResult::AlreadyTaken; +                                } +                            }; + +                            return TakeOptionInputResult::Found( +                                // SAFETY: It can be transmuted safely since it is the +                                // same type and the type is 'static +                                unsafe { transmute_copy(&input) } +                            ); +                        } +                    )* + +                    TakeOptionInputResult::NotFound +                } +            } +        }); +    }; +} + +seq!(N in 0..4 { +    impl_option_inputs!(N); +}); + +pub enum TakeOptionInputResult<Input> +{ +    Found(Input), +    NotFound, +    AlreadyTaken, +} + +include!(concat!(env!("OUT_DIR"), "/system_input_impls.rs")); | 
