Files
2026-05-27 23:29:53 +01:00

4.6 KiB
Raw Permalink Blame History

OpenSpec Change: enterprise-gaming-controller

  • Change ID: enterprise-gaming-controller
  • Package: @suaveplan/controller
  • Category: gaming/engine
  • Target path: gaming/engine/controller
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/controller.md

Summary

Deliver @suaveplan/controller on the genesis branch from scratch at enterprise quality. The package provides controller abstractions — a unified input layer that normalises keyboard, mouse, gamepad (Web Gamepad API), touch, and virtual on-screen controls into a single ActionMap. Actions are named bindings mapped to digital (pressed/released) and analog (axis) values. A ControllerManager polls inputs each tick, resolves conflicts, applies deadzone and sensitivity curves, and fires @suaveplan/events action events. Genesis adds Zod binding schemas, structured errors, OTel input-latency histogram, and 100% coverage.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) ControllerError; CONTROLLER.* codes
@suaveplan/types (workspace:^) Clock
@suaveplan/events (workspace:^) Action-pressed/released events
zod (catalog:forms) Binding config schemas
@opentelemetry/api (catalog:observability) Input-latency histogram

Public API surface

function createControllerManager(options?: ControllerManagerOptions): ControllerManager

interface ControllerManagerOptions {
  deadzone?: number          // analog deadzone 0–1, default 0.1
  bindings?: ActionBinding[]
  clock?: Clock
}

interface ControllerManager {
  registerBinding(binding: ActionBinding): void
  removeBinding(action: string): void
  isPressed(action: string): boolean
  getAxis(action: string): number       // -1 to 1
  poll(dt?: number): void               // called each game tick
  destroy(): void
}

type InputSource = 'keyboard' | 'mouse' | 'gamepad' | 'touch' | 'virtual'

interface ActionBinding {
  action: string
  source: InputSource
  key?: string              // keyboard: e.g. 'KeyW', 'Space'
  button?: number           // gamepad button index
  axis?: number             // gamepad axis index
  mouseButton?: number      // 0=left, 1=middle, 2=right
  touchId?: number
  invert?: boolean
  sensitivity?: number
}

// Keyboard/mouse adapter (for environments with DOM)
function createKeyboardAdapter(): InputAdapter
function createGamepadAdapter(): InputAdapter
function createVirtualAdapter(): InputAdapter & { setAxis(action: string, value: number): void }

interface InputAdapter {
  readonly source: InputSource
  isPressed(key: string | number): boolean
  getAxis(index: number): number
  attach(): void
  detach(): void
}

Error codes

type ControllerErrorCode =
  | 'CONTROLLER_ACTION_NOT_FOUND'
  | 'CONTROLLER_DUPLICATE_BINDING'
  | 'CONTROLLER_INVALID_AXIS'
  | 'CONTROLLER_ADAPTER_ERROR'

New additions for genesis

  1. Multi-source conflict resolution — when multiple sources bind the same action, the last non-zero value wins.
  2. Sensitivity curves — linear, quadratic, and cubic response curves for analog axes.
  3. Virtual adapter — headless adapter for touch/on-screen controls; state driven by caller (used by @suaveplan/react-controller).
  4. OTel — controller.poll_duration_ms histogram; controller.active_bindings gauge.

File layout

gaming/engine/controller/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    core/
      controller-manager/
        controller-manager.ts
        controller-manager.md
        controller-manager.types.ts
        controller-manager.test.ts
        index.ts
    adapters/
      keyboard-adapter/
        keyboard-adapter.ts
        keyboard-adapter.md
        keyboard-adapter.test.ts
        index.ts
      gamepad-adapter/
        gamepad-adapter.ts
        gamepad-adapter.md
        gamepad-adapter.test.ts
        index.ts
      virtual-adapter/
        virtual-adapter.ts
        virtual-adapter.md
        virtual-adapter.test.ts
        index.ts
    utils/
      sensitivity.ts
      sensitivity.md
      sensitivity.test.ts
  tests/
    integration.test.ts

Impact

Downstream: @suaveplan/react-controller wraps virtual adapter in React hooks; @suaveplan/r3f-controller drives Three.js camera/character from action map. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events.