fix(client): space proof moves past the stance cooldown — test_proof_corner_reveal flake (T-1037)

test_proof_corner_reveal drove moves back-to-back. The Gauntlet player is
Walk stance (ticks_per_move=2, server stance.rs), so a move arriving within
the cooldown window is silently throttled (movement.rs::apply_move ->
PlayerMoveCooldown::try_move). _send_and_receive returned the instant a move
landed, so the next send raced the cooldown and an occasional move was
dropped — leaving the player one tile short, which (via the test's cumulative
expectations) cascaded into the corner-reveal perception assert. A different
move dropped each run = flaky; exposed by the heavier post-cascade tick timing.

Test-side fix only — game behaviour is correct and the Gauntlet world is
immutable: after a move lands, wait for the server clock to advance
COOLDOWN_TICKS past the landing tick before the next send. Full client suite
2538/2538 green; the proof suite drops from ~28s (timeouts) to ~4s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 16:24:13 +02:00
co-authored by Claude Opus 4.8
parent cfd5e18d0a
commit 568370b7c2
+27
View File
@@ -16,6 +16,12 @@ extends GdUnitTestSuite
const CONNECT_TIMEOUT: float = 3.0
const RESPONSE_TIMEOUT: float = 5.0
# Server move cooldown: the Gauntlet player is Walk stance (ticks_per_move = 2,
# server stance.rs). A move input arriving within that window is silently
# throttled away (movement.rs::apply_move -> PlayerMoveCooldown::try_move).
# Space consecutive sends past it (2 ticks + 1 margin for apply/snapshot lag)
# so back-to-back moves can never race the cooldown.
const COOLDOWN_TICKS: int = 3
const MAX_PORT_ATTEMPTS: int = 5
var _server_pid: int = -1
@@ -128,6 +134,27 @@ func _send_and_receive(
assert_that(actual.is_equal_approx(expect_position)).override_failure_message(
"player must reach %s after '%s' — last seen %s" % [expect_position, action_name, actual]
).is_true()
# Space the next move past the per-stance cooldown. The server silently
# drops a move that arrives within ticks_per_move ticks of the previous
# one; without this wait, back-to-back sends race the cooldown and an
# occasional move is throttled, leaving the player one tile short (flaky).
# Wait for the server clock to advance COOLDOWN_TICKS past the landing
# tick so the next send can never be throttled. The player is stationary
# meanwhile, so the fresher snapshot is still valid for the caller.
var landed_tick: int = int(snapshot.tick)
var spacing_elapsed := 0.0
while spacing_elapsed < RESPONSE_TIMEOUT:
_bridge.poll()
var m := _bridge.poll_message()
if m.size() > 0:
var d: Variant = Protocol.decode_snapshot(m)
if d != null:
snapshot = d
if int(d.tick) >= landed_tick + COOLDOWN_TICKS:
break
continue
await get_tree().create_timer(0.05).timeout
spacing_elapsed += 0.05
return snapshot