19 KiB
MVP World, Authority And Runtime NavMesh
Status
Этот документ считается каноническим для решений по детерминированному миру, authority model, модульным границам и runtime NavMesh, пока его явно не заменят более новым архитектурным решением.
Purpose
Зафиксировать долгосрочные решения для MVP, чтобы downstream-задачи по FishNet, worldgen, DI, AI и persistence не уехали в разные стороны.
Scope
- deterministic voxel world generation
- authority model для session gameplay
- модульные границы feature-подсистем
- runtime NavMesh в procedural world
- риски WebGL-host режима
Fixed Decisions
1. Базовый мир генерируется детерминированно и локально на каждом peer
Решение:
- базовая геометрия мира не стримится от хоста по сети
- каждый peer генерирует чанк локально из одинакового
seed, одинаковогоVoxelWorldConfigи одинаковой версии world rules
Почему выбрано:
- для WebGL и peer-host модели это минимизирует сетевой трафик
- убирает постоянную сетевую репликацию геометрии чанков
- снимает с хоста роль единственной точки генерации базового мира
- хорошо сочетается с уже существующим
VoxelWorldGenerator, который строит чанк из deterministic inputs
Почему не выбран host-generated world streaming:
- хост получал бы лишнюю CPU-нагрузку на генерацию и лишнюю сетевую нагрузку на раздачу чанков
- late join и догрузка дальних областей становились бы тяжелее по сети
- это хуже укладывается в бюджет WebGL-host
Последствия:
seed, world config и их версия становятся частью session handshake- любое расхождение по config/version между peers недопустимо и должно считаться protocol drift
2. Host остается authoritative для NPC, AI и другого gameplay state
Решение:
- NPC симулируются на хосте
- pathfinding NPC, агро, боевые решения и каноническое положение NPC принадлежат хосту
- клиенты получают состояние NPC по сети и могут делать только визуальное сглаживание
Почему выбрано:
- NPC влияют на бой, урон, столкновения и progression, значит их нельзя отдавать в authority случайному клиенту
- это радикально снижает риск читов и эксплуатационных багов
- упрощает late join, reconnect и дебаг сетевой симуляции
Почему не выбран client-owned NPC:
- ownership у первого встретившего игрока нестабилен при совместной игре
- миграция owner во время боя ломает воспроизводимость path state, aggro state и hit timing
- возрастает риск desync и эксплойтов
- резко усложняется отладка и сопровождение
Последствия:
client-authorityдопустим только для ввода игрока и только при отдельной валидации на сервере- для NPC authority migration в MVP не используется
3. У чанков нет owner и нет chunk ownership migration
Решение:
- чанк не закрепляется за конкретным игроком как за владельцем
- базовый чанк является общей детерминированной сущностью мира, а не network-owned объектом
Почему выбрано:
- при deterministic world generation ownership чанка не дает полезного выигрыша
- chunk ownership добавляет coordination cost, миграцию ответственности и новые классы сетевых гонок без пользы для MVP
- это плохо совместимо с late join и с будущими world deltas
Почему не выбран owner-per-chunk:
- первый увидевший чанк игрок не является надежным authority source
- потребуется сложная логика передачи владения при сближении игроков и при disconnect
- любые расхождения по владельцу чанка приводят к hidden state drift
Последствия:
- изменения чанка в будущем пойдут не через owner migration, а через authoritative world deltas от хоста
4. Runtime NavMesh строится локально на каждом peer по фактической локальной геометрии мира
Решение:
- NavMesh не реплицируется по сети как data blob
- каждый peer строит NavMesh у себя локально из актуальной локальной геометрии мира
- NavMesh всегда считается производным кэшем от world state, а не каноническим состоянием сессии
Почему выбрано:
- NavMesh data тяжелая и плохо подходит для сетевой репликации в peer-host модели
- при deterministic base world и одинаковых world deltas peers могут независимо прийти к одинаковой walkable topology
- это сохраняет сеть для gameplay state, а не для производных навигационных артефактов
Почему не выбран network-streamed NavMesh:
- лишний трафик и высокая сложность синхронизации
- плохая масштабируемость для догрузки чанков и late join
- NavMesh все равно пришлось бы пересобирать при локальных изменениях геометрии
Последствия:
- каноничность gameplay не должна зависеть от клиентского NavMesh
- client NavMesh используется для локальных потребностей, но authoritative decisions по NPC остаются у хоста
- при одинаковом world state peers должны приходить к функционально эквивалентной walkable topology, но NavMesh не считается protocol-grade bit-identical артефактом, от которого зависит correctness multiplayer state
5. Будущие изменения проходимости мира передаются как authoritative world deltas
Решение:
- базовый мир идет из deterministic generation
- любые будущие баррикады, спеллы, разрушаемость, carve и другие изменения мира передаются как authoritative deltas от хоста
- после применения delta каждый peer локально перестраивает затронутые nav regions
Почему выбрано:
- это отделяет immutable base generation от mutable session state
- обеспечивает late join: новому игроку можно отдать base seed/config и журнал world deltas
- не требует вводить ownership migration для чанков
Почему не выбран fully local mutable world:
- local-first изменения мира не могут быть каноническими в кооперативной сетевой игре
- конфликтуют с античитом, late join и persistence
Последствия:
- NavMesh pipeline обязан уметь маркировать локальные nav regions как dirty после world delta
6. NavMesh pipeline должен работать в single-thread budget; многопоточность в WebGL считается только опциональным ускорением
Решение:
- архитектура runtime NavMesh не должна зависеть от наличия потоков
- базовый режим должен укладываться в бюджет кадра на одном потоке
- если deployment позже подтвердит поддержку
SharedArrayBufferиCOOP/COEP, можно добавить threaded optimization, но не делать ее обязательной
Почему выбрано:
- WebGL-host остается одной из целевых платформ
- WebGL multithreading требует специальных заголовков и эксплуатационной дисциплины на стороне хостинга
- завязка critical gameplay pipeline на эту инфраструктуру слишком рискованна для MVP
Почему не выбран threaded-only pipeline:
- он может работать в editor/desktop и развалиться в реальном WebGL deployment
- создаст ложное ощущение приемлемого бюджета, которого не будет на production-hosting
Последствия:
- rebuild должен быть incremental, throttled и bounded
- полносценовый bake вокруг камеры не подходит как каноническая модель
7. Первая итерация NavMesh может приоритизировать одного player actor, но внешний interest-контракт сразу задается как actor set
Решение:
- для первой проверки гипотезы scheduler может стартовать от одного player actor
- внешний reader/read-model контракт не должен жестко фиксировать single-point модель
- целевой контракт для multiplayer host: nav coverage должна учитывать игроков и активных NPC как actor-level interest set
Почему выбрано:
- это минимальный объем для MVP-проверки без ранней переплаты за сложную interest model
- при этом заранее фиксируется, что player-only coverage не является конечной архитектурой
Почему не выбран camera-driven center:
- камера не является каноническим gameplay actor
- в multiplayer и especially on host камера может не совпадать с зоной активной симуляции
- привязка к
Camera.mainломает переносимость решения из test scene в сетевую сессию
Последствия:
- в коде нельзя оставлять
Camera.mainкак канонический источник world/nav interest - target должен представлять actor-level interest, а не presentation-level camera
- reader-контракт для интереса должен уметь вернуть один или несколько actor-level interest points; даже если первая scene wiring временно дает только один player actor, это не должно цементироваться во внешний API
8. Для MVP поддерживается один тип NavMesh agent
Решение:
- сейчас поддерживается только один
agentTypeID
Почему выбрано:
- проект на стадии hypothesis/MVP
- это уменьшает стоимость runtime bake и настройки AI Navigation
- не раздувает матрицу тестирования до появления реальной необходимости
Почему не выбран multi-agent bake сразу:
- рост CPU и memory costs
- усложнение отладки при почти нулевой текущей пользе
Последствия:
- при появлении разных классов существ нужно отдельно пересмотреть agent taxonomy
9. Runtime NavMesh реализуется как sidecar-модуль, а не как hardwired часть world generator
Решение:
VoxelWorldостается владельцем world state и chunk lifecycle- NavMesh реализуется отдельным подключаемым модулем в собственной assembly
- модуль подключается через DI и может быть отключен без переписывания world feature
Почему выбрано:
- это соответствует целевой модели feature-подсистем как подключаемых модулей
- позволяет держать
VoxelWorldcore меньше и стабильнее - упрощает отключение NavMesh в сценах или режимах, где он не нужен
Почему не выбран partial-вариант внутри VoxelWorldGenerator:
- он быстрее в реализации, но цементирует NavMesh внутрь world feature
- делает отключение модуля искусственным
- увеличивает связанность и мешает дальнейшему DI-разделению
Последствия:
- world feature обязан публиковать стабильные контракты для sidecar-потребителей
- NavMesh-модуль не должен зависеть от private nested runtime types
VoxelWorldGenerator
10. Для модульной интеграции используется комбинация MessagePipe и reader-интерфейсов
Решение:
MessagePipeиспользуется для событий world lifecycle и invalidation- отдельные reader-интерфейсы используются для получения актуального snapshot state
- NavMesh service получает
IPublisher<T>иISubscriber<T>через DI, а не через global lookup
Почему выбрано:
- сообщения хорошо решают слабую связность и optional-subscription
- одних сообщений недостаточно, потому что модуль может стартовать позже и пропустить часть lifecycle events
- reader-интерфейсы позволяют восстановить текущее состояние без зависимости от конкретной реализации мира
Почему не выбран message-only подход:
- missed events ломают начальную инициализацию и late subscription
- пришлось бы тащить тяжелые mutable runtime objects прямо в сообщения
- модуль становился бы хрупким при reorder startup sequence
Почему не выбран direct-reference подход:
- прямые ссылки на
VoxelWorldGeneratorубивают модульность VoxelWorldGeneratorпока содержит private nested types и внутренние детали, которые нельзя делать частью внешнего API
Последствия:
- нужны query contracts для чтения актуальных nav build sources чанков и actor-level interest set, например
IChunkNavSourceReaderиIWorldInterestReader - нужны message types для
ChunkNavGeometryReady,ChunkNavGeometryRemovedиWorldInterestChanged GlobalMessagePipeне считается канонической точкой интеграции для feature-кода- world-to-navmesh contracts не должны делать
Transform,MeshColliderиBoxColliderканоническим внешним состоянием там, где достаточно узких source descriptors для build/invalidation
Long-Term Risks
Critical
- WebGL-host может не выдержать одновременно world streaming, runtime NavMesh rebuild и server-authoritative NPC AI.
- Любой drift по
seed,VoxelWorldConfigили world rules между peers приведет к расхождению геометрии и локального NavMesh.
High
- Цель в
50активных NPC может упереться не в один subsystem, а в суммарный CPU budget хоста. - Будущие изменения геометрии потребуют точной invalidation strategy по nav regions; без нее rebuild cost быстро выйдет из-под контроля.
- Если client movement в будущем начнет опираться на локальный NavMesh как на authority source, появятся расхождения с host simulation.
- Если contracts world feature окажутся слишком узкими или, наоборот, будут протекать внутренними типами генератора, sidecar-модуль быстро потеряет изоляцию.
Medium
- Late join требует не только
seed/config, но и корректного воспроизведения authoritative world deltas. - Если region size выбрать слишком крупным, rebuild будет дорогим; если слишком мелким, возрастет число build operations и seam-risk на границах.
- Неаккуратное использование
GlobalMessagePipeвместо DI-инъекции создаст скрытую runtime-зависимость и усложнит тестирование.
Downstream Implications
TASK-0001: этот документ закрывает часть канонических MVP-решений по world/authority/navmesh/module boundaries.TASK-0002: session handshake должен включать world seed, config/version и protocol compatibility checks.TASK-0012: enemy AI проектируется только как host-authoritative.TASK-0023: runtime NavMesh обязан быть local-build, throttled, sidecar-модулем и не должен иметь camera-driven assumptions.