feat(client): WRONG button 60-tick ring buffer captures (#507)

Upgrades bug_report_dialog.gd from single-tick MVP to 60-tick rolling
history. Pre-allocated ring buffers for inputs and snapshots. Outputs
inputs.jsonl (replay-compatible), snapshots.jsonl, and seed.txt on F12.
Inter-frame input accumulation ensures no inputs lost between server ticks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 12:05:42 +01:00
co-authored by Claude Opus 4.6
parent 931399f248
commit c96f1463dc
3 changed files with 243 additions and 6 deletions
+25
View File
@@ -22,6 +22,7 @@ var _last_monologue_tick: int = -1 # Prevent re-consuming monologue when same t
var _last_dialogue_tick: int = -1
var _flash_rect: ColorRect = null # #502/#501: ephemeral screen flash overlay (shared: teleport preempts amber)
var _teleport_in_progress: bool = false # #501: defer smoothing re-enable by one frame after teleport
var _pending_record_inputs: Array = [] # #507: accumulates server-bound inputs across frames; flushed into record_tick() on snapshot arrival
func _ready() -> void:
print("The Settled Reach — client initialized")
@@ -81,6 +82,16 @@ func _process(_delta: float) -> void:
if world_renderer and world_renderer.has_method("update_from_state"):
world_renderer.update_from_state()
# OQ-07 (#522): propagate insert state to all z-layer-6 display nodes.
# Cursor shape still fires (D-056 option a) — only verb labels suppressed.
var _insert := GameState.insert_active
if cursor_renderer and cursor_renderer.has_method("set_insert_active"):
cursor_renderer.set_insert_active(_insert)
if interaction_list and interaction_list.has_method("set_insert_active"):
interaction_list.set_insert_active(_insert)
if interaction_prompt and interaction_prompt.has_method("set_insert_active"):
interaction_prompt.set_insert_active(_insert)
# D-057: Update interaction list from game state
# Suppress during dialogue — player is in conversation, verb list is noise
if interaction_list and interaction_list.has_method("update_from_state"):
@@ -135,6 +146,8 @@ func _process(_delta: float) -> void:
camera.reset_smoothing()
# Send queued input to simulation
# #507: Server-bound inputs are accumulated into _pending_record_inputs across frames.
# At 60fps/10tps, inputs on non-snapshot frames must not be lost from the ring buffer.
var inputs = InputMapper.flush_queue()
for input in inputs:
# #495: F12 WRONG button — client-only, trigger bug report capture
@@ -164,6 +177,18 @@ func _process(_delta: float) -> void:
"verb": null,
}
SimBridge.send_input(input)
_pending_record_inputs.append(input)
# #507: Record tick data to ring buffer — once per server tick (snapshot arrival).
# Flushes all inputs accumulated since the last snapshot (across multiple display frames),
# then clears the accumulator for the next tick.
if snapshot != null and bug_report_dialog and bug_report_dialog.has_method("record_tick"):
bug_report_dialog.record_tick(
GameState.current_tick,
JSON.stringify(GameState.current_snapshot),
_pending_record_inputs
)
_pending_record_inputs.clear()
# Consume-once per tick: show monologue text, then clear.