use bevy::{
app::{AppExit, Events, ManualEventReader},
input::{
keyboard::KeyboardInput,
mouse::{MouseButtonInput, MouseMotion, MouseWheel},
},
prelude::*,
window::WindowResized,
};
use crate::data::FuzzData;
use crate::fuzz_input::FuzzInput;
const CORE_STAGES: &[CoreStage] = &[
CoreStage::First,
CoreStage::PreUpdate,
CoreStage::Update,
CoreStage::PostUpdate,
CoreStage::Last,
];
pub fn fuzz_runner(app: &mut App) {
reset_app_world(app);
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
let mut tick_no = 0;
while let Ok(_) = tick(app, tick_no, &mut app_exit_event_reader) {
tick_no += 1;
}
}
fn tick(
app: &mut App,
_tick_no: usize,
app_exit_event_reader: &mut ManualEventReader<AppExit>,
) -> Result<(), AppExit> {
feed_fuzz_events(app);
for stage in CORE_STAGES {
app.schedule
.stage(stage.clone(), |stage: &mut SystemStage| {
stage.run(&mut app.world);
stage
});
}
if app.world.get_resource::<FuzzData>().unwrap().is_finished() {
return Err(AppExit);
}
if let Some(app_exit_events) = app.world.get_resource_mut::<Events<AppExit>>() {
if let Some(exit) = app_exit_event_reader.iter(&app_exit_events).last() {
return Err(exit.clone());
}
}
Ok(())
}
fn reset_app_world(app: &mut App) {
app.world.clear_entities();
app.schedule
.stage(CoreStage::First, |stage: &mut SystemStage| {
stage.run(&mut app.world);
stage
});
app.schedule
.stage(CoreStage::Startup, |schedule: &mut Schedule| {
schedule.run_once(&mut app.world);
schedule
});
for stage in CORE_STAGES {
app.schedule
.stage(stage.clone(), |stage: &mut SystemStage| {
stage.run(&mut app.world);
stage
});
}
}
fn feed_fuzz_events(app: &mut App) {
let world = app.world.cell();
let mut data = world.get_resource_mut::<FuzzData>().unwrap();
let mut mouse_button_input_events =
world.get_resource_mut::<Events<MouseButtonInput>>().expect(
"Missing MouseButtonInput events (provided by bevy::input::InputPlugin) from the App",
);
let mut mouse_wheel_input_events = world
.get_resource_mut::<Events<MouseWheel>>()
.expect("Missing MouseWheel events (provided by bevy::input::InputPlugin) from the App");
let mut mouse_motion_events = world
.get_resource_mut::<Events<MouseMotion>>()
.expect("Missing MouseMotion events (provided by bevy::input::InputPlugin) from the App");
let mut keyboard_input_events = world
.get_resource_mut::<Events<KeyboardInput>>()
.expect("Missing KeyboardInput events (provided by bevy::input::InputPlugin) from the App");
let mut cursor_moved_events = world
.get_resource_mut::<Events<CursorMoved>>()
.expect("Missing CursorMoved events (provided by bevy::window::WindowPlugin) from the App");
let mut window_resized_events = world.get_resource_mut::<Events<WindowResized>>().expect(
"Missing WindowResized events (provided by bevy::window::WindowPlugin) from the App",
);
let mut break_at_idx = None;
for (idx, event) in data.iter_next().enumerate() {
match event {
FuzzInput::MouseButton(wrap_mouse_button_input) => {
mouse_button_input_events.send(wrap_mouse_button_input.into());
}
FuzzInput::KeyboardInput(keyboard_input) => {
keyboard_input_events.send(KeyboardInput {
scan_code: 0, key_code: match &keyboard_input.key_code {
Some(key_code) => Some(key_code.into()),
None => None,
},
state: (&keyboard_input.state).into(),
});
}
FuzzInput::RunFrame => {
break_at_idx = Some(idx);
break;
}
FuzzInput::MouseWheel(mouse_wheel) => mouse_wheel_input_events.send(mouse_wheel.into()),
FuzzInput::MouseMotion(delta) => {
mouse_motion_events.send(MouseMotion {
delta: delta.into(),
});
}
FuzzInput::CursorMoved(cursor_moved) => cursor_moved_events.send(cursor_moved.into()),
FuzzInput::WindowResized(window_resized) => {
window_resized_events.send(window_resized.into())
}
}
}
data.set_last_idx(break_at_idx);
}