onelobby 0.5.0 → 0.6.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.
Files changed (99) hide show
  1. package/README.md +2 -2
  2. package/blueprint.schema.json +8 -1
  3. package/dist/blueprint.d.ts +4 -0
  4. package/dist/blueprint.js +5 -0
  5. package/dist/blueprint.js.map +1 -1
  6. package/dist/codec.d.ts +23 -0
  7. package/dist/codec.js +28 -0
  8. package/dist/codec.js.map +1 -1
  9. package/dist/direct/browser-host.d.ts +46 -0
  10. package/dist/direct/browser-host.js +93 -0
  11. package/dist/direct/browser-host.js.map +1 -0
  12. package/dist/direct/hub-host.d.ts +34 -23
  13. package/dist/direct/hub-host.js +75 -59
  14. package/dist/direct/hub-host.js.map +1 -1
  15. package/dist/direct/hub-local.d.ts +22 -0
  16. package/dist/direct/hub-local.js +46 -0
  17. package/dist/direct/hub-local.js.map +1 -0
  18. package/dist/direct/hub.d.ts +37 -10
  19. package/dist/direct/hub.js +114 -23
  20. package/dist/direct/hub.js.map +1 -1
  21. package/dist/direct/index.d.ts +36 -3
  22. package/dist/direct/index.js +146 -20
  23. package/dist/direct/index.js.map +1 -1
  24. package/dist/direct/metrics.d.ts +12 -0
  25. package/dist/direct/metrics.js +45 -0
  26. package/dist/direct/metrics.js.map +1 -0
  27. package/dist/direct/portmap.d.ts +9 -2
  28. package/dist/direct/portmap.js +7 -2
  29. package/dist/direct/portmap.js.map +1 -1
  30. package/dist/direct/sha1.d.ts +16 -0
  31. package/dist/direct/sha1.js +111 -0
  32. package/dist/direct/sha1.js.map +1 -0
  33. package/dist/direct-address.d.ts +2 -2
  34. package/dist/direct-address.js.map +1 -1
  35. package/dist/friendly.d.ts +23 -0
  36. package/dist/friendly.js +28 -0
  37. package/dist/friendly.js.map +1 -1
  38. package/dist/index.d.ts +8 -2
  39. package/dist/index.js +6 -2
  40. package/dist/index.js.map +1 -1
  41. package/dist/internals.d.ts +9 -0
  42. package/dist/internals.js +9 -0
  43. package/dist/internals.js.map +1 -1
  44. package/dist/join-code-kind.d.ts +10 -0
  45. package/dist/join-code-kind.js +10 -0
  46. package/dist/join-code-kind.js.map +1 -0
  47. package/dist/joincode.d.ts +10 -1
  48. package/dist/joincode.js +23 -3
  49. package/dist/joincode.js.map +1 -1
  50. package/dist/lobby.d.ts +65 -4
  51. package/dist/lobby.js +151 -12
  52. package/dist/lobby.js.map +1 -1
  53. package/dist/lockstep.d.ts +8 -0
  54. package/dist/lockstep.js +23 -2
  55. package/dist/lockstep.js.map +1 -1
  56. package/dist/nostr/crypto.d.ts +20 -0
  57. package/dist/nostr/crypto.js +78 -0
  58. package/dist/nostr/crypto.js.map +1 -0
  59. package/dist/nostr/host.d.ts +66 -0
  60. package/dist/nostr/host.js +196 -0
  61. package/dist/nostr/host.js.map +1 -0
  62. package/dist/nostr/limits.d.ts +31 -0
  63. package/dist/nostr/limits.js +36 -0
  64. package/dist/nostr/limits.js.map +1 -0
  65. package/dist/nostr/link.d.ts +70 -0
  66. package/dist/nostr/link.js +180 -0
  67. package/dist/nostr/link.js.map +1 -0
  68. package/dist/nostr/relays.d.ts +79 -0
  69. package/dist/nostr/relays.js +315 -0
  70. package/dist/nostr/relays.js.map +1 -0
  71. package/dist/nostr/room.d.ts +43 -0
  72. package/dist/nostr/room.js +104 -0
  73. package/dist/nostr/room.js.map +1 -0
  74. package/dist/nostr/secp256k1.d.ts +10 -0
  75. package/dist/nostr/secp256k1.js +171 -0
  76. package/dist/nostr/secp256k1.js.map +1 -0
  77. package/dist/nostr/socket.d.ts +48 -0
  78. package/dist/nostr/socket.js +141 -0
  79. package/dist/nostr/socket.js.map +1 -0
  80. package/dist/onelobby.bundle.js +146 -0
  81. package/dist/onelobby.bundle.js.map +7 -0
  82. package/dist/onelobby.bundle.mjs +11 -5
  83. package/dist/onelobby.bundle.mjs.map +4 -4
  84. package/dist/onelobby.d.ts +31 -3
  85. package/dist/onelobby.global.js +2599 -46
  86. package/dist/onelobby.global.js.map +4 -4
  87. package/dist/onelobby.global.min.js +11 -5
  88. package/dist/onelobby.global.min.js.map +4 -4
  89. package/dist/onelobby.js +68 -14
  90. package/dist/onelobby.js.map +1 -1
  91. package/dist/pause.d.ts +85 -0
  92. package/dist/pause.js +160 -0
  93. package/dist/pause.js.map +1 -0
  94. package/dist/transport.d.ts +3 -1
  95. package/dist/transport.js.map +1 -1
  96. package/dist/webrtc.d.ts +1 -0
  97. package/dist/webrtc.js +6 -1
  98. package/dist/webrtc.js.map +1 -1
  99. package/package.json +1 -1
