diff options
Diffstat (limited to 'ecs/src/system/observer.rs')
-rw-r--r-- | ecs/src/system/observer.rs | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/ecs/src/system/observer.rs b/ecs/src/system/observer.rs new file mode 100644 index 0000000..5455fd4 --- /dev/null +++ b/ecs/src/system/observer.rs @@ -0,0 +1,276 @@ +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::new::<Relation>(Target::id())] + } +} + +/// 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<'_> + { + ObserveIter { + world: self.world, + inner: self.emitted_event.match_ids.iter(), + } + } +} + +impl<'a, ObservedT: Observed> IntoIterator for &'a Observe<'_, ObservedT> +{ + type IntoIter = ObserveIter<'a>; + type Item = EntityHandle<'a>; + + fn into_iter(self) -> Self::IntoIter + { + self.iter() + } +} + +pub struct ObserveIter<'observe> +{ + world: &'observe World, + inner: SliceIter<'observe, Uid>, +} + +impl<'observe> Iterator for ObserveIter<'observe> +{ + type Item = EntityHandle<'observe>; + + fn next(&mut self) -> Option<Self::Item> + { + let target = *self.inner.next()?; + + self.world.get_entity(target) + } +} + +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<'_>); |