mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
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
4 changed files with 323 additions and 195 deletions
|
@ -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 a new issue