diff options
Diffstat (limited to 'ecs/src/system')
| -rw-r--r-- | ecs/src/system/initializable.rs | 131 | ||||
| -rw-r--r-- | ecs/src/system/observer.rs | 310 | ||||
| -rw-r--r-- | ecs/src/system/stateful.rs | 293 | 
3 files changed, 634 insertions, 100 deletions
| diff --git a/ecs/src/system/initializable.rs b/ecs/src/system/initializable.rs new file mode 100644 index 0000000..b6ec8e8 --- /dev/null +++ b/ecs/src/system/initializable.rs @@ -0,0 +1,131 @@ +use std::marker::PhantomData; + +use seq_macro::seq; + +use crate::system::{Input, Param as SystemParam, System}; +use crate::tuple::{Reduce as TupleReduce, ReduceElement as TupleReduceElement, Tuple}; + +/// A initializable system. +pub trait Initializable<'world, Impl>: System<'world, Impl> +{ +    type Inputs; + +    #[must_use] +    fn initialize(self, inputs: Self::Inputs) -> Self; +} + +pub trait Param<'world, SystemT>: SystemParam<'world> +{ +    fn initialize(system: &mut SystemT, input: Self::Input); +} + +pub struct ParamTupleFilter<'world, SystemT> +{ +    _pd: PhantomData<(&'world (), SystemT)>, +} + +impl<'world, SystemT, ParamT, Accumulator> +    TupleReduceElement<Accumulator, ParamTupleFilter<'world, SystemT>> for ParamT +where +    ParamT: SystemParam< +        'world, +        Input: AppendInitializableParam<'world, Accumulator, ParamT, SystemT>, +    >, +    Accumulator: Tuple, +{ +    type Return = <ParamT::Input as AppendInitializableParam< +        'world, +        Accumulator, +        ParamT, +        SystemT, +    >>::Return; +} + +pub trait AppendInitializableParam<'world, Accumulator, ParamT, SystemT> +{ +    type Return; +} + +impl<'world, InputT, ParamT, Accumulator, SystemT> +    AppendInitializableParam<'world, Accumulator, ParamT, SystemT> for InputT +where +    InputT: Input, +    Accumulator: Tuple, +    ParamT: Param<'world, SystemT>, +{ +    type Return = Accumulator::WithElementAtEnd<ParamT>; +} + +impl<ParamT, Accumulator, SystemT> +    AppendInitializableParam<'_, Accumulator, ParamT, SystemT> for () +where +    Accumulator: Tuple, +{ +    type Return = Accumulator; +} + +pub trait ParamTuple<'world, SystemT> +{ +    type Inputs; + +    fn initialize_all(system: &mut SystemT, inputs: Self::Inputs); +} + +macro_rules! impl_initializable_param_tuple { +    ($c: tt) => { +        seq!(I in 0..$c { +            impl<'world, SystemT, #(Param~I,)*> ParamTuple<'world, SystemT> +                for (#(Param~I,)*) +            where +                #(Param~I: Param<'world, SystemT>,)* +            { +                type Inputs = (#(Param~I::Input,)*); + +                fn initialize_all( +                    system: &mut SystemT, +                    inputs: Self::Inputs, +                ) { +                    #( +                        <Param~I as Param<'world, SystemT>>::initialize( +                            system, +                            inputs.I +                        ); +                    )* +                } +            } +        }); +    }; +} + +seq!(C in 1..16 { +    impl_initializable_param_tuple!(C); +}); + +impl<SystemT> ParamTuple<'_, SystemT> for () +{ +    type Inputs = (); + +    fn initialize_all(_system: &mut SystemT, _inputs: Self::Inputs) {} +} + +/// A tuple of system parameters that may or may not be initializable. +pub trait MaybeInitializableParamTuple<'world, SystemT> +{ +    /// A tuple of the inputs of the initializable system parameters in this tuple. +    type Inputs; + +    fn init_initializable(system: &mut SystemT, inputs: Self::Inputs); +} + +impl<'world, SystemT, Params> MaybeInitializableParamTuple<'world, SystemT> for Params +where +    Params: +        TupleReduce<ParamTupleFilter<'world, SystemT>, Out: ParamTuple<'world, SystemT>>, +{ +    type Inputs = <Params::Out as ParamTuple<'world, SystemT>>::Inputs; + +    fn init_initializable(system: &mut SystemT, inputs: Self::Inputs) +    { +        Params::Out::initialize_all(system, inputs); +    } +} diff --git a/ecs/src/system/observer.rs b/ecs/src/system/observer.rs new file mode 100644 index 0000000..236420c --- /dev/null +++ b/ecs/src/system/observer.rs @@ -0,0 +1,310 @@ +use std::fmt::Debug; +use std::marker::PhantomData; +use std::mem::transmute; +use std::slice::Iter as SliceIter; + +use ecs_macros::Component; +use seq_macro::seq; + +use crate::component::Component; +use crate::entity::Handle as EntityHandle; +use crate::event::Emitted as EmittedEvent; +use crate::pair::Pair; +use crate::system::{ +    Metadata, +    NoCallbacks, +    Param, +    System, +    TypeErased as TypeErasedSystem, +}; +use crate::uid::Uid; +use crate::util::Array; +use crate::World; + +pub trait Observed +{ +    type Events: Array<Pair<Uid, Uid>>; + +    fn events() -> Self::Events; +} + +impl<Relation, Target> Observed for Pair<Relation, Target> +where +    Relation: Component, +    Target: Component, +{ +    type Events = [Pair<Uid, Uid>; 1]; + +    fn events() -> Self::Events +    { +        [Pair::builder() +            .relation::<Relation>() +            .target::<Target>() +            .build()] +    } +} + +/// Observer system. +pub trait Observer<'world, Impl>: System<'world, Impl> +{ +    type ObservedEvents: Array<Pair<Uid, Uid>>; + +    fn observed_events() -> Self::ObservedEvents; + +    fn finish_observer(self) -> (WrapperComponent, Self::Callbacks); +} + +pub struct Observe<'world, ObservedT: Observed> +{ +    _pd: PhantomData<ObservedT>, +    world: &'world World, +    emitted_event: EmittedEvent<'world>, +} + +impl<'world, ObservedT: Observed> Observe<'world, ObservedT> +{ +    pub fn new(world: &'world World, emitted_event: EmittedEvent<'world>) -> Self +    { +        Self { +            _pd: PhantomData, +            world, +            emitted_event, +        } +    } + +    #[must_use] +    pub fn event(&self) -> Uid +    { +        self.emitted_event.event +    } +} + +impl<ObservedT: Observed> Observe<'_, ObservedT> +{ +    #[must_use] +    pub fn iter(&self) -> ObserveIter<'_, ObservedT> +    { +        ObserveIter { +            world: self.world, +            inner: self.emitted_event.match_ids.iter(), +            _pd: PhantomData, +        } +    } +} + +impl<'a, ObservedT: Observed> IntoIterator for &'a Observe<'_, ObservedT> +{ +    type IntoIter = ObserveIter<'a, ObservedT>; +    type Item = <Self::IntoIter as Iterator>::Item; + +    fn into_iter(self) -> Self::IntoIter +    { +        self.iter() +    } +} + +pub struct ObserveIter<'observe, ObservedT: Observed> +{ +    world: &'observe World, +    inner: SliceIter<'observe, Uid>, +    _pd: PhantomData<ObservedT>, +} + +impl<'observe, ObservedT: Observed> Iterator for ObserveIter<'observe, ObservedT> +{ +    type Item = EventMatch<'observe, ObservedT>; + +    fn next(&mut self) -> Option<Self::Item> +    { +        let match_id = *self.inner.next()?; + +        Some(EventMatch { +            world: self.world, +            id: match_id, +            _pd: PhantomData, +        }) +    } +} + +/// A event match. +#[derive(Debug)] +pub struct EventMatch<'world, ObservedT: Observed> +{ +    world: &'world World, +    id: Uid, +    _pd: PhantomData<ObservedT>, +} + +impl<'world, ObservedT: Observed> EventMatch<'world, ObservedT> +{ +    #[must_use] +    pub fn id(&self) -> Uid +    { +        self.id +    } + +    /// Attempts to get the entity with the id of this match. +    #[must_use] +    pub fn get_entity(&self) -> Option<EntityHandle<'world>> +    { +        self.world.get_entity(self.id) +    } +} + +macro_rules! impl_observer { +    ($c: tt) => { +        seq!(I in 0..$c { +            impl<'world, ObservedT, Func, #(TParam~I,)*> System< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Func +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world, Input = ()>,)* +            { +                type Callbacks = NoCallbacks; + +                fn finish(self) -> (TypeErasedSystem, NoCallbacks) +                { +                    unimplemented!(); +                } +            } + +            impl<'world, ObservedT, Func, #(TParam~I,)*> Observer< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Func +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world, Input = ()>,)* +            { +                type ObservedEvents = ObservedT::Events; + +                fn observed_events() -> Self::ObservedEvents +                { +                    ObservedT::events() +                } + +                fn finish_observer(self) -> (WrapperComponent, NoCallbacks) +                { +                    let wrapper_comp = WrapperComponent { +                        run: Box::new(move |world, metadata, emitted_event| { +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let world = unsafe { &*std::ptr::from_ref(world) }; + +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let emitted_event = unsafe { +                                transmute::<EmittedEvent<'_>, EmittedEvent<'_>>( +                                    emitted_event +                                ) +                            }; + +                            self(Observe::new(world, emitted_event), #({ +                                TParam~I::new(world, &metadata) +                            },)*); +                        }), +                    }; + +                    (wrapper_comp, NoCallbacks) +                } +            } +        }); +    }; +} + +seq!(C in 1..16 { +    impl_observer!(C); +}); + +impl<'world, ObservedT, Func> System<'world, fn(Observe<'world, ObservedT>)> for Func +where +    ObservedT: Observed, +    Func: Fn(Observe<'world, ObservedT>) + Copy + 'static, +{ +    type Callbacks = NoCallbacks; + +    fn finish(self) -> (TypeErasedSystem, NoCallbacks) +    { +        const { +            panic!("Observers cannot be used as regular systems"); +        } +    } +} + +impl<'world, ObservedT, Func> Observer<'world, fn(Observe<'world, ObservedT>)> for Func +where +    ObservedT: Observed, +    Func: Fn(Observe<'world, ObservedT>) + Copy + 'static, +{ +    type ObservedEvents = ObservedT::Events; + +    fn observed_events() -> Self::ObservedEvents +    { +        ObservedT::events() +    } + +    fn finish_observer(self) -> (WrapperComponent, NoCallbacks) +    { +        let wrapper_comp = WrapperComponent { +            run: Box::new(move |world, _metadata, emitted_event| { +                // SAFETY: The caller of TypeErased::run ensures the lifetime +                // is correct +                let world = unsafe { &*std::ptr::from_ref(world) }; + +                // SAFETY: The caller of TypeErased::run ensures the lifetime +                // is correct +                let emitted_event = unsafe { +                    transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(emitted_event) +                }; + +                self(Observe::new(world, emitted_event)); +            }), +        }; + +        (wrapper_comp, NoCallbacks) +    } +} + +#[derive(Component)] +pub struct WrapperComponent +{ +    run: Box<RunFn>, +} + +impl WrapperComponent +{ +    pub fn new(run: impl Fn(&World, Metadata, EmittedEvent<'_>) + 'static) -> Self +    { +        Self { run: Box::new(run) } +    } + +    /// Runs the observer system. +    /// +    /// # Safety +    /// `world` must live at least as long as the [`World`] the system belongs to. +    pub unsafe fn run( +        &self, +        world: &World, +        metadata: Metadata, +        emitted_event: EmittedEvent<'_>, +    ) +    { +        (self.run)(world, metadata, emitted_event); +    } +} + +impl Debug for WrapperComponent +{ +    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result +    { +        formatter +            .debug_struct("WrapperComponent") +            .finish_non_exhaustive() +    } +} + +type RunFn = dyn Fn(&World, Metadata, EmittedEvent<'_>); diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index 80ac346..e74ef31 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,150 +1,243 @@ -use std::any::{Any, TypeId}; +use std::mem::transmute;  use std::panic::{RefUnwindSafe, UnwindSafe}; -use hashbrown::HashMap;  use seq_macro::seq; -use crate::component::Component; -use crate::lock::Lock; -use crate::system::{ -    ComponentRefMut, -    Into as IntoSystem, -    Param, -    ParamWithInputFilter, -    System, -    TypeErased, +use crate::component::local::SystemWithLocalComponents; +use crate::component::Parts as ComponentParts; +use crate::event::Emitted as EmittedEvent; +use crate::system::initializable::{Initializable, MaybeInitializableParamTuple}; +use crate::system::observer::{ +    Observe, +    Observed, +    Observer, +    WrapperComponent as ObserverWrapperComponent,  }; -use crate::tuple::{ -    Reduce as TupleReduce, -    Tuple, -    WithAllElemLtStatic as TupleWithAllElemLtStatic, -}; -use crate::uid::Uid; +use crate::system::{Into as IntoSystem, Metadata, Param, System, TypeErased};  use crate::World;  /// A stateful system.  pub struct Stateful<Func>  {      func: Func, -    local_components: HashMap<Uid, Lock<Box<dyn Component>>>, +    local_components: Vec<ComponentParts>,  }  macro_rules! impl_system {      ($c: tt) => {          seq!(I in 0..$c { -            impl<'world, Func, #(TParam~I,)*> System<'world, fn(&'world (), #(TParam~I,)*)> -                for Stateful<Func> +            impl<'world, Func, #(TParam~I,)*> +                System<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func>              where                  Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, -                #(TParam~I: Param<'world>,)* -                #(TParam~I::Input: 'static,)* -                (#(TParam~I::Input,)*): TupleReduce< -                    ParamWithInputFilter, -                    Out: Tuple<InOptions: TupleWithAllElemLtStatic> -                >, +                #(TParam~I: Param<'world, Input: 'static>,)*              { -                type Input = -                    <(#(TParam~I::Input,)*) as TupleReduce<ParamWithInputFilter>>::Out; +                type Callbacks = Callbacks; -                fn initialize(mut self, input: Self::Input) -> Self +                fn finish(self) -> (TypeErased, Self::Callbacks)                  { -                    let mut option_input = input.into_in_options(); - -                    let mut index = 0; - -                    #( -                        if TypeId::of::<TParam~I::Input>() != -                            TypeId::of::<()>() -                        { -                            let input = option_input -                                .get_mut::<Option<TParam~I::Input>>(index) -                                .expect("Input element index out of range") -                                .take() -                                .expect("Input element is already taken"); - -                            TParam~I::initialize( -                                &mut self, -                                input -                            ); - -                            #[allow(unused_assignments)] -                            { -                                index += 1; -                            } -                        } -                    )* +                    let Self { func, local_components } = self; -                    self +                    let callbacks = Callbacks { local_components }; + +                    let type_erased = TypeErased { +                        run: Box::new(move |world, metadata| { +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let world = unsafe { &*std::ptr::from_ref(world) }; + +                            func(#({ +                                TParam~I::new(&world, &metadata) +                            },)*); +                        }), +                    }; + + +                    (type_erased, callbacks)                  } +            } -                fn run<'this>(&'this self, world: &'world World) -                where -                    'this: 'world +            impl<'world, Func, #(TParam~I,)*> +                Initializable<'world, fn(&'world (), #(TParam~I,)*)> for Stateful<Func> +            where +                Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static, +                #(TParam~I: Param<'world, Input: 'static>,)* +                (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> +            { +                type Inputs = < +                    (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self> +                >::Inputs; + +                fn initialize(mut self, inputs: Self::Inputs) -> Self                  { -                    let func = self.func; +                    init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); -                    func(#({ -                        TParam~I::new(self, &world) -                    },)*); +                    self                  } +            } -                fn into_type_erased(self) -> TypeErased +            impl<'world, Func, #(TParam~I,)*> IntoSystem<'world, fn(#(TParam~I,)*)> +                for Func +            where +                #(TParam~I: Param<'world>,)* +                Func: Fn(#(TParam~I,)*) + Copy + 'static, +            { +                type System = Stateful<Func>; + +                fn into_system(self) -> Self::System                  { -                    TypeErased { -                        data: Box::new(self), -                        run: Box::new(|data, world| { -                            // SAFETY: The caller of TypeErased::run ensures the lifetime -                            // is correct -                            let data = unsafe { &*std::ptr::from_ref::<dyn Any>(data) }; +                    Self::System { +                        func: self, +                        local_components: Vec::new(), // TODO: Use Vec::with_capacity +                    } +                } +            } +        }); +    }; +} -                            let me = data.downcast_ref::<Self>().unwrap(); +seq!(C in 1..16 { +    impl_system!(C); +}); -                            // SAFETY: The caller of TypeErased::run ensures the lifetime -                            // is correct -                            let world = unsafe { &*std::ptr::from_ref(world) }; +impl<Func> SystemWithLocalComponents for Stateful<Func> +{ +    fn add_local_component(&mut self, component_parts: ComponentParts) +    { +        self.local_components.push(component_parts); +    } +} -                            me.run(world); -                        }), -                    } +#[derive(Debug)] +pub struct Callbacks +{ +    local_components: Vec<ComponentParts>, +} + +impl crate::system::Callbacks for Callbacks +{ +    fn on_created(&mut self, world: &mut World, metadata: Metadata) +    { +        for local_comp_parts in self.local_components.drain(..) { +            world.add_component(metadata.ent_id, local_comp_parts); +        } +    } +} + +fn init_initializable_params<'world, SystemT, Params>( +    system: &mut SystemT, +    inputs: Params::Inputs, +) where +    Params: MaybeInitializableParamTuple<'world, SystemT>, +{ +    Params::init_initializable(system, inputs); +} + +macro_rules! impl_observer { +    ($c: tt) => { +        seq!(I in 0..$c { +            impl<'world, ObservedT, Func, #(TParam~I,)*> System< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Stateful<Func> +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world>,)* +            { +                type Callbacks = Callbacks; + +                fn finish(self) -> (TypeErased, Callbacks) +                { +                    unimplemented!();                  } +            } -                fn get_local_component_mut<LocalComponent: Component>( -                    &self, -                ) -> Option<ComponentRefMut<LocalComponent>> +            impl<'world, ObservedT, Func, #(TParam~I,)*> Initializable< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Stateful<Func> +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world>,)* +                (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> +            { +                type Inputs = < +                    (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self> +                >::Inputs; + +                fn initialize(mut self, inputs: Self::Inputs) -> Self                  { -                    let local_component = self.local_components -                        .get(&LocalComponent::id())? -                        .write_nonblock() -                        .expect("Failed to aquire read-write local component lock"); +                    init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); -                    Some(ComponentRefMut::new(local_component)) +                    self                  } +            } + +            impl<'world, ObservedT, Func, #(TParam~I,)*> Observer< +                'world, +                fn(Observe<'world, ObservedT>, #(TParam~I,)*) +            > for Stateful<Func> +            where +                ObservedT: Observed, +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static, +                #(TParam~I: Param<'world>,)* +            { +                type ObservedEvents = ObservedT::Events; -                fn set_local_component<LocalComponent: Component>( -                    &mut self, -                    local_component: LocalComponent, -                ) +                fn observed_events() -> Self::ObservedEvents                  { -                    self.local_components -                        .insert( -                            LocalComponent::id(), -                            Lock::new(Box::new(local_component)) -                        ); +                    ObservedT::events() +                } + +                fn finish_observer(self) -> (ObserverWrapperComponent, Callbacks) +                { +                    let Self { func, local_components } = self; + +                    let callbacks = Callbacks { local_components }; + +                    let wrapper_comp = ObserverWrapperComponent::new( +                        move |world, metadata, emitted_event| { +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let world = unsafe { &*std::ptr::from_ref(world) }; + +                            // SAFETY: The caller of TypeErased::run ensures the lifetime +                            // is correct +                            let emitted_event = unsafe { +                                transmute::<EmittedEvent<'_>, EmittedEvent<'_>>( +                                    emitted_event +                                ) +                            }; + +                            func(Observe::new(world, emitted_event), #({ +                                TParam~I::new(world, &metadata) +                            },)*); +                        }, +                    ); + +                    (wrapper_comp, callbacks)                  }              } -            impl<Func, #(TParam~I,)*> IntoSystem<fn(#(TParam~I,)*)> -                for Func +            impl<'world, Func, ObservedT, #(TParam~I,)*> IntoSystem< +                'world, +                fn(Observe<'world, ObservedT>, +                #(TParam~I,)*) +            > for Func              where -                Func: Fn(#(TParam~I,)*) + Copy + 'static, +                ObservedT: Observed, +                #(TParam~I: Param<'world>,)* +                Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) + Copy + 'static,              {                  type System = Stateful<Func>; -                fn into_system(self) -> Self::System +                fn into_system(self) -> Stateful<Func>                  { -                    Self::System { +                    Stateful {                          func: self, -                        local_components: HashMap::new(), +                        local_components: Vec::new(), // TODO: Use Vec::with_capacity                      }                  }              } @@ -153,5 +246,5 @@ macro_rules! impl_system {  }  seq!(C in 1..16 { -    impl_system!(C); +    impl_observer!(C);  }); | 
