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.
- package/README.md +2 -2
- package/blueprint.schema.json +8 -1
- package/dist/blueprint.d.ts +4 -0
- package/dist/blueprint.js +5 -0
- package/dist/blueprint.js.map +1 -1
- package/dist/codec.d.ts +23 -0
- package/dist/codec.js +28 -0
- package/dist/codec.js.map +1 -1
- package/dist/direct/browser-host.d.ts +46 -0
- package/dist/direct/browser-host.js +93 -0
- package/dist/direct/browser-host.js.map +1 -0
- package/dist/direct/hub-host.d.ts +34 -23
- package/dist/direct/hub-host.js +75 -59
- package/dist/direct/hub-host.js.map +1 -1
- package/dist/direct/hub-local.d.ts +22 -0
- package/dist/direct/hub-local.js +46 -0
- package/dist/direct/hub-local.js.map +1 -0
- package/dist/direct/hub.d.ts +37 -10
- package/dist/direct/hub.js +114 -23
- package/dist/direct/hub.js.map +1 -1
- package/dist/direct/index.d.ts +36 -3
- package/dist/direct/index.js +146 -20
- package/dist/direct/index.js.map +1 -1
- package/dist/direct/metrics.d.ts +12 -0
- package/dist/direct/metrics.js +45 -0
- package/dist/direct/metrics.js.map +1 -0
- package/dist/direct/portmap.d.ts +9 -2
- package/dist/direct/portmap.js +7 -2
- package/dist/direct/portmap.js.map +1 -1
- package/dist/direct/sha1.d.ts +16 -0
- package/dist/direct/sha1.js +111 -0
- package/dist/direct/sha1.js.map +1 -0
- package/dist/direct-address.d.ts +2 -2
- package/dist/direct-address.js.map +1 -1
- package/dist/friendly.d.ts +23 -0
- package/dist/friendly.js +28 -0
- package/dist/friendly.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/internals.d.ts +9 -0
- package/dist/internals.js +9 -0
- package/dist/internals.js.map +1 -1
- package/dist/join-code-kind.d.ts +10 -0
- package/dist/join-code-kind.js +10 -0
- package/dist/join-code-kind.js.map +1 -0
- package/dist/joincode.d.ts +10 -1
- package/dist/joincode.js +23 -3
- package/dist/joincode.js.map +1 -1
- package/dist/lobby.d.ts +65 -4
- package/dist/lobby.js +151 -12
- package/dist/lobby.js.map +1 -1
- package/dist/lockstep.d.ts +8 -0
- package/dist/lockstep.js +23 -2
- package/dist/lockstep.js.map +1 -1
- package/dist/nostr/crypto.d.ts +20 -0
- package/dist/nostr/crypto.js +78 -0
- package/dist/nostr/crypto.js.map +1 -0
- package/dist/nostr/host.d.ts +66 -0
- package/dist/nostr/host.js +196 -0
- package/dist/nostr/host.js.map +1 -0
- package/dist/nostr/limits.d.ts +31 -0
- package/dist/nostr/limits.js +36 -0
- package/dist/nostr/limits.js.map +1 -0
- package/dist/nostr/link.d.ts +70 -0
- package/dist/nostr/link.js +180 -0
- package/dist/nostr/link.js.map +1 -0
- package/dist/nostr/relays.d.ts +79 -0
- package/dist/nostr/relays.js +315 -0
- package/dist/nostr/relays.js.map +1 -0
- package/dist/nostr/room.d.ts +43 -0
- package/dist/nostr/room.js +104 -0
- package/dist/nostr/room.js.map +1 -0
- package/dist/nostr/secp256k1.d.ts +10 -0
- package/dist/nostr/secp256k1.js +171 -0
- package/dist/nostr/secp256k1.js.map +1 -0
- package/dist/nostr/socket.d.ts +48 -0
- package/dist/nostr/socket.js +141 -0
- package/dist/nostr/socket.js.map +1 -0
- package/dist/onelobby.bundle.js +146 -0
- package/dist/onelobby.bundle.js.map +7 -0
- package/dist/onelobby.bundle.mjs +11 -5
- package/dist/onelobby.bundle.mjs.map +4 -4
- package/dist/onelobby.d.ts +31 -3
- package/dist/onelobby.global.js +2599 -46
- package/dist/onelobby.global.js.map +4 -4
- package/dist/onelobby.global.min.js +11 -5
- package/dist/onelobby.global.min.js.map +4 -4
- package/dist/onelobby.js +68 -14
- package/dist/onelobby.js.map +1 -1
- package/dist/pause.d.ts +85 -0
- package/dist/pause.js +160 -0
- package/dist/pause.js.map +1 -0
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js.map +1 -1
- package/dist/webrtc.d.ts +1 -0
- package/dist/webrtc.js +6 -1
- package/dist/webrtc.js.map +1 -1
- package/package.json +1 -1
package/dist/onelobby.js
CHANGED
|
@@ -6,11 +6,14 @@ import { OneLobbyError, PROTO_ID, SignalingClient, } from "./signaling.js";
|
|
|
6
6
|
import { defaultSocketFactory, } from "./transport.js";
|
|
7
7
|
import { WebAudioSink, WorkletMic, webCodecsOpus, webCodecsOpusAvailable } from "./voice-browser.js";
|
|
8
8
|
import { webRtcLinkFactory } from "./webrtc.js";
|
|
9
|
-
import { decodeJoinCode, encodeJoinCode, looksLikeJoinCode } from "./joincode.js";
|
|
9
|
+
import { decodeJoinCode, encodeJoinCode, encodeRoomCodeFor, looksLikeJoinCode } from "./joincode.js";
|
|
10
|
+
import { NostrSocket } from "./nostr/socket.js";
|
|
11
|
+
import { DEFAULT_NOSTR_STUN } from "./nostr/relays.js";
|
|
10
12
|
import { DEFAULT_DIRECT_PORT, parseDirectAddress } from "./direct-address.js";
|
|
11
13
|
import { BlueprintError, parseBlueprint } from "./blueprint.js";
|
|
14
|
+
import { hostNostr } from "./direct/browser-host.js";
|
|
12
15
|
export const LIB_NAME = "onelobby";
|
|
13
|
-
export const LIB_VERSION = "0.
|
|
16
|
+
export const LIB_VERSION = "0.6.0";
|
|
14
17
|
export const DEFAULT_READY_TIMEOUT_MS = 15_000;
|
|
15
18
|
function hasMic() {
|
|
16
19
|
return typeof navigator !== "undefined" && !!navigator.mediaDevices?.getUserMedia && typeof AudioWorkletNode !== "undefined";
|
|
@@ -46,6 +49,7 @@ export class OneLobby extends Emitter {
|
|
|
46
49
|
createMic;
|
|
47
50
|
createSink;
|
|
48
51
|
storage;
|
|
52
|
+
nostrConnectFn;
|
|
49
53
|
/** The validated, frozen onelobby.json this kit was built from (`OneLobby.fromConfig`); undefined for `new OneLobby`. */
|
|
50
54
|
blueprint;
|
|
51
55
|
/**
|
|
@@ -121,6 +125,7 @@ export class OneLobby extends Emitter {
|
|
|
121
125
|
this.createMic = mic !== undefined ? mic : hasMic() ? () => new WorkletMic() : null;
|
|
122
126
|
const sink = pick("createSink");
|
|
123
127
|
this.createSink = sink !== undefined ? sink : typeof AudioContext !== "undefined" ? () => new WebAudioSink() : null;
|
|
128
|
+
this.nostrConnectFn = a.nostrConnect;
|
|
124
129
|
this.storage = o.rememberSeat === false ? null : a.storage !== undefined ? a.storage : browserStorage();
|
|
125
130
|
}
|
|
126
131
|
// ---- remembered seat -------------------------------------------------------------------------------------------
|
|
@@ -322,19 +327,19 @@ export class OneLobby extends Emitter {
|
|
|
322
327
|
}
|
|
323
328
|
}
|
|
324
329
|
/**
|
|
325
|
-
* Join a direct-IP host (docs/direct-ip.md): a join code from the host's screen (`6B01-RGFE-P2B`, any case, spaces
|
|
330
|
+
* Join a direct-IP host (docs/design/direct-ip.md): a join code from the host's screen (`6B01-RGFE-P2B`, any case, spaces
|
|
326
331
|
* or hyphens) or, advanced, an address (`ip`, `ip:port`, `[v6]:port`). No lobby server is involved: the code is
|
|
327
332
|
* the host's address. A mistyped code fails at once with `bad_code`; nobody listening is `direct_unreachable`.
|
|
328
333
|
* The address is also given to ICE as the host's UDP candidate. When the host leaves, `sessionEnded("host_left")`.
|
|
329
334
|
*/
|
|
330
335
|
async joinDirect(codeOrAddress, opts = {}) {
|
|
331
|
-
const
|
|
332
|
-
const { sig, welcome } = await this.
|
|
336
|
+
const t = this.directTarget(codeOrAddress, opts);
|
|
337
|
+
const { sig, welcome } = await this.connectDirect(t, opts);
|
|
333
338
|
try {
|
|
334
339
|
const state = await sig.request({ t: "join", ...(opts.password ? { password: opts.password } : {}) }, "lobby");
|
|
335
|
-
const lobby = this.makeLobby(sig, welcome, state, { links });
|
|
340
|
+
const lobby = this.makeLobby(sig, this.withStun(welcome, t, opts), state, { links: t.links });
|
|
336
341
|
// a joiner's joinCode is what it joined with (normalized), so a HUD can show it on both sides
|
|
337
|
-
lobby.joinCode = joinedWith
|
|
342
|
+
lobby.joinCode = t.joinedWith;
|
|
338
343
|
return lobby;
|
|
339
344
|
}
|
|
340
345
|
catch (e) {
|
|
@@ -348,17 +353,51 @@ export class OneLobby extends Emitter {
|
|
|
348
353
|
* same peer id comes back. Rejects with `invalid_token` when the seat is gone: offer `joinDirect` then.
|
|
349
354
|
*/
|
|
350
355
|
async rejoinDirect(codeOrAddress, resumeToken, opts = {}) {
|
|
351
|
-
const
|
|
352
|
-
const lobby = await this.resumeAt(resumeToken,
|
|
353
|
-
lobby.joinCode = joinedWith
|
|
356
|
+
const t = this.directTarget(codeOrAddress, opts);
|
|
357
|
+
const lobby = await this.resumeAt(resumeToken, t.url, t.nostr ? 0 : opts.connectTimeoutMs ?? 5000, { links: t.links }, t.nostr ? this.nostrFactory(t, opts) : undefined, (w) => this.withStun(w, t, opts));
|
|
358
|
+
lobby.joinCode = t.joinedWith;
|
|
354
359
|
return lobby;
|
|
355
360
|
}
|
|
361
|
+
nostrFactory(t, opts) {
|
|
362
|
+
const relays = opts.nostrRelays ?? (this.blueprint ? [...this.blueprint.hosting.nostrRelays] : undefined);
|
|
363
|
+
return () => new NostrSocket({ code: t.nostr, relays: relays && relays.length > 0 ? relays : undefined, connect: this.nostrConnectFn, clock: this.clock, wall: this.wall });
|
|
364
|
+
}
|
|
365
|
+
/** Opens the signaling socket of a direct target: TCP to the host's address, or Nostr relays for a version 1 code. */
|
|
366
|
+
async connectDirect(t, opts) {
|
|
367
|
+
if (!t.nostr)
|
|
368
|
+
return this.connect(t.url, this.socketFactory, opts.connectTimeoutMs ?? 5000);
|
|
369
|
+
// the socket enforces its own deadlines (10 s to reach a relay, 15 s for the host to answer)
|
|
370
|
+
let sock = null;
|
|
371
|
+
const factory = this.nostrFactory(t, opts);
|
|
372
|
+
try {
|
|
373
|
+
return await this.connect(t.url, (u) => (sock = factory(u)), 0);
|
|
374
|
+
}
|
|
375
|
+
catch (e) {
|
|
376
|
+
const why = sock?.failure;
|
|
377
|
+
// same code as an IP host that does not answer, so a game handles one "could not reach the host" error
|
|
378
|
+
if (e instanceof OneLobbyError && e.code === "connect_failed")
|
|
379
|
+
throw new OneLobbyError("direct_unreachable", why || "no answer through the Nostr relays");
|
|
380
|
+
throw e;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/** A Nostr joiner needs STUN; take the host's list (it comes in `welcome`), else the public defaults. */
|
|
384
|
+
withStun(welcome, t, opts) {
|
|
385
|
+
if (!t.nostr || (welcome.ice && welcome.ice.length > 0))
|
|
386
|
+
return welcome;
|
|
387
|
+
const urls = opts.stun ?? [...DEFAULT_NOSTR_STUN];
|
|
388
|
+
return urls.length > 0 ? { ...welcome, ice: [{ urls }] } : welcome;
|
|
389
|
+
}
|
|
356
390
|
directTarget(codeOrAddress, opts) {
|
|
357
391
|
let input = codeOrAddress.trim();
|
|
358
392
|
if (looksLikeJoinCode(input)) {
|
|
359
393
|
const d = decodeJoinCode(input);
|
|
360
394
|
if ("error" in d)
|
|
361
395
|
throw new OneLobbyError(d.error, d.error === "unknown_version" ? `"${input}" is a join code from a newer version of the game` : `"${input}" is not a valid join code: check for a typo`);
|
|
396
|
+
if ("room" in d) {
|
|
397
|
+
// version 1: a Nostr room; signaling runs over public relays (spec/nostr-signaling.md), WebRTC uses STUN
|
|
398
|
+
const shown = encodeRoomCodeFor(d.room);
|
|
399
|
+
return { nostr: shown, url: `nostr:${shown}`, joinedWith: shown, links: this.linkFactory };
|
|
400
|
+
}
|
|
362
401
|
input = `${d.ip}:${d.port}`;
|
|
363
402
|
}
|
|
364
403
|
const addr = parseDirectAddress(input, opts.defaultPort ?? DEFAULT_DIRECT_PORT);
|
|
@@ -366,7 +405,11 @@ export class OneLobby extends Emitter {
|
|
|
366
405
|
throw new OneLobbyError("bad_address", `"${codeOrAddress}" is not a join code or an address (try 6B01-RGFE-P2B or 192.168.1.20:47810)`);
|
|
367
406
|
const inner = this.linkFactory;
|
|
368
407
|
const links = { create: (o) => inner.create({ ...o, iceServers: [], extraRemoteCandidates: [addr.candidate] }) };
|
|
369
|
-
return { addr, links };
|
|
408
|
+
return { nostr: null, url: addr.url, joinedWith: joinedWith(addr), links };
|
|
409
|
+
}
|
|
410
|
+
/** @internal onelobby/direct's Nostr host: the relay seam and the kit's clocks. */
|
|
411
|
+
nostrHostEnv() {
|
|
412
|
+
return { connect: this.nostrConnectFn, clock: this.clock, wall: this.wall };
|
|
370
413
|
}
|
|
371
414
|
/** @internal onelobby/direct's host: create the one lobby on the embedded hub, in direct mode. */
|
|
372
415
|
async hostDirectWith(h, hooks) {
|
|
@@ -381,17 +424,27 @@ export class OneLobby extends Emitter {
|
|
|
381
424
|
throw e;
|
|
382
425
|
}
|
|
383
426
|
}
|
|
427
|
+
/**
|
|
428
|
+
* Host a direct lobby from this browser tab (or any runtime with WebSocket and WebRTC): no Node process, no port.
|
|
429
|
+
* The hub runs in the page and joiners find it through Nostr relays with `lobby.joinCode` (a dc1 version 1 code,
|
|
430
|
+
* `joinCodeKind` "nostr"); they use `kit.joinDirect(code)`. Rejects with `direct_unreachable` when no relay answers
|
|
431
|
+
* within 10 s (a browser has no IP fallback) and `nostr_disabled` when the blueprint has `hosting.nostr` false.
|
|
432
|
+
* Keep the tab visible: hidden tabs are throttled (`lobby.hostHidden`, event `hostHiddenChanged`).
|
|
433
|
+
*/
|
|
434
|
+
hostNostr(opts = {}) {
|
|
435
|
+
return hostNostr(this, opts);
|
|
436
|
+
}
|
|
384
437
|
async rejoin(resumeToken) {
|
|
385
438
|
return this.resumeAt(resumeToken, this.options.lobbyServer, 0);
|
|
386
439
|
}
|
|
387
|
-
async resumeAt(resumeToken, url, timeoutMs, direct) {
|
|
388
|
-
const sig = await this.open(url, this.socketFactory, timeoutMs);
|
|
440
|
+
async resumeAt(resumeToken, url, timeoutMs, direct, factory, tweakWelcome) {
|
|
441
|
+
const sig = await this.open(url, factory ?? this.socketFactory, timeoutMs);
|
|
389
442
|
try {
|
|
390
443
|
// The server answers hello(resumeToken) with welcome then the full lobby state; listen for both up front.
|
|
391
444
|
const lobbyP = sig.expect("lobby");
|
|
392
445
|
const welcomeP = sig.request(this.helloMsg(resumeToken), "welcome");
|
|
393
446
|
lobbyP.catch(() => { }); // settled below or by close()
|
|
394
|
-
const welcome = await welcomeP;
|
|
447
|
+
const welcome = tweakWelcome ? tweakWelcome(await welcomeP) : await welcomeP;
|
|
395
448
|
// spec: an unknown or expired token is ignored -> plain welcome (no peerId), and no lobby follows
|
|
396
449
|
if (welcome.peerId === undefined)
|
|
397
450
|
throw new OneLobbyError("invalid_token", "resume token unknown or expired");
|
|
@@ -453,6 +506,7 @@ function guardLobby(lobby, bp) {
|
|
|
453
506
|
});
|
|
454
507
|
}
|
|
455
508
|
}
|
|
509
|
+
lobby.pause.mode = bp.session.pause;
|
|
456
510
|
if (!bp.chat.text) {
|
|
457
511
|
lobby.chat.send = () => {
|
|
458
512
|
throw new OneLobbyError("chat_disabled", "this game has text chat turned off (chat.text is false)");
|
package/dist/onelobby.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onelobby.js","sourceRoot":"","sources":["../src/onelobby.ts"],"names":[],"mappings":"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","sourcesContent":["import { systemClock, type Clock } from \"./clock.js\";\nimport { Emitter } from \"./emitter.js\";\nimport { normalizeCode } from \"./crockford.js\";\nimport { Lobby, type RateLimit } from \"./lobby.js\";\nimport {\n OneLobbyError,\n PROTO_ID,\n SignalingClient,\n type LobbyState,\n type LobbySummary,\n type WelcomeMsg,\n} from \"./signaling.js\";\nimport {\n defaultSocketFactory,\n type LinkFactory,\n type SocketFactory,\n} from \"./transport.js\";\nimport type { VoiceCodec, FrameSource, AudioSink } from \"./voice-types.js\";\nimport { WebAudioSink, WorkletMic, webCodecsOpus, webCodecsOpusAvailable } from \"./voice-browser.js\";\nimport { webRtcLinkFactory } from \"./webrtc.js\";\nimport { decodeJoinCode, encodeJoinCode, looksLikeJoinCode } from \"./joincode.js\";\nimport { DEFAULT_DIRECT_PORT, parseDirectAddress } from \"./direct-address.js\";\nimport { BlueprintError, parseBlueprint, type Blueprint } from \"./blueprint.js\";\n\nexport const LIB_NAME = \"onelobby\";\nexport const LIB_VERSION = \"0.5.0\";\n\n/** Where `rememberSeat` keeps the resume token: the subset of the Web Storage API it uses. */\nexport interface SeatStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** Seams for tests, adapters and non-browser runtimes. Every default is the browser implementation. */\nexport interface AdvancedOptions {\n socketFactory?: SocketFactory;\n linkFactory?: LinkFactory;\n clock?: Clock;\n /** Wall clock in unix ms, stamps chat lines. */\n wallClock?: () => number;\n /** Opus codec; defaults to WebCodecs when available. Provide a WASM-backed one elsewhere. */\n voiceCodec?: VoiceCodec | null;\n createMic?: (() => FrameSource) | null;\n createSink?: (() => AudioSink) | null;\n /** Storage for `rememberSeat`; defaults to `sessionStorage` when the page has one. null disables it. */\n storage?: SeatStorage | null;\n}\n\nexport interface OneLobbyOptions {\n gameId: string;\n netVersion: string;\n /** `ws://` / `wss://` URL of a onelobby-server. */\n lobbyServer: string;\n mode?: \"lockstep\" | \"raw\";\n tickRate?: number;\n /** Default: tickRate (once a second). */\n hashEveryTicks?: number;\n /** The player's name. Empty (the default): other players see \"Player N\", N being the seat number. */\n displayName?: string;\n relayOnly?: boolean;\n /**\n * Joiners give up on reaching the host after this many ms: `lobby.ready` rejects with code `ice_failed`, the kit\n * emits `error`, and the lobby is left. Half the time goes to a relay-only retry when the server offers TURN.\n * Default 15000; 0 waits forever.\n */\n readyTimeoutMs?: number;\n /**\n * Treat a peer as gone after this many ms of silence. Joiners ping the host every 2 s and the host answers, so the\n * host drops a silent joiner (`peerLeft`, \"timeout\") and a joiner handles a silent host as lost (a direct lobby\n * ends with \"host_left\"). Default 0 (off): a silently dead peer then surfaces when its link or signaling socket\n * times out, within about 30 s. Values under 6000 are raised to 6000.\n */\n peerTimeoutMs?: number;\n /**\n * Keep this tab's seat across a page reload (sessionStorage): `join(code)` and `resume()` take the seat back\n * instead of joining as a new player. Cleared when the session ends. Default true where sessionStorage exists.\n */\n rememberSeat?: boolean;\n /** Test and runtime seams (see AdvancedOptions). */\n advanced?: AdvancedOptions;\n\n /** @deprecated use `advanced.socketFactory` */\n socketFactory?: SocketFactory;\n /** @deprecated use `advanced.linkFactory` */\n linkFactory?: LinkFactory;\n /** @deprecated use `advanced.clock` */\n clock?: Clock;\n /** @deprecated use `advanced.wallClock` */\n wallClock?: () => number;\n /** @deprecated use `advanced.voiceCodec` */\n voiceCodec?: VoiceCodec | null;\n /** @deprecated use `advanced.createMic` */\n createMic?: (() => FrameSource) | null;\n /** @deprecated use `advanced.createSink` */\n createSink?: (() => AudioSink) | null;\n}\n\n/** Per-launch settings for `OneLobby.fromConfig`; everything else comes from the blueprint. */\nexport interface FromConfigOverrides {\n /** The player's name. Checked against `players.identity` and `players.maxNameLength`. */\n displayName?: string;\n /** Wins over `hosting.lobbyServer` (the file is the bottom layer: put `?lobby=` / storage / `<meta>` lookups here). */\n lobbyServer?: string;\n /** Default: `lobby.rejoin` from the file. */\n rememberSeat?: boolean;\n advanced?: AdvancedOptions;\n}\n\nexport interface HostOptions {\n name: string;\n maxPlayers: number;\n /**\n * \"unlisted\" (default): anyone with the code or link can join, but the lobby is not in `list()`.\n * \"public\": also listed. \"private\": the host approves each join.\n */\n visibility?: \"public\" | \"unlisted\" | \"private\";\n lateJoin?: boolean;\n meta?: Record<string, string>;\n /** Per-joiner cap on incoming raw messages, e.g. `{ messages: 60, perMs: 1000 }`; the excess is dropped before `message` fires or is relayed. */\n rateLimit?: RateLimit;\n}\n\nexport interface ListOptions {\n meta?: Record<string, string>;\n limit?: number;\n cursor?: string;\n}\n\nexport interface JoinDirectOptions {\n /** The host's password, if it set one; a wrong one fails with `bad_password`. */\n password?: string;\n /** Port for an address typed without one (default 47810, docs/direct-ip.md s.2). */\n defaultPort?: number;\n /** No answer on the host's TCP port within this long fails with `direct_unreachable` (default 5000). */\n connectTimeoutMs?: number;\n}\n\n/** @internal How onelobby/direct makes a host lobby: its own socket and link factory, and direct mode. */\nexport interface DirectLobbyHooks {\n socketFactory: SocketFactory;\n /** Wraps the kit's own link factory (port pin, no ICE servers). */\n wrapLinks: (inner: LinkFactory) => LinkFactory;\n}\n\nexport type OneLobbyEvents = { error: [Error] };\n\nexport const DEFAULT_READY_TIMEOUT_MS = 15_000;\n\nfunction hasMic(): boolean {\n return typeof navigator !== \"undefined\" && !!navigator.mediaDevices?.getUserMedia && typeof AudioWorkletNode !== \"undefined\";\n}\n\nfunction browserStorage(): SeatStorage | null {\n try {\n const s = (globalThis as { sessionStorage?: SeatStorage }).sessionStorage;\n return s ?? null;\n } catch {\n return null; // storage access can throw (sandboxed iframe, blocked cookies)\n }\n}\n\ninterface Seat {\n code: string;\n token: string;\n server: string;\n}\n\n/**\n * Lobbies alive in this page, by resume token. A remembered seat that is still in use here (two kits in one tab,\n * e.g. a local two-player demo) must not be taken over; after a reload this set starts empty.\n */\nconst liveSeats = new Set<Lobby>();\nfunction seatInUse(token: string): boolean {\n for (const l of liveSeats) if (l.resumeToken === token) return true;\n return false;\n}\n\n/** Entry point (DESIGN s.11.2). */\nexport class OneLobby extends Emitter<OneLobbyEvents> {\n readonly options: Required<Pick<OneLobbyOptions, \"gameId\" | \"netVersion\" | \"lobbyServer\" | \"mode\" | \"tickRate\" | \"hashEveryTicks\" | \"displayName\" | \"relayOnly\" | \"readyTimeoutMs\" | \"peerTimeoutMs\">>;\n private socketFactory: SocketFactory;\n private linkFactory: LinkFactory;\n private clock: Clock;\n private wall: () => number;\n private voiceCodec: VoiceCodec | null;\n private createMic: (() => FrameSource) | null;\n private createSink: (() => AudioSink) | null;\n private storage: SeatStorage | null;\n /** The validated, frozen onelobby.json this kit was built from (`OneLobby.fromConfig`); undefined for `new OneLobby`. */\n readonly blueprint?: Blueprint;\n\n /**\n * Build a kit from an onelobby.json (already parsed; docs/design/blueprint.md s.7). Throws `bad_config` for an\n * invalid file or name, `identity_anonymous` for a name when `players.identity` is \"anonymous\". Then `host()` and\n * `hostDirect()` default their options from the file and the file's chat/listing switches are enforced.\n */\n static fromConfig(json: unknown, overrides: FromConfigOverrides = {}): OneLobby {\n const bp = parseBlueprint(json);\n const name = (overrides.displayName ?? \"\").trim();\n if (name) {\n if (bp.players.identity === \"anonymous\") throw new OneLobbyError(\"identity_anonymous\", \"this game uses anonymous players: no display names\");\n if (name.length > bp.players.maxNameLength)\n throw new BlueprintError(\"displayName\", \"length\", `must be at most ${bp.players.maxNameLength} characters`);\n }\n const kit = new OneLobby({\n gameId: bp.game.id,\n netVersion: bp.game.netVersion,\n lobbyServer: overrides.lobbyServer ?? bp.hosting.lobbyServer,\n mode: bp.sync.mode === \"lockstep\" ? \"lockstep\" : \"raw\", // replication is C# only; JS speaks raw\n tickRate: bp.sync.tickRate,\n hashEveryTicks: bp.sync.hashEveryTicks,\n displayName: name,\n relayOnly: bp.hosting.relayOnly,\n readyTimeoutMs: bp.session.readyTimeoutMs,\n peerTimeoutMs: bp.session.peerTimeoutMs,\n rememberSeat: overrides.rememberSeat ?? bp.lobby.rejoin,\n advanced: overrides.advanced,\n });\n (kit as { blueprint?: Blueprint }).blueprint = bp;\n return kit;\n }\n\n /** @internal Host options the blueprint implies (empty without one). */\n blueprintHostDefaults(): Partial<HostOptions> {\n const bp = this.blueprint;\n if (!bp) return {};\n const name = this.options.displayName;\n return {\n name: name ? `${name}'s game` : \"Game\",\n maxPlayers: bp.lobby.maxPlayers,\n visibility: bp.lobby.listed ? \"public\" : bp.lobby.approveJoins ? \"private\" : \"unlisted\",\n lateJoin: bp.lobby.lateJoin,\n meta: { ...bp.lobby.meta },\n ...(bp.session.rateLimit ? { rateLimit: { ...bp.session.rateLimit } } : {}),\n };\n }\n\n constructor(o: OneLobbyOptions) {\n super();\n const tickRate = o.tickRate ?? 20;\n this.options = {\n gameId: o.gameId,\n netVersion: o.netVersion,\n lobbyServer: o.lobbyServer,\n mode: o.mode ?? \"lockstep\",\n tickRate,\n hashEveryTicks: o.hashEveryTicks ?? tickRate,\n displayName: (o.displayName ?? \"\").trim(),\n relayOnly: o.relayOnly ?? false,\n readyTimeoutMs: o.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,\n peerTimeoutMs: o.peerTimeoutMs ?? 0,\n };\n const a = o.advanced ?? {};\n const pick = <K extends keyof AdvancedOptions & keyof OneLobbyOptions>(k: K) => (a[k] !== undefined ? a[k] : o[k]);\n this.socketFactory = pick(\"socketFactory\") ?? defaultSocketFactory;\n this.linkFactory = pick(\"linkFactory\") ?? webRtcLinkFactory;\n this.clock = pick(\"clock\") ?? systemClock;\n this.wall = pick(\"wallClock\") ?? (() => Date.now());\n const codec = pick(\"voiceCodec\");\n this.voiceCodec = codec !== undefined ? codec : webCodecsOpusAvailable() ? webCodecsOpus : null;\n const mic = pick(\"createMic\");\n this.createMic = mic !== undefined ? mic : hasMic() ? () => new WorkletMic() : null;\n const sink = pick(\"createSink\");\n this.createSink = sink !== undefined ? sink : typeof AudioContext !== \"undefined\" ? () => new WebAudioSink() : null;\n this.storage = o.rememberSeat === false ? null : a.storage !== undefined ? a.storage : browserStorage();\n }\n\n // ---- remembered seat -------------------------------------------------------------------------------------------\n\n private get seatKey(): string {\n return `onelobby.seat.${this.options.gameId}`;\n }\n\n private loadSeat(): Seat | null {\n if (!this.storage) return null;\n try {\n const raw = this.storage.getItem(this.seatKey);\n if (!raw) return null;\n const s = JSON.parse(raw) as Partial<Seat>;\n if (typeof s.code !== \"string\" || typeof s.token !== \"string\" || s.server !== this.options.lobbyServer) return null;\n return s as Seat;\n } catch {\n return null;\n }\n }\n\n private saveSeat(code: string, token: string): void {\n if (!this.storage || !token) return;\n try {\n this.storage.setItem(this.seatKey, JSON.stringify({ code, token, server: this.options.lobbyServer } satisfies Seat));\n } catch {\n /* quota or blocked storage: the seat just is not remembered */\n }\n }\n\n private clearSeat(code?: string): void {\n if (!this.storage) return;\n try {\n if (code !== undefined) {\n const s = this.loadSeat();\n if (s && s.code !== code) return;\n }\n this.storage.removeItem(this.seatKey);\n } catch {\n /* ignore */\n }\n }\n\n /** The lobby code of the seat this tab can take back (rememberSeat), or null. */\n get rememberedCode(): string | null {\n return this.loadSeat()?.code ?? null;\n }\n\n /**\n * Take back the seat remembered from before a page reload (host or joiner). Resolves null when there is none\n * or the server no longer holds it (the seat is forgotten then).\n */\n async resume(): Promise<Lobby | null> {\n const seat = this.loadSeat();\n if (!seat || seatInUse(seat.token)) return null;\n try {\n return await this.rejoin(seat.token);\n } catch (e) {\n if (e instanceof OneLobbyError && e.code === \"invalid_token\") {\n this.clearSeat();\n return null;\n }\n throw e;\n }\n }\n\n // ---- connecting ------------------------------------------------------------------------------------------------\n\n private helloMsg(resumeToken?: string): Record<string, unknown> {\n return {\n t: \"hello\",\n proto: PROTO_ID,\n gameId: this.options.gameId,\n netVersion: this.options.netVersion,\n client: { lib: LIB_NAME, ver: LIB_VERSION, platform: typeof navigator !== \"undefined\" ? \"browser\" : \"node\" },\n name: this.options.displayName.slice(0, 24),\n ...(resumeToken ? { resumeToken } : {}),\n };\n }\n\n private async connect(url = this.options.lobbyServer, factory = this.socketFactory, timeoutMs = 0): Promise<{ sig: SignalingClient; welcome: WelcomeMsg }> {\n const sig = await this.open(url, factory, timeoutMs);\n try {\n const welcome = await sig.request<WelcomeMsg & { t: string }>(this.helloMsg(), \"welcome\");\n return { sig, welcome };\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /** The signaling socket only (no hello yet); with a timeout (direct IP), no answer is `direct_unreachable`. */\n private async open(url: string, factory: SocketFactory, timeoutMs: number): Promise<SignalingClient> {\n if (!url.trim() && factory === defaultSocketFactory) throw new OneLobbyError(\"no_server\", \"no lobby server address (set hosting.lobbyServer or pass lobbyServer)\");\n const sig = new SignalingClient(url, factory, this.clock);\n if (timeoutMs > 0) {\n let cancel = () => {};\n const timeout = new Promise<never>((_, reject) => {\n cancel = this.clock.setTimeout(() => reject(new OneLobbyError(\"direct_unreachable\", `no answer from ${url} within ${timeoutMs} ms`)), timeoutMs);\n });\n try {\n await Promise.race([sig.connect(), timeout]);\n } catch (e) {\n sig.close();\n if (e instanceof OneLobbyError && e.code === \"connect_failed\") throw new OneLobbyError(\"direct_unreachable\", `no answer from ${url}`);\n throw e;\n } finally {\n cancel();\n }\n } else await sig.connect();\n return sig;\n }\n\n private makeLobby(sig: SignalingClient, welcome: WelcomeMsg, state: LobbyState, direct?: { links: LinkFactory }, rateLimit?: RateLimit): Lobby {\n const lobby = new Lobby({\n signaling: sig,\n state,\n ice: welcome.ice ?? [],\n links: direct?.links ?? this.linkFactory,\n direct: !!direct,\n clock: this.clock,\n wall: this.wall,\n hashEveryTicks: this.options.hashEveryTicks,\n relayOnly: state.relayOnly ?? this.options.relayOnly,\n displayName: this.options.displayName,\n voice: { codec: this.voiceCodec, createSource: this.createMic, createSink: this.createSink },\n readyTimeoutMs: this.options.readyTimeoutMs,\n peerTimeoutMs: this.options.peerTimeoutMs,\n rateLimit,\n });\n lobby.errorSink = (e) => this.emit(\"error\", e);\n if (this.blueprint) guardLobby(lobby, this.blueprint);\n if (this.storage && !direct) {\n liveSeats.add(lobby);\n this.saveSeat(lobby.code, lobby.resumeToken);\n lobby.seatSink = (token) => this.saveSeat(lobby.code, token);\n lobby.on(\"sessionEnded\", () => {\n liveSeats.delete(lobby);\n this.clearSeat(lobby.code);\n });\n }\n return lobby;\n }\n\n /** With a blueprint (`fromConfig`) every option defaults from `lobby.*`; explicit options win. */\n async host(opts?: Partial<HostOptions>): Promise<Lobby> {\n const h = { name: \"Game\", maxPlayers: 4, ...this.blueprintHostDefaults(), ...definedOnly(opts) } as HostOptions;\n const { sig, welcome } = await this.connect();\n try {\n const state = await sig.request<LobbyState>(\n {\n t: \"create\",\n name: h.name,\n maxPlayers: h.maxPlayers,\n visibility: h.visibility ?? \"unlisted\",\n mode: this.options.mode,\n tickRate: this.options.tickRate,\n lateJoin: h.lateJoin ?? true,\n meta: h.meta ?? {},\n ...(this.options.relayOnly ? { relayOnly: true } : {}),\n },\n \"lobby\",\n );\n return this.makeLobby(sig, welcome, state, undefined, h.rateLimit);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async join(code: string): Promise<Lobby> {\n const norm = normalizeCode(code);\n if (!norm) throw new OneLobbyError(\"bad_code\", `\"${code}\" is not a valid join code`);\n const seat = this.loadSeat();\n if (seat && seat.code === norm && !seatInUse(seat.token)) {\n // Same tab, same lobby (a page reload): take the old seat back instead of joining as a new player.\n try {\n return await this.rejoin(seat.token);\n } catch (e) {\n this.clearSeat();\n if (!(e instanceof OneLobbyError && e.code === \"invalid_token\")) throw e;\n }\n }\n const { sig, welcome } = await this.connect();\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", code: norm }, \"lobby\");\n return this.makeLobby(sig, welcome, state);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Join a direct-IP host (docs/direct-ip.md): a join code from the host's screen (`6B01-RGFE-P2B`, any case, spaces\n * or hyphens) or, advanced, an address (`ip`, `ip:port`, `[v6]:port`). No lobby server is involved: the code is\n * the host's address. A mistyped code fails at once with `bad_code`; nobody listening is `direct_unreachable`.\n * The address is also given to ICE as the host's UDP candidate. When the host leaves, `sessionEnded(\"host_left\")`.\n */\n async joinDirect(codeOrAddress: string, opts: JoinDirectOptions = {}): Promise<Lobby> {\n const { addr, links } = this.directTarget(codeOrAddress, opts);\n const { sig, welcome } = await this.connect(addr.url, this.socketFactory, opts.connectTimeoutMs ?? 5000);\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", ...(opts.password ? { password: opts.password } : {}) }, \"lobby\");\n const lobby = this.makeLobby(sig, welcome, state, { links });\n // a joiner's joinCode is what it joined with (normalized), so a HUD can show it on both sides\n lobby.joinCode = joinedWith(addr);\n return lobby;\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Take a direct-IP seat back after a drop or a restart: the code (or address) joined before and that lobby's\n * `resumeToken` (save it after every join; it changes on each resume). The host keeps a dropped seat for 60 s and the\n * same peer id comes back. Rejects with `invalid_token` when the seat is gone: offer `joinDirect` then.\n */\n async rejoinDirect(codeOrAddress: string, resumeToken: string, opts: JoinDirectOptions = {}): Promise<Lobby> {\n const { addr, links } = this.directTarget(codeOrAddress, opts);\n const lobby = await this.resumeAt(resumeToken, addr.url, opts.connectTimeoutMs ?? 5000, { links });\n lobby.joinCode = joinedWith(addr);\n return lobby;\n }\n\n private directTarget(codeOrAddress: string, opts: JoinDirectOptions): { addr: NonNullable<ReturnType<typeof parseDirectAddress>>; links: LinkFactory } {\n let input = codeOrAddress.trim();\n if (looksLikeJoinCode(input)) {\n const d = decodeJoinCode(input);\n if (\"error\" in d)\n throw new OneLobbyError(d.error, d.error === \"unknown_version\" ? `\"${input}\" is a join code from a newer version of the game` : `\"${input}\" is not a valid join code: check for a typo`);\n input = `${d.ip}:${d.port}`;\n }\n const addr = parseDirectAddress(input, opts.defaultPort ?? DEFAULT_DIRECT_PORT);\n if (!addr) throw new OneLobbyError(\"bad_address\", `\"${codeOrAddress}\" is not a join code or an address (try 6B01-RGFE-P2B or 192.168.1.20:47810)`);\n const inner = this.linkFactory;\n const links: LinkFactory = { create: (o) => inner.create({ ...o, iceServers: [], extraRemoteCandidates: [addr.candidate] }) };\n return { addr, links };\n }\n\n /** @internal onelobby/direct's host: create the one lobby on the embedded hub, in direct mode. */\n async hostDirectWith(h: HostOptions, hooks: DirectLobbyHooks): Promise<Lobby> {\n const { sig, welcome } = await this.connect(\"onelobby-direct://host/\", hooks.socketFactory);\n try {\n const state = await sig.request<LobbyState>(\n { t: \"create\", name: h.name, maxPlayers: h.maxPlayers, visibility: \"unlisted\", mode: this.options.mode, tickRate: this.options.tickRate,\n lateJoin: h.lateJoin ?? true, meta: h.meta ?? {} },\n \"lobby\",\n );\n return this.makeLobby(sig, welcome, state, { links: hooks.wrapLinks(this.linkFactory) }, h.rateLimit);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async rejoin(resumeToken: string): Promise<Lobby> {\n return this.resumeAt(resumeToken, this.options.lobbyServer, 0);\n }\n\n private async resumeAt(resumeToken: string, url: string, timeoutMs: number, direct?: { links: LinkFactory }): Promise<Lobby> {\n const sig = await this.open(url, this.socketFactory, timeoutMs);\n try {\n // The server answers hello(resumeToken) with welcome then the full lobby state; listen for both up front.\n const lobbyP = sig.expect<LobbyState>(\"lobby\");\n const welcomeP = sig.request<WelcomeMsg & { t: string }>(this.helloMsg(resumeToken), \"welcome\");\n lobbyP.catch(() => {}); // settled below or by close()\n const welcome = await welcomeP;\n // spec: an unknown or expired token is ignored -> plain welcome (no peerId), and no lobby follows\n if (welcome.peerId === undefined) throw new OneLobbyError(\"invalid_token\", \"resume token unknown or expired\");\n const state = await lobbyP;\n return this.makeLobby(sig, welcome, state, direct);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async list(opts: ListOptions = {}): Promise<LobbySummary[]> {\n if (this.blueprint && !this.blueprint.hosting.server) throw new OneLobbyError(\"listing_disabled\", \"this game has no lobby server (hosting.server is false)\");\n const { sig } = await this.connect();\n try {\n const res = await sig.request<{ t: string; items: LobbySummary[] }>(\n { t: \"list\", visibility: \"public\", meta: opts.meta ?? {}, limit: Math.min(100, opts.limit ?? 50), ...(opts.cursor ? { cursor: opts.cursor } : {}) },\n \"lobbies\",\n );\n return res.items ?? [];\n } finally {\n sig.close();\n }\n }\n}\n\n/** The join code for an IPv4 address (a host name or IPv6 has none: its `host:port` instead). */\nfunction joinedWith(addr: { host: string; port: number; isIPv6: boolean; display: string }): string {\n if (addr.isIPv6) return addr.display;\n try {\n return encodeJoinCode(addr.host, addr.port);\n } catch {\n return addr.display;\n }\n}\n\nfunction definedOnly<T extends object>(o: T | undefined): Partial<T> {\n const out: Partial<T> = {};\n if (o) for (const [k, v] of Object.entries(o)) if (v !== undefined) (out as Record<string, unknown>)[k] = v;\n return out;\n}\n\n/** Blueprint switches that gate features on the lobby: voice and text chat; chat.voiceMode is the starting voice mode. */\nfunction guardLobby(lobby: Lobby, bp: Blueprint): void {\n lobby.voice.mode = bp.chat.voiceMode;\n if (!bp.chat.voice) {\n const voice = lobby.voice;\n const d = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(voice), \"enabled\");\n if (d?.get && d.set) {\n Object.defineProperty(voice, \"enabled\", {\n configurable: true,\n get: () => d.get!.call(voice) as boolean,\n set: (v: boolean) => {\n if (v) throw new OneLobbyError(\"voice_disabled\", \"this game has voice chat turned off (chat.voice is false)\");\n d.set!.call(voice, v);\n },\n });\n }\n }\n if (!bp.chat.text) {\n lobby.chat.send = () => {\n throw new OneLobbyError(\"chat_disabled\", \"this game has text chat turned off (chat.text is false)\");\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"onelobby.js","sourceRoot":"","sources":["../src/onelobby.ts"],"names":[],"mappings":"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","sourcesContent":["import { systemClock, type Clock } from \"./clock.js\";\nimport { Emitter } from \"./emitter.js\";\nimport { normalizeCode } from \"./crockford.js\";\nimport { Lobby, type RateLimit } from \"./lobby.js\";\nimport {\n OneLobbyError,\n PROTO_ID,\n SignalingClient,\n type LobbyState,\n type LobbySummary,\n type WelcomeMsg,\n} from \"./signaling.js\";\nimport {\n defaultSocketFactory,\n type LinkFactory,\n type SocketFactory,\n} from \"./transport.js\";\nimport type { VoiceCodec, FrameSource, AudioSink } from \"./voice-types.js\";\nimport { WebAudioSink, WorkletMic, webCodecsOpus, webCodecsOpusAvailable } from \"./voice-browser.js\";\nimport { webRtcLinkFactory } from \"./webrtc.js\";\nimport { decodeJoinCode, encodeJoinCode, encodeRoomCodeFor, looksLikeJoinCode } from \"./joincode.js\";\nimport { NostrSocket } from \"./nostr/socket.js\";\nimport { DEFAULT_NOSTR_STUN, type RelayConnect } from \"./nostr/relays.js\";\nimport { DEFAULT_DIRECT_PORT, parseDirectAddress } from \"./direct-address.js\";\nimport { BlueprintError, parseBlueprint, type Blueprint } from \"./blueprint.js\";\nimport { hostNostr, type BrowserHostOptions } from \"./direct/browser-host.js\";\n\nexport const LIB_NAME = \"onelobby\";\nexport const LIB_VERSION = \"0.6.0\";\n\n/** Where `rememberSeat` keeps the resume token: the subset of the Web Storage API it uses. */\nexport interface SeatStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/** Seams for tests, adapters and non-browser runtimes. Every default is the browser implementation. */\nexport interface AdvancedOptions {\n socketFactory?: SocketFactory;\n linkFactory?: LinkFactory;\n clock?: Clock;\n /** Wall clock in unix ms, stamps chat lines. */\n wallClock?: () => number;\n /** Opus codec; defaults to WebCodecs when available. Provide a WASM-backed one elsewhere. */\n voiceCodec?: VoiceCodec | null;\n createMic?: (() => FrameSource) | null;\n createSink?: (() => AudioSink) | null;\n /** Storage for `rememberSeat`; defaults to `sessionStorage` when the page has one. null disables it. */\n storage?: SeatStorage | null;\n /** WebSocket to a Nostr relay (default: the runtime's `WebSocket`); for Node older than 22 or tests. */\n nostrConnect?: RelayConnect;\n}\n\nexport interface OneLobbyOptions {\n gameId: string;\n netVersion: string;\n /** `ws://` / `wss://` URL of a onelobby-server. */\n lobbyServer: string;\n mode?: \"lockstep\" | \"raw\";\n tickRate?: number;\n /** Default: tickRate (once a second). */\n hashEveryTicks?: number;\n /** The player's name. Empty (the default): other players see \"Player N\", N being the seat number. */\n displayName?: string;\n relayOnly?: boolean;\n /**\n * Joiners give up on reaching the host after this many ms: `lobby.ready` rejects with code `ice_failed`, the kit\n * emits `error`, and the lobby is left. Half the time goes to a relay-only retry when the server offers TURN.\n * Default 15000; 0 waits forever.\n */\n readyTimeoutMs?: number;\n /**\n * Treat a peer as gone after this many ms of silence. Joiners ping the host every 2 s and the host answers, so the\n * host drops a silent joiner (`peerLeft`, \"timeout\") and a joiner handles a silent host as lost (a direct lobby\n * ends with \"host_left\"). Default 0 (off): a silently dead peer then surfaces when its link or signaling socket\n * times out, within about 30 s. Values under 6000 are raised to 6000.\n */\n peerTimeoutMs?: number;\n /**\n * Keep this tab's seat across a page reload (sessionStorage): `join(code)` and `resume()` take the seat back\n * instead of joining as a new player. Cleared when the session ends. Default true where sessionStorage exists.\n */\n rememberSeat?: boolean;\n /** Test and runtime seams (see AdvancedOptions). */\n advanced?: AdvancedOptions;\n\n /** @deprecated use `advanced.socketFactory` */\n socketFactory?: SocketFactory;\n /** @deprecated use `advanced.linkFactory` */\n linkFactory?: LinkFactory;\n /** @deprecated use `advanced.clock` */\n clock?: Clock;\n /** @deprecated use `advanced.wallClock` */\n wallClock?: () => number;\n /** @deprecated use `advanced.voiceCodec` */\n voiceCodec?: VoiceCodec | null;\n /** @deprecated use `advanced.createMic` */\n createMic?: (() => FrameSource) | null;\n /** @deprecated use `advanced.createSink` */\n createSink?: (() => AudioSink) | null;\n}\n\n/** Per-launch settings for `OneLobby.fromConfig`; everything else comes from the blueprint. */\nexport interface FromConfigOverrides {\n /** The player's name. Checked against `players.identity` and `players.maxNameLength`. */\n displayName?: string;\n /** Wins over `hosting.lobbyServer` (the file is the bottom layer: put `?lobby=` / storage / `<meta>` lookups here). */\n lobbyServer?: string;\n /** Default: `lobby.rejoin` from the file. */\n rememberSeat?: boolean;\n advanced?: AdvancedOptions;\n}\n\nexport interface HostOptions {\n name: string;\n maxPlayers: number;\n /**\n * \"unlisted\" (default): anyone with the code or link can join, but the lobby is not in `list()`.\n * \"public\": also listed. \"private\": the host approves each join.\n */\n visibility?: \"public\" | \"unlisted\" | \"private\";\n lateJoin?: boolean;\n meta?: Record<string, string>;\n /** Per-joiner cap on incoming raw messages, e.g. `{ messages: 60, perMs: 1000 }`; the excess is dropped before `message` fires or is relayed. */\n rateLimit?: RateLimit;\n}\n\nexport interface ListOptions {\n meta?: Record<string, string>;\n limit?: number;\n cursor?: string;\n}\n\nexport interface JoinDirectOptions {\n /** The host's password, if it set one; a wrong one fails with `bad_password`. */\n password?: string;\n /** Port for an address typed without one (default 47810, docs/design/direct-ip.md s.2). */\n defaultPort?: number;\n /** No answer on the host's TCP port within this long fails with `direct_unreachable` (default 5000). */\n connectTimeoutMs?: number;\n /** Nostr relays for a Nostr code (version 1); default the blueprint's `hosting.nostrRelays`, else the library list. */\n nostrRelays?: string[];\n /** STUN servers for a Nostr code when the host offers none; default Google's and Cloudflare's public ones. */\n stun?: string[];\n}\n\n/** Where a direct join goes: a host address (TCP) or a Nostr room. */\ninterface DirectTarget {\n /** The display form of the version 1 code, or null for an address. */\n nostr: string | null;\n url: string;\n joinedWith: string;\n links: LinkFactory;\n}\n\n/** @internal How onelobby/direct makes a host lobby: its own socket and link factory, and direct mode. */\nexport interface DirectLobbyHooks {\n socketFactory: SocketFactory;\n /** Wraps the kit's own link factory (port pin, no ICE servers). */\n wrapLinks: (inner: LinkFactory) => LinkFactory;\n}\n\nexport type OneLobbyEvents = { error: [Error] };\n\nexport const DEFAULT_READY_TIMEOUT_MS = 15_000;\n\nfunction hasMic(): boolean {\n return typeof navigator !== \"undefined\" && !!navigator.mediaDevices?.getUserMedia && typeof AudioWorkletNode !== \"undefined\";\n}\n\nfunction browserStorage(): SeatStorage | null {\n try {\n const s = (globalThis as { sessionStorage?: SeatStorage }).sessionStorage;\n return s ?? null;\n } catch {\n return null; // storage access can throw (sandboxed iframe, blocked cookies)\n }\n}\n\ninterface Seat {\n code: string;\n token: string;\n server: string;\n}\n\n/**\n * Lobbies alive in this page, by resume token. A remembered seat that is still in use here (two kits in one tab,\n * e.g. a local two-player demo) must not be taken over; after a reload this set starts empty.\n */\nconst liveSeats = new Set<Lobby>();\nfunction seatInUse(token: string): boolean {\n for (const l of liveSeats) if (l.resumeToken === token) return true;\n return false;\n}\n\n/** Entry point (DESIGN s.11.2). */\nexport class OneLobby extends Emitter<OneLobbyEvents> {\n readonly options: Required<Pick<OneLobbyOptions, \"gameId\" | \"netVersion\" | \"lobbyServer\" | \"mode\" | \"tickRate\" | \"hashEveryTicks\" | \"displayName\" | \"relayOnly\" | \"readyTimeoutMs\" | \"peerTimeoutMs\">>;\n private socketFactory: SocketFactory;\n private linkFactory: LinkFactory;\n private clock: Clock;\n private wall: () => number;\n private voiceCodec: VoiceCodec | null;\n private createMic: (() => FrameSource) | null;\n private createSink: (() => AudioSink) | null;\n private storage: SeatStorage | null;\n private nostrConnectFn: RelayConnect | undefined;\n /** The validated, frozen onelobby.json this kit was built from (`OneLobby.fromConfig`); undefined for `new OneLobby`. */\n readonly blueprint?: Blueprint;\n\n /**\n * Build a kit from an onelobby.json (already parsed; docs/design/blueprint.md s.7). Throws `bad_config` for an\n * invalid file or name, `identity_anonymous` for a name when `players.identity` is \"anonymous\". Then `host()` and\n * `hostDirect()` default their options from the file and the file's chat/listing switches are enforced.\n */\n static fromConfig(json: unknown, overrides: FromConfigOverrides = {}): OneLobby {\n const bp = parseBlueprint(json);\n const name = (overrides.displayName ?? \"\").trim();\n if (name) {\n if (bp.players.identity === \"anonymous\") throw new OneLobbyError(\"identity_anonymous\", \"this game uses anonymous players: no display names\");\n if (name.length > bp.players.maxNameLength)\n throw new BlueprintError(\"displayName\", \"length\", `must be at most ${bp.players.maxNameLength} characters`);\n }\n const kit = new OneLobby({\n gameId: bp.game.id,\n netVersion: bp.game.netVersion,\n lobbyServer: overrides.lobbyServer ?? bp.hosting.lobbyServer,\n mode: bp.sync.mode === \"lockstep\" ? \"lockstep\" : \"raw\", // replication is C# only; JS speaks raw\n tickRate: bp.sync.tickRate,\n hashEveryTicks: bp.sync.hashEveryTicks,\n displayName: name,\n relayOnly: bp.hosting.relayOnly,\n readyTimeoutMs: bp.session.readyTimeoutMs,\n peerTimeoutMs: bp.session.peerTimeoutMs,\n rememberSeat: overrides.rememberSeat ?? bp.lobby.rejoin,\n advanced: overrides.advanced,\n });\n (kit as { blueprint?: Blueprint }).blueprint = bp;\n return kit;\n }\n\n /** @internal Host options the blueprint implies (empty without one). */\n blueprintHostDefaults(): Partial<HostOptions> {\n const bp = this.blueprint;\n if (!bp) return {};\n const name = this.options.displayName;\n return {\n name: name ? `${name}'s game` : \"Game\",\n maxPlayers: bp.lobby.maxPlayers,\n visibility: bp.lobby.listed ? \"public\" : bp.lobby.approveJoins ? \"private\" : \"unlisted\",\n lateJoin: bp.lobby.lateJoin,\n meta: { ...bp.lobby.meta },\n ...(bp.session.rateLimit ? { rateLimit: { ...bp.session.rateLimit } } : {}),\n };\n }\n\n constructor(o: OneLobbyOptions) {\n super();\n const tickRate = o.tickRate ?? 20;\n this.options = {\n gameId: o.gameId,\n netVersion: o.netVersion,\n lobbyServer: o.lobbyServer,\n mode: o.mode ?? \"lockstep\",\n tickRate,\n hashEveryTicks: o.hashEveryTicks ?? tickRate,\n displayName: (o.displayName ?? \"\").trim(),\n relayOnly: o.relayOnly ?? false,\n readyTimeoutMs: o.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS,\n peerTimeoutMs: o.peerTimeoutMs ?? 0,\n };\n const a = o.advanced ?? {};\n const pick = <K extends keyof AdvancedOptions & keyof OneLobbyOptions>(k: K) => (a[k] !== undefined ? a[k] : o[k]);\n this.socketFactory = pick(\"socketFactory\") ?? defaultSocketFactory;\n this.linkFactory = pick(\"linkFactory\") ?? webRtcLinkFactory;\n this.clock = pick(\"clock\") ?? systemClock;\n this.wall = pick(\"wallClock\") ?? (() => Date.now());\n const codec = pick(\"voiceCodec\");\n this.voiceCodec = codec !== undefined ? codec : webCodecsOpusAvailable() ? webCodecsOpus : null;\n const mic = pick(\"createMic\");\n this.createMic = mic !== undefined ? mic : hasMic() ? () => new WorkletMic() : null;\n const sink = pick(\"createSink\");\n this.createSink = sink !== undefined ? sink : typeof AudioContext !== \"undefined\" ? () => new WebAudioSink() : null;\n this.nostrConnectFn = a.nostrConnect;\n this.storage = o.rememberSeat === false ? null : a.storage !== undefined ? a.storage : browserStorage();\n }\n\n // ---- remembered seat -------------------------------------------------------------------------------------------\n\n private get seatKey(): string {\n return `onelobby.seat.${this.options.gameId}`;\n }\n\n private loadSeat(): Seat | null {\n if (!this.storage) return null;\n try {\n const raw = this.storage.getItem(this.seatKey);\n if (!raw) return null;\n const s = JSON.parse(raw) as Partial<Seat>;\n if (typeof s.code !== \"string\" || typeof s.token !== \"string\" || s.server !== this.options.lobbyServer) return null;\n return s as Seat;\n } catch {\n return null;\n }\n }\n\n private saveSeat(code: string, token: string): void {\n if (!this.storage || !token) return;\n try {\n this.storage.setItem(this.seatKey, JSON.stringify({ code, token, server: this.options.lobbyServer } satisfies Seat));\n } catch {\n /* quota or blocked storage: the seat just is not remembered */\n }\n }\n\n private clearSeat(code?: string): void {\n if (!this.storage) return;\n try {\n if (code !== undefined) {\n const s = this.loadSeat();\n if (s && s.code !== code) return;\n }\n this.storage.removeItem(this.seatKey);\n } catch {\n /* ignore */\n }\n }\n\n /** The lobby code of the seat this tab can take back (rememberSeat), or null. */\n get rememberedCode(): string | null {\n return this.loadSeat()?.code ?? null;\n }\n\n /**\n * Take back the seat remembered from before a page reload (host or joiner). Resolves null when there is none\n * or the server no longer holds it (the seat is forgotten then).\n */\n async resume(): Promise<Lobby | null> {\n const seat = this.loadSeat();\n if (!seat || seatInUse(seat.token)) return null;\n try {\n return await this.rejoin(seat.token);\n } catch (e) {\n if (e instanceof OneLobbyError && e.code === \"invalid_token\") {\n this.clearSeat();\n return null;\n }\n throw e;\n }\n }\n\n // ---- connecting ------------------------------------------------------------------------------------------------\n\n private helloMsg(resumeToken?: string): Record<string, unknown> {\n return {\n t: \"hello\",\n proto: PROTO_ID,\n gameId: this.options.gameId,\n netVersion: this.options.netVersion,\n client: { lib: LIB_NAME, ver: LIB_VERSION, platform: typeof navigator !== \"undefined\" ? \"browser\" : \"node\" },\n name: this.options.displayName.slice(0, 24),\n ...(resumeToken ? { resumeToken } : {}),\n };\n }\n\n private async connect(url = this.options.lobbyServer, factory = this.socketFactory, timeoutMs = 0): Promise<{ sig: SignalingClient; welcome: WelcomeMsg }> {\n const sig = await this.open(url, factory, timeoutMs);\n try {\n const welcome = await sig.request<WelcomeMsg & { t: string }>(this.helloMsg(), \"welcome\");\n return { sig, welcome };\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /** The signaling socket only (no hello yet); with a timeout (direct IP), no answer is `direct_unreachable`. */\n private async open(url: string, factory: SocketFactory, timeoutMs: number): Promise<SignalingClient> {\n if (!url.trim() && factory === defaultSocketFactory) throw new OneLobbyError(\"no_server\", \"no lobby server address (set hosting.lobbyServer or pass lobbyServer)\");\n const sig = new SignalingClient(url, factory, this.clock);\n if (timeoutMs > 0) {\n let cancel = () => {};\n const timeout = new Promise<never>((_, reject) => {\n cancel = this.clock.setTimeout(() => reject(new OneLobbyError(\"direct_unreachable\", `no answer from ${url} within ${timeoutMs} ms`)), timeoutMs);\n });\n try {\n await Promise.race([sig.connect(), timeout]);\n } catch (e) {\n sig.close();\n if (e instanceof OneLobbyError && e.code === \"connect_failed\") throw new OneLobbyError(\"direct_unreachable\", `no answer from ${url}`);\n throw e;\n } finally {\n cancel();\n }\n } else await sig.connect();\n return sig;\n }\n\n private makeLobby(sig: SignalingClient, welcome: WelcomeMsg, state: LobbyState, direct?: { links: LinkFactory }, rateLimit?: RateLimit): Lobby {\n const lobby = new Lobby({\n signaling: sig,\n state,\n ice: welcome.ice ?? [],\n links: direct?.links ?? this.linkFactory,\n direct: !!direct,\n clock: this.clock,\n wall: this.wall,\n hashEveryTicks: this.options.hashEveryTicks,\n relayOnly: state.relayOnly ?? this.options.relayOnly,\n displayName: this.options.displayName,\n voice: { codec: this.voiceCodec, createSource: this.createMic, createSink: this.createSink },\n readyTimeoutMs: this.options.readyTimeoutMs,\n peerTimeoutMs: this.options.peerTimeoutMs,\n rateLimit,\n });\n lobby.errorSink = (e) => this.emit(\"error\", e);\n if (this.blueprint) guardLobby(lobby, this.blueprint);\n if (this.storage && !direct) {\n liveSeats.add(lobby);\n this.saveSeat(lobby.code, lobby.resumeToken);\n lobby.seatSink = (token) => this.saveSeat(lobby.code, token);\n lobby.on(\"sessionEnded\", () => {\n liveSeats.delete(lobby);\n this.clearSeat(lobby.code);\n });\n }\n return lobby;\n }\n\n /** With a blueprint (`fromConfig`) every option defaults from `lobby.*`; explicit options win. */\n async host(opts?: Partial<HostOptions>): Promise<Lobby> {\n const h = { name: \"Game\", maxPlayers: 4, ...this.blueprintHostDefaults(), ...definedOnly(opts) } as HostOptions;\n const { sig, welcome } = await this.connect();\n try {\n const state = await sig.request<LobbyState>(\n {\n t: \"create\",\n name: h.name,\n maxPlayers: h.maxPlayers,\n visibility: h.visibility ?? \"unlisted\",\n mode: this.options.mode,\n tickRate: this.options.tickRate,\n lateJoin: h.lateJoin ?? true,\n meta: h.meta ?? {},\n ...(this.options.relayOnly ? { relayOnly: true } : {}),\n },\n \"lobby\",\n );\n return this.makeLobby(sig, welcome, state, undefined, h.rateLimit);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async join(code: string): Promise<Lobby> {\n const norm = normalizeCode(code);\n if (!norm) throw new OneLobbyError(\"bad_code\", `\"${code}\" is not a valid join code`);\n const seat = this.loadSeat();\n if (seat && seat.code === norm && !seatInUse(seat.token)) {\n // Same tab, same lobby (a page reload): take the old seat back instead of joining as a new player.\n try {\n return await this.rejoin(seat.token);\n } catch (e) {\n this.clearSeat();\n if (!(e instanceof OneLobbyError && e.code === \"invalid_token\")) throw e;\n }\n }\n const { sig, welcome } = await this.connect();\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", code: norm }, \"lobby\");\n return this.makeLobby(sig, welcome, state);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Join a direct-IP host (docs/design/direct-ip.md): a join code from the host's screen (`6B01-RGFE-P2B`, any case, spaces\n * or hyphens) or, advanced, an address (`ip`, `ip:port`, `[v6]:port`). No lobby server is involved: the code is\n * the host's address. A mistyped code fails at once with `bad_code`; nobody listening is `direct_unreachable`.\n * The address is also given to ICE as the host's UDP candidate. When the host leaves, `sessionEnded(\"host_left\")`.\n */\n async joinDirect(codeOrAddress: string, opts: JoinDirectOptions = {}): Promise<Lobby> {\n const t = this.directTarget(codeOrAddress, opts);\n const { sig, welcome } = await this.connectDirect(t, opts);\n try {\n const state = await sig.request<LobbyState>({ t: \"join\", ...(opts.password ? { password: opts.password } : {}) }, \"lobby\");\n const lobby = this.makeLobby(sig, this.withStun(welcome, t, opts), state, { links: t.links });\n // a joiner's joinCode is what it joined with (normalized), so a HUD can show it on both sides\n lobby.joinCode = t.joinedWith;\n return lobby;\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Take a direct-IP seat back after a drop or a restart: the code (or address) joined before and that lobby's\n * `resumeToken` (save it after every join; it changes on each resume). The host keeps a dropped seat for 60 s and the\n * same peer id comes back. Rejects with `invalid_token` when the seat is gone: offer `joinDirect` then.\n */\n async rejoinDirect(codeOrAddress: string, resumeToken: string, opts: JoinDirectOptions = {}): Promise<Lobby> {\n const t = this.directTarget(codeOrAddress, opts);\n const lobby = await this.resumeAt(resumeToken, t.url, t.nostr ? 0 : opts.connectTimeoutMs ?? 5000, { links: t.links }, t.nostr ? this.nostrFactory(t, opts) : undefined, (w) => this.withStun(w, t, opts));\n lobby.joinCode = t.joinedWith;\n return lobby;\n }\n\n private nostrFactory(t: DirectTarget, opts: JoinDirectOptions): SocketFactory {\n const relays = opts.nostrRelays ?? (this.blueprint ? [...this.blueprint.hosting.nostrRelays] : undefined);\n return () =>\n new NostrSocket({ code: t.nostr!, relays: relays && relays.length > 0 ? relays : undefined, connect: this.nostrConnectFn, clock: this.clock, wall: this.wall });\n }\n\n /** Opens the signaling socket of a direct target: TCP to the host's address, or Nostr relays for a version 1 code. */\n private async connectDirect(t: DirectTarget, opts: JoinDirectOptions): Promise<{ sig: SignalingClient; welcome: WelcomeMsg }> {\n if (!t.nostr) return this.connect(t.url, this.socketFactory, opts.connectTimeoutMs ?? 5000);\n // the socket enforces its own deadlines (10 s to reach a relay, 15 s for the host to answer)\n let sock: NostrSocket | null = null;\n const factory = this.nostrFactory(t, opts);\n try {\n return await this.connect(t.url, (u) => (sock = factory(u) as NostrSocket), 0);\n } catch (e) {\n const why = (sock as NostrSocket | null)?.failure;\n // same code as an IP host that does not answer, so a game handles one \"could not reach the host\" error\n if (e instanceof OneLobbyError && e.code === \"connect_failed\") throw new OneLobbyError(\"direct_unreachable\", why || \"no answer through the Nostr relays\");\n throw e;\n }\n }\n\n /** A Nostr joiner needs STUN; take the host's list (it comes in `welcome`), else the public defaults. */\n private withStun(welcome: WelcomeMsg, t: DirectTarget, opts: JoinDirectOptions): WelcomeMsg {\n if (!t.nostr || (welcome.ice && welcome.ice.length > 0)) return welcome;\n const urls = opts.stun ?? [...DEFAULT_NOSTR_STUN];\n return urls.length > 0 ? { ...welcome, ice: [{ urls }] } : welcome;\n }\n\n private directTarget(codeOrAddress: string, opts: JoinDirectOptions): DirectTarget {\n let input = codeOrAddress.trim();\n if (looksLikeJoinCode(input)) {\n const d = decodeJoinCode(input);\n if (\"error\" in d)\n throw new OneLobbyError(d.error, d.error === \"unknown_version\" ? `\"${input}\" is a join code from a newer version of the game` : `\"${input}\" is not a valid join code: check for a typo`);\n if (\"room\" in d) {\n // version 1: a Nostr room; signaling runs over public relays (spec/nostr-signaling.md), WebRTC uses STUN\n const shown = encodeRoomCodeFor(d.room);\n return { nostr: shown, url: `nostr:${shown}`, joinedWith: shown, links: this.linkFactory };\n }\n input = `${d.ip}:${d.port}`;\n }\n const addr = parseDirectAddress(input, opts.defaultPort ?? DEFAULT_DIRECT_PORT);\n if (!addr) throw new OneLobbyError(\"bad_address\", `\"${codeOrAddress}\" is not a join code or an address (try 6B01-RGFE-P2B or 192.168.1.20:47810)`);\n const inner = this.linkFactory;\n const links: LinkFactory = { create: (o) => inner.create({ ...o, iceServers: [], extraRemoteCandidates: [addr.candidate] }) };\n return { nostr: null, url: addr.url, joinedWith: joinedWith(addr), links };\n }\n\n /** @internal onelobby/direct's Nostr host: the relay seam and the kit's clocks. */\n nostrHostEnv(): { connect: RelayConnect | undefined; clock: Clock; wall: () => number } {\n return { connect: this.nostrConnectFn, clock: this.clock, wall: this.wall };\n }\n\n /** @internal onelobby/direct's host: create the one lobby on the embedded hub, in direct mode. */\n async hostDirectWith(h: HostOptions, hooks: DirectLobbyHooks): Promise<Lobby> {\n const { sig, welcome } = await this.connect(\"onelobby-direct://host/\", hooks.socketFactory);\n try {\n const state = await sig.request<LobbyState>(\n { t: \"create\", name: h.name, maxPlayers: h.maxPlayers, visibility: \"unlisted\", mode: this.options.mode, tickRate: this.options.tickRate,\n lateJoin: h.lateJoin ?? true, meta: h.meta ?? {} },\n \"lobby\",\n );\n return this.makeLobby(sig, welcome, state, { links: hooks.wrapLinks(this.linkFactory) }, h.rateLimit);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n /**\n * Host a direct lobby from this browser tab (or any runtime with WebSocket and WebRTC): no Node process, no port.\n * The hub runs in the page and joiners find it through Nostr relays with `lobby.joinCode` (a dc1 version 1 code,\n * `joinCodeKind` \"nostr\"); they use `kit.joinDirect(code)`. Rejects with `direct_unreachable` when no relay answers\n * within 10 s (a browser has no IP fallback) and `nostr_disabled` when the blueprint has `hosting.nostr` false.\n * Keep the tab visible: hidden tabs are throttled (`lobby.hostHidden`, event `hostHiddenChanged`).\n */\n hostNostr(opts: BrowserHostOptions = {}): Promise<Lobby> {\n return hostNostr(this, opts);\n }\n\n async rejoin(resumeToken: string): Promise<Lobby> {\n return this.resumeAt(resumeToken, this.options.lobbyServer, 0);\n }\n\n private async resumeAt(\n resumeToken: string,\n url: string,\n timeoutMs: number,\n direct?: { links: LinkFactory },\n factory?: SocketFactory,\n tweakWelcome?: (w: WelcomeMsg) => WelcomeMsg,\n ): Promise<Lobby> {\n const sig = await this.open(url, factory ?? this.socketFactory, timeoutMs);\n try {\n // The server answers hello(resumeToken) with welcome then the full lobby state; listen for both up front.\n const lobbyP = sig.expect<LobbyState>(\"lobby\");\n const welcomeP = sig.request<WelcomeMsg & { t: string }>(this.helloMsg(resumeToken), \"welcome\");\n lobbyP.catch(() => {}); // settled below or by close()\n const welcome = tweakWelcome ? tweakWelcome(await welcomeP) : await welcomeP;\n // spec: an unknown or expired token is ignored -> plain welcome (no peerId), and no lobby follows\n if (welcome.peerId === undefined) throw new OneLobbyError(\"invalid_token\", \"resume token unknown or expired\");\n const state = await lobbyP;\n return this.makeLobby(sig, welcome, state, direct);\n } catch (e) {\n sig.close();\n throw e;\n }\n }\n\n async list(opts: ListOptions = {}): Promise<LobbySummary[]> {\n if (this.blueprint && !this.blueprint.hosting.server) throw new OneLobbyError(\"listing_disabled\", \"this game has no lobby server (hosting.server is false)\");\n const { sig } = await this.connect();\n try {\n const res = await sig.request<{ t: string; items: LobbySummary[] }>(\n { t: \"list\", visibility: \"public\", meta: opts.meta ?? {}, limit: Math.min(100, opts.limit ?? 50), ...(opts.cursor ? { cursor: opts.cursor } : {}) },\n \"lobbies\",\n );\n return res.items ?? [];\n } finally {\n sig.close();\n }\n }\n}\n\n/** The join code for an IPv4 address (a host name or IPv6 has none: its `host:port` instead). */\nfunction joinedWith(addr: { host: string; port: number; isIPv6: boolean; display: string }): string {\n if (addr.isIPv6) return addr.display;\n try {\n return encodeJoinCode(addr.host, addr.port);\n } catch {\n return addr.display;\n }\n}\n\nfunction definedOnly<T extends object>(o: T | undefined): Partial<T> {\n const out: Partial<T> = {};\n if (o) for (const [k, v] of Object.entries(o)) if (v !== undefined) (out as Record<string, unknown>)[k] = v;\n return out;\n}\n\n/** Blueprint switches that gate features on the lobby: voice and text chat; chat.voiceMode is the starting voice mode. */\nfunction guardLobby(lobby: Lobby, bp: Blueprint): void {\n lobby.voice.mode = bp.chat.voiceMode;\n if (!bp.chat.voice) {\n const voice = lobby.voice;\n const d = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(voice), \"enabled\");\n if (d?.get && d.set) {\n Object.defineProperty(voice, \"enabled\", {\n configurable: true,\n get: () => d.get!.call(voice) as boolean,\n set: (v: boolean) => {\n if (v) throw new OneLobbyError(\"voice_disabled\", \"this game has voice chat turned off (chat.voice is false)\");\n d.set!.call(voice, v);\n },\n });\n }\n }\n lobby.pause.mode = bp.session.pause;\n if (!bp.chat.text) {\n lobby.chat.send = () => {\n throw new OneLobbyError(\"chat_disabled\", \"this game has text chat turned off (chat.text is false)\");\n };\n }\n}\n"]}
|
package/dist/pause.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type Message } from "./codec.js";
|
|
2
|
+
import { Emitter } from "./emitter.js";
|
|
3
|
+
import type { Hub } from "./hub.js";
|
|
4
|
+
/** The least time between two accepted pause requests from one peer. */
|
|
5
|
+
export declare const PAUSE_MIN_REQUEST_GAP_MS = 500;
|
|
6
|
+
/**
|
|
7
|
+
* The shared pause: `paused` is on while someone holds the match; `byPeer` is the peer that paused (while paused) or
|
|
8
|
+
* that last resumed it; `pauserLeft` says the pauser left and the host now holds the pause (`byPeer` is then the host).
|
|
9
|
+
*/
|
|
10
|
+
export interface PauseState {
|
|
11
|
+
readonly paused: boolean;
|
|
12
|
+
readonly byPeer: number;
|
|
13
|
+
readonly pauserLeft: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** Why a request was refused: `not_in_game`, `rate_limited` or `not_allowed` (the `ErrorCodes` constants). */
|
|
16
|
+
export type PauseRefusal = "not_in_game" | "rate_limited" | "not_allowed" | "unknown";
|
|
17
|
+
export type PauseEvents = {
|
|
18
|
+
/** The state changed (paused, resumed, pauser left, cleared). Fires on every peer. */
|
|
19
|
+
changed: [PauseState];
|
|
20
|
+
/** A request of this peer was refused; `code` is a `PauseRefusal`. */
|
|
21
|
+
refused: [code: PauseRefusal];
|
|
22
|
+
};
|
|
23
|
+
/** `session.pause` of a blueprint: "off" makes `request` throw `pause_disabled`, "host" makes the default policy host-only. */
|
|
24
|
+
export type PauseMode = "off" | "host" | "anyone";
|
|
25
|
+
export interface PauseDeps {
|
|
26
|
+
/** The lobby's phase now ("lobby" | "in_game"). */
|
|
27
|
+
phase: () => string;
|
|
28
|
+
/** Lockstep: stop (true) or resume (false) the host's frame emission. */
|
|
29
|
+
hold: (held: boolean) => void;
|
|
30
|
+
/** Whether `peerId` is still in the roster. */
|
|
31
|
+
hasPeer: (peerId: number) => boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Shared co-op pause (docs/recipes/pause.md): any player asks the host to pause or resume the match for everyone.
|
|
35
|
+
* The host validates (phase, rate, policy), keeps the state and broadcasts it over the reliable channel. OneLobby moves
|
|
36
|
+
* the state and, in lockstep, stops the host's frames; scaling time, menus and banners stay in the game. It is outside
|
|
37
|
+
* the sim: never put it in a lockstep command or hash.
|
|
38
|
+
*/
|
|
39
|
+
export declare class Pause extends Emitter<PauseEvents> {
|
|
40
|
+
private hub;
|
|
41
|
+
private deps;
|
|
42
|
+
/** Set by a blueprint (`session.pause`); the default is "anyone". */
|
|
43
|
+
mode: PauseMode;
|
|
44
|
+
/**
|
|
45
|
+
* Host: replaces the default policy. Called with (requesting peer, true to pause / false to resume); return true to
|
|
46
|
+
* allow. Default: anyone pauses, the pauser or the host resumes (host only in "host" mode). Phase and rate run first.
|
|
47
|
+
*/
|
|
48
|
+
policy: ((peerId: number, paused: boolean) => boolean) | null;
|
|
49
|
+
private current;
|
|
50
|
+
private lastAccepted;
|
|
51
|
+
constructor(hub: Hub, deps: PauseDeps);
|
|
52
|
+
/** The current shared state (the same on every peer once the host's broadcast has arrived). */
|
|
53
|
+
get state(): PauseState;
|
|
54
|
+
/**
|
|
55
|
+
* True when the local peer may resume now under the default policy: the pauser or the host (in "host" mode, the
|
|
56
|
+
* host). A custom `policy` lives on the host, so on a joiner this is a hint.
|
|
57
|
+
*/
|
|
58
|
+
get canResume(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Ask to pause (true) or resume (false) the match for everyone. Throws `pause_disabled` when the blueprint says
|
|
61
|
+
* `session.pause` is "off" (the default; set it to "anyone" or "host" in onelobby.json). Works only while the lobby
|
|
62
|
+
* is in game (phase "in_game", after `start()`): before that the host refuses with `not_in_game`. The result arrives
|
|
63
|
+
* as `changed` or `refused`; asking for the state that already holds does nothing.
|
|
64
|
+
*/
|
|
65
|
+
request(paused: boolean): void;
|
|
66
|
+
/**
|
|
67
|
+
* Host only: unpause at once, skipping the policy and the rate limit. Call it before a restart or game over so the
|
|
68
|
+
* next round does not start frozen. Does nothing when not paused.
|
|
69
|
+
*/
|
|
70
|
+
clear(): void;
|
|
71
|
+
handle(from: number, msg: Message): void;
|
|
72
|
+
/** Host: a late joiner's link is up; it gets the state when there is one to tell. */
|
|
73
|
+
sendState(peerId: number): void;
|
|
74
|
+
/** A peer is gone: the host takes over the pause its leaver held. */
|
|
75
|
+
onPeerLeft(peerId: number): void;
|
|
76
|
+
/** This peer just became the host (migration): it keeps the state as is, and takes over a pause whose holder is gone. */
|
|
77
|
+
onBecameHost(): void;
|
|
78
|
+
/** The lobby went back to the lobby phase: nothing stays paused. */
|
|
79
|
+
onLobbyPhase(): void;
|
|
80
|
+
private defaultAllows;
|
|
81
|
+
private hostHandle;
|
|
82
|
+
private refuse;
|
|
83
|
+
private stateMessage;
|
|
84
|
+
private set;
|
|
85
|
+
}
|