@voicethere/agent 0.2.0 → 0.2.2

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
@@ -104,7 +104,9 @@ From `agent/`:
104
104
  ```bash
105
105
  npm install
106
106
  npm run live-test:models # select/download sherpa models and export env values
107
- npm run live-test:stack
107
+ npm run live-test:stack:echo # voice echo template
108
+ # or
109
+ npm run live-test:stack:game # data-only game-sync template
108
110
  ```
109
111
 
110
112
  This starts the local starter with your sandboxed bundle and serves the page.
@@ -204,7 +206,7 @@ The runtime processes parent IPC **in order per `sessionId`** while different se
204
206
 
205
207
  For isolated voice agents (default), leave **`shared_child_per_session`** disabled — each session gets its own child process.
206
208
 
207
- See [`templates/game-sync.ts`](./templates/game-sync.ts) for a data-only authoritative server example.
209
+ See [`templates/game-sync.ts`](./templates/game-sync.ts) for a data-only authoritative server example (60Hz server-side movement + collisions, client render-only).
208
210
 
209
211
  ## Building your agent bundle
210
212
 
package/dist/agent.js CHANGED
@@ -20,9 +20,15 @@ var SessionSerialQueue = class {
20
20
  clearAll() {
21
21
  this.tails.clear();
22
22
  }
23
+ hasPending(sessionId) {
24
+ return this.tails.has(sessionId);
25
+ }
23
26
  };
24
27
 
25
28
  // src/runtime.ts
