29 KiB
Базовая версия — чеклист реализации
Практический план: что писать, в каком порядке и когда считать готовым.
Дизайн-решения и обоснования — в 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:
.gitea/workflows/ci.ymlпроходит наhome-runner; Actions включены для репозитория.
Готово, когда: 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,≤4Sentinel,≤4Heal.- Инвариант щита: 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-хостинг готов и включаетсяOrleans:Enabled.- Проекция состояния под клиента: своя рука, свой trigger, открытые зоны; ничего о колоде, руке противника и закрытом уроне.
eventSeq+resyncпо запросу.- Реконнект: 0 / 2 / 10 / 30 с, затем экран «не удалось».
- Политика отсутствия: 5 минут → авто-поражение.
- Таймер хода: 90 с + банк 5 мин.
- Снапшот матча в PostgreSQL + восстановление grain после рестарта. — заголовок + журнал интентов, реплей команд при старте.
- EF-миграции общей схемы (матчи + мета),
Migrateпри старте; InMemory —EnsureCreated. - 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, расширяющееся окно; с JWT по
deckId. - Результат матча: начисление Gold 30/15/20 и XP 150/75/100, запись в историю.
- История матчей и реплеи:
GET /matches,GET /replays/{id}, проекция под игрока. - Rate limiting на все мутирующие эндпоинты.
Готово, когда: новый игрок через API проходит путь регистрация → триал-дека → токены → бустеры → колода → очередь.
— выполнено: MetaGameE2ETests проходит регистрацию, онбординг, бустер, колоду,
очередь, полный матч, историю и реплей; проверено и на реальном PostgreSQL.
После MVP: косметика, квесты, баттлпасс, Draft, друзья/чат.
7. Клиент: каркас и меню [15–20 дней]
Первый блок начат: проект client/ создан из URP-шаблона Unity
6000.6.6f1. Уже работают логин/регистрация, refresh, /me, выход и
главный экран профиля. Подробности — client/README.md и docs/HANDOFF.md.
Визуальное направление зафиксировано: 3D-клиент на URP, отдельные
сцены-экраны Login / Lobby / DeckBuilder / Battle;
DeckBuilder совмещает коллекцию и редактор колоды. Boot — служебная
сцена. Очередь и онбординг — оверлеи Lobby. Текущий
единый Canvas — временный функциональный gray-box. Полное решение —
docs/client-visual-direction.md.
- Unity 6 LTS, URP, Addressables, Input System, Localization. DOTween — следующим шагом (пока UI без внешних твинов).
- 3D-сцены
Login,Lobby,DeckBuilder,BattleиSceneFlow;Boot— стартовая служебная сцена. 3D-окружение пока из примитивов, UI — code-generated uGUI. - Composition root + DI (VContainer), без синглтон-свалок.
ApiClient(HTTP/JSON),PlayerState, таймауты и разборApiError.GameConnection(WebSocket + MessagePack), snapshot/events,ClientGameStateв видеCurrentView+BattleView, очередь и тикет.- Экраны: логин/регистрация, главное меню, онбординг, коллекция, список колод; магазин и декбилдер подключены.
- Очередь: classic-ranked, опрос, отмена, тикет, переход в матч.
- Простой декбилдер: фильтры (клан, грейд, триггер) в оверлее, сброс, сортировка клан→ранг→имя, карточная сетка с количеством, узкий список колод справа, редактор с вертикальным списком карт, статистика по рангам и общему числу, добавление/удаление кликом.
- Декбилдер: drag&drop, live-валидация, редкость/тип/арт, пресеты.
- Базовая коллекция: список, балансы, разбор в пыль с подтверждением; арты и «сколько в колодах» — дальше.
- Магазин в Lobby: бустер за Gold/токен с показом 5 карт, вторая триал-дека; анимация открытия — дальше.
- История матчей и реплей: список результатов, события и кадры.
- Тренировка с ботом: серверный casual-матч, случайный легальный бот, без истории/MMR/наград.
- Локализация RU/EN, переключение на лету (пакет подключён).
- Настройки: язык, звук, FPS, мьют.
Готово, когда: все мета-экраны работают против реального сервера, язык переключается без перезапуска.
8. Клиент: боевая сцена [20–30 дней]
Визуальный каркас есть: доска, рука, карточные рамки/арт-заглушки
по клану, сила/крит, часы, все легальные интенты кнопками, сдача и
результат. Реальные арты подключаются через imageKey, event-фидбек уже
есть; полноценная очередь анимаций — следующая. Финальный визуал —
отдельная 3D-сцена Battle, а не текущий Canvas
(docs/client-visual-direction.md).
- Поле:
R V R/R R Rу каждого игрока, damage/soul слева, колода/дроп справа, зеркально у противника, зона защиты между полями. - Процедурные карты: цвет/градиент клана, рубашка, имя, ранг, триггер, сила/крит; REST/скрытая карта.
- Старт: маллиган (сервер пока даёт только «закончить маллиган»).
- Ввод: кнопки легальных интентов; клик по картам/зонам, подсветка целей, группировка действий и журнал боя.
- Boost: выбор карты заднего ряда как поддержки перед атакой.
- Event-фидбек: атака, guard, drive, урон, heal, retire, perfect guard.
- Отображение силы/крита из
MatchView; модификаторы уже посчитаны сервером. - Очередь event-анимаций и кнопка «Пропустить анимации».
- Полноценные move/attack/drive/damage-анимации карт и стола.
- Таймер хода, индикатор реконнекта, кнопка сдачи (базово).
- Экран результата: победа/поражение; Gold/XP и «ещё матч» — дальше.
- Догон состояния по
resync/snapshot после реконнекта (без переигрывания анимаций). - Экран противника: таймлайн и текстовый лог боя.
Готово, когда: полный матч играется мышью без консоли и без рассинхрона с сервером.
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. Доставка | Чистая машина → установка → игра |