mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 08:23:45 +00:00
test(multiplayer): report transport health on prediction-quality failures
A percentile alone cannot tell "the predictor regressed" from "the client never received the data". The client gate now prints snapshot_loss / snapshot_age / rtt on every run, and on a quality failure with >20% loss says explicitly that the run was transport-starved. It deliberately does not convert the failure into a pass: a client that cannot receive snapshots is still a failed run, just a differently-diagnosed one. Both directions of the new branch verified non-vacuously (forced true so it fires and formats; restored so it stays quiet on a healthy run while the INFO line still prints). Records the investigation behind it in multiplayer-todo.md: the reviewer's 3-process p95 0.688 did not reproduce. An idle third process costs nothing (p99 0.094), a spectator costs a small but real amount (p99 0.094-0.146), and snapshot loss held at 0.0% even under 2x CPU oversubscription - all an order of magnitude inside the 0.5/2.0 gates. Also notes that a previously working class_name can silently drop out of the .godot class cache, which surfaces as a bogus parse error with nothing in git status to explain it.
This commit is contained in:
@@ -290,6 +290,18 @@ func run_client_check(settle_seconds: float, drive_seconds: float, exercise_ball
|
||||
# server consumes, so both the same-sequence raw residual and the exposed
|
||||
# render discontinuity are meaningful free-flight gates. Hard corrections
|
||||
# remain separately gated by cohort.
|
||||
# Transport health, printed alongside the quality numbers and asserted
|
||||
# separately below. Without this a p95 failure is undiagnosable: "the
|
||||
# predictor got worse" and "the client never received the data" look
|
||||
# identical in a percentile. An adversarial review hit exactly that — a
|
||||
# 3-process run failed at p95 0.688 with roughly a third of snapshots
|
||||
# missing, and it could not be told apart from a real regression.
|
||||
var snapshot_loss_pct := float(net_stats.get("snapshot_loss_pct", 0.0))
|
||||
var snapshot_age_ms := float(net_stats.get("snapshot_age_ms", 0.0))
|
||||
print("SMOKE INFO: transport snapshot_loss=%.1f%% snapshot_age=%.1fms rtt=%.1fms" % [
|
||||
snapshot_loss_pct, snapshot_age_ms, NetworkManager.rtt_ms
|
||||
])
|
||||
|
||||
var raw_quality_p95: float = float(prediction_stats.get("free_flight_position_error_p95", INF))
|
||||
var raw_quality_p99: float = float(prediction_stats.get("free_flight_position_error_p99", INF))
|
||||
var raw_rotation_p95: float = float(prediction_stats.get("free_flight_rotation_error_p95", INF))
|
||||
@@ -438,6 +450,12 @@ func run_client_check(settle_seconds: float, drive_seconds: float, exercise_ball
|
||||
])
|
||||
|
||||
var success := verification_movement > 1.0 and local_prediction_ok and prediction_quality_ok and ball_contact_ok and match_state_ok
|
||||
# A run starved of snapshots has not measured prediction quality at all, so
|
||||
# say so explicitly instead of blaming the predictor. Deliberately does NOT
|
||||
# convert the failure into a pass — a client that cannot receive snapshots
|
||||
# is still a failed run, just a differently-diagnosed one.
|
||||
if not prediction_quality_ok and snapshot_loss_pct > 20.0:
|
||||
print("SMOKE FAIL: transport-starved, not a prediction regression (snapshot_loss=%.1f%%) — check host CPU contention before suspecting the predictor" % snapshot_loss_pct)
|
||||
print("SMOKE %s: client locally predicted %.2fm horizontal, local_prediction_ok=%s prediction_quality_ok=%s" % [
|
||||
"PASS" if success else "FAIL", moved_horizontal, str(local_prediction_ok), str(prediction_quality_ok)
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user