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:
parent
99532b6e8f
commit
9e1ed87d5d
|
|
@ -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` — каталог карт с разбором состава сетов.
|
||||||
|
|
|
||||||
|
|
@ -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()` и класть вручную.
|
||||||
Loading…
Reference in New Issue