Skip to content

Commit

Permalink
feat: system proc macro
Browse files Browse the repository at this point in the history
Allows deriving a system from a receiver function
  • Loading branch information
ten3roberts committed Oct 27, 2024
1 parent 0ac9a1b commit 8ec4483
Show file tree
Hide file tree
Showing 28 changed files with 640 additions and 70 deletions.
2 changes: 1 addition & 1 deletion asteroids/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ fn despawn_dead() -> BoxedSystem {
.with_query(Query::new(self::rng().as_mut()).entity(resources()))
.with_query(
Query::new((entity_ids(), position(), velocity(), material().opt()))
.filter(health().modified() & health().le(0.0)),
.with_filter(health().modified() & health().le(0.0)),
)
.with_cmd_mut()
.build(
Expand Down
2 changes: 1 addition & 1 deletion examples/guide/change_detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fn main() {
// ANCHOR: cleanup_system

let query = Query::new((name().opt(), entity_ids(), player().satisfied()))
.filter(health().le(0.0).modified());
.with_filter(health().le(0.0).modified());

let cleanup = System::builder()
.with_name("cleanup")
Expand Down
4 changes: 2 additions & 2 deletions examples/guide/commandbuffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ fn main() -> anyhow::Result<()> {
cmd.apply(&mut world)?;

let id = Query::new(entity_ids())
.filter(name().eq("a"))
.with_filter(name().eq("a"))
.borrow(&world)
.iter()
.next()
Expand Down Expand Up @@ -100,7 +100,7 @@ fn main() -> anyhow::Result<()> {
.with_name("update_world_matrix")
.with_query(
Query::new((entity_ids(), position(), world_matrix().as_mut()))
.filter(position().modified()),
.with_filter(position().modified()),
)
.for_each(|(id, pos, ltw)| {
tracing::info!("Updating world matrix for {id}");
Expand Down
10 changes: 5 additions & 5 deletions examples/guide/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fn main() -> anyhow::Result<()> {
}

let mut query = Query::new((entity_ids(), position(), distance().as_mut()))
.filter(position().modified() & health().gt(0.0));
.with_filter(position().modified() & health().gt(0.0));

println!("Updating distances");
for (id, pos, dist) in &mut query.borrow(&world) {
Expand Down Expand Up @@ -86,7 +86,7 @@ fn main() -> anyhow::Result<()> {
// ANCHOR: shorthand
// Instead of this:
let query = Query::new((position(), health(), distance()))
.filter(position().modified() & health().modified());
.with_filter(position().modified() & health().modified());

// Do this:
let query = Query::new((position().modified(), health().modified(), distance()));
Expand Down Expand Up @@ -115,7 +115,7 @@ fn main() -> anyhow::Result<()> {
.with_name("update_distance")
.with_query(
Query::new((entity_ids(), position(), distance().as_mut()))
.filter(position().modified()),
.with_filter(position().modified()),
)
.for_each(|(id, pos, dist)| {
tracing::debug!("Updating distance for {id} with position: {pos:?}");
Expand All @@ -133,7 +133,7 @@ fn main() -> anyhow::Result<()> {
// ANCHOR: schedule_basic
let despawn = System::builder()
.with_name("delete_outside_world")
.with_query(Query::new((entity_ids(), distance())).filter(distance().gt(50.0)))
.with_query(Query::new((entity_ids(), distance())).with_filter(distance().gt(50.0)))
.with_cmd_mut()
.build(|mut q: QueryBorrow<_, _>, cmd: &mut CommandBuffer| {
for (id, &dist) in &mut q {
Expand Down Expand Up @@ -171,7 +171,7 @@ fn main() -> anyhow::Result<()> {
// eventually be despawned
let move_out = System::builder()
.with_name("move_out")
.with_query(Query::new(position().as_mut()).filter(is_static().without()))
.with_query(Query::new(position().as_mut()).with_filter(is_static().without()))
.for_each(|pos| {
let dir = pos.normalize_or_zero();

Expand Down
2 changes: 1 addition & 1 deletion examples/guide/query_advanced.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ fn main() -> anyhow::Result<()> {
rotation().opt_or_default(),
scale().opt_or(Vec3::ONE),
))
.filter(position().modified() | rotation().modified() | scale().modified()),
.with_filter(position().modified() | rotation().modified() | scale().modified()),
)
.for_each(|(id, world_matrix, pos, rot, scale)| {
tracing::info!("Updating world matrix for: {id} {pos} {rot} {scale}");
Expand Down
4 changes: 2 additions & 2 deletions examples/guide/relations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ fn basic() -> anyhow::Result<()> {

// Matches a relation with any parent
let all_children: Vec<Entity> = Query::new(entity_ids())
.filter(child_of.with_relation())
.with_filter(child_of.with_relation())
.collect_vec(&world);

tracing::info!("Children: {all_children:?}");

let roots = Query::new(entity_ids())
.filter(child_of.without_relation())
.with_filter(child_of.without_relation())
.collect_vec(&world);

tracing::info!("Roots: {roots:?}");
Expand Down
4 changes: 2 additions & 2 deletions examples/guide/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fn main() -> anyhow::Result<()> {
fn despawn_system() -> BoxedSystem {
System::builder()
.with_name("delete_outside_world")
.with_query(Query::new((entity_ids(), distance())).filter(distance().gt(50.0)))
.with_query(Query::new((entity_ids(), distance())).with_filter(distance().gt(50.0)))
.with_cmd_mut()
.build(|mut q: QueryBorrow<_, _>, cmd: &mut CommandBuffer| {
for (id, &dist) in &mut q {
Expand Down Expand Up @@ -132,7 +132,7 @@ fn main() -> anyhow::Result<()> {
fn move_system() -> BoxedSystem {
System::builder()
.with_name("move_out")
.with_query(Query::new(position().as_mut()).filter(is_static().without()))
.with_query(Query::new(position().as_mut()).with_filter(is_static().without()))
.for_each(|pos| {
let dir = pos.normalize_or_zero();

Expand Down
8 changes: 4 additions & 4 deletions examples/query/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ fn main() {
let _span = info_span!("query_with").entered();
// ANCHOR: query_with

let mut query = Query::new((name(), health())).filter(player().with());
let mut query = Query::new((name(), health())).with_filter(player().with());

let mut borrow = query.borrow(&world);

Expand All @@ -136,7 +136,7 @@ fn main() {
let _span = info_span!("query_without").entered();
// ANCHOR: query_without

let mut query = Query::new((name(), health())).filter(player().without());
let mut query = Query::new((name(), health())).with_filter(player().without());

for (name, health) in &mut query.borrow(&world) {
tracing::info!("Npc: {name} at {health} health");
Expand All @@ -150,7 +150,7 @@ fn main() {
// ANCHOR: query_combinators

let mut query =
Query::new((name(), health().opt())).filter(player().with() | health().without());
Query::new((name(), health().opt())).with_filter(player().with() | health().without());

for (name, health) in &mut query.borrow(&world) {
if let Some(health) = health {
Expand All @@ -167,7 +167,7 @@ fn main() {
let _span = info_span!("query_cmp").entered();
// ANCHOR: query_cmp

let mut query = Query::new(name()).filter(health().without() | health().ge(35.0));
let mut query = Query::new(name()).with_filter(health().without() | health().ge(35.0));
for name in &mut query.borrow(&world) {
tracing::info!("{name} is still standing strong");
}
Expand Down
3 changes: 2 additions & 1 deletion flax-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ proc-macro2 = "1.0.69"
proc-macro-crate = "2.0.0"
syn = "2.0.38"
quote = "1.0.33"
itertools = "0.11.0"
itertools = "0.13.0"
heck = "0.5"

[dev-dependencies]
glam = "0.24.2"
Expand Down
63 changes: 61 additions & 2 deletions flax-derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod system;

use std::collections::BTreeSet;

use itertools::Itertools;
Expand All @@ -6,9 +8,66 @@ use proc_macro_crate::FoundCrate;
use quote::{format_ident, quote};
use syn::{
bracketed, parse::Parse, punctuated::Punctuated, spanned::Spanned, Attribute, DataStruct,
DeriveInput, Error, Field, GenericParam, Generics, Ident, ImplGenerics, Index, Lifetime,
LifetimeParam, Result, Token, Type, TypeGenerics, TypeParam, Visibility,
DeriveInput, Error, Field, GenericParam, Generics, Ident, ImplGenerics, Index, ItemFn,
Lifetime, LifetimeParam, Result, Token, Type, TypeGenerics, TypeParam, Visibility,
};
use system::{system_impl, SystemAttrs};

/// Derive a system from a receive function
///
/// Components are generated from the argument name unless otherwise specified with
/// `args(ident=expr)`
///
/// # Attributes
/// - `args`: Specify expressions for the components
/// - `filter`: Additional filter expression for the query
/// - `par`: use inner parallel iteration
/// - `with_world`: `&World`, Access the world
/// - `with_cmd`: `&CommandBuffer`, Access the schedule's commandbuffer
/// - `with_cmd_mut`: `&mut CommandBuffer`, Access the schedule's commandbuffer
/// - `with_query`: `&mut QueryBorrow<Q, F>`, Access another query during iteration
///
/// # Implicit type support
/// If not specified using `args`, the component query expression is inferred from the argument
/// name and type
/// - `name: &T`: `name()`
/// - `name: &mut T`: `name().as_mut()`
/// - `name: T`: `name().copied()`
/// - `name: Option<&T>`: `name().opt()`
/// - `name: Option<&mut T>`: `name().as_mut().opt()`
/// - `name: Option<T>`: `name().copied().opt()`
///
/// Works on both free functions and associated methods
///
/// # Examples
/// ```
/// component! {
/// foo: String,
/// my_type: MyType,
/// }
///
/// pub struct MyType;
///
/// impl MyType {
/// #[system(filter(d().with()))]
/// pub fn method(self: &mut MyType, foo: &String) -> anyhow::Result<()> { }
///
/// #[system(with_cmd_mut)]
/// pub fn method_with_cmd(self: &mut MyType, foo: Option<&String>, cmd: &mut CommandBuffer) { }
/// }
/// ```
#[proc_macro_attribute]
pub fn system(
args: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let args = syn::parse_macro_input!(args as SystemAttrs);
let input = syn::parse_macro_input!(item as ItemFn);

system_impl(args, input)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}

/// ```rust,ignore
/// #[derive(Fetch)]
Expand Down
Loading

0 comments on commit 8ec4483

Please sign in to comment.