@adatechnology/game-test-room 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,6 +68,27 @@ const report = room.loadModule(RENTAL_CARS) // { module, items, scenarios, ok, p
68
68
 
69
69
  The items join the catalogue under the module's group and its scenarios become buttons. The contract checks run at once and write one line per result to the log: `checkDestructible` for every item with an `impl`, `checkAnimation` for every clip (`✓ Rental car: destructible contract ok (4 parts...)` or `✗ ...: <problem>`). The animations panel lists the clips of the aimed or held item; `room.playAnimation(clipId)` plays one (poses are offsets from the rest pose, events go to the log and `sound` events to `onSound`). The example (`examples/test-room`) loads a `DEMO_MODULE` rental car that owns its damage rule, its door `open` clip and a wheel that comes off with a wrench.
70
70
 
71
+ ## Module bench
72
+
73
+ A module that declares `controls`, `layers` or `metrics` (see `@adatechnology/game-contracts`) gets a bench: `loadModule` checks it with
74
+ `checkBench` (a `✓ ... bench ok` / `✗ ... bench broken` line), lends `host.bench` to `attach`, then sends one change per control and layer. The
75
+ panel between the animations and the tests shows the active module (the one of the last `openArea`; else the first loaded with a bench, a later module never takes the panel from the one on screen): a
76
+ range per number, a checkbox per boolean, buttons (up to four options) or a select per choice, a button per action, the layers, and the
77
+ module counters at 4 Hz with a bar when they have a budget (`warn` from 80 %, `bad` past it). A compact line on top shows the room counters
78
+ (`fps`, `frame_ms`, `draw_calls`, `triangles`, `objects`, `fragments`, `particles`, `decals`); `fps` uses the real frame time, so slow motion
79
+ does not change it.
80
+
81
+ ```ts
82
+ const room = createTestRoom({ ..., metrics: ['fps', 'draw_calls'], bench: { state: { lab: params.get('bench.lab') ?? '' }, onChange: (moduleId, encoded) => keepInUrl(moduleId, encoded) } })
83
+ room.bench()?.set('size', 2) // clamped and snapped; false for an unknown id, a wrong type or an option not offered
84
+ room.metrics() // { room: { fps, draw_calls, ... }, module: { particles: 120 } }
85
+ ```
86
+
87
+ The state is `id=value;id=value` (`encodeBenchState` / `decodeBenchState`); decoding treats it as hostile input. `bench.state` is keyed by
88
+ module id, so two modules can share a control id; a module loaded again keeps its live values. The room never touches
89
+ `location`: the example page keeps it in `?bench.<moduleId>=`. `fps` and `frame_ms` use the real frame time, even past the 50 ms the simulation is clamped to. Loading a module again detaches the previous `attach` first. Q now reports the new fire
90
+ mode (`mode` event) and `shot` events carry `rounds` and `mode`.
91
+
71
92
  ## Controls
72
93
 
73
94
  | Input | Action |
@@ -110,6 +131,9 @@ Uma sala de testes em primeira pessoa para three.js. Você passa o seu catálogo
110
131
  derrama líquidos, e lê o que aconteceu em linguagem simples no registro de comportamento (também exportável em JSON, NDJSON ou texto). Os cenários
111
132
  automáticos conferem o resultado e gravam aprovado/reprovado no mesmo registro. Use `locale: 'pt-BR'` para textos em português.
112
133
 
134
+ Um módulo que declara controles, camadas ou contadores ganha um painel de bancada genérico; o estado vai para a URL do exemplo (`?bench.<idDoMódulo>=`) e
135
+ `room.bench()` / `room.metrics()` dirigem e leem a bancada por script.
136
+
113
137
  ## Known limitations
114
138
 
115
139
  The quality select does not recreate the destruction renderer; it only changes the room's own settings. Unit tests use an injected stub renderer (no WebGL).
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ import { SpecFromObjectOptions, DestructionRenderer, Quality } from '@adatechnol
5
5
  import { Arsenal, WeaponState, WeaponDef } from '@adatechnology/game-weapons';
6
6
  import { FluidRenderer } from '@adatechnology/game-fluids-three';
7
7
  import { ContainerSpec, FluidWorld, LiquidDef, ItemUseDef, SurfaceSpec } from '@adatechnology/game-fluids';
8
- import { DestructibleImpl, AnimationClipSpec, TestableModule } from '@adatechnology/game-contracts';
8
+ import { ModuleControl, ControlValue, ModuleLayer, DestructibleImpl, AnimationClipSpec, TestableModule } from '@adatechnology/game-contracts';
9
9
  export { AnimationClipSpec, DestructibleImpl, ItemAnimationContract, ModuleScenario, TestableItem, TestableModule } from '@adatechnology/game-contracts';
10
10
 
11
11
  type NewEntry = NewLogEntry;
