diff --git a/docs/architecture/mvp-world-authority-navmesh.md b/docs/architecture/mvp-world-authority-navmesh.md index 31be503a..9fbfc792 100644 --- a/docs/architecture/mvp-world-authority-navmesh.md +++ b/docs/architecture/mvp-world-authority-navmesh.md @@ -100,6 +100,7 @@ Последствия: - каноничность gameplay не должна зависеть от клиентского NavMesh - client NavMesh используется для локальных потребностей, но authoritative decisions по NPC остаются у хоста +- при одинаковом world state peers должны приходить к функционально эквивалентной walkable topology, но NavMesh не считается protocol-grade bit-identical артефактом, от которого зависит correctness multiplayer state ### 5. Будущие изменения проходимости мира передаются как authoritative world deltas @@ -140,11 +141,12 @@ - rebuild должен быть incremental, throttled и bounded - полносценовый bake вокруг камеры не подходит как каноническая модель -### 7. Первая итерация NavMesh покрывает область вокруг player actor, но долгосрочный контракт расширяется до players + active NPC +### 7. Первая итерация NavMesh может приоритизировать одного player actor, но внешний interest-контракт сразу задается как actor set Решение: -- для первой проверки гипотезы build priority привязывается к player actor -- целевой контракт для multiplayer host: nav coverage должна учитывать игроков и активных NPC +- для первой проверки гипотезы scheduler может стартовать от одного player actor +- внешний reader/read-model контракт не должен жестко фиксировать single-point модель +- целевой контракт для multiplayer host: nav coverage должна учитывать игроков и активных NPC как actor-level interest set Почему выбрано: - это минимальный объем для MVP-проверки без ранней переплаты за сложную interest model @@ -158,6 +160,7 @@ Последствия: - в коде нельзя оставлять `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 @@ -219,9 +222,10 @@ - `VoxelWorldGenerator` пока содержит private nested types и внутренние детали, которые нельзя делать частью внешнего API Последствия: -- нужны contracts для `IChunkNavGeometryReader` и `IWorldInterestReader` +- нужны 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 diff --git a/docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md b/docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md index 443a501f..49b03327 100644 --- a/docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md +++ b/docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md @@ -9,8 +9,8 @@ - текущая test scene: `Assets/Features/VoxelWorld/Scenes/VoxelWorldTestScene.unity` - основной runtime генерации мира: `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs` - в проекте уже есть `ApplicationLifetimeScope` с `MessagePipe` registration -- первая итерация NavMesh coverage строится вокруг player actor -- долгосрочный контракт остается `players + active NPC` +- первая итерация scheduler может начинать приоритизацию от одного player actor +- внешний interest-контракт сразу остается actor-level interest set: `players + active NPC` - один тип агента - динамические изменения мира пока не реализуются, но контракты под них должны быть предусмотрены - WebGL-host остается целевой платформой, поэтому базовый pipeline не зависит от потоков @@ -55,16 +55,23 @@ - один большой moving volume слишком дорог и плохо контролируется по бюджету - region-based rebuild дает лучший компромисс между стоимостью и связностью -### 5. Источники build sources берутся из chunk colliders, публикуемых world feature +### 5. Целью считается эквивалентная walkable topology, а не bit-identical NavMesh artifact + +Почему: +- runtime NavMesh остается derived cache, а не canonical network state +- gameplay correctness не должен опираться на клиентский NavMesh как на источник authority +- это не создает ложного требования к protocol-grade детерминизму там, где он не нужен для MVP + +### 6. Источники build sources публикуются world feature как узкие source descriptors Выбор: -- `GroundCollider` дает box source -- `MountainCollider.sharedMesh` дает mesh source +- world feature отдает snapshot nav sources чанка через стабильный reader contract +- текущая реализация `VoxelWorld` может внутри строить эти sources из `GroundCollider` и `MountainCollider`, но эта деталь не протекает в API sidecar-модуля Почему: - не нужен scene-wide scanning - не требуется отдельная nav-only геометрия на первом этапе -- это наиболее близкое к gameplay представление walkable/non-walkable world geometry +- sidecar-модуль не цементируется на конкретных `Collider`-компонентах и scene hierarchy мира ## Target Module Boundaries @@ -83,27 +90,41 @@ ### Reader Interfaces -- `IChunkNavGeometryReader` +- `IChunkNavSourceReader` - `IWorldInterestReader` Минимальная ответственность: -- `IChunkNavGeometryReader` умеет вернуть текущую nav-геометрию чанка и список уже загруженных чанков -- `IWorldInterestReader` умеет вернуть текущую primary interest point +- `IChunkNavSourceReader` умеет вернуть текущие nav build sources чанка и список уже загруженных чанков +- `IWorldInterestReader` умеет вернуть текущий actor-level interest set ### DTO / Contracts -- `ChunkNavGeometry` +- `ChunkNavSourceSnapshot` +- `ChunkNavBuildSourceDescriptor` +- `WorldInterestPoint` Состав DTO: +- `ChunkNavSourceSnapshot` - `Vector2Int Coord` -- `Transform Root` -- `BoxCollider GroundCollider` -- `MeshCollider MountainCollider` - `int Version` +- `ChunkNavBuildSourceDescriptor[] Sources` + +- `ChunkNavBuildSourceDescriptor` +- `NavMeshBuildSourceShape Shape` +- `Matrix4x4 Transform` +- `Vector3 Size` для box-source +- `Mesh Mesh` для mesh-source +- `int Area` + +- `WorldInterestPoint` +- `Vector3 Position` +- `float Priority` +- `WorldInterestKind Kind` Примечание: -- DTO должен содержать только то, что реально нужно для NavMesh source collection +- DTO должен содержать только то, что реально нужно для NavMesh source collection и build prioritization - private nested types `VoxelWorldGenerator` не должны утекать наружу +- `Transform`, `MeshCollider` и `BoxCollider` не должны становиться каноническим внешним состоянием NavMesh integration, если достаточно source descriptors ### Message Types @@ -132,7 +153,7 @@ ### 2. Реализовать reader interfaces -- `IChunkNavGeometryReader` +- `IChunkNavSourceReader` - `IWorldInterestReader` ### 3. Публиковать сообщения после world lifecycle changes @@ -140,7 +161,7 @@ Нужно публиковать: - `ChunkNavGeometryReadyMessage` после фактического применения collider mesh - `ChunkNavGeometryRemovedMessage` перед уничтожением чанка -- `WorldInterestChangedMessage` при смене actor-level interest point +- `WorldInterestChangedMessage` при изменении actor-level interest set ### 4. Убрать каноническую зависимость от `Camera.main` @@ -183,12 +204,12 @@ ## NavMesh Service Responsibilities - подписаться на world lifecycle messages -- на старте получить snapshot уже загруженных чанков через `IChunkNavGeometryReader` +- на старте получить snapshot уже загруженных чанков через `IChunkNavSourceReader` - построить initial set dirty regions - поддерживать `NavMeshData` по регионам -- собирать `NavMeshBuildSource` из chunk colliders +- собирать `NavMeshBuildSource` из source descriptors, а не через прямой доступ к world colliders - запускать throttled `UpdateNavMeshDataAsync` -- переоценивать build priority относительно current interest point +- переоценивать build priority относительно current interest set - удалять region data, когда она выходит из активного диапазона и становится пустой ## Region Runtime Data @@ -223,15 +244,15 @@ ### Initial Sync 1. Сервис стартует. -2. Через `IChunkNavGeometryReader` получает список уже загруженных чанков. +2. Через `IChunkNavSourceReader` получает список уже загруженных чанков. 3. Помечает соответствующие nav regions dirty. -4. Через `IWorldInterestReader` получает текущую точку интереса. +4. Через `IWorldInterestReader` получает текущий interest set. 5. Запускает scheduler. ### Incremental Update 1. Приходит `ChunkNavGeometryReadyMessage`. -2. Сервис читает актуальную geometry через `IChunkNavGeometryReader`. +2. Сервис читает актуальные sources через `IChunkNavSourceReader`. 3. Помечает nav region dirty. 4. Если chunk расположен на границе region, дополнительно маркирует соседний region. @@ -244,19 +265,19 @@ ### Interest Update 1. Приходит `WorldInterestChangedMessage`. -2. Сервис обновляет current interest point. +2. Сервис обновляет current interest set. 3. Scheduler пересчитывает порядок rebuild и warmup regions. ## Source Collection Rules Для каждого затронутого region: - собрать build sources только из чанков региона и соседнего margin -- использовать только известную geometry из reader interface +- использовать только известные source snapshots из reader interface - не сканировать произвольные объекты сцены -Для каждого chunk geometry: -- добавить `NavMeshBuildSourceShape.Box` из `GroundCollider` -- добавить `NavMeshBuildSourceShape.Mesh` из `MountainCollider.sharedMesh`, если mesh не пустой +Для каждого chunk snapshot: +- добавить sources из `ChunkNavBuildSourceDescriptor` +- если текущий `VoxelWorld` строит эти descriptors из `GroundCollider` и `MountainCollider`, это остается его внутренней деталью и не становится contract-level зависимостью NavMesh-модуля ## Performance Rules @@ -283,6 +304,7 @@ 1. Проверить старт сервиса после world generator: missed events не должны ломать initial sync. 2. Проверить, что модуль работает только через DI-injected `MessagePipe` и reader interfaces. 3. Проверить, что отключение регистрации `VoxelWorldNavMesh` не ломает world feature. +4. Проверить, что внешний interest contract допускает один или несколько interest points, даже если первая scene wiring пока подает только одного player actor. ### Performance diff --git a/docs/tasks/items/TASK-0023.md b/docs/tasks/items/TASK-0023.md index 52c0b3ef..78c88784 100644 --- a/docs/tasks/items/TASK-0023.md +++ b/docs/tasks/items/TASK-0023.md @@ -96,4 +96,6 @@ AI врагов (`TASK-0012`) опирается на NavMesh. Воксельн ## Handoff Notes +Реализация задачи должна идти с учетом принятых решений и уже проведенного ресерча в `docs/architecture/mvp-world-authority-navmesh.md`, `docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md` и текущего runtime-контекста `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs`. Если формулировки task-card расходятся с каноническими решениями и зафиксированным ресерчем, приоритет у этих файлов. + Если в проекте нет пакета NavMeshComponents, возможно придется добавить его или реализовать минимальный runtime builder.