use std::error::Error;
use std::fmt;
use std::marker::PhantomData;
use std::slice;
use pragma_core::graph::TaskContext;
use pragma_core::jobs::{Grain, ThreadPool};
use crate::access::{Access, AccessKind};
use crate::archetype::Archetype;
use crate::query::{Plan, Rows, Tables, run};
use crate::{
EcsError, Query, QueryData, QueryFilter, ReadOnlyQueryData, Res, ResMut, Resource, World,
};
mod sealed {
pub trait Sealed {}
pub trait SealedSystem<Marker> {}
}
pub(crate) use sealed::Sealed;
use sealed::SealedSystem;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) struct SystemTicks {
pub(crate) last_run: u64,
pub(crate) this_run: u64,
}
#[doc(hidden)]
#[derive(Clone, Copy)]
pub struct Env<'w> {
ctx: &'w TaskContext<'w>,
world: &'w World,
ticks: SystemTicks,
}
impl fmt::Debug for Env<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Env")
.field("system", &self.ctx.name())
.finish_non_exhaustive()
}
}
impl<'w> Env<'w> {
pub(crate) const fn new(
ctx: &'w TaskContext<'w>,
world: &'w World,
ticks: SystemTicks,
) -> Self {
Self { ctx, world, ticks }
}
pub(crate) const fn ctx(&self) -> &'w TaskContext<'w> {
self.ctx
}
pub(crate) const fn world(&self) -> &'w World {
self.world
}
}
pub trait SystemParam: Sealed {
#[doc(hidden)]
type State: Send + 'static;
type Item<'w, 's>;
#[doc(hidden)]
fn init() -> Result<Self::State, EcsError>;
#[doc(hidden)]
fn declare(access: &mut Access) -> Result<(), EcsError>;
#[doc(hidden)]
fn fetch<'w, 's>(
state: &'s mut Self::State,
env: Env<'w>,
) -> Result<Self::Item<'w, 's>, EcsError>;
#[doc(hidden)]
fn apply(_state: &mut Self::State, _world: &mut World) -> Result<(), EcsError> {
Ok(())
}
#[doc(hidden)]
fn discard(_state: &mut Self::State) {}
}
pub struct View<'w, 's, Q: QueryData, F: QueryFilter = ()> {
world: &'w World,
pool: &'w ThreadPool,
guard: Q::Guard<'w>,
filter: F::Guard<'w>,
ticks: SystemTicks,
query: &'s mut Query<Q, F>,
}
impl<Q: QueryData, F: QueryFilter> fmt::Debug for View<'_, '_, Q, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("View")
.field("query", &self.query)
.finish_non_exhaustive()
}
}
impl<'w, Q: QueryData, F: QueryFilter> View<'w, '_, Q, F> {
#[must_use]
pub fn access(&self) -> &Access {
self.query.access()
}
pub fn iter(&mut self) -> Result<ViewIter<'_, Q, F>, EcsError>
where
Q: ReadOnlyQueryData,
{
self.rows()
}
pub fn iter_mut(&mut self) -> Result<ViewIter<'_, Q, F>, EcsError> {
self.rows()
}
pub fn par_for_each<'a, G>(&'a mut self, grain: Grain, f: G) -> Result<(), EcsError>
where
Q: ReadOnlyQueryData,
G: Fn(Q::Item<'a>) + Sync,
{
self.par_rows(grain, f)
}
pub fn par_for_each_mut<'a, G>(&'a mut self, grain: Grain, f: G) -> Result<(), EcsError>
where
G: Fn(Q::Item<'a>) + Sync,
{
self.par_rows(grain, f)
}
fn rows(&mut self) -> Result<ViewIter<'_, Q, F>, EcsError> {
self.prepare()?;
let (tables, mask) = self.lent();
Ok(ViewIter {
rows: Rows::new(tables, mask),
})
}
fn par_rows<'a, G>(&'a mut self, grain: Grain, f: G) -> Result<(), EcsError>
where
G: Fn(Q::Item<'a>) + Sync,
{
self.prepare()?;
let plan = Plan::new(self.world.archetypes(), self.query.matched(), grain);
let (pool, entities) = (self.pool, self.world.len());
let (tables, mask) = self.lent();
run::<Q, F, G>(tables, mask, plan, pool, &f, entities)
}
fn lent(&mut self) -> (Lent<'_, Q>, &[bool]) {
let query: &Query<Q, F> = self.query;
let tables = Lent {
tables: self.world.archetypes(),
ids: query.matched().iter(),
cursor: Q::cursor(&mut self.guard),
tick: self.ticks.this_run,
};
(tables, query.mask())
}
fn prepare(&mut self) -> Result<(), EcsError> {
self.query.update(self.world)?;
let (filter, guard) = (&self.filter, &self.guard);
self.query.fill_mask(
self.world.archetypes(),
self.ticks.last_run,
self.world.len(),
|id, ty| F::lent_ticks(filter, ty, id).or_else(|| Q::lent_ticks(guard, ty, id)),
)
}
}
impl<Q: QueryData, F: QueryFilter> Sealed for View<'_, '_, Q, F> {}
impl<Q, F> SystemParam for View<'_, '_, Q, F>
where
Q: QueryData + 'static,
F: QueryFilter + 'static,
{
type State = Query<Q, F>;
type Item<'w, 's> = View<'w, 's, Q, F>;
fn init() -> Result<Self::State, EcsError> {
Query::new()
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
let mut view = Access::new();
Q::declare(&mut view)?;
F::declare(&mut view)?;
access.merge(&view)
}
fn fetch<'w, 's>(
state: &'s mut Self::State,
env: Env<'w>,
) -> Result<Self::Item<'w, 's>, EcsError> {
Ok(View {
world: env.world,
pool: env.ctx.pool(),
guard: Q::acquire(env.ctx)?,
filter: F::acquire::<Q>(env.ctx)?,
ticks: env.ticks,
query: state,
})
}
}
pub struct ViewIter<'a, Q: QueryData, F: QueryFilter = ()> {
rows: Rows<'a, 'a, Q, F, Lent<'a, Q>>,
}
impl<Q: QueryData, F: QueryFilter> fmt::Debug for ViewIter<'_, Q, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ViewIter")
.field("tables_left", &self.rows.left())
.finish_non_exhaustive()
}
}
impl<'a, Q: QueryData, F: QueryFilter> Iterator for ViewIter<'a, Q, F> {
type Item = Q::Item<'a>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.rows.next()
}
fn fold<B, G: FnMut(B, Self::Item) -> B>(self, init: B, g: G) -> B {
self.rows.fold(init, g)
}
}
struct Lent<'a, Q: QueryData> {
tables: &'a [Archetype],
ids: slice::Iter<'a, usize>,
cursor: Q::Cursor<'a>,
tick: u64,
}
impl<'a, Q: QueryData> Tables<'a, Q> for Lent<'a, Q> {
#[inline(always)]
fn next_table(&mut self) -> Option<(usize, Option<Q::Slice<'a>>)> {
let &id = self.ids.next()?;
let entities = self.tables.get(id)?.entities();
let rows = Q::lent(&mut self.cursor, id, entities, self.tick);
Some((entities.len(), rows))
}
fn left(&self) -> usize {
self.ids.len()
}
}
impl<T: Resource> Sealed for Res<'_, T> {}
impl<T: Resource> SystemParam for Res<'_, T> {
type State = ();
type Item<'w, 's> = Res<'w, T>;
fn init() -> Result<Self::State, EcsError> {
Ok(())
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_resource::<T>(AccessKind::Read)
}
fn fetch<'w, 's>((): &'s mut (), env: Env<'w>) -> Result<Self::Item<'w, 's>, EcsError> {
env.ctx.read::<T>().map(Res).map_err(EcsError::graph)
}
}
impl<T: Resource> Sealed for ResMut<'_, T> {}
impl<T: Resource> SystemParam for ResMut<'_, T> {
type State = ();
type Item<'w, 's> = ResMut<'w, T>;
fn init() -> Result<Self::State, EcsError> {
Ok(())
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
access.add_resource::<T>(AccessKind::Write)
}
fn fetch<'w, 's>((): &'s mut (), env: Env<'w>) -> Result<Self::Item<'w, 's>, EcsError> {
env.ctx.write::<T>().map(ResMut).map_err(EcsError::graph)
}
}
pub(crate) type BoxError = Box<dyn Error + Send + Sync>;
pub trait SystemOutput: Sealed {
#[doc(hidden)]
fn into_result(self) -> Result<(), Box<dyn Error + Send + Sync>>;
}
impl SystemOutput for () {
fn into_result(self) -> Result<(), BoxError> {
Ok(())
}
}
impl<E> Sealed for Result<(), E> {}
impl<E: Into<BoxError>> SystemOutput for Result<(), E> {
fn into_result(self) -> Result<(), BoxError> {
self.map_err(Into::into)
}
}
#[doc(hidden)]
pub struct Prepared {
pub(crate) access: Access,
pub(crate) system: Box<dyn RunSystem>,
}
impl fmt::Debug for Prepared {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Prepared")
.field("access", &self.access)
.finish_non_exhaustive()
}
}
#[doc(hidden)]
pub trait RunSystem: Send {
fn run(&mut self, env: Env<'_>) -> Result<(), BoxError>;
fn apply(&mut self, world: &mut World) -> Result<(), EcsError>;
fn discard(&mut self);
}
pub trait IntoSystem<Marker>: SealedSystem<Marker> {
#[doc(hidden)]
fn into_system(self) -> Result<Prepared, EcsError>;
}
struct FunctionSystem<Func, P: SystemParam, Out> {
func: Func,
state: P::State,
marker: PhantomData<fn() -> Out>,
}
type ParamItem<'w, 's, P> = <P as SystemParam>::Item<'w, 's>;
macro_rules! function_system {
($($param:ident $value:ident),*) => {
impl<$($param: SystemParam),*> Sealed for ($($param,)*) {}
#[allow(unused_variables)]
impl<$($param: SystemParam),*> SystemParam for ($($param,)*) {
type State = ($($param::State,)*);
type Item<'w, 's> = ($($param::Item<'w, 's>,)*);
fn init() -> Result<Self::State, EcsError> {
Ok(($($param::init()?,)*))
}
fn declare(access: &mut Access) -> Result<(), EcsError> {
$($param::declare(access)?;)*
Ok(())
}
fn fetch<'w, 's>(
state: &'s mut Self::State,
env: Env<'w>,
) -> Result<Self::Item<'w, 's>, EcsError> {
let ($($value,)*) = state;
Ok(($($param::fetch($value, env)?,)*))
}
fn apply(state: &mut Self::State, world: &mut World) -> Result<(), EcsError> {
let ($($value,)*) = state;
$($param::apply($value, world)?;)*
Ok(())
}
fn discard(state: &mut Self::State) {
let ($($value,)*) = state;
$($param::discard($value);)*
}
}
impl<Func, Out, $($param),*> SealedSystem<fn($($param,)*) -> Out> for Func
where
Func: Send + 'static,
for<'a> &'a mut Func:
FnMut($($param),*) -> Out + FnMut($(ParamItem<'_, '_, $param>),*) -> Out,
Out: SystemOutput + 'static,
$($param: SystemParam + 'static,)*
{
}
impl<Func, Out, $($param),*> IntoSystem<fn($($param,)*) -> Out> for Func
where
Func: Send + 'static,
for<'a> &'a mut Func:
FnMut($($param),*) -> Out + FnMut($(ParamItem<'_, '_, $param>),*) -> Out,
Out: SystemOutput + 'static,
$($param: SystemParam + 'static,)*
{
fn into_system(self) -> Result<Prepared, EcsError> {
let mut access = Access::new();
<($($param,)*) as SystemParam>::declare(&mut access)?;
let system = FunctionSystem::<Func, ($($param,)*), Out> {
func: self,
state: <($($param,)*) as SystemParam>::init()?,
marker: PhantomData,
};
Ok(Prepared {
access,
system: Box::new(system),
})
}
}
impl<Func, Out, $($param),*> RunSystem for FunctionSystem<Func, ($($param,)*), Out>
where
Func: Send + 'static,
for<'a> &'a mut Func:
FnMut($($param),*) -> Out + FnMut($(ParamItem<'_, '_, $param>),*) -> Out,
Out: SystemOutput + 'static,
$($param: SystemParam + 'static,)*
{
fn run(&mut self, env: Env<'_>) -> Result<(), BoxError> {
#[allow(clippy::too_many_arguments)]
fn call<Out, $($param),*>(
mut f: impl FnMut($($param),*) -> Out,
$($value: $param,)*
) -> Out {
f($($value),*)
}
let ($($value,)*) = <($($param,)*) as SystemParam>::fetch(&mut self.state, env)?;
call(&mut self.func, $($value),*).into_result()
}
fn apply(&mut self, world: &mut World) -> Result<(), EcsError> {
<($($param,)*) as SystemParam>::apply(&mut self.state, world)
}
fn discard(&mut self) {
<($($param,)*) as SystemParam>::discard(&mut self.state);
}
}
};
}
function_system!();
function_system!(P0 p0);
function_system!(P0 p0, P1 p1);
function_system!(P0 p0, P1 p1, P2 p2);
function_system!(P0 p0, P1 p1, P2 p2, P3 p3);
function_system!(P0 p0, P1 p1, P2 p2, P3 p3, P4 p4);
function_system!(P0 p0, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5);
function_system!(P0 p0, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6);
function_system!(P0 p0, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7);