use bevy::prelude::*;
pub fn shuffle_schedules(app: &mut App) {
#[cfg(feature = "schedule_shuffle")]
if let Ok(seed) = std::env::var("BEVY_REACT_SHUFFLE_SEED") {
let seed = seed.parse().expect("BEVY_REACT_SHUFFLE_SEED is a u64");
eprintln!("schedules shuffled with BEVY_REACT_SHUFFLE_SEED={seed}");
shuffle(app, seed);
}
#[cfg(not(feature = "schedule_shuffle"))]
let _ = app;
}
#[cfg(feature = "schedule_shuffle")]
fn shuffle(app: &mut App, seed: u64) {
use bevy::ecs::schedule::{ScheduleLabel, SingleThreadedExecutor};
let labels = [
First.intern(),
PreUpdate.intern(),
Update.intern(),
PostUpdate.intern(),
Last.intern(),
];
for label in labels {
app.edit_schedule(label, |schedule| {
let mut settings = schedule.get_build_settings();
settings.shuffle_seed = Some(seed);
schedule
.set_build_settings(settings)
.set_executor(SingleThreadedExecutor::new());
});
}
}
#[cfg(all(test, feature = "schedule_shuffle"))]
mod tests {
use super::*;
#[derive(Resource, Default)]
struct Ran(Vec<u8>);
fn order(seed: u64) -> Vec<u8> {
let mut app = App::new();
shuffle(&mut app, seed);
app.init_resource::<Ran>().add_systems(
Update,
(
|mut ran: ResMut<Ran>| ran.0.push(0),
|mut ran: ResMut<Ran>| ran.0.push(1),
(
|mut ran: ResMut<Ran>| ran.0.push(2),
|mut ran: ResMut<Ran>| ran.0.push(3),
)
.chain(),
),
);
app.update();
std::mem::take(&mut app.world_mut().resource_mut::<Ran>().0)
}
#[test]
fn seeds_reorder_unordered_systems_only() {
let orders: Vec<Vec<u8>> = (0..16).map(order).collect();
assert!(orders.iter().any(|o| *o != orders[0]), "{orders:?}");
assert_eq!(order(5), orders[5]);
for o in &orders {
let at = |n| o.iter().position(|&ran| ran == n).unwrap();
assert!(at(2) < at(3), "{o:?}");
}
}
}