- Defer LOAD_GAME dispatch until SimBridge reaches CONNECTED (critical) - Guard _build_saves_list() against queue_free() race on rapid reopen - Disable save entries with empty newest_save, guard in _on_save_selected - Send before show_loading on F6 quickload, skip overlay on send failure - Clear pending_load_path in _on_new_game()/_on_continue() (stale path) - Add hide_loading(success: bool) API for future failure-state UI - Add test_save_load_flow_sprint21.gd covering LoadingScreen + GameState Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+40
-11
@@ -50,18 +50,16 @@ func _ready() -> void:
|
||||
# Connect to simulation (test mode sets CONNECTED immediately)
|
||||
SimBridge.connect_to_sim()
|
||||
|
||||
# #257: If returning from main menu "Load Game" selection, send load command immediately.
|
||||
# pending_load_path is set by main_menu.gd before changing scene; cleared after dispatch.
|
||||
# #257: If returning from main menu "Load Game" selection, defer dispatch until connected.
|
||||
# In test mode, connect_to_sim() sets CONNECTED synchronously — dispatch fires immediately.
|
||||
# In live mode, state is CONNECTING — signal handler dispatches once connected.
|
||||
if not GameState.pending_load_path.is_empty():
|
||||
var load_path := GameState.pending_load_path
|
||||
GameState.pending_load_path = ""
|
||||
SimBridge.send_input({
|
||||
"action": InputMapper.Action.LOAD_GAME,
|
||||
"timestamp_msec": Time.get_ticks_msec(),
|
||||
"action_data": {"path": load_path},
|
||||
})
|
||||
if loading_screen:
|
||||
loading_screen.show_loading()
|
||||
if SimBridge.state == SimBridge.ConnectionState.CONNECTED:
|
||||
_dispatch_pending_load()
|
||||
else:
|
||||
SimBridge.connection_state_changed.connect(_on_sim_connected_for_load)
|
||||
|
||||
# Camera anchor: snap to player position before the first frame renders.
|
||||
# In test mode poll_snapshot() returns synchronously — position is set
|
||||
@@ -175,10 +173,15 @@ func _process(delta: float) -> void:
|
||||
if input.action == InputMapper.Action.OPEN_JOURNAL:
|
||||
_toggle_journal()
|
||||
continue
|
||||
# #257: LOAD_GAME — show loading screen before sending to server
|
||||
# #257: LOAD_GAME — send first, then show loading screen (avoids stuck overlay if send fails)
|
||||
if input.action == InputMapper.Action.LOAD_GAME:
|
||||
if loading_screen:
|
||||
var err := SimBridge.send_input(input)
|
||||
_pending_record_inputs.append(input)
|
||||
if err == OK and loading_screen:
|
||||
loading_screen.show_loading()
|
||||
elif err != OK:
|
||||
push_error("main.gd: LOAD_GAME send_input failed: %s" % error_string(err))
|
||||
continue
|
||||
# #528: ESC/OPEN_MENU — client-only, toggle audio settings dialog
|
||||
if input.action == InputMapper.Action.OPEN_MENU:
|
||||
if settings_dialog:
|
||||
@@ -483,6 +486,32 @@ func _on_connection_state_changed(old_state: SimBridge.ConnectionState, new_stat
|
||||
gauntlet_hud.finalize()
|
||||
|
||||
|
||||
# #257: Deferred LOAD_GAME dispatch — fires once when SimBridge reaches CONNECTED.
|
||||
# pending_load_path is set by main_menu.gd before scene change.
|
||||
func _on_sim_connected_for_load(_old_state: SimBridge.ConnectionState, new_state: SimBridge.ConnectionState) -> void:
|
||||
if new_state != SimBridge.ConnectionState.CONNECTED:
|
||||
return
|
||||
if SimBridge.connection_state_changed.is_connected(_on_sim_connected_for_load):
|
||||
SimBridge.connection_state_changed.disconnect(_on_sim_connected_for_load)
|
||||
_dispatch_pending_load()
|
||||
|
||||
|
||||
func _dispatch_pending_load() -> void:
|
||||
var load_path := GameState.pending_load_path
|
||||
if load_path.is_empty():
|
||||
return
|
||||
GameState.pending_load_path = ""
|
||||
var err := SimBridge.send_input({
|
||||
"action": InputMapper.Action.LOAD_GAME,
|
||||
"timestamp_msec": Time.get_ticks_msec(),
|
||||
"action_data": {"path": load_path},
|
||||
})
|
||||
if err != OK:
|
||||
push_error("main.gd: failed to send LOAD_GAME after connection — %s" % error_string(err))
|
||||
if loading_screen:
|
||||
loading_screen.hide_loading(false)
|
||||
|
||||
|
||||
# #501: Detect large position jump indicating a teleport (not normal movement).
|
||||
const TELEPORT_DISTANCE_THRESHOLD: float = 5.0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user