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::{Masked, Masks, Plan, 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()?;
Ok(ViewIter {
tables: self.world.archetypes(),
ids: self.query.matched().iter(),
masks: Masks::new(self.query.mask()),
cursor: Q::cursor(&mut self.guard),
tick: self.ticks.this_run,
rows: None,
})
}
fn par_rows<'a, G>(&'a mut self, grain: Grain, f: G) -> Result<(), EcsError>
where
G: Fn(Q::Item<'a>) + Sync,
{
self.prepare()?;
let tables: &'a [Archetype] = self.world.archetypes();
let matched = self.query.matched();
let plan = Plan::new(
matched
.iter()
.filter_map(|&id| tables.get(id))
.map(Archetype::len),
grain,
);
let tick = self.ticks.this_run;
let mut masks = Masks::<F>::new(self.query.mask());
let mut cursor = Q::cursor(&mut self.guard);
let rows = matched.iter().filter_map(|&id| {
let entities = tables.get(id)?.entities();
let mask = masks.take(entities.len());
Some((
entities.len(),
Q::lent(&mut cursor, id, entities, tick)?,
mask,
))
});
run::<Q, G>(rows, plan, self.pool, &f, self.world.len())
}
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 = ()> {
tables: &'a [Archetype],
ids: slice::Iter<'a, usize>,
masks: Masks<'a, F>,
cursor: Q::Cursor<'a>,
tick: u64,
rows: Option<Masked<'a, Q::Iter<'a>>>,
}
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.ids.len())
.finish_non_exhaustive()
}
}
impl<'a, Q: QueryData, F: QueryFilter> ViewIter<'a, Q, F> {
#[inline(always)]
fn table(&mut self) -> Option<Option<Masked<'a, Q::Iter<'a>>>> {
let &id = self.ids.next()?;
let entities = self.tables.get(id)?.entities();
let mask = self.masks.take(entities.len());
Some(
Q::lent(&mut self.cursor, id, entities, self.tick)
.map(|rows| Masked::new(Q::iter(rows), mask)),
)
}
}
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> {
loop {
if let Some(item) = self.rows.as_mut().and_then(Iterator::next) {
return Some(item);
}
self.rows = self.table()?;
}
}
fn fold<B, G: FnMut(B, Self::Item) -> B>(mut self, init: B, mut g: G) -> B {
let mut acc = self
.rows
.take()
.into_iter()
.fold(init, |acc, rows| rows.fold(acc, &mut g));
while let Some(rows) = self.table() {
acc = rows
.into_iter()
.fold(acc, |acc, rows| rows.fold(acc, &mut g));
}
acc
}
}
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);