Files
settled-reach/docs/sprints/sprint-19/client.md
T
jpmschweitzerandClaude Opus 4.6 bccdcfcdcb docs(docs): add frontmatter to all sprint briefings
Standardized YAML frontmatter on all 115 sprint briefing files across
sprints 1-26 with title, description, type, status, sprint number, and
team fields.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 00:15:45 +01:00

7.8 KiB

title, description, type, status, sprint, team
title description type status sprint team
Sprint 19 — Client Briefing Save/load client UI, game session management, GDScript test framework sprint archived 19 client

Sprint 19: Persist — Client Tasks

Goal: The player can save and resume a game session with per-game directories; the simulation tier system gains eviction and scope pinning; and the first test infrastructure ships with information boundary validation and IPC hardening.

Branch: client Agents: Stig (UI), Oscar (networking)

Carry-over from Sprint 18

None. Sprint 18 closed clean.

New Tickets

# Title Blocked by
#554 Save/load: client UI #553 (server)
#258 Game session management
#205 GDScript test framework setup
#206 Scene testing utilities #205
#348 Debug visualization overlay

Use db/connectors/ticket show <id> for full details.

Key Decisions

  • decisions/architecture.md — D-020 (IPC architecture, MessagePack), D-085 (per-game save directory structure)
  • decisions/questions.md — Q-029 (save file format design — open, Sprint 19 uses MessagePack quick-and-dirty format)

Open Questions to Resolve Early

  • Q-029: Save file format design — Sprint 19 ships MessagePack quick-and-dirty format. Do not over-engineer the loading screen metadata. A readable directory name (<timestamp>-<seed>/) per D-085 is sufficient for v0.1. The full versioning/migration design is tracked in Q-029 for a later sprint.

Notes

#554 — Save/load: client UI

Blocked by #553 (server must implement SaveCommand/LoadCommand IPC messages before client can wire F5/F6).

What this ticket must deliver:

  • F5 key mapped in client/scripts/autoloads/input_mapper.gd to send a SaveGame IPC action to the server with the active game directory path (user://saves/<game-id>/quicksave.sav)
  • F6 key mapped to send LoadGame IPC action with the same path
  • Server responds with SaveComplete/LoadComplete — client shows a brief HUD notification ("Saved" / "Loading...")
  • Loading screen scene: reads user://saves/ directory, lists subdirectories sorted by last-modified (most recent first), shows most recent save filename per game directory per D-085
  • F6 from the main menu opens the loading screen
  • The active game-id is tracked in GameState autoload (add current_game_id: String)

Integration points: client/scripts/autoloads/input_mapper.gd (key bindings), client/scripts/autoloads/game_state.gd (current_game_id field), client/scripts/protocol/ (new IPC message encoding), client/scripts/ui/ (loading screen scene).

Save directory path per D-085: user://saves/<timestamp>-<seed>/ where game-id is created on New Game (#258). F5 quicksave writes to user://saves/<game-id>/quicksave.sav. Loading screen lists directories sorted by FileAccess.get_modified_time().

Wireframe reference: docs/design/wireframes/menus/v01-save-load.png.

#258 — Game session management

New Game creates the per-game save directory before any save occurs (D-085 requirement: "directory created on New Game — even before the first save, so the path exists for quicksave/autosave").

What this ticket must deliver:

  • GameState.current_game_id: String — format <timestamp>-<seed> (e.g. 20260225-143022-a7b3f1)
  • On "New Game": generate game-id (timestamp + RNG hex suffix), create user://saves/<game-id>/ directory via DirAccess.make_dir_recursive()
  • On "Continue" / loading screen selection: set current_game_id from the selected directory name
  • "Quit to menu" flow: prompt "Save before quitting?" — F5 save if confirmed
  • Wire the game-id into the SimBridge startup: server subprocess launched with --game-id <id> argument (or equivalent) so server can log with the same ID

Integration points: client/scripts/autoloads/game_state.gd (new fields), client/scripts/protocol/server_process.gd (subprocess launch args), client/scripts/ui/ (main menu scene: New Game / Continue buttons).

Note: game_state.gd is already the largest autoload with 300+ lines. Keep game session logic in a thin wrapper on GameState — do not add another 100-line block directly. Consider a session_manager.gd helper if the logic exceeds 40 lines.

#205 — GDScript test framework setup

The project has no GDScript test infrastructure yet. The Godot client has no equivalent of cargo test.

What this ticket must deliver:

  • Install and configure GUT (Godot Unit Test) as the GDScript test framework — it has the best Godot 4 support and is actively maintained
  • Create client/tests/ as the test root directory
  • client/tests/run_gut.gd: the GUT runner script that CI can invoke headlessly (godot --headless -s client/tests/run_gut.gd)
  • Exit code 0 = all pass, non-zero = failures — required for CI integration (#270 test runner scripts)
  • A single smoke test client/tests/test_protocol.gd: verifies Protocol.decode_snapshot(bytes) returns non-null for a minimal valid msgpack fixture

GUT installation: add as a Godot addon. Check if there is already an addons/ directory in client/.

#206 — Scene testing utilities

Blocked by #205 (GUT must be installed first).

What this ticket must deliver:

  • client/tests/util/scene_helper.gd: loads a scene file by path, instantiates it into a temporary viewport, provides assert_node_exists(path), assert_signal_emitted(node, signal_name), and get_node_at(path) helpers
  • client/tests/test_game_state.gd: tests for GameState.apply_snapshot() — verify that a snapshot dictionary with known fields updates the correct GameState fields
  • client/tests/test_protocol.gd (extend from #205 smoke test): add roundtrip test for Protocol.encode_player_input() and Protocol.decode_player_input()

These utilities are the scaffolding for all future client tests. Keep them minimal and dependency-free — do not require a running server.

#348 — Debug visualization overlay

Dev tool (F3 toggle). The stub client/scripts/ui/debug_overlay.gd already exists.

What this ticket must deliver:

  • Extend debug_overlay.gd to draw on a CanvasLayer above the game world:
    • Pathfinding waypoints: draw lines between waypoint positions from GameState.visible_entities (entities with kind Npc — estimate waypoints from position delta between ticks)
    • Line-of-sight rays: draw lines from player position to each visible entity
    • Vision cone boundary: draw the forward/peripheral arc boundary using GameState.visibility_sectors
    • Information state tags: draw confidence label (Suspects/KnowsOf/KnowsDetails/Direct) above each visible NPC from GameState.player_knowledge
    • Tick timing graph: small line chart in corner showing tick delta over the last 30 ticks
  • F3 toggle: connected to InputMapper action toggle_debug_overlay
  • Debug overlay is dev-only: compiled out in export builds via OS.is_debug_build() check

Integration points: client/scripts/autoloads/game_state.gd (data source), client/scripts/autoloads/input_mapper.gd (F3 action), client/scripts/ui/debug_overlay.gd (extend existing stub).

Dependency Chain

#205 (GDScript test framework) → #206 (scene testing utilities)

#258 (game session management) → #554 (save/load client UI: needs current_game_id)
  #553 (server, ECS extraction) → #554 (save/load client UI: needs IPC commands)

#348 (debug overlay) → standalone, parallel track

Parallel starts: #258, #205, #348 all unblocked week 1. #554 starts after #553 (server) reaches review stage and #258 lands. #206 starts after #205 merges.

PR Workflow

When ready to submit, create a PR with tea CLI. All flags are required to avoid TTY prompts (see CLAUDE.md "Gitea access" section):

tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(client): description" --description "body" --base main --head client