bevy_ecs/system/
builder.rs

1use alloc::{boxed::Box, vec::Vec};
2use bevy_utils::synccell::SyncCell;
3use variadics_please::all_tuples;
4
5use crate::{
6    prelude::QueryBuilder,
7    query::{QueryData, QueryFilter, QueryState},
8    resource::Resource,
9    system::{
10        DynSystemParam, DynSystemParamState, Local, ParamSet, Query, SystemMeta, SystemParam,
11    },
12    world::{
13        FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,
14        FilteredResourcesMutBuilder, FromWorld, World,
15    },
16};
17use core::fmt::Debug;
18
19use super::{init_query_param, Res, ResMut, SystemState};
20
21/// A builder that can create a [`SystemParam`].
22///
23/// ```
24/// # use bevy_ecs::{
25/// #     prelude::*,
26/// #     system::{SystemParam, ParamBuilder},
27/// # };
28/// # #[derive(Resource)]
29/// # struct R;
30/// #
31/// # #[derive(SystemParam)]
32/// # struct MyParam;
33/// #
34/// fn some_system(param: MyParam) {}
35///
36/// fn build_system(builder: impl SystemParamBuilder<MyParam>) {
37///     let mut world = World::new();
38///     // To build a system, create a tuple of `SystemParamBuilder`s
39///     // with a builder for each parameter.
40///     // Note that the builder for a system must be a tuple,
41///     // even if there is only one parameter.
42///     (builder,)
43///         .build_state(&mut world)
44///         .build_system(some_system);
45/// }
46///
47/// fn build_closure_system_infer(builder: impl SystemParamBuilder<MyParam>) {
48///     let mut world = World::new();
49///     // Closures can be used in addition to named functions.
50///     // If a closure is used, the parameter types must all be inferred
51///     // from the builders, so you cannot use plain `ParamBuilder`.
52///     (builder, ParamBuilder::resource())
53///         .build_state(&mut world)
54///         .build_system(|param, res| {
55///             let param: MyParam = param;
56///             let res: Res<R> = res;
57///         });
58/// }
59///
60/// fn build_closure_system_explicit(builder: impl SystemParamBuilder<MyParam>) {
61///     let mut world = World::new();
62///     // Alternately, you can provide all types in the closure
63///     // parameter list and call `build_any_system()`.
64///     (builder, ParamBuilder)
65///         .build_state(&mut world)
66///         .build_any_system(|param: MyParam, res: Res<R>| {});
67/// }
68/// ```
69///
70/// See the documentation for individual builders for more examples.
71///
72/// # List of Builders
73///
74/// [`ParamBuilder`] can be used for parameters that don't require any special building.
75/// Using a `ParamBuilder` will build the system parameter the same way it would be initialized in an ordinary system.
76///
77/// `ParamBuilder` also provides factory methods that return a `ParamBuilder` typed as `impl SystemParamBuilder<P>`
78/// for common system parameters that can be used to guide closure parameter inference.
79///
80/// [`QueryParamBuilder`] can build a [`Query`] to add additional filters,
81/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).
82/// You can also use a [`QueryState`] to build a [`Query`].
83///
84/// [`LocalBuilder`] can build a [`Local`] to supply the initial value for the `Local`.
85///
86/// [`FilteredResourcesParamBuilder`] can build a [`FilteredResources`],
87/// and [`FilteredResourcesMutParamBuilder`] can build a [`FilteredResourcesMut`],
88/// to configure the resources that can be accessed.
89///
90/// [`DynParamBuilder`] can build a [`DynSystemParam`] to determine the type of the inner parameter,
91/// and to supply any `SystemParamBuilder` it needs.
92///
93/// Tuples of builders can build tuples of parameters, one builder for each element.
94/// Note that since systems require a tuple as a parameter, the outer builder for a system will always be a tuple.
95///
96/// A [`Vec`] of builders can build a `Vec` of parameters, one builder for each element.
97///
98/// A [`ParamSetBuilder`] can build a [`ParamSet`].
99/// This can wrap either a tuple or a `Vec`, one builder for each element.
100///
101/// A custom system param created with `#[derive(SystemParam)]` can be buildable if it includes a `#[system_param(builder)]` attribute.
102/// See [the documentation for `SystemParam` derives](SystemParam#builders).
103///
104/// # Safety
105///
106/// The implementor must ensure the following is true.
107/// - [`SystemParamBuilder::build`] correctly registers all [`World`] accesses used
108///   by [`SystemParam::get_param`] with the provided [`system_meta`](SystemMeta).
109/// - None of the world accesses may conflict with any prior accesses registered
110///   on `system_meta`.
111///
112/// Note that this depends on the implementation of [`SystemParam::get_param`],
113/// so if `Self` is not a local type then you must call [`SystemParam::init_state`]
114/// or another [`SystemParamBuilder::build`]
115pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {
116    /// Registers any [`World`] access used by this [`SystemParam`]
117    /// and creates a new instance of this param's [`State`](SystemParam::State).
118    fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State;
119
120    /// Create a [`SystemState`] from a [`SystemParamBuilder`].
121    /// To create a system, call [`SystemState::build_system`] on the result.
122    fn build_state(self, world: &mut World) -> SystemState<P> {
123        SystemState::from_builder(world, self)
124    }
125}
126
127/// A [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
128///
129/// ## Example
130///
131/// ```
132/// # use bevy_ecs::{
133/// #     prelude::*,
134/// #     system::{SystemParam, ParamBuilder},
135/// # };
136/// #
137/// # #[derive(Component)]
138/// # struct A;
139/// #
140/// # #[derive(Resource)]
141/// # struct R;
142/// #
143/// # #[derive(SystemParam)]
144/// # struct MyParam;
145/// #
146/// # let mut world = World::new();
147/// # world.insert_resource(R);
148/// #
149/// fn my_system(res: Res<R>, param: MyParam, query: Query<&A>) {
150///     // ...
151/// }
152///
153/// let system = (
154///     // A plain ParamBuilder can build any parameter type.
155///     ParamBuilder,
156///     // The `of::<P>()` method returns a `ParamBuilder`
157///     // typed as `impl SystemParamBuilder<P>`.
158///     ParamBuilder::of::<MyParam>(),
159///     // The other factory methods return typed builders
160///     // for common parameter types.
161///     ParamBuilder::query::<&A>(),
162/// )
163///     .build_state(&mut world)
164///     .build_system(my_system);
165/// ```
166#[derive(Default, Debug, Clone)]
167pub struct ParamBuilder;
168
169// SAFETY: Calls `SystemParam::init_state`
170unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {
171    fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State {
172        P::init_state(world, meta)
173    }
174}
175
176impl ParamBuilder {
177    /// Creates a [`SystemParamBuilder`] for any [`SystemParam`] that uses its default initialization.
178    pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> {
179        Self
180    }
181
182    /// Helper method for reading a [`Resource`] as a param, equivalent to `of::<Res<T>>()`
183    pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> {
184        Self
185    }
186
187    /// Helper method for mutably accessing a [`Resource`] as a param, equivalent to `of::<ResMut<T>>()`
188    pub fn resource_mut<'w, T: Resource>() -> impl SystemParamBuilder<ResMut<'w, T>> {
189        Self
190    }
191
192    /// Helper method for adding a [`Local`] as a param, equivalent to `of::<Local<T>>()`
193    pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> {
194        Self
195    }
196
197    /// Helper method for adding a [`Query`] as a param, equivalent to `of::<Query<D>>()`
198    pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>>
199    {
200        Self
201    }
202
203    /// Helper method for adding a filtered [`Query`] as a param, equivalent to `of::<Query<D, F>>()`
204    pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>(
205    ) -> impl SystemParamBuilder<Query<'w, 's, D, F>> {
206        Self
207    }
208}
209
210// SAFETY: Calls `init_query_param`, just like `Query::init_state`.
211unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>
212    SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>
213{
214    fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
215        self.validate_world(world.id());
216        init_query_param(world, system_meta, &self);
217        self
218    }
219}
220
221/// A [`SystemParamBuilder`] for a [`Query`].
222/// This takes a closure accepting an `&mut` [`QueryBuilder`] and uses the builder to construct the query's state.
223/// This can be used to add additional filters,
224/// or to configure the components available to [`FilteredEntityRef`](crate::world::FilteredEntityRef) or [`FilteredEntityMut`](crate::world::FilteredEntityMut).
225///
226/// ## Example
227///
228/// ```
229/// # use bevy_ecs::{
230/// #     prelude::*,
231/// #     system::{SystemParam, QueryParamBuilder},
232/// # };
233/// #
234/// # #[derive(Component)]
235/// # struct Player;
236/// #
237/// # let mut world = World::new();
238/// let system = (QueryParamBuilder::new(|builder| {
239///     builder.with::<Player>();
240/// }),)
241///     .build_state(&mut world)
242///     .build_system(|query: Query<()>| {
243///         for _ in &query {
244///             // This only includes entities with a `Player` component.
245///         }
246///     });
247///
248/// // When collecting multiple builders into a `Vec`,
249/// // use `new_box()` to erase the closure type.
250/// let system = (vec![
251///     QueryParamBuilder::new_box(|builder| {
252///         builder.with::<Player>();
253///     }),
254///     QueryParamBuilder::new_box(|builder| {
255///         builder.without::<Player>();
256///     }),
257/// ],)
258///     .build_state(&mut world)
259///     .build_system(|query: Vec<Query<()>>| {});
260/// ```
261#[derive(Clone)]
262pub struct QueryParamBuilder<T>(T);
263
264impl<T> QueryParamBuilder<T> {
265    /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
266    pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self
267    where
268        T: FnOnce(&mut QueryBuilder<D, F>),
269    {
270        Self(f)
271    }
272}
273
274impl<'a, D: QueryData, F: QueryFilter>
275    QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>>
276{
277    /// Creates a [`SystemParamBuilder`] for a [`Query`] that accepts a callback to configure the [`QueryBuilder`].
278    /// This boxes the callback so that it has a common type and can be put in a `Vec`.
279    pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {
280        Self(Box::new(f))
281    }
282}
283
284// SAFETY: Calls `init_query_param`, just like `Query::init_state`.
285unsafe impl<
286        'w,
287        's,
288        D: QueryData + 'static,
289        F: QueryFilter + 'static,
290        T: FnOnce(&mut QueryBuilder<D, F>),
291    > SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>
292{
293    fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
294        let mut builder = QueryBuilder::new(world);
295        (self.0)(&mut builder);
296        let state = builder.build();
297        init_query_param(world, system_meta, &state);
298        state
299    }
300}
301
302macro_rules! impl_system_param_builder_tuple {
303    ($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {
304        #[expect(
305            clippy::allow_attributes,
306            reason = "This is in a macro; as such, the below lints may not always apply."
307        )]
308        #[allow(
309            unused_variables,
310            reason = "Zero-length tuples won't use any of the parameters."
311        )]
312        #[allow(
313            non_snake_case,
314            reason = "The variable names are provided by the macro caller, not by us."
315        )]
316        $(#[$meta])*
317        // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
318        unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {
319            fn build(self, world: &mut World, meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
320                let ($($builder,)*) = self;
321                #[allow(
322                    clippy::unused_unit,
323                    reason = "Zero-length tuples won't generate any calls to the system parameter builders."
324                )]
325                ($($builder.build(world, meta),)*)
326            }
327        }
328    };
329}
330
331all_tuples!(
332    #[doc(fake_variadic)]
333    impl_system_param_builder_tuple,
334    0,
335    16,
336    P,
337    B
338);
339
340// SAFETY: implementors of each `SystemParamBuilder` in the vec have validated their impls
341unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {
342    fn build(self, world: &mut World, meta: &mut SystemMeta) -> <Vec<P> as SystemParam>::State {
343        self.into_iter()
344            .map(|builder| builder.build(world, meta))
345            .collect()
346    }
347}
348
349/// A [`SystemParamBuilder`] for a [`ParamSet`].
350///
351/// To build a [`ParamSet`] with a tuple of system parameters, pass a tuple of matching [`SystemParamBuilder`]s.
352/// To build a [`ParamSet`] with a [`Vec`] of system parameters, pass a `Vec` of matching [`SystemParamBuilder`]s.
353///
354/// # Examples
355///
356/// ```
357/// # use bevy_ecs::{prelude::*, system::*};
358/// #
359/// # #[derive(Component)]
360/// # struct Health;
361/// #
362/// # #[derive(Component)]
363/// # struct Enemy;
364/// #
365/// # #[derive(Component)]
366/// # struct Ally;
367/// #
368/// # let mut world = World::new();
369/// #
370/// let system = (ParamSetBuilder((
371///     QueryParamBuilder::new(|builder| {
372///         builder.with::<Enemy>();
373///     }),
374///     QueryParamBuilder::new(|builder| {
375///         builder.with::<Ally>();
376///     }),
377///     ParamBuilder,
378/// )),)
379///     .build_state(&mut world)
380///     .build_system(buildable_system_with_tuple);
381/// # world.run_system_once(system);
382///
383/// fn buildable_system_with_tuple(
384///     mut set: ParamSet<(Query<&mut Health>, Query<&mut Health>, &World)>,
385/// ) {
386///     // The first parameter is built from the first builder,
387///     // so this will iterate over enemies.
388///     for mut health in set.p0().iter_mut() {}
389///     // And the second parameter is built from the second builder,
390///     // so this will iterate over allies.
391///     for mut health in set.p1().iter_mut() {}
392///     // Parameters that don't need special building can use `ParamBuilder`.
393///     let entities = set.p2().entities();
394/// }
395///
396/// let system = (ParamSetBuilder(vec![
397///     QueryParamBuilder::new_box(|builder| {
398///         builder.with::<Enemy>();
399///     }),
400///     QueryParamBuilder::new_box(|builder| {
401///         builder.with::<Ally>();
402///     }),
403/// ]),)
404///     .build_state(&mut world)
405///     .build_system(buildable_system_with_vec);
406/// # world.run_system_once(system);
407///
408/// fn buildable_system_with_vec(mut set: ParamSet<Vec<Query<&mut Health>>>) {
409///     // As with tuples, the first parameter is built from the first builder,
410///     // so this will iterate over enemies.
411///     for mut health in set.get_mut(0).iter_mut() {}
412///     // And the second parameter is built from the second builder,
413///     // so this will iterate over allies.
414///     for mut health in set.get_mut(1).iter_mut() {}
415///     // You can iterate over the parameters either by index,
416///     // or using the `for_each` method.
417///     set.for_each(|mut query| for mut health in query.iter_mut() {});
418/// }
419/// ```
420#[derive(Debug, Default, Clone)]
421pub struct ParamSetBuilder<T>(pub T);
422
423macro_rules! impl_param_set_builder_tuple {
424    ($(($param: ident, $builder: ident, $meta: ident)),*) => {
425        #[expect(
426            clippy::allow_attributes,
427            reason = "This is in a macro; as such, the below lints may not always apply."
428        )]
429        #[allow(
430            unused_variables,
431            reason = "Zero-length tuples won't use any of the parameters."
432        )]
433        #[allow(
434            non_snake_case,
435            reason = "The variable names are provided by the macro caller, not by us."
436        )]
437        // SAFETY: implementors of each `SystemParamBuilder` in the tuple have validated their impls
438        unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {
439            fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
440                let ParamSetBuilder(($($builder,)*)) = self;
441                // Note that this is slightly different from `init_state`, which calls `init_state` on each param twice.
442                // One call populates an empty `SystemMeta` with the new access, while the other runs against a cloned `SystemMeta` to check for conflicts.
443                // Builders can only be invoked once, so we do both in a single call here.
444                // That means that any `filtered_accesses` in the `component_access_set` will get copied to every `$meta`
445                // and will appear multiple times in the final `SystemMeta`.
446                $(
447                    let mut $meta = system_meta.clone();
448                    let $param = $builder.build(world, &mut $meta);
449                )*
450                // Make the ParamSet non-send if any of its parameters are non-send.
451                if false $(|| !$meta.is_send())* {
452                    system_meta.set_non_send();
453                }
454                $(
455                    system_meta
456                        .component_access_set
457                        .extend($meta.component_access_set);
458                    system_meta
459                        .archetype_component_access
460                        .extend(&$meta.archetype_component_access);
461                )*
462                #[allow(
463                    clippy::unused_unit,
464                    reason = "Zero-length tuples won't generate any calls to the system parameter builders."
465                )]
466                ($($param,)*)
467            }
468        }
469    };
470}
471
472all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B, meta);
473
474// SAFETY: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts
475// with any prior access, a panic will occur.
476unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>
477    SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>
478{
479    fn build(
480        self,
481        world: &mut World,
482        system_meta: &mut SystemMeta,
483    ) -> <Vec<P> as SystemParam>::State {
484        let mut states = Vec::with_capacity(self.0.len());
485        let mut metas = Vec::with_capacity(self.0.len());
486        for builder in self.0 {
487            let mut meta = system_meta.clone();
488            states.push(builder.build(world, &mut meta));
489            metas.push(meta);
490        }
491        if metas.iter().any(|m| !m.is_send()) {
492            system_meta.set_non_send();
493        }
494        for meta in metas {
495            system_meta
496                .component_access_set
497                .extend(meta.component_access_set);
498            system_meta
499                .archetype_component_access
500                .extend(&meta.archetype_component_access);
501        }
502        states
503    }
504}
505
506/// A [`SystemParamBuilder`] for a [`DynSystemParam`].
507/// See the [`DynSystemParam`] docs for examples.
508pub struct DynParamBuilder<'a>(
509    Box<dyn FnOnce(&mut World, &mut SystemMeta) -> DynSystemParamState + 'a>,
510);
511
512impl<'a> DynParamBuilder<'a> {
513    /// Creates a new [`DynParamBuilder`] by wrapping a [`SystemParamBuilder`] of any type.
514    /// The built [`DynSystemParam`] can be downcast to `T`.
515    pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {
516        Self(Box::new(|world, meta| {
517            DynSystemParamState::new::<T>(builder.build(world, meta))
518        }))
519    }
520}
521
522// SAFETY: `DynSystemParam::get_param` will call `get_param` on the boxed `DynSystemParamState`,
523// and the boxed builder was a valid implementation of `SystemParamBuilder` for that type.
524// The resulting `DynSystemParam` can only perform access by downcasting to that param type.
525unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {
526    fn build(
527        self,
528        world: &mut World,
529        meta: &mut SystemMeta,
530    ) -> <DynSystemParam<'w, 's> as SystemParam>::State {
531        (self.0)(world, meta)
532    }
533}
534
535/// A [`SystemParamBuilder`] for a [`Local`].
536/// The provided value will be used as the initial value of the `Local`.
537///
538/// ## Example
539///
540/// ```
541/// # use bevy_ecs::{
542/// #     prelude::*,
543/// #     system::{SystemParam, LocalBuilder, RunSystemOnce},
544/// # };
545/// #
546/// # let mut world = World::new();
547/// let system = (LocalBuilder(100),)
548///     .build_state(&mut world)
549///     .build_system(|local: Local<usize>| {
550///         assert_eq!(*local, 100);
551///     });
552/// # world.run_system_once(system);
553/// ```
554#[derive(Default, Debug, Clone)]
555pub struct LocalBuilder<T>(pub T);
556
557// SAFETY: `Local` performs no world access.
558unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>
559    for LocalBuilder<T>
560{
561    fn build(
562        self,
563        _world: &mut World,
564        _meta: &mut SystemMeta,
565    ) -> <Local<'s, T> as SystemParam>::State {
566        SyncCell::new(self.0)
567    }
568}
569
570/// A [`SystemParamBuilder`] for a [`FilteredResources`].
571/// See the [`FilteredResources`] docs for examples.
572#[derive(Clone)]
573pub struct FilteredResourcesParamBuilder<T>(T);
574
575impl<T> FilteredResourcesParamBuilder<T> {
576    /// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
577    pub fn new(f: T) -> Self
578    where
579        T: FnOnce(&mut FilteredResourcesBuilder),
580    {
581        Self(f)
582    }
583}
584
585impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {
586    /// Creates a [`SystemParamBuilder`] for a [`FilteredResources`] that accepts a callback to configure the [`FilteredResourcesBuilder`].
587    /// This boxes the callback so that it has a common type.
588    pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {
589        Self(Box::new(f))
590    }
591}
592
593// SAFETY: Resource ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this FilteredResources
594// conflicts with any prior access, a panic will occur.
595unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>
596    SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>
597{
598    fn build(
599        self,
600        world: &mut World,
601        meta: &mut SystemMeta,
602    ) -> <FilteredResources<'w, 's> as SystemParam>::State {
603        let mut builder = FilteredResourcesBuilder::new(world);
604        (self.0)(&mut builder);
605        let access = builder.build();
606
607        let combined_access = meta.component_access_set.combined_access();
608        let conflicts = combined_access.get_conflicts(&access);
609        if !conflicts.is_empty() {
610            let accesses = conflicts.format_conflict_list(world);
611            let system_name = &meta.name;
612            panic!("error[B0002]: FilteredResources in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002");
613        }
614
615        if access.has_read_all_resources() {
616            meta.component_access_set
617                .add_unfiltered_read_all_resources();
618            meta.archetype_component_access.read_all_resources();
619        } else {
620            for component_id in access.resource_reads_and_writes() {
621                meta.component_access_set
622                    .add_unfiltered_resource_read(component_id);
623
624                let archetype_component_id = world.initialize_resource_internal(component_id).id();
625                meta.archetype_component_access
626                    .add_resource_read(archetype_component_id);
627            }
628        }
629
630        access
631    }
632}
633
634/// A [`SystemParamBuilder`] for a [`FilteredResourcesMut`].
635/// See the [`FilteredResourcesMut`] docs for examples.
636#[derive(Clone)]
637pub struct FilteredResourcesMutParamBuilder<T>(T);
638
639impl<T> FilteredResourcesMutParamBuilder<T> {
640    /// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
641    pub fn new(f: T) -> Self
642    where
643        T: FnOnce(&mut FilteredResourcesMutBuilder),
644    {
645        Self(f)
646    }
647}
648
649impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {
650    /// Creates a [`SystemParamBuilder`] for a [`FilteredResourcesMut`] that accepts a callback to configure the [`FilteredResourcesMutBuilder`].
651    /// This boxes the callback so that it has a common type.
652    pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {
653        Self(Box::new(f))
654    }
655}
656
657// SAFETY: Resource ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this FilteredResources
658// conflicts with any prior access, a panic will occur.
659unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>
660    SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>
661{
662    fn build(
663        self,
664        world: &mut World,
665        meta: &mut SystemMeta,
666    ) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {
667        let mut builder = FilteredResourcesMutBuilder::new(world);
668        (self.0)(&mut builder);
669        let access = builder.build();
670
671        let combined_access = meta.component_access_set.combined_access();
672        let conflicts = combined_access.get_conflicts(&access);
673        if !conflicts.is_empty() {
674            let accesses = conflicts.format_conflict_list(world);
675            let system_name = &meta.name;
676            panic!("error[B0002]: FilteredResourcesMut in system {system_name} accesses resources(s){accesses} in a way that conflicts with a previous system parameter. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002");
677        }
678
679        if access.has_read_all_resources() {
680            meta.component_access_set
681                .add_unfiltered_read_all_resources();
682            meta.archetype_component_access.read_all_resources();
683        } else {
684            for component_id in access.resource_reads() {
685                meta.component_access_set
686                    .add_unfiltered_resource_read(component_id);
687
688                let archetype_component_id = world.initialize_resource_internal(component_id).id();
689                meta.archetype_component_access
690                    .add_resource_read(archetype_component_id);
691            }
692        }
693
694        if access.has_write_all_resources() {
695            meta.component_access_set
696                .add_unfiltered_write_all_resources();
697            meta.archetype_component_access.write_all_resources();
698        } else {
699            for component_id in access.resource_writes() {
700                meta.component_access_set
701                    .add_unfiltered_resource_write(component_id);
702
703                let archetype_component_id = world.initialize_resource_internal(component_id).id();
704                meta.archetype_component_access
705                    .add_resource_write(archetype_component_id);
706            }
707        }
708
709        access
710    }
711}
712
713#[cfg(test)]
714mod tests {
715    use crate::{
716        entity::Entities,
717        prelude::{Component, Query},
718        reflect::ReflectResource,
719        system::{Local, RunSystemOnce},
720    };
721    use alloc::vec;
722    use bevy_reflect::{FromType, Reflect, ReflectRef};
723
724    use super::*;
725
726    #[derive(Component)]
727    struct A;
728
729    #[derive(Component)]
730    struct B;
731
732    #[derive(Component)]
733    struct C;
734
735    #[derive(Resource, Default, Reflect)]
736    #[reflect(Resource)]
737    struct R {
738        foo: usize,
739    }
740
741    fn local_system(local: Local<u64>) -> u64 {
742        *local
743    }
744
745    fn query_system(query: Query<()>) -> usize {
746        query.iter().count()
747    }
748
749    fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {
750        *a + *b + 1
751    }
752
753    #[test]
754    fn local_builder() {
755        let mut world = World::new();
756
757        let system = (LocalBuilder(10),)
758            .build_state(&mut world)
759            .build_system(local_system);
760
761        let output = world.run_system_once(system).unwrap();
762        assert_eq!(output, 10);
763    }
764
765    #[test]
766    fn query_builder() {
767        let mut world = World::new();
768
769        world.spawn(A);
770        world.spawn_empty();
771
772        let system = (QueryParamBuilder::new(|query| {
773            query.with::<A>();
774        }),)
775            .build_state(&mut world)
776            .build_system(query_system);
777
778        let output = world.run_system_once(system).unwrap();
779        assert_eq!(output, 1);
780    }
781
782    #[test]
783    fn query_builder_state() {
784        let mut world = World::new();
785
786        world.spawn(A);
787        world.spawn_empty();
788
789        let state = QueryBuilder::new(&mut world).with::<A>().build();
790
791        let system = (state,).build_state(&mut world).build_system(query_system);
792
793        let output = world.run_system_once(system).unwrap();
794        assert_eq!(output, 1);
795    }
796
797    #[test]
798    fn multi_param_builder() {
799        let mut world = World::new();
800
801        world.spawn(A);
802        world.spawn_empty();
803
804        let system = (LocalBuilder(0), ParamBuilder)
805            .build_state(&mut world)
806            .build_system(multi_param_system);
807
808        let output = world.run_system_once(system).unwrap();
809        assert_eq!(output, 1);
810    }
811
812    #[test]
813    fn vec_builder() {
814        let mut world = World::new();
815
816        world.spawn((A, B, C));
817        world.spawn((A, B));
818        world.spawn((A, C));
819        world.spawn((A, C));
820        world.spawn_empty();
821
822        let system = (vec![
823            QueryParamBuilder::new_box(|builder| {
824                builder.with::<B>().without::<C>();
825            }),
826            QueryParamBuilder::new_box(|builder| {
827                builder.with::<C>().without::<B>();
828            }),
829        ],)
830            .build_state(&mut world)
831            .build_system(|params: Vec<Query<&mut A>>| {
832                let mut count: usize = 0;
833                params
834                    .into_iter()
835                    .for_each(|mut query| count += query.iter_mut().count());
836                count
837            });
838
839        let output = world.run_system_once(system).unwrap();
840        assert_eq!(output, 3);
841    }
842
843    #[test]
844    fn multi_param_builder_inference() {
845        let mut world = World::new();
846
847        world.spawn(A);
848        world.spawn_empty();
849
850        let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
851            .build_state(&mut world)
852            .build_system(|a, b| *a + *b + 1);
853
854        let output = world.run_system_once(system).unwrap();
855        assert_eq!(output, 1);
856    }
857
858    #[test]
859    fn param_set_builder() {
860        let mut world = World::new();
861
862        world.spawn((A, B, C));
863        world.spawn((A, B));
864        world.spawn((A, C));
865        world.spawn((A, C));
866        world.spawn_empty();
867
868        let system = (ParamSetBuilder((
869            QueryParamBuilder::new(|builder| {
870                builder.with::<B>();
871            }),
872            QueryParamBuilder::new(|builder| {
873                builder.with::<C>();
874            }),
875        )),)
876            .build_state(&mut world)
877            .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
878                params.p0().iter().count() + params.p1().iter().count()
879            });
880
881        let output = world.run_system_once(system).unwrap();
882        assert_eq!(output, 5);
883    }
884
885    #[test]
886    fn param_set_vec_builder() {
887        let mut world = World::new();
888
889        world.spawn((A, B, C));
890        world.spawn((A, B));
891        world.spawn((A, C));
892        world.spawn((A, C));
893        world.spawn_empty();
894
895        let system = (ParamSetBuilder(vec![
896            QueryParamBuilder::new_box(|builder| {
897                builder.with::<B>();
898            }),
899            QueryParamBuilder::new_box(|builder| {
900                builder.with::<C>();
901            }),
902        ]),)
903            .build_state(&mut world)
904            .build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
905                let mut count = 0;
906                params.for_each(|mut query| count += query.iter_mut().count());
907                count
908            });
909
910        let output = world.run_system_once(system).unwrap();
911        assert_eq!(output, 5);
912    }
913
914    #[test]
915    fn dyn_builder() {
916        let mut world = World::new();
917
918        world.spawn(A);
919        world.spawn_empty();
920
921        let system = (
922            DynParamBuilder::new(LocalBuilder(3_usize)),
923            DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {
924                builder.with::<A>();
925            })),
926            DynParamBuilder::new::<&Entities>(ParamBuilder),
927        )
928            .build_state(&mut world)
929            .build_system(
930                |mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {
931                    let local = *p0.downcast_mut::<Local<usize>>().unwrap();
932                    let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
933                    let _entities = p2.downcast_mut::<&Entities>().unwrap();
934                    assert!(p0.downcast_mut::<Query<()>>().is_none());
935                    local + query_count
936                },
937            );
938
939        let output = world.run_system_once(system).unwrap();
940        assert_eq!(output, 4);
941    }
942
943    #[derive(SystemParam)]
944    #[system_param(builder)]
945    struct CustomParam<'w, 's> {
946        query: Query<'w, 's, ()>,
947        local: Local<'s, usize>,
948    }
949
950    #[test]
951    fn custom_param_builder() {
952        let mut world = World::new();
953
954        world.spawn(A);
955        world.spawn_empty();
956
957        let system = (CustomParamBuilder {
958            local: LocalBuilder(100),
959            query: QueryParamBuilder::new(|builder| {
960                builder.with::<A>();
961            }),
962        },)
963            .build_state(&mut world)
964            .build_system(|param: CustomParam| *param.local + param.query.iter().count());
965
966        let output = world.run_system_once(system).unwrap();
967        assert_eq!(output, 101);
968    }
969
970    #[test]
971    fn filtered_resource_conflicts_read_with_res() {
972        let mut world = World::new();
973        (
974            ParamBuilder::resource(),
975            FilteredResourcesParamBuilder::new(|builder| {
976                builder.add_read::<R>();
977            }),
978        )
979            .build_state(&mut world)
980            .build_system(|_r: Res<R>, _fr: FilteredResources| {});
981    }
982
983    #[test]
984    #[should_panic]
985    fn filtered_resource_conflicts_read_with_resmut() {
986        let mut world = World::new();
987        (
988            ParamBuilder::resource_mut(),
989            FilteredResourcesParamBuilder::new(|builder| {
990                builder.add_read::<R>();
991            }),
992        )
993            .build_state(&mut world)
994            .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
995    }
996
997    #[test]
998    #[should_panic]
999    fn filtered_resource_conflicts_read_all_with_resmut() {
1000        let mut world = World::new();
1001        (
1002            ParamBuilder::resource_mut(),
1003            FilteredResourcesParamBuilder::new(|builder| {
1004                builder.add_read_all();
1005            }),
1006        )
1007            .build_state(&mut world)
1008            .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
1009    }
1010
1011    #[test]
1012    fn filtered_resource_mut_conflicts_read_with_res() {
1013        let mut world = World::new();
1014        (
1015            ParamBuilder::resource(),
1016            FilteredResourcesMutParamBuilder::new(|builder| {
1017                builder.add_read::<R>();
1018            }),
1019        )
1020            .build_state(&mut world)
1021            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1022    }
1023
1024    #[test]
1025    #[should_panic]
1026    fn filtered_resource_mut_conflicts_read_with_resmut() {
1027        let mut world = World::new();
1028        (
1029            ParamBuilder::resource_mut(),
1030            FilteredResourcesMutParamBuilder::new(|builder| {
1031                builder.add_read::<R>();
1032            }),
1033        )
1034            .build_state(&mut world)
1035            .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1036    }
1037
1038    #[test]
1039    #[should_panic]
1040    fn filtered_resource_mut_conflicts_write_with_res() {
1041        let mut world = World::new();
1042        (
1043            ParamBuilder::resource(),
1044            FilteredResourcesMutParamBuilder::new(|builder| {
1045                builder.add_write::<R>();
1046            }),
1047        )
1048            .build_state(&mut world)
1049            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1050    }
1051
1052    #[test]
1053    #[should_panic]
1054    fn filtered_resource_mut_conflicts_write_all_with_res() {
1055        let mut world = World::new();
1056        (
1057            ParamBuilder::resource(),
1058            FilteredResourcesMutParamBuilder::new(|builder| {
1059                builder.add_write_all();
1060            }),
1061        )
1062            .build_state(&mut world)
1063            .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1064    }
1065
1066    #[test]
1067    #[should_panic]
1068    fn filtered_resource_mut_conflicts_write_with_resmut() {
1069        let mut world = World::new();
1070        (
1071            ParamBuilder::resource_mut(),
1072            FilteredResourcesMutParamBuilder::new(|builder| {
1073                builder.add_write::<R>();
1074            }),
1075        )
1076            .build_state(&mut world)
1077            .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1078    }
1079
1080    #[test]
1081    fn filtered_resource_reflect() {
1082        let mut world = World::new();
1083        world.insert_resource(R { foo: 7 });
1084
1085        let system = (FilteredResourcesParamBuilder::new(|builder| {
1086            builder.add_read::<R>();
1087        }),)
1088            .build_state(&mut world)
1089            .build_system(|res: FilteredResources| {
1090                let reflect_resource = <ReflectResource as FromType<R>>::from_type();
1091                let ReflectRef::Struct(reflect_struct) =
1092                    reflect_resource.reflect(res).unwrap().reflect_ref()
1093                else {
1094                    panic!()
1095                };
1096                *reflect_struct
1097                    .field("foo")
1098                    .unwrap()
1099                    .try_downcast_ref::<usize>()
1100                    .unwrap()
1101            });
1102
1103        let output = world.run_system_once(system).unwrap();
1104        assert_eq!(output, 7);
1105    }
1106}