1#[cfg(feature = "hotpatching")]
2use crate::{change_detection::DetectChanges, HotPatchChanges};
3use crate::{
4 change_detection::Mut,
5 entity::Entity,
6 error::BevyError,
7 prelude::{FromTemplate, Template},
8 system::{
9 input::SystemInput, BoxedSystem, Commands, If, IntoSystem, Res, RunSystemError,
10 SystemParamValidationError,
11 },
12 template::TemplateContext,
13 world::World,
14};
15use alloc::boxed::Box;
16use bevy_ecs_macros::{Component, Resource};
17use bevy_platform::sync::{Arc, Mutex};
18use bevy_utils::prelude::DebugName;
19use concurrent_queue::ConcurrentQueue;
20use core::{any::TypeId, marker::PhantomData};
21use thiserror::Error;
22
23#[derive(Component)]
25#[require(SystemIdMarker = SystemIdMarker::typed_system_id_marker::<I, O>())]
26pub(crate) struct RegisteredSystem<I, O> {
27 initialized: bool,
28 system: Option<BoxedSystem<I, O>>,
29}
30
31impl<I, O> RegisteredSystem<I, O> {
32 pub fn new(system: BoxedSystem<I, O>) -> Self {
33 RegisteredSystem {
34 initialized: false,
35 system: Some(system),
36 }
37 }
38}
39
40#[derive(Debug, Clone)]
41struct TypeIdAndName {
42 type_id: TypeId,
43 name: DebugName,
44}
45
46impl TypeIdAndName {
47 fn new<T: 'static>() -> Self {
48 Self {
49 type_id: TypeId::of::<T>(),
50 name: DebugName::type_name::<T>(),
51 }
52 }
53}
54
55impl Default for TypeIdAndName {
56 fn default() -> Self {
57 Self {
58 type_id: TypeId::of::<()>(),
59 name: DebugName::type_name::<()>(),
60 }
61 }
62}
63
64#[derive(Debug, Default, Clone, Component)]
66pub struct SystemIdMarker {
67 input_type_id: TypeIdAndName,
68 output_type_id: TypeIdAndName,
69}
70
71impl SystemIdMarker {
72 fn typed_system_id_marker<I: 'static, O: 'static>() -> Self {
73 Self {
74 input_type_id: TypeIdAndName::new::<I>(),
75 output_type_id: TypeIdAndName::new::<O>(),
76 }
77 }
78}
79
80pub struct RemovedSystem<I = (), O = ()> {
85 initialized: bool,
86 system: BoxedSystem<I, O>,
87}
88
89impl<I, O> RemovedSystem<I, O> {
90 pub fn initialized(&self) -> bool {
93 self.initialized
94 }
95
96 pub fn system(self) -> BoxedSystem<I, O> {
98 self.system
99 }
100}
101
102pub fn despawn_unused_registered_systems(
105 despawner: If<Res<RegisteredSystemDespawner>>,
108 mut commands: Commands,
109) {
110 for entity in despawner.queue.try_iter() {
111 commands.entity(entity).try_despawn();
113 }
114}
115
116#[derive(Resource)]
119pub struct RegisteredSystemDespawner {
120 queue: Arc<ConcurrentQueue<Entity>>,
121}
122
123impl Default for RegisteredSystemDespawner {
124 fn default() -> Self {
125 Self {
126 queue: Arc::new(ConcurrentQueue::unbounded()),
127 }
128 }
129}
130
131pub enum SystemHandle<I: SystemInput = (), O = ()> {
149 Strong(Arc<StrongSystemHandle>),
153 Weak(SystemId<I, O>),
155}
156
157impl<I: SystemInput, O> SystemHandle<I, O> {
158 pub fn entity(&self) -> Entity {
160 match self {
161 SystemHandle::Strong(strong) => strong.entity,
162 SystemHandle::Weak(weak) => weak.entity,
163 }
164 }
165}
166
167impl<I: SystemInput, O> Eq for SystemHandle<I, O> {}
168
169impl<I: SystemInput, O> Clone for SystemHandle<I, O> {
171 fn clone(&self) -> Self {
172 match self {
173 SystemHandle::Strong(strong) => SystemHandle::Strong(Arc::clone(strong)),
174 SystemHandle::Weak(weak) => SystemHandle::Weak(*weak),
175 }
176 }
177}
178
179impl<I: SystemInput, O> PartialEq for SystemHandle<I, O> {
182 fn eq(&self, other: &Self) -> bool {
183 self.entity() == other.entity()
184 }
185}
186
187impl<I: SystemInput, O> PartialEq<SystemId<I, O>> for SystemHandle<I, O> {
188 fn eq(&self, other: &SystemId<I, O>) -> bool {
189 self.entity() == other.entity
190 }
191}
192
193impl<I: SystemInput, O> core::hash::Hash for SystemHandle<I, O> {
196 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
197 self.entity().hash(state);
198 }
199}
200
201impl<I: SystemInput, O> core::fmt::Debug for SystemHandle<I, O> {
202 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203 let name = if matches!(self, SystemHandle::Strong(_)) {
204 "StrongSystemHandle"
205 } else {
206 "WeakSystemHandle"
207 };
208 f.debug_tuple(name).field(&self.entity()).finish()
209 }
210}
211
212impl<I: SystemInput, O> From<SystemId<I, O>> for SystemHandle<I, O> {
213 fn from(id: SystemId<I, O>) -> Self {
214 SystemHandle::Weak(id)
215 }
216}
217
218pub struct StrongSystemHandle {
220 entity: Entity,
221 drop_queue: Arc<ConcurrentQueue<Entity>>,
222}
223
224impl Drop for StrongSystemHandle {
225 fn drop(&mut self) {
226 let _ = self.drop_queue.push(self.entity);
228 }
229}
230
231pub struct SystemId<I: SystemInput = (), O = ()> {
236 pub(crate) entity: Entity,
237 pub(crate) marker: PhantomData<fn(I) -> O>,
238}
239
240impl<I: SystemInput, O> SystemId<I, O> {
241 pub fn entity(self) -> Entity {
248 self.entity
249 }
250
251 pub fn from_entity(entity: Entity) -> Self {
256 Self {
257 entity,
258 marker: PhantomData,
259 }
260 }
261}
262
263impl<I: SystemInput, O> Eq for SystemId<I, O> {}
264
265impl<I: SystemInput, O> Copy for SystemId<I, O> {}
267
268impl<I: SystemInput, O> Clone for SystemId<I, O> {
270 fn clone(&self) -> Self {
271 *self
272 }
273}
274
275impl<I: SystemInput, O> PartialEq for SystemId<I, O> {
277 fn eq(&self, other: &Self) -> bool {
278 self.entity == other.entity && self.marker == other.marker
279 }
280}
281
282impl<I: SystemInput, O> core::hash::Hash for SystemId<I, O> {
284 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
285 self.entity.hash(state);
286 }
287}
288
289impl<I: SystemInput, O> core::fmt::Debug for SystemId<I, O> {
290 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
291 f.debug_tuple("SystemId").field(&self.entity).finish()
292 }
293}
294
295impl<I: SystemInput, O> From<&SystemHandle<I, O>> for SystemId<I, O> {
296 fn from(handle: &SystemHandle<I, O>) -> Self {
297 Self::from_entity(handle.entity())
298 }
299}
300
301impl<I: SystemInput, O> From<SystemHandle<I, O>> for SystemId<I, O> {
302 fn from(handle: SystemHandle<I, O>) -> Self {
303 (&handle).into()
304 }
305}
306
307impl<I: SystemInput + 'static, O: 'static> FromTemplate for SystemHandle<I, O> {
308 type Template = SystemHandleTemplate<I, O>;
309}
310
311pub enum SystemHandleTemplate<I: SystemInput + 'static = (), O: 'static = ()> {
313 Handle(SystemHandle<I, O>),
315 Value(SystemHandleValue<I, O>),
323}
324
325pub struct SystemHandleValue<I: SystemInput + 'static = (), O: 'static = ()>(
327 Arc<Mutex<SystemHandleOrValue<I, O>>>,
328);
329
330impl<I: SystemInput + 'static, O: 'static> Clone for SystemHandleValue<I, O> {
331 fn clone(&self) -> Self {
332 Self(Arc::clone(&self.0))
333 }
334}
335
336enum SystemHandleOrValue<I: SystemInput + 'static = (), O: 'static = ()> {
337 Handle(SystemHandle<I, O>),
338 Value(Option<BoxedSystem<I, O>>),
339}
340
341impl<I: SystemInput + 'static, O: 'static> SystemHandleTemplate<I, O> {
342 pub fn value<M>(system: impl IntoSystem<I, O, M>) -> Self {
346 Self::Value(SystemHandleValue(Arc::new(Mutex::new(
347 SystemHandleOrValue::Value(Some(Box::new(IntoSystem::into_system(system)))),
348 ))))
349 }
350}
351
352impl<I: SystemInput + 'static, O: 'static> Template for SystemHandleTemplate<I, O> {
353 type Output = SystemHandle<I, O>;
354
355 fn build_template(
356 &self,
357 context: &mut TemplateContext,
358 ) -> crate::prelude::Result<Self::Output> {
359 match self {
360 Self::Handle(handle) => Ok(handle.clone()),
361 Self::Value(value) => {
362 let mut value_or_id = value.0.lock().unwrap();
363 match &mut *value_or_id {
364 SystemHandleOrValue::Handle(handle) => Ok(handle.clone()),
365 SystemHandleOrValue::Value(system) => {
366 let system = system.take().unwrap();
367 let id = context
368 .entity
369 .world_scope(|world| world.register_tracked_boxed_system(system));
370 *value_or_id = SystemHandleOrValue::Handle(id.clone());
371 Ok(id)
372 }
373 }
374 }
375 }
376 }
377
378 fn clone_template(&self) -> Self {
379 match self {
380 Self::Handle(handle) => Self::Handle(handle.clone()),
381 Self::Value(value) => Self::Value(value.clone()),
382 }
383 }
384}
385
386impl<I: SystemInput + 'static, O: 'static> Default for SystemHandleTemplate<I, O> {
387 fn default() -> Self {
388 Self::Handle(SystemHandle::Weak(SystemId::from_entity(
389 Entity::PLACEHOLDER,
390 )))
391 }
392}
393
394impl<I: SystemInput + 'static, O: 'static> From<SystemHandle<I, O>> for SystemHandleTemplate<I, O> {
395 fn from(handle: SystemHandle<I, O>) -> Self {
396 Self::Handle(handle)
397 }
398}
399
400impl<I: SystemInput + 'static, O: 'static> From<BoxedSystem<I, O>> for SystemHandleTemplate<I, O> {
401 fn from(system: BoxedSystem<I, O>) -> Self {
402 Self::Value(SystemHandleValue(Arc::new(Mutex::new(
403 SystemHandleOrValue::Value(Some(system)),
404 ))))
405 }
406}
407
408impl<I: SystemInput + 'static, O: 'static> From<SystemId<I, O>> for SystemHandleTemplate<I, O> {
409 fn from(id: SystemId<I, O>) -> Self {
410 Self::Handle(SystemHandle::Weak(id))
411 }
412}
413
414pub fn system_value<I: SystemInput + 'static, O: 'static, M>(
418 system: impl IntoSystem<I, O, M>,
419) -> SystemHandleTemplate<I, O> {
420 SystemHandleTemplate::value(system)
421}
422
423#[derive(Resource)]
427pub struct CachedSystemId<S> {
428 pub entity: Entity,
430 _marker: PhantomData<fn() -> S>,
431}
432
433impl<S> CachedSystemId<S> {
434 pub fn new<I: SystemInput, O>(id: SystemId<I, O>) -> Self {
436 Self {
437 entity: id.entity(),
438 _marker: PhantomData,
439 }
440 }
441}
442
443impl World {
444 pub fn register_system<I, O, M>(
456 &mut self,
457 system: impl IntoSystem<I, O, M> + 'static,
458 ) -> SystemId<I, O>
459 where
460 I: SystemInput + 'static,
461 O: 'static,
462 {
463 self.register_boxed_system(Box::new(IntoSystem::into_system(system)))
464 }
465
466 pub fn register_boxed_system<I, O>(&mut self, system: BoxedSystem<I, O>) -> SystemId<I, O>
471 where
472 I: SystemInput + 'static,
473 O: 'static,
474 {
475 let entity = self.spawn(RegisteredSystem::new(system)).id();
476 SystemId::from_entity(entity)
477 }
478
479 pub fn register_tracked_system<I, O, M>(
492 &mut self,
493 system: impl IntoSystem<I, O, M> + 'static,
494 ) -> SystemHandle<I, O>
495 where
496 I: SystemInput + 'static,
497 O: 'static,
498 {
499 self.register_tracked_boxed_system(Box::new(IntoSystem::into_system(system)))
500 }
501
502 pub fn register_tracked_boxed_system<I, O>(
508 &mut self,
509 system: BoxedSystem<I, O>,
510 ) -> SystemHandle<I, O>
511 where
512 I: SystemInput + 'static,
513 O: 'static,
514 {
515 let entity = self.spawn(RegisteredSystem::new(system)).id();
516 let despawner = self.get_resource_or_init::<RegisteredSystemDespawner>();
517
518 SystemHandle::Strong(Arc::new(StrongSystemHandle {
519 entity,
520 drop_queue: despawner.queue.clone(),
521 }))
522 }
523
524 pub fn unregister_system<I, O>(
531 &mut self,
532 id: SystemId<I, O>,
533 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
534 where
535 I: SystemInput + 'static,
536 O: 'static,
537 {
538 match self.get_entity_mut(id.entity) {
539 Ok(mut entity) => {
540 let registered_system = entity
541 .take::<RegisteredSystem<I, O>>()
542 .ok_or(RegisteredSystemError::SelfRemove(id))?;
543 entity.despawn();
544 Ok(RemovedSystem {
545 initialized: registered_system.initialized,
546 system: registered_system
547 .system
548 .ok_or(RegisteredSystemError::SystemMissing(id))?,
549 })
550 }
551 Err(_) => Err(RegisteredSystemError::SystemIdNotRegistered(id)),
552 }
553 }
554
555 pub fn run_system<O: 'static>(
641 &mut self,
642 id: impl Into<SystemId<(), O>>,
643 ) -> Result<O, RegisteredSystemError<(), O>> {
644 self.run_system_with(id, ())
645 }
646
647 pub fn run_system_with<I, O>(
673 &mut self,
674 id: impl Into<SystemId<I, O>>,
675 input: I::Inner<'_>,
676 ) -> Result<O, RegisteredSystemError<I, O>>
677 where
678 I: SystemInput + 'static,
679 O: 'static,
680 {
681 let id = id.into();
682 let mut entity = self
684 .get_entity_mut(id.entity)
685 .map_err(|_| RegisteredSystemError::SystemIdNotRegistered(id))?;
686
687 let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>() else {
689 let Some(system_id_marker) = entity.get::<SystemIdMarker>() else {
690 return Err(RegisteredSystemError::SystemIdNotRegistered(id));
691 };
692 if system_id_marker.input_type_id.type_id != TypeId::of::<I>()
693 || system_id_marker.output_type_id.type_id != TypeId::of::<O>()
694 {
695 return Err(RegisteredSystemError::IncorrectType(
696 id,
697 system_id_marker.clone(),
698 ));
699 }
700 return Err(RegisteredSystemError::MissingRegisteredSystemComponent(id));
701 };
702
703 let mut system = registered_system
704 .system
705 .take()
706 .ok_or(RegisteredSystemError::SystemMissing(id))?;
707
708 if !registered_system.initialized {
710 system.initialize(self);
711 }
712
713 #[cfg(feature = "hotpatching")]
715 if self
716 .get_resource_ref::<HotPatchChanges>()
717 .is_none_or(|r| r.is_changed_after(system.get_last_run()))
718 {
719 system.refresh_hotpatch();
720 }
721
722 let result = system.run_without_applying_deferred(input, self);
725 system.queue_deferred(self.into());
726
727 if let Ok(mut entity) = self.get_entity_mut(id.entity)
729 && let Some(mut registered_system) = entity.get_mut::<RegisteredSystem<I, O>>()
730 {
731 registered_system.system = Some(system);
732 registered_system.initialized = true;
733 }
734
735 self.flush();
737 Ok(result?)
738 }
739
740 pub fn register_system_cached<I, O, M, S>(&mut self, system: S) -> SystemId<I, O>
760 where
761 I: SystemInput + 'static,
762 O: 'static,
763 S: IntoSystem<I, O, M> + 'static,
764 {
765 const {
766 assert!(
767 size_of::<S>() == 0,
768 "Non-ZST systems (e.g. capturing closures, function pointers) cannot be cached.",
769 );
770 }
771
772 if !self.contains_resource::<CachedSystemId<S>>() {
773 let id = self.register_system(system);
774 self.insert_resource(CachedSystemId::<S>::new(id));
775 return id;
776 }
777
778 self.resource_scope(|world, mut id: Mut<CachedSystemId<S>>| {
779 if let Ok(mut entity) = world.get_entity_mut(id.entity) {
780 if !entity.contains::<RegisteredSystem<I, O>>() {
781 entity.insert(RegisteredSystem::new(Box::new(IntoSystem::into_system(
782 system,
783 ))));
784 }
785 } else {
786 id.entity = world.register_system(system).entity();
787 }
788 SystemId::from_entity(id.entity)
789 })
790 }
791
792 pub fn unregister_system_cached<I, O, M, S>(
796 &mut self,
797 _system: S,
798 ) -> Result<RemovedSystem<I, O>, RegisteredSystemError<I, O>>
799 where
800 I: SystemInput + 'static,
801 O: 'static,
802 S: IntoSystem<I, O, M> + 'static,
803 {
804 let id = self
805 .remove_resource::<CachedSystemId<S>>()
806 .ok_or(RegisteredSystemError::SystemNotCached)?;
807 self.unregister_system(SystemId::<I, O>::from_entity(id.entity))
808 }
809
810 pub fn run_system_cached<O: 'static, M, S: IntoSystem<(), O, M> + 'static>(
814 &mut self,
815 system: S,
816 ) -> Result<O, RegisteredSystemError<(), O>> {
817 self.run_system_cached_with(system, ())
818 }
819
820 pub fn run_system_cached_with<I, O, M, S>(
825 &mut self,
826 system: S,
827 input: I::Inner<'_>,
828 ) -> Result<O, RegisteredSystemError<I, O>>
829 where
830 I: SystemInput + 'static,
831 O: 'static,
832 S: IntoSystem<I, O, M> + 'static,
833 {
834 let id = self.register_system_cached(system);
835 self.run_system_with(id, input)
836 }
837}
838
839#[derive(Error)]
841pub enum RegisteredSystemError<I: SystemInput = (), O = ()> {
842 #[error("System {0:?} was not registered")]
846 SystemIdNotRegistered(SystemId<I, O>),
847 #[error("Cached system was not found")]
851 SystemNotCached,
852 #[error("System {0:?} does not have a RegisteredSystem component. This only happens if app code removed the component.")]
854 MissingRegisteredSystemComponent(SystemId<I, O>),
855 #[error("System {0:?} tried to remove itself")]
857 SelfRemove(SystemId<I, O>),
858 #[error("System did not run due to failed parameter validation: {0}")]
861 Skipped(SystemParamValidationError),
862 #[error("System returned error: {0}")]
864 Failed(BevyError),
865 #[error("Could not get system from `{}`, entity was `SystemId<{}, {}>`", DebugName::type_name::<SystemId<I, O>>(), .1.input_type_id.name, .1.output_type_id.name)]
867 IncorrectType(SystemId<I, O>, SystemIdMarker),
868 #[error("The system is not present in the RegisteredSystem component. This can happen if the system was called recursively or if the system panicked on the last run.")]
871 SystemMissing(SystemId<I, O>),
872}
873
874impl<I: SystemInput, O> From<RunSystemError> for RegisteredSystemError<I, O> {
875 fn from(value: RunSystemError) -> Self {
876 match value {
877 RunSystemError::Skipped(err) => Self::Skipped(err),
878 RunSystemError::Failed(err) => Self::Failed(err),
879 }
880 }
881}
882
883impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
884 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
885 match self {
886 Self::SystemIdNotRegistered(arg0) => {
887 f.debug_tuple("SystemIdNotRegistered").field(arg0).finish()
888 }
889 Self::SystemNotCached => write!(f, "SystemNotCached"),
890 Self::MissingRegisteredSystemComponent(arg0) => f
891 .debug_tuple("MissingRegisteredSystemComponent")
892 .field(arg0)
893 .finish(),
894 Self::SelfRemove(arg0) => f.debug_tuple("SelfRemove").field(arg0).finish(),
895 Self::Skipped(arg0) => f.debug_tuple("Skipped").field(arg0).finish(),
896 Self::Failed(arg0) => f.debug_tuple("Failed").field(arg0).finish(),
897 Self::IncorrectType(arg0, arg1) => f
898 .debug_tuple("IncorrectType")
899 .field(arg0)
900 .field(arg1)
901 .finish(),
902 Self::SystemMissing(arg0) => f.debug_tuple("SystemMissing").field(arg0).finish(),
903 }
904 }
905}
906
907#[cfg(test)]
908mod tests {
909 use core::cell::Cell;
910
911 use bevy_utils::default;
912
913 use crate::{
914 prelude::*,
915 system::{
916 despawn_unused_registered_systems, system_value, RegisteredSystemError, SystemHandle,
917 SystemHandleTemplate, SystemId,
918 },
919 };
920
921 #[derive(Resource, Default, PartialEq, Debug)]
922 struct Counter(u8);
923
924 #[test]
925 fn change_detection() {
926 #[derive(Resource, Default)]
927 struct ChangeDetector;
928
929 fn count_up_iff_changed(
930 mut counter: ResMut<Counter>,
931 change_detector: ResMut<ChangeDetector>,
932 ) {
933 if change_detector.is_changed() {
934 counter.0 += 1;
935 }
936 }
937
938 let mut world = World::new();
939 world.init_resource::<ChangeDetector>();
940 world.init_resource::<Counter>();
941 assert_eq!(*world.resource::<Counter>(), Counter(0));
942 let id = world.register_system(count_up_iff_changed);
944 world.run_system(id).expect("system runs successfully");
945 assert_eq!(*world.resource::<Counter>(), Counter(1));
946 world.run_system(id).expect("system runs successfully");
948 assert_eq!(*world.resource::<Counter>(), Counter(1));
949 world.resource_mut::<ChangeDetector>().set_changed();
951 world.run_system(id).expect("system runs successfully");
952 assert_eq!(*world.resource::<Counter>(), Counter(2));
953 }
954
955 #[test]
956 fn local_variables() {
957 fn doubling(last_counter: Local<Counter>, mut counter: ResMut<Counter>) {
959 counter.0 += last_counter.0 .0;
960 last_counter.0 .0 = counter.0;
961 }
962
963 let mut world = World::new();
964 world.insert_resource(Counter(1));
965 assert_eq!(*world.resource::<Counter>(), Counter(1));
966 let id = world.register_system(doubling);
967 world.run_system(id).expect("system runs successfully");
968 assert_eq!(*world.resource::<Counter>(), Counter(1));
969 world.run_system(id).expect("system runs successfully");
970 assert_eq!(*world.resource::<Counter>(), Counter(2));
971 world.run_system(id).expect("system runs successfully");
972 assert_eq!(*world.resource::<Counter>(), Counter(4));
973 world.run_system(id).expect("system runs successfully");
974 assert_eq!(*world.resource::<Counter>(), Counter(8));
975 }
976
977 #[test]
978 fn input_values() {
979 struct NonCopy(u8);
981
982 fn increment_sys(In(NonCopy(increment_by)): In<NonCopy>, mut counter: ResMut<Counter>) {
983 counter.0 += increment_by;
984 }
985
986 let mut world = World::new();
987
988 let id = world.register_system(increment_sys);
989
990 world.insert_resource(Counter(1));
992 assert_eq!(*world.resource::<Counter>(), Counter(1));
993
994 world
995 .run_system_with(id, NonCopy(1))
996 .expect("system runs successfully");
997 assert_eq!(*world.resource::<Counter>(), Counter(2));
998
999 world
1000 .run_system_with(id, NonCopy(1))
1001 .expect("system runs successfully");
1002 assert_eq!(*world.resource::<Counter>(), Counter(3));
1003
1004 world
1005 .run_system_with(id, NonCopy(20))
1006 .expect("system runs successfully");
1007 assert_eq!(*world.resource::<Counter>(), Counter(23));
1008
1009 world
1010 .run_system_with(id, NonCopy(1))
1011 .expect("system runs successfully");
1012 assert_eq!(*world.resource::<Counter>(), Counter(24));
1013 }
1014
1015 #[test]
1016 fn output_values() {
1017 #[derive(Eq, PartialEq, Debug)]
1019 struct NonCopy(u8);
1020
1021 fn increment_sys(mut counter: ResMut<Counter>) -> NonCopy {
1022 counter.0 += 1;
1023 NonCopy(counter.0)
1024 }
1025
1026 let mut world = World::new();
1027
1028 let id = world.register_system(increment_sys);
1029
1030 world.insert_resource(Counter(1));
1032 assert_eq!(*world.resource::<Counter>(), Counter(1));
1033
1034 let output = world.run_system(id).expect("system runs successfully");
1035 assert_eq!(*world.resource::<Counter>(), Counter(2));
1036 assert_eq!(output, NonCopy(2));
1037
1038 let output = world.run_system(id).expect("system runs successfully");
1039 assert_eq!(*world.resource::<Counter>(), Counter(3));
1040 assert_eq!(output, NonCopy(3));
1041 }
1042
1043 #[test]
1044 fn fallible_system() {
1045 fn sys() -> Result<()> {
1046 Err("error")?;
1047 Ok(())
1048 }
1049
1050 let mut world = World::new();
1051 let fallible_system_id = world.register_system(sys);
1052 let output = world.run_system(fallible_system_id);
1053 assert!(matches!(output, Ok(Err(_))));
1054 }
1055
1056 #[test]
1057 fn exclusive_system() {
1058 let mut world = World::new();
1059 let exclusive_system_id = world.register_system(|world: &mut World| {
1060 world.spawn_empty();
1061 });
1062 let entity_count = world.entities.count_spawned();
1063 let _ = world.run_system(exclusive_system_id);
1064 assert_eq!(world.entities.count_spawned(), entity_count + 1);
1065 }
1066
1067 #[test]
1068 fn nested_systems() {
1069 use crate::system::SystemId;
1070
1071 #[derive(Component)]
1072 struct Callback(SystemId);
1073
1074 fn nested(query: Query<&Callback>, mut commands: Commands) {
1075 for callback in query.iter() {
1076 commands.run_system(callback.0);
1077 }
1078 }
1079
1080 let mut world = World::new();
1081 world.insert_resource(Counter(0));
1082
1083 let increment_two = world.register_system(|mut counter: ResMut<Counter>| {
1084 counter.0 += 2;
1085 });
1086 let increment_three = world.register_system(|mut counter: ResMut<Counter>| {
1087 counter.0 += 3;
1088 });
1089 let nested_id = world.register_system(nested);
1090
1091 world.spawn(Callback(increment_two));
1092 world.spawn(Callback(increment_three));
1093 let _ = world.run_system(nested_id);
1094 assert_eq!(*world.resource::<Counter>(), Counter(5));
1095 }
1096
1097 #[test]
1098 fn nested_systems_with_inputs() {
1099 use crate::system::SystemId;
1100
1101 #[derive(Component)]
1102 struct Callback(SystemId<In<u8>>, u8);
1103
1104 fn nested(query: Query<&Callback>, mut commands: Commands) {
1105 for callback in query.iter() {
1106 commands.run_system_with(callback.0, callback.1);
1107 }
1108 }
1109
1110 let mut world = World::new();
1111 world.insert_resource(Counter(0));
1112
1113 let increment_by =
1114 world.register_system(|In(amt): In<u8>, mut counter: ResMut<Counter>| {
1115 counter.0 += amt;
1116 });
1117 let nested_id = world.register_system(nested);
1118
1119 world.spawn(Callback(increment_by, 2));
1120 world.spawn(Callback(increment_by, 3));
1121 let _ = world.run_system(nested_id);
1122 assert_eq!(*world.resource::<Counter>(), Counter(5));
1123 }
1124
1125 #[test]
1126 fn cached_system() {
1127 use crate::system::RegisteredSystemError;
1128
1129 fn four() -> i32 {
1130 4
1131 }
1132
1133 let mut world = World::new();
1134 let old = world.register_system_cached(four);
1135 let new = world.register_system_cached(four);
1136 assert_eq!(old, new);
1137
1138 let result = world.unregister_system_cached(four);
1139 assert!(result.is_ok());
1140 let new = world.register_system_cached(four);
1141 assert_ne!(old, new);
1142
1143 let output = world.run_system(old);
1144 assert!(matches!(
1145 output,
1146 Err(RegisteredSystemError::SystemIdNotRegistered(x)) if x == old,
1147 ));
1148 let output = world.run_system(new);
1149 assert!(matches!(output, Ok(x) if x == four()));
1150 let output = world.run_system_cached(four);
1151 assert!(matches!(output, Ok(x) if x == four()));
1152 let output = world.run_system_cached_with(four, ());
1153 assert!(matches!(output, Ok(x) if x == four()));
1154 }
1155
1156 #[test]
1157 fn cached_fallible_system() {
1158 fn sys() -> Result<()> {
1159 Err("error")?;
1160 Ok(())
1161 }
1162
1163 let mut world = World::new();
1164 let fallible_system_id = world.register_system_cached(sys);
1165 let output = world.run_system(fallible_system_id);
1166 assert!(matches!(output, Ok(Err(_))));
1167 let output = world.run_system_cached(sys);
1168 assert!(matches!(output, Ok(Err(_))));
1169 let output = world.run_system_cached_with(sys, ());
1170 assert!(matches!(output, Ok(Err(_))));
1171 }
1172
1173 #[test]
1174 fn cached_system_commands() {
1175 fn sys(mut counter: ResMut<Counter>) {
1176 counter.0 += 1;
1177 }
1178
1179 let mut world = World::new();
1180 world.insert_resource(Counter(0));
1181 world.commands().run_system_cached(sys);
1182 world.flush_commands();
1183 assert_eq!(world.resource::<Counter>().0, 1);
1184 world.commands().run_system_cached_with(sys, ());
1185 world.flush_commands();
1186 assert_eq!(world.resource::<Counter>().0, 2);
1187 }
1188
1189 #[test]
1190 fn cached_fallible_system_commands() {
1191 fn sys(mut counter: ResMut<Counter>) -> Result {
1192 counter.0 += 1;
1193 Ok(())
1194 }
1195
1196 let mut world = World::new();
1197 world.insert_resource(Counter(0));
1198 world.commands().run_system_cached(sys);
1199 world.flush_commands();
1200 assert_eq!(world.resource::<Counter>().0, 1);
1201 world.commands().run_system_cached_with(sys, ());
1202 world.flush_commands();
1203 assert_eq!(world.resource::<Counter>().0, 2);
1204 }
1205
1206 #[test]
1207 #[should_panic(expected = "This system always fails")]
1208 fn cached_fallible_system_commands_can_fail() {
1209 use crate::system::command;
1210 fn sys() -> Result {
1211 Err("This system always fails".into())
1212 }
1213
1214 let mut world = World::new();
1215 world.commands().queue(command::run_system_cached(sys));
1216 world.flush_commands();
1217 }
1218
1219 #[test]
1220 fn cached_system_adapters() {
1221 fn four() -> i32 {
1222 4
1223 }
1224
1225 fn double(In(i): In<i32>) -> i32 {
1226 i * 2
1227 }
1228
1229 let mut world = World::new();
1230
1231 let output = world.run_system_cached(four.pipe(double));
1232 assert!(matches!(output, Ok(8)));
1233
1234 let output = world.run_system_cached(four.map(|i| i * 2));
1235 assert!(matches!(output, Ok(8)));
1236 }
1237
1238 #[test]
1239 fn cached_system_into_same_system_type() {
1240 struct Foo;
1241 impl IntoSystem<(), (), ()> for Foo {
1242 type System = ApplyDeferred;
1243 fn into_system(_: Self) -> Self::System {
1244 ApplyDeferred
1245 }
1246 }
1247
1248 struct Bar;
1249 impl IntoSystem<(), (), ()> for Bar {
1250 type System = ApplyDeferred;
1251 fn into_system(_: Self) -> Self::System {
1252 ApplyDeferred
1253 }
1254 }
1255
1256 let mut world = World::new();
1257 let foo1 = world.register_system_cached(Foo);
1258 let foo2 = world.register_system_cached(Foo);
1259 let bar1 = world.register_system_cached(Bar);
1260 let bar2 = world.register_system_cached(Bar);
1261
1262 assert_ne!(foo1, bar1);
1266
1267 assert_eq!(foo1, foo2);
1270 assert_eq!(bar1, bar2);
1271 }
1272
1273 #[test]
1274 fn system_with_input_ref() {
1275 fn with_ref(InRef(input): InRef<u8>, mut counter: ResMut<Counter>) {
1276 counter.0 += *input;
1277 }
1278
1279 let mut world = World::new();
1280 world.insert_resource(Counter(0));
1281
1282 let id = world.register_system(with_ref);
1283 world.run_system_with(id, &2).unwrap();
1284 assert_eq!(*world.resource::<Counter>(), Counter(2));
1285 }
1286
1287 #[test]
1288 fn system_with_input_mut() {
1289 #[derive(Event)]
1290 struct MyEvent {
1291 cancelled: bool,
1292 }
1293
1294 fn post(InMut(event): InMut<MyEvent>, counter: ResMut<Counter>) {
1295 if counter.0 > 0 {
1296 event.cancelled = true;
1297 }
1298 }
1299
1300 let mut world = World::new();
1301 world.insert_resource(Counter(0));
1302 let post_system = world.register_system(post);
1303
1304 let mut event = MyEvent { cancelled: false };
1305 world.run_system_with(post_system, &mut event).unwrap();
1306 assert!(!event.cancelled);
1307
1308 world.resource_mut::<Counter>().0 = 1;
1309 world.run_system_with(post_system, &mut event).unwrap();
1310 assert!(event.cancelled);
1311 }
1312
1313 #[test]
1314 fn run_system_invalid_params() {
1315 use crate::system::RegisteredSystemError;
1316 use alloc::string::ToString;
1317
1318 #[derive(Resource)]
1319 struct T;
1320
1321 fn system(_: Res<T>) {}
1322
1323 let mut world = World::new();
1324 let id = world.register_system(system);
1325 let result = world.run_system(id);
1327
1328 assert!(matches!(result, Err(RegisteredSystemError::Failed { .. })));
1329 let expected = "does not exist";
1330 let actual = result.unwrap_err().to_string();
1331
1332 assert!(
1333 actual.contains(expected),
1334 "Expected error message to contain `{}` but got `{}`",
1335 expected,
1336 actual
1337 );
1338 }
1339
1340 #[test]
1341 fn run_system_recursive() {
1342 std::thread_local! {
1343 static INVOCATIONS_LEFT: Cell<i32> = const { Cell::new(3) };
1344 static SYSTEM_ID: Cell<Option<SystemId>> = default();
1345 }
1346
1347 fn system(mut commands: Commands) {
1348 let count = INVOCATIONS_LEFT.get() - 1;
1349 INVOCATIONS_LEFT.set(count);
1350 if count > 0 {
1351 commands.run_system(SYSTEM_ID.get().unwrap());
1352 }
1353 }
1354
1355 let mut world = World::new();
1356 let id = world.register_system(system);
1357 SYSTEM_ID.set(Some(id));
1358 world.run_system(id).unwrap();
1359
1360 assert_eq!(INVOCATIONS_LEFT.get(), 0);
1361 }
1362
1363 #[test]
1364 fn run_system_exclusive_adapters() {
1365 let mut world = World::new();
1366 fn system(_: &mut World) {}
1367 world.run_system_cached(system).unwrap();
1368 world.run_system_cached(system.pipe(system)).unwrap();
1369 world.run_system_cached(system.map(|()| {})).unwrap();
1370 }
1371
1372 #[test]
1373 fn wrong_system_type() {
1374 fn test() -> Result<(), u8> {
1375 Ok(())
1376 }
1377
1378 let mut world = World::new();
1379
1380 let entity = world.register_system_cached(test).entity();
1381
1382 match world.run_system::<u8>(SystemId::from_entity(entity)) {
1383 Ok(_) => panic!("Should fail since called `run_system` with wrong SystemId type."),
1384 Err(RegisteredSystemError::IncorrectType(_, _)) => (),
1385 Err(err) => panic!("Failed with wrong error. `{:?}`", err),
1386 }
1387 }
1388
1389 #[test]
1390 fn despawn_unused() {
1391 let mut world = World::new();
1392
1393 fn system() {}
1394
1395 let handle = world.register_tracked_system(system);
1396 let entity = handle.entity();
1397 drop(handle);
1398
1399 assert!(world.get_entity(entity).is_ok());
1400
1401 world
1402 .run_system_cached(despawn_unused_registered_systems)
1403 .unwrap();
1404
1405 assert!(world.get_entity(entity).is_err());
1406 }
1407
1408 #[test]
1409 fn system_handle_template() {
1410 fn my_system() {}
1411
1412 let mut world = World::new();
1413
1414 {
1415 let my_system_handle = world.register_tracked_system(my_system);
1416 let system_handle = world
1417 .spawn_empty()
1418 .build_template(&SystemHandleTemplate::Handle(my_system_handle.clone()))
1419 .unwrap();
1420 assert_eq!(system_handle, my_system_handle);
1421 }
1422
1423 {
1424 let template = system_value(my_system);
1425
1426 let a = world.spawn_empty().build_template(&template).unwrap();
1427 let b = world.spawn_empty().build_template(&template).unwrap();
1428
1429 assert!(matches!(a, SystemHandle::Strong(_)));
1430 assert!(matches!(b, SystemHandle::Strong(_)));
1431
1432 assert_eq!(a, b);
1433 }
1434 }
1435
1436 #[test]
1437 fn run_system_with_owned_system_handle() {
1438 fn increment(mut counter: ResMut<Counter>) {
1439 counter.0 += 1;
1440 }
1441
1442 let mut world = World::new();
1443 world.insert_resource(Counter(0));
1444
1445 let handle = world.register_tracked_system(increment);
1446 world.run_system(handle).expect("system runs successfully");
1447
1448 assert_eq!(*world.resource::<Counter>(), Counter(1));
1449 }
1450}