# TASK-0023 Runtime NavMesh Implementation Plan

## Goal

Реализовать runtime NavMesh для procedural voxel world как подключаемый sidecar-модуль в отдельной assembly, без camera-driven assumptions, с совместимостью с будущей peer-host multiplayer моделью и уже внедренными `VContainer` + `MessagePipe`.

## Inputs And Assumptions

- текущая test scene: `Assets/Features/VoxelWorld/Scenes/VoxelWorldTestScene.unity`
- основной runtime генерации мира: `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs`
- в проекте уже есть `ApplicationLifetimeScope` с `MessagePipe` registration
- первая итерация scheduler может начинать приоритизацию от одного player actor
- внешний interest-контракт сразу остается actor-level interest set: `players + active NPC`
- один тип агента
- динамические изменения мира пока не реализуются, но контракты под них должны быть предусмотрены
- WebGL-host остается целевой платформой, поэтому базовый pipeline не зависит от потоков

## Chosen Technical Direction

### 1. NavMesh не внедряется в `VoxelWorldGenerator` как internal partial-логика

Решение:
- `VoxelWorldGenerator` остается producer world state и chunk geometry
- runtime NavMesh живет в отдельном модуле и подписывается на world contracts

Почему:
- это соответствует целевой модели feature-модулей как подключаемых подсистем
- модуль можно будет реально отключить
- world feature не будет знать детали nav scheduling и `NavMeshData` lifecycle

### 2. Модуль использует `MessagePipe` для событий, но не опирается только на сообщения

Решение:
- `MessagePipe` используется для lifecycle/invalidation notifications
- reader-интерфейсы используются для чтения текущего состояния world geometry и interest points

Почему:
- message-only подход ломается на late subscription и startup ordering
- NavMesh service должен уметь стартовать позже publisher-а и восстановить актуальное состояние

### 3. Runtime pipeline строится через `NavMeshBuilder.UpdateNavMeshDataAsync`

Почему:
- это дает контроль над `NavMeshData`, `Bounds`, build sources и budget
- лучше подходит для region-based rebuild под WebGL-host, чем sample-подход с одним sliding volume

### 4. NavMesh строится по nav regions, а не per-chunk и не full-volume вокруг target

Выбор:
- отдельный `NavMeshData` на nav region
- стартовый размер region: `2x2` чанка, configurable

Почему:
- per-chunk ведет к слишком большому числу мелких build operations
- один большой moving volume слишком дорог и плохо контролируется по бюджету
- region-based rebuild дает лучший компромисс между стоимостью и связностью

### 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

Выбор:
- world feature отдает snapshot nav sources чанка через стабильный reader contract
- текущая реализация `VoxelWorld` может внутри строить эти sources из `GroundCollider` и `MountainCollider`, но эта деталь не протекает в API sidecar-модуля

Почему:
- не нужен scene-wide scanning
- не требуется отдельная nav-only геометрия на первом этапе
- sidecar-модуль не цементируется на конкретных `Collider`-компонентах и scene hierarchy мира

## Target Module Boundaries

### Assembly Layout

- `Assets/Features/VoxelWorld/Contracts/VoxelWorld.Contracts.asmdef`
- `Assets/Features/VoxelWorld/Runtime/VoxelWorld.Runtime.asmdef`
- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorld.NavMesh.Runtime.asmdef`

Почему так:
- `Contracts` фиксируют стабильную внешнюю границу
- `Runtime` реализует мир и публикует contracts
- `VoxelWorld.NavMesh.Runtime` остается optional consumer-модулем

## Contracts To Add

### Reader Interfaces

- `IChunkNavSourceReader`
- `IWorldInterestReader`

Минимальная ответственность:
- `IChunkNavSourceReader` умеет вернуть текущие nav build sources чанка и список уже загруженных чанков
- `IWorldInterestReader` умеет вернуть текущий actor-level interest set

### DTO / Contracts

- `ChunkNavSourceSnapshot`
- `ChunkNavBuildSourceDescriptor`
- `WorldInterestPoint`

Состав DTO:
- `ChunkNavSourceSnapshot`
- `Vector2Int Coord`
- `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 и build prioritization
- private nested types `VoxelWorldGenerator` не должны утекать наружу
- `Transform`, `MeshCollider` и `BoxCollider` не должны становиться каноническим внешним состоянием NavMesh integration, если достаточно source descriptors

### Message Types

- `ChunkNavGeometryReadyMessage`
- `ChunkNavGeometryRemovedMessage`
- `WorldInterestChangedMessage`
- позже: `ChunkWalkabilityChangedMessage` или аналогичный delta-invalidating message

Правило:
- сообщения несут ключ и минимальные данные invalidation
- тяжелое актуальное состояние читается через reader interfaces

## Required Changes In `VoxelWorldGenerator`

### 1. Перестать быть единственной точкой nav logic

`VoxelWorldGenerator` должен только:
- генерировать и стримить чанки
- создавать/apply collider mesh
- публиковать world contracts

Он не должен:
- владеть dirty nav region queue
- владеть `NavMeshData`
- напрямую запускать `NavMeshBuilder`

### 2. Реализовать reader interfaces

- `IChunkNavSourceReader`
- `IWorldInterestReader`

### 3. Публиковать сообщения после world lifecycle changes

Нужно публиковать:
- `ChunkNavGeometryReadyMessage` после фактического применения collider mesh
- `ChunkNavGeometryRemovedMessage` перед уничтожением чанка
- `WorldInterestChangedMessage` при изменении actor-level interest set

### 4. Убрать каноническую зависимость от `Camera.main`

Для первой итерации допускается scene wiring через explicit target reference, но не через runtime fallback на `Camera.main` как на архитектурную норму.

## NavMesh Module Structure

