System Chaining

Relevant official examples: system_chaining.


Systems can take an input and produce an output, and be connected together to run as a single larger system (chain).

Think of this as "glue", for constructing a larger system out of multiple Rust functions.

One useful application is to be able to return errors from systems (allowing the use of Rust's ? operator) and handle them elsewhere:

fn net_receive(mut netcode: ResMut<MyNetProto>) -> std::io::Result<()> {
    netcode.receive_updates()?;

    Ok(())
}

fn handle_io_errors(In(result): In<std::io::Result<()>>) {
    if let Err(e) = result {
        eprintln!("I/O error occurred: {}", e);
    }
}

Such systems cannot be registered individually (Bevy doesn't know what to do with the input/output). You have to connect them in a chain:

fn main() {
    App::build()
        // ...
        .add_system(net_receive.system().chain(handle_io_errors.system()))
        // ...
        .run();
}

Chaining effectively constructs a single large system out of modular parts. It is not a channel for passing data around. If you want to pass data between systems, you probably want to use Events instead.

Performance Warning

Beware that Bevy treats the whole chain as if it was a single big system, with all the combined resources and queries. This implies that parallelism could be limited, affecting performance.

Avoid adding a system that requires mutable access to anything, as part of multiple chains. It would block all affected chains (and other systems accessing the same data) from running in parallel.