From 1019924a29527eba2c8ec8bd976ece6ed76075b0 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 25 Feb 2024 23:25:03 +0100 Subject: feat(ecs): add support for multiple system queries & local components --- ecs/src/system/stateful.rs | 203 ++++++++++++++++++++++++++++++++------------- ecs/src/system/util.rs | 13 +++ 2 files changed, 158 insertions(+), 58 deletions(-) create mode 100644 ecs/src/system/util.rs (limited to 'ecs/src/system') diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs index b641cf2..9b2f279 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,67 +1,154 @@ -use std::marker::PhantomData; +use std::any::{type_name, TypeId}; +use std::collections::HashMap; +use std::ptr::addr_of_mut; -use crate::component::Local; -use crate::system::{System, TypeErased}; -use crate::{ComponentStorage, Query}; +use seq_macro::seq; + +use crate::component::Component; +use crate::system::util::check_params_are_compatible; +use crate::system::{ + FilteredInputs, + InputFilter, + Into as IntoSystem, + OptionInputs, + Param, + System, + TakeOptionInputResult, + TypeErased, +}; +use crate::ComponentStorage; /// A stateful system. -pub struct Stateful +pub struct Stateful { func: Func, - local_component: Option, - _comps_pd: PhantomData, + local_components: HashMap>, } -impl - System, Local)> - for Stateful -where - Func: Fn(Query, Local), -{ - type Input = LocalComponent; - type Query<'a> = Query<'a, Comps>; - - fn initialize(mut self, input: Self::Input) -> Self - { - self.local_component = Some(input); - - self - } - - fn run(&mut self, component_storage: &mut ComponentStorage) - { - (self.func)( - Query::new(component_storage), - Local::new(self.local_component.as_mut().unwrap()), - ); - } - - fn into_type_erased(self) -> TypeErased - { - TypeErased { - data: Box::new(self), - func: Box::new(move |data, component_storage| { - let this = data.downcast_mut::().unwrap(); - - this.run(component_storage); - }), - } - } -} +macro_rules! impl_system { + ($c: tt) => { + seq!(I in 0..=$c { + impl<'world, Func, #(TParam~I,)*> System + for Stateful + where + Func: Fn(#(TParam~I,)*) + Copy + 'static, + #(TParam~I: Param<'world>,)* + #(TParam~I::Input: 'static,)* + (#(TParam~I::Input,)*): InputFilter + { + type Input = <(#(TParam~I::Input,)*) as InputFilter>::Filtered; -impl - crate::system::Into, Local)> for Func -where - Func: Fn(Query, Local), -{ - type System = Stateful; - - fn into_system(self) -> Self::System - { - Self::System { - func: self, - local_component: None, - _comps_pd: PhantomData, - } - } + fn initialize(mut self, input: Self::Input) -> Self + { + let mut option_input = input.into_in_options(); + + #( + if TypeId::of::() != TypeId::of::<()>() { + let input = match option_input.take::() { + TakeOptionInputResult::Found(input) => input, + TakeOptionInputResult::NotFound => { + panic!( + "Parameter input {} not found", + type_name::() + ); + } + TakeOptionInputResult::AlreadyTaken => { + panic!( + concat!( + "Parameter {} is already initialized. ", + "System cannot contain multiple inputs with ", + "the same type", + ), + type_name::() + ); + + } + }; + + TParam~I::initialize( + &mut self, + input + ); + } + )* + + self + } + + fn run(&mut self, component_storage: &mut ComponentStorage) + { + #( + check_params_are_compatible!(I, TParam~I, $c); + )* + + let func = self.func; + + 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::().unwrap(); + + me.run(component_storage); + }), + } + } + + fn get_local_component_mut( + &mut self, + ) -> Option<&mut LocalComponent> + { + self.local_components + .get_mut(&TypeId::of::())? + .downcast_mut() + } + + fn set_local_component( + &mut self, + local_component: LocalComponent, + ) + { + self.local_components + .insert(TypeId::of::(), + Box::new(local_component)); + } + } + + impl IntoSystem + for Func + where + Func: Fn(#(TParam~I,)*) + Copy + 'static, + { + type System = Stateful; + + fn into_system(self) -> Self::System + { + Self::System { + func: self, + local_components: HashMap::new(), + } + } + } + }); + }; } + +seq!(C in 0..4 { + impl_system!(C); +}); diff --git a/ecs/src/system/util.rs b/ecs/src/system/util.rs new file mode 100644 index 0000000..9d04f1d --- /dev/null +++ b/ecs/src/system/util.rs @@ -0,0 +1,13 @@ +macro_rules! check_params_are_compatible { + ($excluded_index: tt, $param: ident, $cnt: tt) => { + seq!(N in 0..=$cnt { + if N != $excluded_index { + if !$param::is_compatible::() { + panic!("Atleast two parameters are incompatible"); + } + } + }) + }; +} + +pub(crate) use check_params_are_compatible; -- cgit v1.2.3-18-g5258