From 4c24a016f119347179a5c42921974f7cd65fa3ff Mon Sep 17 00:00:00 2001 From: root Date: Mon, 9 Mar 2026 15:21:11 +0000 Subject: [PATCH] docs: update CLAUDE.md with world persistence documentation Co-Authored-By: Claude Opus 4.6 --- CLAUDE.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 42fa97c..0f6596b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,13 +10,15 @@ Multiplayer NPC simulation game (Dwarf Fortress-inspired). Server-authoritative - `server/` -- Node.js + Socket.io game server with ECS - `client/` -- Phaser 3 + TypeScript renderer - `chars/` -- Character sprite assets (do not modify) +- `saves/` -- SQLite save files (gitignored, one .db per region) - `docs/plans/` -- Design and implementation docs ## Commands - `npm install` -- install all workspace dependencies -- `npm -w server run dev` -- start server (port 3001) -- `npm -w server run test` -- run server tests (338 tests) +- `npm -w server run dev` -- start server (port 3001), loads existing save or creates new world +- `npm -w server run dev -- --new-world` -- start server with fresh world (overwrites save) +- `npm -w server run test` -- run server tests (408 tests) - `npm -w server run test:watch` -- run server tests in watch mode - `npm -w client run dev` -- start client dev server (port 3000) - `npm -w client run build` -- build client for production @@ -25,14 +27,19 @@ Multiplayer NPC simulation game (Dwarf Fortress-inspired). Server-authoritative ## Tooling - ESM modules throughout (`"type": "module"`) -- Server: tsx (runtime), vitest (tests) +- Server: tsx (runtime), vitest (tests), better-sqlite3 (persistence) - Client: vite (bundler), Phaser 3 - Shared: compiled to `shared/dist/` (auto-built) ## Key Entry Points -- `server/src/main.ts` -- server startup -- `server/src/game/GameLoop.ts` -- tick loop orchestration +- `server/src/main.ts` -- server startup, --new-world flag, graceful shutdown (SIGINT/SIGTERM) +- `server/src/game/GameLoop.ts` -- tick loop orchestration, save/load integration +- `server/src/persistence/database.ts` -- SQLite connection, schema creation, migration runner +- `server/src/persistence/saveManager.ts` -- orchestrates periodic saves, shutdown saves, load-on-startup +- `server/src/persistence/worldSerializer.ts` -- save/load map tiles and metadata +- `server/src/persistence/entitySerializer.ts` -- save/load entities, components, relationships, bonds +- `server/src/persistence/eventSerializer.ts` -- save/load narration, memory, stockpile, invention events - `server/src/spawner/appearanceGenerator.ts` -- NPC appearance + portrait feature generation - `server/src/spawner/nameGenerator.ts` -- NPC name generation - `server/src/spawner/statGenerator.ts` -- 3d6 stat generation for NPCs @@ -87,3 +94,15 @@ Multiplayer NPC simulation game (Dwarf Fortress-inspired). Server-authoritative - Building: creates structure entity with buildProgress 0→1; structures have type (stockpile/workshop) + subtype + inventory - After gathering, NPCs drop off items at nearest completed stockpile via 'dropoff' goal - Movement pauses during gathering, crafting, and building (same skip pattern) +- World persistence uses SQLite (better-sqlite3) with single file per region at `saves/default.db` +- Event data (narration, memory, stockpile, inventions) is written to DB immediately on occurrence +- Entity state (NPCs, structures, relationships, bonds) is batch-saved every 30 seconds +- Graceful shutdown (Ctrl-C) triggers final save before exit +- On startup, loads from existing save or generates fresh world; `--new-world` flag forces fresh +- Schema versioning via `schema_version` in metadata table; migration runner for future schema changes +- In-memory event buffers (50 entries) are caches over full DB history; populated from DB on startup +- Map tiles stored fully in DB (not regenerated from seed); decoupled from map generator +- Entity IDs preserved across save/load via `World.createEntityWithId()`; nextId counter restored +- Components with Map types (inventory, pairCooldowns, structure.inventory) serialize via Object.fromEntries/new Map +- Transient state (movement paths, active social interactions, NPC goals) reset on load; NPCs resume from idle +- Player entities are not persisted; players reconnect fresh