@@ -186,6 +186,40 @@ interface TestRoomOptions {
186
186
  onSound?: (key: string, point: Vec3, volume: number) => void;
187
187
  /** Show the built-in HUD and panels (default true). */
188
188
  ui?: boolean;
189
+ /** Room counters the bench panel shows and `TestRoom.metrics()` returns (default all); `false` for none. */
190
+ metrics?: readonly RoomMetricId[] | false;
191
+ /**
192
+ * Module bench state. `state` maps a module id to its encoded state, restored when that module is first loaded (a module reloaded
193
+ * keeps its live state instead) before its first changes are sent; ids are per module, so two modules may share a control id.
194
+ * `onChange` gets the encoded state of a module whenever it changes (a page keeps it in its URL). The room never touches `location`.
195
+ */
196
+ bench?: {
197
+ readonly state?: Readonly<Record<string, string>>;
198
+ readonly onChange?: (moduleId: string, encoded: string) => void;
199
+ };
200
+ }
201
+ /** Counters the room measures itself. `fps` and `frame_ms` use the real frame time, so slow motion does not change them. */
202
+ type RoomMetricId = 'fps' | 'frame_ms' | 'draw_calls' | 'triangles' | 'objects' | 'fragments' | 'particles' | 'decals';
203
+ /** A module control as the room holds it: the declaration with the module's patches applied, and its value (none for an action). */
204
+ type BenchControl = ModuleControl & {
205
+ readonly disabled: boolean;
206
+ readonly value?: ControlValue;
207
+ };
208
+ type BenchLayer = ModuleLayer & {
209
+ readonly on: boolean;
210
+ };
211
+ /** The bench of a loaded module, driven from outside the panel (tests, scripts, a page that keeps the state in its URL). */
212
+ interface BenchHandle {
213
+ controls(): BenchControl[];
214
+ layers(): BenchLayer[];
215
+ value(id: string): ControlValue | undefined;
216
+ layer(id: string): boolean;
217
+ /** Numbers are clamped and snapped to the step. False for an unknown id, a wrong type, an option not offered or a disabled control. */
218
+ set(id: string, value: ControlValue): boolean;
219
+ setLayer(id: string, on: boolean): boolean;
220
+ act(id: string): boolean;
221
+ /** `id=value;id=value`, the format `TestRoomOptions.bench.state[moduleId]` reads back. */
222
+ encode(): string;
189
223
  }
190
224
  interface SpawnedObject {
191
225
  objectId: string;
@@ -273,6 +307,13 @@ interface TestRoom {
273
307
  * offsets from the rest pose, interpolated linearly; the item keeps the last pose. Events are logged (and `sound` events reach `onSound`).
274
308
  */
275
309
  playAnimation(clipId: string, objectId?: string): boolean;
310
+ /** The bench of a loaded module (default the active one: the module of the last `openArea`, else the last loaded with a bench), or null. */
311
+ bench(moduleId?: string): BenchHandle | null;
312
+ /** Room counters and the active module's metrics, read now. */
313
+ metrics(): {
314
+ room: Partial<Record<RoomMetricId, number>>;
315
+ module: Record<string, number | string>;
316
+ };
276
317
  dispose(): void;
277
318
  }
278
319
  /** What `TestRoom.loadModule` did. */
@@ -437,4 +478,9 @@ interface BuildFromSpecOptions {
437
478
  */
438
479
  declare function buildFromSpec(THREE: typeof THREE_NS, spec: DestructibleSpec, options?: BuildFromSpecOptions): THREE_NS.Group;
439
480
 
440
- export { type AnimationInfo, type BuildFromSpecOptions, DEMO_ITEMS, DEMO_MATERIALS, type HudState, type InspectInfo, type LogEntry, MATERIAL_COLORS, type ModuleReport, type NewEntry, type NewLogEntry, type RoomStrings, SCENARIOS, type ScenarioContext, type ScenarioReport, type SpawnedObject, type TestRoom, type TestRoomItem, type TestRoomLocale, type TestRoomLog, type TestRoomOptions, type TestRoomScenario, type ThreeNamespace, type WritableLog, brickWallSpec, buildFromSpec, createLog, createTestRoom, demoItems, locales, makeScenarios, resolveStrings, scenariosFor };
481
+ /** `id=value;id=value`, values with `encodeURIComponent`, booleans and layers as `1` / `0`. */
482
+ declare function encodeBenchState(controls: readonly BenchControl[], layers: readonly BenchLayer[]): string;
483
+ /** Reads an encoded state as hostile input: unknown ids, NaN, options not offered and malformed pairs are dropped; numbers are clamped. */
484
+ declare function decodeBenchState(encoded: string, controls: readonly ModuleControl[], layers: readonly ModuleLayer[]): Map<string, ControlValue>;
485
+
486
+ export { type AnimationInfo, type BenchControl, type BenchHandle, type BenchLayer, type BuildFromSpecOptions, DEMO_ITEMS, DEMO_MATERIALS, type HudState, type InspectInfo, type LogEntry, MATERIAL_COLORS, type ModuleReport, type NewEntry, type NewLogEntry, type RoomMetricId, type RoomStrings, SCENARIOS, type ScenarioContext, type ScenarioReport, type SpawnedObject, type TestRoom, type TestRoomItem, type TestRoomLocale, type TestRoomLog, type TestRoomOptions, type TestRoomScenario, type ThreeNamespace, type WritableLog, brickWallSpec, buildFromSpec, createLog, createTestRoom, decodeBenchState, demoItems, encodeBenchState, locales, makeScenarios, resolveStrings, scenariosFor };