Support more kinds of system params in buildable systems. (#14050)
# Objective
Support more kinds of system params in buildable systems, such as a
`ParamSet` or `Vec` containing buildable params or tuples of buildable
params.
## Solution
Replace the `BuildableSystemParam` trait with `SystemParamBuilder` to
make it easier to compose builders. Provide implementations for existing
buildable params, plus tuples, `ParamSet`, and `Vec`.
## Examples
```rust
// ParamSet of tuple: 
let system = (ParamSetBuilder((
    QueryParamBuilder::new(|builder| { builder.with::<B>(); }),
    QueryParamBuilder::new(|builder| { builder.with::<C>(); }),
)),)
    .build_state(&mut world)
    .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
        params.p0().iter().count() + params.p1().iter().count()
    });
	
// ParamSet of Vec:
let system = (ParamSetBuilder(vec![
    QueryParamBuilder::new_box(|builder| { builder.with::<B>(); }),
    QueryParamBuilder::new_box(|builder| { builder.with::<C>(); }),
]),)
    .build_state(&mut world)
    .build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
        let mut count = 0;
        params.for_each(|mut query| count += query.iter_mut().count());
        count
    });
```
## Migration Guide
The API for `SystemBuilder` has changed. Instead of constructing a
builder with a world and then adding params, you first create a tuple of
param builders and then supply the world.
```rust
// Before
let system = SystemBuilder::<()>::new(&mut world)
    .local::<u64>()
    .builder::<Local<u64>>(|x| *x = 10)
    .builder::<Query<&A>>(|builder| { builder.with::<B>(); })
    .build(system);
// After
let system = (
    ParamBuilder,
    LocalBuilder(10),
    QueryParamBuilder::new(|builder| { builder.with::<B>(); }),
)
    .build_state(&mut world)
    .build_system(system);
```
## Possible Future Work
Here are a few possible follow-up changes. I coded them up to prove that
this API can support them, but they aren't necessary for this PR.
* chescock/bevy#1
* chescock/bevy#2
* chescock/bevy#3
			
			
This commit is contained in:
		
							parent
							
								
									9d6a4fbc85
								
							
						
					
					
						commit
						d4ec80d5d2
					
				| @ -60,7 +60,7 @@ pub mod prelude { | ||||
|         }, | ||||
|         system::{ | ||||
|             Commands, Deferred, In, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, | ||||
|             ParamSet, Query, ReadOnlySystem, Res, ResMut, Resource, System, SystemBuilder, | ||||
|             ParamSet, Query, ReadOnlySystem, Res, ResMut, Resource, System, SystemParamBuilder, | ||||
|             SystemParamFunction, | ||||
|         }, | ||||
|         world::{ | ||||
|  | ||||
| @ -1,18 +1,24 @@ | ||||
| use bevy_utils::all_tuples; | ||||
| use bevy_utils::{all_tuples, synccell::SyncCell}; | ||||
| 
 | ||||
| use super::{ | ||||
|     BuildableSystemParam, FunctionSystem, Local, Res, ResMut, Resource, SystemMeta, SystemParam, | ||||
|     SystemParamFunction, SystemState, | ||||
| use crate::{ | ||||
|     prelude::QueryBuilder, | ||||
|     query::{QueryData, QueryFilter, QueryState}, | ||||
|     system::{ | ||||
|         system_param::{Local, ParamSet, SystemParam}, | ||||
|         Query, SystemMeta, | ||||
|     }, | ||||
|     world::{FromWorld, World}, | ||||
| }; | ||||
| use crate::prelude::{FromWorld, Query, World}; | ||||
| use crate::query::{QueryData, QueryFilter}; | ||||
| use std::fmt::Debug; | ||||
| 
 | ||||
| /// Builder struct used to construct state for [`SystemParam`] passed to a system.
 | ||||
| use super::{init_query_param, Res, ResMut, Resource, SystemState}; | ||||
| 
 | ||||
| /// A builder that can create a [`SystemParam`]
 | ||||
| ///
 | ||||
| /// ```
 | ||||
| /// # use bevy_ecs::prelude::*;
 | ||||
| /// # use bevy_ecs_macros::SystemParam;
 | ||||
| /// # use bevy_ecs::system::RunSystemOnce;
 | ||||
| /// # use bevy_ecs::system::{RunSystemOnce, ParamBuilder, LocalBuilder, QueryParamBuilder};
 | ||||
| /// #
 | ||||
| /// # #[derive(Component)]
 | ||||
| /// # struct A;
 | ||||
| @ -28,121 +34,239 @@ use crate::query::{QueryData, QueryFilter}; | ||||
| /// #
 | ||||
| /// # let mut world = World::new();
 | ||||
| /// # world.insert_resource(R);
 | ||||
| ///
 | ||||
| /// #
 | ||||
| /// fn my_system(res: Res<R>, query: Query<&A>, param: MyParam) {
 | ||||
| ///     // ...
 | ||||
| /// }
 | ||||
| ///
 | ||||
| /// // Create a builder from the world, helper methods exist to add `SystemParam`,
 | ||||
| /// // alternatively use `.param::<T>()` for any other `SystemParam` types.
 | ||||
| /// let system = SystemBuilder::<()>::new(&mut world)
 | ||||
| ///     .resource::<R>()
 | ||||
| ///     .query::<&A>()
 | ||||
| ///     .param::<MyParam>()
 | ||||
| ///     .build(my_system);
 | ||||
| /// // To build a system, create a tuple of `SystemParamBuilder`s with a builder for each param.
 | ||||
| /// // `ParamBuilder` can be used to build a parameter using its default initialization,
 | ||||
| /// // and has helper methods to create typed builders.
 | ||||
| /// let system = (
 | ||||
| ///     ParamBuilder,
 | ||||
| ///     ParamBuilder::query::<&A>(),
 | ||||
| ///     ParamBuilder::of::<MyParam>(),
 | ||||
| /// )
 | ||||
| ///     .build_state(&mut world)
 | ||||
| ///     .build_system(my_system);
 | ||||
| ///
 | ||||
| /// // Parameters that the builder is initialised with will appear first in the arguments.
 | ||||
| /// let system = SystemBuilder::<(Res<R>, Query<&A>)>::new(&mut world)
 | ||||
| ///     .param::<MyParam>()
 | ||||
| ///     .build(my_system);
 | ||||
| /// // Other implementations of `SystemParamBuilder` can be used to configure the parameters.
 | ||||
| /// let system = (
 | ||||
| ///     ParamBuilder,
 | ||||
| ///     QueryParamBuilder::new::<&A, ()>(|builder| {
 | ||||
| ///         builder.with::<B>();
 | ||||
| ///     }),
 | ||||
| ///     ParamBuilder,
 | ||||
| /// )
 | ||||
| ///     .build_state(&mut world)
 | ||||
| ///     .build_system(my_system);
 | ||||
| ///
 | ||||
| /// // Parameters that implement `BuildableSystemParam` can use `.builder::<T>()` to build in place.
 | ||||
| /// let system = SystemBuilder::<()>::new(&mut world)
 | ||||
| ///     .resource::<R>()
 | ||||
| ///     .builder::<Query<&A>>(|builder| { builder.with::<B>(); })
 | ||||
| ///     .param::<MyParam>()
 | ||||
| ///     .build(my_system);
 | ||||
| /// fn single_parameter_system(local: Local<u64>) {
 | ||||
| ///     // ...
 | ||||
| /// }
 | ||||
| ///
 | ||||
| /// // Note that the builder for a system must be a tuple, even if there is only one parameter.
 | ||||
| /// let system = (LocalBuilder(2),)
 | ||||
| ///     .build_state(&mut world)
 | ||||
| ///     .build_system(single_parameter_system);
 | ||||
| ///
 | ||||
| /// world.run_system_once(system);
 | ||||
| ///```
 | ||||
| pub struct SystemBuilder<'w, T: SystemParam = ()> { | ||||
|     pub(crate) meta: SystemMeta, | ||||
|     pub(crate) state: T::State, | ||||
|     pub(crate) world: &'w mut World, | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// The implementor must ensure the following is true.
 | ||||
| /// - [`SystemParamBuilder::build`] correctly registers all [`World`] accesses used
 | ||||
| ///   by [`SystemParam::get_param`] with the provided [`system_meta`](SystemMeta).
 | ||||
| /// - None of the world accesses may conflict with any prior accesses registered
 | ||||
| ///   on `system_meta`.
 | ||||
| ///
 | ||||
| /// Note that this depends on the implementation of [`SystemParam::get_param`],
 | ||||
| /// so if `Self` is not a local type then you must call [`SystemParam::init_state`]
 | ||||
| /// or another [`SystemParamBuilder::build`]
 | ||||
| pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized { | ||||
|     /// Registers any [`World`] access used by this [`SystemParam`]
 | ||||
|     /// and creates a new instance of this param's [`State`](SystemParam::State).
 | ||||
|     fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State; | ||||
| 
 | ||||
|     /// Create a [`SystemState`] from a [`SystemParamBuilder`].
 | ||||
|     /// To create a system, call [`SystemState::build_system`] on the result.
 | ||||
|     fn build_state(self, world: &mut World) -> SystemState<P> { | ||||
|         SystemState::from_builder(world, self) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'w, T: SystemParam> SystemBuilder<'w, T> { | ||||
|     /// Construct a new builder with the default state for `T`
 | ||||
|     pub fn new(world: &'w mut World) -> Self { | ||||
|         let mut meta = SystemMeta::new::<T>(); | ||||
|         Self { | ||||
|             state: T::init_state(world, &mut meta), | ||||
|             meta, | ||||
|             world, | ||||
|         } | ||||
| /// A [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
 | ||||
| #[derive(Default, Debug, Copy, Clone)] | ||||
| pub struct ParamBuilder; | ||||
| 
 | ||||
| // SAFETY: Calls `SystemParam::init_state`
 | ||||
| unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder { | ||||
|     fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State { | ||||
|         P::init_state(world, meta) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ParamBuilder { | ||||
|     /// Creates a [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
 | ||||
|     pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> { | ||||
|         Self | ||||
|     } | ||||
| 
 | ||||
|     /// Construct the a system with the built params
 | ||||
|     pub fn build<F, Marker>(self, func: F) -> FunctionSystem<Marker, F> | ||||
|     where | ||||
|         F: SystemParamFunction<Marker, Param = T>, | ||||
|     /// Helper method for reading a [`Resource`] as a param, equivalent to `of::<Res<T>>()`
 | ||||
|     pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> { | ||||
|         Self | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for mutably accessing a [`Resource`] as a param, equivalent to `of::<ResMut<T>>()`
 | ||||
|     pub fn resource_mut<'w, T: Resource>() -> impl SystemParamBuilder<ResMut<'w, T>> { | ||||
|         Self | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for adding a [`Local`] as a param, equivalent to `of::<Local<T>>()`
 | ||||
|     pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> { | ||||
|         Self | ||||
|     } | ||||
| 
 | ||||
|     /// Helper method for adding a [`Query`] as a param, equivalent to `of::<Query<D>>()`
 | ||||
|     pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>> | ||||
|     { | ||||
|         FunctionSystem::from_builder(self, func) | ||||
|         Self | ||||
|     } | ||||
| 
 | ||||
|     /// Return the constructed [`SystemState`]
 | ||||
|     pub fn state(self) -> SystemState<T> { | ||||
|         SystemState::from_builder(self) | ||||
|     /// Helper method for adding a filtered [`Query`] as a param, equivalent to `of::<Query<D, F>>()`
 | ||||
|     pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>( | ||||
|     ) -> impl SystemParamBuilder<Query<'w, 's, D, F>> { | ||||
|         Self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_system_builder { | ||||
|     ($($curr: ident),*) => { | ||||
|         impl<'w, $($curr: SystemParam,)*> SystemBuilder<'w, ($($curr,)*)> { | ||||
|             /// Add `T` as a parameter built from the world
 | ||||
|             pub fn param<T: SystemParam>(mut self) -> SystemBuilder<'w, ($($curr,)* T,)> { | ||||
| // SAFETY: Calls `init_query_param`, just like `Query::init_state`.
 | ||||
| unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static> | ||||
|     SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F> | ||||
| { | ||||
|     fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> { | ||||
|         self.validate_world(world.id()); | ||||
|         init_query_param(world, system_meta, &self); | ||||
|         self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A [`SystemParamBuilder`] for a [`Query`].
 | ||||
| pub struct QueryParamBuilder<T>(T); | ||||
| 
 | ||||
| impl<T> QueryParamBuilder<T> { | ||||
|     /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
 | ||||
|     pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self | ||||
|     where | ||||
|         T: FnOnce(&mut QueryBuilder<D, F>), | ||||
|     { | ||||
|         Self(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, D: QueryData, F: QueryFilter> | ||||
|     QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>> | ||||
| { | ||||
|     /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
 | ||||
|     /// This boxes the callback so that it has a common type and can be put in a `Vec`.
 | ||||
|     pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self { | ||||
|         Self(Box::new(f)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // SAFETY: Calls `init_query_param`, just like `Query::init_state`.
 | ||||
| unsafe impl< | ||||
|         'w, | ||||
|         's, | ||||
|         D: QueryData + 'static, | ||||
|         F: QueryFilter + 'static, | ||||
|         T: FnOnce(&mut QueryBuilder<D, F>), | ||||
|     > SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T> | ||||
| { | ||||
|     fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> { | ||||
|         let mut builder = QueryBuilder::new(world); | ||||
|         (self.0)(&mut builder); | ||||
|         let state = builder.build(); | ||||
|         init_query_param(world, system_meta, &state); | ||||
|         state | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! impl_system_param_builder_tuple { | ||||
|     ($(($param: ident, $builder: ident)),*) => { | ||||
|         // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
 | ||||
|         unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) { | ||||
|             fn build(self, _world: &mut World, _meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State { | ||||
|                 #[allow(non_snake_case)] | ||||
|                 let ($($curr,)*) = self.state; | ||||
|                 SystemBuilder { | ||||
|                     state: ($($curr,)* T::init_state(self.world, &mut self.meta),), | ||||
|                     meta: self.meta, | ||||
|                     world: self.world, | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             /// Helper method for reading a [`Resource`] as a param, equivalent to `.param::<Res<T>>()`
 | ||||
|             pub fn resource<T: Resource>(self) -> SystemBuilder<'w,  ($($curr,)* Res<'static, T>,)> { | ||||
|                 self.param::<Res<T>>() | ||||
|             } | ||||
| 
 | ||||
|             /// Helper method for mutably accessing a [`Resource`] as a param, equivalent to `.param::<ResMut<T>>()`
 | ||||
|             pub fn resource_mut<T: Resource>(self) -> SystemBuilder<'w,  ($($curr,)* ResMut<'static, T>,)> { | ||||
|                 self.param::<ResMut<T>>() | ||||
|             } | ||||
| 
 | ||||
|             /// Helper method for adding a [`Local`] as a param, equivalent to `.param::<Local<T>>()`
 | ||||
|             pub fn local<T: Send + FromWorld>(self) -> SystemBuilder<'w,  ($($curr,)* Local<'static, T>,)> { | ||||
|                 self.param::<Local<T>>() | ||||
|             } | ||||
| 
 | ||||
|             /// Helper method for adding a [`Query`] as a param, equivalent to `.param::<Query<D>>()`
 | ||||
|             pub fn query<D: QueryData>(self) -> SystemBuilder<'w,  ($($curr,)* Query<'static, 'static, D, ()>,)> { | ||||
|                 self.query_filtered::<D, ()>() | ||||
|             } | ||||
| 
 | ||||
|             /// Helper method for adding a filtered [`Query`] as a param, equivalent to `.param::<Query<D, F>>()`
 | ||||
|             pub fn query_filtered<D: QueryData, F: QueryFilter>(self) -> SystemBuilder<'w,  ($($curr,)* Query<'static, 'static, D, F>,)> { | ||||
|                 self.param::<Query<D, F>>() | ||||
|             } | ||||
| 
 | ||||
|             /// Add `T` as a parameter built with the given function
 | ||||
|             pub fn builder<T: BuildableSystemParam>( | ||||
|                 mut self, | ||||
|                 func: impl FnOnce(&mut T::Builder<'_>), | ||||
|             ) -> SystemBuilder<'w, ($($curr,)* T,)> { | ||||
|                 #[allow(non_snake_case)] | ||||
|                 let ($($curr,)*) = self.state; | ||||
|                 SystemBuilder { | ||||
|                     state: ($($curr,)* T::build(self.world, &mut self.meta, func),), | ||||
|                     meta: self.meta, | ||||
|                     world: self.world, | ||||
|                 } | ||||
|                 let ($($builder,)*) = self; | ||||
|                 #[allow(clippy::unused_unit)] | ||||
|                 ($($builder.build(_world, _meta),)*) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| all_tuples!(impl_system_builder, 0, 15, P); | ||||
| all_tuples!(impl_system_param_builder_tuple, 0, 16, P, B); | ||||
| 
 | ||||
| /// A [`SystemParamBuilder`] for a [`ParamSet`].
 | ||||
| /// To build a [`ParamSet`] with a tuple of system parameters, pass a tuple of matching [`SystemParamBuilder`]s.
 | ||||
| /// To build a [`ParamSet`] with a `Vec` of system parameters, pass a `Vec` of matching [`SystemParamBuilder`]s.
 | ||||
| pub struct ParamSetBuilder<T>(T); | ||||
| 
 | ||||
| macro_rules! impl_param_set_builder_tuple { | ||||
|     ($(($param: ident, $builder: ident, $meta: ident)),*) => { | ||||
|         // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
 | ||||
|         unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> { | ||||
|             #[allow(non_snake_case)] | ||||
|             fn build(self, _world: &mut World, _system_meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State { | ||||
|                 let ParamSetBuilder(($($builder,)*)) = self; | ||||
|                 // Note that this is slightly different from `init_state`, which calls `init_state` on each param twice.
 | ||||
|                 // One call populates an empty `SystemMeta` with the new access, while the other runs against a cloned `SystemMeta` to check for conflicts.
 | ||||
|                 // Builders can only be invoked once, so we do both in a single call here.
 | ||||
|                 // That means that any `filtered_accesses` in the `component_access_set` will get copied to every `$meta`
 | ||||
|                 // and will appear multiple times in the final `SystemMeta`.
 | ||||
|                 $( | ||||
|                     let mut $meta = _system_meta.clone(); | ||||
|                     let $param = $builder.build(_world, &mut $meta); | ||||
|                 )* | ||||
|                 // Make the ParamSet non-send if any of its parameters are non-send.
 | ||||
|                 if false $(|| !$meta.is_send())* { | ||||
|                     _system_meta.set_non_send(); | ||||
|                 } | ||||
|                 $( | ||||
|                     _system_meta | ||||
|                         .component_access_set | ||||
|                         .extend($meta.component_access_set); | ||||
|                     _system_meta | ||||
|                         .archetype_component_access | ||||
|                         .extend(&$meta.archetype_component_access); | ||||
|                 )* | ||||
|                 #[allow(clippy::unused_unit)] | ||||
|                 ($($param,)*) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B, meta); | ||||
| 
 | ||||
| /// A [`SystemParamBuilder`] for a [`Local`].
 | ||||
| /// The provided value will be used as the initial value of the `Local`.
 | ||||
| pub struct LocalBuilder<T>(pub T); | ||||
| 
 | ||||
| // SAFETY: `Local` performs no world access.
 | ||||
| unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>> | ||||
|     for LocalBuilder<T> | ||||
| { | ||||
|     fn build( | ||||
|         self, | ||||
|         _world: &mut World, | ||||
|         _meta: &mut SystemMeta, | ||||
|     ) -> <Local<'s, T> as SystemParam>::State { | ||||
|         SyncCell::new(self.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
| @ -155,6 +279,12 @@ mod tests { | ||||
|     #[derive(Component)] | ||||
|     struct A; | ||||
| 
 | ||||
|     #[derive(Component)] | ||||
|     struct B; | ||||
| 
 | ||||
|     #[derive(Component)] | ||||
|     struct C; | ||||
| 
 | ||||
|     fn local_system(local: Local<u64>) -> u64 { | ||||
|         *local | ||||
|     } | ||||
| @ -171,9 +301,9 @@ mod tests { | ||||
|     fn local_builder() { | ||||
|         let mut world = World::new(); | ||||
| 
 | ||||
|         let system = SystemBuilder::<()>::new(&mut world) | ||||
|             .builder::<Local<u64>>(|x| *x = 10) | ||||
|             .build(local_system); | ||||
|         let system = (LocalBuilder(10),) | ||||
|             .build_state(&mut world) | ||||
|             .build_system(local_system); | ||||
| 
 | ||||
|         let result = world.run_system_once(system); | ||||
|         assert_eq!(result, 10); | ||||
| @ -186,11 +316,26 @@ mod tests { | ||||
|         world.spawn(A); | ||||
|         world.spawn_empty(); | ||||
| 
 | ||||
|         let system = SystemBuilder::<()>::new(&mut world) | ||||
|             .builder::<Query<()>>(|query| { | ||||
|                 query.with::<A>(); | ||||
|             }) | ||||
|             .build(query_system); | ||||
|         let system = (QueryParamBuilder::new(|query| { | ||||
|             query.with::<A>(); | ||||
|         }),) | ||||
|             .build_state(&mut world) | ||||
|             .build_system(query_system); | ||||
| 
 | ||||
|         let result = world.run_system_once(system); | ||||
|         assert_eq!(result, 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn query_builder_state() { | ||||
|         let mut world = World::new(); | ||||
| 
 | ||||
|         world.spawn(A); | ||||
|         world.spawn_empty(); | ||||
| 
 | ||||
|         let state = QueryBuilder::new(&mut world).with::<A>().build(); | ||||
| 
 | ||||
|         let system = (state,).build_state(&mut world).build_system(query_system); | ||||
| 
 | ||||
|         let result = world.run_system_once(system); | ||||
|         assert_eq!(result, 1); | ||||
| @ -203,12 +348,38 @@ mod tests { | ||||
|         world.spawn(A); | ||||
|         world.spawn_empty(); | ||||
| 
 | ||||
|         let system = SystemBuilder::<()>::new(&mut world) | ||||
|             .local::<u64>() | ||||
|             .param::<Local<u64>>() | ||||
|             .build(multi_param_system); | ||||
|         let system = (LocalBuilder(0), ParamBuilder) | ||||
|             .build_state(&mut world) | ||||
|             .build_system(multi_param_system); | ||||
| 
 | ||||
|         let result = world.run_system_once(system); | ||||
|         assert_eq!(result, 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn param_set_builder() { | ||||
|         let mut world = World::new(); | ||||
| 
 | ||||
|         world.spawn((A, B, C)); | ||||
|         world.spawn((A, B)); | ||||
|         world.spawn((A, C)); | ||||
|         world.spawn((A, C)); | ||||
|         world.spawn_empty(); | ||||
| 
 | ||||
|         let system = (ParamSetBuilder(( | ||||
|             QueryParamBuilder::new(|builder| { | ||||
|                 builder.with::<B>(); | ||||
|             }), | ||||
|             QueryParamBuilder::new(|builder| { | ||||
|                 builder.with::<C>(); | ||||
|             }), | ||||
|         )),) | ||||
|             .build_state(&mut world) | ||||
|             .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| { | ||||
|                 params.p0().iter().count() + params.p1().iter().count() | ||||
|             }); | ||||
| 
 | ||||
|         let result = world.run_system_once(system); | ||||
|         assert_eq!(result, 5); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -14,7 +14,7 @@ use std::{borrow::Cow, marker::PhantomData}; | ||||
| #[cfg(feature = "trace")] | ||||
| use bevy_utils::tracing::{info_span, Span}; | ||||
| 
 | ||||
| use super::{In, IntoSystem, ReadOnlySystem, SystemBuilder}; | ||||
| use super::{In, IntoSystem, ReadOnlySystem, SystemParamBuilder}; | ||||
| 
 | ||||
| /// The metadata of a [`System`].
 | ||||
| #[derive(Clone)] | ||||
| @ -216,16 +216,34 @@ impl<Param: SystemParam> SystemState<Param> { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Create a [`SystemState`] from a [`SystemBuilder`]
 | ||||
|     pub(crate) fn from_builder(builder: SystemBuilder<Param>) -> Self { | ||||
|     /// Create a [`SystemState`] from a [`SystemParamBuilder`]
 | ||||
|     pub(crate) fn from_builder(world: &mut World, builder: impl SystemParamBuilder<Param>) -> Self { | ||||
|         let mut meta = SystemMeta::new::<Param>(); | ||||
|         meta.last_run = world.change_tick().relative_to(Tick::MAX); | ||||
|         let param_state = builder.build(world, &mut meta); | ||||
|         Self { | ||||
|             meta: builder.meta, | ||||
|             param_state: builder.state, | ||||
|             world_id: builder.world.id(), | ||||
|             meta, | ||||
|             param_state, | ||||
|             world_id: world.id(), | ||||
|             archetype_generation: ArchetypeGeneration::initial(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Create a [`FunctionSystem`] from a [`SystemState`].
 | ||||
|     pub fn build_system<Marker, F: SystemParamFunction<Marker, Param = Param>>( | ||||
|         self, | ||||
|         func: F, | ||||
|     ) -> FunctionSystem<Marker, F> { | ||||
|         FunctionSystem { | ||||
|             func, | ||||
|             param_state: Some(self.param_state), | ||||
|             system_meta: self.meta, | ||||
|             world_id: Some(self.world_id), | ||||
|             archetype_generation: self.archetype_generation, | ||||
|             marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the metadata for this instance.
 | ||||
|     #[inline] | ||||
|     pub fn meta(&self) -> &SystemMeta { | ||||
| @ -425,18 +443,6 @@ impl<Marker, F> FunctionSystem<Marker, F> | ||||
| where | ||||
|     F: SystemParamFunction<Marker>, | ||||
| { | ||||
|     // Create a [`FunctionSystem`] from a [`SystemBuilder`]
 | ||||
|     pub(crate) fn from_builder(builder: SystemBuilder<F::Param>, func: F) -> Self { | ||||
|         Self { | ||||
|             func, | ||||
|             param_state: Some(builder.state), | ||||
|             system_meta: builder.meta, | ||||
|             world_id: Some(builder.world.id()), | ||||
|             archetype_generation: ArchetypeGeneration::initial(), | ||||
|             marker: PhantomData, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Return this system with a new name.
 | ||||
|     ///
 | ||||
|     /// Useful to give closure systems more readable and unique names for debugging and tracing.
 | ||||
|  | ||||
| @ -7,7 +7,6 @@ use crate::{ | ||||
|     change_detection::{Ticks, TicksMut}, | ||||
|     component::{ComponentId, ComponentTicks, Components, Tick}, | ||||
|     entity::Entities, | ||||
|     prelude::QueryBuilder, | ||||
|     query::{ | ||||
|         Access, FilteredAccess, FilteredAccessSet, QueryData, QueryFilter, QueryState, | ||||
|         ReadOnlyQueryData, | ||||
| @ -185,19 +184,6 @@ pub unsafe trait SystemParam: Sized { | ||||
|     ) -> Self::Item<'world, 'state>; | ||||
| } | ||||
| 
 | ||||
| /// A parameter that can be built with [`SystemBuilder`](crate::system::builder::SystemBuilder)
 | ||||
| pub trait BuildableSystemParam: SystemParam { | ||||
|     /// A mutable reference to this type will be passed to the builder function
 | ||||
|     type Builder<'b>; | ||||
| 
 | ||||
|     /// Constructs [`SystemParam::State`] for `Self` using a given builder function
 | ||||
|     fn build( | ||||
|         world: &mut World, | ||||
|         meta: &mut SystemMeta, | ||||
|         func: impl FnOnce(&mut Self::Builder<'_>), | ||||
|     ) -> Self::State; | ||||
| } | ||||
| 
 | ||||
| /// A [`SystemParam`] that only reads a given [`World`].
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| @ -221,17 +207,7 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Qu | ||||
| 
 | ||||
|     fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State { | ||||
|         let state = QueryState::new_with_access(world, &mut system_meta.archetype_component_access); | ||||
|         assert_component_access_compatibility( | ||||
|             &system_meta.name, | ||||
|             std::any::type_name::<D>(), | ||||
|             std::any::type_name::<F>(), | ||||
|             &system_meta.component_access_set, | ||||
|             &state.component_access, | ||||
|             world, | ||||
|         ); | ||||
|         system_meta | ||||
|             .component_access_set | ||||
|             .add(state.component_access.clone()); | ||||
|         init_query_param(world, system_meta, &state); | ||||
|         state | ||||
|     } | ||||
| 
 | ||||
| @ -257,33 +233,22 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Qu | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static> BuildableSystemParam | ||||
|     for Query<'w, 's, D, F> | ||||
| { | ||||
|     type Builder<'b> = QueryBuilder<'b, D, F>; | ||||
| 
 | ||||
|     #[inline] | ||||
|     fn build( | ||||
|         world: &mut World, | ||||
|         system_meta: &mut SystemMeta, | ||||
|         build: impl FnOnce(&mut Self::Builder<'_>), | ||||
|     ) -> Self::State { | ||||
|         let mut builder = QueryBuilder::new(world); | ||||
|         build(&mut builder); | ||||
|         let state = builder.build(); | ||||
|         assert_component_access_compatibility( | ||||
|             &system_meta.name, | ||||
|             std::any::type_name::<D>(), | ||||
|             std::any::type_name::<F>(), | ||||
|             &system_meta.component_access_set, | ||||
|             &state.component_access, | ||||
|             world, | ||||
|         ); | ||||
|         system_meta | ||||
|             .component_access_set | ||||
|             .add(state.component_access.clone()); | ||||
|         state | ||||
|     } | ||||
| pub(crate) fn init_query_param<D: QueryData + 'static, F: QueryFilter + 'static>( | ||||
|     world: &mut World, | ||||
|     system_meta: &mut SystemMeta, | ||||
|     state: &QueryState<D, F>, | ||||
| ) { | ||||
|     assert_component_access_compatibility( | ||||
|         &system_meta.name, | ||||
|         std::any::type_name::<D>(), | ||||
|         std::any::type_name::<F>(), | ||||
|         &system_meta.component_access_set, | ||||
|         &state.component_access, | ||||
|         world, | ||||
|     ); | ||||
|     system_meta | ||||
|         .component_access_set | ||||
|         .add(state.component_access.clone()); | ||||
| } | ||||
| 
 | ||||
| fn assert_component_access_compatibility( | ||||
| @ -874,20 +839,6 @@ unsafe impl<'a, T: FromWorld + Send + 'static> SystemParam for Local<'a, T> { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'w, T: FromWorld + Send + 'static> BuildableSystemParam for Local<'w, T> { | ||||
|     type Builder<'b> = T; | ||||
| 
 | ||||
|     fn build( | ||||
|         world: &mut World, | ||||
|         _meta: &mut SystemMeta, | ||||
|         func: impl FnOnce(&mut Self::Builder<'_>), | ||||
|     ) -> Self::State { | ||||
|         let mut value = T::from_world(world); | ||||
|         func(&mut value); | ||||
|         SyncCell::new(value) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Types that can be used with [`Deferred<T>`] in systems.
 | ||||
| /// This allows storing system-local data which is used to defer [`World`] mutations.
 | ||||
| ///
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Chris Russell
						Chris Russell