17 KiB
17 KiB
Архитектура Vanguard ReMaster
[C] Ниже — фактическое устройство репозитория. Если задача требует изменить границу систем, сначала проверь существующий код и спроси пользователя; не вводи параллельную архитектуру.
1. Технологии и версии
- Unity:
6000.6.6f1(Unity 6), URP17.6.0, Input System1.20.0, uGUI2.6.0, Addressables4.1.1, Localization1.5.13, Newtonsoft.Json3.2.2, VContainer1.19.0. - C#/сервер: .NET SDK
10.0.100,TargetFramework net10.0,LangVersion latest,Nullable enable, implicit usings. Фактически это современный C# (C# 14 toolchain); не используй preview-фичи без проверки. - Сеть: WebSocket
/game, бинарные MessagePack-кадры; контракты —Vanguard.Shared(netstandard2.1для Unity +net10.0для сервера). - Сервер: ASP.NET Core, EF Core/PostgreSQL, Redis, BCrypt, Orleans
(
Microsoft.Orleans.Server 10.4.0), DTO/проекции. - Клиент: сцены
Boot,Login,Lobby,DeckBuilder,Battle; 3D-поле + uGUI HUD. DOTween/UniTask не установлены; анимации —AnimationConfig,Tick, корутинные/сервисные последовательности.
2. Границы решения
content/ → Vanguard.Content → Vanguard.Rules ← Vanguard.Server
↑ ↓
Vanguard.Shared ← Unity client
Vanguard.Rules— чистая библиотека правил: карты, колоды, состояние,GameEngine, события, реплеи. Не добавлять сеть, БД, Orleans, Unity,File/Directoryили UI.Vanguard.Content— загрузка YAML, каталог, валидация контента, токенизация текста/переводы. Не содержит правил хода.Vanguard.Shared— DTO, MessagePack-контракты, union-типы, проекции (MatchProjection), маппингGameCommand ↔ IntentPayload.netstandard2.1не должен ссылаться наVanguard.Rules; проекции собираются вnet10.0.Vanguard.Server— ASP.NET, auth/meta, matchmaking, тикеты, WebSocket,MatchGrain, хранение и Orleans-adapter. Зависит от Rules/Shared/Content; обратной зависимости быть не должно.- Unity client — только представление и ввод; не является источником истины и не повторяет правила сервера, кроме локальных UX-проверок.
3. Серверная игровая логика
3.1. GameEngine
- Единственный владелец изменения
GameStateв матче. - Принимает
GameCommand, проверяет фазу/шаг/зону/стоимость/цели, порождаетGameEventи меняет состояние. GameState: игроки, зоны, фазы,Step,BattleState, pending trigger/ ability/choice/order/queue, RNG,InTransit, winner.Phase:Mulligan, Stand, Draw, Ride, Main, Battle, End.Step:None, Guard, Drive, Trigger, Ability, Choice.CardInstance:Id,PrintingId,Rested,FaceUp, модификаторы, granted abilities, restrictions, overrides.CardDefinition— логическая карта поnameId;CardPrinting— принт/арт.EffectivePower/EffectiveCriticalпересчитывают CONT-бонусы и модификаторы.LegalCommands()— список допустимых команд для текущего игрока; UI использует проекцию, но сервер всё равно перепроверяет.
3.2. События, способности, порядок
AbilityTrigger:OnRideOver,OnPlace,OnBoost,OnPhaseStart,OnAttackDeclared,OnAttackHit,OnBoosted,OnBoostedHit,OnDriveCheck,OnGuard,OnRetireFromRC,OnOpponentRetire,OnLeaveGuardian,OnDamageTaken.AbilityDefinition(AUTO):Trigger,Condition,Optional,CanPay,Pay,Resolve.ActivatedAbility(ACT):CanActivate,RestsSelf,RetiresSelf,RequiresDiscard,FromHand,FromDropZone,Resolve.ContinuousAbility(CONT):Applies,PowerBonus,CriticalBonus; исключения —CardScript.CanBeBoostedиRestrictionType.TriggerFromCard/TriggerAllсобирают срабатывания;RaiseBatchлибо сразу предлагает одно, либо формируетPendingOrder._resolvingDepth+PendingQueue: новые эффекты во время эффекта не исполняются рекурсивно, а разрешаются после текущего.PendingChoice(AskUnitChoice,AskUnitMultiChoice,AskHandCardChoice,AskEmptyRearGuardChoice,AskDamageChoice) — единый механизм выбора; клиент не должен сам решать, что выбрано.
3.3. Матч, сеть, хранение
MatchGrain— один матч, все входы черезSemaphoreSlim; хранитGameEngine, журнал идемпотентностиintentId, таймеры, места.GameWebSocketEndpointпринимает соединение;MatchConnectionRegistryсвязывает игрока с матчем;MatchTickerдвигает часы/таймауты.MatchActorGrain— Orleans-обёртка: активация/восстановление, вызовыMatchGrain; доменная логика остаётся вMatchGrain.IMatchStore/EfMatchStore— snapshot/header в PostgreSQL;MatchRecoveryServiceвосстанавливает; Redis используется для тикетов/ очереди/кластера, не как единственная истина матча.- Интент:
ClientIntent { intentId, basedOnSeq, intent }→MatchGrain.SubmitAsync→ проверка идемпотентности/очереди →IntentMapping.ToCommand→GameEngine.Execute→ события →ServerIntentResult+ServerEvents+ проекция. MatchProjection.ForViewerскрывает: руку противника, колоду, закрытый урон, seed/RNG, чужие pending-выборы; показывает свои руку/урон/зоны.eventSeqобщий и монотонный; скрытые события занимают номер, поэтому пропуски вServerEventsнормальны. Клиент обновляетLastSeqпоtoSeq, а не по последнему видимому событию.
3.4. Правила потоков
- ASP.NET/HTTP — мета, auth, коллекция, колоды, магазин, история, тикеты.
- WebSocket — только матч.
- Все ошибки наружу —
ServerIntentResult/ServerError; исключения не должны ломать матч. - Reconnect:
ClientHello/resumeFromSeq; при невозможности — полный snapshot. Матч не пересоздаётся из-за обрыва. - Сервер не доверяет
slot,printingId,damageIndex, целям; проверяет их по своему состоянию.
4. Клиент Unity
4.1. Composition и сцены
VanguardApp— persistent composition root:VContainer,AppConfig,TokenStore,PlayerState,ApiClient,AuthService,MetaService,SceneFlow.SceneFlow— единая точка переходов;Bootслужебная,Login,Lobby,DeckBuilder,Battle— отдельные сцены.SceneEnvironmentстроит 3D-фон;SceneControllerBase— доступ к контейнеру/Canvas;BattleSceneControllerсоздаётBattleView.- UI в основном code-generated uGUI (
UiKit), статические боевые overlay prefab'ы лежат вResources/Overlays. - Reference-разрешение интерфейса — 1920×1080,
CanvasScaler matchWidthOrHeight = 0.5; standalone открывается в 1920×1080 fullscreen. - Сервисы не зависят от конкретной сцены.
4.2. Сеть и состояние
GameConnection: connect, hello, send intents, read loop, ping, pending intents,LastSeq,CurrentView, reconnect.- UI не открывает сокет напрямую;
BattleViewчитаетMatchViewи отправляетIntentPayload. - После snapshot/hello поле перерисовывается из snapshot, а не из догадок.
- UI узнаёт об изменениях через события
GameConnection(SnapshotReceived,EventsReceived,ClockReceived) и перерисовкуBattleView.Render; не строить отдельную клиентскую модель правил. - Клиент не меняет авторитарное состояние; локальные правила
(
BoardPlacementRules) только для подсветки/драга. - Все сетевые операции —
async/CancellationToken; не блокировать Unity-поток. TokenStoreсейчас хранит dev-токены вPlayerPrefs; для релиза нужен защищённый store. Авторитарное состояние матча клиент не сохраняет — источник истины и persistence на сервере.
4.3. Боевой вид
BattleView— HUD, оверлеи, меню карт, выборы, стоимость, guard, trigger, feedback;Tick()вызывается из сцены.GameBoard3D— 3D-поле, слоты, рука, стопки, пулCard3D, drag/drop, диффы снапшота,CardMoveAnimator,CameraDirector,ArrowDirector.Card3D— карта: арт/рубашка/торец,SetPose, Rest/флип, arc move, подсветка, stats; пул не уничтожает объект каждый кадр. Rest — +90° по часовой вокруг Y; карта урона лежит на боку против часовой; Trigger Zone своей стороны +90°, противника −90° относительно базы; флип — вокруг X. Эффект кладёт карту в Trigger до разрешения, затем в руку/урон.CardMoveAnimator— очередь дуговых перемещений, stagger,InputLockcard-move, таймаут/CancelCard; не оставлять лок навсегда.CameraDirector— снимок pose, zoom/return/restore;EnsureCameraне перезаписывает камеру, пока режиссёр активен.TriggerCheckSequence— zoom → reveal → return, цепочка, InputLocktrigger-sequence, возврат перед выбором цели.ArrowDirector/AttackArrowView— attack/boost стрелки, grow/dash, якоря у края карт.AnimationConfig(ScriptableObject) — длительности, скорости, camera, arrow, stagger;PlaymatLayoutConfig— геометрия/камера/рука/зоны.InputLockService— счётчик причин;ReleaseAllтолько по состоянию UI/ сбою, не для обхода законной блокировки.
4.4. C# и Unity-соглашения (фактические)
- Сервер: file-scoped namespaces,
Nullable enable, implicit usings, проектыnet10.0; доменные типы —sealed class/sealed record,requiredдля обязательных полей,initдля неизменяемых данных. Исключения — только для инфраструктурных ошибок; игровые отказы —CommandResult.Reject. - Клиент: block-scoped namespaces;
MonoBehaviourдля сцен/компонентов;ScriptableObjectдляAnimationConfig,PlaymatLayoutConfig,ArrowConfig; приватные поля_camelCase, публичные свойства/методыPascalCase, сериализуемые поля —lowerCamelCase. - События: сервер —
GameEvent/TriggerFromCard; клиент — C#event Action<T>для UI/board. Не смешивай игровые события с UnityEvent, если нужна чистая логика. - Асинхронность: сеть/сервер —
async/Task/CancellationToken; клиентский UI —Tick()из сцены и сервисные таймеры. DOTween/UniTask не установлены; не добавляй их без решения пользователя. - Производительность: пул
Card3D, общие материалы/шейдеры, неInstantiate/Destroyкаждый кадр; не создавайMaterial/Texture2DвUpdate; кешируйGetComponent. - Сцены/префабы/метаданные: сцены ссылаются на скрипты по GUID; при
копировании сцен/контроллеров копируй
.meta; не открывай второй Unity с тем же проектом; batch — копия безLibrary. - Ресурсы: статический UI/карты —
Resources/Resources/Overlays; Addressables-пакет есть, но арт пока грузится черезCardImageServiceизResources/CardArt. Не обещай Addressables-миграцию без задачи. - Логирование/ошибки: сервер —
ILogger; клиент —Debug.Log/Debug.LogException; ошибки пользователю — статус/оверлей, не «сырой» stack trace. Не логируй токены, seed и скрытые карты.
5. Данные и ресурсы
- Карты:
content/sets/*.yaml(BT01/TD01/TD02),content/translations/ru.yaml,content/keywords.yaml,content/trial-decks.yaml,content/pack_odds.yaml. CardDefinition— логика/статы;CardPrinting— конкретный арт.- 94 карты / 124 принта; 10 кланов; SP — отдельные принты.
- Арт в клиенте сейчас через
Resources/CardArt/{imageKey}иCardImageService; Addressables-пакет установлен, но активная загрузка арта ещё не переведена на него. - Данные карт не дублировать в ScriptableObject: SO — визуал/конфиг, статы и тексты — из каталога/сервера.
- Тексты карт хранятся шаблонами с токенами (
{auto},{cb:2},{vcrc},{clan:kagero}); переводы не должны ломать токены. Ключевые слова/иконки —content/keywords.yaml, правила —docs/translation-rules.md.
5.1. Как добавлять функционал
- Сначала правило/механика в
Vanguard.Rules+ тест; не начинать с UI. - Если меняется сетевой контракт —
Vanguard.Shared, проекция,IntentMapping, пересборка DLL для Unity; версия/совместимость. - Сервер:
GameEngine/MatchGrain/таймеры; не доверять клиенту. - Клиент:
GameConnection/BattleView/GameBoard3D; только представление. - Проверки: rules/server tests, EditMode/smoke,
validate, translations. - Обновить Skill, если решение долговременное; не дублировать в AGENTS.
6. Тесты и проверки
server/tests/Vanguard.Rules.Tests—GameEngine, карты, колоды, протокол, каталожные инварианты.server/tests/Vanguard.Server.Tests— auth/meta, E2E, matchmaking, persistence, Orleans, practice bot, WebSocket.client/Assets/Vanguard/Tests/Editor— раскладка, 3D-поле, оверлеи, локализация, smoke-инварианты.SceneSmoke.Run/RunPlaymat— загрузка сцен/песочницы;StandaloneBuild.BuildWindowsCli— Windows-сборка.- Серверные тесты/validate/translations — только
ssh home/Docker.