add "query system functions"
This commit is contained in:
parent
1d4a574b52
commit
83d5275e10
@ -130,7 +130,7 @@ pub fn impl_fn_systems(_input: TokenStream) -> TokenStream {
|
|||||||
#(#resource: ResourceSet<PreparedResources = #resource> + 'static + Clone,)*
|
#(#resource: ResourceSet<PreparedResources = #resource> + 'static + Clone,)*
|
||||||
#(#view: for<'b> View<'b> + DefaultFilter<Filter = #filter> + ViewElement,
|
#(#view: for<'b> View<'b> + DefaultFilter<Filter = #filter> + ViewElement,
|
||||||
#filter: EntityFilter + Sync + 'static),*
|
#filter: EntityFilter + Sync + 'static),*
|
||||||
> IntoSystem<(#(#command_buffer)*), (#(#resource,)*), (#(#view,)*)> for Func
|
> IntoSystem<(#(#command_buffer)*), (#(#resource,)*), (#(#view,)*), (), ()> for Func
|
||||||
where
|
where
|
||||||
Func: FnMut(#(&mut #command_buffer,)* #(#resource,)* #(#view),*) + Send + Sync + 'static,
|
Func: FnMut(#(&mut #command_buffer,)* #(#resource,)* #(#view),*) + Send + Sync + 'static,
|
||||||
#(<#view as View<'a>>::Iter: Iterator<Item = #view>),*
|
#(<#view as View<'a>>::Iter: Iterator<Item = #view>),*
|
||||||
@ -181,3 +181,135 @@ pub fn impl_fn_systems(_input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
tokens
|
tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn impl_fn_query_systems(_input: TokenStream) -> TokenStream {
|
||||||
|
let max_resources = 8;
|
||||||
|
let max_queries = 4;
|
||||||
|
|
||||||
|
let resources = get_idents(|i| format!("R{}", i), max_resources);
|
||||||
|
let resource_vars = get_idents(|i| format!("r{}", i), max_resources);
|
||||||
|
let views = get_idents(|i| format!("V{}", i), max_queries);
|
||||||
|
let filters = get_idents(|i| format!("VF{}", i), max_queries);
|
||||||
|
let query_vars = get_idents(|i| format!("q{}", i), max_queries);
|
||||||
|
|
||||||
|
let mut tokens = TokenStream::new();
|
||||||
|
|
||||||
|
let command_buffer = vec![Ident::new("CommandBuffer", Span::call_site())];
|
||||||
|
let command_buffer_var = vec![Ident::new("_command_buffer", Span::call_site())];
|
||||||
|
for resource_count in 0..=max_resources {
|
||||||
|
let resource = &resources[0..resource_count];
|
||||||
|
let resource_var = &resource_vars[0..resource_count];
|
||||||
|
|
||||||
|
let resource_tuple = tuple(resource);
|
||||||
|
let resource_var_tuple = tuple(resource_var);
|
||||||
|
|
||||||
|
let resource_access = if resource_count == 0 {
|
||||||
|
quote! { Access::default() }
|
||||||
|
}else {
|
||||||
|
quote! {{
|
||||||
|
let mut resource_access: Access<ResourceTypeId> = Access::default();
|
||||||
|
resource_access
|
||||||
|
.reads
|
||||||
|
.extend(<#resource_tuple as ResourceSet>::read_types().iter());
|
||||||
|
resource_access
|
||||||
|
.writes
|
||||||
|
.extend(<#resource_tuple as ResourceSet>::write_types().iter());
|
||||||
|
resource_access
|
||||||
|
}}
|
||||||
|
};
|
||||||
|
|
||||||
|
for query_count in 1..=max_queries {
|
||||||
|
let view = &views[0..query_count];
|
||||||
|
let filter = &filters[0..query_count];
|
||||||
|
let query_var = &query_vars[0..query_count];
|
||||||
|
|
||||||
|
let view_tuple = tuple(view);
|
||||||
|
let query_var_tuple = tuple(query_var);
|
||||||
|
|
||||||
|
let component_access = if query_count == 0 {
|
||||||
|
quote! { Access::default() }
|
||||||
|
}else {
|
||||||
|
quote! {{
|
||||||
|
let mut component_access: Access<ComponentTypeId> = Access::default();
|
||||||
|
component_access
|
||||||
|
.reads
|
||||||
|
.extend(<#view_tuple as View>::read_types().iter());
|
||||||
|
component_access
|
||||||
|
.writes
|
||||||
|
.extend(<#view_tuple as View>::write_types().iter());
|
||||||
|
component_access
|
||||||
|
}}
|
||||||
|
};
|
||||||
|
|
||||||
|
for command_buffer_index in 0..2 {
|
||||||
|
let command_buffer = &command_buffer[0..command_buffer_index];
|
||||||
|
let command_buffer_var = &command_buffer_var[0..command_buffer_index];
|
||||||
|
|
||||||
|
let view_tuple_avoid_type_collision = if query_count == 1 {
|
||||||
|
quote!{(#(#view)*,)}
|
||||||
|
} else {
|
||||||
|
quote!{(#(#view,)*)}
|
||||||
|
};
|
||||||
|
|
||||||
|
let filter_tuple_avoid_type_collision = if query_count == 1 {
|
||||||
|
quote!{(#(#filter)*,)}
|
||||||
|
} else {
|
||||||
|
quote!{(#(#filter,)*)}
|
||||||
|
};
|
||||||
|
|
||||||
|
tokens.extend(TokenStream::from(quote! {
|
||||||
|
impl<Func,
|
||||||
|
#(#resource: ResourceSet<PreparedResources = #resource> + 'static + Clone,)*
|
||||||
|
#(#view: for<'b> View<'b> + DefaultFilter<Filter = #filter> + ViewElement,
|
||||||
|
#filter: EntityFilter + Sync + 'static),*
|
||||||
|
> IntoSystem<(#(#command_buffer)*), (#(#resource,)*), (), #view_tuple_avoid_type_collision, #filter_tuple_avoid_type_collision> for Func
|
||||||
|
where
|
||||||
|
Func: FnMut(#(&mut #command_buffer,)* &mut SubWorld, #(#resource,)* #(&mut SystemQuery<#view, #filter>),*) + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn system_id(mut self, id: SystemId) -> Box<dyn Schedulable> {
|
||||||
|
let resource_access: Access<ResourceTypeId> = #resource_access;
|
||||||
|
let component_access: Access<ComponentTypeId> = #component_access;
|
||||||
|
|
||||||
|
let run_fn = FuncSystemFnWrapper(
|
||||||
|
move |_command_buffer,
|
||||||
|
_world,
|
||||||
|
_resources: #resource_tuple,
|
||||||
|
_queries: &mut (#(SystemQuery<#view, #filter>),*)
|
||||||
|
| {
|
||||||
|
let #resource_var_tuple = _resources;
|
||||||
|
let #query_var_tuple = _queries;
|
||||||
|
self(#(#command_buffer_var,)*_world,#(#resource_var,)* #(#query_var),*)
|
||||||
|
},
|
||||||
|
PhantomData,
|
||||||
|
);
|
||||||
|
|
||||||
|
Box::new(FuncSystem {
|
||||||
|
name: id,
|
||||||
|
queries: AtomicRefCell::new((#(<#view>::query()),*)),
|
||||||
|
access: SystemAccess {
|
||||||
|
resources: resource_access,
|
||||||
|
components: component_access,
|
||||||
|
tags: Access::default(),
|
||||||
|
},
|
||||||
|
archetypes: ArchetypeAccess::Some(BitSet::default()),
|
||||||
|
_resources: PhantomData::<#resource_tuple>,
|
||||||
|
command_buffer: FxHashMap::default(),
|
||||||
|
run_fn: AtomicRefCell::new(run_fn),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_named(self, name: &'static str) -> Box<dyn Schedulable> {
|
||||||
|
self.system_id(name.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system(self) -> Box<dyn Schedulable> {
|
||||||
|
self.system_id(std::any::type_name::<Self>().to_string().into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
resource::{ResourceSet, ResourceTypeId},
|
resource::{ResourceSet, ResourceTypeId},
|
||||||
schedule::{ArchetypeAccess, Schedulable},
|
schedule::{ArchetypeAccess, Schedulable},
|
||||||
system_fn_types::{FuncSystem, FuncSystemFnWrapper},
|
system_fn_types::{FuncSystem, FuncSystemFnWrapper},
|
||||||
Access, SystemAccess, SystemId, SystemQuery,
|
Access, SubWorld, SystemAccess, SystemId, SystemQuery,
|
||||||
};
|
};
|
||||||
use bit_set::BitSet;
|
use bit_set::BitSet;
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
@ -13,32 +13,38 @@ use legion_core::{
|
|||||||
query::{DefaultFilter, IntoQuery, View, ViewElement},
|
query::{DefaultFilter, IntoQuery, View, ViewElement},
|
||||||
storage::ComponentTypeId,
|
storage::ComponentTypeId,
|
||||||
};
|
};
|
||||||
use legion_fn_system_macro::impl_fn_systems;
|
use legion_fn_system_macro::{impl_fn_query_systems, impl_fn_systems};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
// TODO: add params for component access
|
pub trait IntoSystem<CommandBuffer, Resources, Views, Queries, Filters> {
|
||||||
// TODO: add subworld to function parameters
|
|
||||||
// TODO: somehow support filters
|
|
||||||
pub trait IntoSystem<CommandBuffer, Resources, Components> {
|
|
||||||
fn system_id(self, id: SystemId) -> Box<dyn Schedulable>;
|
fn system_id(self, id: SystemId) -> Box<dyn Schedulable>;
|
||||||
fn system_named(self, name: &'static str) -> Box<dyn Schedulable>;
|
fn system_named(self, name: &'static str) -> Box<dyn Schedulable>;
|
||||||
fn system(self) -> Box<dyn Schedulable>;
|
fn system(self) -> Box<dyn Schedulable>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_fn_systems!();
|
impl_fn_systems!();
|
||||||
|
impl_fn_query_systems!();
|
||||||
|
|
||||||
|
#[allow(type_alias_bounds)]
|
||||||
|
pub type Query<V>
|
||||||
|
where
|
||||||
|
V: for<'a> View<'a> + DefaultFilter,
|
||||||
|
= SystemQuery<V, <V as DefaultFilter>::Filter>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
resource::Resources,
|
resource::Resources,
|
||||||
system_fn_types::{Res, ResMut},
|
system_fn_types::{Res, ResMut},
|
||||||
IntoSystem,
|
IntoSystem, Query, SubWorld,
|
||||||
};
|
};
|
||||||
use legion_core::{
|
use legion_core::{
|
||||||
borrow::{Ref, RefMut},
|
borrow::{Ref, RefMut},
|
||||||
command::CommandBuffer,
|
command::CommandBuffer,
|
||||||
|
query::{Read, Write},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
struct A(usize);
|
struct A(usize);
|
||||||
@ -49,6 +55,38 @@ mod tests {
|
|||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
struct X(usize);
|
struct X(usize);
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_query_system() {
|
||||||
|
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))]);
|
||||||
|
|
||||||
|
fn query_system(world: &mut SubWorld, query: &mut Query<(Read<X>, Write<Y>)>) {
|
||||||
|
for (x, mut y) in query.iter_mut(world) {
|
||||||
|
y.0 = 2;
|
||||||
|
println!("{:?}", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_system2(world: &mut SubWorld, a: Res<A>, query: &mut Query<(Read<X>, Write<Y>)>, query2: &mut Query<Read<X>>) {
|
||||||
|
println!("{:?}", *a);
|
||||||
|
for (x, mut y) in query.iter_mut(world) {
|
||||||
|
y.0 = 2;
|
||||||
|
println!("{:?}", x);
|
||||||
|
}
|
||||||
|
|
||||||
|
for x in query2.iter(world) {
|
||||||
|
println!("{:?}", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut system = query_system.system();
|
||||||
|
let mut system2 = query_system2.system();
|
||||||
|
system.run(&mut world, &mut resources);
|
||||||
|
system2.run(&mut world, &mut resources);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_into_system() {
|
fn test_into_system() {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
|
@ -114,6 +114,22 @@ fn score_check_system(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you need more control over iteration or direct access to SubWorld, you can also use "query systems"
|
||||||
|
// This is how you would represent the system above with a "query system"
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn query_score_check_system(
|
||||||
|
world: &mut SubWorld,
|
||||||
|
game_rules: Res<GameRules>,
|
||||||
|
mut game_state: ResMut<GameState>,
|
||||||
|
query: &mut Query<(Read<Player>, Read<Score>)>,
|
||||||
|
) {
|
||||||
|
for (player, score) in query.iter(world) {
|
||||||
|
if score.value == game_rules.winning_score {
|
||||||
|
game_state.winning_player = Some(player.name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This system ends the game if we meet the right conditions. This fires an AppExit event, which tells our
|
// This system ends the game if we meet the right conditions. This fires an AppExit event, which tells our
|
||||||
// App to quit. Check out the "event.rs" example if you want to learn more about using events.
|
// App to quit. Check out the "event.rs" example if you want to learn more about using events.
|
||||||
fn game_over_system(
|
fn game_over_system(
|
||||||
@ -257,7 +273,9 @@ fn stateful_system(mut state: ComMut<State>, player: Com<Player>, score: ComMut<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If you need more flexibility, you can define complex systems using "system builders".
|
// If you need more flexibility, you can define complex systems using "system builders".
|
||||||
// SystemBuilder enables scenarios like "multiple queries" and "query filters"
|
// The main features SystemBuilder currently provides over "function style systems" are:
|
||||||
|
// * "query filters": filter components in your queries based on some criteria (ex: changed components)
|
||||||
|
// * "additional components": Enables access to a component in your SubWorld, even if it isn't in your queries,
|
||||||
// NOTE: this doesn't do anything relevant to our game, it is just here for illustrative purposes
|
// NOTE: this doesn't do anything relevant to our game, it is just here for illustrative purposes
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn complex_system(resources: &mut Resources) -> Box<dyn Schedulable> {
|
fn complex_system(resources: &mut Resources) -> Box<dyn Schedulable> {
|
||||||
@ -267,6 +285,7 @@ fn complex_system(resources: &mut Resources) -> Box<dyn Schedulable> {
|
|||||||
SystemBuilder::new("complex_system")
|
SystemBuilder::new("complex_system")
|
||||||
.read_resource::<GameState>()
|
.read_resource::<GameState>()
|
||||||
.write_resource::<GameRules>()
|
.write_resource::<GameRules>()
|
||||||
|
.read_component::<Renderable>()
|
||||||
// this query is equivalent to the system we saw above: system(player: Com<Player>, mut score: ComMut<Score>)
|
// this query is equivalent to the system we saw above: system(player: Com<Player>, mut score: ComMut<Score>)
|
||||||
.with_query(<(Read<Player>, Write<Score>)>::query())
|
.with_query(<(Read<Player>, Write<Score>)>::query())
|
||||||
// this query only returns entities with a Player component that has changed since the last update
|
// this query only returns entities with a Player component that has changed since the last update
|
||||||
|
@ -60,12 +60,12 @@ pub use legion::{
|
|||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::Event as LegionEvent,
|
event::Event as LegionEvent,
|
||||||
filter::filter_fns::*,
|
filter::filter_fns::*,
|
||||||
query::{IntoQuery, Query, Read, Tagged, TryRead, TryWrite, Write},
|
query::{IntoQuery, Read, Tagged, TryRead, TryWrite, Write},
|
||||||
systems::{
|
systems::{
|
||||||
bit_set::BitSet,
|
bit_set::BitSet,
|
||||||
resource::{ResourceSet, Resources},
|
resource::{ResourceSet, Resources},
|
||||||
schedule::{Executor, Runnable, Schedulable, Schedule},
|
schedule::{Executor, Runnable, Schedulable, Schedule},
|
||||||
IntoSystem, Res, ResMut, SubWorld, SystemBuilder,
|
IntoSystem, Res, ResMut, SubWorld, SystemBuilder, Query
|
||||||
},
|
},
|
||||||
world::{Universe, World},
|
world::{Universe, World},
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user