Skip to content

[codex] Add run save snapshots - #86

Open
CompleteDotTech wants to merge 196 commits into
Gennadiyev:mainfrom
CompleteDotTech:codex/save-snapshots-docs
Open

[codex] Add run save snapshots#86
CompleteDotTech wants to merge 196 commits into
Gennadiyev:mainfrom
CompleteDotTech:codex/save-snapshots-docs

Conversation

@CompleteDotTech

Copy link
Copy Markdown
Contributor

Summary

Ports the implementation from CompleteDotTech#3 onto Gennadiyev/STS2MCP.

This adds opt-in run save snapshots: automatic snapshot capture on game saves, manual snapshot creation, snapshot listing, and guarded restoration back to the active profile's current-run save slot.

Dependency Note

The source PR was stacked on top of the endpoint-audit work from CompleteDotTech#2.

This upstream PR is therefore stacked on the branch opened as:

Until PR #85 is merged, this PR's diff against main includes the endpoint-audit foundation plus the snapshot implementation. The snapshot-specific commit is Add run save snapshots, followed by this documentation pass.

What Changed

Snapshot endpoint

Adds:

  • GET /api/v1/snapshots
  • POST /api/v1/snapshots

GET /api/v1/snapshots lists snapshot feature status and known snapshots:

  • status
  • kind: snapshots
  • enabled
  • enable_env_var
  • snapshot_root_env_var
  • snapshot_root
  • count
  • snapshots

POST /api/v1/snapshots supports:

  • {"action": "create"}
  • {"action": "resume", "snapshot_id": "..."}

MCP tools

Adds MCP wrappers:

  • list_snapshots()
  • create_snapshot()
  • resume_snapshot(snapshot_id)

These wrappers preserve structured HTTP endpoint errors through the MCP bridge, including http_status.

Opt-in behavior

Snapshots are disabled by default.

Enable automatic snapshot capture by launching the game with:

STS2_MCP_SNAPSHOTS=1

Override the snapshot root with:

STS2_MCP_SNAPSHOT_DIR=/path/to/snapshot/root

Without an override, snapshots are stored under the detected active account save root in sts2_mcp_snapshots.

Automatic and manual snapshots

When enabled, automatic snapshots subscribe to the game save event and copy the active run save whenever STS2 writes it.

Manual snapshot creation copies the active profile's latest:

  • current_run.save
  • current_run_mp.save

into a snapshot directory and writes metadata with profile, save scope, run identity, save time, mode, source file name, and creation time.

Manual creation is rejected on map and shop-like screens because those STS2 save states do not restore exactly:

  • map
  • shop
  • fake_merchant

The docs call out the recommended workflow: snapshot after choosing a map node, before entering a shop, or after leaving it.

Restore behavior

resume restores a selected snapshot to the active profile's current-run save slot.

Restore is rejected while a run is in progress. After a successful restore, use the in-game Continue flow to load the restored run.

If a current-run save already exists at the restore destination, the endpoint first writes a:

.pre_snapshot_resume_*.backup

copy before overwriting it.

Restore safety

The restore path is deliberately not trusted from serialized snapshot metadata.

The implementation:

  • reconstructs snapshot file paths from the snapshot root, snapshot ID, and supported save filename
  • accepts only current_run.save and current_run_mp.save
  • derives restore destinations from the active profile/save root
  • rejects snapshot metadata with unsupported save filenames
  • enumerates only immediate snapshot directories instead of recursively scanning arbitrary trees
  • sanitizes snapshot IDs as path components
  • logs snapshot exceptions and metadata parse failures

Structured errors

Snapshot actions return stable structured error codes such as:

  • snapshots_disabled
  • missing_snapshot_id
  • unknown_snapshot_action
  • snapshot_not_found
  • current_run_save_not_found
  • run_in_progress
  • snapshot_state_not_supported
  • save_manager_unavailable
  • snapshot_restore_path_unavailable
  • snapshots_read_failed
  • snapshot_create_failed
  • snapshot_resume_failed

Snapshot request validation reports malformed requests before feature-disabled state, so clients get stable validation errors for missing actions or missing snapshot IDs.

Documentation

Updates:

  • README.md
  • docs/raw-full.md
  • docs/raw-simplified.md
  • mcp/README.md
  • docs/endpoint-contracts.md

