mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
99e7dc17ec
A shared-world, classic-door-game-style door game in examples/door-game/: a pure-stdlib game engine (tile overworld + location menus, seeded combat, daily turn budget, leveling, shop, event log, leaderboard) behind nine sync door_* FastMCP tools returning monochrome box-drawing frames. The connecting session's LLM plays dungeon master — tool descriptions plus a door_help manual teach a cold model to run the game with zero setup, while the server owns all dice and state, so the DM narrates around facts it cannot bend. Non-obvious decisions: - engine/screen/world/persistence import stdlib only; server.py is the only mcp import. All nine handlers are sync def: on mcp 1.27 they execute inline on the event loop (verified against func_metadata), so tool bodies serialize and one SQLite connection (WAL, per-action commit) is safe. check_same_thread=False exists only because the Store may be constructed on a different thread than the serving loop. - Streamable HTTP serves ONE process = one shared world (players appear on each other's maps; async "while you were away" event feed); stdio is the solo-world fallback. - The economy is content, not code: daily_turns, costs, xp curve, bestow budget, and dungeon tiers live in world.json settings, band-validated by the loader. door_bestow gives the DM capped, event-audited largesse (gold/heal only, never turns) so story generosity cannot melt the shared leaderboard. - Player names and bestow reasons are sanitized (printable-only, length caps) because they flow into the shared event log and from there into other players' DM context — embedded newlines would forge log lines. - Daily turn/bestow pools lazy-reset per UTC day on every consuming path (injectable clock); the dungeon gauntlet is a fixed boss ladder by design. Tests: 109 — engine units with seeded RNG + frozen clock, hand-authored golden frames paired with structural asserts, loader band rejections, and one real-wire integration test (uvicorn + streamablehttp_client) with a two-session shared-world assertion. Negative-tested by reverting the guard and watching the suite fail: the daily turn-budget guard, the bestow cap, and the sanitizer's isprintable clause.