1#[cfg(feature = "bevy_reflect")]
2use crate::reflect::ReflectComponent;
3use crate::{
4 change_detection::Mut,
5 entity::Entity,
6 system::{input::SystemInput, BoxedSystem, IntoSystem, SystemParamValidationError},
7 world::World,
8};
9use alloc::boxed::Box;
10use bevy_ecs_macros::{Component, Resource};
11#[cfg(feature = "bevy_reflect")]
12use bevy_reflect::{std_traits::ReflectDefault, Reflect};
13use core::marker::PhantomData;
14use thiserror::Error;
15
16#[derive(Component)]
18#[require(SystemIdMarker)]
19pub(crate) struct RegisteredSystem<I, O> {
20 initialized: bool,
21 system: BoxedSystem<I, O>,
22}
23
24impl<I, O> RegisteredSystem<I, O> {
25 pub fn new(system: BoxedSystem<I, O>) -> Self {
26 RegisteredSystem {
27 initialized: false,
28 system,
29 }
30 }
31}
32
33#[derive(Component, Default)]
35#[cfg_attr(feature = "bevy_reflect", derive(Reflect))]
36#[cfg_attr(feature = "bevy_reflect", reflect(Component, Default))]
37pub struct SystemIdMarker;
38
39pub struct RemovedSystem<I = (), O = ()> {
44 initialized: bool,
45 system: BoxedSystem<I, O>,
46}
47
48impl<I, O> RemovedSystem<I, O> {
49 pub fn initialized(&self) -> bool {
52 self.initialized
53 }
54
55 pub fn system(self) -> BoxedSystem<I, O> {
57 self.system
58 }
59}
60
61pub struct SystemId<I: SystemInput = (), O = ()> {
66 pub(crate) entity: Entity,
67 pub(crate) marker: PhantomData<fn(I) -> O>,
68}
69
70impl<I: SystemInput, O> SystemId<I, O> {
71 pub fn entity(self) -> Entity {
78 self.entity
79 }
80
81 pub fn from_entity(entity: Entity) -> Self {
86 Self {
87 entity,
88 marker: PhantomData,
89 }
90 }
91}
92
93impl<I: SystemInput, O> Eq for SystemId<I, O> {}
94
95impl<I: SystemInput, O> Copy for SystemId<I, O> {}
97
98impl<I: SystemInput, O> Clone for SystemId<I, O> {
100 fn clone(&self) -> Self {
101 *self
102 }
103}
104
105impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
107 fn eq(&self, other: &Self) -> bool {
108 self.entity == other.entity && self.marker == other.marker
109 }
110}
111
112impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
114 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
115 self.entity.hash(state);
116 }
117}
118
119impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
120 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
121 f.debug_tuple("SystemId").field(&self.entity).finish()
122 }
123}
124
125#[derive(Resource)]
129pub struct CachedSystemId<S> {
130 pub entity: Entity,
132 _marker: PhantomData<fn() -> S>,
133}
134
135impl<S> CachedSystemId<S> {
136 pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
138 Self {
139 entity: id.entity(),
140 _marker: PhantomData,
141 }
142 }
143}
144
145impl World {
146 pub fn register_system<I, O, M>(
158 &mut self,
159 system: impl IntoSystem<I, O, M> + 'static,
160 ) -> SystemId<I, O>
161 where
162 I: SystemInput + 'static,
163 O: 'static,
164 {
165 self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
166 }
167
168 pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
173 where
174 I: SystemInput + 'static,
175 O: 'static,
176 {
177 let entity = self.spawn(RegisteredSystem::new(system)).id();
178 SystemId::from_entity(entity)
179 }
180
181 pub fn unregister_system<I, O>(
188 &mut self,
189 id: SystemId<I, O>,
190 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
191 where
192 I: SystemInput + 'static,
193 O: 'static,
194 {
195 match self.get_entity_mut(id.entity) {
196 Ok(mut entity) => {
197 let registered_system = entity
198 .take::<RegisteredSystem<I, O>>()
199 .ok_or(RegisteredSystemError::SelfRemove(id))?;
200 entity.despawn();
201 Ok(RemovedSystem {
202 initialized: registered_system.initialized,
203 system: registered_system.system,
204 })
205 }
206 Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
207 }
208 }
209
210 pub fn run_system<O: 'static>(
296 &mut self,
297 id: SystemId<(), O>,
298 ) -> Result<O, RegisteredSystemError<(), O>> {
299 self.run_system_with(id, ())
300 }
301
302 pub fn run_system_with<I, O>(
327 &mut self,
328 id: SystemId<I, O>,
329 input: I::Inner<'_>,
330 ) -> Result<O, RegisteredSystemError<I, O>>
331 where
332 I: SystemInput + 'static,
333 O: 'static,
334 {
335 let mut entity = self
337 .get_entity_mut(id.entity)
338 .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
339
340 let RegisteredSystem {
342 mut initialized,
343 mut system,
344 } = entity
345 .take::<RegisteredSystem<I, O>>()
346 .ok_or(RegisteredSystemError::Recursive(id))?;
347
348 if !initialized {
350 system.initialize(self);
351 initialized = true;
352 }
353
354 let result = system
355 .validate_param(self)
356 .map_err(|err| RegisteredSystemError::InvalidParams { system: id, err })
357 .map(|()| {
358 let ret = system.run_without_applying_deferred(input, self);
361 system.queue_deferred(self.into());
362 ret
363 });
364
365 if let Ok(mut entity) = self.get_entity_mut(id.entity) {
367 entity.insert::<RegisteredSystem<I, O>>(RegisteredSystem {
368 initialized,
369 system,
370 });
371 }
372
373 self.flush();
375 result
376 }
377
378 pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
398 where
399 I: SystemInput + 'static,
400 O: 'static,
401 S: IntoSystem<I, O, M> + 'static,
402 {
403 const {
404 assert!(
405 size_of::<S>() == 0,
406 "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
407 );
408 }
409
410 if !self.contains_resource::<CachedSystemId<S>>() {
411 let id = self.register_system(system);
412 self.insert_resource(CachedSystemId::<S>::new(id));
413 return id;
414 }
415
416 self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
417 if let Ok(mut entity) = world.get_entity_mut(id.entity) {
418 if !entity.contains::<RegisteredSystem<I, O>>() {
419 entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
420 system,
421 ))));
422 }
423 } else {
424 id.entity = world.register_system(system).entity();
425 }
426 SystemId::from_entity(id.entity)
427 })
428 }
429
430 pub fn unregister_system_cached<I, O, M, S>(
434 &mut self,
435 _system: S,
436 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
437 where
438 I: SystemInput + 'static,
439 O: 'static,
440 S: IntoSystem<I, O, M> + 'static,
441 {
442 let id = self
443 .remove_resource::<CachedSystemId<S>>()
444 .ok_or(RegisteredSystemError::SystemNotCached)?;
445 self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
446 }
447
448 pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
452 &mut self,
453 system: S,
454 ) -> Result<O, RegisteredSystemError<(), O>> {
455 self.run_system_cached_with(system, ())
456 }
457
458 pub fn run_system_cached_with<I, O, M, S>(
462 &mut self,
463 system: S,
464 input: I::Inner<'_>,
465 ) -> Result<O, RegisteredSystemError<I, O>>
466 where
467 I: SystemInput + 'static,
468 O: 'static,
469 S: IntoSystem<I, O, M> + 'static,
470 {
471 let id = self.register_system_cached(system);
472 self.run_system_with(id, input)
473 }
474}
475
476#[derive(Error)]
478pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
479 #[error("System {0:?} was not registered")]
483 SystemIdNotRegistered(SystemId<I, O>),
484 #[error("Cached system was not found")]
488 SystemNotCached,
489 #[error("System {0:?} tried to run itself recursively")]
491 Recursive(SystemId<I, O>),
492 #[error("System {0:?} tried to remove itself")]
494 SelfRemove(SystemId<I, O>),
495 #[error("System {system:?} did not run due to failed parameter validation: {err}")]
498 InvalidParams {
499 system: SystemId<I, O>,
501 err: SystemParamValidationError,
503 },
504}
505
506impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
507 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
508 match self {
509 Self::SystemIdNotRegistered(arg0) => {
510 f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
511 }
512 Self::SystemNotCached => write!(f, "SystemNotCached"),
513 Self::Recursive(arg0) => f.debug_tuple("Recursive").field(arg0).finish(),
514 Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
515 Self::InvalidParams { system, err } => f
516 .debug_struct("InvalidParams")
517 .field("system", system)
518 .field("err", err)
519 .finish(),
520 }
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use core::cell::Cell;
527
528 use bevy_utils::default;
529
530 use crate::{prelude::*, system::SystemId};
531
532 #[derive(Resource, Default, PartialEq, Debug)]
533 struct Counter(u8);
534
535 #[test]
536 fn change_detection() {
537 #[derive(Resource, Default)]
538 struct ChangeDetector;
539
540 fn count_up_iff_changed(
541 mut counter: ResMut<Counter>,
542 change_detector: ResMut<ChangeDetector>,
543 ) {
544 if change_detector.is_changed() {
545 counter.0 += 1;
546 }
547 }
548
549 let mut world = World::new();
550 world.init_resource::<ChangeDetector>();
551 world.init_resource::<Counter>();
552 assert_eq!(*world.resource::<Counter>(), Counter(0));
553 let id = world.register_system(count_up_iff_changed);
555 world.run_system(id).expect("system runs successfully");
556 assert_eq!(*world.resource::<Counter>(), Counter(1));
557 world.run_system(id).expect("system runs successfully");
559 assert_eq!(*world.resource::<Counter>(), Counter(1));
560 world.resource_mut::<ChangeDetector>().set_changed();
562 world.run_system(id).expect("system runs successfully");
563 assert_eq!(*world.resource::<Counter>(), Counter(2));
564 }
565
566 #[test]
567 fn local_variables() {
568 fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
570 counter.0 += last_counter.0 .0;
571 last_counter.0 .0 = counter.0;
572 }
573
574 let mut world = World::new();
575 world.insert_resource(Counter(1));
576 assert_eq!(*world.resource::<Counter>(), Counter(1));
577 let id = world.register_system(doubling);
578 world.run_system(id).expect("system runs successfully");
579 assert_eq!(*world.resource::<Counter>(), Counter(1));
580 world.run_system(id).expect("system runs successfully");
581 assert_eq!(*world.resource::<Counter>(), Counter(2));
582 world.run_system(id).expect("system runs successfully");
583 assert_eq!(*world.resource::<Counter>(), Counter(4));
584 world.run_system(id).expect("system runs successfully");
585 assert_eq!(*world.resource::<Counter>(), Counter(8));
586 }
587
588 #[test]
589 fn input_values() {
590 struct NonCopy(u8);
592
593 fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
594 counter.0 += increment_by;
595 }
596
597 let mut world = World::new();
598
599 let id = world.register_system(increment_sys);
600
601 world.insert_resource(Counter(1));
603 assert_eq!(*world.resource::<Counter>(), Counter(1));
604
605 world
606 .run_system_with(id, NonCopy(1))
607 .expect("system runs successfully");
608 assert_eq!(*world.resource::<Counter>(), Counter(2));
609
610 world
611 .run_system_with(id, NonCopy(1))
612 .expect("system runs successfully");
613 assert_eq!(*world.resource::<Counter>(), Counter(3));
614
615 world
616 .run_system_with(id, NonCopy(20))
617 .expect("system runs successfully");
618 assert_eq!(*world.resource::<Counter>(), Counter(23));
619
620 world
621 .run_system_with(id, NonCopy(1))
622 .expect("system runs successfully");
623 assert_eq!(*world.resource::<Counter>(), Counter(24));
624 }
625
626 #[test]
627 fn output_values() {
628 #[derive(Eq, PartialEq, Debug)]
630 struct NonCopy(u8);
631
632 fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
633 counter.0 += 1;
634 NonCopy(counter.0)
635 }
636
637 let mut world = World::new();
638
639 let id = world.register_system(increment_sys);
640
641 world.insert_resource(Counter(1));
643 assert_eq!(*world.resource::<Counter>(), Counter(1));
644
645 let output = world.run_system(id).expect("system runs successfully");
646 assert_eq!(*world.resource::<Counter>(), Counter(2));
647 assert_eq!(output, NonCopy(2));
648
649 let output = world.run_system(id).expect("system runs successfully");
650 assert_eq!(*world.resource::<Counter>(), Counter(3));
651 assert_eq!(output, NonCopy(3));
652 }
653
654 #[test]
655 fn exclusive_system() {
656 let mut world = World::new();
657 let exclusive_system_id = world.register_system(|world: &mut World| {
658 world.spawn_empty();
659 });
660 let entity_count = world.entities.len();
661 let _ = world.run_system(exclusive_system_id);
662 assert_eq!(world.entities.len(), entity_count + 1);
663 }
664
665 #[test]
666 fn nested_systems() {
667 use crate::system::SystemId;
668
669 #[derive(Component)]
670 struct Callback(SystemId);
671
672 fn nested(query: Query<&Callback>, mut commands: Commands) {
673 for callback in query.iter() {
674 commands.run_system(callback.0);
675 }
676 }
677
678 let mut world = World::new();
679 world.insert_resource(Counter(0));
680
681 let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
682 counter.0 += 2;
683 });
684 let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
685 counter.0 += 3;
686 });
687 let nested_id = world.register_system(nested);
688
689 world.spawn(Callback(increment_two));
690 world.spawn(Callback(increment_three));
691 let _ = world.run_system(nested_id);
692 assert_eq!(*world.resource::<Counter>(), Counter(5));
693 }
694
695 #[test]
696 fn nested_systems_with_inputs() {
697 use crate::system::SystemId;
698
699 #[derive(Component)]
700 struct Callback(SystemId<In<u8>>, u8);
701
702 fn nested(query: Query<&Callback>, mut commands: Commands) {
703 for callback in query.iter() {
704 commands.run_system_with(callback.0, callback.1);
705 }
706 }
707
708 let mut world = World::new();
709 world.insert_resource(Counter(0));
710
711 let increment_by =
712 world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
713 counter.0 += amt;
714 });
715 let nested_id = world.register_system(nested);
716
717 world.spawn(Callback(increment_by, 2));
718 world.spawn(Callback(increment_by, 3));
719 let _ = world.run_system(nested_id);
720 assert_eq!(*world.resource::<Counter>(), Counter(5));
721 }
722
723 #[test]
724 fn cached_system() {
725 use crate::system::RegisteredSystemError;
726
727 fn four() -> i32 {
728 4
729 }
730
731 let mut world = World::new();
732 let old = world.register_system_cached(four);
733 let new = world.register_system_cached(four);
734 assert_eq!(old, new);
735
736 let result = world.unregister_system_cached(four);
737 assert!(result.is_ok());
738 let new = world.register_system_cached(four);
739 assert_ne!(old, new);
740
741 let output = world.run_system(old);
742 assert!(matches!(
743 output,
744 Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
745 ));
746 let output = world.run_system(new);
747 assert!(matches!(output, Ok(x) if x == four()));
748 let output = world.run_system_cached(four);
749 assert!(matches!(output, Ok(x) if x == four()));
750 let output = world.run_system_cached_with(four, ());
751 assert!(matches!(output, Ok(x) if x == four()));
752 }
753
754 #[test]
755 fn cached_system_commands() {
756 fn sys(mut counter: ResMut<Counter>) {
757 counter.0 = 1;
758 }
759
760 let mut world = World::new();
761 world.insert_resource(Counter(0));
762
763 world.commands().run_system_cached(sys);
764 world.flush_commands();
765
766 assert_eq!(world.resource::<Counter>().0, 1);
767 }
768
769 #[test]
770 fn cached_system_adapters() {
771 fn four() -> i32 {
772 4
773 }
774
775 fn double(In(i): In<i32>) -> i32 {
776 i * 2
777 }
778
779 let mut world = World::new();
780
781 let output = world.run_system_cached(four.pipe(double));
782 assert!(matches!(output, Ok(8)));
783
784 let output = world.run_system_cached(four.map(|i| i * 2));
785 assert!(matches!(output, Ok(8)));
786 }
787
788 #[test]
789 fn cached_system_into_same_system_type() {
790 use crate::error::Result;
791
792 struct Foo;
793 impl IntoSystem<(), Result<()>, ()> for Foo {
794 type System = ApplyDeferred;
795 fn into_system(_: Self) -> Self::System {
796 ApplyDeferred
797 }
798 }
799
800 struct Bar;
801 impl IntoSystem<(), Result<()>, ()> for Bar {
802 type System = ApplyDeferred;
803 fn into_system(_: Self) -> Self::System {
804 ApplyDeferred
805 }
806 }
807
808 let mut world = World::new();
809 let foo1 = world.register_system_cached(Foo);
810 let foo2 = world.register_system_cached(Foo);
811 let bar1 = world.register_system_cached(Bar);
812 let bar2 = world.register_system_cached(Bar);
813
814 assert_ne!(foo1, bar1);
818
819 assert_eq!(foo1, foo2);
822 assert_eq!(bar1, bar2);
823 }
824
825 #[test]
826 fn system_with_input_ref() {
827 fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
828 counter.0 += *input;
829 }
830
831 let mut world = World::new();
832 world.insert_resource(Counter(0));
833
834 let id = world.register_system(with_ref);
835 world.run_system_with(id, &2).unwrap();
836 assert_eq!(*world.resource::<Counter>(), Counter(2));
837 }
838
839 #[test]
840 fn system_with_input_mut() {
841 #[derive(Event)]
842 struct MyEvent {
843 cancelled: bool,
844 }
845
846 fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
847 if counter.0 > 0 {
848 event.cancelled = true;
849 }
850 }
851
852 let mut world = World::new();
853 world.insert_resource(Counter(0));
854 let post_system = world.register_system(post);
855
856 let mut event = MyEvent { cancelled: false };
857 world.run_system_with(post_system, &mut event).unwrap();
858 assert!(!event.cancelled);
859
860 world.resource_mut::<Counter>().0 = 1;
861 world.run_system_with(post_system, &mut event).unwrap();
862 assert!(event.cancelled);
863 }
864
865 #[test]
866 fn run_system_invalid_params() {
867 use crate::system::RegisteredSystemError;
868 use alloc::{format, string::ToString};
869
870 struct T;
871 impl Resource for T {}
872 fn system(_: Res<T>) {}
873
874 let mut world = World::new();
875 let id = world.register_system(system);
876 let result = world.run_system(id);
878
879 assert!(matches!(
880 result,
881 Err(RegisteredSystemError::InvalidParams { .. })
882 ));
883 let expected = format!("System {id:?} did not run due to failed parameter validation: Parameter `Res<T>` failed validation: Resource does not exist");
884 assert_eq!(expected, result.unwrap_err().to_string());
885 }
886
887 #[test]
888 fn run_system_recursive() {
889 std::thread_local! {
890 static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
891 static SYSTEM_ID: Cell<Option<SystemId>> = default();
892 }
893
894 fn system(mut commands: Commands) {
895 let count = INVOCATIONS_LEFT.get() - 1;
896 INVOCATIONS_LEFT.set(count);
897 if count > 0 {
898 commands.run_system(SYSTEM_ID.get().unwrap());
899 }
900 }
901
902 let mut world = World::new();
903 let id = world.register_system(system);
904 SYSTEM_ID.set(Some(id));
905 world.run_system(id).unwrap();
906
907 assert_eq!(INVOCATIONS_LEFT.get(), 0);
908 }
909
910 #[test]
911 fn run_system_exclusive_adapters() {
912 let mut world = World::new();
913 fn system(_: &mut World) {}
914 world.run_system_cached(system).unwrap();
915 world.run_system_cached(system.pipe(system)).unwrap();
916 world.run_system_cached(system.map(|()| {})).unwrap();
917 }
918}