fix(simulation): use non-blocking TCP to prevent game loop stall

read_framed() called read_exact() which blocked the entire bevy Update
schedule waiting for client input — no systems ran until a keystroke
arrived. Switch TcpStream to non-blocking mode so receive_inputs()
returns Ok(vec![]) on WouldBlock instead of blocking. Toggle to
blocking for snapshot writes (reliable delivery). Add 50ms frame
throttle (~20 ticks/sec) since the non-blocking loop would otherwise
spin. Downgrade input receive logging to trace, add error logging for
missing bridge resource and failed observer queries.

Fixes bug #1 (server never sends snapshots) and #2 (camera doesn't
center until first keystroke).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-16 23:25:45 +01:00
co-authored by Claude Opus 4.6
parent 686c89170a
commit d1aba3d554
5 changed files with 69 additions and 19 deletions
+12 -2
View File
@@ -226,13 +226,23 @@ fn main() {
tracing::info!("Simulation initialized, entering game loop");
// Game loop: run until client disconnects
// Game loop: run until client disconnects.
// Targets ~20 ticks/sec (2 game-minutes/sec). The TCP bridge uses
// non-blocking reads, so without throttling this loop would spin.
// Remaining frame budget is available for NPC AI and pathfinding.
let target_frame_time = std::time::Duration::from_millis(50);
loop {
let frame_start = std::time::Instant::now();
app.update();
// Check ServerRunning resource
if !app.world().resource::<ServerRunning>().0 {
break;
}
let elapsed = frame_start.elapsed();
if elapsed < target_frame_time {
std::thread::sleep(target_frame_time - elapsed);
}
}
tracing::info!("Simulation server shutting down");