use crate::prelude::*;
pub struct System<Out = ()> {
pub initialize: Box<dyn Send + Sync + Fn(&mut World)>,
pub run: Box<dyn Send + Sync + FnMut(&World) -> SystemResult<Out>>,
pub name: &'static str,
}
impl<Out> std::fmt::Debug for System<Out> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("System")
.field("name", &self.name)
.finish_non_exhaustive()
}
}
impl<Out> System<Out> {
pub fn initialize(&self, world: &mut World) {
(self.initialize)(world)
}
pub fn run(&mut self, world: &World) -> SystemResult<Out> {
(self.run)(world)
}
pub fn name(&self) -> &'static str {
self.name
}
}
pub trait IntoSystem<Args, Out> {
fn system(self) -> System<Out>;
}
impl<Out> IntoSystem<System<Out>, Out> for System<Out> {
fn system(self) -> System<Out> {
self
}
}
impl<F, Out> IntoSystem<(World, F), Out> for F
where
F: FnMut(&World) -> Out + Send + Sync + 'static,
{
fn system(mut self) -> System<Out> {
System {
initialize: Box::new(|_| ()),
run: Box::new(move |world| Ok(self(world))),
name: std::any::type_name::<F>(),
}
}
}
impl<F, Out> IntoSystem<(World, F, SystemResult<Out>), Out> for F
where
F: FnMut(&World) -> SystemResult<Out> + Send + Sync + 'static,
{
fn system(self) -> System<Out> {
System {
initialize: Box::new(|_| ()),
run: Box::new(self),
name: std::any::type_name::<F>(),
}
}
}
pub trait SystemParam: Sized {
type State;
type Param<'s>;
fn initialize(world: &mut World);
fn get_state(world: &World) -> Self::State;
#[allow(clippy::needless_lifetimes)] fn borrow<'s>(state: &'s mut Self::State) -> Self::Param<'s>;
}
pub struct Res<'a, T: TypedEcsData + Default>(AtomicRef<'a, T>);
impl<'a, T: TypedEcsData + Default> std::ops::Deref for Res<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct ResMut<'a, T: TypedEcsData + Default>(AtomicRefMut<'a, T>);
impl<'a, T: TypedEcsData + Default> std::ops::Deref for ResMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'a, T: TypedEcsData + Default> std::ops::DerefMut for ResMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<'a, T: TypedEcsData + Default> SystemParam for Res<'a, T> {
type State = AtomicResource<T>;
type Param<'p> = Res<'p, T>;
fn initialize(world: &mut World) {
world.resources.init::<T>()
}
fn get_state(world: &World) -> Self::State {
world.resources.get::<T>()
}
fn borrow(state: &mut Self::State) -> Self::Param<'_> {
Res(state.borrow())
}
}
impl<'a, T: TypedEcsData + Default> SystemParam for ResMut<'a, T> {
type State = AtomicResource<T>;
type Param<'p> = ResMut<'p, T>;
fn initialize(world: &mut World) {
world.resources.init::<T>();
}
fn get_state(world: &World) -> Self::State {
world.resources.get::<T>()
}
fn borrow(state: &mut Self::State) -> Self::Param<'_> {
ResMut(state.borrow_mut())
}
}
pub type Comp<'a, T> = AtomicComponentStoreRef<'a, T>;
pub type CompMut<'a, T> = AtomicComponentStoreRefMut<'a, T>;
impl<'a, T: TypedEcsData> SystemParam for Comp<'a, T> {
type State = AtomicComponentStore<T>;
type Param<'p> = Comp<'p, T>;
fn initialize(world: &mut World) {
world.components.init::<T>();
}
fn get_state(world: &World) -> Self::State {
world.components.get::<T>()
}
fn borrow(state: &mut Self::State) -> Self::Param<'_> {
state.borrow()
}
}
impl<'a, T: TypedEcsData> SystemParam for CompMut<'a, T> {
type State = AtomicComponentStore<T>;
type Param<'p> = CompMut<'p, T>;
fn initialize(world: &mut World) {
world.components.init::<T>();
}
fn get_state(world: &World) -> Self::State {
world.components.get::<T>()
}
fn borrow(state: &mut Self::State) -> Self::Param<'_> {
state.borrow_mut()
}
}
macro_rules! impl_system {
($($args:ident,)*) => {
#[allow(unused_parens)]
impl<
F,
Out,
$(
$args: SystemParam,
)*
> IntoSystem<(F, $($args,)*), Out> for F
where for<'a> F: 'static + Send + Sync +
FnMut(
$(
<$args as SystemParam>::Param<'a>,
)*
) -> SystemResult<Out> +
FnMut(
$(
$args,
)*
) -> SystemResult<Out>
{
fn system(mut self) -> System<Out> {
System {
name: std::any::type_name::<F>(),
initialize: Box::new(|_world| {
$(
$args::initialize(_world);
)*
}),
run: Box::new(move |_world| {
$(
#[allow(non_snake_case)]
let mut $args = $args::get_state(_world);
)*
self(
$(
$args::borrow(&mut $args),
)*
)
})
}
}
}
};
}
macro_rules! impl_system_with_empty_return {
($($args:ident,)*) => {
#[allow(unused_parens)]
impl<
F,
$(
$args: SystemParam,
)*
> IntoSystem<(F, $($args,)* ()), ()> for F
where for<'a> F: 'static + Send + Sync +
FnMut(
$(
<$args as SystemParam>::Param<'a>,
)*
) +
FnMut(
$(
$args,
)*
)
{
fn system(mut self) -> System<()> {
System {
name: std::any::type_name::<F>(),
initialize: Box::new(|_world| {
$(
$args::initialize(_world);
)*
}),
run: Box::new(move |_world| {
$(
#[allow(non_snake_case)]
let mut $args = $args::get_state(_world);
)*
self(
$(
$args::borrow(&mut $args),
)*
);
Ok(())
})
}
}
}
};
}
macro_rules! impl_systems {
() => {};
($head:ident, $($idents:ident,)*) => {
impl_system!($head, $($idents,)*);
impl_system_with_empty_return!($head, $($idents,)*);
impl_systems!($($idents,)*);
}
}
impl_system!();
impl_system_with_empty_return!();
impl_systems!(A, B, C, D, E, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z,);
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn convert_system() {
fn tmp(
_var1: AtomicComponentStoreRef<u32>,
_var2: AtomicComponentStoreRef<u64>,
_var3: Res<i32>,
_var4: ResMut<i64>,
) -> SystemResult {
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn tmp2(
_var7: Comp<i64>,
_var8: CompMut<i64>,
_var1: Res<u32>,
_var2: ResMut<u64>,
_var3: Res<u32>,
_var4: ResMut<u64>,
_var5: Res<u32>,
_var6: ResMut<u64>,
_var9: Comp<i64>,
_var10: CompMut<i64>,
_var11: Comp<i64>,
_var12: CompMut<u64>,
) -> SystemResult {
Ok(())
}
let _ = tmp.system();
let _ = tmp2.system();
}
#[test]
fn system_is_send() {
let x = 6;
send(
(move |_var1: Res<u32>| {
let _y = x;
Ok(())
})
.system(),
);
send((|| Ok(())).system());
send(sys.system());
}
fn sys(_var1: Res<u32>) -> SystemResult {
Ok(())
}
fn send<T: Send>(_t: T) {}
#[test]
fn manual_system_run() {
let mut world = World::default();
world.resources.init::<u32>();
}
#[test]
fn system_replace_resource() {
#[derive(Default, TypeUlid, Clone, PartialEq, Eq, Debug)]
#[ulid = "01GNDP03R29SDA1S009KTQF18Y"]
pub struct A;
#[derive(Default, TypeUlid, Clone, Debug)]
#[ulid = "01GNDP0C73TAV0TDKZZB39NQ8C"]
pub struct B {
x: u32,
}
let mut world = World::default();
let mut my_system = (|_a: Res<A>, mut b: ResMut<B>| {
let b2 = B { x: 45 };
*b = b2;
Ok(())
})
.system();
assert!(world.resources.try_get::<B>().is_none());
my_system.initialize(&mut world);
let res = world.resources.get::<B>();
assert_eq!(res.borrow().x, 0);
my_system.run(&world).unwrap();
let res = world.resources.get::<B>();
assert_eq!(res.borrow().x, 45);
let res = world.resources.get::<A>();
assert_eq!(*res.borrow(), A);
}
}