The dedicated endpoint-contract doc now covers snapshot enablement, API actions, restore safety rules, blocked UI states, structured error codes, MCP wrappers, and audit coverage.

Validation

Completed locally on this branch:

  • python3 -m py_compile scripts/audit_endpoints.py scripts/test_mcp_server.py mcp/server.py
  • python3 scripts/audit_endpoints.py --skip-live
  • uv run --project mcp python scripts/test_mcp_server.py
  • "/mnt/c/Program Files/dotnet/dotnet.exe" build STS2_MCP.csproj -c Release -o out/STS2_MCP -p:STS2GameDir="C:\Program Files (x86)\Steam\steamapps\common\Slay the Spire 2"

The .NET build completed successfully with 0 warnings and 0 errors.

Live snapshot validation was not rerun during this upstream port because it requires launching the game with STS2_MCP_SNAPSHOTS=1. The source PR includes live verification notes for snapshot listing, manual creation, and in-progress restore rejection.

CompleteDotTech and others added 30 commits March 26, 2026 11:50
The map screen's player summary only included hp, gold, and potion slot
counts. This adds full relic and potion data to BuildMapState so external
tools can display them outside of combat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Map data is now always returned alongside any game state (combat, event,
shop, etc.) so external tools can display the map without caching. Also
adds a version marker for verifying mod DLL is loaded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Always attach BuildPlayerState output at the top level of every response
so external tools have access to HP, gold, relics, potions, deck piles,
and powers regardless of which screen is active.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exposes player.Deck.Cards in BuildPlayerState so the full deck
composition is available on every screen, not just during combat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Some screens (e.g. treasure) can cause null reference exceptions
when BuildPlayerState accesses combat-only properties.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When NMapScreen is not open (during combat, events, etc.), compute
next_options from the current map point's children so external tools
always have the available path choices.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GET /api/v1/glossary — returns all categories
GET /api/v1/glossary/cards — all cards with pool grouping
GET /api/v1/glossary/relics — all relics with pool grouping
GET /api/v1/glossary/potions — all potions with pool grouping
GET /api/v1/glossary/keywords — unique keywords from all items

Uses current character's pools during a run, plus colorless/curse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tries http://+:15526/ first for network access, falls back to
localhost-only if the URL ACL is not configured.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Returns all cards from the current character's card pool during an
active run. Includes id, name, type, cost, description, rarity, pool,
and keywords for each card.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Returns relics from the current character's relic pool. Shared/event
relics not yet accessible — canonical instance lookup returns null
at runtime. Will revisit access pattern later.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Returns potions from the current character's potion pool.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Harvests unique keywords from all cards, relics, and potions in the
current character's pools. Returns name and description for each.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows visible menu buttons as numbered options in player stats widget.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detects main menu, singleplayer mode select, and character select screens.
Shows available options with enabled/locked state. Character select includes
HP, gold, energy, starting relics, starting deck, and pool counts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Supports navigating Main Menu, Singleplayer Mode Select, and Character
Select screens. Includes back button, character selection with lock
detection, and embark/confirm to start a run.

Actions: menu_select with options:
- Main menu: singleplayer, multiplayer, settings, quit, etc.
- Mode select: standard, daily, custom, back
- Character select: IRONCLAD, SILENT, etc., confirm, back

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Detects NGameOverScreen overlay as state_type: game_over with
continue and main_menu options. Handles via menu_select action.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exposes player profile data: character stats (wins, losses, ascension,
streaks), card stats (pick/skip/win/loss), encounter and enemy stats,
discovered items, total playtime, score, and floors climbed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Profile endpoint now includes by_character win/loss for each encounter
and enemy. Widgets show the breakdown under each entry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds ancient stats with per-character breakdown, epoch milestones,
unlocked achievements, and global stats (wins, losses, runs, best
streak, fastest victory, architect damage).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GET /api/v1/bestiary returns 126 monsters (HP range, moves) and
95 encounters (type, tier). Widget merges with profile win/loss stats.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Encounters now include min/max gold reward and likely_monsters
inferred from encounter name. Widget shows gold and monster columns.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Timeline shows epoch names, descriptions, and unlock requirements.
Multiplayer submenu shows host/join/load options. Settings, compendium,
and timeline screens properly detected.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Timeline widget now groups epochs into Completed (green), Locked (yellow
with unlock requirements), and hidden count.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No more flickering from mouse hover. Shows earned epochs from save
data with slot counts for remaining.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant