Replace IntoSystemSetConfig with IntoSystemSetConfigs (#9247)

# Objective

- Fixes #9244.

## Solution


- Changed the `(Into)SystemSetConfigs` traits and structs be more like
the `(Into)SystemConfigs` traits and structs.
- Replaced uses of `IntoSystemSetConfig` with `IntoSystemSetConfigs`
- Added generic `ItemConfig` and `ItemConfigs` types.
- Changed `SystemConfig(s)` and `SystemSetConfig(s)` to be type aliases
to `ItemConfig(s)`.
- Added generic `process_configs` to `ScheduleGraph`.
- Changed `configure_sets_inner` and `add_systems_inner` to reuse
`process_configs`.

---

## Changelog

- Added `run_if` to `IntoSystemSetConfigs`
- Deprecated `Schedule::configure_set` and `App::configure_set`
- Removed `IntoSystemSetConfig`

## Migration Guide

- Use `App::configure_sets` instead of `App::configure_set`
- Use `Schedule::configure_sets` instead of `Schedule::configure_set`

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
Edgar Geier 2023-09-05 19:15:27 +02:00 committed by GitHub
parent 04885eb49c
commit 118509e4aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 197 additions and 286 deletions

View File

@ -393,21 +393,14 @@ impl App {
} }
/// Configures a system set in the default schedule, adding the set if it does not exist. /// Configures a system set in the default schedule, adding the set if it does not exist.
#[deprecated(since = "0.12.0", note = "Please use `configure_sets` instead.")]
#[track_caller] #[track_caller]
pub fn configure_set( pub fn configure_set(
&mut self, &mut self,
schedule: impl ScheduleLabel, schedule: impl ScheduleLabel,
set: impl IntoSystemSetConfig, set: impl IntoSystemSetConfigs,
) -> &mut Self { ) -> &mut Self {
let mut schedules = self.world.resource_mut::<Schedules>(); self.configure_sets(schedule, set)
if let Some(schedule) = schedules.get_mut(&schedule) {
schedule.configure_set(set);
} else {
let mut new_schedule = Schedule::new(schedule);
new_schedule.configure_set(set);
schedules.insert(new_schedule);
}
self
} }
/// Configures a collection of system sets in the default schedule, adding any sets that do not exist. /// Configures a collection of system sets in the default schedule, adding any sets that do not exist.

View File

