From 72826212117ec62d36c2ae86d36b423208e8d61e Mon Sep 17 00:00:00 2001 From: Alexander Borisov Date: Wed, 8 Apr 2026 13:28:12 +0300 Subject: [PATCH] add clustered nav coverage task Document the follow-up refactor from disconnected region surfaces to interest-cluster-based nav coverage windows for runtime pathing. --- docs/tasks/Index.md | 1 + docs/tasks/items/TASK-0026.md | 301 ++++++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+) create mode 100644 docs/tasks/items/TASK-0026.md diff --git a/docs/tasks/Index.md b/docs/tasks/Index.md index e4654a8a..97391530 100644 --- a/docs/tasks/Index.md +++ b/docs/tasks/Index.md @@ -65,3 +65,4 @@ | TASK-0023 | InProgress | Highest | ai | abysscion | 2d | `docs/tasks/items/TASK-0023.md` | Реализовать runtime NavMesh bake для voxel-чанка и интегрировать обновление навигации при загрузке/изменении чанков. | | TASK-0024 | ToDo | Highest | art | unassigned | 2d | docs/tasks/items/TASK-0024.md | Заменить Minecraft-placeholder арт на легальные ассеты для продакшена и зафиксировать источник/лицензии. | | TASK-0025 | ToDo | Highest | gameplay-core | unassigned | 3d | docs/tasks/items/TASK-0025.md | Перевести player movement на host-authoritative NavMesh pipeline с server-side path planning и shared debug path preview для всех клиентов. | +| TASK-0026 | ToDo | Highest | ai | unassigned | 2d | docs/tasks/items/TASK-0026.md | Перевести основной runtime pathing mode на interest-cluster-based coverage windows, чтобы убрать seam-разрывы region-based NavMesh и учитывать multiplayer interest set. | diff --git a/docs/tasks/items/TASK-0026.md b/docs/tasks/items/TASK-0026.md new file mode 100644 index 00000000..859f147d --- /dev/null +++ b/docs/tasks/items/TASK-0026.md @@ -0,0 +1,301 @@ +--- +id: TASK-0026 +title: Перевести runtime NavMesh на interest-cluster-based coverage +summary: Заменить основной runtime pathing mode с множества region-based NavMeshData на небольшой набор крупных cluster-based coverage windows, чтобы убрать seam-разрывы и сделать покрытие совместимым с multiplayer interest set. +priority: Highest +area: ai +owner: unassigned +created: 2026-04-08 +updated: 2026-04-08 +execution_time: 2d +depends_on: + - TASK-0023 +canonical_docs: + - docs/tasks/Index.md + - docs/architecture/mvp-world-authority-navmesh.md + - docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md +related_files: + - Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshService.cs + - Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshConfig.cs + - Assets/Features/VoxelWorld/Contracts/NavMeshWorldContracts.cs + - Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs +--- + +# TASK-0026 - Перевести runtime NavMesh на interest-cluster-based coverage + +## Status + +Статус задачи ведется в `docs/tasks/Index.md` и является каноническим там. + +## Why + +Текущая region-based runtime NavMesh схема подтверждает локальную работоспособность build pipeline, но уже показала архитектурно важную проблему: pathfinding между соседними зонами покрытия дает `PathPartial`, даже когда destination сам лежит на NavMesh. + +Это означает, что текущий основной runtime pathing mode опирается на набор разрозненных nav islands и не гарантирует непрерывный navigation graph между активными зонами симуляции. + +Для multiplayer host-authoritative pathing этого недостаточно. Хосту нужен не просто локально построенный NavMesh, а непрерывное coverage в активной области симуляции без систематических seam-разрывов на границах мелких регионов. + +## Expected Outcome + +- Основной runtime pathing mode больше не строится как множество мелких независимых `NavMeshData` по nav regions. +- Вместо этого используется небольшой набор крупных `coverage windows`, каждый из которых покрывает `interest cluster`. +- Внутри активной области симуляции pathfinding не ломается на границах бывших nav regions. +- Coverage учитывает не только одного игрока, а multiplayer interest set: `spawn anchors + players + active NPC`. +- Модуль остается sidecar-решением поверх `VoxelWorld`, без hardwiring внутрь `VoxelWorldGenerator`. + +## Current Context + +Сейчас runtime NavMesh уже вынесен в sidecar-модуль и строится локально на каждом peer: + +- `VoxelWorldGenerator` отдает nav sources через contracts; +- `VoxelWorldNavMeshService` собирает sources из chunk snapshots; +- build идет локально и не реплицируется по сети; +- authority gameplay сохраняется у хоста. + +Однако unit of build и unit of scheduling пока выбраны неудачно как основной pathing mode: + +- много мелких `NavMeshData` по region-based сетке; +- pathfinding между region surfaces может распадаться на отдельные islands; +- visual continuity overlay не гарантирует graph connectivity для `NavMesh.CalculatePath` / `NavMeshAgent`. + +## Source Of Truth + +- `docs/architecture/mvp-world-authority-navmesh.md` +- `docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md` +- фактическая реализация `VoxelWorldNavMeshService` +- подтвержденные smoke-test результаты с `PathPartial` на границах region coverage + +## Read First + +- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshService.cs` +- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshConfig.cs` +- `Assets/Features/VoxelWorld/Contracts/NavMeshWorldContracts.cs` +- `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs` +- `docs/architecture/mvp-world-authority-navmesh.md` + +## Fixed Decisions + +### 1. NavMesh remains local-build sidecar state + +NavMesh по-прежнему: + +- строится локально на каждом peer; +- не реплицируется как data blob; +- остается derived cache от world state; +- не является authoritative network state. + +### 2. Chunk stays source/invalidation unit, not build unit + +`Chunk` остается: + +- источником nav build sources; +- unit of invalidation для world lifecycle; +- источником dirty notifications. + +`Chunk` не должен оставаться каноническим unit of nav coverage build. + +### 3. Coverage window is built from interest clusters, not from one player and not from camera + +Нельзя строить канонический runtime pathing вокруг: + +- `Camera.main`; +- только одного tracked player; +- presentation-level сущности. + +Coverage должен строиться вокруг `interest clusters`, формируемых из: + +- spawn anchors; +- players; +- active NPC. + +### 4. One player does not imply one dedicated volume + +Нельзя закреплять правило `один игрок = один volume`. + +Правильная модель: + +- один spatially coherent interest cluster = один coverage window; +- близкие игроки и NPC должны merge'иться в один cluster; +- число active windows должно быть bounded. + +### 5. Scene scan through `NavMeshSurface` sample is not the canonical integration model + +Подход из sample `NavMeshSurfaceVolumeUpdater` полезен как диагностическая подсказка, но не должен становиться буквальной production integration model. + +Канонический путь для проекта: + +- bounds уровня sliding coverage window; +- build sources из `IChunkNavSourceReader` / world contracts; +- DI + typed MessagePipe + reader contracts. + +### 6. Spawn readiness must become first-class + +Покрытие должно учитывать spawn anchors до player movement activation. Нельзя полагаться на то, что NavMesh magically появится только после того, как actor уже стал единственной точкой интереса. + +## Scope In + +- замена основного runtime pathing mode с region-based surfaces на cluster-based coverage windows; +- новый scheduler по coverage windows; +- cluster builder для `players + active NPC + spawn anchors`; +- build source collection по bounds окна, а не по одному nav region; +- read-model для current nav coverage state; +- explicit coverage readiness для spawn / first path command / AI activation; +- bounded merge policy для близких interest points; +- debug visibility coverage windows. + +## Scope Out + +- изменение authority model NPC/AI; +- репликация NavMesh; +- полноценная crowd avoidance система; +- multi-agent taxonomy beyond current single-agent MVP; +- player host-authoritative navigation system как отдельная feature-задача; +- большой рефактор world feature вне необходимого nav contracts surface. + +## Required Architecture + +### New canonical unit: interest cluster + +Нужна новая внутренняя модель coverage: + +- `WorldInterestPoint` остается atomic input; +- `NavInterestCluster` становится unit of grouping; +- `NavCoverageWindow` становится unit of build and readiness. + +Минимальная ответственность cluster builder: + +- собрать текущий interest set; +- spatially merge близкие точки интереса; +- стабилизировать cluster ids между обновлениями; +- строить quantized window bounds с margin. + +### Coverage window replaces region as primary build unit + +`Coverage window` должен хранить: + +- cluster id; +- current bounds; +- `NavMeshData` / `NavMeshDataInstance`; +- dirty/building/ready state; +- список covered chunks или equivalent cached source membership. + +### Source collection remains contract-driven + +Build sources должны собираться: + +- не через scene scan; +- не через direct references на private world internals; +- а через `IChunkNavSourceReader` и chunk nav source snapshots. + +`Chunk` используется как source/invalidation unit, но не как primary coverage unit. + +### Coverage state must be queryable + +Нужен reader contract уровня: + +- `INavCoverageReader` + +Минимальная ответственность: + +- `IsPositionCovered(Vector3 worldPosition)`; +- возможность получить current active coverage windows; +- возможность проверить readiness для spawn/path activation. + +### Scheduler must be bounded and quantized + +Нельзя rebuild'ить coverage window на каждый микрошаг игрока. + +Нужны: + +- quantized movement threshold; +- bounded number of active windows; +- bounded builds per frame; +- rebuild только при существенном смещении cluster bounds, изменении cluster composition или chunk invalidation внутри covered bounds. + +## Suggested Runtime Structure + +### New or refactored runtime types + +- `NavInterestClusterBuilder` +- `NavCoverageWindowRuntime` +- `NavCoverageWindowSnapshot` +- `NavBuildSourceCollector` +- `INavCoverageReader` +- `VoxelWorldClusteredNavMeshService` или equivalent refactor текущего `VoxelWorldNavMeshService` + +### Config changes + +Текущий config должен сместиться от region-centric параметров к cluster/window-centric: + +- `clusterMergeDistance` +- `clusterBoundsPadding` +- `clusterRebuildQuantization` +- `maxActiveCoverageWindows` +- `chunkCollectionMarginInChunks` +- `maxBuildsPerFrame` + +Если старые region-centric поля остаются временно ради миграции, они не должны продолжать определять основной runtime pathing mode. + +## Main Highlights Of Changes + +1. **Смена unit of build** + +- было: `nav region` +- станет: `interest cluster coverage window` + +2. **Смена unit of scheduling** + +- было: очередь dirty regions +- станет: очередь dirty coverage windows + +3. **Смена unit of readiness** + +- было: неявная region-local готовность +- станет: явная coverage readiness по world position + +4. **Смена логики multiplayer coverage** + +- было: первая practical привязка к локальному player interest +- станет: `spawn anchors + players + active NPC` + +5. **Уход от seam-first topology** + +- было: pathing через сеть мелких surfaces с риском disconnected islands +- станет: меньшее число более цельных coverage windows + +## Acceptance Criteria + +- Основной runtime pathing mode больше не опирается на множество мелких region-based `NavMeshData` как на primary navigation graph. +- Destination на NavMesh в соседней активной области больше не приводит систематически к `PathPartial` только из-за seam между бывшими nav regions. +- Coverage формируется по interest clusters, а не по одному tracked player и не по `Camera.main`. +- Spawn anchors участвуют в initial nav coverage. +- Нужное coverage state можно query'ить через reader contract. +- Sidecar-модуль остается отключаемым без переписывания `VoxelWorld` core. +- Build pipeline остается bounded и пригодным для WebGL-host бюджета. + +## Verification + +- ручной тест: pathfinding между соседними активными областями больше не обрывается на границе бывших region surfaces; +- ручной тест: при удалении игроков друг от друга coverage windows корректно split/merge'ятся по кластерам; +- ручной тест: spawn area получает nav coverage до first path command; +- ручной тест: pathfinding внутри cluster window и между близкими covered areas дает `PathComplete`, где раньше получался `PathPartial` из-за seam; +- debug visualization coverage windows подтверждает ожидаемую cluster topology. + +## Risks / Open Questions + +- Один большой coverage window может снять seam-problem, но оказаться слишком тяжелым для host CPU budget; поэтому bounded cluster windows важнее, чем просто "один volume на весь мир". +- Слишком агрессивное merge policy может раздуть rebuild cost; слишком слабое merge policy вернет seam-problem в другой форме. +- Нужна аккуратная стратегия стабильных cluster ids, иначе scheduler и debug tooling будут шумными. +- Возможно понадобится временный dual-mode rollout: region mode как fallback, clustered mode как новый primary pathing mode до подтверждения стабильности. + +## Human Decisions Needed + +- none currently + +## Decision Log + +- `2026-04-08` - подзадача выделена после smoke-test'а runtime NavMesh, который подтвердил локальную работоспособность build pipeline, но выявил `PathPartial` на границах region-based coverage. + +## Handoff Notes + +Эта задача не отменяет базовые решения `TASK-0023`, а уточняет основной runtime pathing mode. Не возвращать интеграцию к `Camera.main` или scene-scan-driven sample как к канонической архитектуре. Sample `NavMeshSurfaceVolumeUpdater` использовать только как источник идеи sliding coverage, но не как буквальную production integration model.