d2d6df3655
Work-Package: M02-GMP-MOVEMENT-001 Agent: sindo-main-codex Tests: coordination and documentation gates; diff check Fidelity: documents preserved sandbox behavior and remaining build-12340 evidence gap
216 lines
11 KiB
Markdown
216 lines
11 KiB
Markdown
# Player Input
|
|
|
|
## Metadata
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Status | Partial |
|
|
| Target/work package | M02 / M02-GMP-INPUT-001, M02-GMP-MOVEMENT-001 consumer update |
|
|
| Owners | Gameplay input boundary; `sindo-main-codex` for current package |
|
|
| Last verified | `435e1c9`, 2026-07-14 |
|
|
| Profiles/capabilities | Current render-sandbox defaults; `Blizzlike335` binding semantics not yet verified |
|
|
|
|
## Purpose
|
|
|
|
Convert remappable Godot Input Map actions into an immutable, scene-free
|
|
`MoveIntent` that can be consumed by the current player controller and a later
|
|
independently testable local movement controller.
|
|
|
|
## Non-goals
|
|
|
|
- Define authoritative server movement, prediction or reconciliation.
|
|
- Own character transform, terrain queries, camera state or animation state.
|
|
- Claim that the current W/S/A/D sandbox mapping exactly matches build 12340.
|
|
- Make sprint or free flight available to the production compatibility profile;
|
|
their profile gate remains a later M02 package.
|
|
|
|
## Context and boundaries
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Devices[Keyboard and mouse] --> InputMap[Godot Input Map]
|
|
InputMap --> Source[PlayerInputSource]
|
|
Source --> Intent[MoveIntent]
|
|
Intent --> Movement[LocalPlayerMovementController]
|
|
Movement --> Controller[ThirdPersonWowController adapter]
|
|
Controller --> Camera[Current camera adapter]
|
|
```
|
|
|
|
Allowed dependencies:
|
|
|
|
- `PlayerInputSource` may read Godot's process-wide `Input` adapter.
|
|
- The scene controller may consume `MoveIntent` and camera-specific action names.
|
|
- `MoveIntent` may contain scalar axes and request flags only.
|
|
|
|
Forbidden dependencies:
|
|
|
|
- `MoveIntent` must not read `Input`, inherit `Node`/`Resource`, or contain world coordinates.
|
|
- The input source must not mutate player transforms, camera nodes or animation state.
|
|
- Renderer, terrain parsers and network codecs must not read Input Map actions.
|
|
|
|
## Public API
|
|
|
|
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|
|
|---|---|---|---|---|
|
|
| `MoveIntent` | Immutable value | Carries normalized forward, strafe and vertical axes plus the sandbox sprint request | Created and consumed on the main physics tick; caller retains reference | Constructor clamps axes; no I/O failure |
|
|
| `MoveIntent.has_translation()` | Query | Reports whether any translation axis is non-zero | Any thread while value remains immutable | None |
|
|
| `PlayerInputActions` | Constants | Provides stable action names shared by configuration and adapters | Process lifetime | Missing project actions are detected by verification |
|
|
| `PlayerInputSource.sample_move_intent()` | Adapter method | Samples configured actions for one physics tick | Main thread; returned intent is independent of adapter lifetime | Missing actions resolve to zero strength and are a configuration defect |
|
|
| `PlayerInputSource.compose_move_intent(...)` | Pure factory | Clamps strengths, cancels opposites and normalizes diagonal planar input | Any thread with scalar inputs | None |
|
|
|
|
## Inputs and outputs
|
|
|
|
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|
|
|---|---|---|---|---|---|
|
|
| Input | Movement action strengths | Godot `InputMap`/`Input` | `PlayerInputSource` | Engine-owned snapshot | Sampled on main physics tick |
|
|
| Input | Mouse action events | Godot `_unhandled_input` dispatch | Current scene controller | Engine-owned event | Current input dispatch only |
|
|
| Output | `MoveIntent` | `PlayerInputSource` | `LocalPlayerMovementController` through scene composition | Immutable caller-held value | One physics tick |
|
|
| Output | Camera action names | `PlayerInputActions` | Current scene controller | Static constants | Process lifetime |
|
|
|
|
Side effects:
|
|
|
|
- `PlayerInputSource` has no side effects beyond reading current engine input.
|
|
- The existing controller still mutates player/camera transforms and mouse mode;
|
|
those responsibilities are intentionally not moved by this package.
|
|
- No filesystem, cache, network, database or renderer-resource mutation occurs.
|
|
|
|
## Data flow
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Project[project.godot action defaults] --> Map[InputMap]
|
|
Remap[User or editor remapping] --> Map
|
|
Map -->|movement strengths| Source[PlayerInputSource]
|
|
Source -->|clamp cancel normalize| Intent[MoveIntent]
|
|
Intent --> Consumer[LocalPlayerMovementController]
|
|
Consumer --> Adapter[ThirdPersonWowController]
|
|
```
|
|
|
|
## Lifecycle/state
|
|
|
|
The adapter is stateless. The scene composition root creates one
|
|
`PlayerInputSource` and `LocalPlayerMovementController` in `_ready`; each physics
|
|
tick produces a new immutable intent. Flight enabled/disabled state is owned by
|
|
the movement controller and is not hidden in the input adapter.
|
|
|
|
## Main sequence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Engine as Godot Input
|
|
participant Source as PlayerInputSource
|
|
participant Intent as MoveIntent
|
|
participant Move as LocalPlayerMovementController
|
|
participant Player as ThirdPersonWowController adapter
|
|
Engine->>Source: movement action strengths
|
|
Source->>Intent: compose normalized axes and requests
|
|
Source-->>Player: sample_move_intent()
|
|
Player->>Move: advance(intent, selected basis, delta)
|
|
Move-->>Player: displacement and velocity state
|
|
```
|
|
|
|
## Ownership, threading and resources
|
|
|
|
- The controller owns the input-source reference for its scene lifetime.
|
|
- Each `MoveIntent` is immutable and retained only by its consumer.
|
|
- Sampling and controller mutation occur on the main physics thread.
|
|
- The module creates no Nodes, Resources, RIDs, workers or background jobs.
|
|
- Input Map definitions are project configuration; runtime remapping remains
|
|
engine-owned and does not mutate the domain value.
|
|
|
|
## Errors, cancellation and recovery
|
|
|
|
| Failure | Detection | Behavior | Diagnostic | Recovery |
|
|
|---|---|---|---|---|
|
|
| Required action missing | Headless contract verifier | Godot returns zero input for that action | `PLAYER_INPUT` failure naming action | Restore action or user mapping |
|
|
| Conflicting opposite actions | Pure composition | Strengths cancel deterministically | Covered by contract test | Release/remap one action |
|
|
| Strength outside range | Pure composition | Value clamps to `[0, 1]` before axis calculation | Covered by contract test | Input adapter may be corrected independently |
|
|
| Controller disabled/unloaded | Scene lifecycle | Sampling stops with physics processing | Existing scene lifecycle | Re-enable or recreate scene |
|
|
|
|
There is no asynchronous operation to cancel and no persisted state to roll back.
|
|
|
|
## Configuration and capabilities
|
|
|
|
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|
|
|---|---|---|---|---|
|
|
| Forward/backward | Physical W/S | Current render sandbox | Yes, through Input Map | Produces signed forward axis |
|
|
| Strafe left/right | Physical A/D | Current render sandbox | Yes | Produces signed right axis |
|
|
| Camera rotate | Right mouse button | Current render sandbox | Yes | Captures/releases mouse for orbit |
|
|
| Camera zoom | Mouse wheel | Current render sandbox | Yes | Adjusts current camera distance |
|
|
| Debug sprint | Shift | Sandbox debug only by intent; profile gate pending | Yes | Requests existing speed multiplier |
|
|
| Debug flight up/down/toggle | E/Q/Space | Sandbox debug only by intent; profile gate pending | Yes | Requests existing free-flight controls |
|
|
|
|
## Persistence, cache and migration
|
|
|
|
The action names are additive project configuration. No saved binding schema,
|
|
cache or migration exists yet. Renaming an action is a contract change and must
|
|
provide a settings migration once user settings are persisted.
|
|
|
|
## Diagnostics and observability
|
|
|
|
- Logs: dedicated verifier emits `PLAYER_INPUT PASS` or one failure per invariant.
|
|
- Metrics: none; sampling cost is one small value allocation per physics tick.
|
|
- Debug views: none.
|
|
- Correlation IDs: not applicable to local synchronous input.
|
|
|
|
## Verification
|
|
|
|
- Unit/contract tests: `src/tools/verify_player_input.gd` validates all actions,
|
|
default bindings, axis cancellation/clamping/normalization and debug request flags.
|
|
- Integration/E2E: `src/tests/scenes/player_input_regression.tscn` is loaded by
|
|
the verifier, which drives forward, camera-zoom and immediate sandbox-flight
|
|
actions through the real scene controller and also rejects direct
|
|
physical-key polling.
|
|
- Fidelity evidence: current sandbox defaults retain W/S/A/D, E/Q, Shift, Space,
|
|
right mouse and wheel bindings. This is observable-regression coverage only.
|
|
- Performance budgets: no I/O, jobs or scene lookup per sample; one immutable
|
|
intent allocation per physics tick.
|
|
- Manual diagnostics: run the Eastern Kingdoms scene and remap one movement
|
|
action in Project Settings before checking movement.
|
|
|
|
## Extension points
|
|
|
|
- A gamepad/accessibility adapter may call `compose_move_intent` without changing
|
|
movement consumers.
|
|
- A later input-context owner may suppress gameplay actions while preserving the
|
|
same intent contract.
|
|
- A future server-aware movement predictor may replace the current local
|
|
movement consumer without changing `PlayerInputSource`.
|
|
|
|
## Capability status
|
|
|
|
| Capability | Status | Evidence | Gap/next step |
|
|
|---|---|---|---|
|
|
| Remappable current sandbox controls | Implemented | `verify_player_input.gd` action/default checks | Add persisted user binding settings later |
|
|
| `PlayerInputSource → MoveIntent` seam | Implemented | Pure composition and controller boundary checks | Local movement consumer is now extracted |
|
|
| Exact 3.3.5a default semantics | Unknown | No original-client binding fixture in this package | Capture build-12340 defaults and mouse-turn/strafe policy |
|
|
| Production profile exclusion of sprint/flight | Partial | Debug actions are named explicitly | Add typed profile/capability gate in M02 |
|
|
| Jump/fall/swim/fly gameplay semantics | Planned | M02 target and gameplay roadmap | Implement after terrain/movement state extraction |
|
|
|
|
## Known gaps and risks
|
|
|
|
- The defaults preserve the pre-M02 sandbox behavior, not verified original-client semantics.
|
|
- Sprint and free flight are still executable by the sandbox controller until a
|
|
typed build-profile gate is introduced.
|
|
- Camera input is action-mapped but camera state/collision remains in the monolithic controller.
|
|
- There is no persisted user keybinding format or UI yet.
|
|
|
|
## Source map
|
|
|
|
| Path | Responsibility |
|
|
|---|---|
|
|
| `project.godot` | Additive default Input Map bindings |
|
|
| `src/domain/input/move_intent.gd` | Immutable locomotion request contract |
|
|
| `src/gameplay/input/player_input_actions.gd` | Stable project action names |
|
|
| `src/gameplay/input/player_input_source.gd` | Engine Input Map adapter and pure composition |
|
|
| `src/gameplay/movement/local_player_movement_controller.gd` | Scene-free movement consumer |
|
|
| `src/scenes/player/third_person_wow_controller.gd` | Composition and camera/input-event adapter |
|
|
| `src/tests/scenes/player_input_regression.tscn` | Asset-free controller movement/camera regression fixture |
|
|
| `src/tools/verify_player_input.gd` | Headless contract/integration regression |
|
|
|
|
## Related decisions and references
|
|
|
|
- ADR: none; this implements the existing architecture and M02 seam.
|
|
- Upstream/reference: `targets/02-player-decomposition.md`,
|
|
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`.
|