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}