Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename system chaining to system welding #3073

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,8 @@ name = "state"
path = "examples/ecs/state.rs"

[[example]]
name = "system_chaining"
path = "examples/ecs/system_chaining.rs"
name = "system_welding"
path = "examples/ecs/system_welding.rs"

[[example]]
name = "system_param"
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub mod prelude {
Schedule, Stage, StageLabel, State, SystemLabel, SystemSet, SystemStage,
},
system::{
Commands, ConfigurableSystem, In, IntoChainSystem, IntoExclusiveSystem, IntoSystem,
Commands, ConfigurableSystem, In, IntoExclusiveSystem, IntoSystem, IntoWeldSystem,
Local, NonSend, NonSendMut, Query, QuerySet, RemovedComponents, Res, ResMut, System,
},
world::{FromWorld, Mut, World},
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule/stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ impl SystemStage {
fn rebuild_orders_and_dependencies(&mut self) {
// This assertion is there to document that a maximum of `u32::MAX / 8` systems should be
// added to a stage to guarantee that change detection has no false positive, but it
// can be circumvented using exclusive or chained systems
// can be circumvented using exclusive or welded systems
assert!(
self.exclusive_at_start.len()
+ self.exclusive_before_commands.len()
Expand Down
16 changes: 8 additions & 8 deletions crates/bevy_ecs/src/schedule/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
SystemSet,
},
system::{ConfigurableSystem, In, IntoChainSystem, Local, Res, ResMut},
system::{ConfigurableSystem, In, IntoWeldSystem, Local, Res, ResMut},
};
use std::{any::TypeId, fmt::Debug, hash::Hash};
use thiserror::Error;
Expand Down Expand Up @@ -100,7 +100,7 @@ where
state.stack.last().unwrap() == pred.as_ref().unwrap() && state.transition.is_none()
})
.config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>)
.weld(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Update.into_label(s))
}
Expand All @@ -120,7 +120,7 @@ where
None => *is_inactive,
})
.config(|(_, _, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>)
.weld(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InactiveUpdate.into_label(s))
}
Expand Down Expand Up @@ -152,7 +152,7 @@ where
None => *is_in_stack,
})
.config(|(_, _, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>)
.weld(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InStackUpdate.into_label(s))
}
Expand All @@ -171,7 +171,7 @@ where
})
})
.config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>)
.weld(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Enter.into_label(s))
}
Expand All @@ -188,7 +188,7 @@ where
})
})
.config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>)
.weld(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Exit.into_label(s))
}
Expand All @@ -204,7 +204,7 @@ where
})
})
.config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>)
.weld(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Pause.into_label(s))
}
Expand All @@ -220,7 +220,7 @@ where
})
})
.config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>)
.weld(should_run_adapter::<T>)
.after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Resume.into_label(s))
}
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/system/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,16 @@ mod function_system;
mod query;
#[allow(clippy::module_inception)]
mod system;
mod system_chaining;
mod system_param;
mod system_welding;

pub use commands::*;
pub use exclusive_system::*;
pub use function_system::*;
pub use query::*;
pub use system::*;
pub use system_chaining::*;
pub use system_param::*;
pub use system_welding::*;

