--- id: TASK-0027 title: Host-authoritative player navigation с shared debug path preview summary: Перевести player movement на host-authoritative NavMesh pipeline с server-side path planning, authoritative path following и общим debug path preview для всех клиентов. priority: Highest area: gameplay-core owner: unassigned created: 2026-04-08 updated: 2026-04-08 execution_time: 3d depends_on: - TASK-0002 - 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/Scripts/Players/PlayerMoving.cs - Assets/Features/VoxelWorld/Prefabs/TestPlayer.prefab - Assets/Features/VoxelWorld/Scenes/VoxelWorldTestScene.unity - Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs - Assets/Features/VoxelWorld/Contracts/NavMeshWorldContracts.cs - Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshService.cs --- # TASK-0027 - Host-authoritative player navigation с shared debug path preview ## Status Статус задачи ведется в `docs/tasks/Index.md` и является каноническим там. ## Why Если игрок тоже должен двигаться по NavMesh, текущий local/client-authoritative movement pipeline становится архитектурно слабым для multiplayer: - клиент не должен быть источником канонического movement outcome; - локальный `NavMeshAgent` не должен быть authoritative mover для player actor; - path planning и path following должны принадлежать хосту; - shared debug path preview для движущихся игроков должен отображать именно authoritative path, который принят хостом, а не локальную клиентскую догадку. Без этого возрастает риск: - desync между client local movement и host state; - race-condition между player spawn и nav coverage readiness; - неотлаживаемых расхождений path preview между peers; - ошибок вроде `Failed to create agent because it is not close enough to the NavMesh`, если movement pipeline завязан на lifecycle локального `NavMeshAgent`. ## Expected Outcome - Игрок отправляет только команду перемещения, а не итог движения. - Хост валидирует destination на своем NavMesh, строит authoritative path и двигает actor канонически. - Клиенты получают authoritative movement state и сглаживают presentation. - Для каждого движущегося игрока существует authoritative debug path preview, который видят все клиенты. - Player spawn и first move command не зависят от hidden scene hacks и не требуют client-authoritative `NavMeshAgent`. ## Current Context В проекте уже зафиксированы и частично реализованы базовые решения по миру и runtime NavMesh: - `TASK-0023` ввел runtime NavMesh как sidecar-модуль поверх voxel world. - `VoxelWorldGenerator` уже публикует nav source snapshots и world interest. - `VoxelWorldNavMeshService` строит NavMesh локально на каждом peer по region-based схеме. При этом current player flow пока не соответствует целевой модели: - `Assets/Scripts/Players/PlayerMoving.cs` ориентирован на локальное movement execution; - `Assets/Features/VoxelWorld/Prefabs/TestPlayer.prefab` все еще держит player movement в client-oriented конфигурации; - spawn/nav readiness для player-on-navmesh еще не оформлены как отдельный контракт; - shared debug path preview для player movement отсутствует. ## Source Of Truth - `docs/architecture/mvp-world-authority-navmesh.md` - `docs/plans/TASK-0023-runtime-navmesh-implementation-plan.md` - фактический player/network/world flow в текущем коде проекта ## Read First - `Assets/Scripts/Players/PlayerMoving.cs` - `Assets/Scripts/Players/CameraFollow.cs` - `Assets/Features/VoxelWorld/Prefabs/TestPlayer.prefab` - `Assets/Features/VoxelWorld/Scenes/VoxelWorldTestScene.unity` - `Assets/Features/VoxelWorld/Runtime/VoxelWorldGenerator.cs` - `Assets/Features/VoxelWorld/Contracts/NavMeshWorldContracts.cs` - `Assets/Features/VoxelWorldNavMesh/Runtime/VoxelWorldNavMeshService.cs` - `Assets/Scripts/VoxelWorld/VoxelWorldNavMeshLifetimeScope.cs` ## Fixed Decisions ### 1. Player movement outcome is host-authoritative Клиент отправляет только move intent. Канонические: - target acceptance/rejection; - path corners; - progress по path; - итоговая позиция; - movement completion/cancel. Все это вычисляется и хранится на хосте. ### 2. Player uses NavMesh for movement, but client NavMesh is not authoritative Клиент может использовать локальный NavMesh только для optional preview/query UX, но не как source of truth для gameplay movement state. ### 3. Do not use client-local `NavMeshAgent` as canonical player mover Нельзя строить player movement correctness на `NavMeshAgent`, который локально двигает owner-клиента. Предпочтительный путь: - host-side `NavMesh.SamplePosition`; - host-side `NavMesh.CalculatePath`; - host-side explicit path follower; - movement execution через контролируемый mover, предпочтительно `CharacterController.Move` или эквивалентный deterministic-ish explicit mover. ### 4. Client does not send path corners or final movement outcome Клиенту нельзя отправлять: - path corners; - velocity как authoritative instruction; - final position; - movement completion. Клиент отправляет только request: - sequence id; - requested destination; - при необходимости легкий UX/debug metadata, не влияющий на authority. ### 5. Shared debug path preview must be authoritative-path-based Обязательный debug preview, который видят все клиенты, должен строиться из authoritative path, рассчитанного хостом. Допустим временный local provisional preview у инициирующего клиента, но он: - не считается каноническим; - должен визуально отличаться; - должен исчезать или заменяться после host accept/reject. ### 6. Spawn/nav readiness must be explicit Нельзя строить pipeline по модели: - player actor spawned; - NavMesh может быть еще не готов; - movement runtime надеется, что agent потом сам корректно «встанет» на NavMesh. Нужен явный readiness contract или equivalent bootstrap policy для spawn regions / first move command. ## Scope In - host-authoritative player movement по NavMesh; - client command pipeline для выбора destination; - host-side destination validation; - host-side path planning; - host-side path following; - replication authoritative movement state на клиентов; - shared debug path preview для каждого движущегося игрока на всех клиентах; - optional local provisional preview для owner-клиента; - nav-aware spawn/bootstrap и первый move command; - интеграция через contracts + DI + MessagePipe, совместимая с `TASK-0023`. ## Scope Out - NPC navigation system; - crowd simulation; - сложная prediction/reconciliation система для player nav movement; - production UI polish path markers; - ownership migration; - репликация NavMesh data blob; - multi-agent support beyond current single-agent MVP; - сохранение movement path через reconnect/persistence beyond current runtime session. ## Required Architecture ### Movement flow #### Client 1. Игрок выбирает destination. 2. Input layer определяет world point. 3. Optional: строит provisional local preview path для UX owner-клиента. 4. Отправляет host command с sequence id и requested destination. #### Host 1. Проверяет ownership и допустимость команды. 2. Проверяет movement lock/state. 3. Проверяет nav readiness для области. 4. Делает `NavMesh.SamplePosition`. 5. Делает `NavMesh.CalculatePath`. 6. Если путь валиден, обновляет canonical movement state. 7. Публикует/реплицирует accepted authoritative path и path preview. 8. Если путь невалиден, отправляет reject reason. #### Host tick 1. Берет текущий active path. 2. Вычисляет движение к текущему corner. 3. Двигает player actor через explicit mover. 4. Продвигает current corner index. 5. По завершении очищает active path preview и переводит actor в `Idle`. #### Clients 1. Получают authoritative movement state. 2. Сглаживают presentation. 3. Отображают authoritative debug path preview для всех moving players. ### Assembly boundaries Рекомендуется отдельный feature-модуль: - `Assets/Features/PlayerNavigation/Contracts/PlayerNavigation.Contracts.asmdef` - `Assets/Features/PlayerNavigation/Runtime/PlayerNavigation.Runtime.asmdef` Нельзя вшивать player navigation hardwired внутрь `VoxelWorldGenerator` или `VoxelWorldNavMeshService`. ### DI and integration model Использовать: - contracts; - DI через `VContainer`; - typed `MessagePipe` publishers/subscribers; - reader interfaces для текущего snapshot state. Не использовать `GlobalMessagePipe`. ### Message vs reader split Через MessagePipe: - lifecycle movement events; - accept/reject events; - preview invalidation/update events. Через reader contracts: - текущее состояние movement state; - текущее состояние authoritative path preview; - nav readiness / spawn readiness snapshot, если требуется queryable access. ## Required New Contracts ### Reader interfaces Нужны как минимум: - `IPlayerMovementStateReader` - `IPlayerPathPreviewReader` - `ISpawnNavReadinessReader` или эквивалентный узкий readiness contract Минимальная ответственность: - `IPlayerMovementStateReader` умеет вернуть current movement snapshot игрока; - `IPlayerPathPreviewReader` умеет вернуть active authoritative preview для игрока или списка игроков; - `ISpawnNavReadinessReader` умеет ответить, готова ли nav coverage для spawn/first-move области. ### DTO / snapshots Ожидаются как минимум: - `PlayerMovementStateSnapshot` - `PlayerPathPreviewSnapshot` - `PlayerMoveRequest` - `PlayerMoveCommandResult` Минимальный состав `PlayerMovementStateSnapshot`: - player network id; - status; - command sequence; - current position; - target position; - move speed; - current corner index; - authoritative path corners; - updated timestamp/network tick. Минимальный состав `PlayerPathPreviewSnapshot`: - player network id; - command sequence; - corners; - `IsAuthoritative`; - `IsActive`. ### Enums Нужны как минимум: - `PlayerMovementStatus` - `PlayerMoveRejectReason` Примерные состояния: - `Idle` - `AwaitingPath` - `Moving` - `Blocked` - `Completed` - `Cancelled` - `Rejected` Примерные reject reasons: - `NoNavCoverage` - `DestinationNotOnNavMesh` - `PathInvalid` - `PathPartial` - `MovementLocked` - `NotOwner` ## Required Messages Нужны typed MessagePipe messages для: - `PlayerMoveRequestedMessage` - `PlayerMoveAcceptedMessage` - `PlayerMoveRejectedMessage` - `PlayerMoveStateChangedMessage` - `PlayerPathPreviewChangedMessage` - `PlayerMovementStoppedMessage` Если для spawn/bootstrap это необходимо, допустим дополнительный readiness message, но только как supplement к reader contract, а не как единственный источник истины. ## Required Runtime Responsibilities ### 1. Client input sender Должен: - собирать local click-to-move input; - вычислять world destination; - отправлять network command хосту; - optional: запускать provisional local preview. Не должен: - канонически двигать actor; - принимать final authoritative решения по path validity. ### 2. Host command validator / planner Должен: - валидировать ownership; - валидировать destination; - делать `NavMesh.SamplePosition`; - делать `NavMesh.CalculatePath`; - обновлять canonical movement state; - публиковать accepted/rejected results. ### 3. Host path follower Должен: - исполнять movement по accepted path; - отслеживать current corner index; - завершать, отменять или репланить путь; - синхронизировать authoritative transform/state. ### 4. Shared preview state + renderer Должны: - хранить authoritative debug path data; - раздавать snapshot presentation-слою; - визуализировать active path preview для всех observed players. Shared preview не должен строиться заново локально на каждом клиенте по его client NavMesh. Источник preview для всех клиентов должен быть authoritative path state от хоста. ## FishNet Requirements ### Client -> Host Использовать `ServerRpc` для move command: - `RequestMoveTo(uint sequence, Vector3 requestedWorldPoint)` ### Host -> Clients Допустимы: - authoritative replicated movement state; - отдельные `TargetRpc` / `ObserversRpc` для accept/reject; - отдельная lightweight replication для shared path preview. Клиент не должен иметь возможности двигать чужого player actor. ## Required Changes In Existing Code ### `Assets/Scripts/Players/PlayerMoving.cs` Нужно перестроить из local movement executor в input sender / thin player navigation entrypoint. ### `Assets/Features/VoxelWorld/Prefabs/TestPlayer.prefab` Нужно пересмотреть: - текущий movement pipeline; - конфигурацию `NetworkTransform`; - player navigation components; - visual debug preview attachment points. Client-authoritative movement не должен оставаться каноническим режимом для nav-based player movement. ### `Assets/Features/VoxelWorld/Scenes/VoxelWorldTestScene.unity` Нужно проверить: - spawn bootstrap; - nav warmup around spawn points; - наличие всего необходимого для тестирования shared path preview. ### Nav readiness bootstrap Нужно добавить явную стратегию, чтобы player spawn / first command не зависели от случайного отсутствия NavMesh в стартовой области. ## Debug Path Preview Requirements Это обязательная часть задачи. ### Functional requirement Каждый движущийся игрок должен иметь debug path preview, который видят все клиенты. Примеры: - игрок A движется -> path видят A, B, C; - игрок B движется -> path видят A, B, C. ### Canonical rule Shared preview должен отображать именно authoritative path, рассчитанный хостом. ### Optional owner-local preview Допустим provisional local preview до host ответа, но он должен: - визуально отличаться; - не считаться каноническим; - исчезать или заменяться после accept/reject. ### Minimal rendering expectation Минимально допустимо: - `LineRenderer` или эквивалентный lightweight renderer; - обновление при accept/replan/stop/complete; - очистка при completion/cancel/reject. ## Acceptance Criteria - Клиент может отправить move request по клику в мир. - Хост валидирует destination на своем NavMesh. - Хост строит authoritative path. - Игрок движется по host-side path, а не по client-authoritative local mover. - Недопустимые destination/path корректно reject'ятся с reason. - Для каждого moving player существует authoritative path preview. - Этот preview виден на всех клиентах. - Preview корректно обновляется при новой команде, replan, stop, cancel и completion. - Если реализован provisional local preview, он визуально отделен от authoritative shared preview. - Player spawn и first move command не зависят от hidden `NavMeshAgent` attach hacks. - Pipeline не опирается на `Camera.main` как канонический источник authority/interest. ## Verification - ручной тест: single host, single client, move command accepted/rejected; - ручной тест: host + 2 clients, оба клиента видят preview друг друга; - ручной тест: invalid destination вне walkable area, host reject без runtime errors; - ручной тест: новая команда во время движения корректно заменяет active path; - ручной тест: late join видит текущее движение и active preview moving players; - ручной тест: первый move command после старта сцены не приводит к runtime ошибкам из-за отсутствия nav coverage; - ручной тест: completion/cancel очищает preview на всех клиентах. ## Risks / Open Questions - Если оставить рядом client-authoritative movement и host-side nav movement, возникнет двойная симуляция и несогласованное состояние. - Если shared preview строить по локальному client NavMesh, разные peers могут видеть разный path debug. - Если spawn/nav readiness не будет оформлен явно, lifecycle race останется даже при правильном movement flow. - Возможно потребуется отдельный узкий contract для nav coverage readiness, которого пока нет в `TASK-0023` runtime-поверхности. ## Human Decisions Needed - none currently ## Decision Log - `2026-04-08` - задача создана после фиксации runtime NavMesh sidecar и обсуждения правильной host-authoritative модели player movement по NavMesh. ## Handoff Notes Реализация задачи должна опираться на уже принятые решения по world authority и runtime NavMesh. Если в ходе реализации возникнет соблазн повесить `NavMeshAgent` на player prefab как client-local authoritative mover, это нужно считать архитектурно неправильным shortcut'ом и не делать. Shared debug path preview обязателен и должен отображать authoritative path для всех клиентов, а не purely local preview инициатора.