@ -71,7 +71,7 @@ pub struct AudioPlugin {
impl Plugin for AudioPlugin { impl Plugin for AudioPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.insert_resource(self.global_volume) app.insert_resource(self.global_volume)
.configure_set(PostUpdate, AudioPlaySet.run_if(audio_output_available)) .configure_sets(PostUpdate, AudioPlaySet.run_if(audio_output_available))
.init_resource::<AudioOutput>(); .init_resource::<AudioOutput>();
#[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))] #[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))]

View File

@ -45,8 +45,8 @@ pub mod prelude {
removal_detection::RemovedComponents, removal_detection::RemovedComponents,
schedule::{ schedule::{
apply_deferred, apply_state_transition, common_conditions::*, Condition, apply_deferred, apply_state_transition, common_conditions::*, Condition,
IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfig, IntoSystemSetConfigs, NextState, IntoSystemConfigs, IntoSystemSet, IntoSystemSetConfigs, NextState, OnEnter, OnExit,
OnEnter, OnExit, OnTransition, Schedule, Schedules, State, States, SystemSet, OnTransition, Schedule, Schedules, State, States, SystemSet,
}, },
system::{ system::{
Commands, Deferred, In, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands, Commands, Deferred, In, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,

View File

@ -47,21 +47,24 @@ impl IntoSystemConfigs<()> for BoxedSystem<(), ()> {
} }
} }
/// Stores configuration for a single system. /// Stores configuration for a single generic node.
pub struct SystemConfig { pub struct NodeConfig<T> {
pub(crate) system: BoxedSystem, pub(crate) node: T,
pub(crate) graph_info: GraphInfo, pub(crate) graph_info: GraphInfo,
pub(crate) conditions: Vec<BoxedCondition>, pub(crate) conditions: Vec<BoxedCondition>,
} }
/// A collection of [`SystemConfig`]. /// Stores configuration for a single system.
pub enum SystemConfigs { pub type SystemConfig = NodeConfig<BoxedSystem>;
/// Configuration for a single system.
SystemConfig(SystemConfig), /// A collections of generic [`NodeConfig`]s.
/// Configuration for a tuple of nested `SystemConfigs` instances. pub enum NodeConfigs<T> {
/// Configuratin for a single node.
NodeConfig(NodeConfig<T>),
/// Configuration for a tuple of nested `Configs` instances.
Configs { Configs {
/// Configuration for each element of the tuple. /// Configuration for each element of the tuple.
configs: Vec<SystemConfigs>, configs: Vec<NodeConfigs<T>>,
/// Run conditions applied to everything in the tuple. /// Run conditions applied to everything in the tuple.
collective_conditions: Vec<BoxedCondition>, collective_conditions: Vec<BoxedCondition>,
/// If `true`, adds `before -> after` ordering constraints between the successive elements. /// If `true`, adds `before -> after` ordering constraints between the successive elements.
@ -69,12 +72,15 @@ pub enum SystemConfigs {
}, },
} }
/// A collection of [`SystemConfig`].
pub type SystemConfigs = NodeConfigs<BoxedSystem>;
impl SystemConfigs { impl SystemConfigs {
fn new_system(system: BoxedSystem) -> Self { fn new_system(system: BoxedSystem) -> Self {
// include system in its default sets // include system in its default sets
let sets = system.default_system_sets().into_iter().collect(); let sets = system.default_system_sets().into_iter().collect();
Self::SystemConfig(SystemConfig { Self::NodeConfig(SystemConfig {
system, node: system,
graph_info: GraphInfo { graph_info: GraphInfo {
sets, sets,
..Default::default() ..Default::default()
@ -82,14 +88,16 @@ impl SystemConfigs {
conditions: Vec::new(), conditions: Vec::new(),
}) })
} }
}
impl<T> NodeConfigs<T> {
/// Adds a new boxed system set to the systems. /// Adds a new boxed system set to the systems.
pub fn in_set_dyn(&mut self, set: BoxedSystemSet) { pub fn in_set_dyn(&mut self, set: BoxedSystemSet) {
match self { match self {
SystemConfigs::SystemConfig(config) => { Self::NodeConfig(config) => {
config.graph_info.sets.push(set); config.graph_info.sets.push(set);
} }
SystemConfigs::Configs { configs, .. } => { Self::Configs { configs, .. } => {
for config in configs { for config in configs {
config.in_set_dyn(set.dyn_clone()); config.in_set_dyn(set.dyn_clone());
} }
@ -99,13 +107,13 @@ impl SystemConfigs {
fn before_inner(&mut self, set: BoxedSystemSet) { fn before_inner(&mut self, set: BoxedSystemSet) {
match self { match self {
SystemConfigs::SystemConfig(config) => { Self::NodeConfig(config) => {
config config
.graph_info .graph_info
.dependencies .dependencies
.push(Dependency::new(DependencyKind::Before, set)); .push(Dependency::new(DependencyKind::Before, set));
} }
SystemConfigs::Configs { configs, .. } => { Self::Configs { configs, .. } => {
for config in configs { for config in configs {
config.before_inner(set.dyn_clone()); config.before_inner(set.dyn_clone());
} }
@ -115,13 +123,13 @@ impl SystemConfigs {
fn after_inner(&mut self, set: BoxedSystemSet) { fn after_inner(&mut self, set: BoxedSystemSet) {
match self { match self {
SystemConfigs::SystemConfig(config) => { Self::NodeConfig(config) => {
config config
.graph_info .graph_info
.dependencies .dependencies
.push(Dependency::new(DependencyKind::After, set)); .push(Dependency::new(DependencyKind::After, set));
} }
SystemConfigs::Configs { configs, .. } => { Self::Configs { configs, .. } => {
for config in configs { for config in configs {
config.after_inner(set.dyn_clone()); config.after_inner(set.dyn_clone());
} }
@ -131,10 +139,10 @@ impl SystemConfigs {
fn distributive_run_if_inner<M>(&mut self, condition: impl Condition<M> + Clone) { fn distributive_run_if_inner<M>(&mut self, condition: impl Condition<M> + Clone) {
match self { match self {
SystemConfigs::SystemConfig(config) => { Self::NodeConfig(config) => {
config.conditions.push(new_condition(condition)); config.conditions.push(new_condition(condition));
} }
SystemConfigs::Configs { configs, .. } => { Self::Configs { configs, .. } => {
for config in configs { for config in configs {
config.distributive_run_if_inner(condition.clone()); config.distributive_run_if_inner(condition.clone());
} }
@ -144,10 +152,10 @@ impl SystemConfigs {
fn ambiguous_with_inner(&mut self, set: BoxedSystemSet) { fn ambiguous_with_inner(&mut self, set: BoxedSystemSet) {
match self { match self {
SystemConfigs::SystemConfig(config) => { Self::NodeConfig(config) => {
ambiguous_with(&mut config.graph_info, set); ambiguous_with(&mut config.graph_info, set);
} }
SystemConfigs::Configs { configs, .. } => { Self::Configs { configs, .. } => {
for config in configs { for config in configs {
config.ambiguous_with_inner(set.dyn_clone()); config.ambiguous_with_inner(set.dyn_clone());
} }
@ -157,10 +165,10 @@ impl SystemConfigs {
fn ambiguous_with_all_inner(&mut self) { fn ambiguous_with_all_inner(&mut self) {
match self { match self {
SystemConfigs::SystemConfig(config) => { Self::NodeConfig(config) => {
config.graph_info.ambiguous_with = Ambiguity::IgnoreAll; config.graph_info.ambiguous_with = Ambiguity::IgnoreAll;
} }
SystemConfigs::Configs { configs, .. } => { Self::Configs { configs, .. } => {
for config in configs { for config in configs {
config.ambiguous_with_all_inner(); config.ambiguous_with_all_inner();
} }
@ -174,10 +182,10 @@ impl SystemConfigs {
/// Prefer `run_if` for run conditions whose type is known at compile time. /// Prefer `run_if` for run conditions whose type is known at compile time.
pub fn run_if_dyn(&mut self, condition: BoxedCondition) { pub fn run_if_dyn(&mut self, condition: BoxedCondition) {
match self { match self {
SystemConfigs::SystemConfig(config) => { Self::NodeConfig(config) => {
config.conditions.push(condition); config.conditions.push(condition);
} }
SystemConfigs::Configs { Self::Configs {
collective_conditions, collective_conditions,
.. ..
} => { } => {
@ -185,6 +193,16 @@ impl SystemConfigs {
} }
} }
} }
fn chain_inner(mut self) -> Self {
match &mut self {
Self::NodeConfig(_) => { /* no op */ }
Self::Configs { chained, .. } => {
*chained = true;
}
}
self
}
} }
/// Types that can convert into a [`SystemConfigs`]. /// Types that can convert into a [`SystemConfigs`].
@ -239,7 +257,7 @@ where
/// that all evaluations in a single schedule run will yield the same result. If another /// 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. /// 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 /// Use [`run_if`](IntoSystemSetConfigs::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 /// 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. /// condition for each contained system separately.
fn distributive_run_if<M>(self, condition: impl Condition<M> + Clone) -> SystemConfigs { fn distributive_run_if<M>(self, condition: impl Condition<M> + Clone) -> SystemConfigs {
@ -265,7 +283,7 @@ where
/// # #[derive(SystemSet, Debug, Eq, PartialEq, Hash, Clone, Copy)] /// # #[derive(SystemSet, Debug, Eq, PartialEq, Hash, Clone, Copy)]
/// # struct C; /// # struct C;
/// schedule.add_systems((a, b).run_if(condition)); /// schedule.add_systems((a, b).run_if(condition));
/// schedule.add_systems((a, b).in_set(C)).configure_set(C.run_if(condition)); /// schedule.add_systems((a, b).in_set(C)).configure_sets(C.run_if(condition));
/// ``` /// ```
/// ///
/// # Note /// # Note
@ -350,14 +368,8 @@ impl IntoSystemConfigs<()> for SystemConfigs {
self self
} }
fn chain(mut self) -> Self { fn chain(self) -> Self {
match &mut self { self.chain_inner()
SystemConfigs::SystemConfig(_) => { /* no op */ }
SystemConfigs::Configs { chained, .. } => {
*chained = true;
}
}
self
} }
} }
@ -386,15 +398,11 @@ macro_rules! impl_system_collection {
all_tuples!(impl_system_collection, 1, 20, P, S); all_tuples!(impl_system_collection, 1, 20, P, S);
/// A [`SystemSet`] with scheduling metadata. /// A [`SystemSet`] with scheduling metadata.
pub struct SystemSetConfig { pub type SystemSetConfig = NodeConfig<BoxedSystemSet>;
pub(super) set: BoxedSystemSet,
pub(super) graph_info: GraphInfo,
pub(super) conditions: Vec<BoxedCondition>,
}
impl SystemSetConfig { impl SystemSetConfig {
#[track_caller] #[track_caller]
fn new(set: BoxedSystemSet) -> Self { pub(super) fn new(set: BoxedSystemSet) -> Self {
// system type sets are automatically populated // system type sets are automatically populated
// to avoid unintentionally broad changes, they cannot be configured // to avoid unintentionally broad changes, they cannot be configured
assert!( assert!(
@ -403,118 +411,15 @@ impl SystemSetConfig {
); );
Self { Self {
set, node: set,
graph_info: GraphInfo::default(), graph_info: GraphInfo::default(),
conditions: Vec::new(), conditions: Vec::new(),
} }
} }
} }
/// Types that can be converted into a [`SystemSetConfig`].
///
/// This has been implemented for all types that implement [`SystemSet`] and boxed trait objects.
pub trait IntoSystemSetConfig: Sized {
/// Convert into a [`SystemSetConfig`].
#[doc(hidden)]
fn into_config(self) -> SystemSetConfig;
/// Add to the provided `set`.
#[track_caller]
fn in_set(self, set: impl SystemSet) -> SystemSetConfig {
self.into_config().in_set(set)
}
/// Run before all systems in `set`.
fn before<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
self.into_config().before(set)
}
/// Run after all systems in `set`.
fn after<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
self.into_config().after(set)
}
/// Run the systems in this set only if the [`Condition`] is `true`.
///
/// The `Condition` will be evaluated at most once (per schedule run),
/// the first time a system in this set prepares to run.
fn run_if<M>(self, condition: impl Condition<M>) -> SystemSetConfig {
self.into_config().run_if(condition)
}
/// Suppress warnings and errors that would result from systems in this set having ambiguities
/// (conflicting access but indeterminate order) with systems in `set`.
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfig {
self.into_config().ambiguous_with(set)
}
/// Suppress warnings and errors that would result from systems in this set having ambiguities
/// (conflicting access but indeterminate order) with any other system.
fn ambiguous_with_all(self) -> SystemSetConfig {
self.into_config().ambiguous_with_all()
}
}
impl<S: SystemSet> IntoSystemSetConfig for S {
#[track_caller]
fn into_config(self) -> SystemSetConfig {
SystemSetConfig::new(Box::new(self))
}
}
impl IntoSystemSetConfig for BoxedSystemSet {
fn into_config(self) -> SystemSetConfig {
SystemSetConfig::new(self)
}
}
impl IntoSystemSetConfig for SystemSetConfig {
fn into_config(self) -> Self {
self
}
#[track_caller]
fn in_set(mut self, set: impl SystemSet) -> Self {
assert!(
set.system_type().is_none(),
"adding arbitrary systems to a system type set is not allowed"
);
self.graph_info.sets.push(Box::new(set));
self
}
fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
self.graph_info.dependencies.push(Dependency::new(
DependencyKind::Before,
Box::new(set.into_system_set()),
));
self
}
fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
self.graph_info.dependencies.push(Dependency::new(
DependencyKind::After,
Box::new(set.into_system_set()),
));
self
}
fn run_if<M>(mut self, condition: impl Condition<M>) -> Self {
self.conditions.push(new_condition(condition));
self
}
fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
ambiguous_with(&mut self.graph_info, Box::new(set.into_system_set()));
self
}
fn ambiguous_with_all(mut self) -> Self {
self.graph_info.ambiguous_with = Ambiguity::IgnoreAll;
self
}
}
/// A collection of [`SystemSetConfig`]. /// A collection of [`SystemSetConfig`].
pub struct SystemSetConfigs { pub type SystemSetConfigs = NodeConfigs<BoxedSystemSet>;
pub(super) sets: Vec<SystemSetConfig>,
/// If `true`, adds `before -> after` ordering constraints between the successive elements.
pub(super) chained: bool,
}
/// Types that can convert into a [`SystemSetConfigs`]. /// Types that can convert into a [`SystemSetConfigs`].
pub trait IntoSystemSetConfigs pub trait IntoSystemSetConfigs
@ -541,6 +446,14 @@ where
self.into_configs().after(set) self.into_configs().after(set)
} }
/// Run the systems in this set(s) only if the [`Condition`] is `true`.
///
/// The `Condition` will be evaluated at most once (per schedule run),
/// the first time a system in this set(s) prepares to run.
fn run_if<M>(self, condition: impl Condition<M>) -> SystemSetConfigs {
self.into_configs().run_if(condition)
}
/// Suppress warnings and errors that would result from systems in these sets having ambiguities /// Suppress warnings and errors that would result from systems in these sets having ambiguities
/// (conflicting access but indeterminate order) with systems in `set`. /// (conflicting access but indeterminate order) with systems in `set`.
fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfigs { fn ambiguous_with<M>(self, set: impl IntoSystemSet<M>) -> SystemSetConfigs {
@ -572,69 +485,77 @@ impl IntoSystemSetConfigs for SystemSetConfigs {
set.system_type().is_none(), set.system_type().is_none(),
"adding arbitrary systems to a system type set is not allowed" "adding arbitrary systems to a system type set is not allowed"
); );
for config in &mut self.sets { self.in_set_dyn(set.dyn_clone());
config.graph_info.sets.push(set.dyn_clone());
}
self self
} }
fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self { fn before<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set(); let set = set.into_system_set();
for config in &mut self.sets { self.before_inner(set.dyn_clone());
config
.graph_info
.dependencies
.push(Dependency::new(DependencyKind::Before, set.dyn_clone()));
}
self self
} }
fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self { fn after<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set(); let set = set.into_system_set();
for config in &mut self.sets { self.after_inner(set.dyn_clone());
config
.graph_info self
.dependencies
.push(Dependency::new(DependencyKind::After, set.dyn_clone()));
} }
fn run_if<M>(mut self, condition: impl Condition<M>) -> SystemSetConfigs {
self.run_if_dyn(new_condition(condition));
self self
} }
fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self { fn ambiguous_with<M>(mut self, set: impl IntoSystemSet<M>) -> Self {
let set = set.into_system_set(); let set = set.into_system_set();
for config in &mut self.sets { self.ambiguous_with_inner(set.dyn_clone());
ambiguous_with(&mut config.graph_info, set.dyn_clone());
}
self self
} }
fn ambiguous_with_all(mut self) -> Self { fn ambiguous_with_all(mut self) -> Self {
for config in &mut self.sets { self.ambiguous_with_all_inner();
config.graph_info.ambiguous_with = Ambiguity::IgnoreAll;
}
self self
} }
fn chain(mut self) -> Self { fn chain(self) -> Self {
self.chained = true; self.chain_inner()
self }
}
impl<S: SystemSet> IntoSystemSetConfigs for S {
fn into_configs(self) -> SystemSetConfigs {
SystemSetConfigs::NodeConfig(SystemSetConfig::new(Box::new(self)))
}
}
impl IntoSystemSetConfigs for BoxedSystemSet {
fn into_configs(self) -> SystemSetConfigs {
SystemSetConfigs::NodeConfig(SystemSetConfig::new(self))
}
}
impl IntoSystemSetConfigs for SystemSetConfig {
fn into_configs(self) -> SystemSetConfigs {
SystemSetConfigs::NodeConfig(self)
} }
} }
macro_rules! impl_system_set_collection { macro_rules! impl_system_set_collection {
($($set: ident),*) => { ($($set: ident),*) => {
impl<$($set: IntoSystemSetConfig),*> IntoSystemSetConfigs for ($($set,)*) impl<$($set: IntoSystemSetConfigs),*> IntoSystemSetConfigs for ($($set,)*)
{ {
#[allow(non_snake_case)] #[allow(non_snake_case)]
fn into_configs(self) -> SystemSetConfigs { fn into_configs(self) -> SystemSetConfigs {
let ($($set,)*) = self; let ($($set,)*) = self;
SystemSetConfigs { SystemSetConfigs::Configs {
sets: vec![$($set.into_config(),)*], configs: vec![$($set.into_configs(),)*],
collective_conditions: Vec::new(),
chained: false, chained: false,
} }
} }
@ -642,4 +563,4 @@ macro_rules! impl_system_set_collection {
} }
} }
all_tuples!(impl_system_set_collection, 0, 15, S); all_tuples!(impl_system_set_collection, 1, 20, S);

View File

@ -25,7 +25,7 @@ mod tests {
use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::atomic::{AtomicU32, Ordering};
pub use crate as bevy_ecs; pub use crate as bevy_ecs;
pub use crate::schedule::{IntoSystemSetConfig, Schedule, SystemSet}; pub use crate::schedule::{IntoSystemSetConfigs, Schedule, SystemSet};
pub use crate::system::{Res, ResMut}; pub use crate::system::{Res, ResMut};
pub use crate::{prelude::World, system::Resource}; pub use crate::{prelude::World, system::Resource};
@ -145,7 +145,7 @@ mod tests {
assert_eq!(world.resource::<SystemOrder>().0, vec![]); assert_eq!(world.resource::<SystemOrder>().0, vec![]);
// modify the schedule after it's been initialized and test ordering with sets // modify the schedule after it's been initialized and test ordering with sets
schedule.configure_set(TestSet::A.after(named_system)); schedule.configure_sets(TestSet::A.after(named_system));
schedule.add_systems(( schedule.add_systems((
make_function_system(3) make_function_system(3)
.before(TestSet::A) .before(TestSet::A)
@ -339,13 +339,13 @@ mod tests {
world.init_resource::<Counter>(); world.init_resource::<Counter>();
schedule.configure_set(TestSet::A.run_if(|| false).run_if(|| false)); schedule.configure_sets(TestSet::A.run_if(|| false).run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::A)); schedule.add_systems(counting_system.in_set(TestSet::A));
schedule.configure_set(TestSet::B.run_if(|| true).run_if(|| false)); schedule.configure_sets(TestSet::B.run_if(|| true).run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::B)); schedule.add_systems(counting_system.in_set(TestSet::B));
schedule.configure_set(TestSet::C.run_if(|| false).run_if(|| true)); schedule.configure_sets(TestSet::C.run_if(|| false).run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::C)); schedule.add_systems(counting_system.in_set(TestSet::C));
schedule.configure_set(TestSet::D.run_if(|| true).run_if(|| true)); schedule.configure_sets(TestSet::D.run_if(|| true).run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::D)); schedule.add_systems(counting_system.in_set(TestSet::D));
schedule.run(&mut world); schedule.run(&mut world);
@ -359,13 +359,13 @@ mod tests {
world.init_resource::<Counter>(); world.init_resource::<Counter>();
schedule.configure_set(TestSet::A.run_if(|| false)); schedule.configure_sets(TestSet::A.run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::A).run_if(|| false)); schedule.add_systems(counting_system.in_set(TestSet::A).run_if(|| false));
schedule.configure_set(TestSet::B.run_if(|| true)); schedule.configure_sets(TestSet::B.run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::B).run_if(|| false)); schedule.add_systems(counting_system.in_set(TestSet::B).run_if(|| false));
schedule.configure_set(TestSet::C.run_if(|| false)); schedule.configure_sets(TestSet::C.run_if(|| false));
schedule.add_systems(counting_system.in_set(TestSet::C).run_if(|| true)); schedule.add_systems(counting_system.in_set(TestSet::C).run_if(|| true));
schedule.configure_set(TestSet::D.run_if(|| true)); schedule.configure_sets(TestSet::D.run_if(|| true));
schedule.add_systems(counting_system.in_set(TestSet::D).run_if(|| true)); schedule.add_systems(counting_system.in_set(TestSet::D).run_if(|| true));
schedule.run(&mut world); schedule.run(&mut world);
@ -431,7 +431,7 @@ mod tests {
world.init_resource::<Bool2>(); world.init_resource::<Bool2>();
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.configure_set( schedule.configure_sets(
TestSet::A TestSet::A
.run_if(|res1: Res<RunConditionBool>| res1.is_changed()) .run_if(|res1: Res<RunConditionBool>| res1.is_changed())
.run_if(|res2: Res<Bool2>| res2.is_changed()), .run_if(|res2: Res<Bool2>| res2.is_changed()),
@ -482,7 +482,7 @@ mod tests {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule schedule
.configure_set(TestSet::A.run_if(|res1: Res<RunConditionBool>| res1.is_changed())); .configure_sets(TestSet::A.run_if(|res1: Res<RunConditionBool>| res1.is_changed()));
schedule.add_systems( schedule.add_systems(
counting_system counting_system
@ -529,7 +529,7 @@ mod tests {
#[should_panic] #[should_panic]
fn dependency_loop() { fn dependency_loop() {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.configure_set(TestSet::X.after(TestSet::X)); schedule.configure_sets(TestSet::X.after(TestSet::X));
} }
#[test] #[test]
@ -537,8 +537,8 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.configure_set(TestSet::A.after(TestSet::B)); schedule.configure_sets(TestSet::A.after(TestSet::B));
schedule.configure_set(TestSet::B.after(TestSet::A)); schedule.configure_sets(TestSet::B.after(TestSet::A));
let result = schedule.initialize(&mut world); let result = schedule.initialize(&mut world);
assert!(matches!( assert!(matches!(
@ -564,7 +564,7 @@ mod tests {
#[should_panic] #[should_panic]
fn hierarchy_loop() { fn hierarchy_loop() {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.configure_set(TestSet::X.in_set(TestSet::X)); schedule.configure_sets(TestSet::X.in_set(TestSet::X));
} }
#[test] #[test]
@ -572,8 +572,8 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.configure_set(TestSet::A.in_set(TestSet::B)); schedule.configure_sets(TestSet::A.in_set(TestSet::B));
schedule.configure_set(TestSet::B.in_set(TestSet::A)); schedule.configure_sets(TestSet::B.in_set(TestSet::A));
let result = schedule.initialize(&mut world); let result = schedule.initialize(&mut world);
assert!(matches!(result, Err(ScheduleBuildError::HierarchyCycle(_)))); assert!(matches!(result, Err(ScheduleBuildError::HierarchyCycle(_))));
@ -625,7 +625,7 @@ mod tests {
fn configure_system_type_set() { fn configure_system_type_set() {
fn foo() {} fn foo() {}
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.configure_set(foo.into_system_set()); schedule.configure_sets(foo.into_system_set());
} }
#[test] #[test]
@ -639,13 +639,13 @@ mod tests {
}); });
// Add `A`. // Add `A`.
schedule.configure_set(TestSet::A); schedule.configure_sets(TestSet::A);
// Add `B` as child of `A`. // Add `B` as child of `A`.
schedule.configure_set(TestSet::B.in_set(TestSet::A)); schedule.configure_sets(TestSet::B.in_set(TestSet::A));
// Add `X` as child of both `A` and `B`. // Add `X` as child of both `A` and `B`.
schedule.configure_set(TestSet::X.in_set(TestSet::A).in_set(TestSet::B)); schedule.configure_sets(TestSet::X.in_set(TestSet::A).in_set(TestSet::B));
// `X` cannot be the `A`'s child and grandchild at the same time. // `X` cannot be the `A`'s child and grandchild at the same time.
let result = schedule.initialize(&mut world); let result = schedule.initialize(&mut world);
@ -661,8 +661,8 @@ mod tests {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
// Add `B` and give it both kinds of relationships with `A`. // Add `B` and give it both kinds of relationships with `A`.
schedule.configure_set(TestSet::B.in_set(TestSet::A)); schedule.configure_sets(TestSet::B.in_set(TestSet::A));
schedule.configure_set(TestSet::B.after(TestSet::A)); schedule.configure_sets(TestSet::B.after(TestSet::A));
let result = schedule.initialize(&mut world); let result = schedule.initialize(&mut world);
assert!(matches!( assert!(matches!(
result, result,

View File

@ -192,15 +192,15 @@ impl Schedule {
/// Add a collection of systems to the schedule. /// Add a collection of systems to the schedule.
pub fn add_systems<M>(&mut self, systems: impl IntoSystemConfigs<M>) -> &mut Self { pub fn add_systems<M>(&mut self, systems: impl IntoSystemConfigs<M>) -> &mut Self {
self.graph.add_systems_inner(systems.into_configs(), false); self.graph.process_configs(systems.into_configs(), false);
self self
} }
/// Configures a system set in this schedule, adding it if it does not exist. /// Configures a system set in this schedule, adding it if it does not exist.
#[deprecated(since = "0.12.0", note = "Please use `configure_sets` instead.")]
#[track_caller] #[track_caller]
pub fn configure_set(&mut self, set: impl IntoSystemSetConfig) -> &mut Self { pub fn configure_set(&mut self, set: impl IntoSystemSetConfigs) -> &mut Self {
self.graph.configure_set(set); self.configure_sets(set)
self
} }
/// Configures a collection of system sets in this schedule, adding them if they does not exist. /// Configures a collection of system sets in this schedule, adding them if they does not exist.
@ -522,30 +522,36 @@ impl ScheduleGraph {
&self.conflicting_systems &self.conflicting_systems
} }
/// Adds the systems to the graph. Returns a vector of all node ids contained the nested `SystemConfigs` /// Adds the config nodes to the graph.
/// if `ancestor_chained` is true. Also returns true if "densely chained", meaning that all nested items ///
/// are linearly chained in the order they are defined /// `collect_nodes` controls whether the `NodeId`s of the processed config nodes are stored in the returned [`ProcessConfigsResult`].
fn add_systems_inner( /// `process_config` is the function which processes each individual config node and returns a corresponding `NodeId`.
///
/// The fields on the returned [`ProcessConfigsResult`] are:
/// - `nodes`: a vector of all node ids contained in the nested `NodeConfigs`
/// - `densely_chained`: a boolean that is true if all nested nodes are linearly chained (with successive `after` orderings) in the order they are defined
#[track_caller]
fn process_configs<T: ProcessNodeConfig>(
&mut self, &mut self,
configs: SystemConfigs, configs: NodeConfigs<T>,
ancestor_chained: bool, collect_nodes: bool,
) -> AddSystemsInnerResult { ) -> ProcessConfigsResult {
match configs { match configs {
SystemConfigs::SystemConfig(config) => { NodeConfigs::NodeConfig(config) => {
let node_id = self.add_system_inner(config).unwrap(); let node_id = T::process_config(self, config);
if ancestor_chained { if collect_nodes {
AddSystemsInnerResult { ProcessConfigsResult {
densely_chained: true, densely_chained: true,
nodes: vec![node_id], nodes: vec![node_id],
} }
} else { } else {
AddSystemsInnerResult { ProcessConfigsResult {
densely_chained: true, densely_chained: true,
nodes: Vec::new(), nodes: Vec::new(),
} }
} }
} }
SystemConfigs::Configs { NodeConfigs::Configs {
mut configs, mut configs,
collective_conditions, collective_conditions,
chained, chained,
@ -557,9 +563,9 @@ impl ScheduleGraph {
for config in &mut configs { for config in &mut configs {
config.in_set_dyn(set.dyn_clone()); config.in_set_dyn(set.dyn_clone());
} }
let mut set_config = set.into_config(); let mut set_config = SystemSetConfig::new(set.dyn_clone());
set_config.conditions.extend(collective_conditions); set_config.conditions.extend(collective_conditions);
self.configure_set(set_config); self.configure_set_inner(set_config).unwrap();
} else { } else {
for condition in collective_conditions { for condition in collective_conditions {
configs[0].run_if_dyn(condition); configs[0].run_if_dyn(condition);
@ -571,15 +577,15 @@ impl ScheduleGraph {
let mut densely_chained = true; let mut densely_chained = true;
if chained { if chained {
let Some(prev) = config_iter.next() else { let Some(prev) = config_iter.next() else {
return AddSystemsInnerResult { return ProcessConfigsResult {
nodes: Vec::new(), nodes: Vec::new(),
densely_chained: true, densely_chained: true,
}; };
}; };
let mut previous_result = self.add_systems_inner(prev, true); let mut previous_result = self.process_configs(prev, true);
densely_chained = previous_result.densely_chained; densely_chained = previous_result.densely_chained;
for current in config_iter { for current in config_iter {
let current_result = self.add_systems_inner(current, true); let current_result = self.process_configs(current, true);
densely_chained = densely_chained && current_result.densely_chained; densely_chained = densely_chained && current_result.densely_chained;
match ( match (
previous_result.densely_chained, previous_result.densely_chained,
@ -635,7 +641,7 @@ impl ScheduleGraph {
} }
} }
if ancestor_chained { if collect_nodes {
nodes_in_scope.append(&mut previous_result.nodes); nodes_in_scope.append(&mut previous_result.nodes);
} }
@ -643,14 +649,14 @@ impl ScheduleGraph {
} }
// ensure the last config's nodes are added // ensure the last config's nodes are added
if ancestor_chained { if collect_nodes {
nodes_in_scope.append(&mut previous_result.nodes); nodes_in_scope.append(&mut previous_result.nodes);
} }
} else { } else {
for config in config_iter { for config in config_iter {
let result = self.add_systems_inner(config, ancestor_chained); let result = self.process_configs(config, collect_nodes);
densely_chained = densely_chained && result.densely_chained; densely_chained = densely_chained && result.densely_chained;
if ancestor_chained { if collect_nodes {
nodes_in_scope.extend(result.nodes); nodes_in_scope.extend(result.nodes);
} }
} }
@ -661,7 +667,7 @@ impl ScheduleGraph {
} }
} }
AddSystemsInnerResult { ProcessConfigsResult {
nodes: nodes_in_scope, nodes: nodes_in_scope,
densely_chained, densely_chained,
} }
@ -677,7 +683,7 @@ impl ScheduleGraph {
// system init has to be deferred (need `&mut World`) // system init has to be deferred (need `&mut World`)
self.uninit.push((id, 0)); self.uninit.push((id, 0));
self.systems.push(SystemNode::new(config.system)); self.systems.push(SystemNode::new(config.node));
self.system_conditions.push(config.conditions); self.system_conditions.push(config.conditions);
Ok(id) Ok(id)
@ -685,46 +691,15 @@ impl ScheduleGraph {
#[track_caller] #[track_caller]
fn configure_sets(&mut self, sets: impl IntoSystemSetConfigs) { fn configure_sets(&mut self, sets: impl IntoSystemSetConfigs) {
let SystemSetConfigs { sets, chained } = sets.into_configs(); self.process_configs(sets.into_configs(), false);
let mut set_iter = sets.into_iter();
if chained {
let Some(prev) = set_iter.next() else { return };
let mut prev_id = self.configure_set_inner(prev).unwrap();
for next in set_iter {
let next_id = self.configure_set_inner(next).unwrap();
self.dependency.graph.add_edge(prev_id, next_id, ());
prev_id = next_id;
}
} else {
for set in set_iter {
if let Err(e) = self.configure_set_inner(set) {
// using `unwrap_or_else(panic!)` led to the error being reported
// from this line instead of in the user code
panic!("{e}");
};
}
}
} }
#[track_caller] fn configure_set_inner(&mut self, set: SystemSetConfig) -> Result<NodeId, ScheduleBuildError> {
fn configure_set(&mut self, set: impl IntoSystemSetConfig) {
if let Err(e) = self.configure_set_inner(set) {
// using `unwrap_or_else(panic!)` led to the error being reported
// from this line instead of in the user code
panic!("{e}");
};
}
#[track_caller]
fn configure_set_inner(
&mut self,
set: impl IntoSystemSetConfig,
) -> Result<NodeId, ScheduleBuildError> {
let SystemSetConfig { let SystemSetConfig {
set, node: set,
graph_info, graph_info,
mut conditions, mut conditions,
} = set.into_config(); } = set;
let id = match self.system_set_ids.get(&set) { let id = match self.system_set_ids.get(&set) {
Some(&id) => id, Some(&id) => id,
@ -1240,14 +1215,35 @@ impl ScheduleGraph {
} }
} }
/// Values returned by `ScheduleGraph::add_systems_inner` /// Values returned by [`ScheduleGraph::process_configs`]
struct AddSystemsInnerResult { struct ProcessConfigsResult {
/// All nodes contained inside this add_systems_inner call's SystemConfigs hierarchy /// All nodes contained inside this process_configs call's [`NodeConfigs`] hierarchy,
/// if `ancestor_chained` is true
nodes: Vec<NodeId>, nodes: Vec<NodeId>,
/// True if and only if all nodes are "densely chained" /// True if and only if all nodes are "densely chained", meaning that all nested nodes
/// are linearly chained (as if `after` system ordering had been applied between each node)
/// in the order they are defined
densely_chained: bool, densely_chained: bool,
} }
/// Trait used by [`ScheduleGraph::process_configs`] to process a single [`NodeConfig`].
trait ProcessNodeConfig: Sized {
/// Process a single [`NodeConfig`].
fn process_config(schedule_graph: &mut ScheduleGraph, config: NodeConfig<Self>) -> NodeId;
}
impl ProcessNodeConfig for BoxedSystem {
fn process_config(schedule_graph: &mut ScheduleGraph, config: NodeConfig<Self>) -> NodeId {
schedule_graph.add_system_inner(config).unwrap()
}
}
impl ProcessNodeConfig for BoxedSystemSet {
fn process_config(schedule_graph: &mut ScheduleGraph, config: NodeConfig<Self>) -> NodeId {
schedule_graph.configure_set_inner(config).unwrap()
}
}
/// Used to select the appropriate reporting function. /// Used to select the appropriate reporting function.
enum ReportCycles { enum ReportCycles {
Hierarchy, Hierarchy,
@ -1718,7 +1714,7 @@ impl ScheduleBuildSettings {
mod tests { mod tests {
use crate::{ use crate::{
self as bevy_ecs, self as bevy_ecs,
schedule::{IntoSystemConfigs, IntoSystemSetConfig, Schedule, SystemSet}, schedule::{IntoSystemConfigs, IntoSystemSetConfigs, Schedule, SystemSet},
world::World, world::World,
}; };
@ -1731,7 +1727,7 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
schedule.configure_set(Set.run_if(|| false)); schedule.configure_sets(Set.run_if(|| false));
schedule.add_systems( schedule.add_systems(
(|| panic!("This system must not run")) (|| panic!("This system must not run"))
.ambiguous_with(|| ()) .ambiguous_with(|| ())

View File

@ -110,7 +110,8 @@ impl<T> SystemSet for SystemTypeSet<T> {
} }
/// A [`SystemSet`] implicitly created when using /// A [`SystemSet`] implicitly created when using
/// [`Schedule::add_systems`](super::Schedule::add_systems). /// [`Schedule::add_systems`](super::Schedule::add_systems) or
/// [`Schedule::configure_sets`](super::Schedule::configure_sets).
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct AnonymousSet(usize); pub struct AnonymousSet(usize);

View File

@ -46,7 +46,7 @@
//! You can **explicitly order** systems: //! You can **explicitly order** systems:
//! //!
//! - by calling the `.before(this_system)` or `.after(that_system)` methods when adding them to your schedule //! - by calling the `.before(this_system)` or `.after(that_system)` methods when adding them to your schedule
//! - by adding them to a [`SystemSet`], and then using `.configure_set(ThisSet.before(ThatSet))` syntax to configure many systems at once //! - by adding them to a [`SystemSet`], and then using `.configure_sets(ThisSet.before(ThatSet))` syntax to configure many systems at once
//! - through the use of `.add_systems((system_a, system_b, system_c).chain())` //! - through the use of `.add_systems((system_a, system_b, system_c).chain())`
//! //!
//! [`SystemSet`]: crate::schedule::SystemSet //! [`SystemSet`]: crate::schedule::SystemSet

View File

@ -161,7 +161,7 @@ impl Render {
); );
schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain()); schedule.configure_sets((ExtractCommands, PrepareAssets, Prepare).chain());
schedule.configure_set( schedule.configure_sets(
QueueMeshes QueueMeshes
.in_set(RenderSet::Queue) .in_set(RenderSet::Queue)
.after(prepare_assets::<Mesh>), .after(prepare_assets::<Mesh>),

View File

@ -216,7 +216,7 @@ impl Plugin for VisibilityPlugin {
app app
// We add an AABB component in CalculateBounds, which must be ready on the same frame. // We add an AABB component in CalculateBounds, which must be ready on the same frame.
.add_systems(PostUpdate, apply_deferred.in_set(CalculateBoundsFlush)) .add_systems(PostUpdate, apply_deferred.in_set(CalculateBoundsFlush))
.configure_set(PostUpdate, CalculateBoundsFlush.after(CalculateBounds)) .configure_sets(PostUpdate, CalculateBoundsFlush.after(CalculateBounds))
.add_systems( .add_systems(
PostUpdate, PostUpdate,
( (

View File

@ -102,7 +102,7 @@ impl Plugin for TransformPlugin {
app.register_type::<Transform>() app.register_type::<Transform>()
.register_type::<GlobalTransform>() .register_type::<GlobalTransform>()
.add_plugins(ValidParentCheckPlugin::<GlobalTransform>::default()) .add_plugins(ValidParentCheckPlugin::<GlobalTransform>::default())
.configure_set( .configure_sets(
PostStartup, PostStartup,
PropagateTransformsSet.in_set(TransformSystem::TransformPropagate), PropagateTransformsSet.in_set(TransformSystem::TransformPropagate),
) )
@ -119,7 +119,7 @@ impl Plugin for TransformPlugin {
propagate_transforms.in_set(PropagateTransformsSet), propagate_transforms.in_set(PropagateTransformsSet),
), ),
) )
.configure_set( .configure_sets(
PostUpdate, PostUpdate,
PropagateTransformsSet.in_set(TransformSystem::TransformPropagate), PropagateTransformsSet.in_set(TransformSystem::TransformPropagate),
) )