shiv 0.1.0-alpha.10

A simple modern Entity Component System
Documentation
use std::marker::PhantomData;

use crate::{
    change_detection::MAX_CHANGE_AGE,
    storage::SparseArray,
    world::{World, WorldId},
};

use super::{
    ExclusiveSystemParam, ExclusiveSystemParamFetch, ExclusiveSystemParamItem,
    ExclusiveSystemParamState, In, InputMarker, IntoSystem, System, SystemMeta,
};

pub struct ExclusiveFunctionSystem<In, Out, Param, Marker, F>
where
    Param: ExclusiveSystemParam,
{
    func: F,
    param_state: Option<Param::Fetch>,
    meta: SystemMeta,
    last_change_ticks: SparseArray<u32>,
    world_id: Option<WorldId>,
    _marker: PhantomData<fn() -> (In, Out, Marker)>,
}

impl<In, Out, Param, Marker, F> ExclusiveFunctionSystem<In, Out, Param, Marker, F>
where
    Param: ExclusiveSystemParam,
{
    #[inline]
    fn store_last_change_tick(&mut self) {
        if let Some(index) = self.world_id.map(|id| id.index()) {
            (self.last_change_ticks).insert(index, self.meta.last_change_tick);
        }
    }

    #[inline]
    fn get_last_change_tick(&self, world: &World) -> u32 {
        let index = world.id().index();
        if let Some(&last_change_tick) = self.last_change_ticks.get(index) {
            last_change_tick
        } else {
            world.change_tick().wrapping_sub(MAX_CHANGE_AGE)
        }
    }
}

#[doc(hidden)]
#[derive(Debug)]
pub struct IsExclusive;

impl<In, Out, Param, Marker, F> IntoSystem<In, Out, (Param, Marker, IsExclusive)> for F
where
    In: 'static,
    Out: 'static,
    Param: ExclusiveSystemParam + 'static,
    Marker: 'static,
    F: ExclusiveSystemParamFunction<In, Out, Param, Marker>,
{
    type System = ExclusiveFunctionSystem<In, Out, Param, Marker, F>;

    fn into_system(self) -> Self::System {
        ExclusiveFunctionSystem {
            func: self,
            param_state: None,
            meta: SystemMeta::new::<Self>(),
            last_change_ticks: SparseArray::new(),
            world_id: None,
            _marker: PhantomData,
        }
    }
}

impl<In, Out, Param, Marker, F> System for ExclusiveFunctionSystem<In, Out, Param, Marker, F>
where
    In: 'static,
    Out: 'static,
    Param: ExclusiveSystemParam + 'static,
    Marker: 'static,
    F: ExclusiveSystemParamFunction<In, Out, Param, Marker>,
{
    type In = In;
    type Out = Out;

    #[inline]
    fn meta(&self) -> &SystemMeta {
        &self.meta
    }

    #[inline]
    unsafe fn meta_mut(&mut self) -> &mut SystemMeta {
        &mut self.meta
    }

    #[inline]
    fn is_exclusive(&self) -> bool {
        true
    }

    #[inline]
    fn init(&mut self, world: &mut World) {
        self.store_last_change_tick();
        self.meta.last_change_tick = self.get_last_change_tick(world);

        self.param_state = Some(<Param::Fetch as ExclusiveSystemParamState>::init(
            world,
            &mut self.meta,
        ));

        self.world_id = Some(world.id());
    }

    #[inline]
    unsafe fn run_unchecked(&mut self, _input: Self::In, _world: &World) -> Self::Out {
        panic!();
    }

    #[inline]
    fn run(&mut self, input: Self::In, world: &mut World) -> Self::Out {
        if self.world_id != Some(world.id()) {
            self.init(world);
        }

        let param = <Param as ExclusiveSystemParam>::Fetch::get_param(
            self.param_state.as_mut().unwrap(),
            &self.meta,
        );

        self.func.run(input, world, param)
    }

    #[inline]
    fn apply(&mut self, world: &mut World) {
        let param_state = self.param_state.as_mut().unwrap();
        param_state.apply(world);
    }
}

pub trait ExclusiveSystemParamFunction<In, Out, Param, Marker>: Send + Sync + 'static
where
    Param: ExclusiveSystemParam,
{
    fn run(&mut self, input: In, world: &mut World, item: ExclusiveSystemParamItem<Param>) -> Out;
}

macro_rules! impl_system_param_function {
    (@ $($param:ident),*) => {
        #[allow(non_snake_case)]
        impl<Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<(), Out, ($($param,)*), ()> for Func
        where
            Out: 'static,
            Func: Send + Sync + 'static,
            Func: FnMut(&mut World, $($param),*) -> Out,
            Func: FnMut(&mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
        {
            #[inline]
            fn run(&mut self, _input: (), world: &mut World, item: ExclusiveSystemParamItem<($($param,)*)>) -> Out {
                let ($($param,)*) = item;
                (self)(world, $($param),*)
            }
        }

        #[allow(non_snake_case)]
        impl<Input, Out, Func, $($param: ExclusiveSystemParam),*> ExclusiveSystemParamFunction<Input, Out, ($($param,)*), InputMarker> for Func
        where
            Out: 'static,
            Func: Send + Sync + 'static,
            Func: FnMut(In<Input>, &mut World, $($param),*) -> Out,
            Func: FnMut(In<Input>, &mut World, $(ExclusiveSystemParamItem<$param>),*) -> Out,
        {
            #[inline]
            fn run(&mut self, input: Input, world: &mut World, item: ExclusiveSystemParamItem<($($param,)*)>) -> Out {
                let ($($param,)*) = item;
                (self)(In::new(input), world, $($param),*)
            }
        }
    };
    ($start:ident $(,$ident:ident)*) => {
        impl_system_param_function!(@ $start $(,$ident)*);
        impl_system_param_function!($($ident),*);
    };
    () => {
        impl_system_param_function!(@);
    };
}

impl_system_param_function!(
    A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z
);