Compare commits

..
Author SHA1 Message Date
EIREXE f2b51ae03b Try reducing concurrent runs.
CI / build-and-test (pull_request) Successful in 11m30s
2026-09-14 13:47:14 +02:00
EIREXE 075a033a2c Add Bevy runtime packages to CI
CI / build-and-test (pull_request) Failing after 5m52s
2026-09-14 12:41:43 +02:00
EIREXE b13d3ce0b3 Allow more time for CI compilation
CI / build-and-test (pull_request) Failing after 5m53s
2026-09-14 12:10:21 +02:00
EIREXE 9d786d1bd5 Add build and test CI
CI / build-and-test (pull_request) Failing after 5m59s
2026-09-14 12:03:46 +02:00
4 changed files with 123 additions and 422 deletions
+3 -9
View File
@@ -10,7 +10,7 @@ on:
jobs: jobs:
build-and-test: build-and-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 15
steps: steps:
- name: Check out source - name: Check out source
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -20,13 +20,7 @@ jobs:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --profile minimal sh -s -- -y --profile minimal
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH" echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/rustup" toolchain install nightly-2026-09-14 --profile minimal "$HOME/.cargo/bin/rustup" toolchain install nightly --profile minimal
- name: Cache Rust dependencies and build artifacts
uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2
with:
shared-key: linux-nightly
cache-bin: false
- name: Install Linux dependencies - name: Install Linux dependencies
run: | run: |
@@ -45,7 +39,7 @@ jobs:
pkg-config pkg-config
- name: Build - name: Build
run: RUSTFLAGS="-D warnings" cargo build --locked -j 4 run: cargo build --locked -j 4
- name: Run unit tests - name: Run unit tests
run: cargo test --locked --lib -j 4 run: cargo test --locked --lib -j 4
+1 -1
View File
@@ -1,2 +1,2 @@
[toolchain] [toolchain]
channel = "nightly-2026-09-14" channel = "nightly"
+99 -342
View File
@@ -3,12 +3,11 @@
use std::{collections::HashMap, time::Duration}; use std::{collections::HashMap, time::Duration};
use bevy::{ use bevy::{
input::InputSystems,
prelude::*, prelude::*,
settings::{ReflectSettingsGroup, SaveSettingsDeferred, SettingsGroup}, settings::{ReflectSettingsGroup, SaveSettingsDeferred, SettingsGroup},
}; };
use bevy_egui::{EguiContexts, EguiPrimaryContextPass, egui}; use bevy_egui::{EguiContexts, EguiPrimaryContextPass, egui};
use bevy_ggrs::{RollbackFrameRate, RunGgrsSystems}; use bevy_ggrs::RollbackFrameRate;
use enum_cycling::EnumCycle; use enum_cycling::EnumCycle;
use enum_cycling_derive::EnumCycle; use enum_cycling_derive::EnumCycle;
@@ -49,7 +48,7 @@ impl PresentationMotionMode {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, EnumCycle, Reflect)] #[derive(EnumCycle, Reflect)]
pub enum GameSpeedDebug { pub enum GameSpeedDebug {
Full, Full,
Half, Half,
@@ -115,7 +114,6 @@ enum DebugFlag {
Hurtboxes, Hurtboxes,
Camera, Camera,
DrawThroughGeometry, DrawThroughGeometry,
FrameStepping,
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@@ -176,10 +174,6 @@ const WORLD_MENU: &[DebugMenuEntry] = &[
DebugMenuEntry::GameSpeed { DebugMenuEntry::GameSpeed {
label: "Game speed", label: "Game speed",
}, },
DebugMenuEntry::Toggle {
label: "Frame stepping",
flag: DebugFlag::FrameStepping,
},
]; ];
const DIAGNOSTICS_MENU: &[DebugMenuEntry] = &[DebugMenuEntry::Toggle { const DIAGNOSTICS_MENU: &[DebugMenuEntry] = &[DebugMenuEntry::Toggle {
@@ -239,8 +233,6 @@ pub struct DebugSettings {
selections: Vec<usize>, selections: Vec<usize>,
#[reflect(ignore)] #[reflect(ignore)]
pub player_rows: HashMap<usize, [FighterDebugRowKind; PLAYER_DEBUG_ROW_COUNT]>, pub player_rows: HashMap<usize, [FighterDebugRowKind; PLAYER_DEBUG_ROW_COUNT]>,
#[reflect(ignore)]
pub frame_stepping: bool,
pub network: bool, pub network: bool,
pub ecb: bool, pub ecb: bool,
pub stage_collision: bool, pub stage_collision: bool,
@@ -261,7 +253,6 @@ impl Default for DebugSettings {
pages: vec![DebugMenuPage::Static(ROOT_MENU)], pages: vec![DebugMenuPage::Static(ROOT_MENU)],
selections: vec![0], selections: vec![0],
player_rows: HashMap::new(), player_rows: HashMap::new(),
frame_stepping: false,
network: false, network: false,
ecb: false, ecb: false,
stage_collision: false, stage_collision: false,
@@ -334,7 +325,6 @@ impl DebugSettings {
DebugFlag::Hurtboxes => self.hurtboxes, DebugFlag::Hurtboxes => self.hurtboxes,
DebugFlag::DrawThroughGeometry => self.draw_through_geometry, DebugFlag::DrawThroughGeometry => self.draw_through_geometry,
DebugFlag::Camera => self.camera, DebugFlag::Camera => self.camera,
DebugFlag::FrameStepping => self.frame_stepping,
} }
} }
@@ -350,7 +340,6 @@ impl DebugSettings {
self.draw_through_geometry = !self.draw_through_geometry self.draw_through_geometry = !self.draw_through_geometry
} }
DebugFlag::Camera => self.camera = !self.camera, DebugFlag::Camera => self.camera = !self.camera,
DebugFlag::FrameStepping => self.frame_stepping = !self.frame_stepping,
} }
} }
@@ -367,25 +356,9 @@ impl DebugSettings {
pub struct DebugToolsPlugin; pub struct DebugToolsPlugin;
#[derive(Resource, Default)]
struct FrameStepState {
requested: bool,
time_backup: Option<Time>,
last_step_time: Time
}
impl Plugin for DebugToolsPlugin { impl Plugin for DebugToolsPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<DebugSettings>() app.init_resource::<DebugSettings>()
.init_resource::<FrameStepState>()
.add_systems(
PreUpdate,
prepare_frame_step
.after(InputSystems)
.before(RunGgrsSystems)
.run_if(in_state(AppState::InMatch)),
)
.add_systems(PreUpdate, restore_frame_step_time.after(RunGgrsSystems))
.add_systems( .add_systems(
Update, Update,
(handle_palette_input, sync_gizmo_depth_bias) (handle_palette_input, sync_gizmo_depth_bias)
@@ -414,141 +387,6 @@ fn page_len(page: DebugMenuPage, player_handles: &[usize]) -> usize {
} }
} }
fn queue_settings_save(commands: &mut Commands) {
commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.1)));
}
fn frame_step_duration(frame_rate: usize) -> Duration {
Duration::from_nanos(1_000_000_000u64 / frame_rate as u64)
}
fn prepare_frame_step(
settings: Res<DebugSettings>,
gamepads: Query<&Gamepad>,
key: Res<ButtonInput<KeyCode>>,
mut state: ResMut<FrameStepState>,
mut time: ResMut<Time>,
frame_rate: Res<RollbackFrameRate>,
) {
if !settings.frame_stepping {
state.requested = false;
return;
}
let pressed = gamepads
.iter()
.any(|gamepad| gamepad.pressed(GamepadButton::DPadDown));
let just_pressed = gamepads
.iter()
.any(|gamepad| gamepad.just_pressed(GamepadButton::DPadDown));
let just_pressed = key.just_pressed(KeyCode::F4) || just_pressed;
let pressed = key.pressed(KeyCode::F4) || pressed;
if just_pressed {
state.last_step_time = time.clone();
}
let can_repress = pressed && (time.elapsed() - state.last_step_time.elapsed()).as_millis() > 500;
if !settings.menu_open && (just_pressed || can_repress)
{
state.requested = true;
}
state.time_backup = Some(*time);
let delta = if state.requested {
frame_step_duration(**frame_rate)
} else {
Duration::ZERO
};
time.advance_by(delta);
}
fn restore_frame_step_time(mut state: ResMut<FrameStepState>, mut time: ResMut<Time>) {
if let Some(backup) = state.time_backup.take() {
*time = backup;
state.requested = false;
}
}
fn activate_selected(
settings: &mut DebugSettings,
page_len: usize,
player_handles: &[usize],
commands: &mut Commands,
) {
if page_len == 0 {
return;
}
match settings.page() {
DebugMenuPage::Static(entries) => match entries[settings.selected()] {
DebugMenuEntry::StopRecording { .. } => {
commands.insert_resource(StopReplayRecording);
}
DebugMenuEntry::Toggle { flag, .. } => {
settings.toggle_flag(flag);
if !matches!(flag, DebugFlag::FrameStepping) {
queue_settings_save(commands);
}
}
DebugMenuEntry::Submenu { entries, .. } => {
settings.push_page(DebugMenuPage::Static(entries));
}
DebugMenuEntry::PlayerSlots { .. } => settings.push_page(DebugMenuPage::PlayerList),
DebugMenuEntry::MotionSampling { .. } => {
settings.cycle_motion_sampling();
queue_settings_save(commands);
}
DebugMenuEntry::GameSpeed { .. } => {
commands.insert_resource(RollbackFrameRate(settings.game_speed.frame_rate()));
queue_settings_save(commands);
}
},
DebugMenuPage::PlayerList => {
let handle = player_handles[settings.selected()];
settings.rows_mut(handle);
settings.push_page(DebugMenuPage::PlayerRows(handle));
}
DebugMenuPage::PlayerRows(_) => {}
}
}
fn cycle_selected_value(settings: &mut DebugSettings, direction: i8, commands: &mut Commands) {
match settings.page() {
DebugMenuPage::Static(entries) => {
if let Some(entry) = entries.get(settings.selected()) {
match entry {
DebugMenuEntry::MotionSampling { .. } => {
settings.cycle_motion_sampling();
queue_settings_save(commands);
}
DebugMenuEntry::GameSpeed { .. } => {
settings.game_speed = if direction > 0 {
settings.game_speed.up()
} else {
settings.game_speed.down()
};
commands
.insert_resource(RollbackFrameRate(settings.game_speed.frame_rate()));
queue_settings_save(commands);
}
_ => {}
}
}
}
DebugMenuPage::PlayerRows(handle) => {
let selected = settings.selected();
let rows = settings.rows_mut(handle);
rows[selected] = rows[selected].cycle(direction);
queue_settings_save(commands);
}
DebugMenuPage::PlayerList => {}
}
}
fn handle_palette_input( fn handle_palette_input(
keyboard: Res<ButtonInput<KeyCode>>, keyboard: Res<ButtonInput<KeyCode>>,
gamepads: Query<(Entity, &Gamepad)>, gamepads: Query<(Entity, &Gamepad)>,
@@ -560,15 +398,6 @@ fn handle_palette_input(
settings.toggle_flag(DebugFlag::DrawThroughGeometry); settings.toggle_flag(DebugFlag::DrawThroughGeometry);
} }
if keyboard.just_pressed(KeyCode::F3) {
settings.menu_open = !settings.menu_open;
settings.controller = None;
if settings.menu_open {
settings.reset_navigation();
}
return;
}
if !settings.menu_open { if !settings.menu_open {
for (entity, gamepad) in &gamepads { for (entity, gamepad) in &gamepads {
if gamepad.just_pressed(GamepadButton::Start) && gamepad.pressed(GamepadButton::DPadUp) if gamepad.just_pressed(GamepadButton::Start) && gamepad.pressed(GamepadButton::DPadUp)
@@ -582,16 +411,15 @@ fn handle_palette_input(
return; return;
} }
let gamepad = if let Some(controller) = settings.controller { let Some(controller) = settings.controller else {
settings.menu_open = false;
return;
};
let Ok((_, gamepad)) = gamepads.get(controller) else { let Ok((_, gamepad)) = gamepads.get(controller) else {
settings.menu_open = false; settings.menu_open = false;
settings.controller = None; settings.controller = None;
return; return;
}; };
Some(gamepad)
} else {
None
};
let player_handles = player_handles(&players); let player_handles = player_handles(&players);
let page = settings.page(); let page = settings.page();
let page_len = page_len(page, &player_handles); let page_len = page_len(page, &player_handles);
@@ -600,51 +428,95 @@ fn handle_palette_input(
*settings.selected_mut() = 0; *settings.selected_mut() = 0;
} }
if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadUp)) if gamepad.just_pressed(GamepadButton::DPadUp) && page_len != 0 {
|| keyboard.just_pressed(KeyCode::ArrowUp)
|| keyboard.just_pressed(KeyCode::KeyW))
&& page_len != 0
{
let selected = settings.selected_mut(); let selected = settings.selected_mut();
*selected = selected.checked_sub(1).unwrap_or(page_len - 1); *selected = selected.checked_sub(1).unwrap_or(page_len - 1);
} }
if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadDown)) if gamepad.just_pressed(GamepadButton::DPadDown) && page_len != 0 {
|| keyboard.just_pressed(KeyCode::ArrowDown)
|| keyboard.just_pressed(KeyCode::KeyS))
&& page_len != 0
{
let selected = settings.selected_mut(); let selected = settings.selected_mut();
*selected = (*selected + 1) % page_len; *selected = (*selected + 1) % page_len;
} }
let direction = if gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadLeft)) let direction = if gamepad.just_pressed(GamepadButton::DPadLeft) {
|| keyboard.just_pressed(KeyCode::ArrowLeft)
|| keyboard.just_pressed(KeyCode::KeyA)
{
Some(-1) Some(-1)
} else if gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::DPadRight)) } else if gamepad.just_pressed(GamepadButton::DPadRight) {
|| keyboard.just_pressed(KeyCode::ArrowRight)
|| keyboard.just_pressed(KeyCode::KeyD)
{
Some(1) Some(1)
} else { } else {
None None
}; };
if let Some(direction) = direction { if let Some(direction) = direction {
cycle_selected_value(&mut settings, direction, &mut commands); match page {
DebugMenuPage::Static(entries) => {
if let Some(entry) = entries.get(settings.selected()) {
match entry {
DebugMenuEntry::StopRecording { .. } => {}
DebugMenuEntry::MotionSampling { .. } => settings.cycle_motion_sampling(),
DebugMenuEntry::GameSpeed { .. } => {
if direction > 0 {
settings.game_speed = settings.game_speed.up();
} else {
settings.game_speed = settings.game_speed.down();
} }
if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::South))
|| keyboard.just_pressed(KeyCode::Enter) commands.insert_resource(RollbackFrameRate(
|| keyboard.just_pressed(KeyCode::Space)) settings.game_speed.frame_rate(),
&& page_len != 0 ));
{
activate_selected(&mut settings, page_len, &player_handles, &mut commands);
} }
if (gamepad.is_some_and(|pad| pad.just_pressed(GamepadButton::West)) _ => {}
|| keyboard.just_pressed(KeyCode::Escape)) }
&& !settings.pop_page() }
{ }
DebugMenuPage::PlayerList => todo!(),
DebugMenuPage::PlayerRows(_) => {
let DebugMenuPage::PlayerRows(handle) = page else {
unreachable!("player row navigation must have a player row page")
};
let selected = settings.selected();
let rows = settings.rows_mut(handle);
rows[selected] = rows[selected].cycle(direction);
commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.1)));
}
}
}
if matches!(page, DebugMenuPage::PlayerRows(_)) {
let direction = if gamepad.just_pressed(GamepadButton::DPadLeft) {
Some(-1)
} else if gamepad.just_pressed(GamepadButton::DPadRight) {
Some(1)
} else {
None
};
if let Some(_direction) = direction {}
}
if gamepad.just_pressed(GamepadButton::South) {
match page {
DebugMenuPage::Static(entries) if page_len != 0 => match entries[settings.selected()] {
DebugMenuEntry::StopRecording { .. } => {
commands.insert_resource(StopReplayRecording);
}
DebugMenuEntry::Toggle { flag, .. } => {
settings.toggle_flag(flag);
commands.queue(SaveSettingsDeferred(Duration::from_secs_f32(0.1)));
}
DebugMenuEntry::Submenu { entries, .. } => {
settings.push_page(DebugMenuPage::Static(entries));
}
DebugMenuEntry::PlayerSlots { .. } => settings.push_page(DebugMenuPage::PlayerList),
DebugMenuEntry::MotionSampling { .. } => settings.cycle_motion_sampling(),
DebugMenuEntry::GameSpeed { .. } => {}
},
DebugMenuPage::PlayerList if page_len != 0 => {
let handle = player_handles[settings.selected()];
settings.rows_mut(handle);
settings.push_page(DebugMenuPage::PlayerRows(handle));
}
_ => {}
}
}
if gamepad.just_pressed(GamepadButton::West) && !settings.pop_page() {
settings.menu_open = false; settings.menu_open = false;
settings.controller = None; settings.controller = None;
} }
@@ -666,9 +538,8 @@ fn sync_gizmo_depth_bias(settings: Res<DebugSettings>, mut configs: ResMut<Gizmo
fn draw_palette( fn draw_palette(
mut contexts: EguiContexts, mut contexts: EguiContexts,
mut settings: ResMut<DebugSettings>, settings: Res<DebugSettings>,
players: Query<&Player>, players: Query<&Player>,
mut commands: Commands,
) -> Result { ) -> Result {
if !settings.menu_open { if !settings.menu_open {
return Ok(()); return Ok(());
@@ -676,7 +547,7 @@ fn draw_palette(
let player_handles = player_handles(&players); let player_handles = player_handles(&players);
let page = settings.page(); let page = settings.page();
let page_len = page_len(page, &player_handles); let selected = settings.selected();
egui::Window::new("Debug") egui::Window::new("Debug")
.anchor(egui::Align2::RIGHT_TOP, egui::vec2(-12.0, 12.0)) .anchor(egui::Align2::RIGHT_TOP, egui::vec2(-12.0, 12.0))
.resizable(false) .resizable(false)
@@ -685,113 +556,38 @@ fn draw_palette(
match page { match page {
DebugMenuPage::Static(entries) => { DebugMenuPage::Static(entries) => {
for (index, entry) in entries.iter().enumerate() { for (index, entry) in entries.iter().enumerate() {
ui.horizontal(|ui| { let marker = if index == selected { ">" } else { " " };
ui.label(if index == settings.selected() {
">"
} else {
" "
});
match entry { match entry {
DebugMenuEntry::StopRecording { label } => { DebugMenuEntry::StopRecording { label } => {
if ui.button(*label).clicked() { ui.label(format!("{marker} {label}"));
*settings.selected_mut() = index;
activate_selected(
&mut settings,
page_len,
&player_handles,
&mut commands,
);
}
} }
DebugMenuEntry::Toggle { label, flag } => { DebugMenuEntry::Toggle { label, flag } => {
let mut value = settings.flag(*flag); let state = if settings.flag(*flag) { "on" } else { "off" };
if ui.checkbox(&mut value, *label).changed() { ui.label(format!("{marker} {label}: {state}"));
settings.toggle_flag(*flag);
if !matches!(flag, DebugFlag::FrameStepping) {
queue_settings_save(&mut commands);
}
}
} }
DebugMenuEntry::Submenu { label, .. } DebugMenuEntry::Submenu { label, .. }
| DebugMenuEntry::PlayerSlots { label } => { | DebugMenuEntry::PlayerSlots { label } => {
if ui.button(format!("{label} >")).clicked() { ui.label(format!("{marker} {label} >"));
*settings.selected_mut() = index;
activate_selected(
&mut settings,
page_len,
&player_handles,
&mut commands,
);
}
} }
DebugMenuEntry::MotionSampling { label } => { DebugMenuEntry::MotionSampling { label } => {
let old = settings.motion_sampling; ui.label(format!(
egui::ComboBox::from_id_salt(("debug-motion", index)) "{marker} {label}: {}",
.width(180.0) settings.motion_sampling.label()
.selected_text(format!("{label}: {}", old.label())) ));
.show_ui(ui, |ui| {
ui.selectable_value(
&mut settings.motion_sampling,
PresentationMotionMode::Interpolation,
"Interpolation",
);
ui.selectable_value(
&mut settings.motion_sampling,
PresentationMotionMode::Extrapolation,
"Extrapolation",
);
});
if settings.motion_sampling != old {
queue_settings_save(&mut commands);
}
} }
DebugMenuEntry::GameSpeed { label } => { DebugMenuEntry::GameSpeed { label } => {
let old = settings.game_speed; ui.label(format!(
egui::ComboBox::from_id_salt(("debug-speed", index)) "{marker} {label}: {}",
.width(180.0) settings.game_speed.label()
.selected_text(format!("{label}: {}", old.label()))
.show_ui(ui, |ui| {
for speed in [
GameSpeedDebug::Full,
GameSpeedDebug::Half,
GameSpeedDebug::Quarter,
] {
ui.selectable_value(
&mut settings.game_speed,
speed,
speed.label(),
);
}
});
if settings.game_speed != old {
commands.insert_resource(RollbackFrameRate(
settings.game_speed.frame_rate(),
)); ));
queue_settings_save(&mut commands);
} }
} }
} }
});
}
} }
DebugMenuPage::PlayerList => { DebugMenuPage::PlayerList => {
for (index, handle) in player_handles.iter().enumerate() { for (index, handle) in player_handles.iter().enumerate() {
ui.horizontal(|ui| { let marker = if index == selected { ">" } else { " " };
ui.label(if index == settings.selected() { ui.label(format!("{marker} Player {} >", handle + 1));
">"
} else {
" "
});
if ui.button(format!("Player {} >", handle + 1)).clicked() {
*settings.selected_mut() = index;
activate_selected(
&mut settings,
page_len,
&player_handles,
&mut commands,
);
}
});
} }
if player_handles.is_empty() { if player_handles.is_empty() {
ui.label("No players in match"); ui.label("No players in match");
@@ -804,30 +600,16 @@ fn draw_palette(
.unwrap_or([FighterDebugRowKind::None; PLAYER_DEBUG_ROW_COUNT]); .unwrap_or([FighterDebugRowKind::None; PLAYER_DEBUG_ROW_COUNT]);
ui.label(format!("Player {}", handle + 1)); ui.label(format!("Player {}", handle + 1));
for (index, row) in rows.iter().enumerate() { for (index, row) in rows.iter().enumerate() {
ui.horizontal(|ui| { let marker = if index == selected { ">" } else { " " };
ui.label(if index == settings.selected() { ui.label(format!("{marker} Row {}: {}", index + 1, row.label()));
">"
} else {
" "
});
egui::ComboBox::from_id_salt(("debug-row", handle, index))
.width(180.0)
.selected_text(format!("Row {}: {}", index + 1, row.label()))
.show_ui(ui, |ui| {
let rows = settings.rows_mut(handle);
for kind in FighterDebugRowKind::ALL {
ui.selectable_value(&mut rows[index], kind, kind.label());
}
});
});
} }
} }
} }
ui.separator(); ui.separator();
let controls = if matches!(page, DebugMenuPage::PlayerRows(_)) { let controls = if matches!(page, DebugMenuPage::PlayerRows(_)) {
"Arrows/WASD: select/change Escape/B: back" "D-pad: select/change B: back"
} else { } else {
"Arrows/WASD: select Enter/A: activate Escape/B: back" "D-pad: select A: enter/toggle Left/right: change B: back"
}; };
ui.label(controls); ui.label(controls);
}); });
@@ -917,29 +699,4 @@ mod tests {
mode.cycle(); mode.cycle();
assert_eq!(mode, PresentationMotionMode::Interpolation); assert_eq!(mode, PresentationMotionMode::Interpolation);
} }
#[test]
fn game_speed_has_expected_frame_rates() {
assert_eq!(GameSpeedDebug::Full.frame_rate(), 60);
assert_eq!(GameSpeedDebug::Half.frame_rate(), 30);
assert_eq!(GameSpeedDebug::Quarter.frame_rate(), 15);
}
#[test]
fn frame_stepping_is_disabled_by_default_and_uses_game_speed_timestep() {
let settings = DebugSettings::default();
assert!(!settings.frame_stepping);
assert_eq!(frame_step_duration(60), Duration::from_nanos(16_666_666));
assert_eq!(frame_step_duration(30), Duration::from_nanos(33_333_333));
}
#[test]
fn empty_player_list_does_not_activate() {
let mut settings = DebugSettings::default();
settings.push_page(DebugMenuPage::PlayerList);
assert_eq!(page_len(settings.page(), &[]), 0);
assert_eq!(settings.selected(), 0);
}
} }
+3 -53
View File
@@ -3,7 +3,6 @@
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
io, io,
path::Path,
}; };
use bevy::{ use bevy::{
@@ -518,7 +517,6 @@ struct FighterBakeDeclaration {
#[derive(Asset, TypePath)] #[derive(Asset, TypePath)]
struct FighterBakeSource { struct FighterBakeSource {
model_bytes: Vec<u8>, model_bytes: Vec<u8>,
buffers: Vec<Vec<u8>>,
animations: HashMap<AnimKind, String>, animations: HashMap<AnimKind, String>,
} }
@@ -552,31 +550,8 @@ impl AssetLoader for FighterBakeSourceLoader {
.read_asset_bytes(&manifest.model_path) .read_asset_bytes(&manifest.model_path)
.await .await
.map_err(|error| io::Error::other(error.to_string()))?; .map_err(|error| io::Error::other(error.to_string()))?;
let gltf = gltf::Gltf::from_slice(&model_bytes)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
let mut buffers = Vec::with_capacity(gltf.buffers().len());
for buffer in gltf.buffers() {
let bytes = match buffer.source() {
gltf::buffer::Source::Uri(uri) => load_context
.read_asset_bytes(AssetPath::from_path_buf(
Path::new(&manifest.model_path)
.parent()
.unwrap_or_else(|| Path::new(""))
.join(uri),
))
.await
.map_err(|error| io::Error::other(error.to_string()))?,
gltf::buffer::Source::Bin => gltf.blob.clone().ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidData, "missing glTF binary blob")
})?,
};
buffers.push(bytes);
}
Ok(FighterBakeSource { Ok(FighterBakeSource {
model_bytes, model_bytes,
buffers,
animations: manifest.animations, animations: manifest.animations,
}) })
} }
@@ -625,7 +600,7 @@ impl AssetTransformer for FighterBakeTransformer {
source: TransformedAsset<Self::AssetInput>, source: TransformedAsset<Self::AssetInput>,
_: &'a Self::Settings, _: &'a Self::Settings,
) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> { ) -> Result<TransformedAsset<Self::AssetOutput>, Self::Error> {
let baked = bake_gltf(&source.model_bytes, &source.buffers, &source.animations)?; let baked = bake_gltf(&source.model_bytes, &source.animations)?;
Ok(source.replace_asset(baked)) Ok(source.replace_asset(baked))
} }
} }
@@ -681,36 +656,11 @@ impl Plugin for BakedAnimationPlugin {
fn bake_gltf( fn bake_gltf(
bytes: &[u8], bytes: &[u8],
buffer_bytes: &[Vec<u8>],
animations: &HashMap<AnimKind, String>, animations: &HashMap<AnimKind, String>,
) -> io::Result<BakedFighterAnimations> { ) -> io::Result<BakedFighterAnimations> {
let gltf = gltf::Gltf::from_slice(bytes) let (document, buffers, _) = gltf::import_slice(bytes)
.map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?; .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?;
let buffers = gltf bake_gltf_document(document, buffers, animations)
.buffers()
.zip(buffer_bytes)
.map(|(buffer, bytes)| {
if bytes.len() < buffer.length() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"glTF buffer {} is too short: expected {}, got {}",
buffer.index(),
buffer.length(),
bytes.len()
),
));
}
Ok(gltf::buffer::Data(bytes.clone()))
})
.collect::<io::Result<Vec<_>>>()?;
if buffers.len() != gltf.buffers().len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"glTF buffer data is incomplete",
));
}
bake_gltf_document(gltf.document, buffers, animations)
} }
fn bake_gltf_document( fn bake_gltf_document(