diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index 43c554dedd0c5..4c1bbfeb358d0 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -4,7 +4,7 @@ use crate::{ plugin::Plugin, stage, startup_stage, PluginGroup, PluginGroupBuilder, }; -use bevy_ecs::{FromResources, IntoQuerySystem, Resources, System, World}; +use bevy_ecs::{FromResources, IntoSystem, Resources, System, World}; /// Configure [App]s using the builder pattern pub struct AppBuilder { diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 074608815c9b5..4b756cf02f341 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -2,7 +2,7 @@ use crate::{ update_asset_storage_system, Asset, AssetLoader, AssetServer, Handle, HandleId, RefChange, }; use bevy_app::{prelude::Events, AppBuilder}; -use bevy_ecs::{FromResources, IntoQuerySystem, ResMut}; +use bevy_ecs::{FromResources, IntoSystem, ResMut}; use bevy_type_registry::RegisterType; use bevy_utils::HashMap; use crossbeam_channel::Sender; diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index d5a78d8831d72..6f5098dede741 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -31,7 +31,7 @@ pub mod prelude { } use bevy_app::{prelude::Plugin, AppBuilder}; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_type_registry::RegisterType; /// Adds support for Assets to an App. Assets are typed collections with change tracking, which are added as App Resources. diff --git a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs index 4d979bbee1d42..bbafb39c8e38a 100644 --- a/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/frame_time_diagnostics_plugin.rs @@ -1,7 +1,7 @@ use crate::{Diagnostic, DiagnosticId, Diagnostics}; use bevy_app::prelude::*; use bevy_core::Time; -use bevy_ecs::{IntoQuerySystem, Res, ResMut}; +use bevy_ecs::{IntoSystem, Res, ResMut}; /// Adds "frame time" diagnostic to an App, specifically "frame time", "fps" and "frame count" #[derive(Default)] diff --git a/crates/bevy_diagnostic/src/lib.rs b/crates/bevy_diagnostic/src/lib.rs index c5f9b4c394de7..ce4b285887e82 100644 --- a/crates/bevy_diagnostic/src/lib.rs +++ b/crates/bevy_diagnostic/src/lib.rs @@ -18,7 +18,7 @@ impl Plugin for DiagnosticsPlugin { app.init_resource::(); #[cfg(feature = "profiler")] { - use bevy_ecs::IntoQuerySystem; + use bevy_ecs::IntoSystem; app.add_resource::>(Box::new( system_profiler::SystemProfiler::default(), )) diff --git a/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs index db7878ad3b22e..62bd855a95caa 100644 --- a/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/print_diagnostics_plugin.rs @@ -1,7 +1,7 @@ use super::{Diagnostic, DiagnosticId, Diagnostics}; use bevy_app::prelude::*; use bevy_core::{Time, Timer}; -use bevy_ecs::{IntoQuerySystem, Res, ResMut}; +use bevy_ecs::{IntoSystem, Res, ResMut}; use std::time::Duration; /// An App Plugin that prints diagnostics to the console diff --git a/crates/bevy_ecs/hecs/macros/src/lib.rs b/crates/bevy_ecs/hecs/macros/src/lib.rs index 021c5f060bb1c..4e541ba187888 100644 --- a/crates/bevy_ecs/hecs/macros/src/lib.rs +++ b/crates/bevy_ecs/hecs/macros/src/lib.rs @@ -22,7 +22,10 @@ use proc_macro::TokenStream; use proc_macro2::{Span, TokenStream as TokenStream2}; use proc_macro_crate::crate_name; use quote::quote; -use syn::{parse_macro_input, DeriveInput, Error, Ident, Index, Lifetime, Path, Result}; +use syn::{ + parse::ParseStream, parse_macro_input, Data, DataStruct, DeriveInput, Error, Field, Fields, + Ident, Index, Lifetime, Path, Result, +}; /// Implement `Bundle` for a monomorphic struct /// @@ -332,3 +335,92 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { tokens } + +#[derive(Default)] +struct SystemParamFieldAttributes { + pub ignore: bool, +} + +static SYSTEM_PARAM_ATTRIBUTE_NAME: &str = "system_param"; + +#[proc_macro_derive(SystemParam, attributes(system_param))] +pub fn derive_system_param(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + let fields = match &ast.data { + Data::Struct(DataStruct { + fields: Fields::Named(fields), + .. + }) => &fields.named, + _ => panic!("expected a struct with named fields"), + }; + + let path_str = if crate_name("bevy").is_ok() { + "bevy::ecs" + } else { + "bevy_ecs" + }; + let path: Path = syn::parse(path_str.parse::().unwrap()).unwrap(); + + let field_attributes = fields + .iter() + .map(|field| { + ( + field, + field + .attrs + .iter() + .find(|a| *a.path.get_ident().as_ref().unwrap() == SYSTEM_PARAM_ATTRIBUTE_NAME) + .map_or_else(SystemParamFieldAttributes::default, |a| { + syn::custom_keyword!(ignore); + let mut attributes = SystemParamFieldAttributes::default(); + a.parse_args_with(|input: ParseStream| { + if input.parse::>()?.is_some() { + attributes.ignore = true; + } + Ok(()) + }) + .expect("invalid 'render_resources' attribute format"); + + attributes + }), + ) + }) + .collect::>(); + let mut fields = Vec::new(); + let mut field_types = Vec::new(); + let mut ignored_fields = Vec::new(); + let mut ignored_field_types = Vec::new(); + for (field, attrs) in field_attributes.iter() { + if attrs.ignore { + ignored_fields.push(field.ident.as_ref().unwrap()); + ignored_field_types.push(&field.ty); + } else { + fields.push(field.ident.as_ref().unwrap()); + field_types.push(&field.ty); + } + } + + let generics = ast.generics; + let (impl_generics, ty_generics, _where_clause) = generics.split_for_impl(); + + let struct_name = &ast.ident; + + TokenStream::from(quote! { + impl #impl_generics #path::SystemParam for #struct_name#ty_generics { + fn init(system_state: &mut #path::SystemState, world: &#path::World, resources: &mut #path::Resources) { + #(<#field_types>::init(system_state, world, resources);)* + } + + unsafe fn get_param( + system_state: &mut #path::SystemState, + world: &#path::World, + resources: &#path::Resources, + ) -> Option { + Some(#struct_name { + #(#fields: <#field_types>::get_param(system_state, world, resources)?,)* + #(#ignored_fields: <#ignored_field_types>::default(),)* + }) + } + } + }) +} diff --git a/crates/bevy_ecs/hecs/src/lib.rs b/crates/bevy_ecs/hecs/src/lib.rs index a432f98de59b8..d72b2506c849b 100644 --- a/crates/bevy_ecs/hecs/src/lib.rs +++ b/crates/bevy_ecs/hecs/src/lib.rs @@ -98,4 +98,4 @@ pub use lazy_static; pub use query::Fetch; #[cfg(feature = "macros")] -pub use bevy_hecs_macros::{impl_query_set, Bundle}; +pub use bevy_hecs_macros::{impl_query_set, Bundle, SystemParam}; diff --git a/crates/bevy_ecs/hecs/src/query.rs b/crates/bevy_ecs/hecs/src/query.rs index 81d6d35943c01..93445ee86fe54 100644 --- a/crates/bevy_ecs/hecs/src/query.rs +++ b/crates/bevy_ecs/hecs/src/query.rs @@ -296,7 +296,7 @@ impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10); /// .collect::>(); /// assert_eq!(components, &[(false, 457)]); /// ``` -pub struct Or(PhantomData); +pub struct Or(pub T); //pub struct Or(PhantomData<(Q1, Q2, Q3)>); #[doc(hidden)] diff --git a/crates/bevy_ecs/hecs/src/world.rs b/crates/bevy_ecs/hecs/src/world.rs index f2bed53072c6d..8f3768f7ea6da 100644 --- a/crates/bevy_ecs/hecs/src/world.rs +++ b/crates/bevy_ecs/hecs/src/world.rs @@ -249,6 +249,7 @@ impl World { /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` + #[inline] pub fn query(&self) -> QueryIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -281,6 +282,7 @@ impl World { /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` + #[inline] pub fn query_mut(&mut self) -> QueryIter<'_, Q> { // SAFE: unique mutable access unsafe { self.query_unchecked() } @@ -288,6 +290,7 @@ impl World { /// Like `query`, but instead of returning a single iterator it returns a "batched iterator", /// where each batch is `batch_size`. This is generally used for parallel iteration. + #[inline] pub fn query_batched(&self, batch_size: usize) -> BatchedIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -298,6 +301,7 @@ impl World { /// Like `query`, but instead of returning a single iterator it returns a "batched iterator", /// where each batch is `batch_size`. This is generally used for parallel iteration. + #[inline] pub fn query_batched_mut(&mut self, batch_size: usize) -> BatchedIter<'_, Q> { // SAFE: unique mutable access unsafe { self.query_batched_unchecked(batch_size) } @@ -316,6 +320,7 @@ impl World { /// # Safety /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. + #[inline] pub unsafe fn query_unchecked(&self) -> QueryIter<'_, Q> { QueryIter::new(&self.archetypes) } @@ -347,6 +352,7 @@ impl World { /// let (number, flag) = world.query_one::<(&i32, &bool)>(a).unwrap(); /// assert_eq!(*number, 123); /// ``` + #[inline] pub fn query_one( &self, entity: Entity, @@ -372,6 +378,7 @@ impl World { /// if *flag { *number *= 2; } /// assert_eq!(*number, 246); /// ``` + #[inline] pub fn query_one_mut( &mut self, entity: Entity, @@ -387,6 +394,7 @@ impl World { /// # Safety /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. + #[inline] pub unsafe fn query_one_unchecked( &self, entity: Entity, @@ -399,6 +407,7 @@ impl World { } /// Borrow the `T` component of `entity` + #[inline] pub fn get(&self, entity: Entity) -> Result<&'_ T, ComponentError> { unsafe { let loc = self.entities.get(entity)?; @@ -414,6 +423,7 @@ impl World { } /// Mutably borrow the `T` component of `entity` + #[inline] pub fn get_mut(&mut self, entity: Entity) -> Result, ComponentError> { // SAFE: uniquely borrows world unsafe { self.get_mut_unchecked(entity) } @@ -434,6 +444,7 @@ impl World { /// # Safety /// This does not check for mutable access correctness. To be safe, make sure this is the only /// thing accessing this entity's T component. + #[inline] pub unsafe fn get_mut_unchecked( &self, entity: Entity, diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 96e284b92174e..b48f77be5bbaa 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -11,10 +11,8 @@ pub use world::*; pub mod prelude { pub use crate::{ - resource::{ChangedRes, FromResources, Local, OrRes, Res, ResMut, Resource, Resources}, - system::{ - Commands, IntoForEachSystem, IntoQuerySystem, IntoThreadLocalSystem, Query, System, - }, + resource::{ChangedRes, FromResources, Local, Res, ResMut, Resource, Resources}, + system::{Commands, IntoSystem, IntoThreadLocalSystem, Query, System}, world::WorldBuilderSource, Added, Bundle, Changed, Component, Entity, Mut, Mutated, Or, QuerySet, Ref, RefMut, With, Without, World, diff --git a/crates/bevy_ecs/src/resource/resource_query.rs b/crates/bevy_ecs/src/resource/resource_query.rs index ab33775bce0e6..c6e3c4bf61372 100644 --- a/crates/bevy_ecs/src/resource/resource_query.rs +++ b/crates/bevy_ecs/src/resource/resource_query.rs @@ -1,11 +1,10 @@ -use super::{FromResources, Resources}; -use crate::{system::SystemId, Resource, ResourceIndex}; -use bevy_hecs::{smaller_tuples_too, TypeAccess}; +use super::FromResources; +use crate::{Resource, ResourceIndex, Resources, SystemId}; use core::{ ops::{Deref, DerefMut}, ptr::NonNull, }; -use std::{any::TypeId, marker::PhantomData}; +use std::marker::PhantomData; // TODO: align TypeAccess api with Query::Fetch @@ -28,15 +27,6 @@ impl<'a, T: Resource> ChangedRes<'a, T> { } } -impl<'a, T: Resource> UnsafeClone for ChangedRes<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { value: self.value } - } -} - -unsafe impl Send for ChangedRes<'_, T> {} -unsafe impl Sync for ChangedRes<'_, T> {} - impl<'a, T: Resource> Deref for ChangedRes<'a, T> { type Target = T; @@ -63,21 +53,6 @@ impl<'a, T: Resource> Res<'a, T> { } } -/// A clone that is unsafe to perform. You probably shouldn't use this. -pub trait UnsafeClone { - #[allow(clippy::missing_safety_doc)] - unsafe fn unsafe_clone(&self) -> Self; -} - -impl<'a, T: Resource> UnsafeClone for Res<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { value: self.value } - } -} - -unsafe impl Send for Res<'_, T> {} -unsafe impl Sync for Res<'_, T> {} - impl<'a, T: Resource> Deref for Res<'a, T> { type Target = T; @@ -108,9 +83,6 @@ impl<'a, T: Resource> ResMut<'a, T> { } } -unsafe impl Send for ResMut<'_, T> {} -unsafe impl Sync for ResMut<'_, T> {} - impl<'a, T: Resource> Deref for ResMut<'a, T> { type Target = T; @@ -128,16 +100,6 @@ impl<'a, T: Resource> DerefMut for ResMut<'a, T> { } } -impl<'a, T: Resource> UnsafeClone for ResMut<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { - value: self.value, - mutated: self.mutated, - _marker: Default::default(), - } - } -} - /// Local resources are unique per-system. Two instances of the same system will each have their own resource. /// Local resources are automatically initialized using the FromResources trait. #[derive(Debug)] @@ -146,10 +108,12 @@ pub struct Local<'a, T: Resource + FromResources> { _marker: PhantomData<&'a T>, } -impl<'a, T: Resource + FromResources> UnsafeClone for Local<'a, T> { - unsafe fn unsafe_clone(&self) -> Self { - Self { - value: self.value, +impl<'a, T: Resource + FromResources> Local<'a, T> { + pub(crate) unsafe fn new(resources: &Resources, id: SystemId) -> Self { + Local { + value: resources + .get_unsafe_ref::(ResourceIndex::System(id)) + .as_ptr(), _marker: Default::default(), } } @@ -169,337 +133,45 @@ impl<'a, T: Resource + FromResources> DerefMut for Local<'a, T> { } } -/// A collection of resource types fetch from a `Resources` collection -pub trait ResourceQuery { - type Fetch: for<'a> FetchResource<'a>; - - fn initialize(_resources: &mut Resources, _system_id: Option) {} -} - -/// Streaming iterators over contiguous homogeneous ranges of resources -pub trait FetchResource<'a>: Sized { - /// Type of value to be fetched - type Item: UnsafeClone; - - fn access() -> TypeAccess; - fn borrow(resources: &Resources); - fn release(resources: &Resources); - - #[allow(clippy::missing_safety_doc)] - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item; - - #[allow(clippy::missing_safety_doc)] - unsafe fn is_some(_resources: &'a Resources, _system_id: Option) -> bool { - true - } -} - -impl<'a, T: Resource> ResourceQuery for Res<'a, T> { - type Fetch = FetchResourceRead; -} - -/// Fetches a shared resource reference -#[derive(Debug)] -pub struct FetchResourceRead(NonNull); - -impl<'a, T: Resource> FetchResource<'a> for FetchResourceRead { - type Item = Res<'a, T>; - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - Res::new(resources.get_unsafe_ref::(ResourceIndex::Global)) - } - - fn borrow(resources: &Resources) { - resources.borrow::(); - } - - fn release(resources: &Resources) { - resources.release::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_read(TypeId::of::()); - access - } -} - -impl<'a, T: Resource> ResourceQuery for ChangedRes<'a, T> { - type Fetch = FetchResourceChanged; -} - -/// Fetches a shared resource reference -#[derive(Debug)] -pub struct FetchResourceChanged(NonNull); - -impl<'a, T: Resource> FetchResource<'a> for FetchResourceChanged { - type Item = ChangedRes<'a, T>; - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - ChangedRes::new(resources.get_unsafe_ref::(ResourceIndex::Global)) - } - - unsafe fn is_some(resources: &'a Resources, _system_id: Option) -> bool { - let (added, mutated) = resources.get_unsafe_added_and_mutated::(ResourceIndex::Global); - *added.as_ptr() || *mutated.as_ptr() - } - - fn borrow(resources: &Resources) { - resources.borrow::(); - } - - fn release(resources: &Resources) { - resources.release::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_read(TypeId::of::()); - access - } -} - -impl<'a, T: Resource> ResourceQuery for ResMut<'a, T> { - type Fetch = FetchResourceWrite; -} - -/// Fetches a unique resource reference -#[derive(Debug)] -pub struct FetchResourceWrite(NonNull); - -impl<'a, T: Resource> FetchResource<'a> for FetchResourceWrite { - type Item = ResMut<'a, T>; - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - let (value, type_state) = - resources.get_unsafe_ref_with_type_state::(ResourceIndex::Global); - ResMut::new(value, type_state.mutated()) - } - - fn borrow(resources: &Resources) { - resources.borrow_mut::(); - } - - fn release(resources: &Resources) { - resources.release_mut::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_write(TypeId::of::()); - access - } -} - -impl<'a, T: Resource + FromResources> ResourceQuery for Local<'a, T> { - type Fetch = FetchResourceLocalMut; - - fn initialize(resources: &mut Resources, id: Option) { - let id = id.expect("Local resources can only be used by systems"); - - // Only add the local resource if it doesn't already exist for this system - if resources.get_local::(id).is_none() { - let value = T::from_resources(resources); - resources.insert_local(id, value); - } - } -} - -/// Fetches a `Local` resource reference -#[derive(Debug)] -pub struct FetchResourceLocalMut(NonNull); - -impl<'a, T: Resource + FromResources> FetchResource<'a> for FetchResourceLocalMut { - type Item = Local<'a, T>; - - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item { - let id = system_id.expect("Local resources can only be used by systems"); - Local { - value: resources - .get_unsafe_ref::(ResourceIndex::System(id)) - .as_ptr(), - _marker: Default::default(), - } - } - - fn borrow(resources: &Resources) { - resources.borrow_mut::(); - } - - fn release(resources: &Resources) { - resources.release_mut::(); - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_write(TypeId::of::()); - access - } -} - -macro_rules! tuple_impl { - ($($name: ident),*) => { - impl<'a, $($name: FetchResource<'a>),*> FetchResource<'a> for ($($name,)*) { - type Item = ($($name::Item,)*); - - #[allow(unused_variables)] - fn borrow(resources: &Resources) { - $($name::borrow(resources);)* - } - - #[allow(unused_variables)] - fn release(resources: &Resources) { - $($name::release(resources);)* - } - - #[allow(unused_variables)] - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item { - ($($name::get(resources, system_id),)*) - } - - #[allow(unused_variables)] - unsafe fn is_some(resources: &'a Resources, system_id: Option) -> bool { - true $(&& $name::is_some(resources, system_id))* - } - - #[allow(unused_mut)] - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - $(access.union(&$name::access());)* - access - } - } - - impl<$($name: ResourceQuery),*> ResourceQuery for ($($name,)*) { - type Fetch = ($($name::Fetch,)*); - - #[allow(unused_variables)] - fn initialize(resources: &mut Resources, system_id: Option) { - $($name::initialize(resources, system_id);)* - } - } - - #[allow(unused_variables)] - #[allow(non_snake_case)] - impl<$($name: UnsafeClone),*> UnsafeClone for ($($name,)*) { - unsafe fn unsafe_clone(&self) -> Self { - let ($($name,)*) = self; - ($($name.unsafe_clone(),)*) - } - } - }; -} - -smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A); - -#[derive(Debug)] -pub struct OrRes(T); - -#[derive(Debug)] -pub struct FetchResourceOr(NonNull); - -macro_rules! tuple_impl_or { - ($($name: ident),*) => { - impl<'a, $($name: FetchResource<'a>),*> FetchResource<'a> for FetchResourceOr<($($name,)*)> { - type Item = OrRes<($($name::Item,)*)>; - - #[allow(unused_variables)] - fn borrow(resources: &Resources) { - $($name::borrow(resources);)* - } - - #[allow(unused_variables)] - fn release(resources: &Resources) { - $($name::release(resources);)* - } - - #[allow(unused_variables)] - unsafe fn get(resources: &'a Resources, system_id: Option) -> Self::Item { - OrRes(($($name::get(resources, system_id),)*)) - } - - #[allow(unused_variables)] - unsafe fn is_some(resources: &'a Resources, system_id: Option) -> bool { - false $(|| $name::is_some(resources, system_id))* - } - - #[allow(unused_mut)] - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - $(access.union(&$name::access());)* - access - } - } - - impl<$($name: ResourceQuery),*> ResourceQuery for OrRes<($($name,)*)> { - type Fetch = FetchResourceOr<($($name::Fetch,)*)>; - - #[allow(unused_variables)] - fn initialize(resources: &mut Resources, system_id: Option) { - $($name::initialize(resources, system_id);)* - } - } - - #[allow(unused_variables)] - #[allow(non_snake_case)] - impl<$($name: UnsafeClone),*> UnsafeClone for OrRes<($($name,)*)> { - unsafe fn unsafe_clone(&self) -> Self { - let OrRes(($($name,)*)) = self; - OrRes(($($name.unsafe_clone(),)*)) - } - } - - impl<$($name,)*> Deref for OrRes<($($name,)*)> { - type Target = ($($name,)*); - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - }; -} - -smaller_tuples_too!(tuple_impl_or, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn changed_resource() { - let mut resources = Resources::default(); - resources.insert(123); - assert_eq!( - resources.query::>().as_deref(), - Some(&(123 as i32)) - ); - resources.clear_trackers(); - assert_eq!(resources.query::>().as_deref(), None); - *resources.query::>().unwrap() += 1; - assert_eq!( - resources.query::>().as_deref(), - Some(&(124 as i32)) - ); - } - - #[test] - fn or_changed_resource() { - let mut resources = Resources::default(); - resources.insert(123); - resources.insert(0.2); - assert!(resources - .query::, ChangedRes)>>() - .is_some(),); - resources.clear_trackers(); - assert!(resources - .query::, ChangedRes)>>() - .is_none(),); - *resources.query::>().unwrap() += 1; - assert!(resources - .query::, ChangedRes)>>() - .is_some(),); - assert!(resources - .query::<(ChangedRes, ChangedRes)>() - .is_none(),); - } -} +// #[cfg(test)] +// mod tests { +// use super::*; + +// #[test] +// fn changed_resource() { +// let mut resources = Resources::default(); +// resources.insert(123); +// assert_eq!( +// resources.query::>().as_deref(), +// Some(&(123 as i32)) +// ); +// resources.clear_trackers(); +// assert_eq!(resources.query::>().as_deref(), None); +// *resources.query::>().unwrap() += 1; +// assert_eq!( +// resources.query::>().as_deref(), +// Some(&(124 as i32)) +// ); +// } + +// #[test] +// fn or_changed_resource() { +// let mut resources = Resources::default(); +// resources.insert(123); +// resources.insert(0.2); +// assert!(resources +// .query::, ChangedRes)>>() +// .is_some(),); +// resources.clear_trackers(); +// assert!(resources +// .query::, ChangedRes)>>() +// .is_none(),); +// *resources.query::>().unwrap() += 1; +// assert!(resources +// .query::, ChangedRes)>>() +// .is_some(),); +// assert!(resources +// .query::<(ChangedRes, ChangedRes)>() +// .is_none(),); +// } +// } diff --git a/crates/bevy_ecs/src/resource/resources.rs b/crates/bevy_ecs/src/resource/resources.rs index a912599e16503..fc0c9c5a6bcf3 100644 --- a/crates/bevy_ecs/src/resource/resources.rs +++ b/crates/bevy_ecs/src/resource/resources.rs @@ -1,4 +1,3 @@ -use super::{FetchResource, ResourceQuery}; use crate::system::SystemId; use bevy_hecs::{Archetype, AtomicBorrow, Entity, Ref, RefMut, TypeInfo, TypeState}; use bevy_utils::HashMap; @@ -264,29 +263,6 @@ impl Resources { }) } - pub fn query(&self) -> Option<::Item> { - unsafe { - if Q::Fetch::is_some(&self, None) { - Some(Q::Fetch::get(&self, None)) - } else { - None - } - } - } - - pub fn query_system( - &self, - id: SystemId, - ) -> Option<::Item> { - unsafe { - if Q::Fetch::is_some(&self, Some(id)) { - Some(Q::Fetch::get(&self, Some(id))) - } else { - None - } - } - } - #[inline] #[allow(clippy::missing_safety_doc)] pub unsafe fn get_unsafe_ref(&self, resource_index: ResourceIndex) -> NonNull { diff --git a/crates/bevy_ecs/src/schedule/parallel_executor.rs b/crates/bevy_ecs/src/schedule/parallel_executor.rs index d9e0fd9f342c9..5f6820dbb9bde 100644 --- a/crates/bevy_ecs/src/schedule/parallel_executor.rs +++ b/crates/bevy_ecs/src/schedule/parallel_executor.rs @@ -532,7 +532,7 @@ mod tests { use crate::{ resource::{Res, ResMut, Resources}, schedule::Schedule, - system::{IntoQuerySystem, IntoThreadLocalSystem, Query}, + system::{IntoSystem, IntoThreadLocalSystem, Query}, Commands, }; use bevy_hecs::{Entity, World}; @@ -556,7 +556,7 @@ mod tests { schedule.add_stage("PreArchetypeChange"); schedule.add_stage("PostArchetypeChange"); - fn insert(mut commands: Commands) { + fn insert(commands: &mut Commands) { commands.spawn((1u32,)); } @@ -603,6 +603,7 @@ mod tests { schedule.add_system_to_stage("update", insert.thread_local_system()); schedule.add_system_to_stage("update", read.system()); + schedule.initialize(&mut world, &mut resources); let mut executor = ParallelExecutor::default(); executor.run(&mut schedule, &mut world, &mut resources); @@ -743,6 +744,7 @@ mod tests { schedule.add_system_to_stage("C", read_isize_res.system()); schedule.add_system_to_stage("C", read_isize_write_f64_res.system()); schedule.add_system_to_stage("C", write_f64_res.system()); + schedule.initialize(&mut world, &mut resources); fn run_executor_and_validate( executor: &mut ParallelExecutor, diff --git a/crates/bevy_ecs/src/schedule/schedule.rs b/crates/bevy_ecs/src/schedule/schedule.rs index 050b066040bd5..fd710946f4a2c 100644 --- a/crates/bevy_ecs/src/schedule/schedule.rs +++ b/crates/bevy_ecs/src/schedule/schedule.rs @@ -198,4 +198,14 @@ impl Schedule { pub fn generation(&self) -> usize { self.generation } + + pub fn run_on_systems(&mut self, mut func: impl FnMut(&mut dyn System)) { + for stage_name in self.stage_order.iter() { + if let Some(stage_systems) = self.stages.get_mut(stage_name) { + for system in stage_systems.iter_mut() { + func(&mut **system); + } + } + } + } } diff --git a/crates/bevy_ecs/src/system/commands.rs b/crates/bevy_ecs/src/system/commands.rs index 6f6981c771c05..849c7d20bbf0b 100644 --- a/crates/bevy_ecs/src/system/commands.rs +++ b/crates/bevy_ecs/src/system/commands.rs @@ -1,8 +1,7 @@ use super::SystemId; use crate::resource::{Resource, Resources}; use bevy_hecs::{Bundle, Component, DynamicBundle, Entity, EntityReserver, World}; -use parking_lot::Mutex; -use std::{marker::PhantomData, sync::Arc}; +use std::marker::PhantomData; /// A [World] mutation pub trait Command: Send + Sync { @@ -157,13 +156,13 @@ impl Command for InsertLocalResource { } #[derive(Default)] -pub struct CommandsInternal { - pub commands: Vec>, - pub current_entity: Option, - pub entity_reserver: Option, +pub struct Commands { + commands: Vec>, + current_entity: Option, + entity_reserver: Option, } -impl CommandsInternal { +impl Commands { pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self { let entity = self .entity_reserver @@ -175,53 +174,6 @@ impl CommandsInternal { self } - pub fn with_bundle( - &mut self, - components: impl DynamicBundle + Send + Sync + 'static, - ) -> &mut Self { - let current_entity = self.current_entity.expect("Cannot add components because the 'current entity' is not set. You should spawn an entity first."); - self.commands.push(Box::new(Insert { - entity: current_entity, - components, - })); - self - } - - pub fn with(&mut self, component: impl Component) -> &mut Self { - let current_entity = self.current_entity.expect("Cannot add component because the 'current entity' is not set. You should spawn an entity first."); - self.commands.push(Box::new(InsertOne { - entity: current_entity, - component, - })); - self - } - - pub fn add_command(&mut self, command: C) -> &mut Self { - self.commands.push(Box::new(command)); - self - } - - pub fn add_command_boxed(&mut self, command: Box) -> &mut Self { - self.commands.push(command); - self - } -} - -/// A queue of [Command]s to run on the current [World] and [Resources]. Todo: remove arc here -#[derive(Default, Clone)] -pub struct Commands { - pub commands: Arc>, -} - -impl Commands { - pub fn spawn(&mut self, components: impl DynamicBundle + Send + Sync + 'static) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.spawn(components); - } - self - } - pub fn spawn_batch(&mut self, components_iter: I) -> &mut Self where I: IntoIterator + Send + Sync + 'static, @@ -235,25 +187,6 @@ impl Commands { self.add_command(Despawn { entity }) } - pub fn with(&mut self, component: impl Component) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.with(component); - } - self - } - - pub fn with_bundle( - &mut self, - components: impl DynamicBundle + Send + Sync + 'static, - ) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.with_bundle(components); - } - self - } - pub fn insert( &mut self, entity: Entity, @@ -281,61 +214,85 @@ impl Commands { }) } + pub fn remove_one(&mut self, entity: Entity) -> &mut Self + where + T: Component, + { + self.add_command(RemoveOne:: { + entity, + phantom: PhantomData, + }) + } + + pub fn remove(&mut self, entity: Entity) -> &mut Self + where + T: Bundle + Send + Sync + 'static, + { + self.add_command(Remove:: { + entity, + phantom: PhantomData, + }) + } + + pub fn with_bundle( + &mut self, + components: impl DynamicBundle + Send + Sync + 'static, + ) -> &mut Self { + let current_entity = self.current_entity.expect("Cannot add components because the 'current entity' is not set. You should spawn an entity first."); + self.commands.push(Box::new(Insert { + entity: current_entity, + components, + })); + self + } + + pub fn with(&mut self, component: impl Component) -> &mut Self { + let current_entity = self.current_entity.expect("Cannot add component because the 'current entity' is not set. You should spawn an entity first."); + self.commands.push(Box::new(InsertOne { + entity: current_entity, + component, + })); + self + } + pub fn add_command(&mut self, command: C) -> &mut Self { - self.commands.lock().add_command(command); + self.commands.push(Box::new(command)); self } pub fn add_command_boxed(&mut self, command: Box) -> &mut Self { - self.commands.lock().add_command_boxed(command); + self.commands.push(command); self } - pub fn apply(&self, world: &mut World, resources: &mut Resources) { - let mut commands = self.commands.lock(); - for command in commands.commands.drain(..) { + pub fn apply(&mut self, world: &mut World, resources: &mut Resources) { + for command in self.commands.drain(..) { command.write(world, resources); } } pub fn current_entity(&self) -> Option { - let commands = self.commands.lock(); - commands.current_entity + self.current_entity } - pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self { - { - let commands = self.commands.lock(); - let current_entity = commands - .current_entity - .expect("The 'current entity' is not set. You should spawn an entity first."); - f(current_entity); - } - self + pub fn set_current_entity(&mut self, entity: Entity) { + self.current_entity = Some(entity); } - pub fn remove_one(&mut self, entity: Entity) -> &mut Self - where - T: Component, - { - self.add_command(RemoveOne:: { - entity, - phantom: PhantomData, - }) + pub fn clear_current_entity(&mut self) { + self.current_entity = None; } - pub fn remove(&mut self, entity: Entity) -> &mut Self - where - T: Bundle + Send + Sync + 'static, - { - self.add_command(Remove:: { - entity, - phantom: PhantomData, - }) + pub fn for_current_entity(&mut self, f: impl FnOnce(Entity)) -> &mut Self { + let current_entity = self + .current_entity + .expect("The 'current entity' is not set. You should spawn an entity first."); + f(current_entity); + self } - pub fn set_entity_reserver(&self, entity_reserver: EntityReserver) { - self.commands.lock().entity_reserver = Some(entity_reserver); + pub fn set_entity_reserver(&mut self, entity_reserver: EntityReserver) { + self.entity_reserver = Some(entity_reserver); } } diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 502913a697907..58fddc3fd86d9 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -1,424 +1,214 @@ -pub use super::Query; -use crate::{ - resource::{FetchResource, ResourceQuery, Resources, UnsafeClone}, - system::{Commands, System, SystemId, ThreadLocalExecution}, - QueryAccess, QuerySet, QueryTuple, TypeAccess, -}; -use bevy_hecs::{ArchetypeComponent, Fetch, Query as HecsQuery, World}; -use std::{any::TypeId, borrow::Cow}; - -#[derive(Debug)] -pub(crate) struct SystemFn +use crate::{Commands, Resources, System, SystemId, SystemParam, ThreadLocalExecution}; +use bevy_hecs::{ArchetypeComponent, QueryAccess, TypeAccess, World}; +use parking_lot::Mutex; +use std::{any::TypeId, borrow::Cow, sync::Arc}; + +pub struct SystemState { + pub(crate) id: SystemId, + pub(crate) name: Cow<'static, str>, + pub(crate) is_initialized: bool, + pub(crate) archetype_component_access: TypeAccess, + pub(crate) resource_access: TypeAccess, + pub(crate) query_archetype_component_accesses: Vec>, + pub(crate) query_accesses: Vec>, + pub(crate) query_type_names: Vec<&'static str>, + pub(crate) commands: Commands, + pub(crate) arc_commands: Option>>, + pub(crate) current_query_index: usize, +} + +impl SystemState { + pub fn reset_indices(&mut self) { + self.current_query_index = 0; + } + + pub fn update(&mut self, world: &World) { + self.archetype_component_access.clear(); + let mut conflict_index = None; + let mut conflict_name = None; + for (i, (query_accesses, component_access)) in self + .query_accesses + .iter() + .zip(self.query_archetype_component_accesses.iter_mut()) + .enumerate() + { + component_access.clear(); + for query_access in query_accesses.iter() { + query_access.get_world_archetype_access(world, Some(component_access)); + } + if !component_access.is_compatible(&self.archetype_component_access) { + conflict_index = Some(i); + conflict_name = component_access + .get_conflict(&self.archetype_component_access) + .and_then(|archetype_component| { + query_accesses + .iter() + .filter_map(|query_access| { + query_access.get_type_name(archetype_component.component) + }) + .next() + }); + break; + } + self.archetype_component_access.union(component_access); + } + if let Some(conflict_index) = conflict_index { + let mut conflicts_with_index = None; + for prior_index in 0..conflict_index { + if !self.query_archetype_component_accesses[conflict_index] + .is_compatible(&self.query_archetype_component_accesses[prior_index]) + { + conflicts_with_index = Some(prior_index); + } + } + panic!("System {} has conflicting queries. {} conflicts with the component access [{}] in this prior query: {}", + core::any::type_name::(), + self.query_type_names[conflict_index], + conflict_name.unwrap_or("Unknown"), + conflicts_with_index.map(|index| self.query_type_names[index]).unwrap_or("Unknown")); + } + } +} + +pub struct FuncSystem where - F: FnMut(&World, &Resources, &mut State) + Send + Sync, - ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, - State: Send + Sync, + F: FnMut(&mut SystemState, &World, &Resources) + Send + Sync + 'static, + Init: FnMut(&mut SystemState, &World, &mut Resources) + Send + Sync + 'static, + ThreadLocalFunc: FnMut(&mut SystemState, &mut World, &mut Resources) + Send + Sync + 'static, { - pub state: State, - pub func: F, - pub thread_local_func: ThreadLocalF, - pub init_func: Init, - pub thread_local_execution: ThreadLocalExecution, - pub resource_access: TypeAccess, - pub name: Cow<'static, str>, - pub id: SystemId, - pub archetype_component_access: TypeAccess, - pub update_func: Update, + func: F, + thread_local_func: ThreadLocalFunc, + init_func: Init, + state: SystemState, } -impl System for SystemFn +impl System for FuncSystem where - F: FnMut(&World, &Resources, &mut State) + Send + Sync, - ThreadLocalF: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Init: FnMut(&mut World, &mut Resources, &mut State) + Send + Sync, - Update: FnMut(&World, &mut TypeAccess, &mut State) + Send + Sync, - State: Send + Sync, + F: FnMut(&mut SystemState, &World, &Resources) + Send + Sync + 'static, + Init: FnMut(&mut SystemState, &World, &mut Resources) + Send + Sync + 'static, + ThreadLocalFunc: FnMut(&mut SystemState, &mut World, &mut Resources) + Send + Sync + 'static, { - fn name(&self) -> Cow<'static, str> { - self.name.clone() + fn name(&self) -> std::borrow::Cow<'static, str> { + self.state.name.clone() + } + + fn id(&self) -> SystemId { + self.state.id } fn update(&mut self, world: &World) { - (self.update_func)(world, &mut self.archetype_component_access, &mut self.state); + self.state.update(world); } fn archetype_component_access(&self) -> &TypeAccess { - &self.archetype_component_access + &self.state.archetype_component_access } - fn resource_access(&self) -> &TypeAccess { - &self.resource_access + fn resource_access(&self) -> &TypeAccess { + &self.state.resource_access } fn thread_local_execution(&self) -> ThreadLocalExecution { - self.thread_local_execution + ThreadLocalExecution::NextFlush } - #[inline] fn run(&mut self, world: &World, resources: &Resources) { - (self.func)(world, resources, &mut self.state); + (self.func)(&mut self.state, world, resources) } fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) { - (self.thread_local_func)(world, resources, &mut self.state); + (self.thread_local_func)(&mut self.state, world, resources) } fn initialize(&mut self, world: &mut World, resources: &mut Resources) { - (self.init_func)(world, resources, &mut self.state); + (self.init_func)(&mut self.state, world, resources); + self.state.is_initialized = true; } - fn id(&self) -> SystemId { - self.id + fn is_initialized(&self) -> bool { + self.state.is_initialized } } -/// Converts `Self` into a For-Each system -pub trait IntoForEachSystem { +pub trait IntoSystem { fn system(self) -> Box; } -struct ForEachState { - commands: Commands, - query_access: QueryAccess, -} - -macro_rules! impl_into_foreach_system { - (($($commands: ident)*), ($($resource: ident),*), ($($component: ident),*)) => { - impl IntoForEachSystem<($($commands,)*), ($($resource,)*), ($($component,)*)> for Func - where - Func: - FnMut($($commands,)* $($resource,)* $($component,)*) + - FnMut( - $($commands,)* - $(<<$resource as ResourceQuery>::Fetch as FetchResource>::Item,)* - $(<<$component as HecsQuery>::Fetch as Fetch>::Item,)*)+ - Send + Sync + 'static, - $($component: HecsQuery,)* - $($resource: ResourceQuery,)* +macro_rules! impl_into_system { + ($($param: ident),*) => { + impl IntoSystem<($($param,)*)> for Func + where Func: FnMut($($param),*) + Send + Sync + 'static, { - #[allow(non_snake_case)] #[allow(unused_variables)] #[allow(unused_unsafe)] - fn system(mut self) -> Box { - let id = SystemId::new(); - Box::new(SystemFn { - state: ForEachState { - commands: Commands::default(), - query_access: <($($component,)*) as HecsQuery>::Fetch::access(), - }, - thread_local_execution: ThreadLocalExecution::NextFlush, - name: core::any::type_name::().into(), - id, - func: move |world, resources, state| { - { - let state_commands = &state.commands; - if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) { - // SAFE: the scheduler has ensured that there is no archetype clashing here - unsafe { - for ($($component,)*) in world.query_unchecked::<($($component,)*)>() { - fn_call!(self, ($($commands, state_commands)*), ($($resource),*), ($($component),*)) - } - } - } - } - }, - thread_local_func: move |world, resources, state| { - state.commands.apply(world, resources); - }, - init_func: move |world, resources, state| { - <($($resource,)*)>::initialize(resources, Some(id)); - state.commands.set_entity_reserver(world.get_entity_reserver()) - }, - resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(), - archetype_component_access: TypeAccess::default(), - update_func: |world, archetype_component_access, state| { - archetype_component_access.clear(); - state.query_access.get_world_archetype_access(world, Some(archetype_component_access)); - }, - }) - } - } - }; -} - -struct QuerySystemState { - query_accesses: Vec>, - query_type_names: Vec<&'static str>, - archetype_component_accesses: Vec>, - commands: Commands, -} - -/// Converts `Self` into a Query System -pub trait IntoQuerySystem { - fn system(self) -> Box; -} - -macro_rules! impl_into_query_system { - (($($commands: ident)*), ($($resource: ident),*), ($($query: ident),*), ($($query_set: ident),*)) => { - impl IntoQuerySystem<($($commands,)*), ($($resource,)*), ($($query,)*), ($($query_set,)*)> for Func where - Func: - FnMut($($commands,)* $($resource,)* $(Query<$query>,)* $(QuerySet<$query_set>,)*) + - FnMut( - $($commands,)* - $(<<$resource as ResourceQuery>::Fetch as FetchResource>::Item,)* - $(Query<$query>,)* - $(QuerySet<$query_set>,)* - ) + - Send + Sync +'static, - $($query: HecsQuery,)* - $($query_set: QueryTuple,)* - $($resource: ResourceQuery,)* - { #[allow(non_snake_case)] - #[allow(unused_variables)] - #[allow(unused_unsafe)] - #[allow(unused_assignments)] - #[allow(unused_mut)] fn system(mut self) -> Box { - let id = SystemId::new(); - let query_accesses = vec![ - $(vec![<$query::Fetch as Fetch>::access()],)* - $($query_set::get_accesses(),)* - ]; - let query_type_names = vec![ - $(std::any::type_name::<$query>(),)* - $(std::any::type_name::<$query_set>(),)* - ]; - let archetype_component_accesses = vec![TypeAccess::default(); query_accesses.len()]; - Box::new(SystemFn { - state: QuerySystemState { - query_accesses, - query_type_names, - archetype_component_accesses, + Box::new(FuncSystem { + state: SystemState { + name: std::any::type_name::().into(), + archetype_component_access: TypeAccess::default(), + resource_access: TypeAccess::default(), + is_initialized: false, + id: SystemId::new(), commands: Commands::default(), + arc_commands: Default::default(), + query_archetype_component_accesses: Vec::new(), + query_accesses: Vec::new(), + query_type_names: Vec::new(), + current_query_index: 0, }, - thread_local_execution: ThreadLocalExecution::NextFlush, - id, - name: core::any::type_name::().into(), - func: move |world, resources, state| { - { - if let Some(($($resource,)*)) = resources.query_system::<($($resource,)*)>(id) { - let mut i = 0; - $( - let $query = Query::<$query>::new( - world, - &state.archetype_component_accesses[i] - ); - i += 1; - )* - $( - let $query_set = QuerySet::<$query_set>::new( - world, - &state.archetype_component_accesses[i] - ); - i += 1; - )* - - let commands = &state.commands; - fn_call!(self, ($($commands, commands)*), ($($resource),*), ($($query),*), ($($query_set),*)) + func: move |state, world, resources| { + state.reset_indices(); + unsafe { + if let Some(($($param,)*)) = <($($param,)*)>::get_param(state, world, resources) { + self($($param),*); } } }, - thread_local_func: move |world, resources, state| { + thread_local_func: |state, world, resources| { state.commands.apply(world, resources); - }, - init_func: move |world, resources, state| { - <($($resource,)*)>::initialize(resources, Some(id)); - state.commands.set_entity_reserver(world.get_entity_reserver()) - - }, - resource_access: <<($($resource,)*) as ResourceQuery>::Fetch as FetchResource>::access(), - archetype_component_access: TypeAccess::default(), - update_func: |world, archetype_component_access, state| { - archetype_component_access.clear(); - let mut conflict_index = None; - let mut conflict_name = None; - for (i, (query_accesses, component_access)) in state.query_accesses.iter().zip(state.archetype_component_accesses.iter_mut()).enumerate() { - component_access.clear(); - for query_access in query_accesses.iter() { - query_access.get_world_archetype_access(world, Some(component_access)); - } - if !component_access.is_compatible(archetype_component_access) { - conflict_index = Some(i); - conflict_name = component_access.get_conflict(archetype_component_access).and_then(|archetype_component| - query_accesses - .iter() - .filter_map(|query_access| query_access.get_type_name(archetype_component.component)) - .next()); - break; - } - archetype_component_access.union(component_access); - } - if let Some(conflict_index) = conflict_index { - let mut conflicts_with_index = None; - for prior_index in 0..conflict_index { - if !state.archetype_component_accesses[conflict_index].is_compatible(&state.archetype_component_accesses[prior_index]) { - conflicts_with_index = Some(prior_index); - } - } - panic!("System {} has conflicting queries. {} conflicts with the component access [{}] in this prior query: {}", - core::any::type_name::(), - state.query_type_names[conflict_index], - conflict_name.unwrap_or("Unknown"), - conflicts_with_index.map(|index| state.query_type_names[index]).unwrap_or("Unknown")); + if let Some(ref commands) = state.arc_commands { + let mut commands = commands.lock(); + commands.apply(world, resources); } }, + init_func: |state, world, resources| { + $($param::init(state, world, resources);)* + }, }) } } - }; -} -macro_rules! fn_call { - ($self:ident, ($($commands: ident, $commands_var: ident)*), ($($resource: ident),*), ($($a: ident),*), ($($b: ident),*)) => { - unsafe { $self($($commands_var.clone(),)* $($resource.unsafe_clone(),)* $($a,)* $($b,)*) } - }; - ($self:ident, ($($commands: ident, $commands_var: ident)*), ($($resource: ident),*), ($($a: ident),*)) => { - unsafe { $self($($commands_var.clone(),)* $($resource.unsafe_clone(),)* $($a,)*) } - }; - ($self:ident, (), ($($resource: ident),*), ($($a: ident),*)) => { - unsafe { $self($($resource.unsafe_clone(),)* $($a,)*) } }; } -macro_rules! impl_into_query_systems { - (($($resource: ident,)*), ($($query: ident),*)) => { - #[rustfmt::skip] - impl_into_query_system!((), ($($resource),*), ($($query),*), ()); - #[rustfmt::skip] - impl_into_query_system!((), ($($resource),*), ($($query),*), (QS1)); - #[rustfmt::skip] - impl_into_query_system!((), ($($resource),*), ($($query),*), (QS1, QS2)); - - #[rustfmt::skip] - impl_into_query_system!((Commands), ($($resource),*), ($($query),*), ()); - #[rustfmt::skip] - impl_into_query_system!((Commands), ($($resource),*), ($($query),*), (QS1)); - #[rustfmt::skip] - impl_into_query_system!((Commands), ($($resource),*), ($($query),*), (QS1, QS2)); - } -} - -macro_rules! impl_into_foreach_systems { - (($($resource: ident,)*), ($($component: ident),*)) => { - #[rustfmt::skip] - impl_into_foreach_system!((), ($($resource),*), ($($component),*)); - #[rustfmt::skip] - impl_into_foreach_system!((Commands), ($($resource),*), ($($component),*)); - } -} - -macro_rules! impl_into_systems { - ($($resource: ident),*) => { - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E,F)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E,F,G)); - #[rustfmt::skip] - impl_into_foreach_systems!(($($resource,)*), (A,B,C,D,E,F,G,H)); - - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), ()); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C,D)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C,D,E)); - #[rustfmt::skip] - impl_into_query_systems!(($($resource,)*), (A,B,C,D,E,F)); - }; -} - -#[rustfmt::skip] -impl_into_systems!(); -#[rustfmt::skip] -impl_into_systems!(Ra); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh,Ri); -#[rustfmt::skip] -impl_into_systems!(Ra,Rb,Rc,Rd,Re,Rf,Rg,Rh,Ri,Rj); - -/// Converts `Self` into a thread local system -pub trait IntoThreadLocalSystem { - fn thread_local_system(self) -> Box; -} - -impl IntoThreadLocalSystem for F -where - F: ThreadLocalSystemFn, -{ - fn thread_local_system(mut self) -> Box { - Box::new(SystemFn { - state: (), - thread_local_func: move |world, resources, _| { - self.run(world, resources); - }, - func: |_, _, _| {}, - init_func: |_, _, _| {}, - update_func: |_, _, _| {}, - thread_local_execution: ThreadLocalExecution::Immediate, - name: core::any::type_name::().into(), - id: SystemId::new(), - resource_access: TypeAccess::default(), - archetype_component_access: TypeAccess::default(), - }) - } -} - -/// A thread local system function -pub trait ThreadLocalSystemFn: Send + Sync + 'static { - fn run(&mut self, world: &mut World, resource: &mut Resources); -} - -impl ThreadLocalSystemFn for F -where - F: FnMut(&mut World, &mut Resources) + Send + Sync + 'static, -{ - fn run(&mut self, world: &mut World, resources: &mut Resources) { - self(world, resources); - } -} +impl_into_system!(); +impl_into_system!(A); +impl_into_system!(A, B); +impl_into_system!(A, B, C); +impl_into_system!(A, B, C, D); +impl_into_system!(A, B, C, D, E); +impl_into_system!(A, B, C, D, E, F); +impl_into_system!(A, B, C, D, E, F, G); +impl_into_system!(A, B, C, D, E, F, G, H); +impl_into_system!(A, B, C, D, E, F, G, H, I); +impl_into_system!(A, B, C, D, E, F, G, H, I, J); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); #[cfg(test)] mod tests { - use super::{IntoForEachSystem, IntoQuerySystem, Query}; + use super::IntoSystem; use crate::{ resource::{ResMut, Resources}, schedule::Schedule, - ChangedRes, Mut, QuerySet, + ChangedRes, Query, QuerySet, System, }; - use bevy_hecs::{Entity, With, World}; + use bevy_hecs::{Entity, Or, With, World}; #[derive(Debug, Eq, PartialEq)] struct A; @@ -480,11 +270,7 @@ mod tests { world.spawn((A, C)); world.spawn((A, D)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", query_system.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, query_system.system()); assert!(*resources.get::().unwrap(), "system ran"); } @@ -493,14 +279,12 @@ mod tests { fn or_query_set_system() { // Regression test for issue #762 use crate::{Added, Changed, Mutated, Or}; - fn query_system( - mut ran: ResMut, - set: QuerySet<( - Query, Changed)>>, - Query, Added)>>, - Query, Mutated)>>, - )>, - ) { + let query_system = move |mut ran: ResMut, + set: QuerySet<( + Query, Changed)>>, + Query, Added)>>, + Query, Mutated)>>, + )>| { let changed = set.q0().iter().count(); let added = set.q1().iter().count(); let mutated = set.q2().iter().count(); @@ -510,36 +294,68 @@ mod tests { assert_eq!(mutated, 0); *ran = true; - } + }; let mut world = World::default(); let mut resources = Resources::default(); resources.insert(false); world.spawn((A, B)); + run_system(&mut world, &mut resources, query_system.system()); + + assert!(*resources.get::().unwrap(), "system ran"); + } + + #[test] + fn changed_resource_system() { + fn incr_e_on_flip(_run_on_flip: ChangedRes, mut query: Query<&mut i32>) { + for mut i in query.iter_mut() { + *i += 1; + } + } + + let mut world = World::default(); + let mut resources = Resources::default(); + resources.insert(false); + let ent = world.spawn((0,)); + let mut schedule = Schedule::default(); schedule.add_stage("update"); - schedule.add_system_to_stage("update", query_system.system()); + schedule.add_system_to_stage("update", incr_e_on_flip.system()); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 1); - assert!(*resources.get::().unwrap(), "system ran"); + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 1); + + *resources.get_mut::().unwrap() = true; + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 2); } #[test] - fn changed_resource_system() { - fn incr_e_on_flip(_run_on_flip: ChangedRes, mut i: Mut) { - *i += 1; + fn changed_resource_or_system() { + fn incr_e_on_flip( + _or: Or<(Option>, Option>)>, + mut query: Query<&mut i32>, + ) { + for mut i in query.iter_mut() { + *i += 1; + } } let mut world = World::default(); let mut resources = Resources::default(); resources.insert(false); + resources.insert::(10); let ent = world.spawn((0,)); let mut schedule = Schedule::default(); schedule.add_stage("update"); schedule.add_system_to_stage("update", incr_e_on_flip.system()); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); assert_eq!(*(world.get::(ent).unwrap()), 1); @@ -550,6 +366,13 @@ mod tests { *resources.get_mut::().unwrap() = true; schedule.run(&mut world, &mut resources); assert_eq!(*(world.get::(ent).unwrap()), 2); + + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 2); + + *resources.get_mut::().unwrap() = 20; + schedule.run(&mut world, &mut resources); + assert_eq!(*(world.get::(ent).unwrap()), 3); } #[test] @@ -561,11 +384,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -577,11 +396,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -592,11 +407,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -608,11 +419,7 @@ mod tests { let mut resources = Resources::default(); world.spawn((A,)); - let mut schedule = Schedule::default(); - schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); - - schedule.run(&mut world, &mut resources); + run_system(&mut world, &mut resources, sys.system()); } #[test] @@ -623,11 +430,15 @@ mod tests { let mut world = World::default(); let mut resources = Resources::default(); world.spawn((A,)); + run_system(&mut world, &mut resources, sys.system()); + } + fn run_system(world: &mut World, resources: &mut Resources, system: Box) { let mut schedule = Schedule::default(); schedule.add_stage("update"); - schedule.add_system_to_stage("update", sys.system()); + schedule.add_system_to_stage("update", system); - schedule.run(&mut world, &mut resources); + schedule.initialize(world, resources); + schedule.run(world, resources); } } diff --git a/crates/bevy_ecs/src/system/into_thread_local.rs b/crates/bevy_ecs/src/system/into_thread_local.rs new file mode 100644 index 0000000000000..f029af89b3ef0 --- /dev/null +++ b/crates/bevy_ecs/src/system/into_thread_local.rs @@ -0,0 +1,79 @@ +pub use super::Query; +use crate::{ + resource::Resources, + system::{System, SystemId, ThreadLocalExecution}, + TypeAccess, +}; +use bevy_hecs::{ArchetypeComponent, World}; +use std::{any::TypeId, borrow::Cow}; + +#[derive(Debug)] +pub(crate) struct ThreadLocalSystemFn +where + Func: FnMut(&mut World, &mut Resources) + Send + Sync, +{ + pub func: Func, + pub resource_access: TypeAccess, + pub archetype_component_access: TypeAccess, + pub name: Cow<'static, str>, + pub id: SystemId, +} + +impl System for ThreadLocalSystemFn +where + Func: FnMut(&mut World, &mut Resources) + Send + Sync, +{ + fn name(&self) -> Cow<'static, str> { + self.name.clone() + } + + fn update(&mut self, _world: &World) {} + + fn archetype_component_access(&self) -> &TypeAccess { + &self.archetype_component_access + } + + fn resource_access(&self) -> &TypeAccess { + &self.resource_access + } + + fn thread_local_execution(&self) -> ThreadLocalExecution { + ThreadLocalExecution::Immediate + } + + fn run(&mut self, _world: &World, _resources: &Resources) {} + + fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) { + (self.func)(world, resources); + } + + fn initialize(&mut self, _world: &mut World, _resources: &mut Resources) {} + + fn id(&self) -> SystemId { + self.id + } + + fn is_initialized(&self) -> bool { + true + } +} + +/// Converts `Self` into a thread local system +pub trait IntoThreadLocalSystem { + fn thread_local_system(self) -> Box; +} + +impl IntoThreadLocalSystem for F +where + F: FnMut(&mut World, &mut Resources) + Send + Sync + 'static, +{ + fn thread_local_system(mut self) -> Box { + Box::new(ThreadLocalSystemFn { + func: move |world, resources| (self)(world, resources), + name: core::any::type_name::().into(), + id: SystemId::new(), + resource_access: TypeAccess::default(), + archetype_component_access: TypeAccess::default(), + }) + } +} diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index ef5e9f20436fd..a1c7b001c49f6 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1,14 +1,18 @@ mod commands; mod into_system; +mod into_thread_local; #[cfg(feature = "profiler")] mod profiler; mod query; #[allow(clippy::module_inception)] mod system; +mod system_param; pub use commands::*; pub use into_system::*; +pub use into_thread_local::*; #[cfg(feature = "profiler")] pub use profiler::*; pub use query::*; pub use system::*; +pub use system_param::*; diff --git a/crates/bevy_ecs/src/system/query/mod.rs b/crates/bevy_ecs/src/system/query/mod.rs index f7494ad3d8e64..e8ec4c4f48520 100644 --- a/crates/bevy_ecs/src/system/query/mod.rs +++ b/crates/bevy_ecs/src/system/query/mod.rs @@ -27,8 +27,14 @@ pub enum QueryError { } impl<'a, Q: HecsQuery> Query<'a, Q> { + /// # Safety + /// This will create a Query that could violate memory safety rules. Make sure that this is only called in + /// ways that ensure the Queries have unique mutable access. #[inline] - pub fn new(world: &'a World, component_access: &'a TypeAccess) -> Self { + pub unsafe fn new( + world: &'a World, + component_access: &'a TypeAccess, + ) -> Self { Self { world, component_access, @@ -37,6 +43,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Iterates over the query results. This can only be called for read-only queries + #[inline] pub fn iter(&self) -> QueryIter<'_, Q> where Q::Fetch: ReadOnlyFetch, @@ -46,6 +53,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Iterates over the query results + #[inline] pub fn iter_mut(&mut self) -> QueryIter<'_, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { self.world.query_unchecked() } @@ -54,6 +62,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { /// Iterates over the query results /// # Safety /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component + #[inline] pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, Q> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict self.world.query_unchecked() @@ -75,6 +84,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Gets the query result for the given `entity` + #[inline] pub fn get(&self, entity: Entity) -> Result<::Item, QueryError> where Q::Fetch: ReadOnlyFetch, @@ -88,6 +98,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { } /// Gets the query result for the given `entity` + #[inline] pub fn get_mut(&mut self, entity: Entity) -> Result<::Item, QueryError> { // SAFE: system runs without conflicts with other systems. same-system queries have runtime borrow checks when they conflict unsafe { @@ -100,6 +111,7 @@ impl<'a, Q: HecsQuery> Query<'a, Q> { /// Gets the query result for the given `entity` /// # Safety /// This allows aliased mutability. You must make sure this call does not result in multiple mutable references to the same component + #[inline] pub unsafe fn get_unsafe( &self, entity: Entity, diff --git a/crates/bevy_ecs/src/system/query/query_set.rs b/crates/bevy_ecs/src/system/query/query_set.rs index 616ad04055bc4..0380781dc7a61 100644 --- a/crates/bevy_ecs/src/system/query/query_set.rs +++ b/crates/bevy_ecs/src/system/query/query_set.rs @@ -17,9 +17,12 @@ pub trait QueryTuple { } impl QuerySet { - pub fn new(world: &World, component_access: &TypeAccess) -> Self { + /// # Safety + /// This will create a set of Query types that could violate memory safety rules. Make sure that this is only called in + /// ways that ensure the Queries have unique mutable access. + pub unsafe fn new(world: &World, component_access: &TypeAccess) -> Self { QuerySet { - value: unsafe { T::new(world, component_access) }, + value: T::new(world, component_access), } } } diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 75d4cc370a1d4..d2f1600df40f4 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -23,6 +23,7 @@ impl SystemId { pub trait System: Send + Sync { fn name(&self) -> Cow<'static, str>; fn id(&self) -> SystemId; + fn is_initialized(&self) -> bool; fn update(&mut self, world: &World); fn archetype_component_access(&self) -> &TypeAccess; fn resource_access(&self) -> &TypeAccess; diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs new file mode 100644 index 0000000000000..8cbdba8ea1091 --- /dev/null +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -0,0 +1,255 @@ +pub use bevy_hecs::SystemParam; + +use crate::{ + ChangedRes, Commands, FromResources, Local, Query, QuerySet, QueryTuple, Res, ResMut, Resource, + ResourceIndex, Resources, SystemState, +}; +use bevy_hecs::{ArchetypeComponent, Fetch, Or, Query as HecsQuery, TypeAccess, World}; +use parking_lot::Mutex; +use std::{any::TypeId, sync::Arc}; + +pub trait SystemParam: Sized { + fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources); + /// # Safety + /// This call might access any of the input parameters in an unsafe way. Make sure the data access is safe in + /// the context of the system scheduler + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + resources: &Resources, + ) -> Option; +} + +impl<'a, Q: HecsQuery> SystemParam for Query<'a, Q> { + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + _resources: &Resources, + ) -> Option { + let query_index = system_state.current_query_index; + let world: &'a World = std::mem::transmute(world); + let archetype_component_access: &'a TypeAccess = + std::mem::transmute(&system_state.query_archetype_component_accesses[query_index]); + system_state.current_query_index += 1; + Some(Query::new(world, archetype_component_access)) + } + + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + system_state + .query_accesses + .push(vec![::access()]); + system_state + .query_type_names + .push(std::any::type_name::()); + } +} + +impl SystemParam for QuerySet { + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + _resources: &Resources, + ) -> Option { + let query_index = system_state.current_query_index; + system_state.current_query_index += 1; + Some(QuerySet::new( + world, + &system_state.query_archetype_component_accesses[query_index], + )) + } + + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + system_state.query_accesses.push(T::get_accesses()); + system_state + .query_type_names + .push(std::any::type_name::()); + } +} + +impl<'a> SystemParam for &'a mut Commands { + fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { + system_state + .commands + .set_entity_reserver(world.get_entity_reserver()) + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + _world: &World, + _resources: &Resources, + ) -> Option { + let commands: &'a mut Commands = std::mem::transmute(&mut system_state.commands); + Some(commands) + } +} + +impl SystemParam for Arc> { + fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { + system_state.arc_commands.get_or_insert_with(|| { + let mut commands = Commands::default(); + commands.set_entity_reserver(world.get_entity_reserver()); + Arc::new(Mutex::new(commands)) + }); + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + _world: &World, + _resources: &Resources, + ) -> Option { + Some(system_state.arc_commands.as_ref().unwrap().clone()) + } +} + +impl<'a, T: Resource> SystemParam for Res<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state.resource_access.add_read(TypeId::of::()); + } + + #[inline] + unsafe fn get_param( + _system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Option { + Some(Res::new( + resources.get_unsafe_ref::(ResourceIndex::Global), + )) + } +} + +impl<'a, T: Resource> SystemParam for ResMut<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state.resource_access.add_write(TypeId::of::()); + } + + #[inline] + unsafe fn get_param( + _system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Option { + let (value, type_state) = + resources.get_unsafe_ref_with_type_state::(ResourceIndex::Global); + Some(ResMut::new(value, type_state.mutated())) + } +} + +impl<'a, T: Resource> SystemParam for ChangedRes<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state.resource_access.add_read(TypeId::of::()); + } + + #[inline] + unsafe fn get_param( + _system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Option { + let (added, mutated) = resources.get_unsafe_added_and_mutated::(ResourceIndex::Global); + if *added.as_ptr() || *mutated.as_ptr() { + Some(ChangedRes::new( + resources.get_unsafe_ref::(ResourceIndex::Global), + )) + } else { + None + } + } +} + +impl<'a, T: Resource + FromResources> SystemParam for Local<'a, T> { + fn init(system_state: &mut SystemState, _world: &World, resources: &mut Resources) { + system_state.resource_access.add_write(TypeId::of::()); + if resources.get_local::(system_state.id).is_none() { + let value = T::from_resources(resources); + resources.insert_local(system_state.id, value); + } + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + _world: &World, + resources: &Resources, + ) -> Option { + Some(Local::new(resources, system_state.id)) + } +} + +macro_rules! impl_system_param_tuple { + ($($param: ident),*) => { + #[allow(unused_variables)] + impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { + fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) { + $($param::init(system_state, world, resources);)* + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + resources: &Resources, + ) -> Option { + Some(($($param::get_param(system_state, world, resources)?,)*)) + } + } + + #[allow(unused_variables)] + #[allow(unused_mut)] + #[allow(non_snake_case)] + impl<$($param: SystemParam),*> SystemParam for Or<($(Option<$param>,)*)> { + fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) { + $($param::init(system_state, world, resources);)* + } + + #[inline] + unsafe fn get_param( + system_state: &mut SystemState, + world: &World, + resources: &Resources, + ) -> Option { + let mut has_some = false; + $( + let $param = $param::get_param(system_state, world, resources); + if $param.is_some() { + has_some = true; + } + )* + + if has_some { + Some(Or(($($param,)*))) + } else { + None + } + } + } + }; +} + +impl_system_param_tuple!(); +impl_system_param_tuple!(A); +impl_system_param_tuple!(A, B); +impl_system_param_tuple!(A, B, C); +impl_system_param_tuple!(A, B, C, D); +impl_system_param_tuple!(A, B, C, D, E); +impl_system_param_tuple!(A, B, C, D, E, F); +impl_system_param_tuple!(A, B, C, D, E, F, G); +impl_system_param_tuple!(A, B, C, D, E, F, G, H); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 92c884eb9a2b8..b3ced9c404fab 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -28,7 +28,7 @@ use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotio use touch::{touch_screen_input_system, TouchInput, Touches}; use bevy_app::startup_stage::STARTUP; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use gamepad::{ gamepad_event_system, GamepadAxis, GamepadButton, GamepadEvent, GamepadEventRaw, GamepadSettings, diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index 3feb8cc6cdd40..8270b46859558 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -14,7 +14,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_asset::{AddAsset, Assets, Handle}; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_render::{prelude::Color, render_graph::RenderGraph, shader}; use bevy_type_registry::RegisterType; use light::Light; diff --git a/crates/bevy_pbr/src/render_graph/lights_node.rs b/crates/bevy_pbr/src/render_graph/lights_node.rs index ffc573394d5fe..fc2295d5c1276 100644 --- a/crates/bevy_pbr/src/render_graph/lights_node.rs +++ b/crates/bevy_pbr/src/render_graph/lights_node.rs @@ -3,7 +3,7 @@ use crate::{ render_graph::uniform, }; use bevy_core::{AsBytes, Byteable}; -use bevy_ecs::{Commands, IntoQuerySystem, Local, Query, Res, ResMut, Resources, System, World}; +use bevy_ecs::{Commands, IntoSystem, Local, Query, Res, ResMut, Resources, System, World}; use bevy_render::{ render_graph::{CommandQueue, Node, ResourceSlots, SystemNode}, renderer::{ diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index 99a9f710bd71b..b61e8d7b49ec1 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -10,12 +10,9 @@ use crate::{ shader::Shader, }; use bevy_asset::{Assets, Handle}; -use bevy_ecs::{ - FetchResource, Query, Res, ResMut, ResourceIndex, ResourceQuery, Resources, SystemId, - TypeAccess, UnsafeClone, -}; +use bevy_ecs::{Query, Res, ResMut, SystemParam}; use bevy_property::Properties; -use std::{any::TypeId, ops::Range, sync::Arc}; +use std::{ops::Range, sync::Arc}; use thiserror::Error; /// A queued command for the renderer @@ -125,100 +122,20 @@ pub enum DrawError { BufferAllocationFailure, } -//#[derive(Debug)] +#[derive(SystemParam)] pub struct DrawContext<'a> { pub pipelines: ResMut<'a, Assets>, pub shaders: ResMut<'a, Assets>, pub pipeline_compiler: ResMut<'a, PipelineCompiler>, pub render_resource_context: Res<'a, Box>, pub shared_buffers: Res<'a, SharedBuffers>, + #[system_param(ignore)] pub current_pipeline: Option>, } -impl<'a> UnsafeClone for DrawContext<'a> { - unsafe fn unsafe_clone(&self) -> Self { - Self { - pipelines: self.pipelines.unsafe_clone(), - shaders: self.shaders.unsafe_clone(), - pipeline_compiler: self.pipeline_compiler.unsafe_clone(), - render_resource_context: self.render_resource_context.unsafe_clone(), - shared_buffers: self.shared_buffers.unsafe_clone(), - current_pipeline: self.current_pipeline.clone(), - } - } -} - -impl<'a> ResourceQuery for DrawContext<'a> { - type Fetch = FetchDrawContext; -} - #[derive(Debug)] pub struct FetchDrawContext; -// TODO: derive this impl -impl<'a> FetchResource<'a> for FetchDrawContext { - type Item = DrawContext<'a>; - - fn borrow(resources: &Resources) { - resources.borrow_mut::>(); - resources.borrow_mut::>(); - resources.borrow_mut::(); - resources.borrow::>(); - resources.borrow::(); - } - - fn release(resources: &Resources) { - resources.release_mut::>(); - resources.release_mut::>(); - resources.release_mut::(); - resources.release::>(); - resources.release::(); - } - - unsafe fn get(resources: &'a Resources, _system_id: Option) -> Self::Item { - let pipelines = { - let (value, type_state) = resources - .get_unsafe_ref_with_type_state::>( - ResourceIndex::Global, - ); - ResMut::new(value, type_state.mutated()) - }; - let shaders = { - let (value, type_state) = - resources.get_unsafe_ref_with_type_state::>(ResourceIndex::Global); - ResMut::new(value, type_state.mutated()) - }; - let pipeline_compiler = { - let (value, type_state) = - resources.get_unsafe_ref_with_type_state::(ResourceIndex::Global); - ResMut::new(value, type_state.mutated()) - }; - - DrawContext { - pipelines, - shaders, - pipeline_compiler, - render_resource_context: Res::new( - resources.get_unsafe_ref::>(ResourceIndex::Global), - ), - shared_buffers: Res::new( - resources.get_unsafe_ref::(ResourceIndex::Global), - ), - current_pipeline: None, - } - } - - fn access() -> TypeAccess { - let mut access = TypeAccess::default(); - access.add_write(TypeId::of::>()); - access.add_write(TypeId::of::>()); - access.add_write(TypeId::of::()); - access.add_read(TypeId::of::>()); - access.add_read(TypeId::of::()); - access - } -} - impl<'a> DrawContext<'a> { pub fn get_uniform_buffer( &self, diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index c96d6416a77fa..4983bb991cea2 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -32,7 +32,7 @@ use crate::prelude::*; use base::{MainPass, Msaa}; use bevy_app::prelude::*; use bevy_asset::AddAsset; -use bevy_ecs::{IntoQuerySystem, IntoThreadLocalSystem}; +use bevy_ecs::{IntoSystem, IntoThreadLocalSystem}; use camera::{ ActiveCameras, Camera, OrthographicProjection, PerspectiveProjection, VisibleEntities, }; diff --git a/crates/bevy_render/src/render_graph/nodes/camera_node.rs b/crates/bevy_render/src/render_graph/nodes/camera_node.rs index c9af7fe0e93cf..bdc5217c75de8 100644 --- a/crates/bevy_render/src/render_graph/nodes/camera_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/camera_node.rs @@ -8,7 +8,7 @@ use crate::{ }; use bevy_core::AsBytes; -use bevy_ecs::{Commands, IntoQuerySystem, Local, Query, Res, ResMut, Resources, System, World}; +use bevy_ecs::{Commands, IntoSystem, Local, Query, Res, ResMut, Resources, System, World}; use bevy_transform::prelude::*; use std::borrow::Cow; diff --git a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs index 9fe24487484db..f577c3806be6d 100644 --- a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs @@ -10,9 +10,7 @@ use crate::{ }; use bevy_asset::{Asset, Assets, Handle, HandleId}; -use bevy_ecs::{ - Commands, Entity, IntoQuerySystem, Local, Query, Res, ResMut, Resources, System, World, -}; +use bevy_ecs::{Commands, Entity, IntoSystem, Local, Query, Res, ResMut, Resources, System, World}; use bevy_utils::HashMap; use renderer::{AssetRenderResourceBindings, BufferId, RenderResourceType, RenderResources}; use std::{hash::Hash, marker::PhantomData, ops::DerefMut}; diff --git a/crates/bevy_render/src/render_graph/system.rs b/crates/bevy_render/src/render_graph/system.rs index 330be0a1f9baa..875c33298c653 100644 --- a/crates/bevy_render/src/render_graph/system.rs +++ b/crates/bevy_render/src/render_graph/system.rs @@ -3,13 +3,18 @@ use bevy_ecs::{Resources, World}; pub fn render_graph_schedule_executor_system(world: &mut World, resources: &mut Resources) { // run render graph systems - let (mut system_schedule, commands) = { + let (mut system_schedule, mut commands) = { let mut render_graph = resources.get_mut::().unwrap(); (render_graph.take_schedule(), render_graph.take_commands()) }; commands.apply(world, resources); if let Some(schedule) = system_schedule.as_mut() { + schedule.run_on_systems(|system| { + if !system.is_initialized() { + system.initialize(world, resources); + } + }); schedule.run(world, resources); } let mut render_graph = resources.get_mut::().unwrap(); diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index cea0da2285d48..d7107e2e2415f 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -26,7 +26,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_asset::{AddAsset, Assets, Handle}; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_math::Vec2; use bevy_render::{ mesh::{shape, Mesh}, diff --git a/crates/bevy_transform/src/hierarchy/child_builder.rs b/crates/bevy_transform/src/hierarchy/child_builder.rs index 74bff7c2e1f3e..b42772cfb8bc7 100644 --- a/crates/bevy_transform/src/hierarchy/child_builder.rs +++ b/crates/bevy_transform/src/hierarchy/child_builder.rs @@ -1,7 +1,5 @@ use crate::prelude::{Children, Parent, PreviousParent}; -use bevy_ecs::{ - Command, Commands, CommandsInternal, Component, DynamicBundle, Entity, Resources, World, -}; +use bevy_ecs::{Command, Commands, Component, DynamicBundle, Entity, Resources, World}; use smallvec::SmallVec; #[derive(Debug)] @@ -42,7 +40,7 @@ pub struct PushChildren { } pub struct ChildBuilder<'a> { - commands: &'a mut CommandsInternal, + commands: &'a mut Commands, push_children: PushChildren, } @@ -75,12 +73,12 @@ impl<'a> ChildBuilder<'a> { self.commands.spawn(components); self.push_children .children - .push(self.commands.current_entity.unwrap()); + .push(self.commands.current_entity().unwrap()); self } pub fn current_entity(&self) -> Option { - self.commands.current_entity + self.commands.current_entity() } pub fn with_bundle( @@ -99,7 +97,7 @@ impl<'a> ChildBuilder<'a> { pub fn for_current_entity(&mut self, func: impl FnOnce(Entity)) -> &mut Self { let current_entity = self .commands - .current_entity + .current_entity() .expect("The 'current entity' is not set. You should spawn an entity first."); func(current_entity); self @@ -114,56 +112,47 @@ pub trait BuildChildren { impl BuildChildren for Commands { fn with_children(&mut self, parent: impl FnOnce(&mut ChildBuilder)) -> &mut Self { - { - let mut commands = self.commands.lock(); - let current_entity = commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); - commands.current_entity = None; - let push_children = { - let mut builder = ChildBuilder { - commands: &mut commands, - push_children: PushChildren { - children: SmallVec::default(), - parent: current_entity, - }, - }; - parent(&mut builder); - builder.push_children + let current_entity = self.current_entity().expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); + self.clear_current_entity(); + let push_children = { + let mut builder = ChildBuilder { + commands: self, + push_children: PushChildren { + children: SmallVec::default(), + parent: current_entity, + }, }; + parent(&mut builder); + builder.push_children + }; - commands.current_entity = Some(current_entity); - commands.add_command(push_children); - } + self.set_current_entity(current_entity); + self.add_command(push_children); self } fn push_children(&mut self, parent: Entity, children: &[Entity]) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.add_command(PushChildren { - children: SmallVec::from(children), - parent, - }); - } + self.add_command(PushChildren { + children: SmallVec::from(children), + parent, + }); self } fn insert_children(&mut self, parent: Entity, index: usize, children: &[Entity]) -> &mut Self { - { - let mut commands = self.commands.lock(); - commands.add_command(InsertChildren { - children: SmallVec::from(children), - index, - parent, - }); - } + self.add_command(InsertChildren { + children: SmallVec::from(children), + index, + parent, + }); self } } impl<'a> BuildChildren for ChildBuilder<'a> { fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self { - let current_entity = self.commands.current_entity.expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); - self.commands.current_entity = None; + let current_entity = self.commands.current_entity().expect("Cannot add children because the 'current entity' is not set. You should spawn an entity first."); + self.commands.clear_current_entity(); let push_children = { let mut builder = ChildBuilder { commands: self.commands, @@ -177,7 +166,7 @@ impl<'a> BuildChildren for ChildBuilder<'a> { builder.push_children }; - self.commands.current_entity = Some(current_entity); + self.commands.set_current_entity(current_entity); self.commands.add_command(push_children); self } diff --git a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs index 9663bea17b15d..b7e4c069d7917 100644 --- a/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs +++ b/crates/bevy_transform/src/hierarchy/hierarchy_maintenance_system.rs @@ -1,10 +1,10 @@ use crate::components::*; -use bevy_ecs::{Commands, Entity, IntoQuerySystem, Query, System, Without}; +use bevy_ecs::{Commands, Entity, IntoSystem, Query, System, Without}; use bevy_utils::HashMap; use smallvec::SmallVec; pub fn parent_update_system( - mut commands: Commands, + commands: &mut Commands, removed_parent_query: Query>, // TODO: ideally this only runs when the Parent component has changed mut changed_parent_query: Query<(Entity, &Parent, Option<&mut PreviousParent>)>, @@ -122,6 +122,7 @@ mod test { }); let parent = parent.unwrap(); commands.apply(&mut world, &mut resources); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); assert_eq!( diff --git a/crates/bevy_transform/src/transform_propagate_system.rs b/crates/bevy_transform/src/transform_propagate_system.rs index 287aa9a7a870f..4727b24528f0d 100644 --- a/crates/bevy_transform/src/transform_propagate_system.rs +++ b/crates/bevy_transform/src/transform_propagate_system.rs @@ -88,10 +88,10 @@ mod test { ), ]) .collect::>(); - // we need to run the schedule three times because components need to be filled in + // we need to run the schedule two times because components need to be filled in // to resolve this problem in code, just add the correct components, or use Commands // which adds all of the components needed with the correct state (see next test) - schedule.run(&mut world, &mut resources); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); schedule.run(&mut world, &mut resources); @@ -142,6 +142,7 @@ mod test { .for_current_entity(|entity| children.push(entity)); }); commands.apply(&mut world, &mut resources); + schedule.initialize(&mut world, &mut resources); schedule.run(&mut world, &mut resources); assert_eq!( diff --git a/crates/bevy_transform/src/transform_system_bundle.rs b/crates/bevy_transform/src/transform_system_bundle.rs index cf76625feeb0b..ec9a3f70090b9 100644 --- a/crates/bevy_transform/src/transform_system_bundle.rs +++ b/crates/bevy_transform/src/transform_system_bundle.rs @@ -3,7 +3,7 @@ use crate::{ transform_propagate_system::transform_propagate_system, transform_systems, }; -use bevy_ecs::{IntoQuerySystem, System}; +use bevy_ecs::{IntoSystem, System}; use hierarchy_maintenance_system::hierarchy_maintenance_systems; use transform_systems::transform_systems; diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 56ccaeb31a772..21b9c4d80a954 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -25,7 +25,7 @@ pub mod prelude { } use bevy_app::prelude::*; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; use bevy_render::render_graph::RenderGraph; use update::ui_z_system; diff --git a/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs b/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs index 0da3d6d1d9a36..079332c93cf89 100644 --- a/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs +++ b/crates/bevy_wgpu/src/diagnostic/wgpu_resource_diagnostics_plugin.rs @@ -1,7 +1,7 @@ use crate::renderer::WgpuRenderResourceContext; use bevy_app::prelude::*; use bevy_diagnostic::{Diagnostic, DiagnosticId, Diagnostics}; -use bevy_ecs::{IntoQuerySystem, Res, ResMut}; +use bevy_ecs::{IntoSystem, Res, ResMut}; use bevy_render::renderer::RenderResourceContext; #[derive(Default)] diff --git a/crates/bevy_wgpu/src/lib.rs b/crates/bevy_wgpu/src/lib.rs index 4c38dcdeda4ae..8cb2c7db69da6 100644 --- a/crates/bevy_wgpu/src/lib.rs +++ b/crates/bevy_wgpu/src/lib.rs @@ -11,7 +11,7 @@ pub use wgpu_renderer::*; pub use wgpu_resources::*; use bevy_app::prelude::*; -use bevy_ecs::{IntoQuerySystem, IntoThreadLocalSystem, Resources, World}; +use bevy_ecs::{IntoSystem, IntoThreadLocalSystem, Resources, World}; use bevy_render::renderer::{free_shared_buffers_system, RenderResourceContext, SharedBuffers}; use renderer::WgpuRenderResourceContext; diff --git a/crates/bevy_window/src/lib.rs b/crates/bevy_window/src/lib.rs index 41d61d5bf6e64..36f7a736ef10f 100644 --- a/crates/bevy_window/src/lib.rs +++ b/crates/bevy_window/src/lib.rs @@ -13,7 +13,7 @@ pub mod prelude { } use bevy_app::prelude::*; -use bevy_ecs::IntoQuerySystem; +use bevy_ecs::IntoSystem; pub struct WindowPlugin { pub add_primary_window: bool, diff --git a/examples/2d/contributors.rs b/examples/2d/contributors.rs index 12e3489abf4ea..a08a25f49f9ec 100644 --- a/examples/2d/contributors.rs +++ b/examples/2d/contributors.rs @@ -46,7 +46,7 @@ const COL_SELECTED: Color = Color::rgb_linear(5.0, 5.0, 5.0); const SHOWCASE_TIMER_SECS: f32 = 3.0; fn setup( - mut cmd: Commands, + commands: &mut Commands, asset_server: Res, mut materials: ResMut>, ) { @@ -54,7 +54,8 @@ fn setup( let texture_handle = asset_server.load("branding/icon.png"); - cmd.spawn(Camera2dComponents::default()) + commands + .spawn(Camera2dComponents::default()) .spawn(UiCameraComponents::default()); let mut sel = ContributorSelection { @@ -76,7 +77,8 @@ fn setup( let mut transform = Transform::from_translation(Vec3::new(pos.0, pos.1, 0.0)); *transform.scale.x_mut() *= if flipped { -1.0 } else { 1.0 }; - cmd.spawn((Contributor { color: col },)) + commands + .spawn((Contributor { color: col },)) .with(Velocity { translation: velocity, rotation: -dir * 5.0, @@ -94,16 +96,17 @@ fn setup( }) .with(transform); - let e = cmd.current_entity().unwrap(); + let e = commands.current_entity().unwrap(); sel.order.push((name, e)); } sel.order.shuffle(&mut rnd); - cmd.spawn((SelectTimer, Timer::from_seconds(SHOWCASE_TIMER_SECS, true))); + commands.spawn((SelectTimer, Timer::from_seconds(SHOWCASE_TIMER_SECS, true))); - cmd.spawn((ContributorDisplay,)) + commands + .spawn((ContributorDisplay,)) .with_bundle(TextComponents { style: Style { align_self: AlignSelf::FlexEnd, @@ -120,7 +123,7 @@ fn setup( ..Default::default() }); - cmd.insert_resource(sel); + commands.insert_resource(sel); } /// Finds the next contributor to display and selects the entity diff --git a/examples/2d/sprite.rs b/examples/2d/sprite.rs index b4cff1721d19b..3e12a7068fb13 100644 --- a/examples/2d/sprite.rs +++ b/examples/2d/sprite.rs @@ -8,7 +8,7 @@ fn main() { } fn setup( - mut commands: Commands, + commands: &mut Commands, asset_server: Res, mut materials: ResMut>, ) { diff --git a/examples/2d/sprite_sheet.rs b/examples/2d/sprite_sheet.rs index ae774b6120a1f..4af3f2aab1893 100644 --- a/examples/2d/sprite_sheet.rs +++ b/examples/2d/sprite_sheet.rs @@ -21,7 +21,7 @@ fn animate_sprite_system( } fn setup( - mut commands: Commands, + commands: &mut Commands, asset_server: Res, mut texture_atlases: ResMut>, ) { diff --git a/examples/2d/texture_atlas.rs b/examples/2d/texture_atlas.rs index e6036d1d34414..564ce5d8a74ac 100644 --- a/examples/2d/texture_atlas.rs +++ b/examples/2d/texture_atlas.rs @@ -21,7 +21,7 @@ fn setup(mut rpg_sprite_handles: ResMut, asset_server: Res, asset_server: Res, mut texture_atlases: ResMut>, diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index e053e10fa818f..5d42e4a903b2c 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -10,7 +10,7 @@ fn main() { /// set up a simple 3D scene fn setup( - mut commands: Commands, + commands: &mut Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index 248c525a5c4d4..f8786c0c210c7 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -8,7 +8,7 @@ fn main() { .run(); } -fn setup(mut commands: Commands, asset_server: Res) { +fn setup(commands: &mut Commands, asset_server: Res) { commands .spawn_scene(asset_server.load("models/FlightHelmet/FlightHelmet.gltf")) .spawn(LightComponents { diff --git a/examples/3d/msaa.rs b/examples/3d/msaa.rs index 264facd4dbf94..43b6d1b1c4fa8 100644 --- a/examples/3d/msaa.rs +++ b/examples/3d/msaa.rs @@ -13,7 +13,7 @@ fn main() { /// set up a simple 3D scene fn setup( - mut commands: Commands, + commands: &mut Commands, mut meshes: ResMut>, mut materials: ResMut>, ) { diff --git a/examples/3d/parenting.rs b/examples/3d/parenting.rs index f97b35c71a405..38d3d7983eb5c 100644 --- a/examples/3d/parenting.rs +++ b/examples/3d/parenting.rs @@ -23,7 +23,7 @@ fn rotator_system(time: Res