use std::any::TypeId;
use crate::ecs::resources::Resources;
pub trait SystemParam {
type Item<'a>;
type State: Default + 'static;
fn fetch<'a>(
world: &'a hecs::World,
resources: &'a Resources,
state: &'a mut Self::State,
) -> Self::Item<'a>;
}
impl<'a> SystemParam for &'a hecs::World {
type Item<'w> = &'w hecs::World;
type State = ();
fn fetch<'w>(
world: &'w hecs::World,
_resources: &'w Resources,
_state: &'w mut Self::State,
) -> Self::Item<'w> {
world
}
}
impl<'a> SystemParam for &'a Resources {
type Item<'w> = &'w Resources;
type State = ();
fn fetch<'w>(
_world: &'w hecs::World,
resources: &'w Resources,
_state: &'w mut Self::State,
) -> Self::Item<'w> {
resources
}
}
pub trait System: 'static {
fn run(&mut self, world: &hecs::World, resources: &Resources);
}
pub struct FunctionSystem<F, Marker, State = ()> {
pub func: F,
state: State,
_marker: std::marker::PhantomData<Marker>,
}
pub trait IntoSystem<Marker> {
type System: System;
fn into_system(self) -> Self::System;
}
enum OrderConstraint {
After(TypeId),
Before(TypeId),
}
pub struct SystemConfig<S, Params> {
system: S,
priority: i32,
constraints: Vec<OrderConstraint>,
_marker: std::marker::PhantomData<fn() -> Params>,
}
impl<S, Params> SystemConfig<S, Params>
where
S: IntoSystem<Params> + 'static,
{
pub fn after<S2, P2>(mut self, _other: S2) -> Self
where
S2: IntoSystem<P2> + 'static,
{
self.constraints.push(OrderConstraint::After(TypeId::of::<S2>()));
self
}
pub fn before<S2, P2>(mut self, _other: S2) -> Self
where
S2: IntoSystem<P2> + 'static,
{
self.constraints.push(OrderConstraint::Before(TypeId::of::<S2>()));
self
}
pub fn priority(mut self, priority: i32) -> Self {
self.priority = priority;
self
}
#[doc(hidden)]
pub fn into_parts(self) -> (TypeId, Box<dyn System>, i32, Vec<(TypeId, TypeId)>) {
let id = TypeId::of::<S>();
let constraints = self
.constraints
.into_iter()
.map(|constraint| match constraint {
OrderConstraint::After(dependency) => (id, dependency),
OrderConstraint::Before(dependent) => (dependent, id),
})
.collect();
(id, Box::new(self.system.into_system()), self.priority, constraints)
}
}
impl<S, Params> From<S> for SystemConfig<S, Params>
where
S: IntoSystem<Params> + 'static,
{
fn from(system: S) -> Self {
SystemConfig {
system,
priority: 0,
constraints: Vec::new(),
_marker: std::marker::PhantomData,
}
}
}
pub trait IntoSystemConfig<Params>: IntoSystem<Params> + Sized {
fn after<S2, P2>(self, other: S2) -> SystemConfig<Self, Params>
where
S2: IntoSystem<P2> + 'static;
fn before<S2, P2>(self, other: S2) -> SystemConfig<Self, Params>
where
S2: IntoSystem<P2> + 'static;
fn priority(self, priority: i32) -> SystemConfig<Self, Params>;
}
impl<T, Params> IntoSystemConfig<Params> for T
where
T: IntoSystem<Params> + 'static,
{
fn after<S2, P2>(self, other: S2) -> SystemConfig<Self, Params>
where
S2: IntoSystem<P2> + 'static,
{
SystemConfig::from(self).after(other)
}
fn before<S2, P2>(self, other: S2) -> SystemConfig<Self, Params>
where
S2: IntoSystem<P2> + 'static,
{
SystemConfig::from(self).before(other)
}
fn priority(self, priority: i32) -> SystemConfig<Self, Params> {
SystemConfig::from(self).priority(priority)
}
}
pub struct SystemChain {
systems: Vec<(TypeId, Box<dyn System>, i32)>,
constraints: Vec<(TypeId, TypeId)>,
}
impl SystemChain {
fn first_id(&self) -> TypeId {
self.systems[0].0
}
fn last_id(&self) -> TypeId {
self.systems[self.systems.len() - 1].0
}
pub fn after<S2, P2>(mut self, _other: S2) -> Self
where
S2: IntoSystem<P2> + 'static,
{
let first = self.first_id();
self.constraints.push((first, TypeId::of::<S2>()));
self
}
pub fn before<S2, P2>(mut self, _other: S2) -> Self
where
S2: IntoSystem<P2> + 'static,
{
let last = self.last_id();
self.constraints.push((TypeId::of::<S2>(), last));
self
}
pub fn priority(mut self, priority: i32) -> Self {
for (_, _, p) in &mut self.systems {
*p = priority;
}
self
}
#[doc(hidden)]
pub fn into_parts(self) -> (Vec<(TypeId, Box<dyn System>, i32)>, Vec<(TypeId, TypeId)>) {
(self.systems, self.constraints)
}
}
pub trait Chain<Marker> {
fn chain(self) -> SystemChain;
}
macro_rules! impl_chain {
($($S:ident : $P:ident),+) => {
impl<$($S, $P),+> Chain<($($P,)+)> for ($($S,)+)
where
$($S: IntoSystem<$P> + 'static,)+
{
#[allow(non_snake_case)]
fn chain(self) -> SystemChain {
let ($($S,)+) = self;
let systems: Vec<(TypeId, Box<dyn System>, i32)> = vec![
$((TypeId::of::<$S>(), Box::new($S.into_system()) as Box<dyn System>, 0),)+
];
let constraints = systems.windows(2).map(|w| (w[1].0, w[0].0)).collect();
SystemChain { systems, constraints }
}
}
};
}
impl_chain!(A: PA, B: PB);
impl_chain!(A: PA, B: PB, C: PC);
impl_chain!(A: PA, B: PB, C: PC, D: PD);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE, F: PF);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE, F: PF, G: PG);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE, F: PF, G: PG, H: PH);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE, F: PF, G: PG, H: PH, I: PI);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE, F: PF, G: PG, H: PH, I: PI, J: PJ);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE, F: PF, G: PG, H: PH, I: PI, J: PJ, K: PK);
impl_chain!(A: PA, B: PB, C: PC, D: PD, E: PE, F: PF, G: PG, H: PH, I: PI, J: PJ, K: PK, L: PL);
macro_rules! impl_system {
($($param:ident),*) => {
impl<T, $($param),*> IntoSystem<($($param,)*)> for T
where
T: FnMut($($param),*) + for<'a> FnMut($($param::Item<'a>),*) + 'static,
$($param: SystemParam + 'static),*
{
type System = FunctionSystem<T, ($($param,)*), ($($param::State,)*)>;
fn into_system(self) -> Self::System {
FunctionSystem {
func: self,
state: Default::default(),
_marker: std::marker::PhantomData,
}
}
}
impl<T, $($param),*> System for FunctionSystem<T, ($($param,)*), ($($param::State,)*)>
where
T: FnMut($($param),*) + for<'a> FnMut($($param::Item<'a>),*) + 'static,
$($param: SystemParam + 'static),*
{
fn run(&mut self, _world: &hecs::World, _resources: &Resources) {
#[allow(non_snake_case)]
let ($($param,)*) = &mut self.state;
(self.func)($($param::fetch(_world, _resources, $param)),*);
}
}
};
}
impl_system!();
impl_system!(A);
impl_system!(A, B);
impl_system!(A, B, C);
impl_system!(A, B, C, D);
impl_system!(A, B, C, D, E);
impl_system!(A, B, C, D, E, F);
impl_system!(A, B, C, D, E, F, G);
impl_system!(A, B, C, D, E, F, G, H);
impl_system!(A, B, C, D, E, F, G, H, I);
impl_system!(A, B, C, D, E, F, G, H, I, J);
impl_system!(A, B, C, D, E, F, G, H, I, J, K);
impl_system!(A, B, C, D, E, F, G, H, I, J, K, L);