use bevy::prelude::*;
use bevy::remote::{BrpError, RemotePlugin};
mod commands;
mod input;
mod quit;
mod ready;
mod pointer;
mod ring;
mod scene;
mod time;
#[cfg(feature = "plugin-guide")]
mod guide;
#[cfg(feature = "plugin-ui-text")]
mod ui_text;
#[cfg(feature = "plugin-screenshot")]
mod screenshot;
pub use commands::{DebugCommandInfo, DebugCommands, DebugCommandsAppExt};
pub use input::{
apply_pending_input, cursor_position, logical_for, typed, DebugCursor, InputAction,
InputCommand, InputKind, PendingInput,
};
pub use ready::DebugReady;
pub use time::ClockRequest;
pub use scene::{SceneLog, ScenePlayback, SceneStep};
#[cfg(feature = "plugin-guide")]
pub use guide::{
evaluate_checkpoint, handle_guide, watch_guide, Checkpoints, Guide, GuideOverlay,
GuideOverlayPlugin, GuideParams, GuidePlacement, Step, StepRecord, BEAT_SECONDS,
};
#[cfg(feature = "plugin-screenshot")]
pub use screenshot::{
handle_screenshot, CaptureState, Captures, DebugCaptureTarget, Provenance, ScreenshotParams,
};
pub use input::handle_input;
pub(crate) fn brp_error(code: i16, message: impl Into<String>) -> BrpError {
BrpError {
code,
message: message.into(),
data: None,
}
}
#[derive(Default)]
pub struct DebuggerPlugin {
host_signals_ready: bool,
host_owns_clock: bool,
}
impl DebuggerPlugin {
pub fn host_signals_ready(mut self) -> Self {
self.host_signals_ready = true;
self
}
}
impl DebuggerPlugin {
pub fn host_owns_clock(mut self) -> Self {
self.host_owns_clock = true;
self
}
}
impl Plugin for DebuggerPlugin {
fn build(&self, app: &mut App) {
let remote = RemotePlugin::default()
.with_method_main("bevy_fairy/input", input::handle_input)
.with_method_main("bevy_fairy/status", ready::handle_status)
.with_method_main("bevy_fairy/commands", commands::handle_commands)
.with_method_main("bevy_fairy/scene", scene::handle_scene)
.with_method_main("bevy_fairy/quit", quit::handle_quit);
#[cfg(feature = "plugin-screenshot")]
let remote = remote
.with_method_main("bevy_fairy/screenshot", screenshot::handle_screenshot)
.with_method_main("bevy_fairy/capture", screenshot::handle_capture);
#[cfg(feature = "plugin-guide")]
let remote = remote
.with_method_main("bevy_fairy/guide", guide::handle_guide)
.with_watching_method_main("bevy_fairy/guide+watch", guide::watch_guide);
app.add_plugins(remote);
app.insert_resource(DebugReady(!self.host_signals_ready))
.init_resource::<DebugCommands>()
.init_resource::<SceneLog>()
.init_resource::<ready::FrameTicks>()
.insert_resource(ready::GameInstance::new())
.add_systems(Last, ready::tick_frames)
.add_systems(Update, scene::replay_next_step);
#[cfg(feature = "plugin-screenshot")]
app.init_resource::<Captures>();
time::register(app, self.host_owns_clock);
ring::register(app);
#[cfg(feature = "plugin-ui-text")]
ui_text::register(app);
if let Ok(path) = std::env::var(scene::SCENE_ENV) {
scene::start_playback(app, &path);
}
#[cfg(feature = "plugin-guide")]
app.add_plugins(guide::GuideOverlayPlugin)
.init_resource::<guide::Guide>()
.init_resource::<guide::Checkpoints>()
.add_systems(Last, guide::evaluate_checkpoint);
app.init_resource::<input::PendingInput>()
.add_message::<bevy::input::keyboard::KeyboardInput>()
.add_message::<bevy::input::mouse::MouseButtonInput>()
.add_message::<bevy::input::mouse::MouseWheel>()
.add_message::<bevy::window::WindowEvent>()
.add_message::<bevy::window::CursorMoved>()
.init_resource::<input::DebugCursor>()
.add_systems(
PreUpdate,
input::apply_pending_input.before(bevy::input::InputSystems),
);
#[cfg(feature = "plugin-screenshot")]
app.configure_sets(
PreUpdate,
bevy::picking::PickingSystems::ProcessInput.after(input::apply_pending_input),
);
}
fn finish(&self, app: &mut App) {
assert!(
app.is_plugin_added::<bevy::input::InputPlugin>(),
"DebuggerPlugin needs bevy::input::InputPlugin: injected keys and clicks are written as KeyboardInput/MouseButtonInput messages, and InputPlugin owns the systems that fold those into ButtonInput. Without it every injection is accepted and read by nobody. Add InputPlugin (DefaultPlugins includes it; MinimalPlugins does not)."
);
}
}