neon-sprawl/docs/plans/NS-23-implementation-plan.md

11 KiB
Raw Blame History

NS-23 — Implementation plan

Story reference

Field Value
Key NS-23
Title E1.M1: Client path-follow baseline (navigation + visible locomotion)
Jira NS-23
Parent context NS-10 — E1.M1 InputAndMovementRuntime · NS-1 — Epic 1 · Slice 1 — Movement and position sync
Decomposition E1.M1 — InputAndMovementRuntime

Delivery: Ship docs/plans/NS-23-implementation-plan.md on the same branch / PR as the NS-23 implementation (plan + code together), matching the NS-19 pattern.

Goal, scope, and out-of-scope

Goal: Deliver visible click-to-move: the avatar moves toward the verified server target using NavigationAgent3D + a baked mesh where applicable, instead of teleporting on the verifying GET. Aligns with E1.M1: path-follow baseline under server authority.

In scope

  • Godot: NavigationRegion3D (baked mesh) over the prototype walkable floor in main.tscn (obstacle geometry included in bake for future use); NavigationAgent3D on the player driving CharacterBody3D motion (move_and_slide toward next path position when the client is following the mesh — Godot 4.x pattern).
  • Pick integration: Reuse ground_pick.gd (walkable group, upward normal); clicked point becomes NavigationAgent3D.target_position (after any needed Y / agent height adjustment consistent with current capsule).
  • Server authority (unchanged contract): One MoveCommand per successful pick with the same world target as today (NS-16); server v1 snap + NS-19 validation unchanged.

Out of scope (per Jira)

  • Full prediction/reconciliation, MMO-grade netcode, server-side nav mesh validation.
  • Replacing InteractionRequest / range rules.

Locked — client nav vs server authority (this slice)

Topic Decision
When to POST Once per click, immediately with the picked destination (same JSON as today). No waypoint streaming to the server in this story.
Server truth Still snap to target on 200 after validation; GET /position after POST remains the reconciliation read (existing PositionAuthorityClient flow).
Straight-line vs path NS-19 validates displacement from current authoritative position to target (horizontal + vertical limits). It does not check path clearance through geometry. Client may follow nav waypoints when the descend bypass is inactive; automatic obstacle detours are not required for this slice (see Locked — prototype movement tradeoff).
After successful move Do not teleport the avatar on the verifying GET when the player is expected to walk: apply the authoritative position as a navigation goal and let the agent complete the path; initial boot sync_from_server remains a hard snap so spawn matches the server.
POST 400 Existing behavior: emit move_rejected, show NS-19 UX; do not start (or clear) a nav path to that target.
Distance limits Default MaxHorizontalStep (e.g. 18 m) remains compatible with single-command long clicks across the prototype; if a future scene needs shorter steps, chain commands or tune limits in a later story.

Locked — prototype movement tradeoff

Topic Decision
Uneven surfaces vs obstacles The client uses a descend bypass in player.gd: when the authoritative goal Y is below the body origin (typical for floor picks vs mid-capsule), steering goes straight in xz toward the goal instead of following nav waypoints first. This keeps stepped bumps smooth; it does not guarantee routing around tall static obstacles on a single click.
Player expectation Multi-click (or chained moves) is acceptable to navigate around geometry the client would otherwise bee-line into. No automatic obstacle navigation is required for NS-23 closure.
Residual polish Visible idle jitter at rest is tracked as tech debt (NS-24), not a blocker for this story.

Acceptance criteria checklist

  • Clicking a valid floor target produces continuous motion toward the verified target without sliding through geometry in the common case; navmesh waypoints are used when the descend bypass does not apply. Obstacle detours on one click are not required (tradeoff above).
  • Authoritative position still converges with the server (MoveCommand + GET); no regression on NS-19 rejection UX (reasonCode / label timeout).
  • Short note in server/README.md and/or client script header: client nav vs server authority for this slice.