Рекомендуемые файлы:
- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshService.cs`
- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshTypes.cs`
- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshConfig.cs`
- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshModule.cs`

Дополнительно, если нужен bridge для scene binding на переходном этапе:
- `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshEntry.cs`

## DI Composition

### Registration Model

`VoxelWorld` регистрирует:
- `VoxelWorldGenerator` как реализацию reader interfaces
- publishers соответствующих world messages

`VoxelWorldNavMesh` регистрирует:
- `VoxelWorldNavMeshService`
- его subscribers
- config instance

### Important Rule

- feature-код не должен использовать `GlobalMessagePipe` как основную integration point
- `IPublisher<T>` и `ISubscriber<T>` должны приходить через DI

Почему:
- это сохраняет тестируемость
- не создает скрытых runtime-зависимостей
- лучше соответствует модульному подключению через `LifetimeScope`

## NavMesh Service Responsibilities

- подписаться на world lifecycle messages
- на старте получить snapshot уже загруженных чанков через `IChunkNavSourceReader`
- построить initial set dirty regions
- поддерживать `NavMeshData` по регионам
- собирать `NavMeshBuildSource` из source descriptors, а не через прямой доступ к world colliders
- запускать throttled `UpdateNavMeshDataAsync`
- переоценивать build priority относительно current interest set
- удалять region data, когда она выходит из активного диапазона и становится пустой

## Region Runtime Data

- `Dictionary<Vector2Int, NavRegionRuntime> navRegions`
- `Queue<Vector2Int> dirtyNavRegions`
- `HashSet<Vector2Int> queuedNavRegions`

`NavRegionRuntime` должен хранить:
- `NavMeshData NavMeshData`
- `NavMeshDataInstance Instance`
- `AsyncOperation ActiveBuild`
- `int Version`
- `bool BuildRequestedWhileRunning`
- `Bounds BuildBounds`

## New Config Settings

Добавить в NavMesh module config:
- `int navRegionSizeInChunks = 2`
- `int maxConcurrentNavMeshBuilds = 1`
- `int maxNavMeshBuildsPerFrame = 1`
- `float navBoundsHorizontalPadding`
- `float navBoundsVerticalPadding`
- `int navWarmupRadiusInRegions`

Примечание:
- для первой итерации `maxConcurrentNavMeshBuilds` должен оставаться `1`

## Build Flow

### Initial Sync

1. Сервис стартует.
2. Через `IChunkNavSourceReader` получает список уже загруженных чанков.
3. Помечает соответствующие nav regions dirty.
4. Через `IWorldInterestReader` получает текущий interest set.
5. Запускает scheduler.

### Incremental Update

1. Приходит `ChunkNavGeometryReadyMessage`.
2. Сервис читает актуальные sources через `IChunkNavSourceReader`.
3. Помечает nav region dirty.
4. Если chunk расположен на границе region, дополнительно маркирует соседний region.

### Removal

1. Приходит `ChunkNavGeometryRemovedMessage`.
2. Сервис помечает соответствующий region dirty.
3. Если region опустел и вышел из активной зоны, удаляет `NavMeshDataInstance`.

### Interest Update

1. Приходит `WorldInterestChangedMessage`.
2. Сервис обновляет current interest set.
3. Scheduler пересчитывает порядок rebuild и warmup regions.

## Source Collection Rules

Для каждого затронутого region:
- собрать build sources только из чанков региона и соседнего margin
- использовать только известные source snapshots из reader interface
- не сканировать произвольные объекты сцены

Для каждого chunk snapshot:
- добавить sources из `ChunkNavBuildSourceDescriptor`
- если текущий `VoxelWorld` строит эти descriptors из `GroundCollider` и `MountainCollider`, это остается его внутренней деталью и не становится contract-level зависимостью NavMesh-модуля

## Performance Rules

- не делать full-scene bake
- не пересобирать NavMesh синхронно через `BuildNavMesh()` на gameplay path
- не строить систему в расчете на обязательный background threading
- держать максимум один активный region build
- rebuild запускать только после фактического применения collider mesh
- события из `MessagePipe` не должны тащить heavy geometry payload, только invalidation keys
- scheduler должен быть bounded и deterministic по budget

## Verification Plan

### Functional

1. Запустить `VoxelWorldTestScene`.
2. Убедиться, что NavMesh module можно отключить и world generation продолжает работать.
3. Подключить NavMesh module и проверить появление walkable NavMesh на уже загруженных чанках.
4. Проверить, что agent из AI Navigation samples строит путь по поверхности.
5. Проверить unload чанков: старый NavMesh не должен оставлять висячие walkable islands.

### Integration

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

1. Быстро перемещать actor target по миру.
2. Снять показатели:
   - active nav regions
   - queued dirty regions
   - builds started
   - stale rebuilds dropped
   - worst-frame rebuild spikes

## Explicit Non-Goals For This Iteration

- `NavMeshObstacle` carving
- multi-agent bake
- networked NavMesh replication
- ownership migration для чанков или NPC
- финальная multiplayer interest model вокруг всех actors
- прямые зависимости NavMesh feature от `VoxelWorldGenerator` internals

## Execution Order

1. Добавить contracts assembly для world-to-navmesh integration.
2. Добавить message types и reader interfaces.
3. Адаптировать `VoxelWorldGenerator` под публикацию world contracts и messages.
4. Создать отдельную assembly и runtime module `VoxelWorld.NavMesh.Runtime`.
5. Реализовать `VoxelWorldNavMeshService` с initial sync через readers и incremental updates через `MessagePipe`.
6. Реализовать region scheduler и `NavMeshBuilder.UpdateNavMeshDataAsync`.
7. Подключить модуль через DI registration.
8. Провести ручную проверку и зафиксировать фактические perf observations.
