feat(multiplayer): task 2.8 net_sim.gd, close out Phase 2

New NetSim autoload: seeded, CLI-driven (--net-sim-latency/-jitter/-loss/-dup)
latency/jitter/loss/duplicate decorator, a true no-op passthrough unless a
flag is set. Wraps MatchSim.send_input/send_snapshot per the design doc's
scope, plus NetworkManager's ping/pong so the already-tested RTT/clock
measurement becomes the acceptance signal for "raises observed RTT" without
waiting on Phase 3's per-peer snapshot echo.

Two real bugs found while building and verifying this against Phase 2's own
milestone gate (a real match under --net-sim-latency 80 --net-sim-jitter
20, not just LAN): a timestamp captured inside a delayed RPC closure
silently ate that side's own added delay out of the round-trip
measurement instead of adding to it; and a delayed send whose target
disconnected (or whose own process had already shut down) during the hold
threw RPC errors, since the existing get_peers() filtering only checked
validity at schedule time. Fixed by capturing timestamps before handing
off to NetSim, and by having NetSim re-validate the target at fire time.

Phase 2's milestone gate now passes for real: a full 1v1 under simulated
80ms latency / 20ms jitter still shows clean server-authoritative
movement and zero RPC errors. Full Phase 1 + Phase 2 regression suite
re-verified clean with NetSim present but inactive.
This commit is contained in:
Josh Creek
2026-08-20 08:50:47 +01:00
parent 39a41c016c
commit 7b150ef72e
7 changed files with 252 additions and 6 deletions
+1
View File
@@ -44,6 +44,7 @@ GameSettings="*res://scripts/game_settings.gd"
VideoSettings="*res://scripts/video_settings.gd"
BackgroundFPS="*res://scripts/background_fps.gd"
PerfOverlay="*res://scripts/perf_overlay.gd"
NetSim="*res://scripts/net_sim.gd"
NetworkManager="*res://scripts/network_manager.gd"
MatchNet="*res://scripts/match_net.gd"
MatchSim="*res://scripts/match_sim.gd"