From 98ee6fc3235cfa3b8f4a3a728b3eb95b237d03d9 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Thu, 4 Jan 2024 23:55:06 -0500 Subject: [PATCH] ecs: cleanup --- lyra-ecs/src/system/fn_sys.rs | 391 +++++++++++++++++++++++++++++++++ lyra-ecs/src/system/mod.rs | 393 +--------------------------------- 2 files changed, 396 insertions(+), 388 deletions(-) create mode 100644 lyra-ecs/src/system/fn_sys.rs diff --git a/lyra-ecs/src/system/fn_sys.rs b/lyra-ecs/src/system/fn_sys.rs new file mode 100644 index 0000000..a159f57 --- /dev/null +++ b/lyra-ecs/src/system/fn_sys.rs @@ -0,0 +1,391 @@ +use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}}; + +use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery}}; + +use super::{System, IntoSystem}; + +pub trait FnArgFetcher { + type Arg<'a>: FnArg; + + fn new() -> Self; + + /// Return the appropriate world access if this fetcher gets the world directly. + /// Return [`Access::None`] if you're only fetching components, or resources. + fn world_access(&self) -> Access { + Access::Read + } + + /// Get the arg from the world + /// + /// # Safety + /// The system executor must ensure that on execution of the system, it will be safe to + /// borrow the world. + unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a>; +} + +pub trait FnArg { + type Fetcher: FnArgFetcher; +} + +pub struct FnSystem { + inner: F, + #[allow(dead_code)] + args: Args, +} + +macro_rules! impl_fn_system_tuple { + ( $($name: ident),+ ) => ( + #[allow(non_snake_case)] + impl System for FnSystem + where + F: for<'a> FnMut($($name::Arg<'a>,)+) -> anyhow::Result<()>, + { + fn world_access(&self) -> Access { + todo!() + } + + fn execute(&mut self, world: NonNull) -> anyhow::Result<()> { + $(let $name = unsafe { $name::new().get(world) };)+ + + (self.inner)($($name,)+)?; + + Ok(()) + } + } + + /* impl IntoSystem for F + where + F: FnMut($($name,)+) -> anyhow::Result<()>, + F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>, + { + type System = FnSystem; + + fn into_system(self) -> Self::System { + FnSystem { + args: ($($name::Fetcher::new(),)+), + inner: self + } + } + } */ + + impl IntoSystem<($($name,)+)> for F + where + F: FnMut($($name,)+) -> anyhow::Result<()>, + F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>, + { + type System = FnSystem; + + fn into_system(self) -> Self::System { + FnSystem { + args: ($($name::Fetcher::new(),)+), + inner: self + } + } + } + ); +} + +// Hopefully up to 16 arguments in a system is good enough. +impl_fn_system_tuple!{ A } +impl_fn_system_tuple!{ A, B } +impl_fn_system_tuple!{ A, B, C } +impl_fn_system_tuple!{ A, B, C, D } +impl_fn_system_tuple!{ A, B, C, D, E } +impl_fn_system_tuple!{ A, B, C, D, E, F2 } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O } +impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P } + +/// An ArgFetcher implementation for query [`View`]s +pub struct ViewArgFetcher { + query: Q::Query +} + +impl<'a, Q: AsQuery> FnArg for View<'a, Q> { + type Fetcher = ViewArgFetcher; +} + +impl FnArgFetcher for ViewArgFetcher { + type Arg<'a> = View<'a, Q>; + + fn new() -> Self { + ViewArgFetcher { + query: ::new(), + } + } + + fn world_access(&self) -> Access { + todo!() + } + + unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + let world = &*world.as_ptr(); + let arch = world.archetypes.values().collect(); + let v = View::new(world, self.query, arch); + + v + } +} + +/// An ArgFetcher implementation for borrowing the [`World`]. +pub struct WorldArgFetcher; + +impl<'a> FnArg for &'a World { + type Fetcher = WorldArgFetcher; +} + +impl FnArgFetcher for WorldArgFetcher { + type Arg<'a> = &'a World; + + fn new() -> Self { + WorldArgFetcher + } + + fn world_access(&self) -> Access { + Access::Read + } + + unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + &*world.as_ptr() + } + +} + +/// An ArgFetcher implementation for mutably borrowing the [`World`]. +pub struct WorldMutArgFetcher; + +impl<'a> FnArg for &'a mut World { + type Fetcher = WorldMutArgFetcher; +} + +impl FnArgFetcher for WorldMutArgFetcher { + type Arg<'a> = &'a mut World; + + fn new() -> Self { + WorldMutArgFetcher + } + + fn world_access(&self) -> Access { + Access::Write + } + + unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + &mut *world.as_ptr() + } +} + +pub struct ResourceArgFetcher { + phantom: PhantomData R> +} + +impl<'a, R: ResourceObject> FnArg for Ref<'a, R> { + type Fetcher = ResourceArgFetcher; +} + +impl FnArgFetcher for ResourceArgFetcher { + type Arg<'a> = Ref<'a, R>; + + fn new() -> Self { + ResourceArgFetcher { + phantom: PhantomData + } + } + + unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + let world = world.as_ref(); + world.get_resource::() + } +} + +pub struct ResourceMutArgFetcher { + phantom: PhantomData R> +} + +impl<'a, R: ResourceObject> FnArg for RefMut<'a, R> { + type Fetcher = ResourceMutArgFetcher; +} + +impl FnArgFetcher for ResourceMutArgFetcher { + type Arg<'a> = RefMut<'a, R>; + + fn new() -> Self { + ResourceMutArgFetcher { + phantom: PhantomData + } + } + + unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { + let world = world.as_ref(); + world.get_resource_mut::() + } +} + +#[cfg(test)] +mod tests { + use std::{ptr::NonNull, cell::RefMut}; + + use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View}}; + use super::{System, IntoSystem}; + + struct SomeCounter(u32); + + #[test] + fn simple_system() { + let mut world = World::new(); + let vecs = &[Vec2::rand(), Vec2::rand(), Vec2::rand(), Vec2::rand()]; + world.spawn((vecs[0],)); + world.spawn((vecs[1],)); + world.spawn((vecs[2],)); + world.spawn((vecs[3],)); + + let mut count = 0; + + let test_system = |view: View>| -> anyhow::Result<()> { + let mut vecs = vecs.to_vec(); + for v in view.into_iter() { + let pos = vecs.iter().position(|vec| *vec == *v) + .expect("Failure to find vec inside list"); + vecs.remove(pos); + + println!("Got v at: {:?}", v); + count += 1; + } + + Ok(()) + }; + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + assert_eq!(count, 4); + } + + #[test] + fn multi_view_system() { + let mut world = World::new(); + world.spawn((Vec2::rand(), Vec3::rand())); + world.spawn((Vec2::rand(), Vec3::rand())); + world.spawn((Vec2::rand(),)); + world.spawn((Vec2::rand(),)); + + let mut count = 0; + + let test_system = |view: View<(QueryBorrow, QueryBorrow)>| -> anyhow::Result<()> { + for (v2, v3) in view.into_iter() { + println!("Got v2 at '{:?}' and v3 at: '{:?}'", v2, v3); + count += 1; + } + + Ok(()) + }; + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + assert_eq!(count, 2); + } + + #[test] + fn world_system() { + let mut world = World::new(); + world.spawn((Vec2::rand(), Vec3::rand())); + world.spawn((Vec2::rand(), Vec3::rand())); + world.add_resource(SomeCounter(0)); + + let test_system = |world: &World| -> anyhow::Result<()> { + let mut counter = world.get_resource_mut::(); + counter.0 += 10; + + Ok(()) + }; + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + let test_system = |world: &mut World| -> anyhow::Result<()> { + let mut counter = world.get_resource_mut::(); + counter.0 += 10; + + Ok(()) + }; + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + let counter = world.get_resource::(); + assert_eq!(counter.0, 20); + } + + #[test] + fn function_system() { + let mut world = World::new(); + world.spawn((Vec2::rand(), Vec3::rand())); + world.spawn((Vec2::rand(), Vec3::rand())); + world.add_resource(SomeCounter(0)); + + let test_system = |world: &World| -> anyhow::Result<()> { + let mut counter = world.get_resource_mut::(); + counter.0 += 10; + + Ok(()) + }; + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + #[allow(dead_code)] + fn test_system(world: &mut World) -> anyhow::Result<()> { + let mut counter = world.get_resource_mut::(); + counter.0 += 10; + + Ok(()) + } + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + let counter = world.get_resource::(); + assert_eq!(counter.0, 20); + } + + #[test] + fn resource_system() { + let mut world = World::new(); + world.spawn((Vec2::rand(), Vec3::rand())); + world.spawn((Vec2::rand(), Vec3::rand())); + world.add_resource(SomeCounter(0)); + + let test_system = |mut counter: RefMut| -> anyhow::Result<()> { + counter.0 += 10; + + Ok(()) + }; + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + let counter = world.get_resource::(); + assert_eq!(counter.0, 10); + } + + #[test] + fn multi_arg_system() { + let mut world = World::new(); + world.spawn((Vec2::rand(), )); + world.spawn((Vec2::rand(), )); + world.add_resource(SomeCounter(0)); + + let test_system = |mut counter: RefMut, view: View>| -> anyhow::Result<()> { + for v2 in view.into_iter() { + println!("Got v2 at '{:?}'", v2); + counter.0 += 1; + } + + Ok(()) + }; + + test_system.into_system().execute(NonNull::from(&world)).unwrap(); + + let counter = world.get_resource::(); + assert_eq!(counter.0, 2); + } +} \ No newline at end of file diff --git a/lyra-ecs/src/system/mod.rs b/lyra-ecs/src/system/mod.rs index 074d9f2..ab87811 100644 --- a/lyra-ecs/src/system/mod.rs +++ b/lyra-ecs/src/system/mod.rs @@ -1,6 +1,6 @@ -use std::{ptr::NonNull, marker::PhantomData, cell::{Ref, RefMut}}; +use std::ptr::NonNull; -use crate::{world::World, Access, ResourceObject, query::{Query, View, AsQuery}}; +use crate::{world::World, Access}; pub mod graph; pub use graph::*; @@ -11,6 +11,9 @@ pub use criteria::*; pub mod batched; pub use batched::*; +pub mod fn_sys; +pub use fn_sys::*; + /// A system that does not mutate the world pub trait System { /// A method that indicates the type of access of the world the system requires. @@ -30,390 +33,4 @@ pub trait IntoSystem { type System: System; fn into_system(self) -> Self::System; -} - -pub trait FnArgFetcher { - type Arg<'a>: FnArg; - - fn new() -> Self; - - /// Return the appropriate world access if this fetcher gets the world directly. - /// Return [`Access::None`] if you're only fetching components, or resources. - fn world_access(&self) -> Access { - Access::Read - } - - /// Get the arg from the world - /// - /// # Safety - /// The system executor must ensure that on execution of the system, it will be safe to - /// borrow the world. - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a>; -} - -pub trait FnArg { - type Fetcher: FnArgFetcher; -} - -pub struct FnSystem { - inner: F, - #[allow(dead_code)] - args: Args, -} - -macro_rules! impl_fn_system_tuple { - ( $($name: ident),+ ) => ( - #[allow(non_snake_case)] - impl System for FnSystem - where - F: for<'a> FnMut($($name::Arg<'a>,)+) -> anyhow::Result<()>, - { - fn world_access(&self) -> Access { - todo!() - } - - fn execute(&mut self, world: NonNull) -> anyhow::Result<()> { - $(let $name = unsafe { $name::new().get(world) };)+ - - (self.inner)($($name,)+)?; - - Ok(()) - } - } - - /* impl IntoSystem for F - where - F: FnMut($($name,)+) -> anyhow::Result<()>, - F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>, - { - type System = FnSystem; - - fn into_system(self) -> Self::System { - FnSystem { - args: ($($name::Fetcher::new(),)+), - inner: self - } - } - } */ - - impl IntoSystem<($($name,)+)> for F - where - F: FnMut($($name,)+) -> anyhow::Result<()>, - F: for<'a> FnMut($(<$name::Fetcher as FnArgFetcher>::Arg<'a>,)+) -> anyhow::Result<()>, - { - type System = FnSystem; - - fn into_system(self) -> Self::System { - FnSystem { - args: ($($name::Fetcher::new(),)+), - inner: self - } - } - } - ); -} - -// Hopefully up to 16 arguments in a system is good enough. -impl_fn_system_tuple!{ A } -impl_fn_system_tuple!{ A, B } -impl_fn_system_tuple!{ A, B, C } -impl_fn_system_tuple!{ A, B, C, D } -impl_fn_system_tuple!{ A, B, C, D, E } -impl_fn_system_tuple!{ A, B, C, D, E, F2 } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O } -impl_fn_system_tuple!{ A, B, C, D, E, F2, G, H, I, J, K, L, M, N, O, P } - -/// An ArgFetcher implementation for query [`View`]s -pub struct ViewArgFetcher { - query: Q::Query -} - -impl<'a, Q: AsQuery> FnArg for View<'a, Q> { - type Fetcher = ViewArgFetcher; -} - -impl FnArgFetcher for ViewArgFetcher { - type Arg<'a> = View<'a, Q>; - - fn new() -> Self { - ViewArgFetcher { - query: ::new(), - } - } - - fn world_access(&self) -> Access { - todo!() - } - - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { - let world = &*world.as_ptr(); - let arch = world.archetypes.values().collect(); - let v = View::new(world, self.query, arch); - - v - } -} - -/// An ArgFetcher implementation for borrowing the [`World`]. -pub struct WorldArgFetcher; - -impl<'a> FnArg for &'a World { - type Fetcher = WorldArgFetcher; -} - -impl FnArgFetcher for WorldArgFetcher { - type Arg<'a> = &'a World; - - fn new() -> Self { - WorldArgFetcher - } - - fn world_access(&self) -> Access { - Access::Read - } - - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { - &*world.as_ptr() - } - -} - -/// An ArgFetcher implementation for mutably borrowing the [`World`]. -pub struct WorldMutArgFetcher; - -impl<'a> FnArg for &'a mut World { - type Fetcher = WorldMutArgFetcher; -} - -impl FnArgFetcher for WorldMutArgFetcher { - type Arg<'a> = &'a mut World; - - fn new() -> Self { - WorldMutArgFetcher - } - - fn world_access(&self) -> Access { - Access::Write - } - - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { - &mut *world.as_ptr() - } -} - -pub struct ResourceArgFetcher { - phantom: PhantomData R> -} - -impl<'a, R: ResourceObject> FnArg for Ref<'a, R> { - type Fetcher = ResourceArgFetcher; -} - -impl FnArgFetcher for ResourceArgFetcher { - type Arg<'a> = Ref<'a, R>; - - fn new() -> Self { - ResourceArgFetcher { - phantom: PhantomData - } - } - - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { - let world = world.as_ref(); - world.get_resource::() - } -} - -pub struct ResourceMutArgFetcher { - phantom: PhantomData R> -} - -impl<'a, R: ResourceObject> FnArg for RefMut<'a, R> { - type Fetcher = ResourceMutArgFetcher; -} - -impl FnArgFetcher for ResourceMutArgFetcher { - type Arg<'a> = RefMut<'a, R>; - - fn new() -> Self { - ResourceMutArgFetcher { - phantom: PhantomData - } - } - - unsafe fn get<'a>(&mut self, world: NonNull) -> Self::Arg<'a> { - let world = world.as_ref(); - world.get_resource_mut::() - } -} - -#[cfg(test)] -mod tests { - use std::{ptr::NonNull, cell::RefMut}; - - use crate::{tests::{Vec2, Vec3}, world::World, query::{QueryBorrow, View}}; - use super::{System, IntoSystem}; - - struct SomeCounter(u32); - - #[test] - fn simple_system() { - let mut world = World::new(); - let vecs = &[Vec2::rand(), Vec2::rand(), Vec2::rand(), Vec2::rand()]; - world.spawn((vecs[0],)); - world.spawn((vecs[1],)); - world.spawn((vecs[2],)); - world.spawn((vecs[3],)); - - let mut count = 0; - - let test_system = |view: View>| -> anyhow::Result<()> { - let mut vecs = vecs.to_vec(); - for v in view.into_iter() { - let pos = vecs.iter().position(|vec| *vec == *v) - .expect("Failure to find vec inside list"); - vecs.remove(pos); - - println!("Got v at: {:?}", v); - count += 1; - } - - Ok(()) - }; - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - assert_eq!(count, 4); - } - - #[test] - fn multi_view_system() { - let mut world = World::new(); - world.spawn((Vec2::rand(), Vec3::rand())); - world.spawn((Vec2::rand(), Vec3::rand())); - world.spawn((Vec2::rand(),)); - world.spawn((Vec2::rand(),)); - - let mut count = 0; - - let test_system = |view: View<(QueryBorrow, QueryBorrow)>| -> anyhow::Result<()> { - for (v2, v3) in view.into_iter() { - println!("Got v2 at '{:?}' and v3 at: '{:?}'", v2, v3); - count += 1; - } - - Ok(()) - }; - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - assert_eq!(count, 2); - } - - #[test] - fn world_system() { - let mut world = World::new(); - world.spawn((Vec2::rand(), Vec3::rand())); - world.spawn((Vec2::rand(), Vec3::rand())); - world.add_resource(SomeCounter(0)); - - let test_system = |world: &World| -> anyhow::Result<()> { - let mut counter = world.get_resource_mut::(); - counter.0 += 10; - - Ok(()) - }; - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - let test_system = |world: &mut World| -> anyhow::Result<()> { - let mut counter = world.get_resource_mut::(); - counter.0 += 10; - - Ok(()) - }; - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - let counter = world.get_resource::(); - assert_eq!(counter.0, 20); - } - - #[test] - fn function_system() { - let mut world = World::new(); - world.spawn((Vec2::rand(), Vec3::rand())); - world.spawn((Vec2::rand(), Vec3::rand())); - world.add_resource(SomeCounter(0)); - - let test_system = |world: &World| -> anyhow::Result<()> { - let mut counter = world.get_resource_mut::(); - counter.0 += 10; - - Ok(()) - }; - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - #[allow(dead_code)] - fn test_system(world: &mut World) -> anyhow::Result<()> { - let mut counter = world.get_resource_mut::(); - counter.0 += 10; - - Ok(()) - } - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - let counter = world.get_resource::(); - assert_eq!(counter.0, 20); - } - - #[test] - fn resource_system() { - let mut world = World::new(); - world.spawn((Vec2::rand(), Vec3::rand())); - world.spawn((Vec2::rand(), Vec3::rand())); - world.add_resource(SomeCounter(0)); - - let test_system = |mut counter: RefMut| -> anyhow::Result<()> { - counter.0 += 10; - - Ok(()) - }; - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - let counter = world.get_resource::(); - assert_eq!(counter.0, 10); - } - - #[test] - fn multi_arg_system() { - let mut world = World::new(); - world.spawn((Vec2::rand(), )); - world.spawn((Vec2::rand(), )); - world.add_resource(SomeCounter(0)); - - let test_system = |mut counter: RefMut, view: View>| -> anyhow::Result<()> { - for v2 in view.into_iter() { - println!("Got v2 at '{:?}'", v2); - counter.0 += 1; - } - - Ok(()) - }; - - test_system.into_system().execute(NonNull::from(&world)).unwrap(); - - let counter = world.get_resource::(); - assert_eq!(counter.0, 2); - } } \ No newline at end of file