29
+ var SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
30
+ var SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
31
+ var DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
26
32
  function isParentMessage(value) {
27
33
  if (!value || typeof value !== "object") return false;
28
34
  const msg = value;
@@ -38,11 +44,42 @@ function parseDataChannelPayload(raw) {
38
44
  var peerEnvBySessionId = /* @__PURE__ */ new Map();
39
45
  var endedSessionIds = /* @__PURE__ */ new Set();
40
46
  var agentLogSessionContext = new AsyncLocalStorage();
47
+ function parseBooleanEnv(value, defaultValue) {
48
+ if (value === void 0) return defaultValue;
49
+ const normalized = value.trim().toLowerCase();
50
+ if (normalized === "") return defaultValue;
51
+ if (["0", "false", "off", "no"].includes(normalized)) return false;
52
+ if (["1", "true", "on", "yes"].includes(normalized)) return true;
53
+ return defaultValue;
54
+ }
55
+ function parseNonNegativeIntegerEnv(value, defaultValue) {
56
+ if (value === void 0) return defaultValue;
57
+ const parsed = Number(value);
58
+ if (!Number.isFinite(parsed) || parsed < 0) return defaultValue;
59
+ return Math.floor(parsed);
60
+ }
61
+ function resolveSessionStartInitDelayMs() {
62
+ const enabled = parseBooleanEnv(
63
+ process.env[SESSION_START_INIT_DELAY_ENABLED_ENV],
64
+ true
65
+ );
66
+ if (!enabled) return 0;
67
+ return parseNonNegativeIntegerEnv(
68
+ process.env[SESSION_START_INIT_DELAY_MS_ENV],
69
+ DEFAULT_SESSION_START_INIT_DELAY_MS
70
+ );
71
+ }
41
72
  async function handleParentMessage(message, handlers) {
42
73
  switch (message.type) {
43
74
  case "session_start":
44
75
  endedSessionIds.delete(message.sessionId);
45
76
  peerEnvBySessionId.set(message.sessionId, message.env);
77
+ const sessionStartInitDelayMs = resolveSessionStartInitDelayMs();
78
+ if (sessionStartInitDelayMs > 0) {
79
+ await new Promise(
80
+ (resolve) => setTimeout(resolve, sessionStartInitDelayMs)
81
+ );
82
+ }
46
83
  await (handlers.onClientJoin ?? handlers.onSessionStart)?.({
47
84
  sessionId: message.sessionId,
48
85
  env: message.env
package/dist/runner.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { buildChildExecArgv, collectAllowFsReadDirs, } from "./sandbox/sandbox.js";
2
2
  export { resolveBundlePath, startSandboxedChild, type SandboxedChild, type StartSandboxedChildOptions, } from "./sandbox/start-child.js";
3
+ export { SessionSerialQueue } from "./session-serial-queue.js";
3
4
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,0BAA0B,GAChC,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,0BAA0B,GAChC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/runner.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { buildChildExecArgv, collectAllowFsReadDirs, } from "./sandbox/sandbox.js";
2
2
  export { resolveBundlePath, startSandboxedChild, } from "./sandbox/start-child.js";
3
+ export { SessionSerialQueue } from "./session-serial-queue.js";
3
4
  //# sourceMappingURL=runner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GAGpB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GAGpB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import type { SpeechEvent } from "@node-webrtc-rust/sdk/voice";
2
2
  import type { DataChannelKind } from "./protocol.js";
3
+ export declare const SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
4
+ export declare const SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
3
5
  export interface SessionContext {
4
6
  sessionId: string;
5
7
  env: Record<string, string>;
@@ -81,6 +83,15 @@ export declare function speak(sessionId: string, text: string): void;
81
83
  export declare function sendToClient(sessionId: string, payload: unknown): void;
82
84
  /** Send raw bytes to the browser peer via the runner parent. */
83
85
  export declare function sendBinaryToClient(sessionId: string, data: Buffer | Uint8Array, channel?: DataChannelKind): void;
86
+ /**
87
+ * Broadcasts binary data to specified clients over a specific data channel.
88
+ *
89
+ * @param data The binary data to be sent, either as a Buffer or Uint8Array.
90
+ * @param sessionIds An array of session IDs representing the target clients.
91
+ * @param channel The data channel over which the binary data is sent. Defaults to "sync".
92
+ * @return void
93
+ */
94
+ export declare function broadCastBinaryToClients(data: Buffer | Uint8Array, sessionIds: readonly string[], channel?: DataChannelKind): void;
84
95
  /** Send the same JSON payload to one or more browser peers. */
85
96
  export declare function broadcastToClients(payload: unknown, sessionIds: readonly string[]): void;
86
97
  /** Structured log forwarded to the runner parent (includes session when in a handler). */
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
package/dist/runtime.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { SessionSerialQueue } from "./session-serial-queue.js";
3
+ export const SESSION_START_INIT_DELAY_ENABLED_ENV = "AGENT_SESSION_START_INIT_DELAY_ENABLED";
4
+ export const SESSION_START_INIT_DELAY_MS_ENV = "AGENT_SESSION_START_INIT_DELAY_MS";
5
+ const DEFAULT_SESSION_START_INIT_DELAY_MS = 500;
3
6
  function isParentMessage(value) {
4
7
  if (!value || typeof value !== "object")
5
8
  return false;
@@ -24,11 +27,41 @@ const peerEnvBySessionId = new Map();
24
27
  const endedSessionIds = new Set();
25
28
  /** Active orchestrator session while a parent IPC handler runs (per-session queue). */
26
29
  const agentLogSessionContext = new AsyncLocalStorage();
30
+ function parseBooleanEnv(value, defaultValue) {
31
+ if (value === undefined)
32
+ return defaultValue;
33
+ const normalized = value.trim().toLowerCase();
34
+ if (normalized === "")
35
+ return defaultValue;
36
+ if (["0", "false", "off", "no"].includes(normalized))
37
+ return false;
38
+ if (["1", "true", "on", "yes"].includes(normalized))
39
+ return true;
40
+ return defaultValue;
41
+ }
42
+ function parseNonNegativeIntegerEnv(value, defaultValue) {
43
+ if (value === undefined)
44
+ return defaultValue;
45
+ const parsed = Number(value);
46
+ if (!Number.isFinite(parsed) || parsed < 0)
47
+ return defaultValue;
48
+ return Math.floor(parsed);
49
+ }
50
+ function resolveSessionStartInitDelayMs() {
51
+ const enabled = parseBooleanEnv(process.env[SESSION_START_INIT_DELAY_ENABLED_ENV], true);
52
+ if (!enabled)
53
+ return 0;
54
+ return parseNonNegativeIntegerEnv(process.env[SESSION_START_INIT_DELAY_MS_ENV], DEFAULT_SESSION_START_INIT_DELAY_MS);
55
+ }
27
56
  async function handleParentMessage(message, handlers) {
28
57
  switch (message.type) {
29
58
  case "session_start":
30
59
  endedSessionIds.delete(message.sessionId);
31
60
  peerEnvBySessionId.set(message.sessionId, message.env);
61
+ const sessionStartInitDelayMs = resolveSessionStartInitDelayMs();
62
+ if (sessionStartInitDelayMs > 0) {
63
+ await new Promise((resolve) => setTimeout(resolve, sessionStartInitDelayMs));
64
+ }
32
65
  await (handlers.onClientJoin ?? handlers.onSessionStart)?.({
33
66
  sessionId: message.sessionId,
34
67
  env: message.env,
@@ -163,8 +196,7 @@ async function runIdleTimeoutHook(handlers, message) {
163
196
  await handlers.onIdleTimeout?.(ctx);
164
197
  }
165
198
  catch (hookError) {
166
- error =
167
- hookError instanceof Error ? hookError.message : String(hookError);
199
+ error = hookError instanceof Error ? hookError.message : String(hookError);
168
200
  agentLog("error", `onIdleTimeout failed: ${error}`, message.sessionId);
169
201
  }
170
202
  process.send?.({
@@ -208,6 +240,25 @@ export function sendBinaryToClient(sessionId, data, channel = "sync") {
208
240
  channel,
209
241
  });
210
242
  }
243
+ /**
244
+ * Broadcasts binary data to specified clients over a specific data channel.
245
+ *
246
+ * @param data The binary data to be sent, either as a Buffer or Uint8Array.
247
+ * @param sessionIds An array of session IDs representing the target clients.
248
+ * @param channel The data channel over which the binary data is sent. Defaults to "sync".
249
+ * @return void
250
+ */
251
+ export function broadCastBinaryToClients(data, sessionIds, channel = "sync") {
252
+ const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);
253
+ for (const sessionId of sessionIds) {
254
+ process.send?.({
255
+ type: "send_binary_to_client",
256
+ sessionId,
257
+ data: buffer,
258
+ channel,
259
+ });
260
+ }
261
+ }
211
262
  /** Send the same JSON payload to one or more browser peers. */
212
263
  export function broadcastToClients(payload, sessionIds) {
213
264
  for (const sessionId of sessionIds) {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -7,5 +7,6 @@ export declare class SessionSerialQueue {
7
7
  enqueue(sessionId: string, task: () => void | Promise<void>): void;
8
8
  clear(sessionId: string): void;
9
9
  clearAll(): void;
10
+ hasPending(sessionId: string): boolean;
10
11
  }
11
12
  //# sourceMappingURL=session-serial-queue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-serial-queue.d.ts","sourceRoot":"","sources":["../src/session-serial-queue.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;IAE1D,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAYlE,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B,QAAQ,IAAI,IAAI;CAGjB"}
1
+ {"version":3,"file":"session-serial-queue.d.ts","sourceRoot":"","sources":["../src/session-serial-queue.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;IAE1D,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAYlE,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B,QAAQ,IAAI,IAAI;IAIhB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;CAGvC"}
@@ -20,5 +20,8 @@ export class SessionSerialQueue {
20
20
  clearAll() {
21
21
  this.tails.clear();
22
22
  }
23
+ hasPending(sessionId) {
24
+ return this.tails.has(sessionId);
25
+ }
23
26
  }
24
27
  //# sourceMappingURL=session-serial-queue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-serial-queue.js","sourceRoot":"","sources":["../src/session-serial-queue.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IACZ,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE1D,OAAO,CAAC,SAAiB,EAAE,IAAgC;QACzD,MAAM,QAAQ,GACZ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACzE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAChC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,OAAO,EAAE,CAAC;gBAC1C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,SAAiB;QACrB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
1
+ {"version":3,"file":"session-serial-queue.js","sourceRoot":"","sources":["../src/session-serial-queue.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IACZ,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE1D,OAAO,CAAC,SAAiB,EAAE,IAAgC;QACzD,MAAM,QAAQ,GACZ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACzE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;YAChC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,OAAO,EAAE,CAAC;gBAC1C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,SAAiB;QACrB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voicethere/agent",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "VoiceThere customer agent SDK — IPC types and runtime helpers for sandboxed child bundles",
5
5
  "type": "module",
6
6
  "exports": {
@@ -42,7 +42,9 @@
42
42
  "live-test:models": "bash ./scripts/select-sherpa-models.sh",
43
43
  "live-test:page": "bash ./scripts/live-test-page.sh",
44
44
  "live-test:starter": "bash ./scripts/live-test-starter.sh",
45
- "live-test:stack": "bash ./scripts/live-test-stack.sh",
45
+ "live-test:stack": "npm run live-test:stack:echo",
46
+ "live-test:stack:echo": "LIVE_TEST_AGENT_ENTRY=examples/agent.ts LIVE_TEST_PAGE_PATH=/examples/live-test/index.html LIVE_TEST_SESSION_MODE=voice bash ./scripts/live-test-stack.sh",
47
+ "live-test:stack:game": "LIVE_TEST_AGENT_ENTRY=templates/game-sync.ts LIVE_TEST_PAGE_PATH=/examples/live-test/game-sync.html LIVE_TEST_SESSION_MODE=data-only bash ./scripts/live-test-stack.sh",
46
48
  "prepublishOnly": "npm run build"
47
49
  },
48
50
  "repository": {
@@ -46,3 +46,21 @@ npx @voicethere/agent verify
46
46
  ```
47
47
 
48
48
  **Voice E2E:** host with the VoiceThere agent runner (platform or internal deployment) — set `AGENT_BUNDLE_PATH` to your built `dist/agent.js`.
49
+
50
+ ## `game-sync.ts`
51
+
52
+ Authoritative multi-object sync sample for real-time games/simulations:
53
+
54
+ - register tracked objects via control DC (`{ type: "register" }`)
55
+ - receive `register_ack` + `object_registered` ownership notifications
56
+ - run server-authoritative simulation (position + velocity) at 60Hz
57
+ - resolve wall bounce + object-object collisions on the server
58
+ - broadcast world-state snapshots to clients over binary sync channel (9-float records)
59
+ - ignore client binary writes by default (safe base for adding intent/input messages later)
60
+ - reuse freed slots on leave to keep world state compact
61
+
62
+ Build:
63
+
64
+ ```bash
65
+ npx @voicethere/agent build --entry templates/game-sync.ts
66
+ ```
@@ -1,68 +1,394 @@
1
1
  /**
2
- * Authoritative tick counter shared child broadcasts `{ type: 'state', tick, players }`.
2
+ * Multiplayer object-sync template with ownership checks.
3
+ *
4
+ * World layout:
5
+ * - one global Float32Array
6
+ * - each tracked object uses exactly 9 floats:
7
+ * [objectId, posX, posY, posZ, posW, dirX, dirY, dirZ, dirW]
8
+ *
9
+ * Control messages:
10
+ * - `{ type: "register" }` -> allocates (or reuses) one 9-float slot
11
+ * - server replies `{ type: "register_ack", objectId }`
12
+ *
13
+ * Simulation:
14
+ * - server-authoritative movement at 60Hz
15
+ * - wall bounce + object-object elastic collisions on server
16
+ * - clients render server snapshots; client binary writes are ignored
17
+ *
18
+ * Broadcast:
19
+ * - 60Hz world-state broadcast starts when at least 1 client is connected
20
+ * - stops when connected client count drops below 1
3
21
  *
4
22
  * Build:
5
23
  * npx @voicethere/agent build --entry templates/game-sync.ts
6
24
  */
7
25
  import {
8
- agentLog,
9
- broadcastToClients,
10
- defineAgent,
11
- sendToClient,
12
- type DataChannelContext,
26
+ agentLog,
27
+ defineAgent,
28
+ sendBinaryToClient,
29
+ sendToClient,
13
30
  } from "@voicethere/agent";
14
31
 
15
- let tick = 0;
16
- const connected = new Set<string>();
32
+ const OBJECT_STRIDE = 9;
33
+ const BROADCAST_HZ = 60;
34
+ const BROADCAST_INTERVAL_MS = Math.floor(1000 / BROADCAST_HZ);
35
+ const BOARD_WIDTH = 1280;
36
+ const BOARD_HEIGHT = 720;
37
+ const OBJECT_RADIUS = 25;
38
+ const MIN_SPEED = 90;
39
+ const MAX_SPEED = 180;
40
+ const COLLISION_RESTITUTION = 1.0;
41
+
42
+ const connectedSessions = new Set<string>();
43
+ const objectOwners = new Map<number, string>(); // objectId -> sessionId
44
+ const sessionObjects = new Map<string, Set<number>>(); // sessionId -> owned objectIds
45
+ // Slots we can recycle. Keeping a free-list avoids unbounded array growth when
46
+ // clients churn (join/leave repeatedly).
47
+ const freeSlots: number[] = [];
48
+
49
+ let worldState = new Float32Array(0);
50
+ let broadcastTimer: NodeJS.Timeout | null = null;
51
+
52
+ interface TrackedObjectInfo {
53
+ objectId: number;
54
+ ownerSessionId: string;
55
+ }
56
+
57
+ function rand(min: number, max: number): number {
58
+ return min + Math.random() * (max - min);
59
+ }
60
+
61
+ function randomVelocity(): number {
62
+ return (Math.random() < 0.5 ? -1 : 1) * rand(MIN_SPEED, MAX_SPEED);
63
+ }
64
+
65
+ function slotToObjectId(slot: number): number {
66
+ // Object ids are 1-based so 0 can represent "unused".
67
+ return slot + 1;
68
+ }
17
69
 
18
- function playerCount(): number {
19
- return connected.size;
70
+ function objectIdToSlot(objectId: number): number {
71
+ // Inverse of slotToObjectId().
72
+ return objectId - 1;
20
73
  }
21
74
 
22
- function broadcastState(excludeSessionId?: string): void {
23
- const payload = { type: "state" as const, tick, players: playerCount() };
24
- const targets = excludeSessionId
25
- ? [...connected].filter((id) => id !== excludeSessionId)
26
- : [...connected];
27
- broadcastToClients(payload, targets);
75
+ function markSlotFree(slot: number): void {
76
+ // Zero the entire record. Slot is still allocated in array length terms,
77
+ // but logically available for reuse.
78
+ const start = slot * OBJECT_STRIDE;
79
+ for (let i = 0; i < OBJECT_STRIDE; i += 1) {
80
+ worldState[start + i] = 0;
81
+ }
28
82
  }
29
83
 
30
- function handleTickMessage(ctx: DataChannelContext): void {
31
- const message = ctx.message;
32
- if (!message || typeof message !== "object") return;
33
- const record = message as { type?: string };
34
- if (record.type !== "tick") return;
35
- tick += 1;
36
- agentLog(
37
- "info",
38
- `tick=${tick} players=${playerCount()} from=${ctx.sessionId}`,
39
- );
40
- broadcastToClients({ type: "state", tick, players: playerCount() }, [
41
- ...connected,
42
- ]);
84
+ function allocateSlot(): number {
85
+ // Prefer recycling old slots before growing worldState.
86
+ const reused = freeSlots.shift();
87
+ if (reused !== undefined) return reused;
88
+
89
+ // Grow by exactly one object record (9 floats).
90
+ const next = new Float32Array(worldState.length + OBJECT_STRIDE);
91
+ next.set(worldState);
92
+ worldState = next;
93
+ return worldState.length / OBJECT_STRIDE - 1;
94
+ }
95
+
96
+ function attachObjectToSession(sessionId: string, objectId: number): void {
97
+ // Tracks ownership in both directions so validation and cleanup are O(1).
98
+ let owned = sessionObjects.get(sessionId);
99
+ if (!owned) {
100
+ owned = new Set<number>();
101
+ sessionObjects.set(sessionId, owned);
102
+ }
103
+ owned.add(objectId);
104
+ objectOwners.set(objectId, sessionId);
105
+ }
106
+
107
+ function releaseObject(objectId: number): void {
108
+ // Remove reverse-ownership references first.
109
+ const owner = objectOwners.get(objectId);
110
+ if (owner) {
111
+ const owned = sessionObjects.get(owner);
112
+ owned?.delete(objectId);
113
+ if (owned && owned.size === 0) {
114
+ sessionObjects.delete(owner);
115
+ }
116
+ }
117
+
118
+ objectOwners.delete(objectId);
119
+
120
+ const slot = objectIdToSlot(objectId);
121
+ if (slot < 0) return;
122
+ if (slot >= worldState.length / OBJECT_STRIDE) return;
123
+
124
+ // Free slot contents and push slot into free-list for future register() calls.
125
+ markSlotFree(slot);
126
+ if (!freeSlots.includes(slot)) {
127
+ freeSlots.push(slot);
128
+ freeSlots.sort((a, b) => a - b);
129
+ }
130
+ }
131
+
132
+ function registerObject(sessionId: string): number {
133
+ // Allocate (or reuse) one slot and stamp object id plus initial state.
134
+ const slot = allocateSlot();
135
+ const objectId = slotToObjectId(slot);
136
+ const start = slot * OBJECT_STRIDE;
137
+ worldState[start] = objectId;
138
+ worldState[start + 1] = rand(OBJECT_RADIUS, BOARD_WIDTH - OBJECT_RADIUS);
139
+ worldState[start + 2] = rand(OBJECT_RADIUS, BOARD_HEIGHT - OBJECT_RADIUS);
140
+ worldState[start + 3] = 0;
141
+ worldState[start + 4] = 1;
142
+ worldState[start + 5] = randomVelocity();
143
+ worldState[start + 6] = randomVelocity();
144
+ worldState[start + 7] = 0;
145
+ worldState[start + 8] = 0;
146
+ attachObjectToSession(sessionId, objectId);
147
+ return objectId;
148
+ }
149
+
150
+ function parseRegisterCommand(message: unknown): boolean {
151
+ if (!message || typeof message !== "object") return false;
152
+ const record = message as { type?: unknown };
153
+ return record.type === "register";
154
+ }
155
+
156
+ function parseChatCommand(
157
+ message: unknown,
158
+ ): { text: string } | null {
159
+ if (!message || typeof message !== "object") return null;
160
+ const record = message as { type?: unknown; text?: unknown };
161
+ if (record.type !== "chat" || typeof record.text !== "string") return null;
162
+ const text = record.text.trim();
163
+ if (!text) return null;
164
+ return {text};
165
+ }
166
+
167
+ function trackedObjectsSnapshot(): TrackedObjectInfo[] {
168
+ const objects: TrackedObjectInfo[] = [];
169
+ for (const [objectId, ownerSessionId] of objectOwners) {
170
+ objects.push({objectId, ownerSessionId});
171
+ }
172
+ objects.sort((a, b) => a.objectId - b.objectId);
173
+ return objects;
174
+ }
175
+
176
+ function notifyObjectRegistered(objectId: number, ownerSessionId: string): void {
177
+ for (const sessionId of connectedSessions) {
178
+ if (sessionId === ownerSessionId) continue;
179
+ sendToClient(sessionId, {
180
+ type: "object_registered",
181
+ objectId,
182
+ ownerSessionId,
183
+ });
184
+ }
185
+ }
186
+
187
+ function notifyObjectReleased(objectId: number, ownerSessionId: string): void {
188
+ for (const sessionId of connectedSessions) {
189
+ if (sessionId === ownerSessionId) continue;
190
+ sendToClient(sessionId, {
191
+ type: "object_released",
192
+ objectId,
193
+ ownerSessionId,
194
+ });
195
+ }
196
+ }
197
+
198
+ function broadcastWorldState(): void {
199
+ if (connectedSessions.size === 0) return;
200
+ // Copy to a detached buffer so downstream sends cannot observe mutations
201
+ // from subsequent writes in the same event loop tick.
202
+ const payload = Buffer.from(worldState.buffer.slice(0));
203
+ for (const sessionId of connectedSessions) {
204
+ sendBinaryToClient(sessionId, payload, "sync");
205
+ }
206
+ }
207
+
208
+ function simulateWorldStep(dtSec: number): void {
209
+ const activeObjectIds = [...objectOwners.keys()];
210
+ for (const objectId of activeObjectIds) {
211
+ const slot = objectIdToSlot(objectId);
212
+ const start = slot * OBJECT_STRIDE;
213
+ if (start < 0 || start + OBJECT_STRIDE > worldState.length) continue;
214
+
215
+ let x = worldState[start + 1] ?? 0;
216
+ let y = worldState[start + 2] ?? 0;
217
+ let vx = worldState[start + 5] ?? 0;
218
+ let vy = worldState[start + 6] ?? 0;
219
+
220
+ x += vx * dtSec;
221
+ y += vy * dtSec;
222
+
223
+ if (x < OBJECT_RADIUS || x > BOARD_WIDTH - OBJECT_RADIUS) {
224
+ vx *= -1;
225
+ x = Math.max(OBJECT_RADIUS, Math.min(BOARD_WIDTH - OBJECT_RADIUS, x));
226
+ }
227
+ if (y < OBJECT_RADIUS || y > BOARD_HEIGHT - OBJECT_RADIUS) {
228
+ vy *= -1;
229
+ y = Math.max(OBJECT_RADIUS, Math.min(BOARD_HEIGHT - OBJECT_RADIUS, y));
230
+ }
231
+
232
+ worldState[start + 1] = x;
233
+ worldState[start + 2] = y;
234
+ worldState[start + 5] = vx;
235
+ worldState[start + 6] = vy;
236
+ }
237
+
238
+ for (let i = 0; i < activeObjectIds.length; i += 1) {
239
+ const aId = activeObjectIds[i];
240
+ const aSlot = objectIdToSlot(aId);
241
+ const aStart = aSlot * OBJECT_STRIDE;
242
+ if (aStart < 0 || aStart + OBJECT_STRIDE > worldState.length) continue;
243
+ for (let j = i + 1; j < activeObjectIds.length; j += 1) {
244
+ const bId = activeObjectIds[j];
245
+ const bSlot = objectIdToSlot(bId);
246
+ const bStart = bSlot * OBJECT_STRIDE;
247
+ if (bStart < 0 || bStart + OBJECT_STRIDE > worldState.length) continue;
248
+
249
+ let ax = worldState[aStart + 1] ?? 0;
250
+ let ay = worldState[aStart + 2] ?? 0;
251
+ let avx = worldState[aStart + 5] ?? 0;
252
+ let avy = worldState[aStart + 6] ?? 0;
253
+ let bx = worldState[bStart + 1] ?? 0;
254
+ let by = worldState[bStart + 2] ?? 0;
255
+ let bvx = worldState[bStart + 5] ?? 0;
256
+ let bvy = worldState[bStart + 6] ?? 0;
257
+
258
+ let dx = bx - ax;
259
+ let dy = by - ay;
260
+ let distSq = dx * dx + dy * dy;
261
+ const minDist = OBJECT_RADIUS * 2;
262
+ const minDistSq = minDist * minDist;
263
+ if (!(distSq > 0 && distSq < minDistSq)) continue;
264
+
265
+ let dist = Math.sqrt(distSq);
266
+ if (dist === 0) {
267
+ // Deterministic fallback axis when centers overlap exactly.
268
+ dx = 1;
269
+ dy = 0;
270
+ dist = 1;
271
+ distSq = 1;
272
+ }
273
+ const nx = dx / dist;
274
+ const ny = dy / dist;
275
+
276
+ // Positional correction prevents objects from remaining overlapped.
277
+ const overlap = minDist - dist;
278
+ const half = overlap * 0.5;
279
+ ax -= nx * half;
280
+ ay -= ny * half;
281
+ bx += nx * half;
282
+ by += ny * half;
283
+
284
+ const rvx = bvx - avx;
285
+ const rvy = bvy - avy;
286
+ const velAlongNormal = rvx * nx + rvy * ny;
287
+ if (velAlongNormal < 0) {
288
+ const impulse = (-(1 + COLLISION_RESTITUTION) * velAlongNormal) / 2;
289
+ avx -= impulse * nx;
290
+ avy -= impulse * ny;
291
+ bvx += impulse * nx;
292
+ bvy += impulse * ny;
293
+ }
294
+
295
+ worldState[aStart + 1] = Math.max(OBJECT_RADIUS, Math.min(BOARD_WIDTH - OBJECT_RADIUS, ax));
296
+ worldState[aStart + 2] = Math.max(OBJECT_RADIUS, Math.min(BOARD_HEIGHT - OBJECT_RADIUS, ay));
297
+ worldState[aStart + 5] = avx;
298
+ worldState[aStart + 6] = avy;
299
+ worldState[bStart + 1] = Math.max(OBJECT_RADIUS, Math.min(BOARD_WIDTH - OBJECT_RADIUS, bx));
300
+ worldState[bStart + 2] = Math.max(OBJECT_RADIUS, Math.min(BOARD_HEIGHT - OBJECT_RADIUS, by));
301
+ worldState[bStart + 5] = bvx;
302
+ worldState[bStart + 6] = bvy;
303
+ }
304
+ }
305
+ }
306
+
307
+ function startBroadcastLoopIfNeeded(): void {
308
+ if (broadcastTimer) return;
309
+ if (connectedSessions.size < 1) return;
310
+
311
+ broadcastTimer = setInterval(() => {
312
+ if (connectedSessions.size < 1) {
313
+ if (broadcastTimer) {
314
+ clearInterval(broadcastTimer);
315
+ broadcastTimer = null;
316
+ }
317
+ return;
318
+ }
319
+ simulateWorldStep(1 / BROADCAST_HZ);
320
+ broadcastWorldState();
321
+ }, BROADCAST_INTERVAL_MS);
322
+
323
+ agentLog("info", `world loop started (${BROADCAST_HZ}Hz)`);
324
+ }
325
+
326
+ function stopBroadcastLoopIfNeeded(): void {
327
+ if (connectedSessions.size >= 1) return;
328
+ if (!broadcastTimer) return;
329
+ clearInterval(broadcastTimer);
330
+ broadcastTimer = null;
331
+ agentLog("info", "world loop stopped");
43
332
  }
44
333
 
45
334
  defineAgent({
46
- onClientJoin({ sessionId }) {
47
- connected.add(sessionId);
48
- sendToClient(sessionId, {
49
- type: "state",
50
- tick,
51
- players: playerCount(),
52
- });
53
- broadcastState(sessionId);
54
- agentLog("info", `join ${sessionId} players=${playerCount()}`);
55
- },
56
-
57
- onClientLeave({ sessionId }) {
58
- connected.delete(sessionId);
59
- broadcastToClients({ type: "state", tick, players: playerCount() }, [
60
- ...connected,
61
- ]);
62
- agentLog("info", `leave ${sessionId} players=${playerCount()}`);
63
- },
64
-
65
- onDataChannelMessage(ctx) {
66
- handleTickMessage(ctx);
67
- },
335
+ onClientJoin({sessionId}) {
336
+ connectedSessions.add(sessionId);
337
+ startBroadcastLoopIfNeeded();
338
+ // New client receives the current ownership map so it can assign stable
339
+ // colors per owner and immediately render known tracked objects.
340
+ sendToClient(sessionId, {
341
+ type: "world_snapshot",
342
+ objects: trackedObjectsSnapshot(),
343
+ });
344
+ agentLog("info", `join ${sessionId} connected=${connectedSessions.size}`);
345
+ },
346
+
347
+ onClientLeave({sessionId}) {
348
+ connectedSessions.delete(sessionId);
349
+
350
+ const owned = sessionObjects.get(sessionId);
351
+ if (owned) {
352
+ for (const objectId of [...owned]) {
353
+ notifyObjectReleased(objectId, sessionId);
354
+ releaseObject(objectId);
355
+ }
356
+ sessionObjects.delete(sessionId);
357
+ }
358
+
359
+ stopBroadcastLoopIfNeeded();
360
+ agentLog(
361
+ "info",
362
+ `leave ${sessionId} connected=${connectedSessions.size} worldFloats=${worldState.length} freeSlots=${freeSlots.length}`,
363
+ );
364
+ },
365
+
366
+ onDataChannelMessage(ctx) {
367
+ // Register is a control-plane message over JSON data channel.
368
+ // Binary channel is reserved for high-frequency state deltas.
369
+ if (parseRegisterCommand(ctx.message)) {
370
+ const objectId = registerObject(ctx.sessionId);
371
+ sendToClient(ctx.sessionId, {type: "register_ack", objectId});
372
+ notifyObjectRegistered(objectId, ctx.sessionId);
373
+ agentLog("info", `register session=${ctx.sessionId} objectId=${objectId}`);
374
+ return;
375
+ }
376
+
377
+ // Optional chat mode for debugging/coordinating live test sessions.
378
+ const chat = parseChatCommand(ctx.message);
379
+ if (!chat) return;
380
+ for (const sessionId of connectedSessions) {
381
+ sendToClient(sessionId, {
382
+ type: "chat_broadcast",
383
+ senderSessionId: ctx.sessionId,
384
+ text: chat.text,
385
+ });
386
+ }
387
+ },
388
+
389
+ onDataChannelBinary(ctx) {
390
+ // Server-authoritative simulation: ignore client binary state writes.
391
+ // Keeping the hook makes intent-based controls easy to add later.
392
+ void ctx;
393
+ },
68
394
  });