bevy_ecs/schedule/
condition.rs

1use alloc::{boxed::Box, format};
2use bevy_utils::prelude::DebugName;
3use core::ops::Not;
4
5use crate::system::{
6    Adapt, AdapterSystem, CombinatorSystem, Combine, IntoSystem, ReadOnlySystem, RunSystemError,
7    System, SystemIn, SystemInput,
8};
9
10/// A type-erased run condition stored in a [`Box`].
11pub type BoxedCondition<In = ()> = Box<dyn ReadOnlySystem<In = In, Out = bool>>;
12
13/// A system that determines if one or more scheduled systems should run.
14///
15/// Implemented for functions and closures that convert into [`System<Out=bool>`](System)
16/// with [read-only](crate::system::ReadOnlySystemParam) parameters.
17///
18/// # Marker type parameter
19///
20/// `SystemCondition` trait has `Marker` type parameter, which has no special meaning,
21/// but exists to work around the limitation of Rust's trait system.
22///
23/// Type parameter in return type can be set to `<()>` by calling [`IntoSystem::into_system`],
24/// but usually have to be specified when passing a condition to a function.
25///
26/// ```
27/// # use bevy_ecs::schedule::SystemCondition;
28/// # use bevy_ecs::system::IntoSystem;
29/// fn not_condition<Marker>(a: impl SystemCondition<Marker>) -> impl SystemCondition<()> {
30///    IntoSystem::into_system(a.map(|x| !x))
31/// }
32/// ```
33///
34/// # Examples
35/// A condition that returns true every other time it's called.
36/// ```
37/// # use bevy_ecs::prelude::*;
38/// fn every_other_time() -> impl SystemCondition<()> {
39///     IntoSystem::into_system(|mut flag: Local<bool>| {
40///         *flag = !*flag;
41///         *flag
42///     })
43/// }
44///
45/// # #[derive(Resource)] struct DidRun(bool);
46/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
47/// # let mut schedule = Schedule::default();
48/// schedule.add_systems(my_system.run_if(every_other_time()));
49/// # let mut world = World::new();
50/// # world.insert_resource(DidRun(false));
51/// # schedule.run(&mut world);
52/// # assert!(world.resource::<DidRun>().0);
53/// # world.insert_resource(DidRun(false));
54/// # schedule.run(&mut world);
55/// # assert!(!world.resource::<DidRun>().0);
56/// ```
57///
58/// A condition that takes a bool as an input and returns it unchanged.
59///
60/// ```
61/// # use bevy_ecs::prelude::*;
62/// fn identity() -> impl SystemCondition<(), In<bool>> {
63///     IntoSystem::into_system(|In(x): In<bool>| x)
64/// }
65///
66/// # fn always_true() -> bool { true }
67/// # let mut app = Schedule::default();
68/// # #[derive(Resource)] struct DidRun(bool);
69/// # fn my_system(mut did_run: ResMut<DidRun>) { did_run.0 = true; }
70/// app.add_systems(my_system.run_if(always_true.pipe(identity())));
71/// # let mut world = World::new();
72/// # world.insert_resource(DidRun(false));
73/// # app.run(&mut world);
74/// # assert!(world.resource::<DidRun>().0);
75pub trait SystemCondition<Marker, In: SystemInput = ()>:
76    IntoSystem<In, bool, Marker, System: ReadOnlySystem>
77{
78    /// Returns a new run condition that only returns `true`
79    /// if both this one and the passed `and` return `true`.
80    ///
81    /// The returned run condition is short-circuiting, meaning
82    /// `and` will only be invoked if `self` returns `true`.
83    ///
84    /// # Examples
85    ///
86    /// ```should_panic
87    /// use bevy_ecs::prelude::*;
88    ///
89    /// #[derive(Resource, PartialEq)]
90    /// struct R(u32);
91    ///
92    /// # let mut app = Schedule::default();
93    /// # let mut world = World::new();
94    /// # fn my_system() {}
95    /// app.add_systems(
96    ///     // The `resource_equals` run condition will panic since we don't initialize `R`,
97    ///     // just like if we used `Res<R>` in a system.
98    ///     my_system.run_if(resource_equals(R(0))),
99    /// );
100    /// # app.run(&mut world);
101    /// ```
102    ///
103    /// Use `.and()` to avoid checking the condition.
104    ///
105    /// ```
106    /// # use bevy_ecs::prelude::*;
107    /// # #[derive(Resource, PartialEq)]
108    /// # struct R(u32);
109    /// # let mut app = Schedule::default();
110    /// # let mut world = World::new();
111    /// # fn my_system() {}
112    /// app.add_systems(
113    ///     // `resource_equals` will only get run if the resource `R` exists.
114    ///     my_system.run_if(resource_exists::<R>.and(resource_equals(R(0)))),
115    /// );
116    /// # app.run(&mut world);
117    /// ```
118    ///
119    /// Note that in this case, it's better to just use the run condition [`resource_exists_and_equals`].
120    ///
121    /// [`resource_exists_and_equals`]: common_conditions::resource_exists_and_equals
122    fn and<M, C: SystemCondition<M, In>>(self, and: C) -> And<Self::System, C::System> {
123        let a = IntoSystem::into_system(self);
124        let b = IntoSystem::into_system(and);
125        let name = format!("{} && {}", a.name(), b.name());
126        CombinatorSystem::new(a, b, DebugName::owned(name))
127    }
128
129    /// Returns a new run condition that only returns `false`
130    /// if both this one and the passed `nand` return `true`.
131    ///
132    /// The returned run condition is short-circuiting, meaning
133    /// `nand` will only be invoked if `self` returns `true`.
134    ///
135    /// # Examples
136    ///
137    /// ```compile_fail
138    /// use bevy::prelude::*;
139    ///
140    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
141    /// pub enum PlayerState {
142    ///     Alive,
143    ///     Dead,
144    /// }
145    ///
146    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
147    /// pub enum EnemyState {
148    ///     Alive,
149    ///     Dead,
150    /// }
151    ///
152    /// # let mut app = Schedule::default();
153    /// # let mut world = World::new();
154    /// # fn game_over_credits() {}
155    /// app.add_systems(
156    ///     // The game_over_credits system will only execute if either the `in_state(PlayerState::Alive)`
157    ///     // run condition or `in_state(EnemyState::Alive)` run condition evaluates to `false`.
158    ///     game_over_credits.run_if(
159    ///         in_state(PlayerState::Alive).nand(in_state(EnemyState::Alive))
160    ///     ),
161    /// );
162    /// # app.run(&mut world);
163    /// ```
164    ///
165    /// Equivalent logic can be achieved by using `not` in concert with `and`:
166    ///
167    /// ```compile_fail
168    /// app.add_systems(
169    ///     game_over_credits.run_if(
170    ///         not(in_state(PlayerState::Alive).and(in_state(EnemyState::Alive)))
171    ///     ),
172    /// );
173    /// ```
174    fn nand<M, C: SystemCondition<M, In>>(self, nand: C) -> Nand<Self::System, C::System> {
175        let a = IntoSystem::into_system(self);
176        let b = IntoSystem::into_system(nand);
177        let name = format!("!({} && {})", a.name(), b.name());
178        CombinatorSystem::new(a, b, DebugName::owned(name))
179    }
180
181    /// Returns a new run condition that only returns `true`
182    /// if both this one and the passed `nor` return `false`.
183    ///
184    /// The returned run condition is short-circuiting, meaning
185    /// `nor` will only be invoked if `self` returns `false`.
186    ///
187    /// # Examples
188    ///
189    /// ```compile_fail
190    /// use bevy::prelude::*;
191    ///
192    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
193    /// pub enum WeatherState {
194    ///     Sunny,
195    ///     Cloudy,
196    /// }
197    ///
198    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
199    /// pub enum SoilState {
200    ///     Fertilized,
201    ///     NotFertilized,
202    /// }
203    ///
204    /// # let mut app = Schedule::default();
205    /// # let mut world = World::new();
206    /// # fn slow_plant_growth() {}
207    /// app.add_systems(
208    ///     // The slow_plant_growth system will only execute if both the `in_state(WeatherState::Sunny)`
209    ///     // run condition and `in_state(SoilState::Fertilized)` run condition evaluate to `false`.
210    ///     slow_plant_growth.run_if(
211    ///         in_state(WeatherState::Sunny).nor(in_state(SoilState::Fertilized))
212    ///     ),
213    /// );
214    /// # app.run(&mut world);
215    /// ```
216    ///
217    /// Equivalent logic can be achieved by using `not` in concert with `or`:
218    ///
219    /// ```compile_fail
220    /// app.add_systems(
221    ///     slow_plant_growth.run_if(
222    ///         not(in_state(WeatherState::Sunny).or(in_state(SoilState::Fertilized)))
223    ///     ),
224    /// );
225    /// ```
226    fn nor<M, C: SystemCondition<M, In>>(self, nor: C) -> Nor<Self::System, C::System> {
227        let a = IntoSystem::into_system(self);
228        let b = IntoSystem::into_system(nor);
229        let name = format!("!({} || {})", a.name(), b.name());
230        CombinatorSystem::new(a, b, DebugName::owned(name))
231    }
232
233    /// Returns a new run condition that returns `true`
234    /// if either this one or the passed `or` return `true`.
235    ///
236    /// The returned run condition is short-circuiting, meaning
237    /// `or` will only be invoked if `self` returns `false`.
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// use bevy_ecs::prelude::*;
243    ///
244    /// #[derive(Resource, PartialEq)]
245    /// struct A(u32);
246    ///
247    /// #[derive(Resource, PartialEq)]
248    /// struct B(u32);
249    ///
250    /// # let mut app = Schedule::default();
251    /// # let mut world = World::new();
252    /// # #[derive(Resource)] struct C(bool);
253    /// # fn my_system(mut c: ResMut<C>) { c.0 = true; }
254    /// app.add_systems(
255    ///     // Only run the system if either `A` or `B` exist.
256    ///     my_system.run_if(resource_exists::<A>.or(resource_exists::<B>)),
257    /// );
258    /// #
259    /// # world.insert_resource(C(false));
260    /// # app.run(&mut world);
261    /// # assert!(!world.resource::<C>().0);
262    /// #
263    /// # world.insert_resource(A(0));
264    /// # app.run(&mut world);
265    /// # assert!(world.resource::<C>().0);
266    /// #
267    /// # world.remove_resource::<A>();
268    /// # world.insert_resource(B(0));
269    /// # world.insert_resource(C(false));
270    /// # app.run(&mut world);
271    /// # assert!(world.resource::<C>().0);
272    /// ```
273    fn or<M, C: SystemCondition<M, In>>(self, or: C) -> Or<Self::System, C::System> {
274        let a = IntoSystem::into_system(self);
275        let b = IntoSystem::into_system(or);
276        let name = format!("{} || {}", a.name(), b.name());
277        CombinatorSystem::new(a, b, DebugName::owned(name))
278    }
279
280    /// Returns a new run condition that only returns `true`
281    /// if `self` and `xnor` **both** return `false` or **both** return `true`.
282    ///
283    /// # Examples
284    ///
285    /// ```compile_fail
286    /// use bevy::prelude::*;
287    ///
288    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
289    /// pub enum CoffeeMachineState {
290    ///     Heating,
291    ///     Brewing,
292    ///     Inactive,
293    /// }
294    ///
295    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
296    /// pub enum TeaKettleState {
297    ///     Heating,
298    ///     Steeping,
299    ///     Inactive,
300    /// }
301    ///
302    /// # let mut app = Schedule::default();
303    /// # let mut world = World::new();
304    /// # fn take_drink_orders() {}
305    /// app.add_systems(
306    ///     // The take_drink_orders system will only execute if the `in_state(CoffeeMachineState::Inactive)`
307    ///     // run condition and `in_state(TeaKettleState::Inactive)` run conditions both evaluate to `false`,
308    ///     // or both evaluate to `true`.
309    ///     take_drink_orders.run_if(
310    ///         in_state(CoffeeMachineState::Inactive).xnor(in_state(TeaKettleState::Inactive))
311    ///     ),
312    /// );
313    /// # app.run(&mut world);
314    /// ```
315    ///
316    /// Equivalent logic can be achieved by using `not` in concert with `xor`:
317    ///
318    /// ```compile_fail
319    /// app.add_systems(
320    ///     take_drink_orders.run_if(
321    ///         not(in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive)))
322    ///     ),
323    /// );
324    /// ```
325    fn xnor<M, C: SystemCondition<M, In>>(self, xnor: C) -> Xnor<Self::System, C::System> {
326        let a = IntoSystem::into_system(self);
327        let b = IntoSystem::into_system(xnor);
328        let name = format!("!({} ^ {})", a.name(), b.name());
329        CombinatorSystem::new(a, b, DebugName::owned(name))
330    }
331
332    /// Returns a new run condition that only returns `true`
333    /// if either `self` or `xor` return `true`, but not both.
334    ///
335    /// # Examples
336    ///
337    /// ```compile_fail
338    /// use bevy::prelude::*;
339    ///
340    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
341    /// pub enum CoffeeMachineState {
342    ///     Heating,
343    ///     Brewing,
344    ///     Inactive,
345    /// }
346    ///
347    /// #[derive(States, Debug, Clone, PartialEq, Eq, Hash)]
348    /// pub enum TeaKettleState {
349    ///     Heating,
350    ///     Steeping,
351    ///     Inactive,
352    /// }
353    ///
354    /// # let mut app = Schedule::default();
355    /// # let mut world = World::new();
356    /// # fn prepare_beverage() {}
357    /// app.add_systems(
358    ///     // The prepare_beverage system will only execute if either the `in_state(CoffeeMachineState::Inactive)`
359    ///     // run condition or `in_state(TeaKettleState::Inactive)` run condition evaluates to `true`,
360    ///     // but not both.
361    ///     prepare_beverage.run_if(
362    ///         in_state(CoffeeMachineState::Inactive).xor(in_state(TeaKettleState::Inactive))
363    ///     ),
364    /// );
365    /// # app.run(&mut world);
366    /// ```
367    fn xor<M, C: SystemCondition<M, In>>(self, xor: C) -> Xor<Self::System, C::System> {
368        let a = IntoSystem::into_system(self);
369        let b = IntoSystem::into_system(xor);
370        let name = format!("({} ^ {})", a.name(), b.name());
371        CombinatorSystem::new(a, b, DebugName::owned(name))
372    }
373}
374
375impl<Marker, In: SystemInput, F> SystemCondition<Marker, In> for F where
376    F: IntoSystem<In, bool, Marker, System: ReadOnlySystem>
377{
378}
379
380/// A collection of [run conditions](SystemCondition) that may be useful in any bevy app.
381pub mod common_conditions {
382    use super::{NotSystem, SystemCondition};
383    use crate::{
384        change_detection::DetectChanges,
385        lifecycle::RemovedComponents,
386        message::{Message, MessageReader},
387        prelude::{Component, Query, With},
388        query::QueryFilter,
389        resource::Resource,
390        system::{In, IntoSystem, Local, Res, System, SystemInput},
391    };
392    use alloc::format;
393
394    /// A [`SystemCondition`]-satisfying system that returns `true`
395    /// on the first time the condition is run and false every time after.
396    ///
397    /// # Example
398    ///
399    /// ```
400    /// # use bevy_ecs::prelude::*;
401    /// # #[derive(Resource, Default)]
402    /// # struct Counter(u8);
403    /// # let mut app = Schedule::default();
404    /// # let mut world = World::new();
405    /// # world.init_resource::<Counter>();
406    /// app.add_systems(
407    ///     // `run_once` will only return true the first time it's evaluated
408    ///     my_system.run_if(run_once),
409    /// );
410    ///
411    /// fn my_system(mut counter: ResMut<Counter>) {
412    ///     counter.0 += 1;
413    /// }
414    ///
415    /// // This is the first time the condition will be evaluated so `my_system` will run
416    /// app.run(&mut world);
417    /// assert_eq!(world.resource::<Counter>().0, 1);
418    ///
419    /// // This is the seconds time the condition will be evaluated so `my_system` won't run
420    /// app.run(&mut world);
421    /// assert_eq!(world.resource::<Counter>().0, 1);
422    /// ```
423    pub fn run_once(mut has_run: Local<bool>) -> bool {
424        if !*has_run {
425            *has_run = true;
426            true
427        } else {
428            false
429        }
430    }
431
432    /// A [`SystemCondition`]-satisfying system that returns `true`
433    /// if the resource exists.
434    ///
435    /// # Example
436    ///
437    /// ```
438    /// # use bevy_ecs::prelude::*;
439    /// # #[derive(Resource, Default)]
440    /// # struct Counter(u8);
441    /// # let mut app = Schedule::default();
442    /// # let mut world = World::new();
443    /// app.add_systems(
444    ///     // `resource_exists` will only return true if the given resource exists in the world
445    ///     my_system.run_if(resource_exists::<Counter>),
446    /// );
447    ///
448    /// fn my_system(mut counter: ResMut<Counter>) {
449    ///     counter.0 += 1;
450    /// }
451    ///
452    /// // `Counter` hasn't been added so `my_system` won't run
453    /// app.run(&mut world);
454    /// world.init_resource::<Counter>();
455    ///
456    /// // `Counter` has now been added so `my_system` can run
457    /// app.run(&mut world);
458    /// assert_eq!(world.resource::<Counter>().0, 1);
459    /// ```
460    pub fn resource_exists<T>(res: Option<Res<T>>) -> bool
461    where
462        T: Resource,
463    {
464        res.is_some()
465    }
466
467    /// Generates a [`SystemCondition`]-satisfying closure that returns `true`
468    /// if the resource is equal to `value`.
469    ///
470    /// # Panics
471    ///
472    /// The condition will panic if the resource does not exist.
473    ///
474    /// # Example
475    ///
476    /// ```
477    /// # use bevy_ecs::prelude::*;
478    /// # #[derive(Resource, Default, PartialEq)]
479    /// # struct Counter(u8);
480    /// # let mut app = Schedule::default();
481    /// # let mut world = World::new();
482    /// # world.init_resource::<Counter>();
483    /// app.add_systems(
484    ///     // `resource_equals` will only return true if the given resource equals the given value
485    ///     my_system.run_if(resource_equals(Counter(0))),
486    /// );
487    ///
488    /// fn my_system(mut counter: ResMut<Counter>) {
489    ///     counter.0 += 1;
490    /// }
491    ///
492    /// // `Counter` is `0` so `my_system` can run
493    /// app.run(&mut world);
494    /// assert_eq!(world.resource::<Counter>().0, 1);
495    ///
496    /// // `Counter` is no longer `0` so `my_system` won't run
497    /// app.run(&mut world);
498    /// assert_eq!(world.resource::<Counter>().0, 1);
499    /// ```
500    pub fn resource_equals<T>(value: T) -> impl FnMut(Res<T>) -> bool
501    where
502        T: Resource + PartialEq,
503    {
504        move |res: Res<T>| *res == value
505    }
506
507    /// Generates a [`SystemCondition`]-satisfying closure that returns `true`
508    /// if the resource exists and is equal to `value`.
509    ///
510    /// The condition will return `false` if the resource does not exist.
511    ///
512    /// # Example
513    ///
514    /// ```
515    /// # use bevy_ecs::prelude::*;
516    /// # #[derive(Resource, Default, PartialEq)]
517    /// # struct Counter(u8);
518    /// # let mut app = Schedule::default();
519    /// # let mut world = World::new();
520    /// app.add_systems(
521    ///     // `resource_exists_and_equals` will only return true
522    ///     // if the given resource exists and equals the given value
523    ///     my_system.run_if(resource_exists_and_equals(Counter(0))),
524    /// );
525    ///
526    /// fn my_system(mut counter: ResMut<Counter>) {
527    ///     counter.0 += 1;
528    /// }
529    ///
530    /// // `Counter` hasn't been added so `my_system` can't run
531    /// app.run(&mut world);
532    /// world.init_resource::<Counter>();
533    ///
534    /// // `Counter` is `0` so `my_system` can run
535    /// app.run(&mut world);
536    /// assert_eq!(world.resource::<Counter>().0, 1);
537    ///
538    /// // `Counter` is no longer `0` so `my_system` won't run
539    /// app.run(&mut world);
540    /// assert_eq!(world.resource::<Counter>().0, 1);
541    /// ```
542    pub fn resource_exists_and_equals<T>(value: T) -> impl FnMut(Option<Res<T>>) -> bool
543    where
544        T: Resource + PartialEq,
545    {
546        move |res: Option<Res<T>>| match res {
547            Some(res) => *res == value,
548            None => false,
549        }
550    }
551
552    /// A [`SystemCondition`]-satisfying system that returns `true`
553    /// if the resource of the given type has been added since the condition was last checked.
554    ///
555    /// # Example
556    ///
557    /// ```
558    /// # use bevy_ecs::prelude::*;
559    /// # #[derive(Resource, Default)]
560    /// # struct Counter(u8);
561    /// # let mut app = Schedule::default();
562    /// # let mut world = World::new();
563    /// app.add_systems(
564    ///     // `resource_added` will only return true if the
565    ///     // given resource was just added
566    ///     my_system.run_if(resource_added::<Counter>),
567    /// );
568    ///
569    /// fn my_system(mut counter: ResMut<Counter>) {
570    ///     counter.0 += 1;
571    /// }
572    ///
573    /// world.init_resource::<Counter>();
574    ///
575    /// // `Counter` was just added so `my_system` will run
576    /// app.run(&mut world);
577    /// assert_eq!(world.resource::<Counter>().0, 1);
578    ///
579    /// // `Counter` was not just added so `my_system` will not run
580    /// app.run(&mut world);
581    /// assert_eq!(world.resource::<Counter>().0, 1);
582    /// ```
583    pub fn resource_added<T>(res: Option<Res<T>>) -> bool
584    where
585        T: Resource,
586    {
587        match res {
588            Some(res) => res.is_added(),
589            None => false,
590        }
591    }
592
593    /// A [`SystemCondition`]-satisfying system that returns `true`
594    /// if the resource of the given type has been added or mutably dereferenced
595    /// since the condition was last checked.
596    ///
597    /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
598    /// Bevy does not compare resources to their previous values.
599    ///
600    /// # Panics
601    ///
602    /// The condition will panic if the resource does not exist.
603    ///
604    /// # Example
605    ///
606    /// ```
607    /// # use bevy_ecs::prelude::*;
608    /// # #[derive(Resource, Default)]
609    /// # struct Counter(u8);
610    /// # let mut app = Schedule::default();
611    /// # let mut world = World::new();
612    /// # world.init_resource::<Counter>();
613    /// app.add_systems(
614    ///     // `resource_changed` will only return true if the
615    ///     // given resource was just changed (or added)
616    ///     my_system.run_if(
617    ///         resource_changed::<Counter>
618    ///         // By default detecting changes will also trigger if the resource was
619    ///         // just added, this won't work with my example so I will add a second
620    ///         // condition to make sure the resource wasn't just added
621    ///         .and(not(resource_added::<Counter>))
622    ///     ),
623    /// );
624    ///
625    /// fn my_system(mut counter: ResMut<Counter>) {
626    ///     counter.0 += 1;
627    /// }
628    ///
629    /// // `Counter` hasn't been changed so `my_system` won't run
630    /// app.run(&mut world);
631    /// assert_eq!(world.resource::<Counter>().0, 0);
632    ///
633    /// world.resource_mut::<Counter>().0 = 50;
634    ///
635    /// // `Counter` was just changed so `my_system` will run
636    /// app.run(&mut world);
637    /// assert_eq!(world.resource::<Counter>().0, 51);
638    /// ```
639    pub fn resource_changed<T>(res: Res<T>) -> bool
640    where
641        T: Resource,
642    {
643        res.is_changed()
644    }
645
646    /// A [`SystemCondition`]-satisfying system that returns `true`
647    /// if the resource of the given type has been added or mutably dereferenced since the condition
648    /// was last checked.
649    ///
650    /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
651    /// Bevy does not compare resources to their previous values.
652    ///
653    /// The condition will return `false` if the resource does not exist.
654    ///
655    /// # Example
656    ///
657    /// ```
658    /// # use bevy_ecs::prelude::*;
659    /// # #[derive(Resource, Default)]
660    /// # struct Counter(u8);
661    /// # let mut app = Schedule::default();
662    /// # let mut world = World::new();
663    /// app.add_systems(
664    ///     // `resource_exists_and_changed` will only return true if the
665    ///     // given resource exists and was just changed (or added)
666    ///     my_system.run_if(
667    ///         resource_exists_and_changed::<Counter>
668    ///         // By default detecting changes will also trigger if the resource was
669    ///         // just added, this won't work with my example so I will add a second
670    ///         // condition to make sure the resource wasn't just added
671    ///         .and(not(resource_added::<Counter>))
672    ///     ),
673    /// );
674    ///
675    /// fn my_system(mut counter: ResMut<Counter>) {
676    ///     counter.0 += 1;
677    /// }
678    ///
679    /// // `Counter` doesn't exist so `my_system` won't run
680    /// app.run(&mut world);
681    /// world.init_resource::<Counter>();
682    ///
683    /// // `Counter` hasn't been changed so `my_system` won't run
684    /// app.run(&mut world);
685    /// assert_eq!(world.resource::<Counter>().0, 0);
686    ///
687    /// world.resource_mut::<Counter>().0 = 50;
688    ///
689    /// // `Counter` was just changed so `my_system` will run
690    /// app.run(&mut world);
691    /// assert_eq!(world.resource::<Counter>().0, 51);
692    /// ```
693    pub fn resource_exists_and_changed<T>(res: Option<Res<T>>) -> bool
694    where
695        T: Resource,
696    {
697        match res {
698            Some(res) => res.is_changed(),
699            None => false,
700        }
701    }
702
703    /// A [`SystemCondition`]-satisfying system that returns `true`
704    /// if the resource of the given type has been added, removed or mutably dereferenced since the condition
705    /// was last checked.
706    ///
707    /// **Note** that simply *mutably dereferencing* a resource is considered a change ([`DerefMut`](std::ops::DerefMut)).
708    /// Bevy does not compare resources to their previous values.
709    ///
710    /// The condition will return `false` if the resource does not exist.
711    ///
712    /// # Example
713    ///
714    /// ```
715    /// # use bevy_ecs::prelude::*;
716    /// # #[derive(Resource, Default)]
717    /// # struct Counter(u8);
718    /// # let mut app = Schedule::default();
719    /// # let mut world = World::new();
720    /// # world.init_resource::<Counter>();
721    /// app.add_systems(
722    ///     // `resource_changed_or_removed` will only return true if the
723    ///     // given resource was just changed or removed (or added)
724    ///     my_system.run_if(
725    ///         resource_changed_or_removed::<Counter>
726    ///         // By default detecting changes will also trigger if the resource was
727    ///         // just added, this won't work with my example so I will add a second
728    ///         // condition to make sure the resource wasn't just added
729    ///         .and(not(resource_added::<Counter>))
730    ///     ),
731    /// );
732    ///
733    /// #[derive(Resource, Default)]
734    /// struct MyResource;
735    ///
736    /// // If `Counter` exists, increment it, otherwise insert `MyResource`
737    /// fn my_system(mut commands: Commands, mut counter: Option<ResMut<Counter>>) {
738    ///     if let Some(mut counter) = counter {
739    ///         counter.0 += 1;
740    ///     } else {
741    ///         commands.init_resource::<MyResource>();
742    ///     }
743    /// }
744    ///
745    /// // `Counter` hasn't been changed so `my_system` won't run
746    /// app.run(&mut world);
747    /// assert_eq!(world.resource::<Counter>().0, 0);
748    ///
749    /// world.resource_mut::<Counter>().0 = 50;
750    ///
751    /// // `Counter` was just changed so `my_system` will run
752    /// app.run(&mut world);
753    /// assert_eq!(world.resource::<Counter>().0, 51);
754    ///
755    /// world.remove_resource::<Counter>();
756    ///
757    /// // `Counter` was just removed so `my_system` will run
758    /// app.run(&mut world);
759    /// assert_eq!(world.contains_resource::<MyResource>(), true);
760    /// ```
761    pub fn resource_changed_or_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
762    where
763        T: Resource,
764    {
765        if let Some(value) = res {
766            *existed = true;
767            value.is_changed()
768        } else if *existed {
769            *existed = false;
770            true
771        } else {
772            false
773        }
774    }
775
776    /// A [`SystemCondition`]-satisfying system that returns `true`
777    /// if the resource of the given type has been removed since the condition was last checked.
778    ///
779    /// # Example
780    ///
781    /// ```
782    /// # use bevy_ecs::prelude::*;
783    /// # #[derive(Resource, Default)]
784    /// # struct Counter(u8);
785    /// # let mut app = Schedule::default();
786    /// # let mut world = World::new();
787    /// # world.init_resource::<Counter>();
788    /// app.add_systems(
789    ///     // `resource_removed` will only return true if the
790    ///     // given resource was just removed
791    ///     my_system.run_if(resource_removed::<MyResource>),
792    /// );
793    ///
794    /// #[derive(Resource, Default)]
795    /// struct MyResource;
796    ///
797    /// fn my_system(mut counter: ResMut<Counter>) {
798    ///     counter.0 += 1;
799    /// }
800    ///
801    /// world.init_resource::<MyResource>();
802    ///
803    /// // `MyResource` hasn't just been removed so `my_system` won't run
804    /// app.run(&mut world);
805    /// assert_eq!(world.resource::<Counter>().0, 0);
806    ///
807    /// world.remove_resource::<MyResource>();
808    ///
809    /// // `MyResource` was just removed so `my_system` will run
810    /// app.run(&mut world);
811    /// assert_eq!(world.resource::<Counter>().0, 1);
812    /// ```
813    pub fn resource_removed<T>(res: Option<Res<T>>, mut existed: Local<bool>) -> bool
814    where
815        T: Resource,
816    {
817        if res.is_some() {
818            *existed = true;
819            false
820        } else if *existed {
821            *existed = false;
822            true
823        } else {
824            false
825        }
826    }
827
828    /// A [`SystemCondition`]-satisfying system that returns `true`
829    /// if there are any new events of the given type since it was last called.
830    ///
831    /// # Example
832    ///
833    /// ```
834    /// # use bevy_ecs::prelude::*;
835    /// # #[derive(Resource, Default)]
836    /// # struct Counter(u8);
837    /// # let mut app = Schedule::default();
838    /// # let mut world = World::new();
839    /// # world.init_resource::<Counter>();
840    /// # world.init_resource::<Messages<MyMessage>>();
841    /// # app.add_systems(bevy_ecs::message::message_update_system.before(my_system));
842    ///
843    /// app.add_systems(
844    ///     my_system.run_if(on_message::<MyMessage>),
845    /// );
846    ///
847    /// #[derive(Message)]
848    /// struct MyMessage;
849    ///
850    /// fn my_system(mut counter: ResMut<Counter>) {
851    ///     counter.0 += 1;
852    /// }
853    ///
854    /// // No new `MyMessage` events have been push so `my_system` won't run
855    /// app.run(&mut world);
856    /// assert_eq!(world.resource::<Counter>().0, 0);
857    ///
858    /// world.resource_mut::<Messages<MyMessage>>().write(MyMessage);
859    ///
860    /// // A `MyMessage` event has been pushed so `my_system` will run
861    /// app.run(&mut world);
862    /// assert_eq!(world.resource::<Counter>().0, 1);
863    /// ```
864    pub fn on_message<M: Message>(mut reader: MessageReader<M>) -> bool {
865        // The messages need to be consumed, so that there are no false positives on subsequent
866        // calls of the run condition. Simply checking `is_empty` would not be enough.
867        // PERF: note that `count` is efficient (not actually looping/iterating),
868        // due to Bevy having a specialized implementation for messages.
869        reader.read().count() > 0
870    }
871
872    /// A [`SystemCondition`]-satisfying system that returns `true`
873    /// if there are any entities with the given component type.
874    ///
875    /// # Example
876    ///
877    /// ```
878    /// # use bevy_ecs::prelude::*;
879    /// # #[derive(Resource, Default)]
880    /// # struct Counter(u8);
881    /// # let mut app = Schedule::default();
882    /// # let mut world = World::new();
883    /// # world.init_resource::<Counter>();
884    /// app.add_systems(
885    ///     my_system.run_if(any_with_component::<MyComponent>),
886    /// );
887    ///
888    /// #[derive(Component)]
889    /// struct MyComponent;
890    ///
891    /// fn my_system(mut counter: ResMut<Counter>) {
892    ///     counter.0 += 1;
893    /// }
894    ///
895    /// // No entities exist yet with a `MyComponent` component so `my_system` won't run
896    /// app.run(&mut world);
897    /// assert_eq!(world.resource::<Counter>().0, 0);
898    ///
899    /// world.spawn(MyComponent);
900    ///
901    /// // An entities with `MyComponent` now exists so `my_system` will run
902    /// app.run(&mut world);
903    /// assert_eq!(world.resource::<Counter>().0, 1);
904    /// ```
905    pub fn any_with_component<T: Component>(query: Query<(), With<T>>) -> bool {
906        !query.is_empty()
907    }
908
909    /// A [`SystemCondition`]-satisfying system that returns `true`
910    /// if there are any entity with a component of the given type removed.
911    pub fn any_component_removed<T: Component>(mut removals: RemovedComponents<T>) -> bool {
912        // `RemovedComponents` based on events and therefore events need to be consumed,
913        // so that there are no false positives on subsequent calls of the run condition.
914        // Simply checking `is_empty` would not be enough.
915        // PERF: note that `count` is efficient (not actually looping/iterating),
916        // due to Bevy having a specialized implementation for events.
917        removals.read().count() > 0
918    }
919
920    /// A [`SystemCondition`]-satisfying system that returns `true`
921    /// if there are any entities that match the given [`QueryFilter`].
922    pub fn any_match_filter<F: QueryFilter>(query: Query<(), F>) -> bool {
923        !query.is_empty()
924    }
925
926    /// Generates a [`SystemCondition`] that inverses the result of passed one.
927    ///
928    /// # Example
929    ///
930    /// ```
931    /// # use bevy_ecs::prelude::*;
932    /// # #[derive(Resource, Default)]
933    /// # struct Counter(u8);
934    /// # let mut app = Schedule::default();
935    /// # let mut world = World::new();
936    /// # world.init_resource::<Counter>();
937    /// app.add_systems(
938    ///     // `not` will inverse any condition you pass in.
939    ///     // Since the condition we choose always returns true
940    ///     // this system will never run
941    ///     my_system.run_if(not(always)),
942    /// );
943    ///
944    /// fn my_system(mut counter: ResMut<Counter>) {
945    ///     counter.0 += 1;
946    /// }
947    ///
948    /// fn always() -> bool {
949    ///     true
950    /// }
951    ///
952    /// app.run(&mut world);
953    /// assert_eq!(world.resource::<Counter>().0, 0);
954    /// ```
955    pub fn not<Marker, TOut, T>(condition: T) -> NotSystem<T::System>
956    where
957        TOut: core::ops::Not,
958        T: IntoSystem<(), TOut, Marker>,
959    {
960        let condition = IntoSystem::into_system(condition);
961        let name = format!("!{}", condition.name());
962        NotSystem::new(super::NotMarker, condition, name.into())
963    }
964
965    /// Generates a [`SystemCondition`] that returns true when the passed one changes.
966    ///
967    /// The first time this is called, the passed condition is assumed to have been previously false.
968    ///
969    /// # Example
970    ///
971    /// ```
972    /// # use bevy_ecs::prelude::*;
973    /// # #[derive(Resource, Default)]
974    /// # struct Counter(u8);
975    /// # let mut app = Schedule::default();
976    /// # let mut world = World::new();
977    /// # world.init_resource::<Counter>();
978    /// app.add_systems(
979    ///     my_system.run_if(condition_changed(resource_exists::<MyResource>)),
980    /// );
981    ///
982    /// #[derive(Resource)]
983    /// struct MyResource;
984    ///
985    /// fn my_system(mut counter: ResMut<Counter>) {
986    ///     counter.0 += 1;
987    /// }
988    ///
989    /// // `MyResource` is initially there, the inner condition is true, the system runs once
990    /// world.insert_resource(MyResource);
991    /// app.run(&mut world);
992    /// assert_eq!(world.resource::<Counter>().0, 1);
993    /// app.run(&mut world);
994    /// assert_eq!(world.resource::<Counter>().0, 1);
995    ///
996    /// // We remove `MyResource`, the inner condition is now false, the system runs one more time.
997    /// world.remove_resource::<MyResource>();
998    /// app.run(&mut world);
999    /// assert_eq!(world.resource::<Counter>().0, 2);
1000    /// app.run(&mut world);
1001    /// assert_eq!(world.resource::<Counter>().0, 2);
1002    /// ```
1003    pub fn condition_changed<Marker, CIn, C>(condition: C) -> impl SystemCondition<(), CIn>
1004    where
1005        CIn: SystemInput,
1006        C: SystemCondition<Marker, CIn>,
1007    {
1008        IntoSystem::into_system(condition.pipe(|In(new): In<bool>, mut prev: Local<bool>| {
1009            let changed = *prev != new;
1010            *prev = new;
1011            changed
1012        }))
1013    }
1014
1015    /// Generates a [`SystemCondition`] that returns true when the result of
1016    /// the passed one went from false to true since the last time this was called.
1017    ///
1018    /// The first time this is called, the passed condition is assumed to have been previously false.
1019    ///
1020    /// # Example
1021    ///
1022    /// ```
1023    /// # use bevy_ecs::prelude::*;
1024    /// # #[derive(Resource, Default)]
1025    /// # struct Counter(u8);
1026    /// # let mut app = Schedule::default();
1027    /// # let mut world = World::new();
1028    /// # world.init_resource::<Counter>();
1029    /// app.add_systems(
1030    ///     my_system.run_if(condition_changed_to(true, resource_exists::<MyResource>)),
1031    /// );
1032    ///
1033    /// #[derive(Resource)]
1034    /// struct MyResource;
1035    ///
1036    /// fn my_system(mut counter: ResMut<Counter>) {
1037    ///     counter.0 += 1;
1038    /// }
1039    ///
1040    /// // `MyResource` is initially there, the inner condition is true, the system runs once
1041    /// world.insert_resource(MyResource);
1042    /// app.run(&mut world);
1043    /// assert_eq!(world.resource::<Counter>().0, 1);
1044    /// app.run(&mut world);
1045    /// assert_eq!(world.resource::<Counter>().0, 1);
1046    ///
1047    /// // We remove `MyResource`, the inner condition is now false, the system doesn't run.
1048    /// world.remove_resource::<MyResource>();
1049    /// app.run(&mut world);
1050    /// assert_eq!(world.resource::<Counter>().0, 1);
1051    ///
1052    /// // We reinsert `MyResource` again, so the system will run one more time
1053    /// world.insert_resource(MyResource);
1054    /// app.run(&mut world);
1055    /// assert_eq!(world.resource::<Counter>().0, 2);
1056    /// app.run(&mut world);
1057    /// assert_eq!(world.resource::<Counter>().0, 2);
1058    /// ```
1059    pub fn condition_changed_to<Marker, CIn, C>(
1060        to: bool,
1061        condition: C,
1062    ) -> impl SystemCondition<(), CIn>
1063    where
1064        CIn: SystemInput,
1065        C: SystemCondition<Marker, CIn>,
1066    {
1067        IntoSystem::into_system(condition.pipe(
1068            move |In(new): In<bool>, mut prev: Local<bool>| -> bool {
1069                let now_true = *prev != new && new == to;
1070                *prev = new;
1071                now_true
1072            },
1073        ))
1074    }
1075}
1076
1077/// Invokes [`Not`] with the output of another system.
1078///
1079/// See [`common_conditions::not`] for examples.
1080pub type NotSystem<S> = AdapterSystem<NotMarker, S>;
1081
1082/// Used with [`AdapterSystem`] to negate the output of a system via the [`Not`] operator.
1083#[doc(hidden)]
1084#[derive(Clone, Copy)]
1085pub struct NotMarker;
1086
1087impl<S: System<Out: Not>> Adapt<S> for NotMarker {
1088    type In = S::In;
1089    type Out = <S::Out as Not>::Output;
1090
1091    fn adapt(
1092        &mut self,
1093        input: <Self::In as SystemInput>::Inner<'_>,
1094        run_system: impl FnOnce(SystemIn<'_, S>) -> Result<S::Out, RunSystemError>,
1095    ) -> Result<Self::Out, RunSystemError> {
1096        run_system(input).map(Not::not)
1097    }
1098}
1099
1100/// Combines the outputs of two systems using the `&&` operator.
1101pub type And<A, B> = CombinatorSystem<AndMarker, A, B>;
1102
1103/// Combines and inverts the outputs of two systems using the `&&` and `!` operators.
1104pub type Nand<A, B> = CombinatorSystem<NandMarker, A, B>;
1105
1106/// Combines and inverts the outputs of two systems using the `||` and `!` operators.
1107pub type Nor<A, B> = CombinatorSystem<NorMarker, A, B>;
1108
1109/// Combines the outputs of two systems using the `||` operator.
1110pub type Or<A, B> = CombinatorSystem<OrMarker, A, B>;
1111
1112/// Combines and inverts the outputs of two systems using the `^` and `!` operators.
1113pub type Xnor<A, B> = CombinatorSystem<XnorMarker, A, B>;
1114
1115/// Combines the outputs of two systems using the `^` operator.
1116pub type Xor<A, B> = CombinatorSystem<XorMarker, A, B>;
1117
1118#[doc(hidden)]
1119pub struct AndMarker;
1120
1121impl<In, A, B> Combine<A, B> for AndMarker
1122where
1123    for<'a> In: SystemInput<Inner<'a>: Copy>,
1124    A: System<In = In, Out = bool>,
1125    B: System<In = In, Out = bool>,
1126{
1127    type In = In;
1128    type Out = bool;
1129
1130    fn combine<T>(
1131        input: <Self::In as SystemInput>::Inner<'_>,
1132        data: &mut T,
1133        a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1134        b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1135    ) -> Result<Self::Out, RunSystemError> {
1136        Ok(a(input, data).unwrap_or(false) && b(input, data).unwrap_or(false))
1137    }
1138}
1139
1140#[doc(hidden)]
1141pub struct NandMarker;
1142
1143impl<In, A, B> Combine<A, B> for NandMarker
1144where
1145    for<'a> In: SystemInput<Inner<'a>: Copy>,
1146    A: System<In = In, Out = bool>,
1147    B: System<In = In, Out = bool>,
1148{
1149    type In = In;
1150    type Out = bool;
1151
1152    fn combine<T>(
1153        input: <Self::In as SystemInput>::Inner<'_>,
1154        data: &mut T,
1155        a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1156        b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1157    ) -> Result<Self::Out, RunSystemError> {
1158        Ok(!(a(input, data).unwrap_or(false) && b(input, data).unwrap_or(false)))
1159    }
1160}
1161
1162#[doc(hidden)]
1163pub struct NorMarker;
1164
1165impl<In, A, B> Combine<A, B> for NorMarker
1166where
1167    for<'a> In: SystemInput<Inner<'a>: Copy>,
1168    A: System<In = In, Out = bool>,
1169    B: System<In = In, Out = bool>,
1170{
1171    type In = In;
1172    type Out = bool;
1173
1174    fn combine<T>(
1175        input: <Self::In as SystemInput>::Inner<'_>,
1176        data: &mut T,
1177        a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1178        b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1179    ) -> Result<Self::Out, RunSystemError> {
1180        Ok(!(a(input, data).unwrap_or(false) || b(input, data).unwrap_or(false)))
1181    }
1182}
1183
1184#[doc(hidden)]
1185pub struct OrMarker;
1186
1187impl<In, A, B> Combine<A, B> for OrMarker
1188where
1189    for<'a> In: SystemInput<Inner<'a>: Copy>,
1190    A: System<In = In, Out = bool>,
1191    B: System<In = In, Out = bool>,
1192{
1193    type In = In;
1194    type Out = bool;
1195
1196    fn combine<T>(
1197        input: <Self::In as SystemInput>::Inner<'_>,
1198        data: &mut T,
1199        a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1200        b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1201    ) -> Result<Self::Out, RunSystemError> {
1202        Ok(a(input, data).unwrap_or(false) || b(input, data).unwrap_or(false))
1203    }
1204}
1205
1206#[doc(hidden)]
1207pub struct XnorMarker;
1208
1209impl<In, A, B> Combine<A, B> for XnorMarker
1210where
1211    for<'a> In: SystemInput<Inner<'a>: Copy>,
1212    A: System<In = In, Out = bool>,
1213    B: System<In = In, Out = bool>,
1214{
1215    type In = In;
1216    type Out = bool;
1217
1218    fn combine<T>(
1219        input: <Self::In as SystemInput>::Inner<'_>,
1220        data: &mut T,
1221        a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1222        b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1223    ) -> Result<Self::Out, RunSystemError> {
1224        Ok(!(a(input, data).unwrap_or(false) ^ b(input, data).unwrap_or(false)))
1225    }
1226}
1227
1228#[doc(hidden)]
1229pub struct XorMarker;
1230
1231impl<In, A, B> Combine<A, B> for XorMarker
1232where
1233    for<'a> In: SystemInput<Inner<'a>: Copy>,
1234    A: System<In = In, Out = bool>,
1235    B: System<In = In, Out = bool>,
1236{
1237    type In = In;
1238    type Out = bool;
1239
1240    fn combine<T>(
1241        input: <Self::In as SystemInput>::Inner<'_>,
1242        data: &mut T,
1243        a: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<A::Out, RunSystemError>,
1244        b: impl FnOnce(SystemIn<'_, A>, &mut T) -> Result<B::Out, RunSystemError>,
1245    ) -> Result<Self::Out, RunSystemError> {
1246        Ok(a(input, data).unwrap_or(false) ^ b(input, data).unwrap_or(false))
1247    }
1248}
1249
1250#[cfg(test)]
1251mod tests {
1252    use super::{common_conditions::*, SystemCondition};
1253    use crate::error::{BevyError, DefaultErrorHandler, ErrorContext};
1254    use crate::{
1255        change_detection::{Res, ResMut},
1256        component::Component,
1257        message::Message,
1258        query::With,
1259        schedule::{IntoScheduleConfigs, Schedule},
1260        system::{IntoSystem, Local, System},
1261        world::World,
1262    };
1263    use bevy_ecs_macros::{Resource, SystemSet};
1264
1265    #[derive(Resource, Default)]
1266    struct Counter(usize);
1267
1268    fn increment_counter(mut counter: ResMut<Counter>) {
1269        counter.0 += 1;
1270    }
1271
1272    fn double_counter(mut counter: ResMut<Counter>) {
1273        counter.0 *= 2;
1274    }
1275
1276    fn every_other_time(mut has_ran: Local<bool>) -> bool {
1277        *has_ran = !*has_ran;
1278        *has_ran
1279    }
1280
1281    #[test]
1282    fn run_condition() {
1283        let mut world = World::new();
1284        world.init_resource::<Counter>();
1285        let mut schedule = Schedule::default();
1286
1287        // Run every other cycle
1288        schedule.add_systems(increment_counter.run_if(every_other_time));
1289
1290        schedule.run(&mut world);
1291        schedule.run(&mut world);
1292        assert_eq!(world.resource::<Counter>().0, 1);
1293        schedule.run(&mut world);
1294        schedule.run(&mut world);
1295        assert_eq!(world.resource::<Counter>().0, 2);
1296
1297        // Run every other cycle opposite to the last one
1298        schedule.add_systems(increment_counter.run_if(not(every_other_time)));
1299
1300        schedule.run(&mut world);
1301        schedule.run(&mut world);
1302        assert_eq!(world.resource::<Counter>().0, 4);
1303        schedule.run(&mut world);
1304        schedule.run(&mut world);
1305        assert_eq!(world.resource::<Counter>().0, 6);
1306    }
1307
1308    #[test]
1309    fn combinators_with_maybe_failing_condition() {
1310        #![allow(
1311            clippy::nonminimal_bool,
1312            clippy::overly_complex_bool_expr,
1313            reason = "Trailing `|| false` and `&& true` are used in this test to visually remain consistent with the combinators"
1314        )]
1315
1316        use crate::system::RunSystemOnce;
1317        use alloc::sync::Arc;
1318        use std::sync::Mutex;
1319
1320        // Things that should be tested:
1321        // - the final result of the combinator is correct
1322        // - the systems that are expected to run do run
1323        // - the systems that are expected to not run do not run
1324
1325        #[derive(Component)]
1326        struct Vacant;
1327
1328        // SystemConditions don't have mutable access to the world, so we use a
1329        // `Res<AtomicCounter>` to count invocations.
1330        #[derive(Resource, Default)]
1331        struct Counter(Arc<Mutex<usize>>);
1332
1333        // The following constants are used to represent a system having run.
1334        // both are prime so that when multiplied they give a unique value for any TRUE^n*FALSE^m
1335        const FALSE: usize = 2;
1336        const TRUE: usize = 3;
1337
1338        // this is a system, but has the same side effect as `test_true`
1339        fn is_true_inc(counter: Res<Counter>) -> bool {
1340            test_true(&counter)
1341        }
1342
1343        // this is a system, but has the same side effect as `test_false`
1344        fn is_false_inc(counter: Res<Counter>) -> bool {
1345            test_false(&counter)
1346        }
1347
1348        // This condition will always yield `false`, because `Vacant` is never present.
1349        fn vacant(_: crate::system::Single<&Vacant>) -> bool {
1350            true
1351        }
1352
1353        fn test_true(counter: &Counter) -> bool {
1354            *counter.0.lock().unwrap() *= TRUE;
1355            true
1356        }
1357
1358        fn test_false(counter: &Counter) -> bool {
1359            *counter.0.lock().unwrap() *= FALSE;
1360            false
1361        }
1362
1363        // Helper function that runs a logic call and returns the result, as
1364        // well as the composite number of the calls.
1365        fn logic_call_result(f: impl FnOnce(&Counter) -> bool) -> (usize, bool) {
1366            let counter = Counter(Arc::new(Mutex::new(1)));
1367            let result = f(&counter);
1368            (*counter.0.lock().unwrap(), result)
1369        }
1370
1371        // `test_true` and `test_false` can't fail like the systems can, and so
1372        // we use them to model the short circuiting behavior of rust's logical
1373        // operators. The goal is to end up with a composite number that
1374        // describes rust's behavior and compare that to the result of the
1375        // combinators.
1376
1377        // we expect `true() || false()` to yield `true`, and short circuit
1378        // after `true()`
1379        assert_eq!(
1380            logic_call_result(|c| test_true(c) || test_false(c)),
1381            (TRUE.pow(1) * FALSE.pow(0), true)
1382        );
1383
1384        let mut world = World::new();
1385        world.init_resource::<Counter>();
1386
1387        // ensure there are no `Vacant` entities
1388        assert!(world.query::<&Vacant>().iter(&world).next().is_none());
1389        assert!(matches!(
1390            world.run_system_once((|| true).or(vacant)),
1391            Ok(true)
1392        ));
1393
1394        // This system should fail
1395        assert!(RunSystemOnce::run_system_once(&mut world, vacant).is_err());
1396
1397        #[track_caller]
1398        fn assert_system<Marker>(
1399            world: &mut World,
1400            system: impl IntoSystem<(), bool, Marker>,
1401            equivalent_to: impl FnOnce(&Counter) -> bool,
1402        ) {
1403            use crate::system::System;
1404
1405            *world.resource::<Counter>().0.lock().unwrap() = 1;
1406
1407            let system = IntoSystem::into_system(system);
1408            let name = system.name();
1409
1410            let out = RunSystemOnce::run_system_once(&mut *world, system).unwrap_or(false);
1411
1412            let (expected_counter, expected) = logic_call_result(equivalent_to);
1413            let caller = core::panic::Location::caller();
1414            let counter = *world.resource::<Counter>().0.lock().unwrap();
1415
1416            assert_eq!(
1417                out,
1418                expected,
1419                "At {}:{} System `{name}` yielded unexpected value `{out}`, expected `{expected}`",
1420                caller.file(),
1421                caller.line(),
1422            );
1423
1424            assert_eq!(
1425                counter, expected_counter,
1426                "At {}:{} System `{name}` did not increment counter as expected: expected `{expected_counter}`, got `{counter}`",
1427                caller.file(),
1428                caller.line(),
1429            );
1430        }
1431
1432        assert_system(&mut world, is_true_inc.or(vacant), |c| {
1433            test_true(c) || false
1434        });
1435        assert_system(&mut world, is_true_inc.nor(vacant), |c| {
1436            !(test_true(c) || false)
1437        });
1438        assert_system(&mut world, is_true_inc.xor(vacant), |c| {
1439            test_true(c) ^ false
1440        });
1441        assert_system(&mut world, is_true_inc.xnor(vacant), |c| {
1442            !(test_true(c) ^ false)
1443        });
1444        assert_system(&mut world, is_true_inc.and(vacant), |c| {
1445            test_true(c) && false
1446        });
1447        assert_system(&mut world, is_true_inc.nand(vacant), |c| {
1448            !(test_true(c) && false)
1449        });
1450
1451        // even if `vacant` fails as the first condition, where applicable (or,
1452        // xor), `is_true_inc` should still be called. `and` and `nand` short
1453        // circuit on an initial `false`.
1454        assert_system(&mut world, vacant.or(is_true_inc), |c| {
1455            false || test_true(c)
1456        });
1457        assert_system(&mut world, vacant.nor(is_true_inc), |c| {
1458            !(false || test_true(c))
1459        });
1460        assert_system(&mut world, vacant.xor(is_true_inc), |c| {
1461            false ^ test_true(c)
1462        });
1463        assert_system(&mut world, vacant.xnor(is_true_inc), |c| {
1464            !(false ^ test_true(c))
1465        });
1466        assert_system(&mut world, vacant.and(is_true_inc), |c| {
1467            false && test_true(c)
1468        });
1469        assert_system(&mut world, vacant.nand(is_true_inc), |c| {
1470            !(false && test_true(c))
1471        });
1472
1473        // the same logic ought to be the case with a condition that runs, but yields `false`:
1474        assert_system(&mut world, is_true_inc.or(is_false_inc), |c| {
1475            test_true(c) || test_false(c)
1476        });
1477        assert_system(&mut world, is_true_inc.nor(is_false_inc), |c| {
1478            !(test_true(c) || test_false(c))
1479        });
1480        assert_system(&mut world, is_true_inc.xor(is_false_inc), |c| {
1481            test_true(c) ^ test_false(c)
1482        });
1483        assert_system(&mut world, is_true_inc.xnor(is_false_inc), |c| {
1484            !(test_true(c) ^ test_false(c))
1485        });
1486        assert_system(&mut world, is_true_inc.and(is_false_inc), |c| {
1487            test_true(c) && test_false(c)
1488        });
1489        assert_system(&mut world, is_true_inc.nand(is_false_inc), |c| {
1490            !(test_true(c) && test_false(c))
1491        });
1492
1493        // and where one condition yields `false` and the other fails:
1494        assert_system(&mut world, is_false_inc.or(vacant), |c| {
1495            test_false(c) || false
1496        });
1497        assert_system(&mut world, is_false_inc.nor(vacant), |c| {
1498            !(test_false(c) || false)
1499        });
1500        assert_system(&mut world, is_false_inc.xor(vacant), |c| {
1501            test_false(c) ^ false
1502        });
1503        assert_system(&mut world, is_false_inc.xnor(vacant), |c| {
1504            !(test_false(c) ^ false)
1505        });
1506        assert_system(&mut world, is_false_inc.and(vacant), |c| {
1507            test_false(c) && false
1508        });
1509        assert_system(&mut world, is_false_inc.nand(vacant), |c| {
1510            !(test_false(c) && false)
1511        });
1512
1513        // and where both conditions yield `true`:
1514        assert_system(&mut world, is_true_inc.or(is_true_inc), |c| {
1515            test_true(c) || test_true(c)
1516        });
1517        assert_system(&mut world, is_true_inc.nor(is_true_inc), |c| {
1518            !(test_true(c) || test_true(c))
1519        });
1520        assert_system(&mut world, is_true_inc.xor(is_true_inc), |c| {
1521            test_true(c) ^ test_true(c)
1522        });
1523        assert_system(&mut world, is_true_inc.xnor(is_true_inc), |c| {
1524            !(test_true(c) ^ test_true(c))
1525        });
1526        assert_system(&mut world, is_true_inc.and(is_true_inc), |c| {
1527            test_true(c) && test_true(c)
1528        });
1529        assert_system(&mut world, is_true_inc.nand(is_true_inc), |c| {
1530            !(test_true(c) && test_true(c))
1531        });
1532
1533        // and where both conditions yield `false`:
1534        assert_system(&mut world, is_false_inc.or(is_false_inc), |c| {
1535            test_false(c) || test_false(c)
1536        });
1537        assert_system(&mut world, is_false_inc.nor(is_false_inc), |c| {
1538            !(test_false(c) || test_false(c))
1539        });
1540        assert_system(&mut world, is_false_inc.xor(is_false_inc), |c| {
1541            test_false(c) ^ test_false(c)
1542        });
1543        assert_system(&mut world, is_false_inc.xnor(is_false_inc), |c| {
1544            !(test_false(c) ^ test_false(c))
1545        });
1546        assert_system(&mut world, is_false_inc.and(is_false_inc), |c| {
1547            test_false(c) && test_false(c)
1548        });
1549        assert_system(&mut world, is_false_inc.nand(is_false_inc), |c| {
1550            !(test_false(c) && test_false(c))
1551        });
1552    }
1553
1554    #[test]
1555    fn run_condition_combinators() {
1556        let mut world = World::new();
1557        world.init_resource::<Counter>();
1558        let mut schedule = Schedule::default();
1559
1560        schedule.add_systems(
1561            (
1562                increment_counter.run_if(every_other_time.and(|| true)), // Run every odd cycle.
1563                increment_counter.run_if(every_other_time.nand(|| false)), // Always run.
1564                double_counter.run_if(every_other_time.nor(|| false)),   // Run every even cycle.
1565                increment_counter.run_if(every_other_time.or(|| true)),  // Always run.
1566                increment_counter.run_if(every_other_time.xnor(|| true)), // Run every odd cycle.
1567                double_counter.run_if(every_other_time.xnor(|| false)),  // Run every even cycle.
1568                increment_counter.run_if(every_other_time.xor(|| false)), // Run every odd cycle.
1569                double_counter.run_if(every_other_time.xor(|| true)),    // Run every even cycle.
1570            )
1571                .chain(),
1572        );
1573
1574        schedule.run(&mut world);
1575        assert_eq!(world.resource::<Counter>().0, 5);
1576        schedule.run(&mut world);
1577        assert_eq!(world.resource::<Counter>().0, 52);
1578    }
1579
1580    #[test]
1581    fn multiple_run_conditions() {
1582        let mut world = World::new();
1583        world.init_resource::<Counter>();
1584        let mut schedule = Schedule::default();
1585
1586        // Run every other cycle
1587        schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| true));
1588        // Never run
1589        schedule.add_systems(increment_counter.run_if(every_other_time).run_if(|| false));
1590
1591        schedule.run(&mut world);
1592        assert_eq!(world.resource::<Counter>().0, 1);
1593        schedule.run(&mut world);
1594        assert_eq!(world.resource::<Counter>().0, 1);
1595    }
1596
1597    #[test]
1598    fn multiple_run_conditions_is_and_operation() {
1599        let mut world = World::new();
1600        world.init_resource::<Counter>();
1601
1602        let mut schedule = Schedule::default();
1603
1604        // This should never run, if multiple run conditions worked
1605        // like an OR condition then it would always run
1606        schedule.add_systems(
1607            increment_counter
1608                .run_if(every_other_time)
1609                .run_if(not(every_other_time)),
1610        );
1611
1612        schedule.run(&mut world);
1613        assert_eq!(world.resource::<Counter>().0, 0);
1614        schedule.run(&mut world);
1615        assert_eq!(world.resource::<Counter>().0, 0);
1616    }
1617    #[derive(Component)]
1618    struct TestComponent;
1619
1620    #[derive(Message)]
1621    struct TestMessage;
1622
1623    #[derive(Resource)]
1624    struct TestResource(());
1625
1626    fn test_system() {}
1627
1628    // Ensure distributive_run_if compiles with the common conditions.
1629    #[test]
1630    fn distributive_run_if_compiles() {
1631        Schedule::default().add_systems(
1632            (test_system, test_system)
1633                .distributive_run_if(run_once)
1634                .distributive_run_if(resource_exists::<TestResource>)
1635                .distributive_run_if(resource_added::<TestResource>)
1636                .distributive_run_if(resource_changed::<TestResource>)
1637                .distributive_run_if(resource_exists_and_changed::<TestResource>)
1638                .distributive_run_if(resource_changed_or_removed::<TestResource>)
1639                .distributive_run_if(resource_removed::<TestResource>)
1640                .distributive_run_if(on_message::<TestMessage>)
1641                .distributive_run_if(any_with_component::<TestComponent>)
1642                .distributive_run_if(any_match_filter::<With<TestComponent>>)
1643                .distributive_run_if(not(run_once)),
1644        );
1645    }
1646
1647    #[test]
1648    fn run_if_error_contains_system() {
1649        let mut world = World::new();
1650        world.insert_resource(DefaultErrorHandler(my_error_handler));
1651
1652        #[derive(Resource)]
1653        struct MyResource;
1654
1655        fn condition(_res: Res<MyResource>) -> bool {
1656            true
1657        }
1658
1659        fn my_error_handler(_: BevyError, ctx: ErrorContext) {
1660            let a = IntoSystem::into_system(system_a);
1661            let b = IntoSystem::into_system(system_b);
1662            assert!(
1663                matches!(ctx, ErrorContext::RunCondition { system, on_set, .. } if (on_set && system == b.name()) || (!on_set && system == a.name()))
1664            );
1665        }
1666
1667        fn system_a() {}
1668        fn system_b() {}
1669
1670        let mut schedule = Schedule::default();
1671        schedule.add_systems(system_a.run_if(condition));
1672        schedule.run(&mut world);
1673
1674        #[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
1675        struct Set;
1676
1677        let mut schedule = Schedule::default();
1678        schedule
1679            .add_systems((system_b,).in_set(Set))
1680            .configure_sets(Set.run_if(condition));
1681        schedule.run(&mut world);
1682    }
1683}