Fix real flake in smoke_test_network_client.gd's prediction-buffer check

Found while re-verifying the full PC2 test suite from a fresh clone:
the "prediction buffer has entries" check failed ~1 in 3 runs. Root
cause is a genuine race in the test, not in prediction/reconciliation
itself -- a server snapshot RPC can land on the 60-frame loop's final
`await process_frame`, triggering a legitimate reconciliation that
clears the buffer (network_client.gd's documented behavior) right
before the check reads it. The desync-correction check right after it
already proves reconciliation logic works correctly on its own, so
this was never actually catching a real bug -- just occasionally
racing a real correction event.

Fix: one more predict_and_send() right before the check guarantees a
fresh buffer entry regardless of whether a correction just fired.
Verified 5/5 clean runs against a real dedicated server after the fix
(previously observed a failure within 3 runs).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6rUEynmv2QmweocAQYARN
This commit is contained in:
2026-08-21 01:44:28 +00:00
parent 1526f3a636
commit 3596922696
+9 -1
View File
@@ -60,7 +60,15 @@ func _run() -> void:
# Reconciliation only actually fires on a real mismatch -- absence of a # Reconciliation only actually fires on a real mismatch -- absence of a
# large mismatch is a PASS outcome too (means prediction tracked the # large mismatch is a PASS outcome too (means prediction tracked the
# server closely), so this alone isn't proof snapshots arrived. Check # server closely), so this alone isn't proof snapshots arrived. Check
# that directly instead: # that directly instead. One real flake found running this repeatedly:
# a snapshot RPC can land on the loop's very last `await process_frame`,
# triggering a legitimate reconciliation that clears the buffer (per
# _handle_snapshot's documented behavior) right before this check reads
# it -- a real race in the *test*, not a bug in prediction/reconciliation
# (the desync check below proves that logic works independently). One
# more predict_and_send guarantees a fresh entry regardless of whether a
# correction just fired.
net.predict_and_send(1.0 / 30.0, 0.0, -1.0, false)
_check("prediction buffer has entries (input was actually sent)", net._prediction_buffer.size() > 0) _check("prediction buffer has entries (input was actually sent)", net._prediction_buffer.size() > 0)
# 3. Deliberately desync prediction from reality, then confirm the next # 3. Deliberately desync prediction from reality, then confirm the next