Add project skill for architecture rules and abilities
CI / test (push) Has been cancelled
Details
CI / test (push) Has been cancelled
Details
This commit is contained in:
parent
228bc4ce8c
commit
923b82be85
|
|
@ -0,0 +1,257 @@
|
||||||
|
---
|
||||||
|
name: Vanguard ReMaster
|
||||||
|
description: Use for any work in the Vanguard ReMaster repository: server rules and card abilities, battle mechanics, WebSocket/MessagePack protocol and networking, Unity client UI/3D board, tests, deployment, or project structure questions.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Vanguard ReMaster
|
||||||
|
|
||||||
|
Рабочие правила для этого репозитория. Сначала прочитать
|
||||||
|
`docs/SESSION-STATE.md`, затем `docs/HANDOFF.md` и
|
||||||
|
`docs/client-visual-direction.md`.
|
||||||
|
|
||||||
|
## 1. Структура проекта
|
||||||
|
|
||||||
|
```text
|
||||||
|
content/
|
||||||
|
sets/*.yaml карты, принты, тексты, флаги механик
|
||||||
|
translations/ru.yaml русские тексты и токены
|
||||||
|
keywords.yaml ключевые слова
|
||||||
|
docs/ протокол, правила, состояние, аудит способностей
|
||||||
|
server/
|
||||||
|
src/Vanguard.Shared/ DTO, MessagePack-контракты, union-типы, проекции
|
||||||
|
src/Vanguard.Content/ загрузка YAML, каталог карт, валидация контента
|
||||||
|
src/Vanguard.Rules/ чистое игровое ядро: состояние, команды, события
|
||||||
|
src/Vanguard.Server/ ASP.NET, auth, meta, matchmaking, Orleans, WebSocket
|
||||||
|
src/Vanguard.Cli/ validate, stats, translations, stress, deck
|
||||||
|
tests/Vanguard.Rules.Tests/ тесты правил и карт
|
||||||
|
tests/Vanguard.Server.Tests/тесты сервера и инфраструктуры
|
||||||
|
client/Assets/Vanguard/
|
||||||
|
Runtime/Api/ HTTP-клиент и DTO мета-API
|
||||||
|
Runtime/Auth/ JWT, refresh, TokenStore
|
||||||
|
Runtime/State/ PlayerState и клиентское состояние
|
||||||
|
Runtime/Meta/ каталог карт, коллекция, колоды
|
||||||
|
Runtime/Battle/ GameConnection, InputLockService, анимации
|
||||||
|
Runtime/Board3D/ 3D-поле, карты, слоты, зоны, drag&drop
|
||||||
|
Runtime/UI/ uGUI-экраны и оверлеи
|
||||||
|
Runtime/App/ сцены, SceneFlow, bootstrap
|
||||||
|
Editor/ сборка сцен, Windows standalone, smoke-проверки
|
||||||
|
Tests/Editor/ Unity EditMode тесты
|
||||||
|
```
|
||||||
|
|
||||||
|
Ключевые границы:
|
||||||
|
|
||||||
|
- `Vanguard.Rules` — чистая библиотека. Не добавлять в неё сеть, БД,
|
||||||
|
Orleans, Unity или файловый I/O.
|
||||||
|
- `Vanguard.Shared` — контракты. `netstandard2.1`-сборка используется Unity,
|
||||||
|
поэтому там только DTO/сериализация без ссылки на `Vanguard.Rules`.
|
||||||
|
- `Vanguard.Server` зависит от `Rules`, `Shared`, `Content`; обратной
|
||||||
|
зависимости быть не должно.
|
||||||
|
- Клиент не является источником истины: правила, скрытая информация и
|
||||||
|
случайность остаются на сервере.
|
||||||
|
|
||||||
|
## 2. Сетевой код
|
||||||
|
|
||||||
|
### Протокол
|
||||||
|
|
||||||
|
- Транспорт матча — WebSocket `/game`, бинарные MessagePack-фреймы.
|
||||||
|
- Контракты и union-номера — только в `Vanguard.Shared/Protocol`.
|
||||||
|
Не дублировать DTO в клиенте или сервере.
|
||||||
|
- Изменение union-номера или `[Key(n)]` — ломающее изменение протокола.
|
||||||
|
Добавлять новые поля/типы, не переиспользовать старые номера.
|
||||||
|
- `ProtocolConstants.Version` и `MaxFrameBytes` — общие ограничения.
|
||||||
|
Сервер обязан отклонять неизвестную версию и слишком большой фрейм.
|
||||||
|
- `ClientIntent` содержит `intentId` (GUID) и `basedOnSeq`; повтор с тем же
|
||||||
|
`intentId` должен быть идемпотентным. Результат — `ServerIntentResult`.
|
||||||
|
- `eventSeq` монотонно растёт. Клиент не применяет события старше
|
||||||
|
`LastSeq`; для пропуска — `ClientResync` и полный snapshot.
|
||||||
|
- Сервер присылает `ServerHello`, `ServerSnapshot`, `ServerEvents`,
|
||||||
|
`ServerIntentResult`, `ServerClock`, ping/pong и `ServerError`.
|
||||||
|
- Скрытые данные (колода, рука противника, закрытый урон, seed) не уходят
|
||||||
|
клиенту. Проверять каждую новую view/проекцию.
|
||||||
|
|
||||||
|
### Сервер
|
||||||
|
|
||||||
|
- HTTP-концы и realtime разделены: REST — мета/аутентификация/матчмейкинг,
|
||||||
|
WebSocket — только матч.
|
||||||
|
- `GameWebSocketEndpoint` принимает соединение; `MatchConnectionRegistry`
|
||||||
|
связывает игрока с матчем; `MatchGrain` хранит авторитарное состояние и
|
||||||
|
обрабатывает интенты.
|
||||||
|
- Команда проходит: intent → проверка `basedOnSeq`/идемпотентности →
|
||||||
|
`GameEngine.Execute` → события → snapshot/clock.
|
||||||
|
- Нельзя доверять клиентским `slot`, `printingId`, `damageIndex` и спискам
|
||||||
|
целей: сервер заново проверяет их по своему состоянию.
|
||||||
|
- Ошибки — через `ServerIntentResult`/`ServerError`, не через исключения
|
||||||
|
наружу. Внутренние исключения логировать, наружу отдавать код/причину.
|
||||||
|
- Reconnect/resume: новый сокет с `resumeFromSeq`; при невозможности —
|
||||||
|
полный snapshot. Не пересоздавать матч из-за обрыва.
|
||||||
|
|
||||||
|
### Клиент
|
||||||
|
|
||||||
|
- Вся сеть — в `GameConnection`: connect, send, receive loop, ping,
|
||||||
|
pending intents, seq, reconnect. UI не работает с сокетом напрямую.
|
||||||
|
- `BattleView`/`MatchGrain`-клиентская часть только читает `MatchView` и
|
||||||
|
отправляет `IntentPayload`; не менять локально авторитарное состояние.
|
||||||
|
- Пока `_sending`/`_snapshotPending`/reconnect overlay активны, ввод
|
||||||
|
блокируется; stale input locks освобождаются по состоянию UI.
|
||||||
|
- После `SnapshotReceived`/`ServerHello` перерисовать поле из snapshot, а не
|
||||||
|
накапливать догадки по событиям.
|
||||||
|
- Все сетевые операции — async + `CancellationToken`; не блокировать
|
||||||
|
Unity-поток.
|
||||||
|
|
||||||
|
## 3. Способности карт
|
||||||
|
|
||||||
|
Источник текста — `content/sets/*.yaml` (`textEn`). Каждая текстовая
|
||||||
|
способность должна иметь ровно одну реализацию: `CardScript`, общий движок
|
||||||
|
или флаг данных. Текст без реализации — баг. Полный аудит:
|
||||||
|
`docs/ability-implementation.md`.
|
||||||
|
|
||||||
|
### AUTO
|
||||||
|
|
||||||
|
`AbilityDefinition`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
new AbilityDefinition
|
||||||
|
{
|
||||||
|
Trigger = AbilityTrigger.OnPlace,
|
||||||
|
Condition = ctx => ...,
|
||||||
|
Optional = true,
|
||||||
|
CanPay = ctx => ...,
|
||||||
|
Pay = ctx => ...,
|
||||||
|
Resolve = ctx => ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `Trigger` — только реальное окно события: `OnPlace`, `OnRideOver`,
|
||||||
|
`OnAttackDeclared`, `OnAttackHit`, `OnBoost`, `OnBoostedHit`,
|
||||||
|
`OnDriveCheck`, `OnGuard`, `OnRetireFromRC`, `OnOpponentRetire`,
|
||||||
|
`OnLeaveGuardian`, `OnPhaseStart`, `OnDamageTaken`.
|
||||||
|
- Проверять зону источника: `IsVanguardOf`, `IsRearGuardOf`, `IsInHand`.
|
||||||
|
`OnPlace` не должен срабатывать от VC, если текст требует RC.
|
||||||
|
- `Optional` + `CanPay`/`Pay` — для «you may pay the cost». Без оплаты
|
||||||
|
эффект не выполняется.
|
||||||
|
- Несколько целей/карт — только `AskUnitChoice`, `AskUnitMultiChoice`,
|
||||||
|
`AskHandCardChoice`, `AskEmptyRearGuardChoice`.
|
||||||
|
- AUTO не должен напрямую менять UI; сервер меняет состояние и добавляет
|
||||||
|
события. Клиент показывает оверлей подтверждения для optional.
|
||||||
|
|
||||||
|
### ACT
|
||||||
|
|
||||||
|
`ActivatedAbility`:
|
||||||
|
|
||||||
|
- `CanActivate` — только проверка условий; оплата и эффект в `Resolve`
|
||||||
|
(или `Pay` для AUTO).
|
||||||
|
- Флаги: `RestsSelf`, `RetiresSelf`, `RequiresDiscard`, `FromHand`,
|
||||||
|
`FromDropZone`.
|
||||||
|
- ACT из руки/дропа попадает в `LegalCommands` только в разрешённой фазе;
|
||||||
|
клиент показывает действие у карты, не отдельным глобальным списком.
|
||||||
|
- Стоимость (discard, Counter Blast, field units) выбирается по объектам
|
||||||
|
в руке/уроне/поле; подтверждение — кнопка панели «Действия».
|
||||||
|
- `RetiresSelf`/`RestsSelf` не должны активироваться из руки/дропа, если
|
||||||
|
текст этого не разрешает.
|
||||||
|
|
||||||
|
### CONT
|
||||||
|
|
||||||
|
- Сила/критика — `ContinuousAbility` с `Applies`, `PowerBonus`,
|
||||||
|
`CriticalBonus`.
|
||||||
|
- Ограничения, которые не выражаются бонусом, — отдельный движок:
|
||||||
|
`RestrictionType` (`CannotAttack`, `CannotBoost`, `CannotStand`,
|
||||||
|
`CannotIntercept`, `CannotBeChosen`) или `CardScript.CanBeBoosted`.
|
||||||
|
- CONT пересчитывается из состояния каждый раз; не хранить вычисленную
|
||||||
|
силу как истину.
|
||||||
|
|
||||||
|
### Общие механики
|
||||||
|
|
||||||
|
- **Sentinel** — данные `sentinel`; общий Guard + сброс карты того же клана.
|
||||||
|
Отдельный `CardScript` не нужен.
|
||||||
|
- **Heal/Trigger** — данные `trigger`; общий движок drive/damage check.
|
||||||
|
- **Tejas** — `snipeBackRow` в данных + общий движок атаки заднего ряда.
|
||||||
|
- **Restraint** — флаг `restraint` + `IsRestraint`; ACT снимает через
|
||||||
|
`RestraintOverride`.
|
||||||
|
- **Boost/Intercept** — `hasBoost`/`hasIntercept` и общий движок.
|
||||||
|
- **Perfect Guard** — Sentinel + optional discard cost; nullify до конца боя.
|
||||||
|
|
||||||
|
### Клиентские правила способностей
|
||||||
|
|
||||||
|
- ACT/AUTO выбираются кликом по карте/цели, а не списком кнопок.
|
||||||
|
- OPTIONAL AUTO — модальный оверлей «Активировать»/«Пропустить».
|
||||||
|
- Сброс для стоимости — клик по карте в руке.
|
||||||
|
- Counter Blast — клик по карте урона; доступные/выбранные подсвечиваются.
|
||||||
|
- Подтверждение оплаты — в панели «Действия»; отмена — правый клик.
|
||||||
|
- Карта в руке с ACT: клик открывает меню действий с валидными пунктами
|
||||||
|
(Призвать/Сменить/Активировать/Подробнее). Смена/призыв также работают
|
||||||
|
драгом.
|
||||||
|
|
||||||
|
## 4. Новые карты и изменения правил
|
||||||
|
|
||||||
|
Порядок:
|
||||||
|
|
||||||
|
1. Добавить/исправить карту в `content/sets/*.yaml`; текст — `textEn`.
|
||||||
|
2. Найти все AUTO/ACT/CONT/ключевые механики в тексте.
|
||||||
|
3. Реализовать в `CardScripts` или общем движке; не оставлять дублей.
|
||||||
|
4. Добавить хотя бы один тест на триггер/активацию/условие.
|
||||||
|
5. Проверить `CardScripts.Default` целиком: нет ли карты с текстом без
|
||||||
|
скрипта.
|
||||||
|
6. Прогнать серверные тесты, `validate`, `translations check`.
|
||||||
|
7. Для клиентских изменений — Unity EditMode, `SCENE_SMOKE`,
|
||||||
|
`PLAYMAT_SMOKE`, пересобрать standalone.
|
||||||
|
|
||||||
|
## 5. Игровые механики
|
||||||
|
|
||||||
|
- **Ride**: hand → VC; ранг не выше текущего +1. Старый Vanguard уходит в
|
||||||
|
soul. Первый ход: без Battle.
|
||||||
|
- **Call**: hand → RC. Занятый RC разрешён: старый юнит уходит в Drop.
|
||||||
|
Правило действует также для call из soul/deck и выбора круга.
|
||||||
|
- **Move**: RC ↔ RC только в своей колонке; занятые круги меняются местами.
|
||||||
|
- **Attack**: только передний ряд; цель — Vanguard или передний RC
|
||||||
|
противника. Tejas может бить задний ряд своей колонки.
|
||||||
|
- **Boost**: стоящий юнит заднего ряда → передний юнит/VC той же колонки;
|
||||||
|
сила бустера добавляется до конца боя.
|
||||||
|
- **Guard**: карты из руки, ранг не выше ранга своего Vanguard. Выбор
|
||||||
|
карт/перехватчиков — по объектам; одна кнопка «Не защищаться»/
|
||||||
|
«Защищаться». Щит учитывается сразу; после подтверждения — Guardian.
|
||||||
|
- **Intercept**: подходящий RC; атака переносится на него.
|
||||||
|
- **Drive/Damage Check**: только по правилам; триггеры/Heal — общий движок.
|
||||||
|
- **Counter Blast/Soul Blast/Soul Charge/Counter Charge** — общие методы
|
||||||
|
`GameEngine`, не дублировать в скриптах.
|
||||||
|
- **Победа**: 6 damage; после 4+ heal уходит в drop. Проверять инварианты
|
||||||
|
зон и уникальность `instanceId`.
|
||||||
|
|
||||||
|
## 6. Клиентские взаимодействия
|
||||||
|
|
||||||
|
- Выбор — клик/драг по карте, кругу, зоне или полю. Кнопки — только
|
||||||
|
глобальные действия и подтверждения.
|
||||||
|
- Drag-жесты:
|
||||||
|
- передний ряд → карта противника: attack;
|
||||||
|
- задний ряд → передний ряд/VC: boost;
|
||||||
|
- рука → RC: call;
|
||||||
|
- рука → VC: ride.
|
||||||
|
- Панель «Действия» не показывает полный список смен; действия берутся из
|
||||||
|
меню выбранной карты и валидных интентов.
|
||||||
|
- Информационные оверлеи не перехватывают клики и не дублируют выбор
|
||||||
|
кнопками.
|
||||||
|
|
||||||
|
## 7. Проверки и окружение
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# сервер — только на ssh home / Docker, не локальным dotnet
|
||||||
|
ssh home "docker run --rm -v ~/VanguardReMaster-test:/src -w /src \
|
||||||
|
mcr.microsoft.com/dotnet/sdk:10.0 dotnet test server/Vanguard.slnx"
|
||||||
|
|
||||||
|
# validate / translations
|
||||||
|
ssh home "docker run --rm ... dotnet run --project server/src/Vanguard.Cli -- validate"
|
||||||
|
ssh home "docker run --rm ... dotnet run --project server/src/Vanguard.Cli -- translations check"
|
||||||
|
```
|
||||||
|
|
||||||
|
Клиент: Unity **6000.6.6f1**, URP. Не открывать второй Unity с тем же
|
||||||
|
проектом; batch-проверки — в копии без `Library`. Тесты:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
& 'C:\Program Files\Unity\Hub\Editor\6000.6.6f1\Editor\Unity.exe' `
|
||||||
|
-batchmode -nographics -projectPath <copy> `
|
||||||
|
-runTests -testPlatform EditMode -testResults <copy>\TestResults.xml
|
||||||
|
```
|
||||||
|
|
||||||
|
Smoke: `Vanguard.Editor.SceneSmoke.Run` и `RunPlaymat`. Windows build:
|
||||||
|
`Vanguard.Editor.StandaloneBuild.BuildWindowsCli`.
|
||||||
|
|
||||||
|
После блока: тесты, `validate`, `translations check`, коммит и push.
|
||||||
Loading…
Reference in New Issue