VanguardReMaster/IMPLEMENTATION_CHECKLIST.md

25 KiB
Raw Blame History

Базовая версия — чеклист реализации

Практический план: что писать, в каком порядке и когда считать готовым. Дизайн-решения и обоснования — в DEVELOPMENT_CHECKLIST.md. Данные карт — в content/cards-mvp.md.


0. Границы базовой версии

Входит:

  • Платформа: Windows. Unity + WPF-лаунчер.
  • Формат: Classic, один сет — BT01 + TD01 + TD02 (94 карты).
  • Аккаунт, коллекция, полный декбилдер, магазин бустеров и триал-дек.
  • 2 очереди: Classic Ranked и Classic Casual. Матч 1×1, BO1.
  • Полный игровой цикл: матч → результат → Gold/XP → бустер → карта в коллекцию.
  • Базовый туториал + выдача триал-деки и 5 токенов.
  • Локализация RU + EN.
  • Лаунчер с проверкой версии и докачкой.

Не входит: турниры, команды, территории, чат, друзья, баттлпасс, квесты, Draft, косметика, мобилка, реплеи в клиенте (только серверная запись).

Стек (зафиксирован)

Слой Технология
Сервер .NET 10 LTS, ASP.NET Core, Orleans, EF Core, PostgreSQL 16, Redis 7
Протокол матча TCP + MessagePack, кадры с префиксом длины
Мета-API HTTP/JSON, JWT
Клиент Unity 6 LTS, URP, Addressables, Input System, Unity Localization, DOTween
Лаунчер WPF, .NET 10 LTS, CommunityToolkit.Mvvm
Контейнеры Docker Compose, nginx

Оценка (один разработчик, полный день)

Этап Дни
1. Репозиторий и каркас 2–3
2. Контент-пайплайн 4–6
3. Игровое ядро 20–30
4. Скрипты 94 карт 15–25
5. Сеть и матч 8–12
6. Мета-API 10–14
7. Клиент: каркас и меню 15–20
8. Клиент: боевая сцена 20–30
9. Туториал 5–7
10. Лаунчер 5–7
11. QA и сборка 7–10
Итого ~110–165

