Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Implement Sub-App Labels #2695

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/bevy_app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bevy_derive = { path = "../bevy_derive", version = "0.5.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" }
bevy_reflect = { path = "../bevy_reflect", version = "0.5.0", optional = true }
bevy_utils = { path = "../bevy_utils", version = "0.5.0" }
bevy_app_macros = { path = "./macros", version = "0.5.0" }

# other
serde = { version = "1.0", features = ["derive"], optional = true }
Expand Down
16 changes: 16 additions & 0 deletions crates/bevy_app/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "bevy_app_macros"
version = "0.5.0"
description = "Bevy App Macros"
edition = "2018"
license = "MIT OR Apache-2.0"

[lib]
proc-macro = true

[dependencies]
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.5.0" }

syn = "1.0"
quote = "1.0"
proc-macro2 = "1.0"
19 changes: 19 additions & 0 deletions crates/bevy_app/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
extern crate proc_macro;

use bevy_macro_utils::{derive_label, BevyManifest};
use proc_macro::TokenStream;
use quote::format_ident;

#[proc_macro_derive(SubAppLabel)]
pub fn derive_sub_app_label(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let mut trait_path = bevy_app_path();
trait_path
.segments
.push(format_ident!("SubAppLabel").into());
derive_label(input, trait_path)
}

fn bevy_app_path() -> syn::Path {
BevyManifest::default().get_path("bevy_app")
}
33 changes: 22 additions & 11 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ use bevy_ecs::{
},
world::World,
};
use bevy_utils::tracing::debug;
use bevy_utils::{tracing::debug, HashMap};
use std::{fmt::Debug, hash::Hash};

pub use bevy_app_macros::SubAppLabel;

#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;

bevy_utils::define_label!(SubAppLabel);
type BoxedSubAppLabel = Box<dyn SubAppLabel>;

#[allow(clippy::needless_doctest_main)]
/// Containers of app logic and data
///
Expand Down Expand Up @@ -40,7 +45,7 @@ pub struct App {
pub world: World,
pub runner: Box<dyn Fn(App)>,
pub schedule: Schedule,
sub_apps: Vec<SubApp>,
sub_apps: HashMap<BoxedSubAppLabel, SubApp>,
}

struct SubApp {
Expand Down Expand Up @@ -77,7 +82,7 @@ impl App {
world: Default::default(),
schedule: Default::default(),
runner: Box::new(run_once),
sub_apps: Vec::new(),
sub_apps: HashMap::default(),
}
}

Expand All @@ -87,7 +92,7 @@ impl App {
#[cfg(feature = "trace")]
let _bevy_frame_update_guard = bevy_frame_update_span.enter();
self.schedule.run(&mut self.world);
for sub_app in self.sub_apps.iter_mut() {
for sub_app in self.sub_apps.values_mut() {
(sub_app.runner)(&mut self.world, &mut sub_app.app);
}
}
Expand Down Expand Up @@ -589,19 +594,25 @@ impl App {

pub fn add_sub_app(
&mut self,
label: impl SubAppLabel,
app: App,
f: impl Fn(&mut World, &mut App) + 'static,
) -> &mut Self {
self.sub_apps.push(SubApp {
app,
runner: Box::new(f),
});
self.sub_apps.insert(
Box::new(label),
SubApp {
app,
runner: Box::new(f),
},
);
self
}

// TODO: use labels instead of indices
pub fn sub_app_mut(&mut self, index: usize) -> &mut App {
&mut self.sub_apps[index].app
pub fn sub_app_mut(&mut self, label: impl SubAppLabel) -> Option<&mut App> {
let label = Box::new(label) as BoxedSubAppLabel;
self.sub_apps
.get_mut(&label)
.map(|sub_app| &mut sub_app.app)
}
}

Expand Down
4 changes: 0 additions & 4 deletions crates/bevy_ecs/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@
name = "bevy_ecs_macros"
version = "0.5.0"
description = "Bevy ECS Macros"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
edition = "2018"
license = "MIT OR Apache-2.0"

Expand Down
53 changes: 25 additions & 28 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
extern crate proc_macro;

use bevy_macro_utils::BevyManifest;
use bevy_macro_utils::{derive_label, BevyManifest};
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use proc_macro2::Span;
use quote::{format_ident, quote};
use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
token::Comma,
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt,
Path, Result, Token,
Result, Token,
};

struct AllTuples {
Expand Down Expand Up @@ -436,46 +436,43 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
#[proc_macro_derive(SystemLabel)]
pub fn derive_system_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);

derive_label(input, Ident::new("SystemLabel", Span::call_site())).into()
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("SystemLabel").into());
derive_label(input, trait_path)
}

#[proc_macro_derive(StageLabel)]
pub fn derive_stage_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_label(input, Ident::new("StageLabel", Span::call_site())).into()
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path.segments.push(format_ident!("StageLabel").into());
derive_label(input, trait_path)
}

