1use bevy_utils::{all_tuples, synccell::SyncCell};
2
3use crate::{
4 prelude::QueryBuilder,
5 query::{QueryData, QueryFilter, QueryState},
6 system::{
7 DynSystemParam, DynSystemParamState, Local, ParamSet, Query, SystemMeta, SystemParam,
8 },
9 world::{
10 FilteredResources, FilteredResourcesBuilder, FilteredResourcesMut,
11 FilteredResourcesMutBuilder, FromWorld, World,
12 },
13};
14use core::fmt::Debug;
15
16use super::{init_query_param, Res, ResMut, Resource, SystemState};
17
18pub unsafe trait SystemParamBuilder<P: SystemParam>: Sized {
113 fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State;
116
117 fn build_state(self, world: &mut World) -> SystemState<P> {
120 SystemState::from_builder(world, self)
121 }
122}
123
124#[derive(Default, Debug, Copy, Clone)]
164pub struct ParamBuilder;
165
166unsafe impl<P: SystemParam> SystemParamBuilder<P> for ParamBuilder {
168 fn build(self, world: &mut World, meta: &mut SystemMeta) -> P::State {
169 P::init_state(world, meta)
170 }
171}
172
173impl ParamBuilder {
174 pub fn of<T: SystemParam>() -> impl SystemParamBuilder<T> {
176 Self
177 }
178
179 pub fn resource<'w, T: Resource>() -> impl SystemParamBuilder<Res<'w, T>> {
181 Self
182 }
183
184 pub fn resource_mut<'w, T: Resource>() -> impl SystemParamBuilder<ResMut<'w, T>> {
186 Self
187 }
188
189 pub fn local<'s, T: FromWorld + Send + 'static>() -> impl SystemParamBuilder<Local<'s, T>> {
191 Self
192 }
193
194 pub fn query<'w, 's, D: QueryData + 'static>() -> impl SystemParamBuilder<Query<'w, 's, D, ()>>
196 {
197 Self
198 }
199
200 pub fn query_filtered<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>(
202 ) -> impl SystemParamBuilder<Query<'w, 's, D, F>> {
203 Self
204 }
205}
206
207unsafe impl<'w, 's, D: QueryData + 'static, F: QueryFilter + 'static>
209 SystemParamBuilder<Query<'w, 's, D, F>> for QueryState<D, F>
210{
211 fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
212 self.validate_world(world.id());
213 init_query_param(world, system_meta, &self);
214 self
215 }
216}
217
218pub struct QueryParamBuilder<T>(T);
259
260impl<T> QueryParamBuilder<T> {
261 pub fn new<D: QueryData, F: QueryFilter>(f: T) -> Self
263 where
264 T: FnOnce(&mut QueryBuilder<D, F>),
265 {
266 Self(f)
267 }
268}
269
270impl<'a, D: QueryData, F: QueryFilter>
271 QueryParamBuilder<Box<dyn FnOnce(&mut QueryBuilder<D, F>) + 'a>>
272{
273 pub fn new_box(f: impl FnOnce(&mut QueryBuilder<D, F>) + 'a) -> Self {
276 Self(Box::new(f))
277 }
278}
279
280unsafe impl<
282 'w,
283 's,
284 D: QueryData + 'static,
285 F: QueryFilter + 'static,
286 T: FnOnce(&mut QueryBuilder<D, F>),
287 > SystemParamBuilder<Query<'w, 's, D, F>> for QueryParamBuilder<T>
288{
289 fn build(self, world: &mut World, system_meta: &mut SystemMeta) -> QueryState<D, F> {
290 let mut builder = QueryBuilder::new(world);
291 (self.0)(&mut builder);
292 let state = builder.build();
293 init_query_param(world, system_meta, &state);
294 state
295 }
296}
297
298macro_rules! impl_system_param_builder_tuple {
299 ($(#[$meta:meta])* $(($param: ident, $builder: ident)),*) => {
300 $(#[$meta])*
301 unsafe impl<$($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<($($param,)*)> for ($($builder,)*) {
303 fn build(self, _world: &mut World, _meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
304 #[allow(non_snake_case)]
305 let ($($builder,)*) = self;
306 #[allow(clippy::unused_unit)]
307 ($($builder.build(_world, _meta),)*)
308 }
309 }
310 };
311}
312
313all_tuples!(
314 #[doc(fake_variadic)]
315 impl_system_param_builder_tuple,
316 0,
317 16,
318 P,
319 B
320);
321
322unsafe impl<P: SystemParam, B: SystemParamBuilder<P>> SystemParamBuilder<Vec<P>> for Vec<B> {
324 fn build(self, world: &mut World, meta: &mut SystemMeta) -> <Vec<P> as SystemParam>::State {
325 self.into_iter()
326 .map(|builder| builder.build(world, meta))
327 .collect()
328 }
329}
330
331pub struct ParamSetBuilder<T>(pub T);
403
404macro_rules! impl_param_set_builder_tuple {
405 ($(($param: ident, $builder: ident, $meta: ident)),*) => {
406 unsafe impl<'w, 's, $($param: SystemParam,)* $($builder: SystemParamBuilder<$param>,)*> SystemParamBuilder<ParamSet<'w, 's, ($($param,)*)>> for ParamSetBuilder<($($builder,)*)> {
408 #[allow(non_snake_case)]
409 fn build(self, _world: &mut World, _system_meta: &mut SystemMeta) -> <($($param,)*) as SystemParam>::State {
410 let ParamSetBuilder(($($builder,)*)) = self;
411 $(
417 let mut $meta = _system_meta.clone();
418 let $param = $builder.build(_world, &mut $meta);
419 )*
420 if false $(|| !$meta.is_send())* {
422 _system_meta.set_non_send();
423 }
424 $(
425 _system_meta
426 .component_access_set
427 .extend($meta.component_access_set);
428 _system_meta
429 .archetype_component_access
430 .extend(&$meta.archetype_component_access);
431 )*
432 #[allow(clippy::unused_unit)]
433 ($($param,)*)
434 }
435 }
436 };
437}
438
439all_tuples!(impl_param_set_builder_tuple, 1, 8, P, B, meta);
440
441unsafe impl<'w, 's, P: SystemParam, B: SystemParamBuilder<P>>
444 SystemParamBuilder<ParamSet<'w, 's, Vec<P>>> for ParamSetBuilder<Vec<B>>
445{
446 fn build(
447 self,
448 world: &mut World,
449 system_meta: &mut SystemMeta,
450 ) -> <Vec<P> as SystemParam>::State {
451 let mut states = Vec::with_capacity(self.0.len());
452 let mut metas = Vec::with_capacity(self.0.len());
453 for builder in self.0 {
454 let mut meta = system_meta.clone();
455 states.push(builder.build(world, &mut meta));
456 metas.push(meta);
457 }
458 if metas.iter().any(|m| !m.is_send()) {
459 system_meta.set_non_send();
460 }
461 for meta in metas {
462 system_meta
463 .component_access_set
464 .extend(meta.component_access_set);
465 system_meta
466 .archetype_component_access
467 .extend(&meta.archetype_component_access);
468 }
469 states
470 }
471}
472
473pub struct DynParamBuilder<'a>(
476 Box<dyn FnOnce(&mut World, &mut SystemMeta) -> DynSystemParamState + 'a>,
477);
478
479impl<'a> DynParamBuilder<'a> {
480 pub fn new<T: SystemParam + 'static>(builder: impl SystemParamBuilder<T> + 'a) -> Self {
483 Self(Box::new(|world, meta| {
484 DynSystemParamState::new::<T>(builder.build(world, meta))
485 }))
486 }
487}
488
489unsafe impl<'a, 'w, 's> SystemParamBuilder<DynSystemParam<'w, 's>> for DynParamBuilder<'a> {
493 fn build(
494 self,
495 world: &mut World,
496 meta: &mut SystemMeta,
497 ) -> <DynSystemParam<'w, 's> as SystemParam>::State {
498 (self.0)(world, meta)
499 }
500}
501
502pub struct LocalBuilder<T>(pub T);
522
523unsafe impl<'s, T: FromWorld + Send + 'static> SystemParamBuilder<Local<'s, T>>
525 for LocalBuilder<T>
526{
527 fn build(
528 self,
529 _world: &mut World,
530 _meta: &mut SystemMeta,
531 ) -> <Local<'s, T> as SystemParam>::State {
532 SyncCell::new(self.0)
533 }
534}
535
536pub struct FilteredResourcesParamBuilder<T>(T);
539
540impl<T> FilteredResourcesParamBuilder<T> {
541 pub fn new(f: T) -> Self
543 where
544 T: FnOnce(&mut FilteredResourcesBuilder),
545 {
546 Self(f)
547 }
548}
549
550impl<'a> FilteredResourcesParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesBuilder) + 'a>> {
551 pub fn new_box(f: impl FnOnce(&mut FilteredResourcesBuilder) + 'a) -> Self {
554 Self(Box::new(f))
555 }
556}
557
558unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesBuilder)>
561 SystemParamBuilder<FilteredResources<'w, 's>> for FilteredResourcesParamBuilder<T>
562{
563 fn build(
564 self,
565 world: &mut World,
566 meta: &mut SystemMeta,
567 ) -> <FilteredResources<'w, 's> as SystemParam>::State {
568 let mut builder = FilteredResourcesBuilder::new(world);
569 (self.0)(&mut builder);
570 let access = builder.build();
571
572 let combined_access = meta.component_access_set.combined_access();
573 let conflicts = combined_access.get_conflicts(&access);
574 if !conflicts.is_empty() {
575 let accesses = conflicts.format_conflict_list(world);
576 let system_name = &meta.name;
577 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");
578 }
579
580 if access.has_read_all_resources() {
581 meta.component_access_set
582 .add_unfiltered_read_all_resources();
583 meta.archetype_component_access.read_all_resources();
584 } else {
585 for component_id in access.resource_reads_and_writes() {
586 meta.component_access_set
587 .add_unfiltered_resource_read(component_id);
588
589 let archetype_component_id = world.initialize_resource_internal(component_id).id();
590 meta.archetype_component_access
591 .add_resource_read(archetype_component_id);
592 }
593 }
594
595 access
596 }
597}
598
599pub struct FilteredResourcesMutParamBuilder<T>(T);
602
603impl<T> FilteredResourcesMutParamBuilder<T> {
604 pub fn new(f: T) -> Self
606 where
607 T: FnOnce(&mut FilteredResourcesMutBuilder),
608 {
609 Self(f)
610 }
611}
612
613impl<'a> FilteredResourcesMutParamBuilder<Box<dyn FnOnce(&mut FilteredResourcesMutBuilder) + 'a>> {
614 pub fn new_box(f: impl FnOnce(&mut FilteredResourcesMutBuilder) + 'a) -> Self {
617 Self(Box::new(f))
618 }
619}
620
621unsafe impl<'w, 's, T: FnOnce(&mut FilteredResourcesMutBuilder)>
624 SystemParamBuilder<FilteredResourcesMut<'w, 's>> for FilteredResourcesMutParamBuilder<T>
625{
626 fn build(
627 self,
628 world: &mut World,
629 meta: &mut SystemMeta,
630 ) -> <FilteredResourcesMut<'w, 's> as SystemParam>::State {
631 let mut builder = FilteredResourcesMutBuilder::new(world);
632 (self.0)(&mut builder);
633 let access = builder.build();
634
635 let combined_access = meta.component_access_set.combined_access();
636 let conflicts = combined_access.get_conflicts(&access);
637 if !conflicts.is_empty() {
638 let accesses = conflicts.format_conflict_list(world);
639 let system_name = &meta.name;
640 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");
641 }
642
643 if access.has_read_all_resources() {
644 meta.component_access_set
645 .add_unfiltered_read_all_resources();
646 meta.archetype_component_access.read_all_resources();
647 } else {
648 for component_id in access.resource_reads() {
649 meta.component_access_set
650 .add_unfiltered_resource_read(component_id);
651
652 let archetype_component_id = world.initialize_resource_internal(component_id).id();
653 meta.archetype_component_access
654 .add_resource_read(archetype_component_id);
655 }
656 }
657
658 if access.has_write_all_resources() {
659 meta.component_access_set
660 .add_unfiltered_write_all_resources();
661 meta.archetype_component_access.write_all_resources();
662 } else {
663 for component_id in access.resource_writes() {
664 meta.component_access_set
665 .add_unfiltered_resource_write(component_id);
666
667 let archetype_component_id = world.initialize_resource_internal(component_id).id();
668 meta.archetype_component_access
669 .add_resource_write(archetype_component_id);
670 }
671 }
672
673 access
674 }
675}
676
677#[cfg(test)]
678mod tests {
679 use crate as bevy_ecs;
680 use crate::{
681 entity::Entities,
682 prelude::{Component, Query},
683 system::{Local, RunSystemOnce},
684 };
685
686 use super::*;
687
688 #[derive(Component)]
689 struct A;
690
691 #[derive(Component)]
692 struct B;
693
694 #[derive(Component)]
695 struct C;
696
697 #[derive(Resource, Default)]
698 struct R;
699
700 fn local_system(local: Local<u64>) -> u64 {
701 *local
702 }
703
704 fn query_system(query: Query<()>) -> usize {
705 query.iter().count()
706 }
707
708 fn multi_param_system(a: Local<u64>, b: Local<u64>) -> u64 {
709 *a + *b + 1
710 }
711
712 #[test]
713 fn local_builder() {
714 let mut world = World::new();
715
716 let system = (LocalBuilder(10),)
717 .build_state(&mut world)
718 .build_system(local_system);
719
720 let output = world.run_system_once(system).unwrap();
721 assert_eq!(output, 10);
722 }
723
724 #[test]
725 fn query_builder() {
726 let mut world = World::new();
727
728 world.spawn(A);
729 world.spawn_empty();
730
731 let system = (QueryParamBuilder::new(|query| {
732 query.with::<A>();
733 }),)
734 .build_state(&mut world)
735 .build_system(query_system);
736
737 let output = world.run_system_once(system).unwrap();
738 assert_eq!(output, 1);
739 }
740
741 #[test]
742 fn query_builder_state() {
743 let mut world = World::new();
744
745 world.spawn(A);
746 world.spawn_empty();
747
748 let state = QueryBuilder::new(&mut world).with::<A>().build();
749
750 let system = (state,).build_state(&mut world).build_system(query_system);
751
752 let output = world.run_system_once(system).unwrap();
753 assert_eq!(output, 1);
754 }
755
756 #[test]
757 fn multi_param_builder() {
758 let mut world = World::new();
759
760 world.spawn(A);
761 world.spawn_empty();
762
763 let system = (LocalBuilder(0), ParamBuilder)
764 .build_state(&mut world)
765 .build_system(multi_param_system);
766
767 let output = world.run_system_once(system).unwrap();
768 assert_eq!(output, 1);
769 }
770
771 #[test]
772 fn vec_builder() {
773 let mut world = World::new();
774
775 world.spawn((A, B, C));
776 world.spawn((A, B));
777 world.spawn((A, C));
778 world.spawn((A, C));
779 world.spawn_empty();
780
781 let system = (vec![
782 QueryParamBuilder::new_box(|builder| {
783 builder.with::<B>().without::<C>();
784 }),
785 QueryParamBuilder::new_box(|builder| {
786 builder.with::<C>().without::<B>();
787 }),
788 ],)
789 .build_state(&mut world)
790 .build_system(|params: Vec<Query<&mut A>>| {
791 let mut count: usize = 0;
792 params
793 .into_iter()
794 .for_each(|mut query| count += query.iter_mut().count());
795 count
796 });
797
798 let output = world.run_system_once(system).unwrap();
799 assert_eq!(output, 3);
800 }
801
802 #[test]
803 fn multi_param_builder_inference() {
804 let mut world = World::new();
805
806 world.spawn(A);
807 world.spawn_empty();
808
809 let system = (LocalBuilder(0u64), ParamBuilder::local::<u64>())
810 .build_state(&mut world)
811 .build_system(|a, b| *a + *b + 1);
812
813 let output = world.run_system_once(system).unwrap();
814 assert_eq!(output, 1);
815 }
816
817 #[test]
818 fn param_set_builder() {
819 let mut world = World::new();
820
821 world.spawn((A, B, C));
822 world.spawn((A, B));
823 world.spawn((A, C));
824 world.spawn((A, C));
825 world.spawn_empty();
826
827 let system = (ParamSetBuilder((
828 QueryParamBuilder::new(|builder| {
829 builder.with::<B>();
830 }),
831 QueryParamBuilder::new(|builder| {
832 builder.with::<C>();
833 }),
834 )),)
835 .build_state(&mut world)
836 .build_system(|mut params: ParamSet<(Query<&mut A>, Query<&mut A>)>| {
837 params.p0().iter().count() + params.p1().iter().count()
838 });
839
840 let output = world.run_system_once(system).unwrap();
841 assert_eq!(output, 5);
842 }
843
844 #[test]
845 fn param_set_vec_builder() {
846 let mut world = World::new();
847
848 world.spawn((A, B, C));
849 world.spawn((A, B));
850 world.spawn((A, C));
851 world.spawn((A, C));
852 world.spawn_empty();
853
854 let system = (ParamSetBuilder(vec![
855 QueryParamBuilder::new_box(|builder| {
856 builder.with::<B>();
857 }),
858 QueryParamBuilder::new_box(|builder| {
859 builder.with::<C>();
860 }),
861 ]),)
862 .build_state(&mut world)
863 .build_system(|mut params: ParamSet<Vec<Query<&mut A>>>| {
864 let mut count = 0;
865 params.for_each(|mut query| count += query.iter_mut().count());
866 count
867 });
868
869 let output = world.run_system_once(system).unwrap();
870 assert_eq!(output, 5);
871 }
872
873 #[test]
874 fn dyn_builder() {
875 let mut world = World::new();
876
877 world.spawn(A);
878 world.spawn_empty();
879
880 let system = (
881 DynParamBuilder::new(LocalBuilder(3_usize)),
882 DynParamBuilder::new::<Query<()>>(QueryParamBuilder::new(|builder| {
883 builder.with::<A>();
884 })),
885 DynParamBuilder::new::<&Entities>(ParamBuilder),
886 )
887 .build_state(&mut world)
888 .build_system(
889 |mut p0: DynSystemParam, mut p1: DynSystemParam, mut p2: DynSystemParam| {
890 let local = *p0.downcast_mut::<Local<usize>>().unwrap();
891 let query_count = p1.downcast_mut::<Query<()>>().unwrap().iter().count();
892 let _entities = p2.downcast_mut::<&Entities>().unwrap();
893 assert!(p0.downcast_mut::<Query<()>>().is_none());
894 local + query_count
895 },
896 );
897
898 let output = world.run_system_once(system).unwrap();
899 assert_eq!(output, 4);
900 }
901
902 #[derive(SystemParam)]
903 #[system_param(builder)]
904 struct CustomParam<'w, 's> {
905 query: Query<'w, 's, ()>,
906 local: Local<'s, usize>,
907 }
908
909 #[test]
910 fn custom_param_builder() {
911 let mut world = World::new();
912
913 world.spawn(A);
914 world.spawn_empty();
915
916 let system = (CustomParamBuilder {
917 local: LocalBuilder(100),
918 query: QueryParamBuilder::new(|builder| {
919 builder.with::<A>();
920 }),
921 },)
922 .build_state(&mut world)
923 .build_system(|param: CustomParam| *param.local + param.query.iter().count());
924
925 let output = world.run_system_once(system).unwrap();
926 assert_eq!(output, 101);
927 }
928
929 #[test]
930 fn filtered_resource_conflicts_read_with_res() {
931 let mut world = World::new();
932 (
933 ParamBuilder::resource(),
934 FilteredResourcesParamBuilder::new(|builder| {
935 builder.add_read::<R>();
936 }),
937 )
938 .build_state(&mut world)
939 .build_system(|_r: Res<R>, _fr: FilteredResources| {});
940 }
941
942 #[test]
943 #[should_panic]
944 fn filtered_resource_conflicts_read_with_resmut() {
945 let mut world = World::new();
946 (
947 ParamBuilder::resource_mut(),
948 FilteredResourcesParamBuilder::new(|builder| {
949 builder.add_read::<R>();
950 }),
951 )
952 .build_state(&mut world)
953 .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
954 }
955
956 #[test]
957 #[should_panic]
958 fn filtered_resource_conflicts_read_all_with_resmut() {
959 let mut world = World::new();
960 (
961 ParamBuilder::resource_mut(),
962 FilteredResourcesParamBuilder::new(|builder| {
963 builder.add_read_all();
964 }),
965 )
966 .build_state(&mut world)
967 .build_system(|_r: ResMut<R>, _fr: FilteredResources| {});
968 }
969
970 #[test]
971 fn filtered_resource_mut_conflicts_read_with_res() {
972 let mut world = World::new();
973 (
974 ParamBuilder::resource(),
975 FilteredResourcesMutParamBuilder::new(|builder| {
976 builder.add_read::<R>();
977 }),
978 )
979 .build_state(&mut world)
980 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
981 }
982
983 #[test]
984 #[should_panic]
985 fn filtered_resource_mut_conflicts_read_with_resmut() {
986 let mut world = World::new();
987 (
988 ParamBuilder::resource_mut(),
989 FilteredResourcesMutParamBuilder::new(|builder| {
990 builder.add_read::<R>();
991 }),
992 )
993 .build_state(&mut world)
994 .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
995 }
996
997 #[test]
998 #[should_panic]
999 fn filtered_resource_mut_conflicts_write_with_res() {
1000 let mut world = World::new();
1001 (
1002 ParamBuilder::resource(),
1003 FilteredResourcesMutParamBuilder::new(|builder| {
1004 builder.add_write::<R>();
1005 }),
1006 )
1007 .build_state(&mut world)
1008 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1009 }
1010
1011 #[test]
1012 #[should_panic]
1013 fn filtered_resource_mut_conflicts_write_all_with_res() {
1014 let mut world = World::new();
1015 (
1016 ParamBuilder::resource(),
1017 FilteredResourcesMutParamBuilder::new(|builder| {
1018 builder.add_write_all();
1019 }),
1020 )
1021 .build_state(&mut world)
1022 .build_system(|_r: Res<R>, _fr: FilteredResourcesMut| {});
1023 }
1024
1025 #[test]
1026 #[should_panic]
1027 fn filtered_resource_mut_conflicts_write_with_resmut() {
1028 let mut world = World::new();
1029 (
1030 ParamBuilder::resource_mut(),
1031 FilteredResourcesMutParamBuilder::new(|builder| {
1032 builder.add_write::<R>();
1033 }),
1034 )
1035 .build_state(&mut world)
1036 .build_system(|_r: ResMut<R>, _fr: FilteredResourcesMut| {});
1037 }
1038}