Technical approach

  1. main.tscn / navigation

    • Add NavigationRegion3D covering walkable floor; include obstacle StaticBody3D geometry in baking as required by Godot 4 so paths wrap obstacles.
    • Bake after layout changes; document any editor step in client/README.md if bake is not fully reproducible from scene alone.
  2. Player / agent

    • Attach NavigationAgent3D; replace NS-14-style direct steering toward _goal with path following (velocity toward get_next_path_position() or equivalent, move_and_slide).
    • Keep snap_to_server for boot and any hard reconcile case you explicitly document (default: boot only).
  3. Wiring (main.gd / PositionAuthorityClient)

    • On target_chosen: call submit_move_target as today (POST → GET).
    • Distinguish authoritative position from boot vs after successful move so main.gd snaps on boot but sets nav target (no snap) after verify when implementing path-follow.
    • Optional small API: second signal, enum on existing signal, or sync_from_server-only snap path — pick the smallest change that stays readable per godot-client-script-organization.
  4. Documentation

    • client/README.md: Movement section: nav agent + server authority, and the uneven-surface vs obstacle tradeoff (multi-click around obstacles).
    • server/README.md: Server validates end target and step; client nav is presentational only (no server navmesh); obstacle detours not guaranteed.

Resolved — agent shape vs capsule (was risk)

Prototype player collider in main.tscn: CapsuleShape3D_playerradius = 0.4, height = 1.0.

Setting Locked choice
NavigationAgent3D.radius / height Match the physics capsule (0.4 / 1.0) so nav clearance matches what move_and_slide actually sweeps.
Navmesh bake agent In NavigationRegion3D bake settings, use the same radius/height (or Godots equivalent agent parameters) so baked polygons match runtime pathfinding.
Corner clipping vs “stuck” If the mesh clips into box corners, increase agent radius slightly and rebake (wider corridor on the mesh) before shrinking physics. Do not silently shrink the capsule without an explicit design change.
Vertical alignment Keep path_height_offset (or equivalent) at 0 unless the capsule foot position and floor snap misalign; the pick already supplies floor Y.
Arrival Align target_desired_distance (and related agent tolerances) with existing player ARRIVE_EPS (~0.35 m) so stop distance matches prior steering feel unless QA says otherwise.

Resolved — navigation sync / frame order (was risk)

Topic Locked choice
Where motion runs Path queries (get_next_path_position, velocity toward next point) and move_and_slide run only in Player._physics_process, not in _process or UI thread.
When target_position is set main.gd (or signal handler) may set the agent target in response to target_chosen / authority callbacks; the first consumable segment appears after the navigation map updates.
First frame after new target If NavigationAgent3D.is_navigation_finished() or a zero-length first step appears on the same frame as target_position assignment, defer one tick: await get_tree().physics_frame once after setting target or simply rely on next _physics_process (preferred — avoid await in hot path). Do not poll path from _input without deferral.
Map readiness After adding NavigationRegion3D, ensure the region is baked and enabled before F5; startup sync_from_server snap does not need a path.

Files to add (expected)

Path Purpose
None required by name Prefer extending existing player.gd / scene unless a dedicated player_navigation.gd reduces main.gd bloat.

Files to modify

Path Rationale
client/scenes/main.tscn NavigationRegion3D, bake source meshes, agent node setup.
client/scripts/player.gd Path-follow via NavigationAgent3D; clarify header (NS-14 → NS-23 evolution).
client/scripts/main.gd Boot snap vs post-move nav goal wiring.
client/scripts/position_authority_client.gd If needed: signal shape or phase so main can snap only on boot.
client/README.md Manual check steps for path-follow + server.
server/README.md Client nav vs authority note (AC).
docs/decomposition/modules/E1_M1_InputAndMovementRuntime.md Implementation snapshot when NS-23 ships.

Tests

Action What to cover
Manual (required) Server running; click-move toward targets (including stepped bumps); NS-19 reject targets — same rejection behavior as before; cold start — player matches server position. Optional: confirm multi-click can route around the prototype Obstacle when a single click bee-lines.
Automated None required for this story unless an existing headless hook can assert nav baking (unlikely); prefer manual for Godot nav.

Open questions / risks

Prototype tradeoff is explicit under Locked — prototype movement tradeoff (bumps vs single-click obstacle routing). Agent/bake alignment and physics-frame path consumption remain locked in Resolved — agent shape vs capsule and Resolved — navigation sync / frame order. Reopen if scene scale or avatar collider changes materially.

PR / review

Cross-check E1.M1, client README, and server README. Confirm NS-19 props in main.tscn still behave after nav region changes.