#[cfg(test)]
mod tests {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,21 @@ use crate::{
};
use std::borrow::Cow;

/// A [`System`] that chains two systems together, creating a new system that routes the output of
/// A [`System`] that welds two systems together, creating a new system that routes the output of
/// the first system into the input of the second system, yielding the output of the second system.
///
/// Given two systems A and B, A may be chained with B as `A.chain(B)` if the output type of A is
/// equal to the input type of B.
/// Given two systems A and B, B may be welded onto A as `A.weld(B)`
/// if the output type of A is the same as the input type of B.
///
/// Note that for [`FunctionSystem`](crate::system::FunctionSystem)s the output is the return value
/// of the function and the input is the first [`SystemParam`](crate::system::SystemParam) if it is
/// tagged with [`In`](crate::system::In) or `()` if the function has no designated input parameter.
/// The output type is the return value of the function.
/// The input type is the first [`SystemParam`](crate::system::SystemParam)
/// if it is wrapped by [`In`](crate::system::In).
/// If no input parameter is designated, the input type is `()`.
///
/// Systems can only be welded in a one-to-one fashion, and
/// welding systems causes them to be treated as a single cohesive unit by the scheduler.
/// System welding is a specialized tool designed to be used when handling the output of systems;
/// for general purpose inter-system communication you should use [`Events`](crate::event::Events).
///
/// # Examples
///
Expand All @@ -28,10 +34,10 @@ use std::borrow::Cow;
/// let mut world = World::default();
/// world.insert_resource(Message("42".to_string()));
///
/// // chain the `parse_message_system`'s output into the `filter_system`s input
/// let mut chained_system = parse_message_system.chain(filter_system);
/// chained_system.initialize(&mut world);
/// assert_eq!(chained_system.run((), &mut world), Some(42));
/// // Weld the `parse_message_system`'s output into the `filter_system`s input
/// let mut welded_system = parse_message_system.weld(filter_system);
/// welded_system.initialize(&mut world);
/// assert_eq!(welded_system.run((), &mut world), Some(42));
/// }
///
/// struct Message(String);
Expand All @@ -44,15 +50,15 @@ use std::borrow::Cow;
/// result.ok().filter(|&n| n < 100)
/// }
/// ```
pub struct ChainSystem<SystemA, SystemB> {
pub struct WeldSystem<SystemA, SystemB> {
system_a: SystemA,
system_b: SystemB,
name: Cow<'static, str>,
component_access: Access<ComponentId>,
archetype_component_access: Access<ArchetypeComponentId>,
}

impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for ChainSystem<SystemA, SystemB> {
impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for WeldSystem<SystemA, SystemB> {
type In = SystemA::In;
type Out = SystemB::Out;

Expand Down Expand Up @@ -107,33 +113,33 @@ impl<SystemA: System, SystemB: System<In = SystemA::Out>> System for ChainSystem
}
}

/// An extension trait providing the [`IntoChainSystem::chain`] method for convenient [`System`]
/// chaining.
/// An extension trait providing the [`IntoWeldSystem::weld`] method
/// for convenient [`System`] welding.
///
/// This trait is blanket implemented for all system pairs that fulfill the chaining requirement.
/// This trait is blanket implemented for all system pairs that fulfill the welding requirement.
///
/// See [`ChainSystem`].
pub trait IntoChainSystem<ParamA, Payload, SystemB, ParamB, Out>:
/// See [`WeldSystem`].
pub trait IntoWeldSystem<ParamA, Payload, SystemB, ParamB, Out>:
IntoSystem<(), Payload, ParamA> + Sized
where
SystemB: IntoSystem<Payload, Out, ParamB>,
{
/// Chain this system `A` with another system `B` creating a new system that feeds system A's
/// Weld this system `A` with another system `B` creating a new system that feeds system A's
/// output into system `B`, returning the output of system `B`.
fn chain(self, system: SystemB) -> ChainSystem<Self::System, SystemB::System>;
fn weld(self, system: SystemB) -> WeldSystem<Self::System, SystemB::System>;
}

impl<SystemA, ParamA, Payload, SystemB, ParamB, Out>
IntoChainSystem<ParamA, Payload, SystemB, ParamB, Out> for SystemA
IntoWeldSystem<ParamA, Payload, SystemB, ParamB, Out> for SystemA
where
SystemA: IntoSystem<(), Payload, ParamA>,
SystemB: IntoSystem<Payload, Out, ParamB>,
{
fn chain(self, system: SystemB) -> ChainSystem<SystemA::System, SystemB::System> {
fn weld(self, system: SystemB) -> WeldSystem<SystemA::System, SystemB::System> {
let system_a = self.system();
let system_b = system.system();
ChainSystem {
name: Cow::Owned(format!("Chain({}, {})", system_a.name(), system_b.name())),
WeldSystem {
name: Cow::Owned(format!("Weld({}, {})", system_a.name(), system_b.name())),
system_a,
system_b,
archetype_component_access: Default::default(),
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ Example | File | Description
`removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame.
`startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)
`state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state
`system_chaining` | [`ecs/system_chaining.rs`](./ecs/system_chaining.rs) | Chain two systems together, specifying a return type in a system (such as `Result`)
`system_welding` | [`ecs/system_chaining.rs`](./ecs/system_chaining.rs) | Weld two systems together, passing the returned value from the first system into the second
`system_param` | [`ecs/system_param.rs`](./ecs/system_param.rs) | Illustrates creating custom system parameters with `SystemParam`
`system_sets` | [`ecs/system_sets.rs`](./ecs/system_sets.rs) | Shows `SystemSet` use along with run criterion
`timers` | [`ecs/timers.rs`](./ecs/timers.rs) | Illustrates ticking `Timer` resources inside systems and handling their state
Expand Down
14 changes: 10 additions & 4 deletions examples/ecs/system_chaining.rs → examples/ecs/system_welding.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
use anyhow::Result;
use bevy::prelude::*;

/// System welding is a valuable but niche tool that allows you to pass the output from one system
/// as input into a second system on a one-to-one basis.
///
/// It is most useful for error handling, or applying similar system adaptors to existing code.
/// For more general-purpose system communication, you should probably use Events instead.
/// For system ordering, use `.before` and `.after`.
fn main() {
App::new()
.insert_resource(Message("42".to_string()))
.add_system(parse_message_system.chain(handler_system))
.add_system(parse_message_system.weld(handler_system))
.run();
}

struct Message(String);

// this system produces a Result<usize> output by trying to parse the Message resource
// This system produces a Result<usize> output by trying to parse the Message resource
fn parse_message_system(message: Res<Message>) -> Result<usize> {
Ok(message.0.parse::<usize>()?)
}

// This system takes a Result<usize> input and either prints the parsed value or the error message
// Try changing the Message resource to something that isn't an integer. You should see the error
// message printed.
// Try changing the Message resource to something that isn't an integer.
// You should see the error message printed.
fn handler_system(In(result): In<Result<usize>>) {
match result {
Ok(value) => println!("parsed message: {}", value),
Expand Down