summaryrefslogtreecommitdiff
path: root/ecs/src/system.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2024-02-25 23:25:03 +0100
committerHampusM <hampus@hampusmat.com>2024-02-26 19:54:00 +0100
commit1019924a29527eba2c8ec8bd976ece6ed76075b0 (patch)
treeb26d8bd872684a0c87802e057d8952c42be31f7a /ecs/src/system.rs
parent00055d6af92d59a86eb00f110c77c699a562d33e (diff)
feat(ecs): add support for multiple system queries & local components
Diffstat (limited to 'ecs/src/system.rs')
-rw-r--r--ecs/src/system.rs226
1 files changed, 193 insertions, 33 deletions
diff --git a/ecs/src/system.rs b/ecs/src/system.rs
index 22e2215..d90e0a2 100644
--- a/ecs/src/system.rs
+++ b/ecs/src/system.rs
@@ -1,16 +1,21 @@
-use std::any::Any;
+use std::any::{Any, TypeId};
use std::convert::Infallible;
use std::fmt::Debug;
+use std::mem::{transmute_copy, ManuallyDrop};
+use std::ptr::addr_of_mut;
-use crate::component::Sequence as ComponentSequence;
-use crate::{ComponentStorage, Query};
+use seq_macro::seq;
+
+use crate::component::Component;
+use crate::system::util::check_params_are_compatible;
+use crate::ComponentStorage;
pub mod stateful;
+mod util;
+
pub trait System<Impl>: 'static
{
- type Query<'a>;
-
type Input;
#[must_use]
@@ -19,39 +24,90 @@ pub trait System<Impl>: 'static
fn run(&mut self, component_storage: &mut ComponentStorage);
fn into_type_erased(self) -> TypeErased;
-}
-impl<Func, Comps> System<fn(Query<Comps>)> for Func
-where
- Func: Fn(Query<Comps>) + 'static,
- Comps: ComponentSequence,
-{
- type Input = Infallible;
- type Query<'a> = Query<'a, Comps>;
+ fn get_local_component_mut<LocalComponent: Component>(
+ &mut self,
+ ) -> Option<&mut LocalComponent>;
- fn initialize(self, _input: Self::Input) -> Self
- {
- self
- }
-
- fn run(&mut self, component_storage: &mut ComponentStorage)
- {
- self(Query::new(component_storage));
- }
+ fn set_local_component<LocalComponent: Component>(
+ &mut self,
+ local_component: LocalComponent,
+ );
+}
- fn into_type_erased(self) -> TypeErased
- {
- TypeErased {
- data: Box::new(self),
- func: Box::new(|data, component_storage| {
- let me = data.downcast_mut::<Func>().unwrap();
-
- me.run(component_storage);
- }),
- }
- }
+macro_rules! impl_system {
+ ($c: tt) => {
+ seq!(I in 0..=$c {
+ impl<'world, Func, #(TParam~I,)*> System<fn(#(TParam~I,)*)>
+ for Func
+ where
+ Func: Fn(#(TParam~I,)*) + Copy + 'static,
+ #(TParam~I: Param<'world, Flags = NoInitParamFlag>,)*
+ {
+ type Input = Infallible;
+
+ fn initialize(self, _input: Self::Input) -> Self
+ {
+ self
+ }
+
+ fn run(&mut self, component_storage: &mut ComponentStorage)
+ {
+ #(
+ check_params_are_compatible!(I, TParam~I, $c);
+ )*
+
+ let func = *self;
+
+ func(#({
+ // SAFETY: All parameters are compatible so this is fine
+ let this = unsafe {
+ &mut *addr_of_mut!(*self)
+ };
+
+ // SAFETY: All parameters are compatible so this is fine
+ let component_storage = unsafe {
+ &mut *addr_of_mut!(*component_storage)
+ };
+
+ TParam~I::new(this, component_storage)
+ },)*);
+ }
+
+ fn into_type_erased(self) -> TypeErased
+ {
+ TypeErased {
+ data: Box::new(self),
+ func: Box::new(|data, component_storage| {
+ let me = data.downcast_mut::<Func>().unwrap();
+
+ me.run(component_storage);
+ }),
+ }
+ }
+
+ fn get_local_component_mut<LocalComponent: Component>(
+ &mut self,
+ ) -> Option<&mut LocalComponent>
+ {
+ panic!("System does not have any local components");
+ }
+
+ fn set_local_component<LocalComponent: Component>(
+ &mut self,
+ _local_component: LocalComponent,
+ ) {
+ panic!("System does not have any local components");
+ }
+ }
+ });
+ };
}
+seq!(C in 0..=4 {
+ impl_system!(C);
+});
+
pub trait Into<Impl>
{
type System;
@@ -83,3 +139,107 @@ impl Debug for TypeErased
/// Function in [`TypeErased`] used to run the system.
type TypeErasedFunc = dyn Fn(&mut dyn Any, &mut ComponentStorage);
+
+/// A parameter to a [`System`].
+///
+/// # Safety
+/// The `is_compatible` function is used for safety so it must be implemented properly.
+pub unsafe trait Param<'world>
+{
+ type Input;
+ type Flags;
+
+ fn initialize<SystemImpl>(system: &mut impl System<SystemImpl>, input: Self::Input);
+
+ fn new<SystemImpl>(
+ system: &'world mut impl System<SystemImpl>,
+ component_storage: &'world mut ComponentStorage,
+ ) -> Self;
+
+ fn is_compatible<Other: Param<'world>>() -> bool;
+
+ fn get_comparable() -> Box<dyn Any>;
+}
+
+pub struct NoInitParamFlag {}
+
+/// A type which can be used as input to a [`System`].
+pub trait Input: 'static {}
+
+pub trait InputFilter
+{
+ type Filtered: FilteredInputs;
+}
+
+pub trait FilteredInputs
+{
+ type InOptions: OptionInputs;
+
+ fn into_in_options(self) -> Self::InOptions;
+}
+
+macro_rules! impl_filtered_inputs {
+ ($cnt: tt) => {
+ seq!(I in 0..$cnt {
+ impl<#(Input~I: Input,)*> FilteredInputs for (#(Input~I,)*) {
+ type InOptions = (#(Option<Input~I>,)*);
+
+ fn into_in_options(self) -> Self::InOptions {
+ #![allow(clippy::unused_unit)]
+ (#(Some(self.I),)*)
+ }
+ }
+ });
+ };
+}
+
+seq!(N in 0..4 {
+ impl_filtered_inputs!(N);
+});
+
+pub trait OptionInputs
+{
+ fn take<Input: 'static>(&mut self) -> TakeOptionInputResult<Input>;
+}
+
+macro_rules! impl_option_inputs {
+ ($cnt: tt) => {
+ seq!(I in 0..$cnt {
+ impl<#(Input~I: 'static,)*> OptionInputs for (#(Option<Input~I>,)*) {
+ fn take<Input: 'static>(&mut self) -> TakeOptionInputResult<Input> {
+ #(
+ if TypeId::of::<Input~I>() == TypeId::of::<Input>() {
+ let input = match self.I.take() {
+ Some(input) => ManuallyDrop::new(input),
+ None => {
+ return TakeOptionInputResult::AlreadyTaken;
+ }
+ };
+
+ return TakeOptionInputResult::Found(
+ // SAFETY: It can be transmuted safely since it is the
+ // same type and the type is 'static
+ unsafe { transmute_copy(&input) }
+ );
+ }
+ )*
+
+ TakeOptionInputResult::NotFound
+ }
+ }
+ });
+ };
+}
+
+seq!(N in 0..4 {
+ impl_option_inputs!(N);
+});
+
+pub enum TakeOptionInputResult<Input>
+{
+ Found(Input),
+ NotFound,
+ AlreadyTaken,
+}
+
+include!(concat!(env!("OUT_DIR"), "/system_input_impls.rs"));