Implement IntoSystem trait for flat functions using macros
This commit is contained in:
parent
f1a03a7a3a
commit
9230c370ba
@ -2,7 +2,7 @@ use legion::{
|
||||
filter::EntityFilter,
|
||||
prelude::{
|
||||
into_resource_system, IntoQuery, ResourceSet, Resources, Runnable,
|
||||
Schedulable, World, into_resource_for_each_system, into_for_each_system,
|
||||
Schedulable, World, into_resource_for_each_system,
|
||||
},
|
||||
query::{DefaultFilter, View},
|
||||
};
|
||||
@ -34,16 +34,6 @@ where
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn for_each<'a, Q, F, R>(name: &'static str, system: F) -> Self
|
||||
where
|
||||
Q: IntoQuery + DefaultFilter<Filter = R>,
|
||||
<Q as View<'a>>::Iter: Iterator<Item = Q> + 'a,
|
||||
F: FnMut(Q) + Send + Sync + 'static,
|
||||
R: EntityFilter + Sync + 'static,
|
||||
{
|
||||
into_for_each_system(name, system).into()
|
||||
}
|
||||
|
||||
pub fn resource_for_each<'a, Q, F, R, X>(name: &'static str, system: F) -> Self
|
||||
where
|
||||
Q: IntoQuery + DefaultFilter<Filter = R>,
|
||||
|
@ -1,3 +1,4 @@
|
||||
#![feature(trace_macros)]
|
||||
pub mod resource;
|
||||
pub mod schedule;
|
||||
|
||||
@ -16,6 +17,7 @@ pub mod prelude {
|
||||
resource::{ResourceSet, Resources, PreparedRead as Resource, PreparedWrite as ResourceMut},
|
||||
schedule::{Executor, Runnable, Schedulable, Schedule},
|
||||
System, SystemBuilder,
|
||||
into_for_each_system, into_resource_system, into_resource_for_each_system,
|
||||
into_resource_system, into_resource_for_each_system,
|
||||
IntoSystem
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
resource::{ResourceSet, ResourceTypeId},
|
||||
resource::{PreparedRead, Resource, ResourceSet, ResourceTypeId},
|
||||
schedule::{ArchetypeAccess, Schedulable},
|
||||
Access, System, SystemAccess, SystemFnWrapper, SystemQuery,
|
||||
};
|
||||
@ -7,47 +7,12 @@ use bit_set::BitSet;
|
||||
use fxhash::FxHashMap;
|
||||
use legion_core::{
|
||||
borrow::AtomicRefCell,
|
||||
filter::EntityFilter,
|
||||
query::{DefaultFilter, IntoQuery, View},
|
||||
filter::{And, EntityFilter, EntityFilterTuple},
|
||||
query::{DefaultFilter, IntoQuery, View, ViewElement},
|
||||
storage::ComponentTypeId,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub fn into_for_each_system<'a, Q, F, R>(name: &'static str, mut system: F) -> Box<dyn Schedulable>
|
||||
where
|
||||
Q: IntoQuery + DefaultFilter<Filter = R>,
|
||||
<Q as View<'a>>::Iter: Iterator<Item = Q> + 'a,
|
||||
F: FnMut(Q) + Send + Sync + 'static,
|
||||
R: EntityFilter + Sync + 'static,
|
||||
{
|
||||
let resource_access: Access<ResourceTypeId> = Access::default();
|
||||
let mut component_access: Access<ComponentTypeId> = Access::default();
|
||||
component_access.reads.extend(Q::read_types().iter());
|
||||
component_access.writes.extend(Q::write_types().iter());
|
||||
|
||||
let run_fn = SystemFnWrapper(
|
||||
move |_, world, _, query: &mut SystemQuery<Q, <Q as DefaultFilter>::Filter>| {
|
||||
for components in query.iter_mut(world) {
|
||||
system(components);
|
||||
}
|
||||
},
|
||||
PhantomData,
|
||||
);
|
||||
|
||||
Box::new(System {
|
||||
name: name.into(),
|
||||
queries: AtomicRefCell::new(Q::query()),
|
||||
access: SystemAccess {
|
||||
resources: resource_access,
|
||||
components: component_access,
|
||||
tags: Access::default(),
|
||||
},
|
||||
archetypes: ArchetypeAccess::Some(BitSet::default()),
|
||||
_resources: PhantomData::<()>,
|
||||
command_buffer: FxHashMap::default(),
|
||||
run_fn: AtomicRefCell::new(run_fn),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn into_resource_for_each_system<'a, Q, F, R, X>(
|
||||
name: &'static str,
|
||||
@ -127,11 +92,274 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub trait IntoSystem<'a, ResourceArgs, ComponentArgs>
|
||||
where
|
||||
ComponentArgs: IntoQuery + DefaultFilter,
|
||||
{
|
||||
fn into_system(self, name: &'static str) -> Box<dyn Schedulable>;
|
||||
}
|
||||
|
||||
// impl<F, X: Resource + Send + Sync + 'static, A: Component, B: Component> IntoSystem<(X,), (A, B)> for F
|
||||
// where
|
||||
// F: for<'a> FnMut(&X, Ref<'a, A>, Ref<'a, B>) + Send + Sync + 'static,
|
||||
// {
|
||||
// fn into_system(mut self, name: &'static str) -> Box<dyn Schedulable> {
|
||||
// let mut resource_access: Access<ResourceTypeId> = Access::default();
|
||||
// resource_access
|
||||
// .reads
|
||||
// .extend(<PreparedRead<X>>::read_types().iter());
|
||||
// resource_access
|
||||
// .writes
|
||||
// .extend(<PreparedRead<X>>::write_types().iter());
|
||||
// let mut component_access: Access<ComponentTypeId> = Access::default();
|
||||
// component_access
|
||||
// .reads
|
||||
// .extend(<(Ref<A>, Ref<B>) as View>::read_types().iter());
|
||||
// component_access
|
||||
// .writes
|
||||
// .extend(<(Ref<A>, Ref<B>) as View>::write_types().iter());
|
||||
|
||||
// let run_fn = SystemFnWrapper(
|
||||
// move |_,
|
||||
// world,
|
||||
// x: &mut PreparedRead<X>,
|
||||
// query: &mut SystemQuery<
|
||||
// (Ref<A>, Ref<B>),
|
||||
// EntityFilterTuple<
|
||||
// And<(ComponentFilter<A>, ComponentFilter<B>)>,
|
||||
// And<(Passthrough, Passthrough)>,
|
||||
// And<(Passthrough, Passthrough)>,
|
||||
// >,
|
||||
// >| {
|
||||
// for (a, b) in query.iter_mut(world) {
|
||||
// self(&*x, a, b);
|
||||
// }
|
||||
// },
|
||||
// PhantomData,
|
||||
// );
|
||||
|
||||
// Box::new(System {
|
||||
// name: name.into(),
|
||||
// queries: AtomicRefCell::new(<(Ref<A>, Ref<B>)>::query()),
|
||||
// access: SystemAccess {
|
||||
// resources: resource_access,
|
||||
// components: component_access,
|
||||
// tags: Access::default(),
|
||||
// },
|
||||
// archetypes: ArchetypeAccess::Some(BitSet::default()),
|
||||
// _resources: PhantomData::<PreparedRead<X>>,
|
||||
// command_buffer: FxHashMap::default(),
|
||||
// run_fn: AtomicRefCell::new(run_fn),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
impl<
|
||||
'a,
|
||||
F,
|
||||
X: Resource + Send + Sync + 'static,
|
||||
A: for<'b> View<'b> + DefaultFilter<Filter = AF> + ViewElement,
|
||||
AF: EntityFilter + Sync + 'static,
|
||||
B: for<'b> View<'b> + DefaultFilter<Filter = BF> + ViewElement,
|
||||
BF: EntityFilter + Sync + 'static,
|
||||
> IntoSystem<'a, (X,), (A, B)> for F
|
||||
where
|
||||
F: FnMut(&X, A, B) + Send + Sync + 'static,
|
||||
<A as View<'a>>::Iter: Iterator<Item = A>,
|
||||
<B as View<'a>>::Iter: Iterator<Item = B>,
|
||||
{
|
||||
fn into_system(mut self, name: &'static str) -> Box<dyn Schedulable> {
|
||||
let mut resource_access: Access<ResourceTypeId> = Access::default();
|
||||
resource_access
|
||||
.reads
|
||||
.extend(<PreparedRead<X>>::read_types().iter());
|
||||
resource_access
|
||||
.writes
|
||||
.extend(<PreparedRead<X>>::write_types().iter());
|
||||
let mut component_access: Access<ComponentTypeId> = Access::default();
|
||||
component_access
|
||||
.reads
|
||||
.extend(<(A, B) as View>::read_types().iter());
|
||||
component_access
|
||||
.writes
|
||||
.extend(<(A, B) as View>::write_types().iter());
|
||||
|
||||
let run_fn = SystemFnWrapper(
|
||||
move |_,
|
||||
world,
|
||||
x: &mut PreparedRead<X>,
|
||||
query: &mut SystemQuery<
|
||||
(A, B),
|
||||
EntityFilterTuple<
|
||||
And<(
|
||||
<AF as EntityFilter>::ArchetypeFilter,
|
||||
<BF as EntityFilter>::ArchetypeFilter,
|
||||
)>,
|
||||
And<(
|
||||
<AF as EntityFilter>::ChunksetFilter,
|
||||
<BF as EntityFilter>::ChunksetFilter,
|
||||
)>,
|
||||
And<(
|
||||
<AF as EntityFilter>::ChunkFilter,
|
||||
<BF as EntityFilter>::ChunkFilter,
|
||||
)>,
|
||||
>,
|
||||
>| {
|
||||
for (a, b) in query.iter_mut(world) {
|
||||
self(&*x, a, b);
|
||||
}
|
||||
},
|
||||
PhantomData,
|
||||
);
|
||||
|
||||
Box::new(System {
|
||||
name: name.into(),
|
||||
queries: AtomicRefCell::new(<(A, B)>::query()),
|
||||
access: SystemAccess {
|
||||
resources: resource_access,
|
||||
components: component_access,
|
||||
tags: Access::default(),
|
||||
},
|
||||
archetypes: ArchetypeAccess::Some(BitSet::default()),
|
||||
_resources: PhantomData::<PreparedRead<X>>,
|
||||
command_buffer: FxHashMap::default(),
|
||||
run_fn: AtomicRefCell::new(run_fn),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_system {
|
||||
($(($view:ident, $filter:ident, $var:ident)),+) => {
|
||||
impl<'a,
|
||||
Func,
|
||||
$($view: for<'b> View<'b> + DefaultFilter<Filter = $filter> + ViewElement,
|
||||
$filter: EntityFilter + Sync + 'static),+
|
||||
> IntoSystem<'a, (), ($($view,)+)> for Func
|
||||
where
|
||||
Func: FnMut($($view),+) + Send + Sync + 'static,
|
||||
$(<$view as View<'a>>::Iter: Iterator<Item = $view>),+
|
||||
{
|
||||
fn into_system(mut self, name: &'static str) -> Box<dyn Schedulable> {
|
||||
let resource_access: Access<ResourceTypeId> = Access::default();
|
||||
let component_access: Access<ComponentTypeId> = component_access!(($($view),+));
|
||||
|
||||
let run_fn = SystemFnWrapper(
|
||||
move |_,
|
||||
world,
|
||||
_: &mut (),
|
||||
query: &mut system_query!($($view, $filter),+)
|
||||
,
|
||||
| {
|
||||
for tuple!($($var),+) in query.iter_mut(world) {
|
||||
self($($var),+);
|
||||
}
|
||||
},
|
||||
PhantomData,
|
||||
);
|
||||
|
||||
Box::new(System {
|
||||
name: name.into(),
|
||||
queries: AtomicRefCell::new(query!($($view),+)),
|
||||
access: SystemAccess {
|
||||
resources: resource_access,
|
||||
components: component_access,
|
||||
tags: Access::default(),
|
||||
},
|
||||
archetypes: ArchetypeAccess::Some(BitSet::default()),
|
||||
_resources: PhantomData::<()>,
|
||||
command_buffer: FxHashMap::default(),
|
||||
run_fn: AtomicRefCell::new(run_fn),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tuple {
|
||||
// single value: v1
|
||||
($value:ident) => { $value };
|
||||
// multiple values: (v1, v2, v3)
|
||||
($($value:ident),+) => { ($($value),+) }
|
||||
}
|
||||
|
||||
macro_rules! component_access {
|
||||
(()) => {Access::default()};
|
||||
(($($view:ident),+)) => {{
|
||||
let mut component_access: Access<ComponentTypeId> = Access::default();
|
||||
component_access
|
||||
.reads
|
||||
.extend(<tuple!($($view),+) as View>::read_types().iter());
|
||||
component_access
|
||||
.writes
|
||||
.extend(<tuple!($($view),+) as View>::write_types().iter());
|
||||
component_access
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! system_query {
|
||||
($view:ident, $filter:ident) => {
|
||||
SystemQuery<
|
||||
$view,
|
||||
$filter
|
||||
>
|
||||
};
|
||||
($($view:ident, $filter:ident),+) => {
|
||||
SystemQuery<
|
||||
($($view),+),
|
||||
EntityFilterTuple<
|
||||
And<(
|
||||
$(<$filter as EntityFilter>::ArchetypeFilter),+
|
||||
)>,
|
||||
And<(
|
||||
$(<$filter as EntityFilter>::ChunksetFilter),+
|
||||
)>,
|
||||
And<(
|
||||
$(<$filter as EntityFilter>::ChunkFilter),+
|
||||
)>,
|
||||
>
|
||||
>
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! query {
|
||||
(()) => { () };
|
||||
($($query:ident),+) => {
|
||||
<tuple!($($query),+)>::query()
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e), (F, FF, f)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e), (F, FF, f), (G, GF, g)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e), (F, FF, f), (G, GF, g), (H, HF, h)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e), (F, FF, f), (G, GF, g), (H, HF, h), (I, IF, i)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e), (F, FF, f), (G, GF, g), (H, HF, h), (I, IF, i), (J, JF, j)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e), (F, FF, f), (G, GF, g), (H, HF, h), (I, IF, i), (J, JF, j), (K, KF, k)];
|
||||
#[rustfmt::skip]
|
||||
impl_system![(A, AF, a), (B, BF, b), (C, CF, c), (D, DF, d), (E, EF, e), (F, FF, f), (G, GF, g), (H, HF, h), (I, IF, i), (J, JF, j), (K, KF, k), (L, LF, l)];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
into_resource_for_each_system,
|
||||
resource::{PreparedRead, PreparedWrite, Resources},
|
||||
IntoSystem,
|
||||
};
|
||||
use legion_core::{
|
||||
borrow::{Ref, RefMut},
|
||||
@ -147,6 +375,29 @@ mod tests {
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
struct X(usize);
|
||||
|
||||
#[test]
|
||||
fn test_into_system() {
|
||||
// fn read_system(a: &A, x: Ref<X>, y: Ref<Y>) {
|
||||
// println!("{} {} {}", a.0, x.0, y.0);
|
||||
// }
|
||||
|
||||
// fn read_system(x: Ref<X>) {
|
||||
// println!("{}", x.0);
|
||||
// }
|
||||
fn read_system(x: Ref<X>, y: Ref<Y>, mut z: RefMut<A>) {
|
||||
z.0 += 1;
|
||||
println!("{} {} {}", x.0, y.0, z.0);
|
||||
}
|
||||
|
||||
let mut world = World::new();
|
||||
let mut resources = Resources::default();
|
||||
resources.insert(A(0));
|
||||
world.insert((), vec![(X(1), Y(1)), (X(2), Y(2))]);
|
||||
|
||||
let mut system = read_system.into_system("hi");
|
||||
system.run(&mut world, &mut resources);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_system_fn() {
|
||||
fn read_write_system(_: &mut (), (_x, mut y): (Ref<X>, RefMut<Y>)) { y.0 += 1; }
|
||||
|
@ -6,8 +6,9 @@ fn main() {
|
||||
.add_default_plugins()
|
||||
.add_event::<MyEvent>()
|
||||
.add_startup_system(setup)
|
||||
.add_system_init(system_b)
|
||||
.add_system(System::for_each("system_a", system_a))
|
||||
.add_system_init(built_system)
|
||||
.add_system(simple_system.into_system("simple_system"))
|
||||
.add_system(closure_system())
|
||||
.run();
|
||||
}
|
||||
|
||||
@ -26,31 +27,40 @@ fn setup(world: &mut World, resources: &mut Resources) {
|
||||
world.insert((), vec![(X(0), Y(1)), (X(2), Y(3))]);
|
||||
}
|
||||
|
||||
fn system_a((x, y): (Ref<X>, RefMut<Y>)) {
|
||||
|
||||
// runs once for each entity with the X and Y component
|
||||
fn simple_system(x: Ref<X>, mut y: RefMut<Y>) {
|
||||
y.0 += 1;
|
||||
println!("processed entity: {} {}", x.0, y.0);
|
||||
}
|
||||
|
||||
// fn system_a(x: Ref<X>, mut y: RefMut<Y>) {
|
||||
// does the same thing as the first system, but also captures the "counter" variable and uses it as internal state
|
||||
fn closure_system() -> Box<dyn Schedulable> {
|
||||
let mut counter = 0;
|
||||
(move |x: Ref<X>, mut y: RefMut<Y>| {
|
||||
y.0 += 1;
|
||||
println!("processed entity: {} {}", x.0, y.0);
|
||||
println!("ran {} times", counter);
|
||||
counter += 1;
|
||||
}).into_system("closure_system")
|
||||
}
|
||||
|
||||
// }
|
||||
// fn system_a((my_events, a): &mut (Resource<Events<MyEvent>>, Resource<A>), (x, mut y): (Ref<X>, RefMut<Y>)) {
|
||||
|
||||
// }
|
||||
|
||||
fn system_b(resources: &mut Resources) -> Box<dyn Schedulable> {
|
||||
// if you need more flexibility, you can define complex systems using the system builder
|
||||
fn built_system(resources: &mut Resources) -> Box<dyn Schedulable> {
|
||||
let mut my_event_reader = resources.get_event_reader::<MyEvent>();
|
||||
SystemBuilder::new("example")
|
||||
.read_resource::<Events<MyEvent>>()
|
||||
.write_resource::<A>()
|
||||
.with_query(<(Read<X>, Write<Y>)>::query())
|
||||
.build(move |_command_buffer, world, (my_events, ref mut a), query| {
|
||||
for event in my_event_reader.iter(&my_events) {
|
||||
a.0 += event.0;
|
||||
println!("modified resource A with event: {}", event.0);
|
||||
}
|
||||
for (x, mut y) in query.iter_mut(world) {
|
||||
y.0 += 1;
|
||||
println!("processed entity: {} {}", x.0, y.0);
|
||||
}
|
||||
})
|
||||
.build(
|
||||
move |_command_buffer, world, (my_events, ref mut a), query| {
|
||||
for event in my_event_reader.iter(&my_events) {
|
||||
a.0 += event.0;
|
||||
println!("modified resource A with event: {}", event.0);
|
||||
}
|
||||
for (x, mut y) in query.iter_mut(world) {
|
||||
y.0 += 1;
|
||||
println!("processed entity: {} {}", x.0, y.0);
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ pub use legion::{
|
||||
resource::{ResourceSet, Resources, PreparedRead as Resource, PreparedWrite as ResourceMut},
|
||||
schedule::{Executor, Runnable, Schedulable, Schedule},
|
||||
SubWorld, SystemBuilder,
|
||||
IntoSystem
|
||||
},
|
||||
world::{Universe, World},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user