updated skills
This commit is contained in:
@@ -63,4 +63,3 @@ The coverage gate is 100/100/100/100 (istanbul). The package's
|
||||
`tests/setup/polyfills.ts` registers an in-memory `MeterProvider` before
|
||||
source modules import the OTel meter so the telemetry assertions resolve
|
||||
against a real exporter, with no mocks.
|
||||
|
||||
|
||||
@@ -85,30 +85,30 @@ type error in strict-mode consumers.
|
||||
## API Reference
|
||||
|
||||
- `CONTROLLER_CODES`
|
||||
- `INVALID_OPTIONS` — Zod boundary rejected the constructor options
|
||||
- `INVALID_OPTIONS` — Zod boundary rejected the constructor options
|
||||
bundle; `context.issues` carries the Zod issue array.
|
||||
- `INVALID_INPUT_KEY` — `setThreshold` (or future device adapters)
|
||||
- `INVALID_INPUT_KEY` — `setThreshold` (or future device adapters)
|
||||
received an input key not present in `CONTROLLER_INPUT_KEYS`.
|
||||
- `DESTROYED_INSTANCE` — operation attempted after `destroy()` on an
|
||||
- `DESTROYED_INSTANCE` — operation attempted after `destroy()` on an
|
||||
instance that does not silently no-op (strict-mode opt-in).
|
||||
- `KEYBOARD_TARGET_MISSING` — `KeyboardController.attach()` was
|
||||
- `KEYBOARD_TARGET_MISSING` — `KeyboardController.attach()` was
|
||||
called but neither an `EventTargetLike` was injected nor a global
|
||||
`window` was discoverable; `context.target` reports the resolved
|
||||
value.
|
||||
- `INVALID_KEY_MAP` — `KeyboardController` received a `keyMap` whose
|
||||
- `INVALID_KEY_MAP` — `KeyboardController` received a `keyMap` whose
|
||||
value is not one of `CONTROLLER_INPUT_KEYS`; `context.entry`
|
||||
carries the `[key, input]` tuple that failed validation.
|
||||
- `GAMEPAD_API_UNAVAILABLE` — `GamepadController.update()` was
|
||||
- `GAMEPAD_API_UNAVAILABLE` — `GamepadController.update()` was
|
||||
called but neither a `GamepadSource` was injected nor a global
|
||||
`navigator.getGamepads` was discoverable.
|
||||
- `INVALID_GAMEPAD_MAP` — `GamepadController` received a
|
||||
- `INVALID_GAMEPAD_MAP` — `GamepadController` received a
|
||||
`buttonMap`/`axisMap` whose value targets an input key not present
|
||||
in `CONTROLLER_INPUT_KEYS`; `context.entry` carries the failing
|
||||
tuple.
|
||||
- `DUPLICATE_CHILD` — `ControllerMerger.addController()` received a
|
||||
- `DUPLICATE_CHILD` — `ControllerMerger.addController()` received a
|
||||
child already attached, or a second child whose `id` collides with
|
||||
an existing one; `context` carries the child id.
|
||||
- `UNKNOWN_CHILD` — `ControllerMerger.removeController()` was called
|
||||
- `UNKNOWN_CHILD` — `ControllerMerger.removeController()` was called
|
||||
with a child that is not currently attached.
|
||||
- `ControllerCode` — literal union of every code string.
|
||||
- `class ControllerError extends SuaveplanError<ControllerCode>` —
|
||||
|
||||
@@ -98,15 +98,15 @@ console.log(gp.getValue("right")); // 191
|
||||
## API Reference
|
||||
|
||||
- `class GamepadController extends BaseController`
|
||||
- `constructor(options?: GamepadControllerOptions)`
|
||||
- `update(): void` — polls the source, diffs against last frame,
|
||||
- `constructor(options?: GamepadControllerOptions)`
|
||||
- `update(): void` — polls the source, diffs against last frame,
|
||||
pushes updates through `setState()`. Throws
|
||||
`ControllerError(GAMEPAD_API_UNAVAILABLE)` when no source is
|
||||
available. No-op after `destroy()`.
|
||||
- `getDeadzone(): number` — resolved deadzone.
|
||||
- `getPadIndex(): number` — bound pad index.
|
||||
- `getButtonMap(): GamepadButtonMap` — immutable button map.
|
||||
- `getAxisMap(): GamepadAxisMap` — immutable axis map.
|
||||
- `getDeadzone(): number` — resolved deadzone.
|
||||
- `getPadIndex(): number` — bound pad index.
|
||||
- `getButtonMap(): GamepadButtonMap` — immutable button map.
|
||||
- `getAxisMap(): GamepadAxisMap` — immutable axis map.
|
||||
- `DEFAULT_GAMEPAD_BUTTON_MAP: GamepadButtonMap` — W3C standard
|
||||
mapping.
|
||||
- `DEFAULT_GAMEPAD_AXIS_MAP: GamepadAxisMap` — W3C standard axis
|
||||
|
||||
@@ -91,15 +91,15 @@ kb.destroy();
|
||||
## API Reference
|
||||
|
||||
- `class KeyboardController extends BaseController`
|
||||
- `constructor(options?: KeyboardControllerOptions)`
|
||||
- `attach(): void` — bind `keydown`/`keyup` to the resolved
|
||||
- `constructor(options?: KeyboardControllerOptions)`
|
||||
- `attach(): void` — bind `keydown`/`keyup` to the resolved
|
||||
target. Throws `ControllerError(KEYBOARD_TARGET_MISSING)` when
|
||||
no target is available.
|
||||
- `detach(): void` — idempotent removal of listeners.
|
||||
- `getPressedKeys(): readonly string[]` — snapshot of physical
|
||||
- `detach(): void` — idempotent removal of listeners.
|
||||
- `getPressedKeys(): readonly string[]` — snapshot of physical
|
||||
keys currently held down (by `code` or `key` identifier).
|
||||
- `getKeyMap(): KeyboardKeyMap` — immutable key → input map.
|
||||
- `destroy(): void` — detaches listeners and clears state.
|
||||
- `getKeyMap(): KeyboardKeyMap` — immutable key → input map.
|
||||
- `destroy(): void` — detaches listeners and clears state.
|
||||
- `DEFAULT_KEYBOARD_KEY_MAP: KeyboardKeyMap` — frozen default map
|
||||
used when `options.keyMap` is omitted.
|
||||
- `interface KeyboardControllerOptions extends BaseControllerOptions`
|
||||
|
||||
@@ -114,27 +114,27 @@ merger.destroy(); // destroys padB as well
|
||||
## API Reference
|
||||
|
||||
- `class ControllerMerger extends BaseController`
|
||||
- `constructor(options?: ControllerMergerOptions)` — accepts every
|
||||
- `constructor(options?: ControllerMergerOptions)` — accepts every
|
||||
`BaseControllerOptions` field plus `children?: readonly BaseController[]`
|
||||
seeded eagerly via `addController()`.
|
||||
- `addController(child: BaseController): void` — subscribes to
|
||||
- `addController(child: BaseController): void` — subscribes to
|
||||
`onAnyInput` + `onCombo`, captures the current snapshot, marks the
|
||||
merger dirty. Throws `CONTROLLER.DUPLICATE_CHILD` on identity or
|
||||
`id` collision.
|
||||
- `removeController(child: BaseController): void` — disposes
|
||||
- `removeController(child: BaseController): void` — disposes
|
||||
subscriptions, drops the snapshot, marks dirty. Throws
|
||||
`CONTROLLER.UNKNOWN_CHILD` when the child is not attached.
|
||||
- `getChildren(): MergerChildren` — read-only snapshot of children
|
||||
- `getChildren(): MergerChildren` — read-only snapshot of children
|
||||
in insertion order.
|
||||
- `get childCount(): number` — current attachment count.
|
||||
- `update(): void` — polls every child, then runs the per-field max
|
||||
- `get childCount(): number` — current attachment count.
|
||||
- `update(): void` — polls every child, then runs the per-field max
|
||||
reduction over `CONTROLLER_INPUT_KEYS` and routes diffs through
|
||||
`updateInput` so threshold/deadzone gating + event emission stay
|
||||
consistent.
|
||||
- `destroy(): void` — disposes all subscriptions, destroys every
|
||||
- `destroy(): void` — disposes all subscriptions, destroys every
|
||||
child, clears local state, then defers to `BaseController.destroy`.
|
||||
- `interface ControllerMergerOptions extends BaseControllerOptions`
|
||||
- `children?: readonly BaseController[]` — initial seed.
|
||||
- `children?: readonly BaseController[]` — initial seed.
|
||||
- `type MergerChildren = readonly BaseController[]`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
@@ -88,4 +88,3 @@ bun run docs:check --filter=@suaveplan/ecs
|
||||
## License
|
||||
|
||||
MIT © Suaveplan Team
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ const snapshots = await /* meterProvider */;
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The meter is resolved at module load via `metrics.getMeter("@suaveplan/ecs")`. The OTel API guarantees a stable meter reference: when a real SDK is later registered, *previously created* instruments continue to emit through the new provider. This is the documented "no-op-then-bind" pattern.
|
||||
- The meter is resolved at module load via `metrics.getMeter("@suaveplan/ecs")`. The OTel API guarantees a stable meter reference: when a real SDK is later registered, _previously created_ instruments continue to emit through the new provider. This is the documented "no-op-then-bind" pattern.
|
||||
- The histogram unit is `"ms"`, matching `performance.now()`. The world records the wall-clock difference between the start and end of `execute(dt)`.
|
||||
- Counter attributes are keyed by `op`, not by code-path, so dashboards can sum spawn + despawn as a single series or split per-op trivially.
|
||||
- Tests should reset the in-memory exporter between cases (`testInMemoryMetricExporter.reset()`) to avoid cross-test bleed; the package's `world.spec-conformance.test.ts` does this in an `afterEach` hook.
|
||||
|
||||
@@ -13,7 +13,7 @@ The optional Zod validator opts the descriptor into runtime validation: `World.a
|
||||
## Features
|
||||
|
||||
- Deep-freeze on the descriptor and its `defaults` — strict-mode mutation throws.
|
||||
- Defaults are *copied* on construction, so caller mutations after `defineComponent` don't leak into the descriptor.
|
||||
- Defaults are _copied_ on construction, so caller mutations after `defineComponent` don't leak into the descriptor.
|
||||
- Optional `schema?: z.ZodType<T>` validator threaded through to the world's validation step.
|
||||
- specific addition over upstream's `defineComponent` — upstream lacked the Zod hook.
|
||||
|
||||
@@ -61,5 +61,5 @@ world.addComponent(entity, Health, { current: -1, max: 100 });
|
||||
|
||||
- The defaults clone is shallow (`{ ...defaults }`). For nested mutable references, callers should freeze deeply themselves before passing in. The component schema is identity-stable, but interior mutability of a passed reference is out of scope.
|
||||
- The schema property is added conditionally (omitted from the object when not supplied) so JSON serialisation of a descriptor produces a clean two-key shape when no validator is supplied.
|
||||
- Validation is *not* applied at registration — only at component-add / component-set boundaries. This keeps the registry decoupled from Zod and means a schema can be retrofitted onto an existing component without rewiring registration.
|
||||
- Validation is _not_ applied at registration — only at component-add / component-set boundaries. This keeps the registry decoupled from Zod and means a schema can be retrofitted onto an existing component without rewiring registration.
|
||||
- The factory does not generate IDs; the registry assigns those on first registration via `World.registerComponent` or implicitly during `addComponent`.
|
||||
|
||||
@@ -6,7 +6,7 @@ _(no submodules)_
|
||||
|
||||
## Purpose
|
||||
|
||||
A *system* is the unit of behaviour in an ECS — a function that runs over every entity matching a query and mutates component state. `defineSystem` is the factory that fills in sensible defaults (`phase: "update"`, `priority: 0`, `enabled: true`) and produces a frozen `SystemDefinition`. `SystemScheduler` is the world's internal sorted registry: it orders systems by phase then priority, lazily resorting only when the set changes.
|
||||
A _system_ is the unit of behaviour in an ECS — a function that runs over every entity matching a query and mutates component state. `defineSystem` is the factory that fills in sensible defaults (`phase: "update"`, `priority: 0`, `enabled: true`) and produces a frozen `SystemDefinition`. `SystemScheduler` is the world's internal sorted registry: it orders systems by phase then priority, lazily resorting only when the set changes.
|
||||
|
||||
Phase ordering is fixed at `preUpdate → update → postUpdate → render`, mirroring the canonical game-loop pipeline. Priority breaks ties within a phase, lowest first.
|
||||
|
||||
|
||||
@@ -163,4 +163,3 @@ full deferral table and rationale.
|
||||
- [`src/engine/game-loop/game-loop.md`](src/engine/game-loop/game-loop.md)
|
||||
- [`src/engine/plugin-manager/plugin-manager.md`](src/engine/plugin-manager/plugin-manager.md)
|
||||
- [`src/observability/otel.md`](src/observability/otel.md)
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ mgr.stopHeartbeatMonitor();
|
||||
|
||||
`new PlayerManager(deps, options?)` where `deps` is
|
||||
`{ clock, timers, logger, events }`. Options:
|
||||
|
||||
- `timeoutThresholdMs` (default 30000) — ms without heartbeat before a
|
||||
connected player is marked `timeout`.
|
||||
- `reconnectionGraceMs` (default 10000) — ms after a disconnect during
|
||||
|
||||
@@ -105,7 +105,7 @@ const match = engine.createMatchFromQueue(
|
||||
|
||||
The skill-based algorithm tries each candidate as an anchor and grows
|
||||
the group greedily until either `maxPlayers` is reached or no further
|
||||
peers fit inside the `skillRange`. The match returned is the *largest*
|
||||
peers fit inside the `skillRange`. The match returned is the _largest_
|
||||
viable group; on ties the first-found anchor wins, matching the
|
||||
upstream behaviour. The engine deliberately does not own the
|
||||
`QueueManager`'s "remove on match" semantics — `createMatchFromQueue`
|
||||
|
||||
@@ -101,7 +101,7 @@ const stats = qm.getStats();
|
||||
returns `[]` if not enough tickets are waiting; throws
|
||||
`INVALID_MATCH_CRITERIA` on a non-positive size.
|
||||
- `getQueueSize(queueKey): number` / `getQueuePosition(queueKey, playerId)`.
|
||||
- `getStats(): readonly QueueStats[]` — one entry per *known* queue key
|
||||
- `getStats(): readonly QueueStats[]` — one entry per _known_ queue key
|
||||
(preserved even after the queue empties, so dashboards see history).
|
||||
- `clear(): void` — drops every queue.
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ seeker waits. It is the matchmaking primitive that powers
|
||||
isolation when callers want fine-grained control over rating math, such
|
||||
as during round-by-round tournament seeding or AI bot pairing.
|
||||
|
||||
Two pieces of math drive the matcher: the *effective range* (base
|
||||
Two pieces of math drive the matcher: the _effective range_ (base
|
||||
`ratingRange` plus `waitTimeBonus * waitMinutes`, capped at `maxRange`)
|
||||
and the *score* (by default `Math.abs(seeker.rating - candidate.rating)`,
|
||||
and the _score_ (by default `Math.abs(seeker.rating - candidate.rating)`,
|
||||
overridable via the `score` dep so callers can plug in Glicko or
|
||||
TrueSkill). Tie-breaks across candidates with identical scores can be
|
||||
made deterministic by injecting an `Rng`.
|
||||
|
||||
@@ -110,6 +110,6 @@ rather than a live `MapIterator` to keep callers from mutating the
|
||||
internal map by accident. The `onSpectatorJoined` callback receives the
|
||||
fully-constructed `Spectator` (id + `joinedAt` + permissions) so a
|
||||
network fan-out caller does not need a second lookup to broadcast the
|
||||
join. Duplicate-spectator detection runs *before* cap enforcement so an
|
||||
join. Duplicate-spectator detection runs _before_ cap enforcement so an
|
||||
already-attached viewer never accidentally fails with
|
||||
`SPECTATOR_LIMIT_REACHED` when the session happens to be full.
|
||||
|
||||
Reference in New Issue
Block a user