1#[cfg(feature = "bevy_reflect")]
2use crate::reflect::ReflectComponent;
3use crate::{
4 self as bevy_ecs,
5 bundle::Bundle,
6 change_detection::Mut,
7 entity::Entity,
8 system::{input::SystemInput, BoxedSystem, IntoSystem, System},
9 world::{Command, World},
10};
11use bevy_ecs_macros::{Component, Resource};
12#[cfg(feature = "bevy_reflect")]
13use bevy_reflect::Reflect;
14use core::marker::PhantomData;
15use derive_more::derive::{Display, Error};
16
17#[derive(Component)]
19struct RegisteredSystem<I, O> {
20 initialized: bool,
21 system: BoxedSystem<I, O>,
22}
23
24#[derive(Component)]
26#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
27#[cfg_attr(feature = "bevy_reflect", reflect(Component))]
28pub struct SystemIdMarker;
29
30pub struct RemovedSystem<I = (), O = ()> {
35 initialized: bool,
36 system: BoxedSystem<I, O>,
37}
38
39impl<I, O> RemovedSystem<I, O> {
40 pub fn initialized(&self) -> bool {
43 self.initialized
44 }
45
46 pub fn system(self) -> BoxedSystem<I, O> {
48 self.system
49 }
50}
51
52pub struct SystemId<I: SystemInput = (), O = ()> {
57 pub(crate) entity: Entity,
58 pub(crate) marker: PhantomData<fn(I) -> O>,
59}
60
61impl<I: SystemInput, O> SystemId<I, O> {
62 pub fn entity(self) -> Entity {
69 self.entity
70 }
71
72 pub fn from_entity(entity: Entity) -> Self {
77 Self {
78 entity,
79 marker: PhantomData,
80 }
81 }
82}
83
84impl<I: SystemInput, O> Eq for SystemId<I, O> {}
85
86impl<I: SystemInput, O> Copy for SystemId<I, O> {}
88
89impl<I: SystemInput, O> Clone for SystemId<I, O> {
91 fn clone(&self) -> Self {
92 *self
93 }
94}
95
96impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
98 fn eq(&self, other: &Self) -> bool {
99 self.entity == other.entity && self.marker == other.marker
100 }
101}
102
103impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
105 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
106 self.entity.hash(state);
107 }
108}
109
110impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
111 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112 f.debug_tuple("SystemId").field(&self.entity).finish()
113 }
114}
115
116#[derive(Resource)]
120pub struct CachedSystemId<S: System>(pub SystemId<S::In, S::Out>);
121
122fn system_bundle<I: 'static, O: 'static>(system: BoxedSystem<I, O>) -> impl Bundle {
124 (
125 RegisteredSystem {
126 initialized: false,
127 system,
128 },
129 SystemIdMarker,
130 )
131}
132
133impl World {
134 pub fn register_system<I, O, M>(
146 &mut self,
147 system: impl IntoSystem<I, O, M> + 'static,
148 ) -> SystemId<I, O>
149 where
150 I: SystemInput + 'static,
151 O: 'static,
152 {
153 self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
154 }
155
156 pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
161 where
162 I: SystemInput + 'static,
163 O: 'static,
164 {
165 let entity = self.spawn(system_bundle(system)).id();
166 SystemId::from_entity(entity)
167 }
168
169 pub fn unregister_system<I, O>(
176 &mut self,
177 id: SystemId<I, O>,
178 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
179 where
180 I: SystemInput + 'static,
181 O: 'static,
182 {
183 match self.get_entity_mut(id.entity) {
184 Ok(mut entity) => {
185 let registered_system = entity
186 .take::<RegisteredSystem<I, O>>()
187 .ok_or(RegisteredSystemError::SelfRemove(id))?;
188 entity.despawn();
189 Ok(RemovedSystem {
190 initialized: registered_system.initialized,
191 system: registered_system.system,
192 })
193 }
194 Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
195 }
196 }
197
198 pub fn run_system<O: 'static>(
286 &mut self,
287 id: SystemId<(), O>,
288 ) -> Result<O, RegisteredSystemError<(), O>> {
289 self.run_system_with_input(id, ())
290 }
291
292 pub fn run_system_with_input<I, O>(
319 &mut self,
320 id: SystemId<I, O>,
321 input: I::Inner<'_>,
322 ) -> Result<O, RegisteredSystemError<I, O>>
323 where
324 I: SystemInput + 'static,
325 O: 'static,
326 {
327 let mut entity = self
329 .get_entity_mut(id.entity)
330 .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
331
332 let RegisteredSystem {
334 mut initialized,
335 mut system,
336 } = entity
337 .take::<RegisteredSystem<I, O>>()
338 .ok_or(RegisteredSystemError::Recursive(id))?;
339
340 if !initialized {
342 system.initialize(self);
343 initialized = true;
344 }
345
346 let result = if system.validate_param(self) {
347 Ok(system.run(input, self))
348 } else {
349 Err(RegisteredSystemError::InvalidParams(id))
350 };
351
352 if let Ok(mut entity) = self.get_entity_mut(id.entity) {
354 entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
355 initialized,
356 system,
357 });
358 }
359 result
360 }
361
362 pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
382 where
383 I: SystemInput + 'static,
384 O: 'static,
385 S: IntoSystem<I, O, M> + 'static,
386 {
387 const {
388 assert!(
389 size_of::<S>() == 0,
390 "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
391 );
392 }
393
394 if !self.contains_resource::<CachedSystemId<S::System>>() {
395 let id = self.register_system(system);
396 self.insert_resource(CachedSystemId::<S::System>(id));
397 return id;
398 }
399
400 self.resource_scope(|world, mut id: Mut<CachedSystemId<S::System>>| {
401 if let Ok(mut entity) = world.get_entity_mut(id.0.entity()) {
402 if !entity.contains::<RegisteredSystem<I, O>>() {
403 entity.insert(system_bundle(Box::new(IntoSystem::into_system(system))));
404 }
405 } else {
406 id.0 = world.register_system(system);
407 }
408 id.0
409 })
410 }
411
412 pub fn unregister_system_cached<I, O, M, S>(
416 &mut self,
417 _system: S,
418 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
419 where
420 I: SystemInput + 'static,
421 O: 'static,
422 S: IntoSystem<I, O, M> + 'static,
423 {
424 let id = self
425 .remove_resource::<CachedSystemId<S::System>>()
426 .ok_or(RegisteredSystemError::SystemNotCached)?;
427 self.unregister_system(id.0)
428 }
429
430 pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
434 &mut self,
435 system: S,
436 ) -> Result<O, RegisteredSystemError<(), O>> {
437 self.run_system_cached_with(system, ())
438 }
439
440 pub fn run_system_cached_with<I, O, M, S>(
444 &mut self,
445 system: S,
446 input: I::Inner<'_>,
447 ) -> Result<O, RegisteredSystemError<I, O>>
448 where
449 I: SystemInput + 'static,
450 O: 'static,
451 S: IntoSystem<I, O, M> + 'static,
452 {
453 let id = self.register_system_cached(system);
454 self.run_system_with_input(id, input)
455 }
456}
457
458#[derive(Debug, Clone)]
470pub struct RunSystemWithInput<I: SystemInput + 'static> {
471 system_id: SystemId<I>,
472 input: I::Inner<'static>,
473}
474
475pub type RunSystem = RunSystemWithInput<()>;
487
488impl RunSystem {
489 pub fn new(system_id: SystemId) -> Self {
491 Self::new_with_input(system_id, ())
492 }
493}
494
495impl<I: SystemInput + 'static> RunSystemWithInput<I> {
496 pub fn new_with_input(system_id: SystemId<I>, input: I::Inner<'static>) -> Self {
499 Self { system_id, input }
500 }
501}
502
503impl<I> Command for RunSystemWithInput<I>
504where
505 I: SystemInput<Inner<'static>: Send> + 'static,
506{
507 #[inline]
508 fn apply(self, world: &mut World) {
509 _ = world.run_system_with_input(self.system_id, self.input);
510 }
511}
512
513pub struct RegisterSystem<I: SystemInput + 'static, O: 'static> {
517 system: BoxedSystem<I, O>,
518 entity: Entity,
519}
520
521impl<I, O> RegisterSystem<I, O>
522where
523 I: SystemInput + 'static,
524 O: 'static,
525{
526 pub fn new<M, S: IntoSystem<I, O, M> + 'static>(system: S, entity: Entity) -> Self {
528 Self {
529 system: Box::new(IntoSystem::into_system(system)),
530 entity,
531 }
532 }
533}
534
535impl<I, O> Command for RegisterSystem<I, O>
536where
537 I: SystemInput + Send + 'static,
538 O: Send + 'static,
539{
540 fn apply(self, world: &mut World) {
541 if let Ok(mut entity) = world.get_entity_mut(self.entity) {
542 entity.insert(system_bundle(self.system));
543 }
544 }
545}
546
547pub struct UnregisterSystem<I: SystemInput + 'static, O: 'static> {
549 system_id: SystemId<I, O>,
550}
551
552impl<I, O> UnregisterSystem<I, O>
553where
554 I: SystemInput + 'static,
555 O: 'static,
556{
557 pub fn new(system_id: SystemId<I, O>) -> Self {
559 Self { system_id }
560 }
561}
562
563impl<I, O> Command for UnregisterSystem<I, O>
564where
565 I: SystemInput + 'static,
566 O: 'static,
567{
568 fn apply(self, world: &mut World) {
569 let _ = world.unregister_system(self.system_id);
570 }
571}
572
573pub struct RunSystemCachedWith<S, I, O, M>
578where
579 I: SystemInput,
580 S: IntoSystem<I, O, M>,
581{
582 system: S,
583 input: I::Inner<'static>,
584 _phantom: PhantomData<(fn() -> O, fn() -> M)>,
585}
586
587impl<S, I, O, M> RunSystemCachedWith<S, I, O, M>
588where
589 I: SystemInput,
590 S: IntoSystem<I, O, M>,
591{
592 pub fn new(system: S, input: I::Inner<'static>) -> Self {
595 Self {
596 system,
597 input,
598 _phantom: PhantomData,
599 }
600 }
601}
602
603impl<S, I, O, M> Command for RunSystemCachedWith<S, I, O, M>
604where
605 I: SystemInput<Inner<'static>: Send> + Send + 'static,
606 O: Send + 'static,
607 S: IntoSystem<I, O, M> + Send + 'static,
608 M: 'static,
609{
610 fn apply(self, world: &mut World) {
611 let _ = world.run_system_cached_with(self.system, self.input);
612 }
613}
614
615#[derive(Error, Display)]
617pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
618 #[display("System {_0:?} was not registered")]
622 SystemIdNotRegistered(SystemId<I, O>),
623 #[display("Cached system was not found")]
627 SystemNotCached,
628 #[display("System {_0:?} tried to run itself recursively")]
630 Recursive(SystemId<I, O>),
631 #[display("System {_0:?} tried to remove itself")]
633 SelfRemove(SystemId<I, O>),
634 #[display("The data required by the system {_0:?} was not found in the world and the system did not run due to failed parameter validation.")]
638 InvalidParams(SystemId<I, O>),
639}
640
641impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
642 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
643 match self {
644 Self::SystemIdNotRegistered(arg0) => {
645 f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
646 }
647 Self::SystemNotCached => write!(f, "SystemNotCached"),
648 Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
649 Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
650 Self::InvalidParams(arg0) => f.debug_tuple("InvalidParams").field(arg0).finish(),
651 }
652 }
653}
654
655mod tests {
656 use crate::prelude::*;
657 use crate::{self as bevy_ecs};
658
659 #[derive(Resource, Default, PartialEq, Debug)]
660 struct Counter(u8);
661
662 #[test]
663 fn change_detection() {
664 #[derive(Resource, Default)]
665 struct ChangeDetector;
666
667 fn count_up_iff_changed(
668 mut counter: ResMut<Counter>,
669 change_detector: ResMut<ChangeDetector>,
670 ) {
671 if change_detector.is_changed() {
672 counter.0 += 1;
673 }
674 }
675
676 let mut world = World::new();
677 world.init_resource::<ChangeDetector>();
678 world.init_resource::<Counter>();
679 assert_eq!(*world.resource::<Counter>(), Counter(0));
680 let id = world.register_system(count_up_iff_changed);
682 world.run_system(id).expect("system runs successfully");
683 assert_eq!(*world.resource::<Counter>(), Counter(1));
684 world.run_system(id).expect("system runs successfully");
686 assert_eq!(*world.resource::<Counter>(), Counter(1));
687 world.resource_mut::<ChangeDetector>().set_changed();
689 world.run_system(id).expect("system runs successfully");
690 assert_eq!(*world.resource::<Counter>(), Counter(2));
691 }
692
693 #[test]
694 fn local_variables() {
695 fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
697 counter.0 += last_counter.0 .0;
698 last_counter.0 .0 = counter.0;
699 }
700
701 let mut world = World::new();
702 world.insert_resource(Counter(1));
703 assert_eq!(*world.resource::<Counter>(), Counter(1));
704 let id = world.register_system(doubling);
705 world.run_system(id).expect("system runs successfully");
706 assert_eq!(*world.resource::<Counter>(), Counter(1));
707 world.run_system(id).expect("system runs successfully");
708 assert_eq!(*world.resource::<Counter>(), Counter(2));
709 world.run_system(id).expect("system runs successfully");
710 assert_eq!(*world.resource::<Counter>(), Counter(4));
711 world.run_system(id).expect("system runs successfully");
712 assert_eq!(*world.resource::<Counter>(), Counter(8));
713 }
714
715 #[test]
716 fn input_values() {
717 struct NonCopy(u8);
719
720 fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
721 counter.0 += increment_by;
722 }
723
724 let mut world = World::new();
725
726 let id = world.register_system(increment_sys);
727
728 world.insert_resource(Counter(1));
730 assert_eq!(*world.resource::<Counter>(), Counter(1));
731
732 world
733 .run_system_with_input(id, NonCopy(1))
734 .expect("system runs successfully");
735 assert_eq!(*world.resource::<Counter>(), Counter(2));
736
737 world
738 .run_system_with_input(id, NonCopy(1))
739 .expect("system runs successfully");
740 assert_eq!(*world.resource::<Counter>(), Counter(3));
741
742 world
743 .run_system_with_input(id, NonCopy(20))
744 .expect("system runs successfully");
745 assert_eq!(*world.resource::<Counter>(), Counter(23));
746
747 world
748 .run_system_with_input(id, NonCopy(1))
749 .expect("system runs successfully");
750 assert_eq!(*world.resource::<Counter>(), Counter(24));
751 }
752
753 #[test]
754 fn output_values() {
755 #[derive(Eq, PartialEq, Debug)]
757 struct NonCopy(u8);
758
759 fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
760 counter.0 += 1;
761 NonCopy(counter.0)
762 }
763
764 let mut world = World::new();
765
766 let id = world.register_system(increment_sys);
767
768 world.insert_resource(Counter(1));
770 assert_eq!(*world.resource::<Counter>(), Counter(1));
771
772 let output = world.run_system(id).expect("system runs successfully");
773 assert_eq!(*world.resource::<Counter>(), Counter(2));
774 assert_eq!(output, NonCopy(2));
775
776 let output = world.run_system(id).expect("system runs successfully");
777 assert_eq!(*world.resource::<Counter>(), Counter(3));
778 assert_eq!(output, NonCopy(3));
779 }
780
781 #[test]
782 fn exclusive_system() {
783 let mut world = World::new();
784 let exclusive_system_id = world.register_system(|world: &mut World| {
785 world.spawn_empty();
786 });
787 let entity_count = world.entities.len();
788 let _ = world.run_system(exclusive_system_id);
789 assert_eq!(world.entities.len(), entity_count + 1);
790 }
791
792 #[test]
793 fn nested_systems() {
794 use crate::system::SystemId;
795
796 #[derive(Component)]
797 struct Callback(SystemId);
798
799 fn nested(query: Query<&Callback>, mut commands: Commands) {
800 for callback in query.iter() {
801 commands.run_system(callback.0);
802 }
803 }
804
805 let mut world = World::new();
806 world.insert_resource(Counter(0));
807
808 let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
809 counter.0 += 2;
810 });
811 let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
812 counter.0 += 3;
813 });
814 let nested_id = world.register_system(nested);
815
816 world.spawn(Callback(increment_two));
817 world.spawn(Callback(increment_three));
818 let _ = world.run_system(nested_id);
819 assert_eq!(*world.resource::<Counter>(), Counter(5));
820 }
821
822 #[test]
823 fn nested_systems_with_inputs() {
824 use crate::system::SystemId;
825
826 #[derive(Component)]
827 struct Callback(SystemId<In<u8>>, u8);
828
829 fn nested(query: Query<&Callback>, mut commands: Commands) {
830 for callback in query.iter() {
831 commands.run_system_with_input(callback.0, callback.1);
832 }
833 }
834
835 let mut world = World::new();
836 world.insert_resource(Counter(0));
837
838 let increment_by =
839 world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
840 counter.0 += amt;
841 });
842 let nested_id = world.register_system(nested);
843
844 world.spawn(Callback(increment_by, 2));
845 world.spawn(Callback(increment_by, 3));
846 let _ = world.run_system(nested_id);
847 assert_eq!(*world.resource::<Counter>(), Counter(5));
848 }
849
850 #[test]
851 fn cached_system() {
852 use crate::system::RegisteredSystemError;
853
854 fn four() -> i32 {
855 4
856 }
857
858 let mut world = World::new();
859 let old = world.register_system_cached(four);
860 let new = world.register_system_cached(four);
861 assert_eq!(old, new);
862
863 let result = world.unregister_system_cached(four);
864 assert!(result.is_ok());
865 let new = world.register_system_cached(four);
866 assert_ne!(old, new);
867
868 let output = world.run_system(old);
869 assert!(matches!(
870 output,
871 Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
872 ));
873 let output = world.run_system(new);
874 assert!(matches!(output, Ok(x) if x == four()));
875 let output = world.run_system_cached(four);
876 assert!(matches!(output, Ok(x) if x == four()));
877 let output = world.run_system_cached_with(four, ());
878 assert!(matches!(output, Ok(x) if x == four()));
879 }
880
881 #[test]
882 fn cached_system_commands() {
883 fn sys(mut counter: ResMut<Counter>) {
884 counter.0 = 1;
885 }
886
887 let mut world = World::new();
888 world.insert_resource(Counter(0));
889
890 world.commands().run_system_cached(sys);
891 world.flush_commands();
892
893 assert_eq!(world.resource::<Counter>().0, 1);
894 }
895
896 #[test]
897 fn cached_system_adapters() {
898 fn four() -> i32 {
899 4
900 }
901
902 fn double(In(i): In<i32>) -> i32 {
903 i * 2
904 }
905
906 let mut world = World::new();
907
908 let output = world.run_system_cached(four.pipe(double));
909 assert!(matches!(output, Ok(8)));
910
911 let output = world.run_system_cached(four.map(|i| i * 2));
912 assert!(matches!(output, Ok(8)));
913 }
914
915 #[test]
916 fn system_with_input_ref() {
917 fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
918 counter.0 += *input;
919 }
920
921 let mut world = World::new();
922 world.insert_resource(Counter(0));
923
924 let id = world.register_system(with_ref);
925 world.run_system_with_input(id, &2).unwrap();
926 assert_eq!(*world.resource::<Counter>(), Counter(2));
927 }
928
929 #[test]
930 fn system_with_input_mut() {
931 #[derive(Event)]
932 struct MyEvent {
933 cancelled: bool,
934 }
935
936 fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
937 if counter.0 > 0 {
938 event.cancelled = true;
939 }
940 }
941
942 let mut world = World::new();
943 world.insert_resource(Counter(0));
944 let post_system = world.register_system(post);
945
946 let mut event = MyEvent { cancelled: false };
947 world
948 .run_system_with_input(post_system, &mut event)
949 .unwrap();
950 assert!(!event.cancelled);
951
952 world.resource_mut::<Counter>().0 = 1;
953 world
954 .run_system_with_input(post_system, &mut event)
955 .unwrap();
956 assert!(event.cancelled);
957 }
958
959 #[test]
960 fn run_system_invalid_params() {
961 use crate::system::RegisteredSystemError;
962
963 struct T;
964 impl Resource for T {}
965 fn system(_: Res<T>) {}
966
967 let mut world = World::new();
968 let id = world.register_system(system.param_warn_once());
969 let result = world.run_system(id);
971
972 assert!(matches!(
973 result,
974 Err(RegisteredSystemError::InvalidParams(_))
975 ));
976 }
977}