diff options
Diffstat (limited to 'ecs/src')
-rw-r--r-- | ecs/src/actions.rs | 14 | ||||
-rw-r--r-- | ecs/src/component.rs | 6 | ||||
-rw-r--r-- | ecs/src/component/local.rs | 67 | ||||
-rw-r--r-- | ecs/src/entity.rs | 35 | ||||
-rw-r--r-- | ecs/src/lib.rs | 94 | ||||
-rw-r--r-- | ecs/src/query.rs | 66 | ||||
-rw-r--r-- | ecs/src/query/flexible.rs | 5 | ||||
-rw-r--r-- | ecs/src/sole.rs | 14 | ||||
-rw-r--r-- | ecs/src/system.rs | 114 | ||||
-rw-r--r-- | ecs/src/system/initializable.rs | 131 | ||||
-rw-r--r-- | ecs/src/system/stateful.rs | 165 |
11 files changed, 432 insertions, 279 deletions
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs index e0efeda..2dd68bf 100644 --- a/ecs/src/actions.rs +++ b/ecs/src/actions.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use std::rc::{Rc, Weak}; use crate::component::{Parts as ComponentParts, Sequence as ComponentSequence}; -use crate::system::{Param as SystemParam, System}; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::uid::{Kind as UidKind, Uid}; use crate::{ActionQueue, World}; @@ -100,17 +100,7 @@ impl<'world> SystemParam<'world> for Actions<'world> { type Input = (); - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { Self::new(&world.data.action_queue) } diff --git a/ecs/src/component.rs b/ecs/src/component.rs index 3c2112e..8f946f0 100644 --- a/ecs/src/component.rs +++ b/ecs/src/component.rs @@ -295,6 +295,12 @@ impl PartsBuilder data: Box::new(data), } } + + #[must_use] + pub fn build_with_any_data(self, id: Uid, data: Box<dyn Any>) -> Parts + { + Parts { id, name: self.name, data } + } } impl Default for PartsBuilder diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs index 0f6f641..6b2463f 100644 --- a/ecs/src/component/local.rs +++ b/ecs/src/component/local.rs @@ -1,7 +1,17 @@ +use std::any::type_name; use std::ops::{Deref, DerefMut}; -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::system::{Param as SystemParam, System}; +use ecs_macros::Component; + +use crate::component::{ + Component, + HandleMut as ComponentHandleMut, + IntoParts as _, + Parts as ComponentParts, +}; +use crate::pair::Pair; +use crate::system::initializable::Param as InitializableParam; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::World; /// Holds a component which is local to a single system. @@ -17,27 +27,44 @@ where { type Input = LocalComponent; - fn initialize<SystemImpl>( - system: &mut impl System<'world, SystemImpl>, - input: Self::Input, - ) + fn new(world: &'world World, system_metadata: &SystemMetadata) -> Self { - system.set_local_component(input); - } + let Some(system_ent) = world.get_entity(system_metadata.ent_id) else { + panic!( + "System entity with ID {} does not exist", + system_metadata.ent_id + ); + }; - fn new<SystemImpl>( - system: &'world impl System<'world, SystemImpl>, - _world: &'world World, - ) -> Self - { - let local_component = system - .get_local_component_mut::<LocalComponent>() - .expect("Local component is uninitialized"); + let Some(local_component) = system_ent.get_with_id_mut::<LocalComponent>( + Pair::new::<IsLocalComponent>(LocalComponent::id()).id(), + ) else { + panic!( + "Local component {} of system with ID {} is uninitialized", + type_name::<LocalComponent>(), + system_metadata.ent_id + ); + }; Self { local_component } } } +impl<'world, LocalComponent, SystemT> InitializableParam<'world, SystemT> + for Local<'world, LocalComponent> +where + LocalComponent: Component, + SystemT: SystemWithLocalComponents, + Self: SystemParam<'world, Input = LocalComponent>, +{ + fn initialize(system: &mut SystemT, input: Self::Input) + { + system.add_local_component( + Pair::new_with_comp_target::<IsLocalComponent>(input).into_parts(), + ); + } +} + impl<LocalComponent> Deref for Local<'_, LocalComponent> where LocalComponent: Component, @@ -59,3 +86,11 @@ where &mut self.local_component } } + +pub trait SystemWithLocalComponents +{ + fn add_local_component(&mut self, component_parts: ComponentParts); +} + +#[derive(Component)] +struct IsLocalComponent; diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs index 6c9ec32..34fd19f 100644 --- a/ecs/src/entity.rs +++ b/ecs/src/entity.rs @@ -69,7 +69,7 @@ impl<'a> Handle<'a> #[must_use] pub fn get_mut<ComponentT: Component>( &self, - ) -> Option<ComponentHandleMut<'_, ComponentT>> + ) -> Option<ComponentHandleMut<'a, ComponentT>> { assert_eq!(ComponentT::id().kind(), UidKind::Component); @@ -87,6 +87,39 @@ impl<'a> Handle<'a> ) } + /// Returns a mutable reference to the component with the ID `id` in this entity. + /// `None` is returned if the component isn't found. + /// + /// # Panics + /// Will panic if: + /// - The ID is not a component/pair ID + /// - The component is borrowed elsewhere + /// - The component type is incorrect + #[must_use] + pub fn get_with_id_mut<ComponentData: 'static>( + &self, + id: Uid, + ) -> Option<ComponentHandleMut<'a, ComponentData>> + { + assert!( + matches!(id.kind(), UidKind::Component | UidKind::Pair), + "ID {id:?} is not a component/pair ID" + ); + + let component = self.get_matching_components(id).next()?; + + Some( + ComponentHandleMut::from_entity_component_ref(&component).unwrap_or_else( + |err| { + panic!( + "Creating handle to component {} failed: {err}", + type_name::<ComponentData>() + ); + }, + ), + ) + } + #[inline] #[must_use] pub fn get_matching_components(&self, component_uid: Uid) diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs index fa31460..2b4302a 100644 --- a/ecs/src/lib.rs +++ b/ecs/src/lib.rs @@ -18,7 +18,7 @@ use crate::component::{ Parts as ComponentParts, Sequence as ComponentSequence, }; -use crate::entity::Declaration as EntityDeclaration; +use crate::entity::{Declaration as EntityDeclaration, Handle as EntityHandle}; use crate::event::component::{ Added as ComponentAddedEvent, Removed as ComponentRemovedEvent, @@ -30,15 +30,15 @@ use crate::phase::{Phase, START as START_PHASE}; use crate::query::flexible::Query as FlexibleQuery; use crate::query::term::Without; use crate::query::{ - Iter as QueryIter, TermWithFieldTuple as QueryTermWithFieldTuple, TermWithoutFieldTuple as QueryTermWithoutFieldTuple, Terms as QueryTerms, TermsBuilderInterface, + MAX_TERM_CNT as QUERY_MAX_TERM_CNT, }; use crate::sole::Sole; use crate::stats::Stats; -use crate::system::{System, SystemComponent}; +use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent}; use crate::uid::{Kind as UidKind, Uid}; pub mod actions; @@ -126,6 +126,19 @@ impl World } } + pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts) + { + let added_component_ids = Self::add_entity_components( + entity_id, + [component_parts], + &mut self.data.component_storage, + ); + + for comp_id in added_component_ids { + self.emit_event_by_id::<ComponentAddedEvent>(comp_id); + } + } + pub fn create_declared_entity(&mut self, entity_decl: &EntityDeclaration) { entity_decl.create(self); @@ -148,10 +161,14 @@ impl World system: impl System<'this, SystemImpl>, ) { - self.create_entity(( - SystemComponent { system: system.into_type_erased() }, + let (type_erased_system, mut system_callbacks) = system.finish(); + + let system_ent_id = self.create_entity(( + SystemComponent { system: type_erased_system }, Pair::new::<DependsOn>(phase_euid), )); + + system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id }); } pub fn register_observer_system<'this, SystemImpl>( @@ -160,10 +177,12 @@ impl World event: Pair<Uid, Uid>, ) { - self.create_entity(( - SystemComponent { system: system.into_type_erased() }, - event, - )); + let (type_erased_system, mut system_callbacks) = system.finish(); + + let system_ent_id = + self.create_entity((SystemComponent { system: type_erased_system }, event)); + + system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id }); } /// Adds a extensions. @@ -190,6 +209,20 @@ impl World FlexibleQuery::new(self, terms) } + pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>> + { + let archetype = self + .data + .component_storage + .get_entity_archetype(entity_id)?; + + let entity = archetype + .get_entity_by_id(entity_id) + .expect("Should exist since archetype was found by entity id"); + + Some(EntityHandle::new(archetype, entity)) + } + /// Performs a single tick. /// # Panics /// Will panic if mutable internal lock cannot be acquired. @@ -289,21 +322,23 @@ impl World fn query_and_run_systems(&self, phase_euid: Uid) { - let system_query = self.flexible_query( - QueryTerms::<2>::builder() - .with_required([ - SystemComponent::id(), - Pair::new::<DependsOn>(phase_euid).id(), - ]) - .build(), + let system_query = Query::<(&SystemComponent,)>::from_flexible_query( + self.flexible_query( + QueryTerms::<QUERY_MAX_TERM_CNT>::builder() + .with_required([ + SystemComponent::id(), + Pair::new::<DependsOn>(phase_euid).id(), + ]) + .build(), + ), ); - for (system_component,) in - QueryIter::<(&SystemComponent,), _>::new(self, system_query.iter()) - { + for (system_ent_id, (system_component,)) in system_query.iter_with_euids() { // SAFETY: The world lives long enough unsafe { - system_component.system.run(self); + system_component + .system + .run(self, SystemMetadata { ent_id: system_ent_id }); } } } @@ -499,15 +534,22 @@ impl World return; } - let query = self.flexible_query( - QueryTerms::<2>::builder() - .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()]) - .build(), + let query = Query::<(&SystemComponent,)>::from_flexible_query( + self.flexible_query( + QueryTerms::<QUERY_MAX_TERM_CNT>::builder() + .with_required([ + SystemComponent::id(), + Pair::new::<Event>(target).id(), + ]) + .build(), + ), ); - for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) { + for (system_ent_id, (system,)) in query.iter_with_euids() { unsafe { - system.system.run(self); + system + .system + .run(self, SystemMetadata { ent_id: system_ent_id }); } } } diff --git a/ecs/src/query.rs b/ecs/src/query.rs index dfe9dad..1f281f1 100644 --- a/ecs/src/query.rs +++ b/ecs/src/query.rs @@ -10,7 +10,7 @@ use crate::component::{ }; use crate::entity::Handle as EntityHandle; use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery}; -use crate::system::{Param as SystemParam, System}; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::uid::{Kind as UidKind, Uid, With as WithUid}; use crate::util::array_vec::ArrayVec; use crate::util::Array; @@ -19,15 +19,16 @@ use crate::World; pub mod flexible; pub mod term; +// A term tuple type can have a maximum of 17 elements +pub const MAX_TERM_CNT: usize = 17; + #[derive(Debug)] pub struct Query<'world, FieldTerms, FieldlessTerms = ()> where FieldTerms: TermWithFieldTuple, FieldlessTerms: TermWithoutFieldTuple, { - world: &'world World, - // A term tuple type can have a maximum of 17 elements - inner: FlexibleQuery<'world, 17>, + inner: FlexibleQuery<'world, MAX_TERM_CNT>, _pd: PhantomData<(FieldTerms, FieldlessTerms)>, } @@ -46,7 +47,7 @@ where tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); Iter { - world: self.world, + world: self.inner.world(), iter: self.inner.iter(), comps_pd: PhantomData, } @@ -62,7 +63,7 @@ where tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); ComponentAndEuidIter { - world: self.world, + world: self.inner.world(), iter: self.inner.iter(), comps_pd: PhantomData, } @@ -84,7 +85,7 @@ where tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>()); Iter { - world: self.world, + world: self.inner.world(), iter: func(self.inner.iter()), comps_pd: PhantomData, } @@ -97,6 +98,25 @@ where Some(self.inner.iter().nth(entity_index)?.uid()) } + /// Returns a new `Query` created from a [`FlexibleQuery`]. + /// + /// # Important notes + /// The terms in `FieldTerms` and `FieldlessTerms` must be compatible with the terms + /// in the given [`FlexibleQuery`], otherwise any method call or iterating might + /// panic. + #[must_use] + pub fn from_flexible_query( + flexible_query: FlexibleQuery<'world, MAX_TERM_CNT>, + ) -> Self + { + // TODO: Check compatability of terms + + Self { + inner: flexible_query, + _pd: PhantomData, + } + } + pub(crate) fn new(world: &'world World) -> Self { let mut terms_builder = Terms::builder(); @@ -105,7 +125,6 @@ where FieldlessTerms::apply_terms_to_builder(&mut terms_builder); Self { - world, inner: world.flexible_query(terms_builder.build()), _pd: PhantomData, } @@ -135,17 +154,7 @@ where { type Input = (); - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { Self::new(world) } @@ -441,25 +450,6 @@ where comps_pd: PhantomData<FieldTerms>, } -impl<'query, 'world, FieldTerms, EntityHandleIter> - Iter<'query, 'world, FieldTerms, EntityHandleIter> -where - FieldTerms: TermWithFieldTuple, - EntityHandleIter: Iterator<Item = EntityHandle<'query>>, - 'world: 'query, -{ - /// Creates a new iterator from the given entity handle iterator. - /// - /// # Important - /// All of the yielded entities of the entity handle iterator should match the - /// terms `Terms`. The [`Self::next`] function will panic if it encounters a - /// entity that does not match the terms `Terms`. - pub fn new(world: &'world World, iter: EntityHandleIter) -> Self - { - Self { world, iter, comps_pd: PhantomData } - } -} - impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator for Iter<'query, 'world, FieldTerms, EntityHandleIter> where diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs index add30b0..50997b4 100644 --- a/ecs/src/query/flexible.rs +++ b/ecs/src/query/flexible.rs @@ -39,6 +39,11 @@ impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT> } } + pub fn world(&self) -> &'world World + { + self.world + } + pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self { Self { world, terms } diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs index 1cce419..f148224 100644 --- a/ecs/src/sole.rs +++ b/ecs/src/sole.rs @@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Weak}; use crate::lock::{Lock, WriteGuard}; -use crate::system::{Param as SystemParam, System}; +use crate::system::{Metadata as SystemMetadata, Param as SystemParam}; use crate::World; /// A type which has a single instance and is shared globally. @@ -85,17 +85,7 @@ where { type Input = (); - fn initialize<SystemImpl>( - _system: &mut impl System<'world, SystemImpl>, - _input: Self::Input, - ) - { - } - - fn new<SystemImpl>( - _system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self + fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self { let sole = world.data.sole_storage.get::<SoleT>().unwrap_or_else(|| { panic!("Sole {} was not found in world", type_name::<SoleT>()) diff --git a/ecs/src/system.rs b/ecs/src/system.rs index 603c015..a7ca4df 100644 --- a/ecs/src/system.rs +++ b/ecs/src/system.rs @@ -1,37 +1,31 @@ -use std::any::Any; -use std::convert::Infallible; use std::fmt::Debug; use ecs_macros::Component; use seq_macro::seq; -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::tuple::{ReduceElement as TupleReduceElement, Tuple}; +use crate::uid::Uid; use crate::World; +pub mod initializable; pub mod stateful; -pub trait System<'world, Impl>: 'static +/// Metadata of a system. +#[derive(Debug)] +#[non_exhaustive] +pub struct Metadata { - type Input; + pub ent_id: Uid, +} - #[must_use] - fn initialize(self, input: Self::Input) -> Self; +pub trait System<'world, Impl>: 'static +{ + type Callbacks: Callbacks; - fn run<'this>(&'this self, world: &'world World) + fn run<'this>(&'this self, world: &'world World, metadata: Metadata) where 'this: 'world; - fn into_type_erased(self) -> TypeErased; - - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>>; - - fn set_local_component<LocalComponent: Component>( - &mut self, - local_component: LocalComponent, - ); + fn finish(self) -> (TypeErased, Self::Callbacks); } macro_rules! impl_system { @@ -43,58 +37,34 @@ macro_rules! impl_system { Func: Fn(#(TParam~I,)*) + Copy + 'static, #(TParam~I: Param<'world, Input = ()>,)* { - type Input = Infallible; + type Callbacks = NoCallbacks; - fn initialize(self, _input: Self::Input) -> Self - { - self - } - - fn run<'this>(&'this self, world: &'world World) + fn run<'this>(&'this self, world: &'world World, metadata: Metadata) where 'this: 'world { let func = *self; func(#({ - TParam~I::new(self, world) + TParam~I::new(world, &metadata) },)*); } - fn into_type_erased(self) -> TypeErased + fn finish(self) -> (TypeErased, Self::Callbacks) { - 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(data) }; - - let me = data - .downcast_ref::<Func>() - .expect("Function downcast failed"); - + 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) }; - me.run(world); + self(#({ + TParam~I::new(world, &metadata) + },)*); }), - } - } + }; - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<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"); + (type_erased, NoCallbacks) } } }); @@ -114,7 +84,6 @@ pub trait Into<Impl> pub struct TypeErased { - data: Box<dyn Any>, run: Box<TypeErasedRunFn>, } @@ -124,12 +93,9 @@ impl TypeErased /// /// # Safety /// `world_data` must live at least as long as the [`World`] the system belongs to. - pub unsafe fn run(&self, world: &World) + pub unsafe fn run(&self, world: &World, metadata: Metadata) { - // You have to dereference for downcasting to work for some reason - let data = &*self.data; - - (self.run)(data, world); + (self.run)(world, metadata); } } @@ -142,41 +108,29 @@ impl Debug for TypeErased } /// Function in [`TypeErased`] used to run the system. -type TypeErasedRunFn = dyn Fn(&dyn Any, &World); +type TypeErasedRunFn = dyn Fn(&World, Metadata); /// A parameter to a [`System`]. pub trait Param<'world> { type Input; - fn initialize<SystemImpl>( - system: &mut impl System<'world, SystemImpl>, - input: Self::Input, - ); - - fn new<SystemImpl>( - system: &'world impl System<'world, SystemImpl>, - world: &'world World, - ) -> Self; + fn new(world: &'world World, system_metadata: &Metadata) -> Self; } /// A type which can be used as input to a [`System`]. pub trait Input: 'static {} -/// Component tuple reducing operation to get the parameters that takes input. -pub struct ParamWithInputFilter; - -impl<InputT: Input, Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> - for InputT -where - Accumulator: Tuple, +pub trait Callbacks { - type Return = Accumulator::WithElementAtEnd<Self>; + fn on_created(&mut self, world: &mut World, metadata: Metadata); } -impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for () +pub struct NoCallbacks; + +impl Callbacks for NoCallbacks { - type Return = Accumulator; + fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {} } #[derive(Debug, Component)] diff --git a/ecs/src/system/initializable.rs b/ecs/src/system/initializable.rs new file mode 100644 index 0000000..40fbb0f --- /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<'world, ParamT, Accumulator, SystemT> + AppendInitializableParam<'world, 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<'world, SystemT> ParamTuple<'world, 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/stateful.rs b/ecs/src/system/stateful.rs index 2ec448c..93bfd5e 100644 --- a/ecs/src/system/stateful.rs +++ b/ecs/src/system/stateful.rs @@ -1,137 +1,81 @@ -use std::any::{type_name, Any, TypeId}; use std::panic::{RefUnwindSafe, UnwindSafe}; -use hashbrown::HashMap; use seq_macro::seq; -use crate::component::{Component, HandleMut as ComponentHandleMut}; -use crate::lock::Lock; -use crate::system::{ - Into as IntoSystem, - Param, - ParamWithInputFilter, - System, - TypeErased, -}; -use crate::tuple::{ - Reduce as TupleReduce, - Tuple, - WithAllElemLtStatic as TupleWithAllElemLtStatic, -}; -use crate::uid::Uid; +use crate::component::local::SystemWithLocalComponents; +use crate::component::Parts as ComponentParts; +use crate::system::initializable::{Initializable, MaybeInitializableParamTuple}; +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 Any>>>, + 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 - { - 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; - } - } - )* - - self - } - - fn run<'this>(&'this self, world: &'world World) + fn run<'this>(&'this self, world: &'world World, metadata: Metadata) where 'this: 'world { let func = self.func; func(#({ - TParam~I::new(self, &world) + TParam~I::new(&world, &metadata) },)*); } - fn into_type_erased(self) -> TypeErased + fn finish(self) -> (TypeErased, Self::Callbacks) { - 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) }; + let Self { func, local_components } = self; - let me = data.downcast_ref::<Self>().unwrap(); + 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) }; - me.run(world); + func(#({ + TParam~I::new(&world, &metadata) + },)*); }), - } - } + }; - fn get_local_component_mut<LocalComponent: Component>( - &self, - ) -> Option<ComponentHandleMut<LocalComponent>> - { - let local_component = self.local_components - .get(&LocalComponent::id())? - .write_nonblock() - .expect("Failed to aquire read-write local component lock"); - Some(ComponentHandleMut::new(local_component).unwrap()) + (type_erased, callbacks) } + } + + 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 set_local_component<LocalComponent: Component>( - &mut self, - local_component: LocalComponent, - ) + fn initialize(mut self, inputs: Self::Inputs) -> Self { - self.local_components - .insert( - LocalComponent::id(), - Lock::new( - Box::new(local_component), - type_name::<LocalComponent>() - ) - ); + init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs); + + self } } @@ -146,7 +90,7 @@ macro_rules! impl_system { { Self::System { func: self, - local_components: HashMap::new(), + local_components: Vec::new(), // TODO: Use Vec::with_capacity } } } @@ -157,3 +101,36 @@ macro_rules! impl_system { seq!(C in 1..16 { impl_system!(C); }); + +impl<Func> SystemWithLocalComponents for Stateful<Func> +{ + fn add_local_component(&mut self, component_parts: ComponentParts) + { + self.local_components.push(component_parts); + } +} + +#[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); +} |