Skip to content

Commit

Permalink
Add distributive_run_if to IntoSystemConfigs (bevyengine#7724)
Browse files Browse the repository at this point in the history
# Objective

- Fixes bevyengine#7659.

## Solution

- This PR extracted the `distributive_run_if` part of bevyengine#7676, because it does not require the controversial introduction of anonymous system sets.
- The distinctive name should make the user aware about the differences between `IntoSystemConfig::run_if` and `IntoSystemConfigs::distributive_run_if`.
- The documentation explains in detail the consequences of using the API and possible pit falls when using it.
- A test demonstrates the possibility of changing the condition result, resulting in some of the systems not being run.

---

## Changelog

### Added
- Add `distributive_run_if` to `IntoSystemConfigs` to enable adding a run condition to each system when using `add_systems`.
  • Loading branch information
geieredgar authored and myreprise1 committed Feb 20, 2023
1 parent 4a123e2 commit 57bd070
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 0 deletions.
42 changes: 42 additions & 0 deletions crates/bevy_ecs/src/schedule/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,40 @@ where
self.into_configs().after(set)
}

/// Add a run condition to each contained system.
///
/// Each system will receive its own clone of the [`Condition`] and will only run
/// if the `Condition` is true.
///
/// Each individual condition will be evaluated at most once (per schedule run),
/// right before the corresponding system prepares to run.
///
/// This is equivalent to calling [`run_if`](IntoSystemConfig::run_if) on each individual
/// system, as shown below:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # let mut app = Schedule::new();
/// # fn a() {}
/// # fn b() {}
/// # fn condition() -> bool { true }
/// app.add_systems((a, b).distributive_run_if(condition));
/// app.add_systems((a.run_if(condition), b.run_if(condition)));
/// ```
///
/// # Note
///
/// Because the conditions are evaluated separately for each system, there is no guarantee
/// that all evaluations in a single schedule run will yield the same result. If another
/// system is run inbetween two evaluations it could cause the result of the condition to change.
///
/// Use [`run_if`](IntoSystemSetConfig::run_if) on a [`SystemSet`] if you want to make sure
/// that either all or none of the systems are run, or you don't want to evaluate the run
/// condition for each contained system separately.
fn distributive_run_if<P>(self, condition: impl Condition<P> + Clone) -> SystemConfigs {
self.into_configs().distributive_run_if(condition)
}

/// Suppress warnings and errors that would result from these systems having ambiguities
/// (conflicting access but indeterminate order) with systems in `set`.
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> SystemConfigs {
Expand Down Expand Up @@ -603,6 +637,14 @@ impl IntoSystemConfigs<()> for SystemConfigs {
self
}

fn distributive_run_if<P>(mut self, condition: impl Condition<P> + Clone) -> SystemConfigs {
for config in &mut self.systems {
config.conditions.push(new_condition(condition.clone()));
}

self
}

fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set();
for config in &mut self.systems {
Expand Down
26 changes: 26 additions & 0 deletions crates/bevy_ecs/src/schedule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,32 @@ mod tests {
assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
}

#[test]
fn systems_with_distributive_condition() {
let mut world = World::default();
let mut schedule = Schedule::default();

world.insert_resource(RunConditionBool(true));
world.init_resource::<SystemOrder>();

fn change_condition(mut condition: ResMut<RunConditionBool>) {
condition.0 = false;
}

schedule.add_systems(
(
make_function_system(0),
change_condition,
make_function_system(1),
)
.chain()
.distributive_run_if(|condition: Res<RunConditionBool>| condition.0),
);

schedule.run(&mut world);
assert_eq!(world.resource::<SystemOrder>().0, vec![0]);
}

#[test]
fn run_exclusive_system_with_condition() {
let mut world = World::default();
Expand Down

0 comments on commit 57bd070

Please sign in to comment.