1. Репозиторий и каркас [2–3 дня]

  • git init, ветка master.
  • .gitignore: Unity (Library/, Temp/, Logs/, UserSettings/, Builds/), .NET (bin/, obj/), WPF (publish/), секреты (.env, appsettings.Production.json).
  • Отдельно игнорировать только Unity-генерируемые *.csproj/*.sln в /client, чтобы не убить server/*.sln.
  • .gitattributes: * text=auto eol=lf, *.unity, *.prefab, *.asset → merge=unityyamlmerge, *.png/*.wav → binary.
  • Структура (на 20.10.2026 — server, content, tools; client и launcher появятся со своими блоками):
    /server
      Vanguard.slnx
      /src/Vanguard.Rules           чистое ядро правил, без Orleans и БД
      /src/Vanguard.Content         загрузчик и валидатор контента
      /src/Vanguard.Shared          контракты, DTO, MessagePack
      /src/Vanguard.Cli             headless-раннер матчей
      /tests/Vanguard.Rules.Tests
    /content/sets/BT01.yaml
    /content/sets/TD01.yaml
    /content/sets/TD02.yaml
    /tools/import_cards.py
    
  • Directory.Build.props: net10.0, Nullable=enable, LangVersion=latest.
  • docker-compose.yml: postgres, redis (AOF), game-server с контентом.
  • docker compose up поднимает БД, Redis и API; /health отвечает.
  • CI: сборка solution + dotnet test на push.

Готово, когда: docker compose up + dotnet run дают живой /health.


2. Контент-пайплайн [4–6 дней]

  • Схема YAML: printingId, nameId, cardType, grade, power, shield, critical, triggerType, clan, rarity, isSentinel, isRestraint, name_en/ru, text_en/ru, imageKey.
  • Импорт с официального сайта → три YAML-файла (BT01, TD01, TD02). 94 карты, 124 принта.
  • Валидатор:
    • sum(deck) = 50, ≤4 по nameId, ≤4 Sentinel, ≤4 Heal.
    • Инвариант щита: grade 0 → 5000 только у Draw, иначе 10000.
    • Уникальность printingId, наличие nameId.
    • Ссылка SP-принта на существующий базовый номер.
  • Загрузчик: YAML → in-memory каталог, используется и сервером, и тестами.
  • Ключевые слова и шаблоны: content/keywords.yaml, content/translations/ru.yaml, команды translations build|check.
  • Сид в PostgreSQL: cards, printings, sets, card_legality (пустая).
  • Экспорт каталога для клиента (JSON + список Addressables-ключей).

Готово: dotnet run --project server/src/Vanguard.Cli -- validate → 94 карты, 124 принта, 0 ошибок.


3. Игровое ядро [20–30 дней] — критический путь

Пишется как чистая библиотека: без Orleans, без БД, без сети. Вход — состояние + интент, выход — новое состояние + события.

  • GameState: игроки, зоны, фазы, счётчики, rngState.
  • Зоны: Vanguard, Rear-Guard (3 колонки × 2 ряда), Guardian, Soul, Damage, Drop, Trigger, Deck, Hand.
  • Юнит: instanceId, printingId, состояние Stand/Rest.
  • Damage: список карт с faceUp, скрыт от обоих игроков.
  • Фазы: Mulligan → Stand → Draw → Ride → Main → Battle → End.
  • Старт: выбор grade 0 → выставление → маллиган.
  • Действия: Ride, Call, Attack, Guard, Drive, Damage, Stand/Rest, Retire, Draw.
  • Поддержка (Boost) как отдельная способность: по умолчанию у 0–1 ранга, передаёт силу карте впереди в той же колонне; BoostOverride позволяет выдать или снять её эффектом.
  • Идеальная защита (Sentinel): щит 0; оплата сбросом карты того же клана полностью отменяет попадание; без оплаты не даёт ничего.
  • Перехват: карта 2 ранга из переднего ряда выходит в круг защиты, сила атаки сравнивается с ней; InterceptOverride и InterceptFromBackRow позволяют выдать способность эффектом.
  • Перемещение Арьергарда в пределах колонки; если круг занят — карты меняются местами; у центрального заднего ряда пары нет.
  • Discard, Shuffle как отдельные интенты.
  • Число проверок принадлежит атакующей карте: 0–2 ранг — одна, 3+ — две; Арьергард по умолчанию без проверок; DriveChecksOverride меняет число (в том числе снимает двойную).
  • Результат боя: Hit / Guarded как событие.
  • Событийная шина: Triggered (OnRideOver, OnPlace, OnBoost, OnBoosted, OnDriveCheck, OnLeaveGuardian, OnPhaseStart, OnAttackDeclared, OnAttackHit), Continuous с пересчётом при запросе, Activated (повернуть себя + сброс карты).
  • Событие OnDamageTaken и триггеры на получение урона.
  • Check Timing: сбор сработавших, порядок (активный игрок первым, выбор игрока), пауза на опциональные способности.
  • Выбор целей: PendingChoice + ChooseCommand, используется Blaster Blade для уничтожения Арьергарда противника.
  • Стоимости: CB, SB, сброс карты, поворот себя, уничтожение себя; «карты из поля в душу» и «карта из руки в колоду» — частными хелперами.
  • Выбор цели игроком (PendingChoice / ChooseCommand); «до N» / «ровно N» — AskUnitMultiChoice.
  • Выбор карты из руки как стоимости эффекта (Morgana: сброс → +4000).
  • Модификаторы силы/крита с источником и сроком жизни (StatModifier); выдача и снятие способностей эффектами (GrantedAbility).
  • Выбор от min до max целей на поле (AskUnitMultiChoice) для «до N» и «ровно N».
  • Запреты с источником и сроком жизни (CardRestriction): не вставать, не атаковать, не поддерживать, не перехватывать, не быть выбранным; снятие Сдерживания эффектом.
  • OnAttackHit срабатывает и при попадании по Арьергарду.
  • Поиск в колоде без просмотра: 0 совпадений — уведомление и перемешивание, 1 — сразу в руку, несколько — выбор; разные арты считаются разными картами.
  • Поиск с призывом в круг Арьергарда (Alfred) и с райдом (API готов; Llew и Aleph ждут стоимость «карты из поля в душу»).
  • Просмотр верхней карты с решением «оставить на верху / положить в низ» (Cocoa).
  • [~] Слои модификаторов силы/крита: усиления от триггеров живут до конца хода, постоянных слоёв нет.
  • Валидация интентов: Rejected(reason) без изменения состояния.
  • Список легальных интентов на каждый момент (для UI и античита). — GameEngine.LegalCommands() покрывает фазы, выборы, триггеры и активируемые способности.
  • [~] Детерминированный RNG: один поток на партию, состояние в GameState.
  • Журнал событий + seed; сериализуемый реплей. — MatchRecord, MatchRecorder, CommandCodec, ReplayRunner, EventHasher; запись переживает JSON и воспроизводится бит-в-бит по хэшу событий.
  • [~] Защита от бесконечных циклов и дедлоков. — 50 партий случайного бота в тестах и 1000 через stress; лимит 5000 шагов на партию в стресс-тесте. Глобального лимита шагов внутри движка нет.
  • Инварианты партии: 50 карт в зонах каждого игрока, уникальные instanceId, урон ≤ 6.
  • Карты «в пути» между зонами (GameState.InTransit) — например, найденная карта ждёт выбора круга.

Сделано: каркас движка (Engine/), все фазы, бой, способности, 66/66 карт, LegalCommands, случайный бот (1000 партий через stress), реплей с хэшем событий. Всего 117 тестов.

Готово, когда: два бота случайными легальными интентами доигрывают 1000 партий без падений, инварианты (сумма карт, счётчики ≥ 0) держатся, реплей воспроизводится бит-в-бит. — выполнено: dotnet run --project server/src/Vanguard.Cli -- stress 1000 → 1000/1000 без ошибок; реплей проверяется тестом.


4. Карты [15–25 дней]

  • Хелперы: Draw, Retire, SearchCall, SearchHand, SearchRide, Stand, SoulCharge, SoulBlast, CounterBlast, FlipDamage, ToSoul, ToDeck, Discard, LookTop, ModifyPower, ModifyCrit, GainKeyword. — большая часть уже есть в GameEngine; не хватает общих «карты из поля в душу» и «из руки в колоду» с выбором любых карт.
  • [~] Шаблон карты: скрипт = класс CardScript, текст карты хранится в YAML (отдельного комментария с текстом нет).
  • Порядок: сначала простые триггеры, затем стоимости и активируемые способности.
  • По каждой карте: скрипт + тест «дано → сыграл → ожидаю» (для ключевых механик).
  • RU/EN тексты для всех 94.
  • Сила/щит для 14 карт, которых нет в BT01.
  • Дашборд покрытия: scripted / tested / total. — 66 из 66 карт с текстом закрыты: 59 скриптов + Tejas (флаг атаки заднего ряда) + 6 карт Sentinel/Heal, которые обрабатывает движок. Оставшихся карт нет.
  • Триггеры OnDriveCheck (Asura Kaiser, Brigitte, Tristan, Goku), OnBoostedHit (Dreadmaster, Aermo), OnGuard (Hagakure), OnRetireFromRC (Blightops), OnOpponentRetire (Joka, Rakshasa, Yaksha).
  • Способности из руки (Gancelot, Yaksha) и из сброса (Monster Frank).
  • Возврат карты с поля в колоду: после боя (Juggernaut, Brakki) и в конце хода (Lozenge, Battleraizer).
  • Триггер OnLeaveGuardian (Rock the Wall, Redshoe Milly).
  • Wyvern Strike, Tejas: атака заднего ряда только из своей колонки; обычные карты атакуют только передний ряд.

Готово, когда: 94/94 заскриптованы, 94/94 покрыты тестом, приёмка пройдена по чеклисту карты.


5. Сеть и матч [8–12 дней]

  • Спецификация протокола в docs/protocol.md: кадры, handshake, версия, ping/pong, reconnect.
  • Vanguard.Shared: DTO интентов и событий, MessagePack-резолвер.
  • IGameTicketService: одноразовый тикет на подключение (TTL 60 с).
  • Транспорт матча: WebSocket (ASP.NET Core); резерв — TCP с префиксом длины. Accept, handshake, auth по тикету, привязка к grain.
  • IMatchGrain: 1 матч = 1 grain, последовательная обработка. — реализовано in-process; Orleans-хостинг — следующий шаг, контракт отделён.
  • Проекция состояния под клиента: своя рука, свой trigger, открытые зоны; ничего о колоде, руке противника и закрытом уроне.
  • eventSeq + resync по запросу.
  • Реконнект: 0 / 2 / 10 / 30 с, затем экран «не удалось».
  • Политика отсутствия: 5 минут → авто-поражение.
  • Таймер хода: 90 с + банк 5 мин.
  • Снапшот матча в PostgreSQL + восстановление grain после рестарта. — заголовок + журнал интентов, реплей команд при старте.
  • Redis: одноразовые тикеты и очередь.
  • Idempotency по intentId.
  • HTTP-очередь и тикеты: docs/server-api.md.
  • E2E: два клиента играют полный матч по WebSocket; обрыв и возврат проверены.

Готово, когда: два клиента играют полный матч по сети; kill процесса клиента на любом шаге восстанавливается. — выполнено в Vanguard.Server.Tests; рестарт сервера восстанавливает матчи из PostgreSQL (проверено на ssh home).


6. Мета-API [10–14 дней]

  • Модель User, BCrypt, JWT (access 15 мин + refresh 30 дней, ротация).
  • Эндпоинты: register, login, refresh, logout, me.
  • Коллекция: GET /collection (сгруппировано по принтам), счётчики без верхней границы.
  • Колоды: CRUD, validate, лимит 30 на формат, шаринг по коду.
  • Магазин: GET /store, POST /buy/booster, POST /buy/trialdeck, POST /redeem/token.
  • Генератор бустера: слот 4 × C + 1 × R+ (R 70 / RR 22 / RRR 7 / SP 1).
  • Duplicate protection по printingId, замена внутри редкости, переброс редкости при её насыщении.
  • Экономика: журнал транзакций, idempotencyKey, баланс вычисляемый.
  • Пыль: разбор C 5 / R 10 / RR 25 / RRR 100, крафт ×4, SP не крафтится.
  • Онбординг: выбор триал-деки + 5 токенов, флаг «выбор сделан».
  • Матчмейкинг: 2 очереди, скрытый MMR, расширяющееся окно.
  • Результат матча: начисление Gold 30/15/20 и XP 150/75/100, запись в историю.
  • Rate limiting на все мутирующие эндпоинты.

Готово, когда: новый игрок через API проходит путь регистрация → триал-дека → токены → бустеры → колода → очередь.


7. Клиент: каркас и меню [15–20 дней]

  • Unity 6 LTS, URP, Addressables, Input System, Localization, DOTween.
  • Сцены: Boot, Meta, Battle.
  • Composition root + DI (VContainer), без синглтон-свалок.
  • ApiClient (HTTP/JSON), MetaState, обработка офлайна и таймаутов.
  • GameConnection (TCP + MessagePack), очередь событий, ClientGameState.
  • Экраны: логин/регистрация, главное меню, профиль (минимальный), магазин, коллекция, декбилдер, очередь, настройки.
  • Декбилдер: фильтры (клан, грейд, редкость, тип, арт), счётчики, валидация в реальном времени.
  • Коллекция: арты раздельно, «сколько в колодах», разбор в пыль с подтверждением.
  • Магазин: бустер (анимация открытия), триал-деки, обмен токенов.
  • Локализация RU/EN, переключение на лету.
  • Настройки: язык, звук, FPS, мьют.

Готово, когда: все мета-экраны работают против реального сервера, язык переключается без перезапуска.


8. Клиент: боевая сцена [20–30 дней]

  • Поле: Vanguard, 2 колонки × 2 ряда, Guardian, рука, Damage, Soul, Drop, Trigger.
  • Старт: выбор grade 0, маллиган.
  • Ввод: клик/тап → контекстное меню → подсветка валидных целей.
  • Отображение силы/крита с модификаторами и подсветкой баффов.
  • Очередь анимаций строго по событиям сервера: ride, call, attack, drive, damage, retire, boost, guard.
  • Скип/ускорение анимаций.
  • Экран противника: таймлайн и текстовый лог боя.
  • Таймер хода, индикатор реконнекта, кнопка сдачи.
  • Экран результата: победа/поражение, Gold/XP, кнопка «ещё матч».
  • Догон состояния после реконнекта (resync + переигрывание очереди).

Готово, когда: полный матч играется мышью без консоли и без рассинхрона с сервером.


9. Туториал [5–7 дней]

  • TutorialStep как ScriptableObject: текст, подсветка, ожидаемое действие.
  • Сценарий: Ride → Call → Boost → Attack → Drive Check → Damage Check → победа.
  • Работает поверх реального движка (локальный матч), не фейковая сцена.
  • Кнопка «Пропустить».
  • По завершении: выбор триал-деки + 5 токенов + флаг онбординга.

Готово, когда: новый аккаунт с нуля доходит до первой своей колоды без подсказок извне.


10. Лаунчер [5–7 дней]

  • WPF, MVVM, тёмная тема, окно 1000×640.
  • Логин через тот же API, сохранение refresh-токена в DPAPI.
  • version.json: clientVersion, minVersion, список файлов {path, hash, size, url}.
  • Проверка хешей локальных файлов, докачка только изменённого.
  • Прогресс, скорость, пауза, возобновление.
  • Кнопка «Играть» запускает клиент с game-тикетом.
  • Обработка «сервер недоступен», «нет места», «файл повреждён → перекачать».
  • Самообновление лаунчера.
  • Установщик (Inno Setup), установка без прав администратора.

Готово, когда: чистая машина → установка → логин → докачка → запуск игры.


11. Сборка и QA [7–10 дней]

  • Скрипт сборки: сервер (docker), клиент (Unity batchmode), лаунчер (dotnet publish), манифест, загрузка на CDN.
  • Тестовый стенд: БД с бесконечной валютой, аккаунты QA.
  • Прогон «золотых матчей»: 50 реплеев воспроизводятся после каждого изменения ядра.
  • E2E: регистрация → туториал → бустеры → колода → матч → награда.
  • Проверка скрытой информации: дамп трафика не содержит руки и колоды противника, закрытого урона и seed.
  • Проверка реконнекта на 20 разных шагах.
  • Профилирование клиента: 60 FPS на средней машине.
  • Инструкция запуска для нового разработчика (README).

12. Финальный коммит

  • .gitignore актуален, в индексе нет Library/, bin/, obj/, .env, билдов.
  • Нет секретов и ключей в истории (git log -p | Select-String "password|secret|key").
  • git add -A и один коммит:
    git commit -m "Base version: server, client, launcher"
    
  • Тег: git tag v0.1.0-base.
  • Дальше — обычная история, по одному смыслу на коммит.

Гейты (не двигаться дальше, пока не выполнено)

Гейт Критерий
G1. Ядро 1000 случайных партий без падений, реплей бит-в-бит
G2. Контент 94/94 карт заскриптованы и протестированы
G3. Сеть Полный матч по сети, реконнект на любом шаге
G4. Мета Регистрация → колода → ранкед матч → награда
G5. Клиент Полный цикл мышью, без консоли
G6. Доставка Чистая машина → установка → игра