Files
TheDeclineOfWarriors/docs/architecture/mvp-world-authority-navmesh.md
T
Alexander Borisov 6227542d2d document modular navmesh and agent prompts
Update the runtime NavMesh architecture to a DI and MessagePipe sidecar model, and add reusable agent prompt templates that capture the project's current multiplayer, WebGL, and modularity constraints.
2026-04-08 02:19:03 +03:00

18 KiB
Raw Blame History

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 остаются у хоста

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, но долгосрочный контракт расширяется до players + active NPC

Решение:

  • для первой проверки гипотезы build priority привязывается к player actor
  • целевой контракт для multiplayer host: nav coverage должна учитывать игроков и активных NPC

Почему выбрано:

  • это минимальный объем для 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

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-подсистем как подключаемых модулей
  • позволяет держать VoxelWorld core меньше и стабильнее
  • упрощает отключение 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

Последствия:

  • нужны contracts для IChunkNavGeometryReader и IWorldInterestReader
  • нужны message types для ChunkNavGeometryReady, ChunkNavGeometryRemoved и WorldInterestChanged
  • GlobalMessagePipe не считается канонической точкой интеграции для feature-кода

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.