diff options
| author | HampusM <hampus@hampusmat.com> | 2026-06-02 21:18:20 +0200 |
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2026-06-02 21:18:20 +0200 |
| commit | 7c7b85983ef72564117d2059487b25aa3e5e5bad (patch) | |
| tree | 4fdc4535808e9894cd7ba4e41f1504b6ce389d8f | |
| parent | cce08a8557f41f792074cc46c1a7f8e8213e3b2d (diff) | |
refactor(engine-ecs): make systems & observers same component
| -rw-r--r-- | engine-ecs/src/lib.rs | 22 | ||||
| -rw-r--r-- | engine-ecs/src/system.rs | 33 | ||||
| -rw-r--r-- | engine-ecs/src/system/initializable.rs | 4 | ||||
| -rw-r--r-- | engine-ecs/src/system/observer.rs | 107 | ||||
| -rw-r--r-- | engine-ecs/src/system/stateful.rs | 86 | ||||
| -rw-r--r-- | engine-ecs/src/util.rs | 17 |
6 files changed, 119 insertions, 150 deletions
diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs index a60ddcd..70816b3 100644 --- a/engine-ecs/src/lib.rs +++ b/engine-ecs/src/lib.rs @@ -49,7 +49,7 @@ use crate::query::{ }; use crate::sole::{Single, Sole}; use crate::stats::Stats; -use crate::system::observer::{Observer, WrapperComponent as ObserverWrapperComponent}; +use crate::system::observer::Observer; use crate::system::{Callbacks, Metadata as SystemMetadata, System, SystemComponent}; use crate::uid::{Kind as UidKind, Uid}; @@ -163,13 +163,13 @@ impl World ) where ObserverT: Observer<'this, SystemImpl>, { - let (wrapper_comp, mut system_callbacks) = observer.finish_observer(); + let (system, mut system_callbacks) = observer.finish_observer(); let ent_id = Uid::new_unique(UidKind::Entity); self.create_ent( ent_id, - [wrapper_comp.into_parts()].into_iter().chain( + [SystemComponent { system }.into_parts()].into_iter().chain( ObserverT::observed_events() .into_iter() .map(IntoComponentParts::into_parts), @@ -386,9 +386,11 @@ impl World // SAFETY: The world lives long enough if let Err(err) = unsafe { - system - .system - .run(self, SystemMetadata { ent_id: system_entity.uid() }) + system.system.run( + self, + SystemMetadata { ent_id: system_entity.uid() }, + None, + ) } { cold_path(); @@ -585,20 +587,20 @@ impl World { assert_eq!(event_id.kind(), UidKind::Pair); - let query = Query::<(&ObserverWrapperComponent,)>::from_flexible_query( + let query = Query::<(&SystemComponent,)>::from_flexible_query( self.flexible_query( QueryTerms::<QUERY_MAX_TERM_CNT>::builder() - .with_required([ObserverWrapperComponent::id(), event_id]) + .with_required([SystemComponent::id(), event_id]) .build(), ), ); for (observer_ent_id, (observer,)) in query.iter_with_euids() { if let Err(err) = unsafe { - observer.run( + observer.system.run( self, SystemMetadata { ent_id: observer_ent_id }, - emitted_event.clone(), + Some(emitted_event.clone()), ) } { cold_path(); diff --git a/engine-ecs/src/system.rs b/engine-ecs/src/system.rs index 38e480d..5395b14 100644 --- a/engine-ecs/src/system.rs +++ b/engine-ecs/src/system.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use seq_macro::seq; use crate::error::Error; +use crate::event::Emitted as EmittedEvent; use crate::uid::Uid; use crate::{Component, World}; @@ -31,7 +32,7 @@ macro_rules! impl_system { impl<'world, Func, Ret, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*) -> Ret> for Func where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static, Ret: ReturnValue, #(TParam~I: Param<'world, Input = ()>,)* { @@ -41,16 +42,23 @@ macro_rules! impl_system { { #![allow(unused)] + crate::util::const_assert!( + size_of::<Func>() == 0, + "System function is not zero-sized (not function pointer)" + ); + let type_erased = TypeErased { - run: Box::new(move |world, metadata| { + run: |world, metadata, _| { // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let world = unsafe { &*std::ptr::from_ref(world) }; - self(#({ + let func = unsafe { std::mem::zeroed::<Func>() }; + + func(#({ TParam~I::new(world, &metadata) },)*).into_result() - }), + }, name: std::any::type_name::<Self>() }; @@ -74,8 +82,8 @@ pub trait Into<'world, Impl> pub struct TypeErased { - run: Box<TypeErasedRunFn>, - name: &'static str, + pub run: TypeErasedRunFn, + pub name: &'static str, } impl TypeErased @@ -84,9 +92,14 @@ 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, metadata: Metadata) -> Result<(), Error> + pub unsafe fn run( + &self, + world: &World, + metadata: Metadata, + evt: Option<EmittedEvent<'_>>, + ) -> Result<(), Error> { - (self.run)(world, metadata) + (self.run)(world, metadata, evt) } pub fn name(&self) -> &'static str @@ -153,5 +166,5 @@ pub(crate) struct SystemComponent pub(crate) system: TypeErased, } -/// Function in [`TypeErased`] used to run the system. -type TypeErasedRunFn = dyn Fn(&World, Metadata) -> Result<(), Error>; +type TypeErasedRunFn = + fn(&World, Metadata, Option<EmittedEvent<'_>>) -> Result<(), Error>; diff --git a/engine-ecs/src/system/initializable.rs b/engine-ecs/src/system/initializable.rs index b6ec8e8..fa925e0 100644 --- a/engine-ecs/src/system/initializable.rs +++ b/engine-ecs/src/system/initializable.rs @@ -2,11 +2,11 @@ use std::marker::PhantomData; use seq_macro::seq; -use crate::system::{Input, Param as SystemParam, System}; +use crate::system::{Input, Param as SystemParam}; use crate::tuple::{Reduce as TupleReduce, ReduceElement as TupleReduceElement, Tuple}; /// A initializable system. -pub trait Initializable<'world, Impl>: System<'world, Impl> +pub trait Initializable<'world, Impl> { type Inputs; diff --git a/engine-ecs/src/system/observer.rs b/engine-ecs/src/system/observer.rs index 1ad7496..e5345cd 100644 --- a/engine-ecs/src/system/observer.rs +++ b/engine-ecs/src/system/observer.rs @@ -7,15 +7,13 @@ use std::slice::Iter as SliceIter; use seq_macro::seq; use crate::entity::Handle as EntityHandle; -use crate::error::Error; use crate::event::Emitted as EmittedEvent; use crate::pair::Pair; use crate::system::{ - Metadata, + Callbacks, NoCallbacks, Param, ReturnValue as SystemReturnValue, - System, TypeErased as TypeErasedSystem, }; use crate::uid::Uid; @@ -46,13 +44,14 @@ where } /// Observer system. -pub trait Observer<'world, Impl>: System<'world, Impl> +pub trait Observer<'world, Impl> { + type Callbacks: Callbacks; type ObservedEvents: Array<Pair<Uid, Uid>>; fn observed_events() -> Self::ObservedEvents; - fn finish_observer(self) -> (WrapperComponent, Self::Callbacks); + fn finish_observer(self) -> (TypeErasedSystem, Self::Callbacks); } pub struct Observe<'world, ObservedT: Observed> @@ -155,36 +154,17 @@ impl<'world, ObservedT: Observed> EventMatch<'world, ObservedT> macro_rules! impl_observer { ($c: tt) => { seq!(I in 0..$c { - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> System< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Func - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: SystemReturnValue, - #(TParam~I: Param<'world, Input = ()>,)* - { - type Callbacks = NoCallbacks; - - fn finish(self) -> (TypeErasedSystem, NoCallbacks) - { - const { - panic!("Observers cannot be used as regular systems"); - } - } - } - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Observer< 'world, fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Func where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static, Ret: SystemReturnValue, #(TParam~I: Param<'world, Input = ()>,)* { + type Callbacks = NoCallbacks; type ObservedEvents = ObservedT::Events; fn observed_events() -> Self::ObservedEvents @@ -192,16 +172,24 @@ macro_rules! impl_observer { ObservedT::events() } - fn finish_observer(self) -> (WrapperComponent, NoCallbacks) + fn finish_observer(self) -> (TypeErasedSystem, NoCallbacks) { - #[allow(unused)] + #![allow(unused)] - let wrapper_comp = WrapperComponent::new( - move |world, metadata, emitted_event| { + crate::util::const_assert!( + size_of::<Func>() == 0, + "System function is not zero-sized (not function pointer)" + ); + + let system = TypeErasedSystem { + run: |world, metadata, emitted_event| { // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let world = unsafe { &*std::ptr::from_ref(world) }; + let emitted_event = + emitted_event.expect("No event info passed to observer"); + // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let emitted_event = unsafe { @@ -210,14 +198,16 @@ macro_rules! impl_observer { ) }; - self(Observe::new(world, emitted_event), #({ + let func = unsafe { std::mem::zeroed::<Func>() }; + + func(Observe::new(world, emitted_event), #({ TParam~I::new(world, &metadata) },)*).into_result() }, - type_name::<Func>() - ); + name: type_name::<Func>() + }; - (wrapper_comp, NoCallbacks) + (system, NoCallbacks) } } }); @@ -227,52 +217,3 @@ macro_rules! impl_observer { seq!(C in 0..16 { impl_observer!(C); }); - -#[derive(Component)] -pub struct WrapperComponent -{ - run: Box<RunFn>, - name: &'static str, -} - -impl WrapperComponent -{ - pub fn new( - run: impl Fn(&World, Metadata, EmittedEvent<'_>) -> Result<(), Error> + 'static, - name: &'static str, - ) -> Self - { - Self { run: Box::new(run), name } - } - - /// 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<'_>, - ) -> Result<(), Error> - { - (self.run)(world, metadata, emitted_event) - } - - pub fn name(&self) -> &'static str - { - self.name - } -} - -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<'_>) -> Result<(), Error>; diff --git a/engine-ecs/src/system/stateful.rs b/engine-ecs/src/system/stateful.rs index b73baeb..c9354c3 100644 --- a/engine-ecs/src/system/stateful.rs +++ b/engine-ecs/src/system/stateful.rs @@ -1,4 +1,5 @@ use std::any::type_name; +use std::marker::PhantomData; use std::mem::transmute; use seq_macro::seq; @@ -7,12 +8,7 @@ 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::system::observer::{Observe, Observed, Observer}; use crate::system::{ Into as IntoSystem, Metadata, @@ -24,10 +20,10 @@ use crate::system::{ use crate::World; /// A stateful system. -pub struct Stateful<Func> +pub struct Stateful<Func: Copy + 'static> { - func: Func, local_components: Vec<ComponentParts>, + _pd: PhantomData<Func>, } macro_rules! impl_system { @@ -36,7 +32,7 @@ macro_rules! impl_system { impl<'world, Func, Ret, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func> where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static, Ret: ReturnValue, #(TParam~I: Param<'world, Input: 'static>,)* { @@ -44,22 +40,29 @@ macro_rules! impl_system { fn finish(self) -> (TypeErased, Self::Callbacks) { - let Self { func, local_components } = self; + crate::util::const_assert!( + size_of::<Func>() == 0, + "System function is not zero-sized (not function pointer)" + ); + + let Self { local_components, .. } = self; let callbacks = Callbacks { local_components }; let type_erased = TypeErased { - run: Box::new(move |world, metadata| { + run: |world, metadata, _| { // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let world = unsafe { &*std::ptr::from_ref(world) }; + let func = unsafe { std::mem::zeroed::<Func>() }; + func(#({ TParam~I::new(&world, &metadata) },)*); Ok(()) - }), + }, name: type_name::<Func>() }; @@ -71,7 +74,7 @@ macro_rules! impl_system { impl<'world, Func, Ret, #(TParam~I,)*> Initializable<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func> where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static, Ret: ReturnValue, #(TParam~I: Param<'world, Input: 'static>,)* (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> @@ -91,7 +94,7 @@ macro_rules! impl_system { impl<'world, Func, Ret, #(TParam~I,)*> IntoSystem<'world, fn(#(TParam~I,)*) -> Ret> for Func where - Func: Fn(#(TParam~I,)*) -> Ret + 'static, + Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static, Ret: ReturnValue, #(TParam~I: Param<'world>,)* { @@ -100,8 +103,8 @@ macro_rules! impl_system { fn into_system(self) -> Self::System { Self::System { - func: self, local_components: Vec::new(), // TODO: Use Vec::with_capacity + _pd: PhantomData } } } @@ -113,7 +116,7 @@ seq!(C in 1..16 { impl_system!(C); }); -impl<Func> SystemWithLocalComponents for Stateful<Func> +impl<Func: Copy + 'static> SystemWithLocalComponents for Stateful<Func> { fn add_local_component(&mut self, component_parts: ComponentParts) { @@ -149,31 +152,13 @@ fn init_initializable_params<'world, SystemT, Params>( macro_rules! impl_observer { ($c: tt) => { seq!(I in 0..$c { - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> System< - 'world, - fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret - > for Stateful<Func> - where - ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, - Ret: ReturnValue, - #(TParam~I: Param<'world>,)* - { - type Callbacks = Callbacks; - - fn finish(self) -> (TypeErased, Callbacks) - { - unimplemented!(); - } - } - impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Initializable< 'world, fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret > for Stateful<Func> where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static, Ret: ReturnValue, #(TParam~I: Param<'world>,)* (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self> @@ -196,10 +181,11 @@ macro_rules! impl_observer { > for Stateful<Func> where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static, Ret: ReturnValue, #(TParam~I: Param<'world>,)* { + type Callbacks = Callbacks; type ObservedEvents = ObservedT::Events; fn observed_events() -> Self::ObservedEvents @@ -207,20 +193,28 @@ macro_rules! impl_observer { ObservedT::events() } - fn finish_observer(self) -> (ObserverWrapperComponent, Callbacks) + fn finish_observer(self) -> (TypeErased, Callbacks) { #![allow(unused)] - let Self { func, local_components } = self; + crate::util::const_assert!( + size_of::<Func>() == 0, + "System function is not zero-sized (not function pointer)" + ); + + let Self { local_components, .. } = self; let callbacks = Callbacks { local_components }; - let wrapper_comp = ObserverWrapperComponent::new( - move |world, metadata, emitted_event| { + let system = TypeErased { + run: |world, metadata, emitted_event| { // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let world = unsafe { &*std::ptr::from_ref(world) }; + let emitted_event = + emitted_event.expect("No event info passed to observer"); + // SAFETY: The caller of TypeErased::run ensures the lifetime // is correct let emitted_event = unsafe { @@ -229,14 +223,16 @@ macro_rules! impl_observer { ) }; + let func = unsafe { std::mem::zeroed::<Func>() }; + func(Observe::new(world, emitted_event), #({ TParam~I::new(world, &metadata) },)*).into_result() }, - type_name::<Func>() - ); + name: type_name::<Func>() + }; - (wrapper_comp, callbacks) + (system, callbacks) } } @@ -246,7 +242,7 @@ macro_rules! impl_observer { > for Func where ObservedT: Observed, - Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + 'static, + Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static, Ret: ReturnValue, #(TParam~I: Param<'world>,)* { @@ -255,8 +251,8 @@ macro_rules! impl_observer { fn into_system(self) -> Stateful<Func> { Stateful { - func: self, local_components: Vec::new(), // TODO: Use Vec::with_capacity + _pd: PhantomData } } } diff --git a/engine-ecs/src/util.rs b/engine-ecs/src/util.rs index 27e9748..8de44e4 100644 --- a/engine-ecs/src/util.rs +++ b/engine-ecs/src/util.rs @@ -382,6 +382,23 @@ macro_rules! impl_multiple { pub(crate) use impl_multiple; +macro_rules! const_assert { + ($expr: expr, $message: literal) => { + const { + if !($expr) { + panic!(concat!( + "Const assertion failed: ", + stringify!($expr), + ": ", + $message + )) + } + } + }; +} + +pub(crate) use const_assert; + mod sealed { pub trait Sealed {} |
