99 lines
7.3 KiB
Markdown
99 lines
7.3 KiB
Markdown
# NS-23 — Implementation plan
|
|
|
|
## Story reference
|
|
|
|
| Field | Value |
|
|
|--------|--------|
|
|
| **Key** | NS-23 |
|
|
| **Title** | E1.M1: Client path-follow baseline (navigation + visible locomotion) |
|
|
| **Jira** | [NS-23](https://neon-sprawl.atlassian.net/browse/NS-23) |
|
|
| **Parent context** | [NS-10 — E1.M1 InputAndMovementRuntime](https://neon-sprawl.atlassian.net/browse/NS-10) · [NS-1 — Epic 1](https://neon-sprawl.atlassian.net/browse/NS-1) · Slice 1 — Movement and position sync |
|
|
| **Decomposition** | [E1.M1 — InputAndMovementRuntime](../decomposition/modules/E1_M1_InputAndMovementRuntime.md) |
|
|
|
|
**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 **follows a path** toward the clicked point using a **navigation mesh** baseline (obstacle-aware), not only steering at a goal or snapping on server ack alone. Aligns with E1.M1: path-follow baseline under server authority.
|
|
|
|
**In scope**
|
|
|
|
- **Godot:** `NavigationRegion3D` (baked mesh) over the prototype walkable floor in `main.tscn`, respecting existing static obstacles; **`NavigationAgent3D`** (on player or child node) driving **`CharacterBody3D`** motion (`move_and_slide` toward next path position — follow Godot 4.x recommended 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](NS-16-implementation-plan.md)); server **v1 snap** + **[NS-19](NS-19-implementation-plan.md)** 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 nav** is responsible for **visible** obstacle avoidance. |
|
|
| **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. |
|
|
|
|
## Acceptance criteria checklist
|
|
|
|
- [ ] Clicking a valid floor target produces **continuous motion** along a plausible path around static obstacles in the prototype scene (not sliding through walls).
|
|
- [ ] 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](../../.cursor/rules/godot-client-script-organization.md).
|
|
|
|
4. **Documentation**
|
|
- **`client/README.md`:** Update movement section: navmesh path-follow + server authority one-liner.
|
|
- **`server/README.md`:** One short subsection or bullet: server validates **end target** and **step**; **client** nav is **presentational** for obstacle following in this slice.
|
|
|
|
## 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 around obstacles — path goes around; click NS-19 reject targets — same rejection behavior as before; cold start — player matches server position. |
|
|
| **Automated** | None required for this story unless an existing headless hook can assert nav baking (unlikely); prefer manual for Godot nav. |
|
|
|
|
## Open questions / risks
|
|
|
|
- **Agent radius / height** vs capsule: mis-tuning causes clipping or “stuck”; match **`CollisionShape3D`** to agent parameters.
|
|
- **Frame ordering:** `NavigationServer` sync — follow Godot docs (`await get_tree().physics_frame` if required before first path query).
|
|
|
|
## PR / review
|
|
|
|
Cross-check [E1.M1](../decomposition/modules/E1_M1_InputAndMovementRuntime.md), [client README](../../client/README.md), and [server README](../../server/README.md). Confirm NS-19 props in `main.tscn` still behave after nav region changes.
|