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