Drive server: savestates, ff6cli --serve, ff6_drive MCP plugin #13

Merged
Gatchan merged 6 commits from drive-server into main 2026-10-29 09:36:12 +00:00
Owner

Drive server and MCP adapter (brief §8.3), first version.

Content

  • Core debug API (ff6_debug.h): ff6_debug_state_save/load (in-memory savestate of the whole core: LakeSnes state + frame counter + picture buffer + audio ring buffer and last frame audio, so a loaded state shows and plays exactly as saved; version-checked header, invalid states rejected with the core unchanged), ff6_debug_memory_write (poke WRAM/VRAM/CGRAM/OAM/SRAM), ff6_debug_frame_output (current picture without running a frame). Port modes and counters are not part of a state; a pending shadow call is cancelled and counted as skipped on save and on load.
  • ff6cli --serve (hosts/cli/serve.c): line protocol on stdin/stdout, one ok/err reply per command, key=value pairs: info, run, peek, poke, save/load (slots 0-9), shot, port, ports, quit. Errors never stop the server. Documented in docs/build.md.
  • MCP plugin tools/mcp/tools/ff6_drive.py (stdlib only): ff6_start/stop/info/run/screenshot/peek/poke/save_state/load_state/port/ports, one ff6cli process kept in ctx.state; screenshot returned as an MCP image. Clear errors for missing binary/ROM/session.
  • Tests: rom.savestate.{US,JP}_V10 (save at frame 690, run 150 frames across the A press, load, replay: identical per-frame picture+audio hashes on the same core and on a second core; invalid state rejected; poke checks), rom.serve.{US,JP}_V10 (CMake script feeds a command file to ff6cli --serve, checks all 17 replies and that the picture after load equals the one at save; runs under wine too). tools/mcp/tests/test_ff6_drive.py drives the MCP host over stdio (exit 77 when host/build/ROM missing).

Validation

  • Builds without warnings: debug (ASan/UBSan), release, clang, mingw.
  • release ctest: 9/9 passed, slow included (687.9 s; rom.ports 311.6 s US / 308.6 s JP, savestate 11.4 s, serve 5.8 s).
  • debug ctest -LE slow: 7/7 passed (245.9 s).
  • mingw ctest -LE slow under wine: 7/7 passed (79.6 s).
  • MCP stdio test: 20/20 checks OK (6.2 s); host selftest ALL OK.

Not covered

  • The pending-shadow cancel path is not exercised by a test (the only port, battle/Rand, is reached ~9500 frames in).

Fixes #6

🤖 Generated with Claude Code

Drive server and MCP adapter (brief §8.3), first version. ## Content - **Core debug API** (`ff6_debug.h`): `ff6_debug_state_save/load` (in-memory savestate of the whole core: LakeSnes state + frame counter + picture buffer + audio ring buffer and last frame audio, so a loaded state shows and plays exactly as saved; version-checked header, invalid states rejected with the core unchanged), `ff6_debug_memory_write` (poke WRAM/VRAM/CGRAM/OAM/SRAM), `ff6_debug_frame_output` (current picture without running a frame). Port modes and counters are not part of a state; a pending shadow call is cancelled and counted as skipped on save and on load. - **`ff6cli --serve`** (`hosts/cli/serve.c`): line protocol on stdin/stdout, one `ok`/`err` reply per command, `key=value` pairs: `info`, `run`, `peek`, `poke`, `save`/`load` (slots 0-9), `shot`, `port`, `ports`, `quit`. Errors never stop the server. Documented in `docs/build.md`. - **MCP plugin** `tools/mcp/tools/ff6_drive.py` (stdlib only): `ff6_start/stop/info/run/screenshot/peek/poke/save_state/load_state/port/ports`, one ff6cli process kept in `ctx.state`; screenshot returned as an MCP image. Clear errors for missing binary/ROM/session. - **Tests**: `rom.savestate.{US,JP}_V10` (save at frame 690, run 150 frames across the A press, load, replay: identical per-frame picture+audio hashes on the same core and on a second core; invalid state rejected; poke checks), `rom.serve.{US,JP}_V10` (CMake script feeds a command file to `ff6cli --serve`, checks all 17 replies and that the picture after `load` equals the one at `save`; runs under wine too). `tools/mcp/tests/test_ff6_drive.py` drives the MCP host over stdio (exit 77 when host/build/ROM missing). ## Validation - Builds without warnings: debug (ASan/UBSan), release, clang, mingw. - release `ctest`: 9/9 passed, slow included (687.9 s; rom.ports 311.6 s US / 308.6 s JP, savestate 11.4 s, serve 5.8 s). - debug `ctest -LE slow`: 7/7 passed (245.9 s). - mingw `ctest -LE slow` under wine: 7/7 passed (79.6 s). - MCP stdio test: 20/20 checks OK (6.2 s); host selftest ALL OK. ## Not covered - The pending-shadow cancel path is not exercised by a test (the only port, battle/Rand, is reached ~9500 frames in). Fixes #6 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Gatchan/FF6Portage!13
No description provided.