#[proc_macro_derive(AmbiguitySetLabel)]
pub fn derive_ambiguity_set_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_label(input, Ident::new("AmbiguitySetLabel", Span::call_site())).into()
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("AmbiguitySetLabel").into());
derive_label(input, trait_path)
}

#[proc_macro_derive(RunCriteriaLabel)]
pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
derive_label(input, Ident::new("RunCriteriaLabel", Span::call_site())).into()
}

fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 {
let ident = input.ident;
let ecs_path: Path = bevy_ecs_path();

let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap());

quote! {
impl #impl_generics #ecs_path::schedule::#label_type for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> Box<dyn #ecs_path::schedule::#label_type> {
Box::new(Clone::clone(self))
}
}
}
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("RunCriteriaLabel").into());
derive_label(input, trait_path)
}

fn bevy_ecs_path() -> syn::Path {
Expand Down
110 changes: 5 additions & 105 deletions crates/bevy_ecs/src/schedule/label.rs
Original file line number Diff line number Diff line change
@@ -1,115 +1,15 @@
pub use bevy_ecs_macros::{AmbiguitySetLabel, RunCriteriaLabel, StageLabel, SystemLabel};

use std::{
any::Any,
borrow::Cow,
fmt::Debug,
hash::{Hash, Hasher},
};
use bevy_utils::define_label;

pub trait DynEq: Any {
fn as_any(&self) -> &dyn Any;

fn dyn_eq(&self, other: &dyn DynEq) -> bool;
}

impl<T> DynEq for T
where
T: Any + Eq,
{
fn as_any(&self) -> &dyn Any {
self
}

fn dyn_eq(&self, other: &dyn DynEq) -> bool {
if let Some(other) = other.as_any().downcast_ref::<T>() {
return self == other;
}
false
}
}

pub trait DynHash: DynEq {
fn as_dyn_eq(&self) -> &dyn DynEq;

fn dyn_hash(&self, state: &mut dyn Hasher);
}

impl<T> DynHash for T
where
T: DynEq + Hash,
{
fn as_dyn_eq(&self) -> &dyn DynEq {
self
}

fn dyn_hash(&self, mut state: &mut dyn Hasher) {
T::hash(self, &mut state);
self.type_id().hash(&mut state);
}
}

pub trait StageLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn StageLabel>;
}
define_label!(StageLabel);
pub(crate) type BoxedStageLabel = Box<dyn StageLabel>;

pub trait SystemLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn SystemLabel>;
}
define_label!(SystemLabel);
pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>;

pub trait AmbiguitySetLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn AmbiguitySetLabel>;
}
define_label!(AmbiguitySetLabel);
pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>;

pub trait RunCriteriaLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel>;
}
define_label!(RunCriteriaLabel);
pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>;

macro_rules! impl_label {
($trait_name:ident) => {
impl PartialEq for dyn $trait_name {
fn eq(&self, other: &Self) -> bool {
self.dyn_eq(other.as_dyn_eq())
}
}

impl Eq for dyn $trait_name {}

impl Hash for dyn $trait_name {
fn hash<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}

impl Clone for Box<dyn $trait_name> {
fn clone(&self) -> Self {
self.dyn_clone()
}
}

impl $trait_name for Cow<'static, str> {
fn dyn_clone(&self) -> Box<dyn $trait_name> {
Box::new(self.clone())
}
}

impl $trait_name for &'static str {
fn dyn_clone(&self) -> Box<dyn $trait_name> {
Box::new(<&str>::clone(self))
}
}
};
}

impl_label!(StageLabel);
impl_label!(SystemLabel);
impl_label!(AmbiguitySetLabel);
impl_label!(RunCriteriaLabel);
1 change: 1 addition & 0 deletions crates/bevy_macro_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ keywords = ["bevy"]
[dependencies]
cargo-manifest = "0.2.3"
syn = "1.0"
quote = "1.0"
27 changes: 27 additions & 0 deletions crates/bevy_macro_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ extern crate proc_macro;

use cargo_manifest::{DepsSet, Manifest};
use proc_macro::TokenStream;
use quote::quote;
use std::{env, path::PathBuf};

pub struct BevyManifest {
Expand Down Expand Up @@ -59,3 +60,29 @@ fn get_path(path: &str) -> syn::Path {
fn parse_str<T: syn::parse::Parse>(path: &str) -> T {
syn::parse(path.parse::<TokenStream>().unwrap()).unwrap()
}

/// Derive a label trait
///
/// # Args
///
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label trait
/// - `trait_path`: The path [`syn::Path`] to the label trait
pub fn derive_label(input: syn::DeriveInput, trait_path: syn::Path) -> TokenStream {
let ident = input.ident;

let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap());

(quote! {
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> Box<dyn #trait_path> {
Box::new(Clone::clone(self))
}
}
})
.into()
}
Loading