Expand description
Threaded multi-world management and signal transmission for bevy.
A SubWorld is a Component that stores a JoinHandle to a thread running an App. Supported operations include sending an exit signal, checking if the SubWorld has completed its startup schedule, and extracting data with pollable extract systems.
Extraction is when a system is sent to a SubWorld to be ran. The output of this system can
be polled with an ExtractHandle
, similar to an async task or a bevy task.
Signals are either components or resources. When a Signal is a Resource,
it sends or receives signals from a parent world. When a Signal is a Component,
it sends and receives signals to/from a subworld. Signals receivers/transmitters
are automatically inserted onto the SubWorld as components and within the SubWorld
as Resources when SubWorldBuilder::with_signal
is called.
§Usage
This simple example spawns a SubWorld and ping/pongs a signal back and forth. It also demonstrates extraction by reading and mutating a resource in the SubWorld.
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(MinimalPlugins)
.add_systems(Startup, spawn_subworld)
.add_systems(Update, (
send_pings,
extract_data,
))
.run();
}
// Your custom signal type
enum MySignal {
Ping,
Pong
}
#[derive(Resource)]
struct GameData {
num: u32,
}
fn spawn_subworld(
world_id: WorldId,
mut commands: Commands,
) {
SubWorld::build(world_id, &mut commands)
.with_signal::<MySignal>() // < register your signal type
.start(move |app| {
app
.insert_resource(GameData { num: 1 })
.add_systems(Update, respond_to_pings);
});
}
/// Extract and increment the GameState's `num` variable from each system.
fn extract_data(
mut query: Query<(&SubWorld, Entity, Option<&mut ExtractHandle<u32>>)>,
mut commands: Commands,
) {
for (subworld, entity, handle) in &mut query {
if let Some(mut handle) = handle {
if let Ok(num) = handle.poll() {
log::info!("Extracted Number: '{num}'. Time: '{:?}'", handle.duration().unwrap());
commands.entity(entity).remove::<ExtractHandle<u32>>();
if num > 100 {
subworld.exit();
}
}
} else {
let handle: ExtractHandle<u32> = subworld.extract(
move |mut state: ResMut<GameData>| {
state.num += 1;
state.num
}
);
commands.entity(entity).insert(handle);
}
}
}
/// Send pings to Subworlds
fn send_pings(
mut query: Query<(&mut SignalRx<MySignal>, &SignalTx<MySignal>), With<SubWorld>>,
) {
for (mut rx, tx) in &mut query {
for signal in &mut rx {
match signal {
MySignal::Ping => log::info!("Ping Received!"),
MySignal::Pong => {
tx.send(MySignal::Ping)
}
}
}
tx.send(MySignal::Ping);
}
}
/// Respond to pings as a SubWorld
fn respond_to_pings(
mut signal_rx: ResMut<SignalRx<MySignal>>,
signal_tx: Res<SignalTx<MySignal>>,
) {
for signal in &mut signal_rx {
match signal {
MySignal::Ping => {
let _ = signal_tx.send(MySignal::Pong);
},
MySignal::Pong => {}
}
}
}
Structs§
- Extract
Handle - A Handle to a system running in a SubWorld. This handle can be polled to get the output.
- Signal
Rx - A signal receiver between a SubWorld and SuperWorld. When used as a Resource, these are signals from the SuperWorld it belongs to. When used as a Component, these are signals from the SubWorld it is attached to.
- Signal
Tx - A signal transmitter between a SubWorld and SuperWorld. When used as a Resource, these are signals to the SuperWorld it belongs to. When used as a Component, these are signals to a SubWorld it is attached to.
- SubWorld
- A Handle to a bevy App running in its own thread.
- SubWorld
Builder - Super
World - A handle to the parent World of a SubWorld.