Add session handoff with current state and next steps

docs/HANDOFF.md records the completed engine, card coverage, replay, stress results, key files, decisions, remaining server/meta/client work and the pitfalls found during development. README links to it.
This commit is contained in:
MpakobecEx 2026-10-21 14:06:10 +05:00
parent 99532b6e8f
commit 9e1ed87d5d
2 changed files with 322 additions and 0 deletions

View File

@ -113,4 +113,5 @@ dotnet run --project server/src/Vanguard.Cli -- translations check
- `plan.md` — исходный дизайн-документ. - `plan.md` — исходный дизайн-документ.
- `DEVELOPMENT_CHECKLIST.md` — решения, механики, экономика, риски. - `DEVELOPMENT_CHECKLIST.md` — решения, механики, экономика, риски.
- `IMPLEMENTATION_CHECKLIST.md` — план реализации по блокам и гейтам. - `IMPLEMENTATION_CHECKLIST.md` — план реализации по блокам и гейтам.
- `docs/HANDOFF.md` — состояние на конец сессии и порядок следующих шагов.
- `content/cards-mvp.md` — каталог карт с разбором состава сетов. - `content/cards-mvp.md` — каталог карт с разбором состава сетов.

321
docs/HANDOFF.md Normal file
View File

@ -0,0 +1,321 @@
# Передача сессии
Состояние проекта на **21 октября 2026**. Документ нужен, чтобы новая сессия
продолжила работу без повторного разбора истории.
---
## 1. Проект
Фан-ремастер **Cardfight!! Vanguard** базовой эры (2011), формат **Classic**.
MVP: бустер **BT01** + триал-деки **TD01/TD02** — 94 карты, 124 принта.
Стек:
- сервер — **.NET 10**, планируются **Orleans**, PostgreSQL, Redis;
- клиент — **Unity 6** (ещё не начат);
- лаунчер — **WPF** (ещё не начат);
- контент — YAML + C#-скрипты карт;
- транспорт — WebSocket/TCP + MessagePack (решение зафиксировано, кода ещё нет).
Репозиторий:
- ветка `master`;
- remote `ssh://git@192.168.31.158:2222/mpakobecex/VanguardReMaster.git`;
- последний коммит на момент передачи — **`99532b6`**;
- рабочее дерево чистое.
Главные принципы:
- сервер авторитарный, клиент тонкий;
- скрытая информация (колода, рука противника, закрытый урон, seed) не уходит клиенту;
- `Vanguard.Rules` — чистая библиотека без сети, БД и Orleans: команда на вход, события на выход;
- каждый блок работы коммитится и пушится в `master`.
---
## 2. Текущее состояние
| Проверка | Результат |
|---|---|
| Тесты | **117/117** |
| `validate` | 0 ошибок, 0 предупреждений |
| `translations check` | 94/94 |
| `stress 1000` | 1000/1000 партий, 0 ошибок |
| Карты | **94/94** (66 с текстом + 28 ванильных) |
| Сервер / клиент / лаунчер | не начаты |
Блок «Игровое ядро» из `IMPLEMENTATION_CHECKLIST.md` закрыт по его критериям:
боты доигрывают 1000 партий, инварианты держатся, реплей воспроизводится
бит-в-бит по хэшу событий.
---
## 3. Как проверить
`dotnet` установлен в `%LOCALAPPDATA%\dotnet`, поэтому в PowerShell:
```powershell
$env:PATH = "$env:LOCALAPPDATA\dotnet;$env:PATH"
dotnet test server\Vanguard.slnx
dotnet run --project server\src\Vanguard.Cli -- validate
dotnet run --project server\src\Vanguard.Cli -- translations check
dotnet run --project server\src\Vanguard.Cli -- stress 1000
```
Ожидаемо:
```
Пройден! : не пройдено 0, пройдено 117
OK: ошибок 0, предупреждений 0
Переведено: 94/94
Партий: 1000, завершено: 1000, ошибок: 0
```
Solution — **`server/Vanguard.slnx`**, не `.sln`.
---
## 4. Что сделано
### 4.1. Контент
- 94 карты, 124 принта в `content/sets/BT01.yaml`, `TD01.yaml`, `TD02.yaml`.
- Импортёр `tools/import_cards.py`.
- Валидатор контента: щиты, Sentinel, параллельные арты, лимиты колоды.
- Токенизация текстов, `content/keywords.yaml`.
- Русские переводы `content/translations/ru.yaml` — 94/94.
- Канон перевода `docs/translation-rules.md` v0.8.
### 4.2. Игровое ядро
`server/src/Vanguard.Rules/Engine/GameEngine.cs`.
Реализовано:
- фазы `Mulligan → Stand → Draw → Ride → Main → Battle → End`;
- стартовый Авангард, маллиган, добор;
- райд, призыв, перемещение в пределах колонки (занятый круг меняется местами);
- атака по Авангарду и переднему ряду Арьергарда;
- поддержка, перехват, идеальная защита (Sentinel);
- drive check и damage check с выбором целей;
- триггеры: Critical, Stand, Draw, Heal;
- победа/поражение, детерминированный RNG;
- стоимости: Counter Blast, Soul Blast, сброс, поворот себя, уничтожение себя;
- поиск в колоде без просмотра, поиск с призывом и с райдом;
- просмотр верхней карты;
- способности: `Triggered`, `Continuous`, `Activated`;
- модификаторы силы/крита с источником и сроком жизни (`StatModifier`);
- выдача и снятие способностей (`GrantedAbility`);
- запреты (`CardRestriction`): не вставать, не атаковать, не поддерживать,
не перехватывать, не быть выбранным;
- снятие Сдерживания эффектом;
- возврат карты с поля в колоду после боя и в конце хода;
- способности из руки, сброса и круга защиты;
- `AskUnitMultiChoice` для «до N» / «ровно N»;
- `LegalCommands()` — все легальные команды текущего момента;
- `GameInvariants.Check()` — проверка 50 карт, уникальных `instanceId`, урона ≤ 6.
Триггеры (`AbilityTrigger`):
`OnRideOver`, `OnPlace`, `OnBoost`, `OnBoosted`, `OnBoostedHit`, `OnDriveCheck`,
`OnGuard`, `OnRetireFromRC`, `OnOpponentRetire`, `OnLeaveGuardian`,
`OnPhaseStart`, `OnAttackDeclared`, `OnAttackHit`, `OnDamageTaken` (объявлен,
но пока не вызывается — карт с ним нет).
### 4.3. Карты
- **59** скриптов в `CardScripts.cs`, `CardScriptsBT01.cs`, `CardScriptsTD.cs`.
- **6** карт Sentinel/Heal обрабатываются данными и движком без скрипта.
- **Tejas** реализован флагом `snipeBackRow` в YAML.
- **28** ванильных карт скрипта не требуют.
- Итого **66/66** карт с текстом закрыты.
### 4.4. Реплей
`server/src/Vanguard.Rules/Replay/`:
- `CommandCodec` — команда в стабильную строку и обратно;
- `MatchRecord` — seed, колоды, команды, хэш событий;
- `MatchRecorder` — записывает только принятые команды;
- `ReplayRunner` — воспроизводит и проверяет хэш;
- `EventHasher` — FNV-1a по тексту событий.
Запись переживает JSON и воспроизводится бит-в-бит.
### 4.5. Диагностика
- `server/src/Vanguard.Rules/Diagnostics/GameInvariants.cs`;
- CLI-команда `stress [партий]` — случайный бот, лимит 5000 шагов на партию.
### 4.6. Тесты
`server/tests/Vanguard.Rules.Tests/GameEngineTests.cs` — **117** тестов:
- механики фаз, боя, защиты, перехвата, триггеров;
- все ключевые карты и стоимости;
- 50 детерминированных партий случайного бота с инвариантами;
- JSON-реплей полной партии с проверкой хэша.
---
## 5. Структура и ключевые файлы
```
/server/src/Vanguard.Rules
/Engine/GameEngine.cs основной движок (~2500 строк)
/Engine/GameState.cs состояние, зоны, InTransit
/Engine/GameModels.cs CardInstance, Step, Phase, PendingChoice
/Engine/GameCommands.cs команды
/Engine/GameEvents.cs события
/Engine/Modifiers.cs StatModifier, GrantedAbility, CardRestriction
/Engine/Abilities/Abilities.cs AbilityDefinition, ActivatedAbility, триггеры
/Engine/Abilities/CardScripts.cs реестр Default + базовые карты
/Engine/Abilities/CardScriptsBT01.cs
/Engine/Abilities/CardScriptsTD.cs
/Cards/CardDefinition.cs статы, Sentinel, Restraint, snipeBackRow
/Decks/DeckValidator.cs правила колоды
/Replay/ запись и воспроизведение
/Diagnostics/GameInvariants.cs
/server/src/Vanguard.Content загрузка YAML и валидация
/server/src/Vanguard.Cli validate, stats, translations, stress
/server/tests/Vanguard.Rules.Tests 117 тестов
/content/sets карты
/content/translations/ru.yaml переводы
/docs/translation-rules.md канон перевода
/docs/HANDOFF.md этот файл
```
---
## 6. Принятые решения
Их не нужно пересматривать без причины.
- Формат называется **Classic**, не Standard.
- Имена карт английские; `nameRu` зарезервировано.
- Термины: Авангард, Арьергард, ранг, смена, поддержка, проверка,
фазы Подъёма/Добора/Смены/Основная/Боевая/Завершающая, завершающий шаг,
Сдерживание, «вы», буква ё.
- Способности ищутся по `nameId`, а не по принту.
- Лимит 4 копий — по `nameId`, только правило декбилдера.
- Дубль в коллекции — по `printingId`; порог 4, замена внутри редкости.
- Бустер: 5 карт = 4 C + 1 R+; веса R 70 / RR 22 / RRR 7 / SP 1.
- Gold за матч: 30/15/20 (победа/поражение/ничья); Gold = XP / 5.
- Таймер хода 90 с + банк 5 мин.
- Порядок триггеров выбирает игрок; опциональные способности спрашиваются.
- Непрерывные эффекты пересчитываются при каждом запросе.
- Поиск в колоде: 0 — уведомление и перемешивание, 1 — сразу, 2+ — выбор.
- Эффекты не вкладываются: цепочка строго друг за другом.
- Каждый блок — коммит и push в `master`.
---
## 7. Что осталось
### 7.1. Ближайший блок — сервер
Порядок:
1. **`docs/protocol.md`** — спецификация:
- кадры, handshake, версия, ping/pong;
- `intent` с `intentId`, `eventSeq`;
- `resync` по запросу;
- реконнект 0 / 2 / 10 / 30 с;
- отсутствие 5 минут → авто-поражение;
- таймер 90 с + банк 5 мин.
2. **`Vanguard.Shared`**:
- DTO интентов и событий;
- MessagePack-резолвер;
- проекция состояния под клиента.
3. **`Vanguard.Server`**:
- ASP.NET Core + WebSocket;
- `IGameTicketService` (одноразовый тикет, TTL 60 с);
- `IMatchGrain` — 1 матч = 1 grain, последовательная обработка;
- матчмейкинг: 2 очереди (Classic Ranked / Casual);
- скрытый MMR, расширяющееся окно;
- идемпотентность по `intentId`.
4. **Проекция состояния**:
- своя рука и свой триггер — полностью;
- открытые зоны — полностью;
- колода, рука противника, закрытый урон — только количество;
- seed и RNG не уходят.
5. **Персистентность**:
- PostgreSQL — снапшот матча, восстановление grain;
- Redis — тикеты, очереди, кэш.
6. **Docker Compose** + `/health`.
Критерий готовности: два клиента играют полный матч по сети; kill клиента
на любом шаге восстанавливается.
### 7.2. Мета-API
- `User`, BCrypt, JWT (access 15 мин + refresh 30 дней, ротация).
- `register`, `login`, `refresh`, `logout`, `me`.
- Коллекция по принтам, без верхнего предела.
- Колоды: CRUD, `validate`, лимит 30, шаринг по коду.
- Магазин, бустеры, триал-деки, токены.
- Пыль: разбор C 5 / R 10 / RR 25 / RRR 100, крафт ×4, SP не крафтится.
- Онбординг: триал-дека + 5 токенов.
- Матчмейкинг, результат матча, Gold/XP, история.
- Rate limiting.
### 7.3. Клиент
- Unity 6 LTS, URP, Addressables, Input System, Localization, DOTween.
- Сцены `Boot`, `Meta`, `Battle`.
- VContainer, `ApiClient`, `GameConnection`.
- Экраны: логин, меню, магазин, коллекция, декбилдер, очередь, настройки.
- Боевая сцена: поле, рука, зоны, анимации, таймер.
- Локализация RU/EN на лету.
### 7.4. Лаунчер
- WPF: обновления, запуск клиента, выбор сервера.
### 7.5. Мелкие технические долги
- `OnDamageTaken` не вызывается — добавить, когда появится карта с таким триггером.
- Глобальный лимит шагов внутри движка не enforced — только в стресс-тесте.
- `GameEngine.cs` большой; при росте разнести на partial-файлы.
- `MatchRecord` сейчас JSON; для сети позже MessagePack.
- В `LegalCommands()` не входят активируемые способности со стоимостью «сброс карты» —
для UI их нужно будет перечислить с вариантами сброса.
---
## 8. Порядок действий в новой сессии
1. Прочитать `docs/HANDOFF.md`, `IMPLEMENTATION_CHECKLIST.md`, `README.md`.
2. Прогнать проверки из раздела 3.
3. Начать с `docs/protocol.md` и `Vanguard.Shared`.
4. После каждого блока: тесты, `validate`, `translations check`, коммит, push.
5. Не ломать `Vanguard.Rules`: сервер должен зависеть от него, а не наоборот.
---
## 9. Подводные камни
- **Кодировка.** Не использовать PowerShell `Get-Content`/`Set-Content` для
UTF-8 файлов с русским текстом: однажды это уже испортило комментарии.
Правки — только через `edit`/`write`, либо Python с `encoding="utf-8"`.
- **PATH.** `dotnet` лежит в `%LOCALAPPDATA%\dotnet`; без добавления в PATH
команды не находятся.
- **Solution.** `server/Vanguard.slnx`, не `.sln`.
- **Тесты.** `StartedEngine()` принудительно ставит Conroe стартовым Авангардом,
чтобы Barcgal не срабатывал.
- **Порядок фаз.** Вход в Основную — `EnterMainPhase()` с `OnPhaseStart`.
- **Отложенные продолжения.** `State.Continuation` — что выполнить после паузы;
`State.PendingQueue` + `_resolvingDepth` запрещают вложенность эффектов.
- **`OfferAbility`.** Возвращает `Skipped / Paused / Resolved`; вызывающий код
продолжает очередь только при `Skipped`. Ошибка здесь уже приводила к потере
шага защиты.
- **`InTransit`.** Найденная карта, ждущая выбора круга, лежит в
`State.InTransit`, иначе инвариант «50 карт» падает.
- **Задний ряд.** Обычные карты атакуют только передний ряд; Tejas — задний
в своей колонке через `snipeBackRow`.
- **Реестр карт.** Новая карта без записи в `CardScripts.Default` молча
останется без способностей; проверять покрытие скриптом.
- **Тестовые колоды.** `BuildDeck()` в тестах не содержит все карты; для
конкретной карты использовать `MakeCard()` и класть вручную.