M3: модуль сохранения

По ARCHITECTURE.md: реестр независимых секций, JSON с ключом на секцию.
Ключи — явные строковые константы, а не имена классов: релизная сборка
минифицирует имена и молча потеряла бы все сохранения.

- ISaveSection, SaveManager, IStorage + MemoryStorage
- CocosStorage: единственный файл модуля, знающий про движок;
  при отказе хранилища уходит в память, а не роняет игру
- StatsSection: сыграно/побед/поражений/ничьих, текущая и лучшая серия
- терпимая загрузка: битый JSON, чужая версия, сломанная секция
  или отвалившееся хранилище не ломают остальное
- проверка чистоты расширена на modules/ со списком адаптеров

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ftc
2026-09-13 10:45:52 +03:00
co-authored by Claude Opus 5
parent c60fbcbb94
commit 51117a698c
17 changed files with 756 additions and 29 deletions
+13 -4
View File
@@ -24,9 +24,11 @@ from the layers below it and never from the ones above.
core/ pure rules and bots — no `cc` import anywhere
```
The rule enforced by review and by the test harness: **nothing under
`assets/scripts/core/` may import from `cc`.** It is plain TypeScript, runnable
under Node, and that is what makes the rules testable without the engine.
The rule, enforced by `npm run check:core-purity`: **nothing under
`assets/scripts/core/` may import from `cc`**, and under
`assets/scripts/modules/` only the adapters explicitly listed in that script
may. Everything else is plain TypeScript, runnable under Node, which is what
makes the rules and the save format testable without the engine.
## Folder map
@@ -49,7 +51,8 @@ assets/
match/ Match (stateful shell), runMatch (headless driver)
util/ seeded RNG, shuffle
modules/
save/ SaveManager, ISaveSection, StatsSection, storage
save/ SaveManager, ISaveSection, StatsSection,
IStorage + MemoryStorage, CocosStorage (engine)
app/ Bootstrap, ServiceLocator, SceneRouter
ui/
lobby/ lobby screen controller
@@ -108,6 +111,12 @@ warning rather than failing the whole load, so a bad release cannot brick a save
Keys are explicit string constants, not class names — release builds minify.
The document written is `{ version, sections: { <key>: <payload> } }`. Storage
sits behind `IStorage`, so all of the save logic is testable headlessly;
`CocosStorage` is the only file in the module that imports the engine, and it
falls back to an in-memory store when the device refuses to persist (private
browsing, a full quota) rather than taking the game down.
## Conventions
- TypeScript, 4-space indent, `PascalCase` for types, `camelCase` for members,