diff --git a/crates/game-client/src/hud.rs b/crates/game-client/src/hud.rs index bb24152..98e0d4f 100644 --- a/crates/game-client/src/hud.rs +++ b/crates/game-client/src/hud.rs @@ -1,5 +1,4 @@ use bevy::{ - app::AppExit, picking::hover::Hovered, prelude::*, ui::UiRect, @@ -8,6 +7,9 @@ use bevy::{ }, }; +#[cfg(not(target_arch = "wasm32"))] +use bevy::app::AppExit; + use crate::{ interaction::{InteractionState, OrderMode}, map_view::map_view_status_bundle, @@ -964,7 +966,7 @@ fn update_hud( { let mut overlay = panels.p2(); if let MatchPhase::Victory(winner) = view.phase { - let local_won = winner == u32::from(view.local_player); + let local_won = winner == view.local_player; overlay.0.display = Display::Flex; overlay.1.set_all(if local_won { CYAN } else { CORAL }); @@ -997,7 +999,7 @@ fn update_hud( fn leave_match_after_victory( keyboard: Res>, view: Res, - mut app_exit: MessageWriter, + #[cfg(not(target_arch = "wasm32"))] mut app_exit: MessageWriter, ) { if !matches!(view.phase, MatchPhase::Victory(_)) || !keyboard.just_pressed(KeyCode::Escape) { return; diff --git a/crates/game-client/src/observe.rs b/crates/game-client/src/observe.rs index 19ea0b0..33fff11 100644 --- a/crates/game-client/src/observe.rs +++ b/crates/game-client/src/observe.rs @@ -112,9 +112,10 @@ impl ObserveState { } fn note_frame_spike(&mut self, now_secs: f64, frame_ms: f64, fps: Option) { - if self.last_frame_spike_at_secs.is_some_and(|previous| { - now_secs - previous < FRAME_SPIKE_COOLDOWN_SECS - }) { + if self + .last_frame_spike_at_secs + .is_some_and(|previous| now_secs - previous < FRAME_SPIKE_COOLDOWN_SECS) + { return; } self.last_frame_spike_at_secs = Some(now_secs); @@ -299,8 +300,8 @@ mod tests { #[test] fn frame_spike_is_rate_limited() { let mut state = ObserveState::new(false); - state.note_frame_spike(40.0, Some(25.0)); - state.note_frame_spike(50.0, Some(20.0)); + state.note_frame_spike(40.0, 40.0, Some(25.0)); + state.note_frame_spike(41.0, 50.0, Some(20.0)); assert_eq!(state.events.len(), 1); assert_eq!(state.events[0].key, keys::PERF_FRAME_SPIKE); } diff --git a/crates/game-client/src/online.rs b/crates/game-client/src/online.rs index 1eb601d..fc92332 100644 --- a/crates/game-client/src/online.rs +++ b/crates/game-client/src/online.rs @@ -1812,13 +1812,7 @@ fn synchronize_authoritative_view( ) { let previous_local_player = view.local_player; for event in transport.signals.drain() { - apply_lifecycle_event( - &mut transport, - &mut view, - &mut updates, - &mut observe, - event, - ); + apply_lifecycle_event(&mut transport, &mut view, &mut updates, &mut observe, event); } let dirty = transport.signals.take_dirty(); diff --git a/tools/match-e2e/src/main.rs b/tools/match-e2e/src/main.rs index 25d1351..1c7f52f 100644 --- a/tools/match-e2e/src/main.rs +++ b/tools/match-e2e/src/main.rs @@ -1541,15 +1541,20 @@ fn ensure_match_running(client: &Client, timeout: Duration, poll: Duration) -> R } } } - wait_until("match phase Running after start_match", timeout, poll, || { - let state = client - .conn - .db - .match_state() - .singleton_id() - .find(&SINGLETON_ID); - Ok(state.and_then(|row| (row.phase == MatchPhase::Running).then_some(()))) - })?; + wait_until( + "match phase Running after start_match", + timeout, + poll, + || { + let state = client + .conn + .db + .match_state() + .singleton_id() + .find(&SINGLETON_ID); + Ok(state.and_then(|row| (row.phase == MatchPhase::Running).then_some(()))) + }, + )?; Ok(()) } Some(other) => bail!("match is in unexpected phase {other:?}; expected Lobby or Running"), @@ -1597,13 +1602,8 @@ fn run_reconnect_soak( .and_then(|slot| (!slot.connected).then_some(()))) }, )?; - let reconnected = Client::connect( - "player one reconnect", - token_path, - host, - database, - timeout, - )?; + let reconnected = + Client::connect("player one reconnect", token_path, host, database, timeout)?; ensure!( reconnected.identity == original_identity, "persisted player-one token resolved to a different identity on cycle {cycle}" @@ -1614,12 +1614,7 @@ fn run_reconnect_soak( timeout, poll, || { - let Some(slot) = observer - .conn - .db - .player_slot() - .player_id() - .find(&PLAYER_ONE) + let Some(slot) = observer.conn.db.player_slot().player_id().find(&PLAYER_ONE) else { return Ok(None); }; @@ -1648,14 +1643,17 @@ fn run_reconnect_soak( sorted.sort_unstable(); let p50_ms = percentile_sorted(&sorted, 50); let p95_ms = percentile_sorted(&sorted, 95); - let max_ms = *sorted.last().context("reconnect soak produced no timings")?; + let max_ms = *sorted + .last() + .context("reconnect soak produced no timings")?; Ok(ReconnectSoakReport { kind: "reconnect-soak", host: host.to_owned(), database: database.to_owned(), cycles_requested: cycles, - cycles_completed: cycle_reports.len() as u32, + cycles_completed: u32::try_from(cycle_reports.len()) + .context("reconnect soak cycle count exceeds u32")?, p50_ms, p95_ms, max_ms, @@ -1989,6 +1987,20 @@ fn exercise_cluster_first_controls( else { return Ok(None); }; + let visible_packet_total = client + .conn + .db + .transit_packet() + .iter() + .filter(|packet| packet.order_id == order.order_id) + .map(|packet| packet.infantry) + .sum::(); + if visible_packet_total != order.in_transit_infantry { + // Table callbacks from one transaction may reach the SDK + // cache in different turns. Wait for the coherent snapshot; + // a durable mismatch still times out and fails this phase. + return Ok(None); + } assert_cluster_action_order( &client.conn, &order, @@ -2257,6 +2269,20 @@ fn establish_cluster_contact_with_expansions( else { return Ok(None); }; + let visible_packet_total = client + .conn + .db + .transit_packet() + .iter() + .filter(|packet| packet.order_id == order.order_id) + .map(|packet| packet.infantry) + .sum::(); + if visible_packet_total != order.in_transit_infantry { + // Table callbacks from one transaction may reach the SDK + // cache in different turns. Wait for the coherent snapshot; + // a durable mismatch still times out and fails this phase. + return Ok(None); + } assert_cluster_action_order( &client.conn, &order,