@realnation/builder-shared-sdk 1.0.4 → 1.1.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 +6 -0
- package/dist/events.d.ts +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +1 -3
- package/dist/flow.d.ts.map +1 -1
- package/dist/flow.js +12 -4
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/oss.d.ts.map +1 -1
- package/dist/oss.js +7 -4
- package/dist/runtime/capability.d.ts +45 -0
- package/dist/runtime/capability.d.ts.map +1 -0
- package/dist/runtime/capability.js +59 -0
- package/dist/runtime/connection.d.ts +92 -0
- package/dist/runtime/connection.d.ts.map +1 -0
- package/dist/runtime/connection.js +247 -0
- package/dist/runtime/decode.d.ts +40 -0
- package/dist/runtime/decode.d.ts.map +1 -0
- package/dist/runtime/decode.js +106 -0
- package/dist/runtime/facade.d.ts +165 -0
- package/dist/runtime/facade.d.ts.map +1 -0
- package/dist/runtime/facade.js +354 -0
- package/dist/runtime/index.d.ts +42 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +70 -0
- package/dist/runtime/mock.d.ts +58 -0
- package/dist/runtime/mock.d.ts.map +1 -0
- package/dist/runtime/mock.js +276 -0
- package/dist/runtime/protocol.d.ts +231 -0
- package/dist/runtime/protocol.d.ts.map +1 -0
- package/dist/runtime/protocol.js +19 -0
- package/dist/runtime/vue/index.d.ts +26 -0
- package/dist/runtime/vue/index.d.ts.map +1 -0
- package/dist/runtime/vue/index.js +47 -0
- package/dist/runtime/vue/player.d.ts +49 -0
- package/dist/runtime/vue/player.d.ts.map +1 -0
- package/dist/runtime/vue/player.js +95 -0
- package/package.json +24 -3
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QikSense Runtime SDK — the single entry point for web experiences.
|
|
3
|
+
*
|
|
4
|
+
* Games write `runtime.body.onUpdate(...)`, never `socket.onmessage`. Protocol,
|
|
5
|
+
* reconnect, heartbeat and capability negotiation all live below this line.
|
|
6
|
+
*
|
|
7
|
+
* Framework-free by construction. The Vue adapter is a separate subpath
|
|
8
|
+
* (`@realnation/builder-shared-sdk/runtime/vue`) so Canvas/WebGL experiences,
|
|
9
|
+
* tooling and non-Vue hosts never pull in a reactivity system they do not want.
|
|
10
|
+
*/
|
|
11
|
+
import { WsConnection } from './connection.js';
|
|
12
|
+
import { toConnectStatus } from './capability.js';
|
|
13
|
+
import { RuntimeFacade } from './facade.js';
|
|
14
|
+
import { MockTransport } from './mock.js';
|
|
15
|
+
export class Runtime extends RuntimeFacade {
|
|
16
|
+
/** Named for the handshake result, not the runtime.status command above it. */
|
|
17
|
+
lastConnectStatus = null;
|
|
18
|
+
constructor(transport, timers) {
|
|
19
|
+
super(transport, {
|
|
20
|
+
setTimer: timers?.setTimer,
|
|
21
|
+
clearTimer: timers?.clearTimer,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Opens the connection and returns the runtime's verdict.
|
|
26
|
+
*
|
|
27
|
+
* Waits for exactly one message type. The protocol guarantees hello is always
|
|
28
|
+
* answered with ready — success or not — so there is no race between "wait for
|
|
29
|
+
* ready" and "wait for an error message".
|
|
30
|
+
*/
|
|
31
|
+
async connect() {
|
|
32
|
+
const ready = await this.transport.connect();
|
|
33
|
+
this.lastConnectStatus = toConnectStatus(ready);
|
|
34
|
+
return this.lastConnectStatus;
|
|
35
|
+
}
|
|
36
|
+
/** The last handshake result. Null before connect() resolves. */
|
|
37
|
+
get connectStatus() {
|
|
38
|
+
return this.lastConnectStatus;
|
|
39
|
+
}
|
|
40
|
+
get state() {
|
|
41
|
+
return this.transport.state;
|
|
42
|
+
}
|
|
43
|
+
close() {
|
|
44
|
+
this.dispose();
|
|
45
|
+
this.transport.close();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export function createRuntime(options = {}) {
|
|
49
|
+
const transport = options.source === 'mock'
|
|
50
|
+
? new MockTransport({
|
|
51
|
+
require: options.require,
|
|
52
|
+
fps: options.mockFps,
|
|
53
|
+
setTimer: options.setTimer,
|
|
54
|
+
clearTimer: options.clearTimer,
|
|
55
|
+
now: options.now,
|
|
56
|
+
})
|
|
57
|
+
: new WsConnection(options);
|
|
58
|
+
return new Runtime(transport, options);
|
|
59
|
+
}
|
|
60
|
+
/* --- public surface -------------------------------------------------------
|
|
61
|
+
* Named exports only. `export *` would collide with the package root's generic
|
|
62
|
+
* names (Capability, Pointer) the moment anyone imports both.
|
|
63
|
+
*/
|
|
64
|
+
export { WsConnection, defaultUrl } from './connection.js';
|
|
65
|
+
export { MockTransport } from './mock.js';
|
|
66
|
+
export { RuntimeFacade } from './facade.js';
|
|
67
|
+
export { toConnectStatus, explainMiss, describeGate } from './capability.js';
|
|
68
|
+
export { decodeSilhouette, parseBinaryFrame, fnv1a64, binaryCorrelationId, QSBN_HEADER_BYTES } from './decode.js';
|
|
69
|
+
export { RuntimeCommandError } from './facade.js';
|
|
70
|
+
export { SCHEMA_HASH, RUNTIME_URL, GRID_W, GRID_H } from './protocol.js';
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synthetic source. Same envelope as real hardware, no device required.
|
|
3
|
+
*
|
|
4
|
+
* Principle: the mock may be stricter than the real runtime, never looser.
|
|
5
|
+
* A permissive mock lets code pass here and fail on hardware — the most
|
|
6
|
+
* expensive failure mode there is, because it defers the bug to the one
|
|
7
|
+
* environment where a camera is plugged in.
|
|
8
|
+
*
|
|
9
|
+
* Concretely that means: it answers pong but never sends ping (heartbeat
|
|
10
|
+
* direction matches), and it emits ready.requested and device:connected.config
|
|
11
|
+
* even though nothing here reads them.
|
|
12
|
+
*/
|
|
13
|
+
import type { ConnectionState, Transport } from './connection.js';
|
|
14
|
+
import type { Capability, ClientMessage, HelloMessage, ReadyMessage, ServerMessage } from './protocol.js';
|
|
15
|
+
export interface MockOptions {
|
|
16
|
+
require?: HelloMessage['require'];
|
|
17
|
+
fps?: number;
|
|
18
|
+
/** Capabilities the fake runtime claims. Defaults to whatever was requested. */
|
|
19
|
+
provides?: Capability['name'][];
|
|
20
|
+
setTimer?: (fn: () => void, ms: number) => unknown;
|
|
21
|
+
clearTimer?: (h: unknown) => void;
|
|
22
|
+
now?: () => number;
|
|
23
|
+
/** Drives frames manually instead of on a timer. For deterministic tests. */
|
|
24
|
+
manual?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare class MockTransport implements Transport {
|
|
27
|
+
private messageHandlers;
|
|
28
|
+
private binaryHandlers;
|
|
29
|
+
private stateHandlers;
|
|
30
|
+
private timerHandle;
|
|
31
|
+
private _state;
|
|
32
|
+
private frame;
|
|
33
|
+
private readonly opts;
|
|
34
|
+
/** Set once ping has been observed, so tests can assert direction. */
|
|
35
|
+
pingsReceived: number;
|
|
36
|
+
constructor(options?: MockOptions);
|
|
37
|
+
get state(): ConnectionState;
|
|
38
|
+
private setState;
|
|
39
|
+
connect(): Promise<ReadyMessage>;
|
|
40
|
+
private schedule;
|
|
41
|
+
/** Emits one frame's worth of events. Public so `manual: true` tests can step. */
|
|
42
|
+
tick(): void;
|
|
43
|
+
private makeBody;
|
|
44
|
+
private makePointer;
|
|
45
|
+
private makeSilhouette;
|
|
46
|
+
send(msg: ClientMessage): void;
|
|
47
|
+
private answer;
|
|
48
|
+
/**
|
|
49
|
+
* Public so tests and demos can inject an exact frame. Real sessions never
|
|
50
|
+
* call it — the mock drives itself from tick().
|
|
51
|
+
*/
|
|
52
|
+
emit(msg: ServerMessage): void;
|
|
53
|
+
onMessage(fn: (m: ServerMessage) => void): () => void;
|
|
54
|
+
onBinary(fn: (d: ArrayBuffer) => void): () => void;
|
|
55
|
+
onStateChange(fn: (s: ConnectionState) => void): () => void;
|
|
56
|
+
close(): void;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=mock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/runtime/mock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,KAAK,EAEV,UAAU,EACV,aAAa,EAGb,YAAY,EAGZ,YAAY,EAGZ,aAAa,EACd,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,QAAQ,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACnD,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAQD,qBAAa,aAAc,YAAW,SAAS;IAC7C,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,aAAa,CAA2C;IAChE,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAC,IAAI,CACuB;IAC5C,sEAAsE;IACtE,aAAa,SAAK;gBAEN,OAAO,GAAE,WAAgB;IAYrC,IAAI,KAAK,IAAI,eAAe,CAE3B;IAED,OAAO,CAAC,QAAQ;IAMhB,OAAO,IAAI,OAAO,CAAC,YAAY,CAAC;IA4ChC,OAAO,CAAC,QAAQ;IAShB,kFAAkF;IAClF,IAAI,IAAI,IAAI;IAsBZ,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,cAAc;IA2BtB,IAAI,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAsB9B,OAAO,CAAC,MAAM;IA4Bd;;;OAGG;IACH,IAAI,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI;IAI9B,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,aAAa,KAAK,IAAI,GAAG,MAAM,IAAI;IAKrD,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI;IAKlD,aAAa,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,eAAe,KAAK,IAAI,GAAG,MAAM,IAAI;IAK3D,KAAK,IAAI,IAAI;CAOd"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { GRID_H, GRID_W } from './protocol.js';
|
|
2
|
+
const JOINT_NAMES = [
|
|
3
|
+
'head', 'neck', 'leftShoulder', 'rightShoulder', 'leftElbow', 'rightElbow',
|
|
4
|
+
'leftHand', 'rightHand', 'leftHip', 'rightHip', 'leftKnee', 'rightKnee',
|
|
5
|
+
'leftFoot', 'rightFoot',
|
|
6
|
+
];
|
|
7
|
+
export class MockTransport {
|
|
8
|
+
messageHandlers = new Set();
|
|
9
|
+
binaryHandlers = new Set();
|
|
10
|
+
stateHandlers = new Set();
|
|
11
|
+
timerHandle = null;
|
|
12
|
+
_state = 'idle';
|
|
13
|
+
frame = 0;
|
|
14
|
+
opts;
|
|
15
|
+
/** Set once ping has been observed, so tests can assert direction. */
|
|
16
|
+
pingsReceived = 0;
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.opts = {
|
|
19
|
+
fps: options.fps ?? 30,
|
|
20
|
+
setTimer: options.setTimer ?? ((fn, ms) => setTimeout(fn, ms)),
|
|
21
|
+
clearTimer: options.clearTimer ?? ((h) => clearTimeout(h)),
|
|
22
|
+
now: options.now ?? (() => Date.now()),
|
|
23
|
+
manual: options.manual ?? false,
|
|
24
|
+
require: options.require,
|
|
25
|
+
provides: options.provides,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
get state() {
|
|
29
|
+
return this._state;
|
|
30
|
+
}
|
|
31
|
+
setState(s) {
|
|
32
|
+
if (this._state === s)
|
|
33
|
+
return;
|
|
34
|
+
this._state = s;
|
|
35
|
+
for (const fn of this.stateHandlers)
|
|
36
|
+
fn(s);
|
|
37
|
+
}
|
|
38
|
+
connect() {
|
|
39
|
+
const requested = this.opts.require?.capabilities ?? [];
|
|
40
|
+
const provided = this.opts.provides
|
|
41
|
+
? this.opts.provides.map((name) => ({ name, version: 1 }))
|
|
42
|
+
: requested.map((c) => ({ name: c.name, version: c.version ?? 1, params: c.params }));
|
|
43
|
+
const providedNames = new Set(provided.map((c) => c.name));
|
|
44
|
+
const missing = requested
|
|
45
|
+
.filter((c) => !c.optional && !providedNames.has(c.name))
|
|
46
|
+
.map((c) => ({ name: c.name, reason: 'NO_PLUGIN' }));
|
|
47
|
+
const unsatisfied = requested
|
|
48
|
+
.filter((c) => c.optional && !providedNames.has(c.name))
|
|
49
|
+
.map((c) => ({ name: c.name, reason: 'NO_PLUGIN' }));
|
|
50
|
+
const ready = {
|
|
51
|
+
type: 'ready',
|
|
52
|
+
ok: missing.length === 0,
|
|
53
|
+
runtimeVersion: '0.0.0-mock',
|
|
54
|
+
protocolVersion: 1,
|
|
55
|
+
clientId: 'mock-client',
|
|
56
|
+
capabilities: provided,
|
|
57
|
+
missing,
|
|
58
|
+
unsatisfied,
|
|
59
|
+
plugins: ['mock'],
|
|
60
|
+
// Echoed even though nothing here reads it: a mock that drops the
|
|
61
|
+
// troubleshooting fields would hide bugs in code that relies on them.
|
|
62
|
+
requested: this.opts.require ?? {},
|
|
63
|
+
};
|
|
64
|
+
this.setState('ready');
|
|
65
|
+
this.emit(ready);
|
|
66
|
+
this.emit({
|
|
67
|
+
type: 'device:connected',
|
|
68
|
+
device: 'mock',
|
|
69
|
+
id: 'mock-0',
|
|
70
|
+
config: {
|
|
71
|
+
runtime: { preset: 'standard', minDistanceM: 0.2, maxDistanceM: 3.0, gain: 1.0 },
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
if (!this.opts.manual)
|
|
75
|
+
this.schedule();
|
|
76
|
+
return Promise.resolve(ready);
|
|
77
|
+
}
|
|
78
|
+
schedule() {
|
|
79
|
+
const period = Math.round(1000 / this.opts.fps);
|
|
80
|
+
const tick = () => {
|
|
81
|
+
this.tick();
|
|
82
|
+
this.timerHandle = this.opts.setTimer(tick, period);
|
|
83
|
+
};
|
|
84
|
+
this.timerHandle = this.opts.setTimer(tick, period);
|
|
85
|
+
}
|
|
86
|
+
/** Emits one frame's worth of events. Public so `manual: true` tests can step. */
|
|
87
|
+
tick() {
|
|
88
|
+
const t = this.frame / this.opts.fps;
|
|
89
|
+
const ts = this.opts.now();
|
|
90
|
+
this.frame++;
|
|
91
|
+
this.emit(this.makeBody(t, ts));
|
|
92
|
+
this.emit(this.makePointer(t, ts));
|
|
93
|
+
// A gesture every two seconds, alternating, so handlers get exercised.
|
|
94
|
+
if (this.frame % (this.opts.fps * 2) === 0) {
|
|
95
|
+
const gestures = ['raise_left', 'raise_right', 'jump', 'wave'];
|
|
96
|
+
this.emit({
|
|
97
|
+
type: 'gesture:update',
|
|
98
|
+
player: 0,
|
|
99
|
+
gesture: gestures[(this.frame / (this.opts.fps * 2)) % gestures.length | 0],
|
|
100
|
+
ts,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (this.frame % 3 === 0)
|
|
104
|
+
this.emit(this.makeSilhouette(t, ts));
|
|
105
|
+
}
|
|
106
|
+
makeBody(t, ts) {
|
|
107
|
+
const sway = Math.sin(t * 0.7) * 0.08;
|
|
108
|
+
const skeleton = {};
|
|
109
|
+
for (const name of JOINT_NAMES) {
|
|
110
|
+
skeleton[name] = jointFor(name, t, sway);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
type: 'body:update',
|
|
114
|
+
player: 0,
|
|
115
|
+
center: { x: 0.5 + sway, y: 0.5 },
|
|
116
|
+
depth: 2.0 + Math.sin(t * 0.3) * 0.2,
|
|
117
|
+
ts,
|
|
118
|
+
skeleton,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
makePointer(t, ts) {
|
|
122
|
+
const x = 0.5 + Math.sin(t * 1.1) * 0.35;
|
|
123
|
+
const y = 0.5 + Math.cos(t * 0.9) * 0.3;
|
|
124
|
+
return {
|
|
125
|
+
type: 'pointer:update',
|
|
126
|
+
id: 0,
|
|
127
|
+
x: clamp01(x),
|
|
128
|
+
y: clamp01(y),
|
|
129
|
+
down: Math.sin(t * 1.1) > 0.8,
|
|
130
|
+
raw: { x: x * 1.4 - 0.2, y: y * 1.4 - 0.2 },
|
|
131
|
+
ts,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
makeSilhouette(t, ts) {
|
|
135
|
+
const bytes = new Uint8Array(Math.ceil((GRID_W * GRID_H) / 8));
|
|
136
|
+
const cx = GRID_W * (0.5 + Math.sin(t * 0.7) * 0.15);
|
|
137
|
+
let set = 0;
|
|
138
|
+
for (let y = 0; y < GRID_H; y++) {
|
|
139
|
+
// A rough torso-and-head column.
|
|
140
|
+
const halfWidth = y < GRID_H * 0.2 ? GRID_W * 0.05 : GRID_W * 0.11;
|
|
141
|
+
for (let x = 0; x < GRID_W; x++) {
|
|
142
|
+
if (y > GRID_H * 0.1 && Math.abs(x - cx) < halfWidth) {
|
|
143
|
+
const i = y * GRID_W + x;
|
|
144
|
+
bytes[i >> 3] |= 0x80 >> (i & 7); // MSB is leftmost, as the runtime packs
|
|
145
|
+
set++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
type: 'depth:silhouette',
|
|
151
|
+
w: GRID_W,
|
|
152
|
+
h: GRID_H,
|
|
153
|
+
mask: bytesToBase64(bytes),
|
|
154
|
+
coverage: set / (GRID_W * GRID_H),
|
|
155
|
+
nearest: 1.8,
|
|
156
|
+
farthest: 2.4,
|
|
157
|
+
ts,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
send(msg) {
|
|
161
|
+
if (msg.type === 'ping') {
|
|
162
|
+
this.pingsReceived++;
|
|
163
|
+
this.emit({ type: 'pong', ts: msg.ts });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (msg.type === 'request') {
|
|
167
|
+
this.answer(msg.id, msg.name);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (msg.type === 'reconfigure') {
|
|
171
|
+
// Same guarantee as the real runtime: retuning does not interrupt the
|
|
172
|
+
// stream, and the effective values come back on device:connected.
|
|
173
|
+
this.emit({
|
|
174
|
+
type: 'device:connected',
|
|
175
|
+
device: 'mock',
|
|
176
|
+
id: 'mock-0',
|
|
177
|
+
config: msg.config,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
answer(id, name) {
|
|
182
|
+
const reply = (ok, body) => this.emit({ type: 'response', id, ok, ...body });
|
|
183
|
+
switch (name) {
|
|
184
|
+
case 'device.list':
|
|
185
|
+
reply(true, { result: { devices: [{ id: 'mock-0', plugin: 'mock', status: 'running' }] } });
|
|
186
|
+
break;
|
|
187
|
+
case 'runtime.status':
|
|
188
|
+
reply(true, { result: { version: '0.0.0-mock', uptimeMs: this.frame * 33, clients: 1 } });
|
|
189
|
+
break;
|
|
190
|
+
case 'camera.config':
|
|
191
|
+
reply(true, { result: { width: 1280, height: 720, fps: 30 } });
|
|
192
|
+
break;
|
|
193
|
+
case 'camera.start':
|
|
194
|
+
case 'camera.stop':
|
|
195
|
+
reply(true, { result: { ok: true } });
|
|
196
|
+
break;
|
|
197
|
+
case 'camera.snapshot':
|
|
198
|
+
reply(true, { result: { mime: 'image/png', data: TINY_PNG_BASE64 } });
|
|
199
|
+
break;
|
|
200
|
+
default:
|
|
201
|
+
// Mirrors the real runtime: nothing claimed the name.
|
|
202
|
+
reply(false, { error: { code: 'UNSUPPORTED', message: `mock does not implement ${name}` } });
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Public so tests and demos can inject an exact frame. Real sessions never
|
|
208
|
+
* call it — the mock drives itself from tick().
|
|
209
|
+
*/
|
|
210
|
+
emit(msg) {
|
|
211
|
+
for (const fn of this.messageHandlers)
|
|
212
|
+
fn(msg);
|
|
213
|
+
}
|
|
214
|
+
onMessage(fn) {
|
|
215
|
+
this.messageHandlers.add(fn);
|
|
216
|
+
return () => this.messageHandlers.delete(fn);
|
|
217
|
+
}
|
|
218
|
+
onBinary(fn) {
|
|
219
|
+
this.binaryHandlers.add(fn);
|
|
220
|
+
return () => this.binaryHandlers.delete(fn);
|
|
221
|
+
}
|
|
222
|
+
onStateChange(fn) {
|
|
223
|
+
this.stateHandlers.add(fn);
|
|
224
|
+
return () => this.stateHandlers.delete(fn);
|
|
225
|
+
}
|
|
226
|
+
close() {
|
|
227
|
+
if (this.timerHandle !== null) {
|
|
228
|
+
this.opts.clearTimer(this.timerHandle);
|
|
229
|
+
this.timerHandle = null;
|
|
230
|
+
}
|
|
231
|
+
this.setState('closed');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function jointFor(name, t, sway) {
|
|
235
|
+
const wave = Math.sin(t * 1.6);
|
|
236
|
+
const table = {
|
|
237
|
+
head: [0.5, 0.14],
|
|
238
|
+
neck: [0.5, 0.22],
|
|
239
|
+
leftShoulder: [0.42, 0.26],
|
|
240
|
+
rightShoulder: [0.58, 0.26],
|
|
241
|
+
leftElbow: [0.37, 0.38],
|
|
242
|
+
rightElbow: [0.63, 0.38],
|
|
243
|
+
leftHand: [0.33 - wave * 0.12, 0.5 - wave * 0.22],
|
|
244
|
+
rightHand: [0.67 + wave * 0.12, 0.5 + wave * 0.18],
|
|
245
|
+
leftHip: [0.45, 0.56],
|
|
246
|
+
rightHip: [0.55, 0.56],
|
|
247
|
+
leftKnee: [0.45, 0.74],
|
|
248
|
+
rightKnee: [0.55, 0.74],
|
|
249
|
+
leftFoot: [0.45, 0.92],
|
|
250
|
+
rightFoot: [0.55, 0.92],
|
|
251
|
+
};
|
|
252
|
+
const [x, y] = table[name] ?? [0.5, 0.5];
|
|
253
|
+
return {
|
|
254
|
+
x: clamp01(x + sway),
|
|
255
|
+
y: clamp01(y),
|
|
256
|
+
z: 2.0,
|
|
257
|
+
c: 0.9,
|
|
258
|
+
valid: true, // derived from confidence in the real path, never hardcoded there
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function clamp01(v) {
|
|
262
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
263
|
+
}
|
|
264
|
+
function bytesToBase64(bytes) {
|
|
265
|
+
const g = globalThis;
|
|
266
|
+
if (g.Buffer)
|
|
267
|
+
return g.Buffer.from(bytes).toString('base64');
|
|
268
|
+
let bin = '';
|
|
269
|
+
for (let i = 0; i < bytes.length; i++)
|
|
270
|
+
bin += String.fromCharCode(bytes[i]);
|
|
271
|
+
if (typeof g.btoa === 'function')
|
|
272
|
+
return g.btoa(bin);
|
|
273
|
+
throw new Error('no base64 encoder available');
|
|
274
|
+
}
|
|
275
|
+
/** 1x1 transparent PNG. Enough for snapshot plumbing to be exercised. */
|
|
276
|
+
const TINY_PNG_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==';
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QikSense Runtime protocol types.
|
|
3
|
+
*
|
|
4
|
+
* @generated from protocol/*.schema.json — DO NOT EDIT BY HAND.
|
|
5
|
+
* schema-hash: 608c0091
|
|
6
|
+
*
|
|
7
|
+
* Regenerate with: node protocol/generate.mjs
|
|
8
|
+
*
|
|
9
|
+
* When copying this file into qik-sense-shared-sdk, keep schema-hash intact:
|
|
10
|
+
* a test there asserts it matches, which is the only automatic signal that the
|
|
11
|
+
* copy has fallen behind the schemas.
|
|
12
|
+
*/
|
|
13
|
+
/** Hash of the schema inputs this file was generated from. */
|
|
14
|
+
export declare const SCHEMA_HASH = "608c0091";
|
|
15
|
+
/** Default local endpoint. https pages must use wss to avoid mixed-content blocking. */
|
|
16
|
+
export declare const RUNTIME_URL = "ws://127.0.0.1:17872";
|
|
17
|
+
/** Silhouette grid geometry. Must match qs_silhouette_v1 in qs_data_abi.h. */
|
|
18
|
+
export declare const GRID_W = 64;
|
|
19
|
+
export declare const GRID_H = 48;
|
|
20
|
+
/**
|
|
21
|
+
* Closed set of built-in commands (design/02 §6.2). Closed rather than free-form so the SDK can expose one semantic method per command; a client cannot invent a name the runtime has no handler for.
|
|
22
|
+
*/
|
|
23
|
+
export type RequestName = "device.list" | "runtime.status" | "camera.config" | "camera.start" | "camera.stop" | "camera.snapshot" | "camera.offer" | "camera.ice";
|
|
24
|
+
/**
|
|
25
|
+
* Why a command failed. UNSUPPORTED means no handler claimed the name — the runtime tried its built-ins and every loaded plugin.
|
|
26
|
+
*/
|
|
27
|
+
export interface RequestError {
|
|
28
|
+
code: "UNSUPPORTED" | "NO_DEVICE" | "TIMEOUT" | "BAD_ARGS" | "INTERNAL";
|
|
29
|
+
message?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Web → Runtime. `id` is client-generated and echoed back verbatim; the runtime never interprets it.
|
|
33
|
+
*/
|
|
34
|
+
export interface RequestMessage {
|
|
35
|
+
type: "request";
|
|
36
|
+
id: string;
|
|
37
|
+
name: RequestName;
|
|
38
|
+
args?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Runtime → Web, paired by `id`. Same shape as ready: one message type, `ok` carries the verdict, so clients never branch on which message arrived.
|
|
42
|
+
*/
|
|
43
|
+
export interface ResponseMessage {
|
|
44
|
+
type: "response";
|
|
45
|
+
id: string;
|
|
46
|
+
ok: boolean;
|
|
47
|
+
/** When true a WS binary frame follows immediately, carrying the 16-byte QSBN header (design/02 §9). Whether a command sets this is fixed per name — never per call — so the client can decide how to read before it asks. */
|
|
48
|
+
binary?: boolean;
|
|
49
|
+
result?: Record<string, unknown>;
|
|
50
|
+
error?: RequestError;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Live retuning. Fields that do not affect stream parameters (calibration, thresholds, filtering) must take effect without interrupting the stream — that is the precondition for keeping Pointer in the runtime.
|
|
54
|
+
*/
|
|
55
|
+
export interface ReconfigureMessage {
|
|
56
|
+
type: "reconfigure";
|
|
57
|
+
config: RuntimeConfig;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Two namespaces with different owners (design/04 §2.2). `runtime` is consumed by the native runtime; `web` is for the browser side and the runtime does not read it.
|
|
61
|
+
*/
|
|
62
|
+
export interface RuntimeConfig {
|
|
63
|
+
runtime?: RuntimeNamespace;
|
|
64
|
+
/** Opaque to the runtime. Present so one config object can travel intact through the runtime to the client. */
|
|
65
|
+
web?: Record<string, unknown>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Consumed by the native runtime only.
|
|
69
|
+
*/
|
|
70
|
+
export interface RuntimeNamespace {
|
|
71
|
+
preset?: "close" | "standard" | "far";
|
|
72
|
+
minDistanceM?: number;
|
|
73
|
+
maxDistanceM?: number;
|
|
74
|
+
calibBox?: CalibBox;
|
|
75
|
+
gain?: number;
|
|
76
|
+
smoothing?: "none" | "ema" | "oneEuro";
|
|
77
|
+
lockPolicy?: "nearest" | "largest" | "first";
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Physical region mapped onto the 0..1 game plane. Owned by the runtime namespace because the runtime is what computes Pointer.
|
|
81
|
+
*/
|
|
82
|
+
export interface CalibBox {
|
|
83
|
+
x: number;
|
|
84
|
+
y: number;
|
|
85
|
+
w: number;
|
|
86
|
+
h: number;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Left/right are labelled from the subject's point of view; the runtime never mirrors. Screen mirroring belongs to the web layer, and doing it on both sides flips twice.
|
|
90
|
+
*/
|
|
91
|
+
export interface Joint {
|
|
92
|
+
/** normalized 0..1, unmirrored */
|
|
93
|
+
x: number;
|
|
94
|
+
/** normalized 0..1 */
|
|
95
|
+
y: number;
|
|
96
|
+
/** metres from camera */
|
|
97
|
+
z: number;
|
|
98
|
+
/** model confidence 0..1 */
|
|
99
|
+
c: number;
|
|
100
|
+
/** derived from confidence; never hardcoded true */
|
|
101
|
+
valid: boolean;
|
|
102
|
+
}
|
|
103
|
+
export interface BodyUpdate {
|
|
104
|
+
type: "body:update";
|
|
105
|
+
player: number;
|
|
106
|
+
center?: {
|
|
107
|
+
x?: number;
|
|
108
|
+
y?: number;
|
|
109
|
+
};
|
|
110
|
+
/** body centre distance, metres */
|
|
111
|
+
depth?: number;
|
|
112
|
+
ts: Timestamp;
|
|
113
|
+
skeleton: Record<string, Joint>;
|
|
114
|
+
}
|
|
115
|
+
export interface GestureUpdate {
|
|
116
|
+
type: "gesture:update";
|
|
117
|
+
player: number;
|
|
118
|
+
gesture: "raise_left" | "raise_right" | "jump" | "squat" | "wave" | "swipe" | "push";
|
|
119
|
+
ts: Timestamp;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* x/y are already calibrated into 0..1 game space. `raw` is the uncalibrated position and exists only for the tuning HUD — reading it from gameplay bypasses calibration and is wrong at any non-default standing position.
|
|
123
|
+
*/
|
|
124
|
+
export interface PointerUpdate {
|
|
125
|
+
type: "pointer:update";
|
|
126
|
+
id: number;
|
|
127
|
+
x: number;
|
|
128
|
+
y: number;
|
|
129
|
+
down: boolean;
|
|
130
|
+
raw?: {
|
|
131
|
+
x?: number;
|
|
132
|
+
y?: number;
|
|
133
|
+
};
|
|
134
|
+
ts: Timestamp;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Occupancy grid, independent of any body tracker — it comes straight off the depth frame, which makes it the safest path for first light on new hardware.
|
|
138
|
+
*/
|
|
139
|
+
export interface DepthSilhouette {
|
|
140
|
+
type: "depth:silhouette";
|
|
141
|
+
w: number;
|
|
142
|
+
h: number;
|
|
143
|
+
/** base64 of w*h/8 bytes, row-major, MSB is the leftmost pixel. This layout is what the browser hit test decodes; changing it requires changing both sides. */
|
|
144
|
+
mask: string;
|
|
145
|
+
/** fraction of set cells; 0 = nobody in frame */
|
|
146
|
+
coverage?: number;
|
|
147
|
+
nearest?: number;
|
|
148
|
+
farthest?: number;
|
|
149
|
+
ts: Timestamp;
|
|
150
|
+
}
|
|
151
|
+
export interface DeviceConnected {
|
|
152
|
+
type: "device:connected";
|
|
153
|
+
device: string;
|
|
154
|
+
id?: string;
|
|
155
|
+
/** The values actually in effect after presets are expanded, so thresholds can be checked in devtools without a rebuild. */
|
|
156
|
+
config?: Record<string, unknown>;
|
|
157
|
+
}
|
|
158
|
+
export interface DeviceError {
|
|
159
|
+
type: "device:error";
|
|
160
|
+
device?: string;
|
|
161
|
+
/** NO_TRACKER must be reported explicitly: silently producing no skeleton is indistinguishable from nobody standing in front of the camera. */
|
|
162
|
+
code: "NO_DEVICE" | "NO_TRACKER" | "START_FAILED" | "CAPABILITY_CONFLICT";
|
|
163
|
+
message?: string;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Client-initiated only. The runtime answers pong and never sends ping; a client waiting for a server ping would decide the link is dead.
|
|
167
|
+
*/
|
|
168
|
+
export interface Ping {
|
|
169
|
+
type: "ping";
|
|
170
|
+
ts?: number;
|
|
171
|
+
}
|
|
172
|
+
export interface Pong {
|
|
173
|
+
type: "pong";
|
|
174
|
+
ts?: number;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Monotonic milliseconds, same clock and unit from plugin to browser. Not Unix epoch — it is only ever used for intervals, and wall clock gets stepped by NTP.
|
|
178
|
+
*/
|
|
179
|
+
export type Timestamp = number;
|
|
180
|
+
/**
|
|
181
|
+
* A named capability with a version and capability-specific params. Params matter: an experience needing leftHand cannot tell from the name alone whether a body-tracking plugin supplies it.
|
|
182
|
+
*/
|
|
183
|
+
export interface Capability {
|
|
184
|
+
name: "body-tracking" | "hand-tracking" | "face-tracking" | "pointer" | "gesture" | "imu" | "depth" | "depth-silhouette" | "camera-stream";
|
|
185
|
+
version?: number;
|
|
186
|
+
params?: Record<string, unknown>;
|
|
187
|
+
/** When absent this capability does not block startup; it is reported in ready.unsatisfied instead. */
|
|
188
|
+
optional?: boolean;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Why a requested capability could not be provided. Six distinct reasons because they need six different responses in the field; a single 'device not connected' cannot be acted on.
|
|
192
|
+
*/
|
|
193
|
+
export interface CapabilityMiss {
|
|
194
|
+
name: string;
|
|
195
|
+
reason: "NO_PLUGIN" | "NO_DEVICE" | "PARAMS" | "VERSION" | "CAPABILITY_CONFLICT" | "START_FAILED";
|
|
196
|
+
detail?: Record<string, unknown>;
|
|
197
|
+
}
|
|
198
|
+
export interface HelloMessage {
|
|
199
|
+
type: "hello";
|
|
200
|
+
client: string;
|
|
201
|
+
sessionId?: string;
|
|
202
|
+
protocolVersion?: number;
|
|
203
|
+
/** Clients declare capabilities, never plugin names — that is what keeps Builder and Runner independent of any vendor. */
|
|
204
|
+
require?: {
|
|
205
|
+
capabilities?: Capability[];
|
|
206
|
+
config?: Record<string, unknown>;
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
export interface ReadyMessage {
|
|
210
|
+
type: "ready";
|
|
211
|
+
/** True when `missing` is empty. Unsatisfied optional capabilities do not affect it. */
|
|
212
|
+
ok: boolean;
|
|
213
|
+
runtimeVersion: string;
|
|
214
|
+
protocolVersion?: number;
|
|
215
|
+
clientId: string;
|
|
216
|
+
capabilities?: Capability[];
|
|
217
|
+
missing?: CapabilityMiss[];
|
|
218
|
+
unsatisfied?: CapabilityMiss[];
|
|
219
|
+
plugins?: string[];
|
|
220
|
+
/** Verbatim echo of hello.require. Answers the most common field question — 'my device config had no effect' — by showing whether it arrived at all. */
|
|
221
|
+
requested?: Record<string, unknown>;
|
|
222
|
+
}
|
|
223
|
+
/** Anything the runtime sends. The trailing member keeps forward-compatibility:
|
|
224
|
+
* an older client must ignore unknown types rather than fail. */
|
|
225
|
+
export type ServerMessage = ReadyMessage | BodyUpdate | GestureUpdate | PointerUpdate | DepthSilhouette | DeviceConnected | DeviceError | Pong | ResponseMessage | {
|
|
226
|
+
type: string;
|
|
227
|
+
[k: string]: unknown;
|
|
228
|
+
};
|
|
229
|
+
/** Anything a client sends. */
|
|
230
|
+
export type ClientMessage = HelloMessage | Ping | RequestMessage | ReconfigureMessage;
|
|
231
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/runtime/protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QikSense Runtime protocol types.
|
|
3
|
+
*
|
|
4
|
+
* @generated from protocol/*.schema.json — DO NOT EDIT BY HAND.
|
|
5
|
+
* schema-hash: 608c0091
|
|
6
|
+
*
|
|
7
|
+
* Regenerate with: node protocol/generate.mjs
|
|
8
|
+
*
|
|
9
|
+
* When copying this file into qik-sense-shared-sdk, keep schema-hash intact:
|
|
10
|
+
* a test there asserts it matches, which is the only automatic signal that the
|
|
11
|
+
* copy has fallen behind the schemas.
|
|
12
|
+
*/
|
|
13
|
+
/** Hash of the schema inputs this file was generated from. */
|
|
14
|
+
export const SCHEMA_HASH = '608c0091';
|
|
15
|
+
/** Default local endpoint. https pages must use wss to avoid mixed-content blocking. */
|
|
16
|
+
export const RUNTIME_URL = 'ws://127.0.0.1:17872';
|
|
17
|
+
/** Silhouette grid geometry. Must match qs_silhouette_v1 in qs_data_abi.h. */
|
|
18
|
+
export const GRID_W = 64;
|
|
19
|
+
export const GRID_H = 48;
|