# Передача сессии Состояние проекта на **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. Текущее состояние | Проверка | Результат | |---|---| | Тесты | **129/129** | | `validate` | 0 ошибок, 0 предупреждений | | `translations check` | 94/94 | | `stress 1000` | 1000/1000 партий, 0 ошибок | | Карты | **94/94** (66 с текстом + 28 ванильных) | | Протокол и контракты | `docs/protocol.md`, `Vanguard.Shared` готовы | | Сервер / клиент / лаунчер | не начаты | Блок «Игровое ядро» из `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, пройдено 129 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-реплей полной партии с проверкой хэша. `server/tests/Vanguard.Rules.Tests/ProtocolTests.cs` — **12** тестов: - round-trip всех кадров клиента и сервера через MessagePack, включая вложенные union'ы `MatchView` и `EventView`; - отказ на пустом и слишком большом кадре; - соответствие интентов и `GameCommand` в обе стороны; - скрытие руки противника, колоды и закрытого урона; - отсутствие seed/RNG в DTO; - `LegalIntents` только у решающего игрока; - скрытие поиска в колоде от противника и сохранение глобальных номеров событий при фильтрации. ### 4.7. Сеть: протокол и контракты - `docs/protocol.md` — версия 1: WebSocket + MessagePack, handshake по одноразовому тикету, `intentId` и идемпотентность, `eventSeq` и resync, heartbeat, реконнект 0/2/10/30 с, таймер 90 с + банк 5 мин, авто-поражение через 5 минут отсутствия, таблица видимости зон. - `Vanguard.Shared` — мультитаргет `netstandard2.1;net10.0`: - `netstandard2.1` — DTO и MessagePack для Unity, без ссылки на правила; - `net10.0` — дополнительно проекция состояния поверх `Vanguard.Rules`. - DTO: кадры клиента и сервера, 18 интентов, 30 событий, `MatchView`, `PlayerView`, `CardView`, `ClockView`. - `ProtocolResolver` — `StandardResolver` + интернирование строк; `MessagePackSecurity.UntrustedData`; лимит кадра 256 КиБ. - `MatchProjection` — отдельный `MatchView` на каждого игрока; `EventProjection` — фильтрация скрытых событий и диапазон по `eventSeq`; `IntentMapping` — единственное место связи DTO с командами движка. - Проекция вызывается для наблюдателя без побочных эффектов и не трогает `LegalCommands()` чужого игрока (иначе утекала бы рука решающего). --- ## 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/src/Vanguard.Shared /Protocol/*.cs кадры, интенты, события, view, кодек /Projection/*.cs проекция состояния, фильтр событий, маппинг /server/tests/Vanguard.Rules.Tests 129 тестов /content/sets карты /content/translations/ru.yaml переводы /docs/translation-rules.md канон перевода /docs/protocol.md протокол матча, версия 1 /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. **[x] `docs/protocol.md`** — версия 1: кадры, handshake по тикету, `intent` с `intentId` и `eventSeq`, resync, реконнект 0 / 2 / 10 / 30 с, отсутствие 5 минут → авто-поражение, таймер 90 с + банк 5 мин, heartbeat, коды ошибок, таблица видимости. 2. **[x] `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. Продолжить с `Vanguard.Server`: ASP.NET Core + WebSocket, тикеты, `IMatchGrain`, матчмейкинг. `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 не срабатывал. - **Shared.** Мультитаргет: файлы `Projection/` компилируются только для `net10.0`; в `netstandard2.1` (Unity) ссылки на `Vanguard.Rules` нет. - **Видимость.** `LegalCommands()` описывает решающего игрока. Вызывать его для наблюдателя нельзя — в списке есть printingId из чужой руки. - **Порядок фаз.** Вход в Основную — `EnterMainPhase()` с `OnPhaseStart`. - **Отложенные продолжения.** `State.Continuation` — что выполнить после паузы; `State.PendingQueue` + `_resolvingDepth` запрещают вложенность эффектов. - **`OfferAbility`.** Возвращает `Skipped / Paused / Resolved`; вызывающий код продолжает очередь только при `Skipped`. Ошибка здесь уже приводила к потере шага защиты. - **`InTransit`.** Найденная карта, ждущая выбора круга, лежит в `State.InTransit`, иначе инвариант «50 карт» падает. - **Задний ряд.** Обычные карты атакуют только передний ряд; Tejas — задний в своей колонке через `snipeBackRow`. - **Реестр карт.** Новая карта без записи в `CardScripts.Default` молча останется без способностей; проверять покрытие скриптом. - **Тестовые колоды.** `BuildDeck()` в тестах не содержит все карты; для конкретной карты использовать `MakeCard()` и класть вручную.