package/README.md CHANGED
@@ -9,7 +9,7 @@ You need a OneLobby lobby server (the WebSocket URL, like `wss://lobby.example.c
9
9
  ## Use it from a `<script>` tag (no build tools)
10
10
 
11
11
  ```html
12
- <script src="https://cdn.jsdelivr.net/npm/onelobby@0.5.0/dist/onelobby.global.min.js"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/onelobby@0.6.0/dist/onelobby.global.min.js"></script>
13
13
  <script>
14
14
  const kit = new OneLobby.OneLobby({
15
15
  gameId: "com.you.yourgame", // any unique name for your game
@@ -51,7 +51,7 @@ const lobby = await kit.host({ name: "My game", maxPlayers: 4 });
51
51
  console.log("Tell your friend this code:", formatCode(lobby.code));
52
52
  ```
53
53
 
54
- Without a bundler: `import { OneLobby } from "https://cdn.jsdelivr.net/npm/onelobby@0.5.0/dist/onelobby.bundle.mjs"`.
54
+ Without a bundler: `import { OneLobby } from "https://cdn.jsdelivr.net/npm/onelobby@0.6.0/dist/onelobby.bundle.mjs"`.
55
55
 
56
56
  ## Made for players
57
57
 
@@ -55,6 +55,13 @@
55
55
  },
56
56
  "directPort": { "description": "Port a direct host listens on (falls back up to +9 when taken).", "type": "integer", "minimum": 1024, "maximum": 65535, "default": 47810 },
57
57
  "openPort": { "description": "Ask the router to open the direct port (UPnP / NAT-PMP / PCP).", "type": "boolean", "default": true },
58
+ "nostr": { "description": "Direct host: also hand out a Nostr code (version 1) that works through public Nostr relays, no port forwarding needed. Off: IP codes only.", "type": "boolean", "default": true },
59
+ "nostrRelays": {
60
+ "description": "Nostr relay URLs (wss://) for the Nostr code. Default: the library's list, probed with checkNostrRelays when the blueprint was written.",
61
+ "type": "array",
62
+ "items": { "type": "string" },
63
+ "default": ["wss://nos.lol", "wss://relay.primal.net", "wss://nostr-pub.wellorder.net", "wss://nostr.oxtr.dev", "wss://nostr.bitcoiner.social", "wss://relay.nostr.net", "wss://nostr.sathoarder.com"]
64
+ },
58
65
  "relayOnly": { "description": "Server lobbies only: force TURN so players never see each other's IP.", "type": "boolean", "default": false },
59
66
  "allowedOrigins": {
60
67
  "description": "Extra page origins a direct host accepts browser joiners from.",
@@ -134,7 +141,7 @@
134
141
  "default": "end"
135
142
  },
136
143
  "pause": {
137
- "description": "off: no network pause. host: only the host pauses and resumes. anyone: any player can (planned, blueprint s.10).",
144
+ "description": "off: no network pause (lobby.Pause.Request / pause.request throws pause_disabled). host: only the host pauses and resumes. anyone: any player pauses; the pauser or the host resumes. A lobby made without a blueprint behaves as anyone.",
138
145
  "enum": ["off", "host", "anyone"],
139
146
  "default": "off"
140
147
  },
@@ -19,6 +19,8 @@ export interface Blueprint {
19
19
  readonly lobbyServer: string;
20
20
  readonly directPort: number;
21
21
  readonly openPort: boolean;
22
+ readonly nostr: boolean;
23
+ readonly nostrRelays: readonly string[];
22
24
  readonly relayOnly: boolean;
23
25
  readonly allowedOrigins: readonly string[];
24
26
  };
@@ -91,6 +93,8 @@ export interface BlueprintInput {
91
93
  lobbyServer?: string;
92
94
  directPort?: number;
93
95
  openPort?: boolean;
96
+ nostr?: boolean;
97
+ nostrRelays?: string[];
94
98
  relayOnly?: boolean;
95
99
  allowedOrigins?: string[];
96
100
  };
package/dist/blueprint.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * object. Errors carry the dotted `path` and the `rule` id the shared vectors (spec/vectors/blueprint.json) expect.
5
5
  */
6
6
  import { OneLobbyError } from "./signaling.js";
7
+ import { DEFAULT_NOSTR_RELAYS } from "./nostr/relays.js";
7
8
  /** Identity function for TypeScript completion in a `.ts` config (like Vite's `defineConfig`). */
8
9
  export function defineBlueprint(x) {
9
10
  return x;
@@ -33,6 +34,8 @@ const ROOT = obj({
33
34
  lobbyServer: str(),
34
35
  directPort: int(1024, 65535),
35
36
  openPort: bool,
37
+ nostr: bool,
38
+ nostrRelays: { k: "strarr" },
36
39
  relayOnly: bool,
37
40
  allowedOrigins: { k: "strarr" },
38
41
  }),
@@ -212,6 +215,8 @@ function fill(input) {
212
215
  lobbyServer: hosting.lobbyServer ?? "",
213
216
  directPort: hosting.directPort ?? 47810,
214
217
  openPort: hosting.openPort ?? true,
218
+ nostr: hosting.nostr ?? true,
219
+ nostrRelays: hosting.nostrRelays ?? [...DEFAULT_NOSTR_RELAYS],
215
220
  relayOnly: hosting.relayOnly ?? false,
216
221
  allowedOrigins: hosting.allowedOrigins ?? [],
217
222
  },
@@ -1 +1 @@
1
- {"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Blueprint v1: the `onelobby.json` settings file (docs/design/blueprint.md s.4, spec/blueprint.schema.json).\n * `parseBlueprint` validates by hand (the package has no dependencies), fills the defaults and returns a frozen\n * object. Errors carry the dotted `path` and the `rule` id the shared vectors (spec/vectors/blueprint.json) expect.\n */\nimport { OneLobbyError } from \"./signaling.js\";\n\nexport type BlueprintEngine = \"web\" | \"node-browser\" | \"godot-csharp\" | \"unity\" | \"dotnet\";\n\nexport interface Blueprint {\n readonly version: 1;\n readonly game: { readonly id: string; readonly netVersion: string; readonly title?: string };\n readonly engine?: BlueprintEngine;\n readonly hosting: {\n readonly direct: boolean;\n readonly server: boolean;\n readonly lobbyServer: string;\n readonly directPort: number;\n readonly openPort: boolean;\n readonly relayOnly: boolean;\n readonly allowedOrigins: readonly string[];\n };\n readonly lobby: {\n readonly maxPlayers: number;\n readonly listed: boolean;\n readonly approveJoins: boolean;\n readonly password: boolean;\n readonly lateJoin: boolean;\n readonly rejoin: boolean;\n readonly joinLink: boolean;\n readonly meta: Readonly<Record<string, string>>;\n };\n readonly sync: {\n readonly mode: \"raw\" | \"lockstep\" | \"replication\";\n readonly tickRate: number;\n readonly hashEveryTicks: number;\n };\n readonly players: { readonly identity: \"name\" | \"anonymous\" | \"platform\"; readonly maxNameLength: number };\n readonly chat: { readonly text: boolean; readonly voice: boolean; readonly voiceMode: \"pushToTalk\" | \"voiceActivity\" };\n readonly session: {\n readonly hostLeaves: \"end\" | \"migrate\";\n readonly pause: \"off\" | \"host\" | \"anyone\";\n readonly readyTimeoutMs: number;\n readonly peerTimeoutMs: number;\n readonly rateLimit?: { readonly messages: number; readonly perMs: number };\n readonly runInBackground: boolean;\n };\n readonly platforms: {\n readonly steam: boolean | { readonly appId?: number };\n readonly epic: boolean | { readonly [k: string]: string };\n readonly discord: boolean;\n };\n readonly ui: { readonly kind: \"drop-in\" | \"generated\" | \"none\"; readonly entry?: string; readonly theme: \"light\" | \"dark\" | \"auto\" };\n}\n\n/** What you may write: only `game.id` is required. */\nexport interface BlueprintInput {\n $schema?: string;\n $comment?: string;\n version?: 1;\n game: { $comment?: string; id: string; netVersion?: string; title?: string };\n engine?: BlueprintEngine;\n hosting?: {\n $comment?: string;\n direct?: boolean;\n server?: boolean;\n lobbyServer?: string;\n directPort?: number;\n openPort?: boolean;\n relayOnly?: boolean;\n allowedOrigins?: string[];\n };\n lobby?: {\n $comment?: string;\n maxPlayers?: number;\n listed?: boolean;\n approveJoins?: boolean;\n password?: boolean;\n lateJoin?: boolean;\n rejoin?: boolean;\n joinLink?: boolean;\n meta?: Record<string, string>;\n };\n sync?: { $comment?: string; mode?: \"raw\" | \"lockstep\" | \"replication\"; tickRate?: number; hashEveryTicks?: number };\n players?: { $comment?: string; identity?: \"name\" | \"anonymous\" | \"platform\"; maxNameLength?: number };\n chat?: { $comment?: string; text?: boolean; voice?: boolean; voiceMode?: \"pushToTalk\" | \"voiceActivity\" };\n session?: {\n $comment?: string;\n hostLeaves?: \"end\" | \"migrate\";\n pause?: \"off\" | \"host\" | \"anyone\";\n readyTimeoutMs?: number;\n peerTimeoutMs?: number;\n rateLimit?: { messages: number; perMs: number };\n runInBackground?: boolean;\n };\n platforms?: {\n $comment?: string;\n steam?: boolean | { appId?: number };\n epic?: boolean | { [k: string]: string };\n discord?: boolean;\n };\n ui?: { $comment?: string; kind?: \"drop-in\" | \"generated\" | \"none\"; entry?: string; theme?: \"light\" | \"dark\" | \"auto\" };\n}\n\n/** Identity function for TypeScript completion in a `.ts` config (like Vite's `defineConfig`). */\nexport function defineBlueprint<T extends BlueprintInput>(x: T): T {\n return x;\n}\n\n/** `bad_config`: the file is not a valid blueprint. `path` is dotted (\"\" = root), `rule` a vector rule id. */\nexport class BlueprintError extends OneLobbyError {\n constructor(public path: string, public rule: string, detail: string) {\n super(\"bad_config\", `onelobby.json: ${path ? path + \": \" : \"\"}${detail}`);\n }\n}\n\n// ---- schema as data (key order = spec/blueprint.schema.json) ----------------------------------------------------\n\ntype Node =\n | { k: \"obj\"; req?: string[]; props: Record<string, Node> }\n | { k: \"str\"; pattern?: RegExp; min?: number; max?: number }\n | { k: \"int\"; min?: number; max?: number; zeroOr?: boolean }\n | { k: \"bool\" }\n | { k: \"enum\"; vals: string[] }\n | { k: \"const\"; val: number }\n | { k: \"strarr\" }\n | { k: \"meta\" }\n | { k: \"rate\" }\n | { k: \"steam\" }\n | { k: \"epic\" };\n\nconst obj = (props: Record<string, Node>, req?: string[]): Node => ({ k: \"obj\", props, req });\nconst bool: Node = { k: \"bool\" };\nconst str = (o: { pattern?: RegExp; min?: number; max?: number } = {}): Node => ({ k: \"str\", ...o });\nconst int = (min?: number, max?: number, zeroOr?: boolean): Node => ({ k: \"int\", min, max, zeroOr });\nconst en = (...vals: string[]): Node => ({ k: \"enum\", vals });\n\nconst ROOT: Node = obj(\n {\n version: { k: \"const\", val: 1 },\n game: obj(\n { id: str({ pattern: /^[A-Za-z0-9._-]{1,64}$/ }), netVersion: str({ min: 1, max: 32 }), title: str({ max: 64 }) },\n [\"id\"],\n ),\n engine: en(\"web\", \"node-browser\", \"godot-csharp\", \"unity\", \"dotnet\"),\n hosting: obj({\n direct: bool,\n server: bool,\n lobbyServer: str(),\n directPort: int(1024, 65535),\n openPort: bool,\n relayOnly: bool,\n allowedOrigins: { k: \"strarr\" },\n }),\n lobby: obj({\n maxPlayers: int(2, 64),\n listed: bool,\n approveJoins: bool,\n password: bool,\n lateJoin: bool,\n rejoin: bool,\n joinLink: bool,\n meta: { k: \"meta\" },\n }),\n sync: obj({ mode: en(\"raw\", \"lockstep\", \"replication\"), tickRate: int(1, 120), hashEveryTicks: int(1, 100000) }),\n players: obj({ identity: en(\"name\", \"anonymous\", \"platform\"), maxNameLength: int(1, 24) }),\n chat: obj({ text: bool, voice: bool, voiceMode: en(\"pushToTalk\", \"voiceActivity\") }),\n session: obj({\n hostLeaves: en(\"end\", \"migrate\"),\n pause: en(\"off\", \"host\", \"anyone\"),\n readyTimeoutMs: int(0, 600000),\n peerTimeoutMs: int(6000, 600000, true),\n rateLimit: { k: \"rate\" },\n runInBackground: bool,\n }),\n platforms: obj({ steam: { k: \"steam\" }, epic: { k: \"epic\" }, discord: bool }),\n ui: obj({ kind: en(\"drop-in\", \"generated\", \"none\"), entry: str(), theme: en(\"light\", \"dark\", \"auto\") }),\n },\n [\"game\"],\n);\n\n/** Allowed (and ignored) anywhere. */\nconst IGNORED = new Set([\"$schema\", \"$comment\"]);\n\nclass Fail {\n constructor(public path: string, public rule: string, public detail: string) {}\n}\nconst join = (p: string, k: string | number) => (p ? `${p}.${k}` : String(k));\nconst isObj = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nfunction check(n: Node, v: unknown, path: string): void {\n switch (n.k) {\n case \"obj\": {\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be an object\");\n for (const r of n.req ?? []) if (!(r in v)) throw new Fail(join(path, r), \"required\", \"is required\");\n for (const k of Object.keys(v)) if (!IGNORED.has(k) && !(k in n.props)) throw new Fail(join(path, k), \"unknown_key\", \"unknown key\");\n for (const [k, sub] of Object.entries(n.props)) if (k in v) check(sub, v[k], join(path, k));\n return;\n }\n case \"str\": {\n if (typeof v !== \"string\") throw new Fail(path, \"type\", \"must be a string\");\n if ((n.min !== undefined && v.length < n.min) || (n.max !== undefined && v.length > n.max))\n throw new Fail(path, \"length\", `must be ${n.min ?? 0} to ${n.max ?? \"any\"} characters`);\n if (n.pattern && !n.pattern.test(v)) throw new Fail(path, \"pattern\", `must match ${n.pattern.source}`);\n return;\n }\n case \"int\": {\n if (typeof v !== \"number\" || !Number.isInteger(v)) throw new Fail(path, \"type\", \"must be an integer\");\n if (n.zeroOr && v === 0) return;\n if ((n.min !== undefined && v < n.min) || (n.max !== undefined && v > n.max))\n throw new Fail(path, \"range\", `must be ${n.zeroOr ? \"0 or \" : \"\"}${n.min ?? \"any\"} to ${n.max ?? \"any\"}`);\n return;\n }\n case \"bool\":\n if (typeof v !== \"boolean\") throw new Fail(path, \"type\", \"must be true or false\");\n return;\n case \"enum\":\n if (typeof v !== \"string\" || !n.vals.includes(v)) throw new Fail(path, \"enum\", `must be one of ${n.vals.join(\", \")}`);\n return;\n case \"const\":\n if (v !== n.val) throw new Fail(path, \"const\", `must be ${n.val}`);\n return;\n case \"strarr\": {\n if (!Array.isArray(v)) throw new Fail(path, \"type\", \"must be an array of strings\");\n v.forEach((x, i) => {\n if (typeof x !== \"string\") throw new Fail(join(path, i), \"type\", \"must be a string\");\n });\n return;\n }\n case \"meta\": {\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be an object of strings\");\n const keys = Object.keys(v);\n if (keys.length > 16) throw new Fail(path, \"length\", \"at most 16 keys\");\n for (const k of keys) {\n if (k.length > 64) throw new Fail(join(path, k), \"length\", \"key is longer than 64 characters\");\n const x = v[k];\n if (typeof x !== \"string\") throw new Fail(join(path, k), \"type\", \"must be a string\");\n if (x.length > 64) throw new Fail(join(path, k), \"length\", \"value is longer than 64 characters\");\n }\n return;\n }\n case \"rate\":\n check(obj({ messages: int(1), perMs: int(1) }, [\"messages\", \"perMs\"]), v, path);\n return;\n case \"steam\":\n if (typeof v === \"boolean\") return;\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be true, false or { appId }\");\n check(obj({ appId: int(1) }), v, path);\n return;\n case \"epic\": {\n if (typeof v === \"boolean\") return;\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be true, false or { productId }\");\n for (const k of Object.keys(v)) if (!IGNORED.has(k) && typeof v[k] !== \"string\") throw new Fail(join(path, k), \"type\", \"must be a string\");\n return;\n }\n }\n}\n\n// ---- defaults and cross rules -----------------------------------------------------------------------------------\n\nconst sect = (x: unknown): Record<string, unknown> => (isObj(x) ? x : {});\nfunction strip(o: unknown): unknown {\n if (Array.isArray(o)) return o.map(strip);\n if (!isObj(o)) return o;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(o)) if (k !== \"$comment\" && k !== \"$schema\") out[k] = strip(v);\n return out;\n}\nfunction deepFreeze<T>(o: T): T {\n if (typeof o === \"object\" && o !== null && !Object.isFrozen(o)) {\n Object.freeze(o);\n for (const v of Object.values(o)) deepFreeze(v);\n }\n return o;\n}\n\nfunction fill(input: Record<string, unknown>): Blueprint {\n const game = sect(input.game);\n const engine = input.engine as BlueprintEngine | undefined;\n const hosting = sect(input.hosting);\n const lobby = sect(input.lobby);\n const sync = sect(input.sync);\n const players = sect(input.players);\n const chat = sect(input.chat);\n const session = sect(input.session);\n const platforms = sect(input.platforms);\n const ui = sect(input.ui);\n const tickRate = (sync.tickRate as number | undefined) ?? 20;\n const web = engine === \"web\";\n const out = {\n version: 1,\n game: { id: game.id, netVersion: game.netVersion ?? \"1\", ...(game.title !== undefined ? { title: game.title } : {}) },\n ...(engine !== undefined ? { engine } : {}),\n hosting: {\n direct: hosting.direct ?? !web,\n server: hosting.server ?? web,\n lobbyServer: hosting.lobbyServer ?? \"\",\n directPort: hosting.directPort ?? 47810,\n openPort: hosting.openPort ?? true,\n relayOnly: hosting.relayOnly ?? false,\n allowedOrigins: hosting.allowedOrigins ?? [],\n },\n lobby: {\n maxPlayers: lobby.maxPlayers ?? 4,\n listed: lobby.listed ?? false,\n approveJoins: lobby.approveJoins ?? false,\n password: lobby.password ?? false,\n lateJoin: lobby.lateJoin ?? true,\n rejoin: lobby.rejoin ?? true,\n joinLink: lobby.joinLink ?? (web || engine === \"node-browser\"),\n meta: lobby.meta ?? {},\n },\n sync: { mode: sync.mode ?? \"raw\", tickRate, hashEveryTicks: sync.hashEveryTicks ?? tickRate },\n players: { identity: players.identity ?? \"name\", maxNameLength: players.maxNameLength ?? 24 },\n chat: { text: chat.text ?? true, voice: chat.voice ?? false, voiceMode: chat.voiceMode ?? \"pushToTalk\" },\n session: {\n hostLeaves: session.hostLeaves ?? \"end\",\n pause: session.pause ?? \"off\",\n readyTimeoutMs: session.readyTimeoutMs ?? 15000,\n peerTimeoutMs: session.peerTimeoutMs ?? 0,\n ...(session.rateLimit !== undefined ? { rateLimit: session.rateLimit } : {}),\n runInBackground: session.runInBackground ?? true,\n },\n platforms: { steam: platforms.steam ?? false, epic: platforms.epic ?? false, discord: platforms.discord ?? false },\n ui: {\n kind: ui.kind ?? (web ? \"drop-in\" : \"generated\"),\n ...(ui.entry !== undefined ? { entry: ui.entry } : {}),\n theme: ui.theme ?? \"auto\",\n },\n };\n return out as unknown as Blueprint;\n}\n\nfunction crossRules(b: Blueprint): void {\n if (!b.hosting.direct && !b.hosting.server) throw new Fail(\"hosting\", \"hosting_none\", \"at least one of direct, server must be true\");\n if (b.lobby.listed && b.lobby.approveJoins) throw new Fail(\"lobby\", \"listed_and_approve\", \"listed and approveJoins cannot both be true\");\n if (b.engine === \"web\" && b.hosting.direct) throw new Fail(\"hosting.direct\", \"direct_needs_listener\", \"engine web cannot host direct (a browser cannot listen on a port)\");\n const csharp = b.engine === \"godot-csharp\" || b.engine === \"unity\" || b.engine === \"dotnet\";\n if (b.sync.mode === \"replication\" && !csharp) throw new Fail(\"sync.mode\", \"replication_needs_csharp\", \"replication needs engine godot-csharp, unity or dotnet\");\n if (b.sync.mode === \"replication\" && b.session.hostLeaves === \"migrate\") throw new Fail(\"session.hostLeaves\", \"migrate_with_replication\", \"replication has no host migration; use end\");\n if (b.session.hostLeaves === \"migrate\" && !b.hosting.server) throw new Fail(\"session.hostLeaves\", \"migrate_needs_server\", \"migrate needs hosting.server (direct lobbies always end)\");\n}\n\n/**\n * Validate `json` (an already-parsed onelobby.json), fill every default and return the frozen result.\n * Throws a `BlueprintError` (`OneLobbyError` code `bad_config`, with `path` and `rule`) on the first problem.\n */\nexport function parseBlueprint(json: unknown): Blueprint {\n let b: Blueprint;\n try {\n check(ROOT, json, \"\");\n b = fill(strip(json) as Record<string, unknown>);\n crossRules(b);\n } catch (e) {\n if (e instanceof Fail) throw new BlueprintError(e.path, e.rule, e.detail);\n throw e;\n }\n return deepFreeze(b);\n}\n"]}
1
+ {"version":3,"file":"blueprint.js","sourceRoot":"","sources":["../src/blueprint.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Blueprint v1: the `onelobby.json` settings file (docs/design/blueprint.md s.4, spec/blueprint.schema.json).\n * `parseBlueprint` validates by hand (the package has no dependencies), fills the defaults and returns a frozen\n * object. Errors carry the dotted `path` and the `rule` id the shared vectors (spec/vectors/blueprint.json) expect.\n */\nimport { OneLobbyError } from \"./signaling.js\";\nimport { DEFAULT_NOSTR_RELAYS } from \"./nostr/relays.js\";\n\nexport type BlueprintEngine = \"web\" | \"node-browser\" | \"godot-csharp\" | \"unity\" | \"dotnet\";\n\nexport interface Blueprint {\n readonly version: 1;\n readonly game: { readonly id: string; readonly netVersion: string; readonly title?: string };\n readonly engine?: BlueprintEngine;\n readonly hosting: {\n readonly direct: boolean;\n readonly server: boolean;\n readonly lobbyServer: string;\n readonly directPort: number;\n readonly openPort: boolean;\n readonly nostr: boolean;\n readonly nostrRelays: readonly string[];\n readonly relayOnly: boolean;\n readonly allowedOrigins: readonly string[];\n };\n readonly lobby: {\n readonly maxPlayers: number;\n readonly listed: boolean;\n readonly approveJoins: boolean;\n readonly password: boolean;\n readonly lateJoin: boolean;\n readonly rejoin: boolean;\n readonly joinLink: boolean;\n readonly meta: Readonly<Record<string, string>>;\n };\n readonly sync: {\n readonly mode: \"raw\" | \"lockstep\" | \"replication\";\n readonly tickRate: number;\n readonly hashEveryTicks: number;\n };\n readonly players: { readonly identity: \"name\" | \"anonymous\" | \"platform\"; readonly maxNameLength: number };\n readonly chat: { readonly text: boolean; readonly voice: boolean; readonly voiceMode: \"pushToTalk\" | \"voiceActivity\" };\n readonly session: {\n readonly hostLeaves: \"end\" | \"migrate\";\n readonly pause: \"off\" | \"host\" | \"anyone\";\n readonly readyTimeoutMs: number;\n readonly peerTimeoutMs: number;\n readonly rateLimit?: { readonly messages: number; readonly perMs: number };\n readonly runInBackground: boolean;\n };\n readonly platforms: {\n readonly steam: boolean | { readonly appId?: number };\n readonly epic: boolean | { readonly [k: string]: string };\n readonly discord: boolean;\n };\n readonly ui: { readonly kind: \"drop-in\" | \"generated\" | \"none\"; readonly entry?: string; readonly theme: \"light\" | \"dark\" | \"auto\" };\n}\n\n/** What you may write: only `game.id` is required. */\nexport interface BlueprintInput {\n $schema?: string;\n $comment?: string;\n version?: 1;\n game: { $comment?: string; id: string; netVersion?: string; title?: string };\n engine?: BlueprintEngine;\n hosting?: {\n $comment?: string;\n direct?: boolean;\n server?: boolean;\n lobbyServer?: string;\n directPort?: number;\n openPort?: boolean;\n nostr?: boolean;\n nostrRelays?: string[];\n relayOnly?: boolean;\n allowedOrigins?: string[];\n };\n lobby?: {\n $comment?: string;\n maxPlayers?: number;\n listed?: boolean;\n approveJoins?: boolean;\n password?: boolean;\n lateJoin?: boolean;\n rejoin?: boolean;\n joinLink?: boolean;\n meta?: Record<string, string>;\n };\n sync?: { $comment?: string; mode?: \"raw\" | \"lockstep\" | \"replication\"; tickRate?: number; hashEveryTicks?: number };\n players?: { $comment?: string; identity?: \"name\" | \"anonymous\" | \"platform\"; maxNameLength?: number };\n chat?: { $comment?: string; text?: boolean; voice?: boolean; voiceMode?: \"pushToTalk\" | \"voiceActivity\" };\n session?: {\n $comment?: string;\n hostLeaves?: \"end\" | \"migrate\";\n pause?: \"off\" | \"host\" | \"anyone\";\n readyTimeoutMs?: number;\n peerTimeoutMs?: number;\n rateLimit?: { messages: number; perMs: number };\n runInBackground?: boolean;\n };\n platforms?: {\n $comment?: string;\n steam?: boolean | { appId?: number };\n epic?: boolean | { [k: string]: string };\n discord?: boolean;\n };\n ui?: { $comment?: string; kind?: \"drop-in\" | \"generated\" | \"none\"; entry?: string; theme?: \"light\" | \"dark\" | \"auto\" };\n}\n\n/** Identity function for TypeScript completion in a `.ts` config (like Vite's `defineConfig`). */\nexport function defineBlueprint<T extends BlueprintInput>(x: T): T {\n return x;\n}\n\n/** `bad_config`: the file is not a valid blueprint. `path` is dotted (\"\" = root), `rule` a vector rule id. */\nexport class BlueprintError extends OneLobbyError {\n constructor(public path: string, public rule: string, detail: string) {\n super(\"bad_config\", `onelobby.json: ${path ? path + \": \" : \"\"}${detail}`);\n }\n}\n\n// ---- schema as data (key order = spec/blueprint.schema.json) ----------------------------------------------------\n\ntype Node =\n | { k: \"obj\"; req?: string[]; props: Record<string, Node> }\n | { k: \"str\"; pattern?: RegExp; min?: number; max?: number }\n | { k: \"int\"; min?: number; max?: number; zeroOr?: boolean }\n | { k: \"bool\" }\n | { k: \"enum\"; vals: string[] }\n | { k: \"const\"; val: number }\n | { k: \"strarr\" }\n | { k: \"meta\" }\n | { k: \"rate\" }\n | { k: \"steam\" }\n | { k: \"epic\" };\n\nconst obj = (props: Record<string, Node>, req?: string[]): Node => ({ k: \"obj\", props, req });\nconst bool: Node = { k: \"bool\" };\nconst str = (o: { pattern?: RegExp; min?: number; max?: number } = {}): Node => ({ k: \"str\", ...o });\nconst int = (min?: number, max?: number, zeroOr?: boolean): Node => ({ k: \"int\", min, max, zeroOr });\nconst en = (...vals: string[]): Node => ({ k: \"enum\", vals });\n\nconst ROOT: Node = obj(\n {\n version: { k: \"const\", val: 1 },\n game: obj(\n { id: str({ pattern: /^[A-Za-z0-9._-]{1,64}$/ }), netVersion: str({ min: 1, max: 32 }), title: str({ max: 64 }) },\n [\"id\"],\n ),\n engine: en(\"web\", \"node-browser\", \"godot-csharp\", \"unity\", \"dotnet\"),\n hosting: obj({\n direct: bool,\n server: bool,\n lobbyServer: str(),\n directPort: int(1024, 65535),\n openPort: bool,\n nostr: bool,\n nostrRelays: { k: \"strarr\" },\n relayOnly: bool,\n allowedOrigins: { k: \"strarr\" },\n }),\n lobby: obj({\n maxPlayers: int(2, 64),\n listed: bool,\n approveJoins: bool,\n password: bool,\n lateJoin: bool,\n rejoin: bool,\n joinLink: bool,\n meta: { k: \"meta\" },\n }),\n sync: obj({ mode: en(\"raw\", \"lockstep\", \"replication\"), tickRate: int(1, 120), hashEveryTicks: int(1, 100000) }),\n players: obj({ identity: en(\"name\", \"anonymous\", \"platform\"), maxNameLength: int(1, 24) }),\n chat: obj({ text: bool, voice: bool, voiceMode: en(\"pushToTalk\", \"voiceActivity\") }),\n session: obj({\n hostLeaves: en(\"end\", \"migrate\"),\n pause: en(\"off\", \"host\", \"anyone\"),\n readyTimeoutMs: int(0, 600000),\n peerTimeoutMs: int(6000, 600000, true),\n rateLimit: { k: \"rate\" },\n runInBackground: bool,\n }),\n platforms: obj({ steam: { k: \"steam\" }, epic: { k: \"epic\" }, discord: bool }),\n ui: obj({ kind: en(\"drop-in\", \"generated\", \"none\"), entry: str(), theme: en(\"light\", \"dark\", \"auto\") }),\n },\n [\"game\"],\n);\n\n/** Allowed (and ignored) anywhere. */\nconst IGNORED = new Set([\"$schema\", \"$comment\"]);\n\nclass Fail {\n constructor(public path: string, public rule: string, public detail: string) {}\n}\nconst join = (p: string, k: string | number) => (p ? `${p}.${k}` : String(k));\nconst isObj = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nfunction check(n: Node, v: unknown, path: string): void {\n switch (n.k) {\n case \"obj\": {\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be an object\");\n for (const r of n.req ?? []) if (!(r in v)) throw new Fail(join(path, r), \"required\", \"is required\");\n for (const k of Object.keys(v)) if (!IGNORED.has(k) && !(k in n.props)) throw new Fail(join(path, k), \"unknown_key\", \"unknown key\");\n for (const [k, sub] of Object.entries(n.props)) if (k in v) check(sub, v[k], join(path, k));\n return;\n }\n case \"str\": {\n if (typeof v !== \"string\") throw new Fail(path, \"type\", \"must be a string\");\n if ((n.min !== undefined && v.length < n.min) || (n.max !== undefined && v.length > n.max))\n throw new Fail(path, \"length\", `must be ${n.min ?? 0} to ${n.max ?? \"any\"} characters`);\n if (n.pattern && !n.pattern.test(v)) throw new Fail(path, \"pattern\", `must match ${n.pattern.source}`);\n return;\n }\n case \"int\": {\n if (typeof v !== \"number\" || !Number.isInteger(v)) throw new Fail(path, \"type\", \"must be an integer\");\n if (n.zeroOr && v === 0) return;\n if ((n.min !== undefined && v < n.min) || (n.max !== undefined && v > n.max))\n throw new Fail(path, \"range\", `must be ${n.zeroOr ? \"0 or \" : \"\"}${n.min ?? \"any\"} to ${n.max ?? \"any\"}`);\n return;\n }\n case \"bool\":\n if (typeof v !== \"boolean\") throw new Fail(path, \"type\", \"must be true or false\");\n return;\n case \"enum\":\n if (typeof v !== \"string\" || !n.vals.includes(v)) throw new Fail(path, \"enum\", `must be one of ${n.vals.join(\", \")}`);\n return;\n case \"const\":\n if (v !== n.val) throw new Fail(path, \"const\", `must be ${n.val}`);\n return;\n case \"strarr\": {\n if (!Array.isArray(v)) throw new Fail(path, \"type\", \"must be an array of strings\");\n v.forEach((x, i) => {\n if (typeof x !== \"string\") throw new Fail(join(path, i), \"type\", \"must be a string\");\n });\n return;\n }\n case \"meta\": {\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be an object of strings\");\n const keys = Object.keys(v);\n if (keys.length > 16) throw new Fail(path, \"length\", \"at most 16 keys\");\n for (const k of keys) {\n if (k.length > 64) throw new Fail(join(path, k), \"length\", \"key is longer than 64 characters\");\n const x = v[k];\n if (typeof x !== \"string\") throw new Fail(join(path, k), \"type\", \"must be a string\");\n if (x.length > 64) throw new Fail(join(path, k), \"length\", \"value is longer than 64 characters\");\n }\n return;\n }\n case \"rate\":\n check(obj({ messages: int(1), perMs: int(1) }, [\"messages\", \"perMs\"]), v, path);\n return;\n case \"steam\":\n if (typeof v === \"boolean\") return;\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be true, false or { appId }\");\n check(obj({ appId: int(1) }), v, path);\n return;\n case \"epic\": {\n if (typeof v === \"boolean\") return;\n if (!isObj(v)) throw new Fail(path, \"type\", \"must be true, false or { productId }\");\n for (const k of Object.keys(v)) if (!IGNORED.has(k) && typeof v[k] !== \"string\") throw new Fail(join(path, k), \"type\", \"must be a string\");\n return;\n }\n }\n}\n\n// ---- defaults and cross rules -----------------------------------------------------------------------------------\n\nconst sect = (x: unknown): Record<string, unknown> => (isObj(x) ? x : {});\nfunction strip(o: unknown): unknown {\n if (Array.isArray(o)) return o.map(strip);\n if (!isObj(o)) return o;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(o)) if (k !== \"$comment\" && k !== \"$schema\") out[k] = strip(v);\n return out;\n}\nfunction deepFreeze<T>(o: T): T {\n if (typeof o === \"object\" && o !== null && !Object.isFrozen(o)) {\n Object.freeze(o);\n for (const v of Object.values(o)) deepFreeze(v);\n }\n return o;\n}\n\nfunction fill(input: Record<string, unknown>): Blueprint {\n const game = sect(input.game);\n const engine = input.engine as BlueprintEngine | undefined;\n const hosting = sect(input.hosting);\n const lobby = sect(input.lobby);\n const sync = sect(input.sync);\n const players = sect(input.players);\n const chat = sect(input.chat);\n const session = sect(input.session);\n const platforms = sect(input.platforms);\n const ui = sect(input.ui);\n const tickRate = (sync.tickRate as number | undefined) ?? 20;\n const web = engine === \"web\";\n const out = {\n version: 1,\n game: { id: game.id, netVersion: game.netVersion ?? \"1\", ...(game.title !== undefined ? { title: game.title } : {}) },\n ...(engine !== undefined ? { engine } : {}),\n hosting: {\n direct: hosting.direct ?? !web,\n server: hosting.server ?? web,\n lobbyServer: hosting.lobbyServer ?? \"\",\n directPort: hosting.directPort ?? 47810,\n openPort: hosting.openPort ?? true,\n nostr: hosting.nostr ?? true,\n nostrRelays: hosting.nostrRelays ?? [...DEFAULT_NOSTR_RELAYS],\n relayOnly: hosting.relayOnly ?? false,\n allowedOrigins: hosting.allowedOrigins ?? [],\n },\n lobby: {\n maxPlayers: lobby.maxPlayers ?? 4,\n listed: lobby.listed ?? false,\n approveJoins: lobby.approveJoins ?? false,\n password: lobby.password ?? false,\n lateJoin: lobby.lateJoin ?? true,\n rejoin: lobby.rejoin ?? true,\n joinLink: lobby.joinLink ?? (web || engine === \"node-browser\"),\n meta: lobby.meta ?? {},\n },\n sync: { mode: sync.mode ?? \"raw\", tickRate, hashEveryTicks: sync.hashEveryTicks ?? tickRate },\n players: { identity: players.identity ?? \"name\", maxNameLength: players.maxNameLength ?? 24 },\n chat: { text: chat.text ?? true, voice: chat.voice ?? false, voiceMode: chat.voiceMode ?? \"pushToTalk\" },\n session: {\n hostLeaves: session.hostLeaves ?? \"end\",\n pause: session.pause ?? \"off\",\n readyTimeoutMs: session.readyTimeoutMs ?? 15000,\n peerTimeoutMs: session.peerTimeoutMs ?? 0,\n ...(session.rateLimit !== undefined ? { rateLimit: session.rateLimit } : {}),\n runInBackground: session.runInBackground ?? true,\n },\n platforms: { steam: platforms.steam ?? false, epic: platforms.epic ?? false, discord: platforms.discord ?? false },\n ui: {\n kind: ui.kind ?? (web ? \"drop-in\" : \"generated\"),\n ...(ui.entry !== undefined ? { entry: ui.entry } : {}),\n theme: ui.theme ?? \"auto\",\n },\n };\n return out as unknown as Blueprint;\n}\n\nfunction crossRules(b: Blueprint): void {\n if (!b.hosting.direct && !b.hosting.server) throw new Fail(\"hosting\", \"hosting_none\", \"at least one of direct, server must be true\");\n if (b.lobby.listed && b.lobby.approveJoins) throw new Fail(\"lobby\", \"listed_and_approve\", \"listed and approveJoins cannot both be true\");\n if (b.engine === \"web\" && b.hosting.direct) throw new Fail(\"hosting.direct\", \"direct_needs_listener\", \"engine web cannot host direct (a browser cannot listen on a port)\");\n const csharp = b.engine === \"godot-csharp\" || b.engine === \"unity\" || b.engine === \"dotnet\";\n if (b.sync.mode === \"replication\" && !csharp) throw new Fail(\"sync.mode\", \"replication_needs_csharp\", \"replication needs engine godot-csharp, unity or dotnet\");\n if (b.sync.mode === \"replication\" && b.session.hostLeaves === \"migrate\") throw new Fail(\"session.hostLeaves\", \"migrate_with_replication\", \"replication has no host migration; use end\");\n if (b.session.hostLeaves === \"migrate\" && !b.hosting.server) throw new Fail(\"session.hostLeaves\", \"migrate_needs_server\", \"migrate needs hosting.server (direct lobbies always end)\");\n}\n\n/**\n * Validate `json` (an already-parsed onelobby.json), fill every default and return the frozen result.\n * Throws a `BlueprintError` (`OneLobbyError` code `bad_config`, with `path` and `rule`) on the first problem.\n */\nexport function parseBlueprint(json: unknown): Blueprint {\n let b: Blueprint;\n try {\n check(ROOT, json, \"\");\n b = fill(strip(json) as Record<string, unknown>);\n crossRules(b);\n } catch (e) {\n if (e instanceof Fail) throw new BlueprintError(e.path, e.rule, e.detail);\n throw e;\n }\n return deepFreeze(b);\n}\n"]}
package/dist/codec.d.ts CHANGED
@@ -25,6 +25,9 @@ export declare const MsgType: {
25
25
  readonly chat: 96;
26
26
  readonly chat_history: 97;
27
27
  readonly chat_reject: 98;
28
+ readonly pause_request: 112;
29
+ readonly pause_state: 113;
30
+ readonly pause_refuse: 114;
28
31
  };
29
32
  /** welcome_dc.mode: 1 = lockstep, 2 = raw (0 reserved) per spec/binary.md. */
30
33
  export declare const MODE_LOCKSTEP = 1;
@@ -119,6 +122,18 @@ export type Message = {
119
122
  type: "chat_reject";
120
123
  id: number;
121
124
  reason: number;
125
+ } | {
126
+ type: "pause_request";
127
+ paused: number;
128
+ } | {
129
+ type: "pause_state";
130
+ paused: number;
131
+ byPeer: number;
132
+ flags: number;
133
+ } | {
134
+ type: "pause_refuse";
135
+ paused: number;
136
+ reason: number;
122
137
  };
123
138
  export interface Decoded {
124
139
  route: number;
@@ -139,3 +154,11 @@ export declare const CHAT_REJECT: {
139
154
  };
140
155
  export type ChatRejectReason = "rate" | "length" | "muted_by_host" | "unknown";
141
156
  export declare function chatRejectName(code: number): ChatRejectReason;
157
+ /** `pause_state.flags` bit 0: the pauser left and the host holds the pause. */
158
+ export declare const PAUSE_FLAG_PAUSER_LEFT = 1;
159
+ /** `pause_refuse.reason`: 1 not_in_game, 2 rate_limited, 3 not_allowed. */
160
+ export declare const PAUSE_REFUSE: {
161
+ readonly notInGame: 1;
162
+ readonly rateLimited: 2;
163
+ readonly notAllowed: 3;
164
+ };
package/dist/codec.js CHANGED
@@ -25,6 +25,9 @@ export const MsgType = {
25
25
  chat: 0x60,
26
26
  chat_history: 0x61,
27
27
  chat_reject: 0x62,
28
+ pause_request: 0x70,
29
+ pause_state: 0x71,
30
+ pause_refuse: 0x72,
28
31
  };
29
32
  /** welcome_dc.mode: 1 = lockstep, 2 = raw (0 reserved) per spec/binary.md. */
30
33
  export const MODE_LOCKSTEP = 1;
@@ -187,6 +190,18 @@ export function encodeMessage(msg, route = 0, allowOversize = false) {
187
190
  w.u32(msg.id);
188
191
  w.u8(msg.reason);
189
192
  break;
193
+ case "pause_request":
194
+ w.u8(msg.paused);
195
+ break;
196
+ case "pause_state":
197
+ w.u8(msg.paused);
198
+ w.u16(msg.byPeer);
199
+ w.u8(msg.flags);
200
+ break;
201
+ case "pause_refuse":
202
+ w.u8(msg.paused);
203
+ w.u8(msg.reason);
204
+ break;
190
205
  }
191
206
  const out = w.done();
192
207
  if (!allowOversize && out.length > MAX_FRAME_BYTES)
@@ -289,6 +304,15 @@ export function decodeMessage(buf, reassembled = false) {
289
304
  case "chat_reject":
290
305
  msg = { type, id: r.u32(), reason: r.u8() };
291
306
  break;
307
+ case "pause_request":
308
+ msg = { type, paused: r.u8() };
309
+ break;
310
+ case "pause_state":
311
+ msg = { type, paused: r.u8(), byPeer: r.u16(), flags: r.u8() };
312
+ break;
313
+ case "pause_refuse":
314
+ msg = { type, paused: r.u8(), reason: r.u8() };
315
+ break;
292
316
  }
293
317
  if (r.pos !== buf.length)
294
318
  throw new CodecError("trailing", `${buf.length - r.pos} extra bytes`);
@@ -305,4 +329,8 @@ export const CHAT_REJECT = { rate: 1, length: 2, mutedByHost: 3 };
305
329
  export function chatRejectName(code) {
306
330
  return code === 1 ? "rate" : code === 2 ? "length" : code === 3 ? "muted_by_host" : "unknown";
307
331
  }
332
+ /** `pause_state.flags` bit 0: the pauser left and the host holds the pause. */
333
+ export const PAUSE_FLAG_PAUSER_LEFT = 1;
334
+ /** `pause_refuse.reason`: 1 not_in_game, 2 rate_limited, 3 not_allowed. */
335
+ export const PAUSE_REFUSE = { notInGame: 1, rateLimited: 2, notAllowed: 3 };
308
336
  //# sourceMappingURL=codec.js.map
package/dist/codec.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Binary codec for the peer data channels (DESIGN s.8). Little-endian, one message per frame,\n * 3-byte header `type:u8, route:u16`. Pure functions; no I/O.\n */\n\nexport const MAX_FRAME_BYTES = 16 * 1024;\n/** Cap on one reassembled chunk set (spec/binary.md \"Snapshot and chunks\"). */\nexport const MAX_REASSEMBLED_BYTES = 64 * 1024 * 1024;\nexport const HEADER_BYTES = 3;\nexport const PROTO = 1;\n\nexport const MsgType = {\n hi: 0x01,\n welcome_dc: 0x02,\n cmd: 0x10,\n frame: 0x11,\n hash: 0x12,\n desync: 0x13,\n snapshot_req: 0x14,\n snapshot: 0x15,\n chunk: 0x16,\n raw: 0x20,\n ping: 0x30,\n pong: 0x31,\n host_claim: 0x40,\n voice: 0x50,\n chat: 0x60,\n chat_history: 0x61,\n chat_reject: 0x62,\n} as const;\n\n/** welcome_dc.mode: 1 = lockstep, 2 = raw (0 reserved) per spec/binary.md. */\nexport const MODE_LOCKSTEP = 1;\nexport const MODE_RAW = 2;\n\nexport type CodecErrorCode = \"too_short\" | \"trailing\" | \"too_long\" | \"bad_utf8\" | \"bad_value\";\nexport class CodecError extends Error {\n constructor(public code: CodecErrorCode, detail = \"\") {\n super(detail ? `${code}: ${detail}` : code);\n this.name = \"CodecError\";\n }\n}\n\nexport interface FrameCommand {\n peerId: number;\n bytes: Uint8Array;\n}\nexport interface ChatLine {\n peerId: number;\n id: number;\n ts: number;\n text: string;\n}\n\nexport type Message =\n | { type: \"hi\"; proto: number; peerId: number; lastTick: number }\n | { type: \"welcome_dc\"; tick: number; tickRate: number; mode: number; hostId: number }\n | { type: \"cmd\"; seq: number; bytes: Uint8Array }\n | { type: \"frame\"; tick: number; commands: FrameCommand[] }\n | { type: \"hash\"; tick: number; hash: bigint }\n | { type: \"desync\"; tick: number; hostHash: bigint; yourHash: bigint }\n | { type: \"snapshot_req\"; sinceTick: number }\n | { type: \"snapshot\"; tick: number; total: number; flags: number }\n | { type: \"chunk\"; id: number; index: number; count: number; bytes: Uint8Array }\n | { type: \"raw\"; bytes: Uint8Array }\n | { type: \"ping\"; n: number; t: number }\n | { type: \"pong\"; n: number; t: number }\n | { type: \"host_claim\"; epoch: number; hostId: number; tick: number }\n | { type: \"voice\"; seq: number; flags: number; frames: Uint8Array[] }\n | { type: \"chat\"; id: number; ts: number; text: string }\n | { type: \"chat_history\"; lines: ChatLine[] }\n | { type: \"chat_reject\"; id: number; reason: number };\n\nexport interface Decoded {\n route: number;\n msg: Message;\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder(\"utf-8\", { fatal: true });\nfunction decodeText(b: Uint8Array): string {\n try {\n return dec.decode(b);\n } catch {\n throw new CodecError(\"bad_utf8\");\n }\n}\n\nclass Writer {\n buf: Uint8Array;\n dv: DataView;\n pos = 0;\n constructor(size: number) {\n this.buf = new Uint8Array(size);\n this.dv = new DataView(this.buf.buffer);\n }\n ensure(n: number) {\n if (this.pos + n <= this.buf.length) return;\n const nb = new Uint8Array(Math.max(this.buf.length * 2, this.pos + n));\n nb.set(this.buf);\n this.buf = nb;\n this.dv = new DataView(nb.buffer);\n }\n u8(v: number) { this.ensure(1); this.dv.setUint8(this.pos, v); this.pos += 1; }\n u16(v: number) { this.ensure(2); this.dv.setUint16(this.pos, v, true); this.pos += 2; }\n u32(v: number) { this.ensure(4); this.dv.setUint32(this.pos, v >>> 0, true); this.pos += 4; }\n i64(v: number) { this.ensure(8); this.dv.setBigInt64(this.pos, BigInt(v), true); this.pos += 8; }\n u64(v: bigint) { this.ensure(8); this.dv.setBigUint64(this.pos, BigInt.asUintN(64, v), true); this.pos += 8; }\n bytes(b: Uint8Array) { this.ensure(b.length); this.buf.set(b, this.pos); this.pos += b.length; }\n done(): Uint8Array { return this.buf.slice(0, this.pos); }\n}\n\nclass Reader {\n dv: DataView;\n pos = 0;\n constructor(public buf: Uint8Array) {\n this.dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n }\n need(n: number) { if (this.pos + n > this.buf.length) throw new CodecError(\"too_short\", \"truncated frame\"); }\n u8() { this.need(1); const v = this.dv.getUint8(this.pos); this.pos += 1; return v; }\n u16() { this.need(2); const v = this.dv.getUint16(this.pos, true); this.pos += 2; return v; }\n u32() { this.need(4); const v = this.dv.getUint32(this.pos, true); this.pos += 4; return v; }\n i64() { this.need(8); const v = this.dv.getBigInt64(this.pos, true); this.pos += 8; return Number(v); }\n u64() { this.need(8); const v = this.dv.getBigUint64(this.pos, true); this.pos += 8; return v; }\n bytes(n: number) { this.need(n); const v = this.buf.slice(this.pos, this.pos + n); this.pos += n; return v; }\n rest() { const v = this.buf.slice(this.pos); this.pos = this.buf.length; return v; }\n}\n\nexport function encodeMessage(msg: Message, route = 0, allowOversize = false): Uint8Array {\n const w = new Writer(64);\n w.u8(MsgType[msg.type]);\n w.u16(route);\n switch (msg.type) {\n case \"hi\": w.u8(msg.proto); w.u16(msg.peerId); w.i64(msg.lastTick); break;\n case \"welcome_dc\": w.i64(msg.tick); w.u8(msg.tickRate); w.u8(msg.mode); w.u16(msg.hostId); break;\n case \"cmd\": w.u32(msg.seq); w.u16(msg.bytes.length); w.bytes(msg.bytes); break;\n case \"frame\":\n w.i64(msg.tick); w.u16(msg.commands.length);\n for (const c of msg.commands) { w.u16(c.peerId); w.u16(c.bytes.length); w.bytes(c.bytes); }\n break;\n case \"hash\": w.i64(msg.tick); w.u64(msg.hash); break;\n case \"desync\": w.i64(msg.tick); w.u64(msg.hostHash); w.u64(msg.yourHash); break;\n case \"snapshot_req\": w.i64(msg.sinceTick); break;\n case \"snapshot\": w.i64(msg.tick); w.u32(msg.total); w.u8(msg.flags); break;\n case \"chunk\": w.u32(msg.id); w.u16(msg.index); w.u16(msg.count); w.bytes(msg.bytes); break;\n case \"raw\": w.bytes(msg.bytes); break;\n case \"ping\": case \"pong\": w.u32(msg.n); w.u32(msg.t); break;\n case \"host_claim\": w.u16(msg.epoch); w.u16(msg.hostId); w.i64(msg.tick); break;\n case \"voice\":\n w.u16(msg.seq); w.u8(msg.flags); w.u8(msg.frames.length);\n for (const f of msg.frames) { w.u16(f.length); w.bytes(f); }\n break;\n case \"chat\": { const t = enc.encode(msg.text); w.u32(msg.id); w.i64(msg.ts); w.u16(t.length); w.bytes(t); break; }\n case \"chat_history\":\n w.u16(msg.lines.length);\n for (const l of msg.lines) {\n const t = enc.encode(l.text);\n w.u16(l.peerId); w.u32(l.id); w.i64(l.ts); w.u16(t.length); w.bytes(t);\n }\n break;\n case \"chat_reject\": w.u32(msg.id); w.u8(msg.reason); break;\n }\n const out = w.done();\n if (!allowOversize && out.length > MAX_FRAME_BYTES) throw new CodecError(\"too_long\", `${out.length} > ${MAX_FRAME_BYTES}`);\n return out;\n}\n\nconst TYPE_BY_ID = new Map<number, keyof typeof MsgType>(\n (Object.keys(MsgType) as Array<keyof typeof MsgType>).map((k) => [MsgType[k], k]),\n);\n\n/**\n * Decode one frame. Returns null for unknown types (ignored for forward compatibility); throws on a truncated body.\n * A frame reassembled from chunks passes `reassembled` so the cap is MAX_REASSEMBLED_BYTES instead of one frame's.\n */\nexport function decodeMessage(buf: Uint8Array, reassembled = false): Decoded | null {\n const max = reassembled ? MAX_REASSEMBLED_BYTES : MAX_FRAME_BYTES;\n if (buf.length > max) throw new CodecError(\"too_long\", `${buf.length} > ${max}`);\n if (buf.length < HEADER_BYTES) throw new CodecError(\"too_short\", \"header\");\n const r = new Reader(buf);\n const type = TYPE_BY_ID.get(r.u8());\n const route = r.u16();\n if (!type) return null;\n let msg: Message;\n switch (type) {\n case \"hi\": msg = { type, proto: r.u8(), peerId: r.u16(), lastTick: r.i64() }; break;\n case \"welcome_dc\": msg = { type, tick: r.i64(), tickRate: r.u8(), mode: r.u8(), hostId: r.u16() }; break;\n case \"cmd\": { const seq = r.u32(); msg = { type, seq, bytes: r.bytes(r.u16()) }; break; }\n case \"frame\": {\n const tick = r.i64(); const n = r.u16(); const commands: FrameCommand[] = [];\n for (let i = 0; i < n; i++) { const peerId = r.u16(); commands.push({ peerId, bytes: r.bytes(r.u16()) }); }\n msg = { type, tick, commands }; break;\n }\n case \"hash\": msg = { type, tick: r.i64(), hash: r.u64() }; break;\n case \"desync\": msg = { type, tick: r.i64(), hostHash: r.u64(), yourHash: r.u64() }; break;\n case \"snapshot_req\": msg = { type, sinceTick: r.i64() }; break;\n case \"snapshot\": msg = { type, tick: r.i64(), total: r.u32(), flags: r.u8() }; break;\n case \"chunk\": msg = { type, id: r.u32(), index: r.u16(), count: r.u16(), bytes: r.rest() }; break;\n case \"raw\": msg = { type, bytes: r.rest() }; break;\n case \"ping\": case \"pong\": msg = { type, n: r.u32(), t: r.u32() }; break;\n case \"host_claim\": msg = { type, epoch: r.u16(), hostId: r.u16(), tick: r.i64() }; break;\n case \"voice\": {\n const seq = r.u16(); const flags = r.u8(); const n = r.u8(); const frames: Uint8Array[] = [];\n for (let i = 0; i < n; i++) frames.push(r.bytes(r.u16()));\n msg = { type, seq, flags, frames }; break;\n }\n case \"chat\": { const id = r.u32(); const ts = r.i64(); msg = { type, id, ts, text: decodeText(r.bytes(r.u16())) }; break; }\n case \"chat_history\": {\n const n = r.u16(); const lines: ChatLine[] = [];\n for (let i = 0; i < n; i++) {\n const peerId = r.u16(); const id = r.u32(); const ts = r.i64();\n lines.push({ peerId, id, ts, text: decodeText(r.bytes(r.u16())) });\n }\n msg = { type, lines }; break;\n }\n case \"chat_reject\": msg = { type, id: r.u32(), reason: r.u8() }; break;\n }\n if (r.pos !== buf.length) throw new CodecError(\"trailing\", `${buf.length - r.pos} extra bytes`);\n return { route, msg };\n}\n\n/** Rewrite the route field on a copy (the host does this when relaying). */\nexport function withRoute(buf: Uint8Array, route: number): Uint8Array {\n const out = buf.slice();\n out[1] = route & 0xff;\n out[2] = (route >> 8) & 0xff;\n return out;\n}\n\nexport const CHAT_REJECT = { rate: 1, length: 2, mutedByHost: 3 } as const;\nexport type ChatRejectReason = \"rate\" | \"length\" | \"muted_by_host\" | \"unknown\";\nexport function chatRejectName(code: number): ChatRejectReason {\n return code === 1 ? \"rate\" : code === 2 ? \"length\" : code === 3 ? \"muted_by_host\" : \"unknown\";\n}\n"]}
1
+ {"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Binary codec for the peer data channels (DESIGN s.8). Little-endian, one message per frame,\n * 3-byte header `type:u8, route:u16`. Pure functions; no I/O.\n */\n\nexport const MAX_FRAME_BYTES = 16 * 1024;\n/** Cap on one reassembled chunk set (spec/binary.md \"Snapshot and chunks\"). */\nexport const MAX_REASSEMBLED_BYTES = 64 * 1024 * 1024;\nexport const HEADER_BYTES = 3;\nexport const PROTO = 1;\n\nexport const MsgType = {\n hi: 0x01,\n welcome_dc: 0x02,\n cmd: 0x10,\n frame: 0x11,\n hash: 0x12,\n desync: 0x13,\n snapshot_req: 0x14,\n snapshot: 0x15,\n chunk: 0x16,\n raw: 0x20,\n ping: 0x30,\n pong: 0x31,\n host_claim: 0x40,\n voice: 0x50,\n chat: 0x60,\n chat_history: 0x61,\n chat_reject: 0x62,\n pause_request: 0x70,\n pause_state: 0x71,\n pause_refuse: 0x72,\n} as const;\n\n/** welcome_dc.mode: 1 = lockstep, 2 = raw (0 reserved) per spec/binary.md. */\nexport const MODE_LOCKSTEP = 1;\nexport const MODE_RAW = 2;\n\nexport type CodecErrorCode = \"too_short\" | \"trailing\" | \"too_long\" | \"bad_utf8\" | \"bad_value\";\nexport class CodecError extends Error {\n constructor(public code: CodecErrorCode, detail = \"\") {\n super(detail ? `${code}: ${detail}` : code);\n this.name = \"CodecError\";\n }\n}\n\nexport interface FrameCommand {\n peerId: number;\n bytes: Uint8Array;\n}\nexport interface ChatLine {\n peerId: number;\n id: number;\n ts: number;\n text: string;\n}\n\nexport type Message =\n | { type: \"hi\"; proto: number; peerId: number; lastTick: number }\n | { type: \"welcome_dc\"; tick: number; tickRate: number; mode: number; hostId: number }\n | { type: \"cmd\"; seq: number; bytes: Uint8Array }\n | { type: \"frame\"; tick: number; commands: FrameCommand[] }\n | { type: \"hash\"; tick: number; hash: bigint }\n | { type: \"desync\"; tick: number; hostHash: bigint; yourHash: bigint }\n | { type: \"snapshot_req\"; sinceTick: number }\n | { type: \"snapshot\"; tick: number; total: number; flags: number }\n | { type: \"chunk\"; id: number; index: number; count: number; bytes: Uint8Array }\n | { type: \"raw\"; bytes: Uint8Array }\n | { type: \"ping\"; n: number; t: number }\n | { type: \"pong\"; n: number; t: number }\n | { type: \"host_claim\"; epoch: number; hostId: number; tick: number }\n | { type: \"voice\"; seq: number; flags: number; frames: Uint8Array[] }\n | { type: \"chat\"; id: number; ts: number; text: string }\n | { type: \"chat_history\"; lines: ChatLine[] }\n | { type: \"chat_reject\"; id: number; reason: number }\n | { type: \"pause_request\"; paused: number }\n | { type: \"pause_state\"; paused: number; byPeer: number; flags: number }\n | { type: \"pause_refuse\"; paused: number; reason: number };\n\nexport interface Decoded {\n route: number;\n msg: Message;\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder(\"utf-8\", { fatal: true });\nfunction decodeText(b: Uint8Array): string {\n try {\n return dec.decode(b);\n } catch {\n throw new CodecError(\"bad_utf8\");\n }\n}\n\nclass Writer {\n buf: Uint8Array;\n dv: DataView;\n pos = 0;\n constructor(size: number) {\n this.buf = new Uint8Array(size);\n this.dv = new DataView(this.buf.buffer);\n }\n ensure(n: number) {\n if (this.pos + n <= this.buf.length) return;\n const nb = new Uint8Array(Math.max(this.buf.length * 2, this.pos + n));\n nb.set(this.buf);\n this.buf = nb;\n this.dv = new DataView(nb.buffer);\n }\n u8(v: number) { this.ensure(1); this.dv.setUint8(this.pos, v); this.pos += 1; }\n u16(v: number) { this.ensure(2); this.dv.setUint16(this.pos, v, true); this.pos += 2; }\n u32(v: number) { this.ensure(4); this.dv.setUint32(this.pos, v >>> 0, true); this.pos += 4; }\n i64(v: number) { this.ensure(8); this.dv.setBigInt64(this.pos, BigInt(v), true); this.pos += 8; }\n u64(v: bigint) { this.ensure(8); this.dv.setBigUint64(this.pos, BigInt.asUintN(64, v), true); this.pos += 8; }\n bytes(b: Uint8Array) { this.ensure(b.length); this.buf.set(b, this.pos); this.pos += b.length; }\n done(): Uint8Array { return this.buf.slice(0, this.pos); }\n}\n\nclass Reader {\n dv: DataView;\n pos = 0;\n constructor(public buf: Uint8Array) {\n this.dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n }\n need(n: number) { if (this.pos + n > this.buf.length) throw new CodecError(\"too_short\", \"truncated frame\"); }\n u8() { this.need(1); const v = this.dv.getUint8(this.pos); this.pos += 1; return v; }\n u16() { this.need(2); const v = this.dv.getUint16(this.pos, true); this.pos += 2; return v; }\n u32() { this.need(4); const v = this.dv.getUint32(this.pos, true); this.pos += 4; return v; }\n i64() { this.need(8); const v = this.dv.getBigInt64(this.pos, true); this.pos += 8; return Number(v); }\n u64() { this.need(8); const v = this.dv.getBigUint64(this.pos, true); this.pos += 8; return v; }\n bytes(n: number) { this.need(n); const v = this.buf.slice(this.pos, this.pos + n); this.pos += n; return v; }\n rest() { const v = this.buf.slice(this.pos); this.pos = this.buf.length; return v; }\n}\n\nexport function encodeMessage(msg: Message, route = 0, allowOversize = false): Uint8Array {\n const w = new Writer(64);\n w.u8(MsgType[msg.type]);\n w.u16(route);\n switch (msg.type) {\n case \"hi\": w.u8(msg.proto); w.u16(msg.peerId); w.i64(msg.lastTick); break;\n case \"welcome_dc\": w.i64(msg.tick); w.u8(msg.tickRate); w.u8(msg.mode); w.u16(msg.hostId); break;\n case \"cmd\": w.u32(msg.seq); w.u16(msg.bytes.length); w.bytes(msg.bytes); break;\n case \"frame\":\n w.i64(msg.tick); w.u16(msg.commands.length);\n for (const c of msg.commands) { w.u16(c.peerId); w.u16(c.bytes.length); w.bytes(c.bytes); }\n break;\n case \"hash\": w.i64(msg.tick); w.u64(msg.hash); break;\n case \"desync\": w.i64(msg.tick); w.u64(msg.hostHash); w.u64(msg.yourHash); break;\n case \"snapshot_req\": w.i64(msg.sinceTick); break;\n case \"snapshot\": w.i64(msg.tick); w.u32(msg.total); w.u8(msg.flags); break;\n case \"chunk\": w.u32(msg.id); w.u16(msg.index); w.u16(msg.count); w.bytes(msg.bytes); break;\n case \"raw\": w.bytes(msg.bytes); break;\n case \"ping\": case \"pong\": w.u32(msg.n); w.u32(msg.t); break;\n case \"host_claim\": w.u16(msg.epoch); w.u16(msg.hostId); w.i64(msg.tick); break;\n case \"voice\":\n w.u16(msg.seq); w.u8(msg.flags); w.u8(msg.frames.length);\n for (const f of msg.frames) { w.u16(f.length); w.bytes(f); }\n break;\n case \"chat\": { const t = enc.encode(msg.text); w.u32(msg.id); w.i64(msg.ts); w.u16(t.length); w.bytes(t); break; }\n case \"chat_history\":\n w.u16(msg.lines.length);\n for (const l of msg.lines) {\n const t = enc.encode(l.text);\n w.u16(l.peerId); w.u32(l.id); w.i64(l.ts); w.u16(t.length); w.bytes(t);\n }\n break;\n case \"chat_reject\": w.u32(msg.id); w.u8(msg.reason); break;\n case \"pause_request\": w.u8(msg.paused); break;\n case \"pause_state\": w.u8(msg.paused); w.u16(msg.byPeer); w.u8(msg.flags); break;\n case \"pause_refuse\": w.u8(msg.paused); w.u8(msg.reason); break;\n }\n const out = w.done();\n if (!allowOversize && out.length > MAX_FRAME_BYTES) throw new CodecError(\"too_long\", `${out.length} > ${MAX_FRAME_BYTES}`);\n return out;\n}\n\nconst TYPE_BY_ID = new Map<number, keyof typeof MsgType>(\n (Object.keys(MsgType) as Array<keyof typeof MsgType>).map((k) => [MsgType[k], k]),\n);\n\n/**\n * Decode one frame. Returns null for unknown types (ignored for forward compatibility); throws on a truncated body.\n * A frame reassembled from chunks passes `reassembled` so the cap is MAX_REASSEMBLED_BYTES instead of one frame's.\n */\nexport function decodeMessage(buf: Uint8Array, reassembled = false): Decoded | null {\n const max = reassembled ? MAX_REASSEMBLED_BYTES : MAX_FRAME_BYTES;\n if (buf.length > max) throw new CodecError(\"too_long\", `${buf.length} > ${max}`);\n if (buf.length < HEADER_BYTES) throw new CodecError(\"too_short\", \"header\");\n const r = new Reader(buf);\n const type = TYPE_BY_ID.get(r.u8());\n const route = r.u16();\n if (!type) return null;\n let msg: Message;\n switch (type) {\n case \"hi\": msg = { type, proto: r.u8(), peerId: r.u16(), lastTick: r.i64() }; break;\n case \"welcome_dc\": msg = { type, tick: r.i64(), tickRate: r.u8(), mode: r.u8(), hostId: r.u16() }; break;\n case \"cmd\": { const seq = r.u32(); msg = { type, seq, bytes: r.bytes(r.u16()) }; break; }\n case \"frame\": {\n const tick = r.i64(); const n = r.u16(); const commands: FrameCommand[] = [];\n for (let i = 0; i < n; i++) { const peerId = r.u16(); commands.push({ peerId, bytes: r.bytes(r.u16()) }); }\n msg = { type, tick, commands }; break;\n }\n case \"hash\": msg = { type, tick: r.i64(), hash: r.u64() }; break;\n case \"desync\": msg = { type, tick: r.i64(), hostHash: r.u64(), yourHash: r.u64() }; break;\n case \"snapshot_req\": msg = { type, sinceTick: r.i64() }; break;\n case \"snapshot\": msg = { type, tick: r.i64(), total: r.u32(), flags: r.u8() }; break;\n case \"chunk\": msg = { type, id: r.u32(), index: r.u16(), count: r.u16(), bytes: r.rest() }; break;\n case \"raw\": msg = { type, bytes: r.rest() }; break;\n case \"ping\": case \"pong\": msg = { type, n: r.u32(), t: r.u32() }; break;\n case \"host_claim\": msg = { type, epoch: r.u16(), hostId: r.u16(), tick: r.i64() }; break;\n case \"voice\": {\n const seq = r.u16(); const flags = r.u8(); const n = r.u8(); const frames: Uint8Array[] = [];\n for (let i = 0; i < n; i++) frames.push(r.bytes(r.u16()));\n msg = { type, seq, flags, frames }; break;\n }\n case \"chat\": { const id = r.u32(); const ts = r.i64(); msg = { type, id, ts, text: decodeText(r.bytes(r.u16())) }; break; }\n case \"chat_history\": {\n const n = r.u16(); const lines: ChatLine[] = [];\n for (let i = 0; i < n; i++) {\n const peerId = r.u16(); const id = r.u32(); const ts = r.i64();\n lines.push({ peerId, id, ts, text: decodeText(r.bytes(r.u16())) });\n }\n msg = { type, lines }; break;\n }\n case \"chat_reject\": msg = { type, id: r.u32(), reason: r.u8() }; break;\n case \"pause_request\": msg = { type, paused: r.u8() }; break;\n case \"pause_state\": msg = { type, paused: r.u8(), byPeer: r.u16(), flags: r.u8() }; break;\n case \"pause_refuse\": msg = { type, paused: r.u8(), reason: r.u8() }; break;\n }\n if (r.pos !== buf.length) throw new CodecError(\"trailing\", `${buf.length - r.pos} extra bytes`);\n return { route, msg };\n}\n\n/** Rewrite the route field on a copy (the host does this when relaying). */\nexport function withRoute(buf: Uint8Array, route: number): Uint8Array {\n const out = buf.slice();\n out[1] = route & 0xff;\n out[2] = (route >> 8) & 0xff;\n return out;\n}\n\nexport const CHAT_REJECT = { rate: 1, length: 2, mutedByHost: 3 } as const;\nexport type ChatRejectReason = \"rate\" | \"length\" | \"muted_by_host\" | \"unknown\";\nexport function chatRejectName(code: number): ChatRejectReason {\n return code === 1 ? \"rate\" : code === 2 ? \"length\" : code === 3 ? \"muted_by_host\" : \"unknown\";\n}\n\n/** `pause_state.flags` bit 0: the pauser left and the host holds the pause. */\nexport const PAUSE_FLAG_PAUSER_LEFT = 1;\n/** `pause_refuse.reason`: 1 not_in_game, 2 rate_limited, 3 not_allowed. */\nexport const PAUSE_REFUSE = { notInGame: 1, rateLimited: 2, notAllowed: 3 } as const;\n"]}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * `kit.hostNostr()`: host a direct lobby from a browser tab (or any runtime with WebSocket + WebRTC), with no Node process
3
+ * and no listening port. The signaling hub runs inside the page, the host's own socket is wired to it in memory, and
4
+ * joiners reach it through public Nostr relays with a normal dc1 version 1 code (spec/nostr-signaling.md). There is no IP
5
+ * fallback: with no relay reachable it rejects with `direct_unreachable`. Nothing here imports a Node module.
6
+ */
7
+ import type { OneLobby } from "../onelobby.js";
8
+ import type { Lobby, RateLimit } from "../lobby.js";
9
+ /** The slice of `document` the visibility watcher uses (a test seam; default `globalThis.document`). */
10
+ export interface VisibilityDoc {
11
+ visibilityState?: string;
12
+ addEventListener(type: "visibilitychange", listener: () => void): void;
13
+ removeEventListener(type: "visibilitychange", listener: () => void): void;
14
+ }
15
+ export interface BrowserHostOptions {
16
+ /** Optional; joiners must send the same one or get `bad_password`. */
17
+ password?: string;
18
+ maxPlayers?: number;
19
+ /** Lobby name; default "<displayName>'s game". */
20
+ name?: string;
21
+ lateJoin?: boolean;
22
+ meta?: Record<string, string>;
23
+ /** Per-joiner cap on incoming raw messages, e.g. `{ messages: 60, perMs: 1000 }`; the excess is dropped. */
24
+ rateLimit?: RateLimit;
25
+ /** Relays (default: the blueprint's `hosting.nostrRelays`, else the library list) and STUN servers (default Google's and Cloudflare's). */
26
+ nostr?: {
27
+ relays?: string[];
28
+ stun?: string[];
29
+ };
30
+ /** Receives the startup line (`lobby.directHostLog`). Default `console.log`; `false` prints nothing. */
31
+ log?: ((line: string) => void) | false;
32
+ /** Receives the warning printed when the host tab goes hidden. Default `console.warn`; `false` prints nothing. */
33
+ warn?: ((line: string) => void) | false;
34
+ /** How long to wait for a first relay before rejecting with `direct_unreachable` (default 10000). */
35
+ relayTimeoutMs?: number;
36
+ /** Test seam: the document whose `visibilityState` is watched. Default `globalThis.document`. */
37
+ document?: VisibilityDoc | null;
38
+ }
39
+ /** A browser host lobby: the normal Lobby (`hostHidden` / `hostHiddenChanged` tell when the tab is throttled). */
40
+ export type BrowserHostLobby = Lobby;
41
+ /**
42
+ * The startup line of a browser host, same family as `formatHostLog`:
43
+ * `OneLobby: browser host (no port); code 7HQ-4K2-... (Nostr); nostr 4/5 relays`.
44
+ */
45
+ export declare function formatBrowserHostLog(code: string, relays: string): string;
46
+ export declare function hostNostr(kit: OneLobby, opts?: BrowserHostOptions): Promise<BrowserHostLobby>;
@@ -0,0 +1,93 @@
1
+ import { encodeRoomCode } from "../joincode.js";
2
+ import { NostrHostBridge } from "../nostr/host.js";
3
+ import { HOST_NOSTR_FIRST_RELAY_MS } from "../nostr/limits.js";
4
+ import { DEFAULT_NOSTR_RELAYS, DEFAULT_NOSTR_STUN } from "../nostr/relays.js";
5
+ import { OneLobbyError } from "../signaling.js";
6
+ import { SignalingHub, directHubOptions } from "./hub.js";
7
+ import { LocalHubSocket } from "./hub-local.js";
8
+ /**
9
+ * The startup line of a browser host, same family as `formatHostLog`:
10
+ * `OneLobby: browser host (no port); code 7HQ-4K2-... (Nostr); nostr 4/5 relays`.
11
+ */
12
+ export function formatBrowserHostLog(code, relays) {
13
+ return `OneLobby: browser host (no port); code ${code} (Nostr); nostr ${relays} relays`;
14
+ }
15
+ export async function hostNostr(kit, opts = {}) {
16
+ const bp = kit.blueprint;
17
+ if (bp && !bp.hosting.nostr) {
18
+ throw new OneLobbyError("nostr_disabled", "this game has Nostr signaling turned off (hosting.nostr is false), and a browser host has no other way to be reached");
19
+ }
20
+ const defined = Object.fromEntries(Object.entries(opts).filter(([, v]) => v !== undefined));
21
+ const o = bp
22
+ ? {
23
+ maxPlayers: bp.lobby.maxPlayers,
24
+ lateJoin: bp.lobby.lateJoin,
25
+ meta: { ...bp.lobby.meta },
26
+ ...(bp.session.rateLimit ? { rateLimit: { ...bp.session.rateLimit } } : {}),
27
+ ...defined,
28
+ nostr: { ...defined.nostr, relays: defined.nostr?.relays ?? [...bp.hosting.nostrRelays] },
29
+ }
30
+ : defined;
31
+ const maxPlayers = o.maxPlayers ?? 4;
32
+ const relays = o.nostr?.relays && o.nostr.relays.length > 0 ? o.nostr.relays : [...DEFAULT_NOSTR_RELAYS];
33
+ const stunUrls = o.nostr?.stun ?? [...DEFAULT_NOSTR_STUN];
34
+ const hub = new SignalingHub({ ...directHubOptions(maxPlayers, o.password), stunUrls });
35
+ const ticker = setInterval(() => hub.tick(), 1000);
36
+ ticker.unref?.();
37
+ const code = encodeRoomCode();
38
+ const env = kit.nostrHostEnv();
39
+ let bridge;
40
+ const teardown = () => {
41
+ clearInterval(ticker);
42
+ bridge?.close();
43
+ };
44
+ try {
45
+ try {
46
+ bridge = await NostrHostBridge.start(hub, { code, relays, connect: env.connect, clock: env.clock, wall: env.wall, maxPlayers });
47
+ }
48
+ catch (e) {
49
+ throw new OneLobbyError("direct_unreachable", `cannot reach any Nostr relay (${e instanceof Error ? e.message : String(e)}); a browser host has no other way to be reached`);
50
+ }
51
+ if (!(await bridge.settle(o.relayTimeoutMs ?? HOST_NOSTR_FIRST_RELAY_MS))) {
52
+ throw new OneLobbyError("direct_unreachable", `no Nostr relay reachable (tried ${bridge.relaysTotal}); a browser host has no IP fallback, check the connection or pass other relays`);
53
+ }
54
+ const name = o.name ?? `${kit.options.displayName || "Player"}'s game`;
55
+ const h = { name, maxPlayers, lateJoin: o.lateJoin ?? true, meta: o.meta, rateLimit: o.rateLimit };
56
+ // joiners have no address to dial: links use the hub's STUN list, no port pin
57
+ const lobby = await kit.hostDirectWith(h, { socketFactory: () => new LocalHubSocket(hub), wrapLinks: (inner) => inner });
58
+ lobby.linkAddressSink = (peerId, ip) => hub.reportLinkAddress(peerId, ip);
59
+ lobby.on("sessionEnded", teardown);
60
+ lobby.joinCode = code;
61
+ lobby.joinCodeKind = "nostr";
62
+ lobby.directCode = "";
63
+ lobby.directCodeKind = "none";
64
+ lobby.portMapping = "off";
65
+ lobby.directHostLog = formatBrowserHostLog(code, `${bridge.relaysConnected}/${bridge.relaysTotal}`);
66
+ watchVisibility(lobby, o);
67
+ if (o.log !== false)
68
+ (o.log ?? console.log)(lobby.directHostLog);
69
+ return lobby;
70
+ }
71
+ catch (e) {
72
+ teardown();
73
+ throw e;
74
+ }
75
+ }
76
+ /** Sets `lobby.hostHidden` and warns when the tab goes hidden: browsers throttle timers there (about 1/s, ~1/min after 5 min). */
77
+ function watchVisibility(lobby, o) {
78
+ const doc = o.document !== undefined ? o.document : globalThis.document;
79
+ if (!doc || typeof doc.addEventListener !== "function")
80
+ return;
81
+ const warn = o.warn === false ? null : (o.warn ?? ((s) => console.warn(s)));
82
+ const check = () => {
83
+ const hidden = doc.visibilityState === "hidden";
84
+ const changed = hidden !== lobby.hostHidden;
85
+ lobby.setHostHidden(hidden);
86
+ if (changed && hidden)
87
+ warn?.("OneLobby: the host tab is hidden; the browser slows its timers, so players may lag or drop. Keep this tab visible while hosting.");
88
+ };
89
+ doc.addEventListener("visibilitychange", check);
90
+ lobby.on("sessionEnded", () => doc.removeEventListener("visibilitychange", check));
91
+ check();
92
+ }
93
+ //# sourceMappingURL=browser-host.js.map