VanguardReMaster/docs/HANDOFF.md

17 KiB
Raw Blame History

Передача сессии

Состояние проекта на 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:

$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() и класть вручную.