onelobby 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +77 -2
- package/dist/chat.d.ts +60 -0
- package/dist/chat.js +163 -0
- package/dist/chat.js.map +1 -0
- package/dist/chunking.d.ts +17 -0
- package/dist/chunking.js +59 -0
- package/dist/chunking.js.map +1 -0
- package/dist/clock.d.ts +8 -0
- package/dist/clock.js +39 -0
- package/dist/clock.js.map +1 -0
- package/dist/codec.d.ts +141 -0
- package/dist/codec.js +308 -0
- package/dist/codec.js.map +1 -0
- package/dist/crockford.d.ts +14 -0
- package/dist/crockford.js +55 -0
- package/dist/crockford.js.map +1 -0
- package/dist/deflate.d.ts +4 -0
- package/dist/deflate.js +32 -0
- package/dist/deflate.js.map +1 -0
- package/dist/deprecated.d.ts +18 -0
- package/dist/deprecated.js +11 -0
- package/dist/deprecated.js.map +1 -0
- package/dist/direct/hub-host.d.ts +82 -0
- package/dist/direct/hub-host.js +393 -0
- package/dist/direct/hub-host.js.map +1 -0
- package/dist/direct/hub.d.ts +178 -0
- package/dist/direct/hub.js +862 -0
- package/dist/direct/hub.js.map +1 -0
- package/dist/direct/index.d.ts +57 -0
- package/dist/direct/index.js +71 -0
- package/dist/direct/index.js.map +1 -0
- package/dist/direct/portmap.d.ts +84 -0
- package/dist/direct/portmap.js +465 -0
- package/dist/direct/portmap.js.map +1 -0
- package/dist/direct-address.d.ts +20 -0
- package/dist/direct-address.js +61 -0
- package/dist/direct-address.js.map +1 -0
- package/dist/emitter.d.ts +9 -0
- package/dist/emitter.js +36 -0
- package/dist/emitter.js.map +1 -0
- package/dist/friendly.d.ts +20 -0
- package/dist/friendly.js +120 -0
- package/dist/friendly.js.map +1 -0
- package/dist/global.d.ts +8 -0
- package/dist/global.js +9 -0
- package/dist/global.js.map +1 -0
- package/dist/gridpaint.d.ts +27 -0
- package/dist/gridpaint.js +106 -0
- package/dist/gridpaint.js.map +1 -0
- package/dist/hub.d.ts +25 -0
- package/dist/hub.js +2 -0
- package/dist/hub.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/internals.d.ts +28 -0
- package/dist/internals.js +24 -0
- package/dist/internals.js.map +1 -0
- package/dist/joincode.d.ts +21 -0
- package/dist/joincode.js +84 -0
- package/dist/joincode.js.map +1 -0
- package/dist/lobby.d.ts +203 -0
- package/dist/lobby.js +840 -0
- package/dist/lobby.js.map +1 -0
- package/dist/lockstep.d.ts +132 -0
- package/dist/lockstep.js +471 -0
- package/dist/lockstep.js.map +1 -0
- package/dist/migration.d.ts +17 -0
- package/dist/migration.js +34 -0
- package/dist/migration.js.map +1 -0
- package/dist/onelobby.bundle.mjs +140 -0
- package/dist/onelobby.bundle.mjs.map +7 -0
- package/dist/onelobby.d.ts +151 -0
- package/dist/onelobby.global.js +3897 -0
- package/dist/onelobby.global.js.map +7 -0
- package/dist/onelobby.global.min.js +140 -0
- package/dist/onelobby.global.min.js.map +7 -0
- package/dist/onelobby.js +337 -0
- package/dist/onelobby.js.map +1 -0
- package/dist/signaling.d.ts +94 -0
- package/dist/signaling.js +134 -0
- package/dist/signaling.js.map +1 -0
- package/dist/socket.d.ts +63 -0
- package/dist/socket.js +142 -0
- package/dist/socket.js.map +1 -0
- package/dist/testing.d.ts +135 -0
- package/dist/testing.js +438 -0
- package/dist/testing.js.map +1 -0
- package/dist/transport.d.ts +80 -0
- package/dist/transport.js +7 -0
- package/dist/transport.js.map +1 -0
- package/dist/ui/controller.d.ts +137 -0
- package/dist/ui/controller.js +337 -0
- package/dist/ui/controller.js.map +1 -0
- package/dist/ui/element.d.ts +22 -0
- package/dist/ui/element.js +284 -0
- package/dist/ui/element.js.map +1 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/server-url.d.ts +9 -0
- package/dist/ui/server-url.js +39 -0
- package/dist/ui/server-url.js.map +1 -0
- package/dist/ui/styles.d.ts +2 -0
- package/dist/ui/styles.js +70 -0
- package/dist/ui/styles.js.map +1 -0
- package/dist/voice-browser.d.ts +19 -0
- package/dist/voice-browser.js +151 -0
- package/dist/voice-browser.js.map +1 -0
- package/dist/voice-core.d.ts +98 -0
- package/dist/voice-core.js +233 -0
- package/dist/voice-core.js.map +1 -0
- package/dist/voice-types.d.ts +35 -0
- package/dist/voice-types.js +3 -0
- package/dist/voice-types.js.map +1 -0
- package/dist/voice.d.ts +68 -0
- package/dist/voice.js +223 -0
- package/dist/voice.js.map +1 -0
- package/dist/webrtc.d.ts +29 -0
- package/dist/webrtc.js +169 -0
- package/dist/webrtc.js.map +1 -0
- package/package.json +93 -4
package/dist/joincode.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { CROCKFORD_ALPHABET } from "./crockford.js";
|
|
2
|
+
/**
|
|
3
|
+
* `dc1` join codes (docs/direct-ip.md s.2a, spec/reference/joincode.mjs): a direct-IP host's IPv4 address and port in
|
|
4
|
+
* 11 Crockford characters, shown `6B01-RGFE-P2B`. The code is the address in disguise, so decoding asks nobody.
|
|
5
|
+
* Bits, most significant first: version:2 (0 = IPv4) | ipv4:32 | port:16 | crc:5 (CRC-5, polynomial 0x05, over the 50
|
|
6
|
+
* bits before it). One mistyped character always fails the check.
|
|
7
|
+
*/
|
|
8
|
+
export const JOIN_CODE_LENGTH = 11;
|
|
9
|
+
function crc5(value, bits) {
|
|
10
|
+
let crc = 0;
|
|
11
|
+
for (let i = bits - 1; i >= 0; i--) {
|
|
12
|
+
const bit = Number((value >> BigInt(i)) & 1n);
|
|
13
|
+
const top = (crc >> 4) & 1;
|
|
14
|
+
crc = (crc << 1) & 0x1f;
|
|
15
|
+
if (top ^ bit)
|
|
16
|
+
crc ^= 0x05;
|
|
17
|
+
}
|
|
18
|
+
return crc;
|
|
19
|
+
}
|
|
20
|
+
function parseIPv4(ip) {
|
|
21
|
+
const parts = ip.split(".");
|
|
22
|
+
if (parts.length !== 4)
|
|
23
|
+
return null;
|
|
24
|
+
let n = 0;
|
|
25
|
+
for (const p of parts) {
|
|
26
|
+
if (!/^\d{1,3}$/.test(p) || Number(p) > 255)
|
|
27
|
+
return null;
|
|
28
|
+
n = n * 256 + Number(p);
|
|
29
|
+
}
|
|
30
|
+
return n;
|
|
31
|
+
}
|
|
32
|
+
/** Display form (`6B01-RGFE-P2B`) for an IPv4 address and port 1-65535. Throws on anything else. */
|
|
33
|
+
export function encodeJoinCode(ipv4, port) {
|
|
34
|
+
const ip = parseIPv4(ipv4);
|
|
35
|
+
if (ip === null)
|
|
36
|
+
throw new Error(`not an IPv4 address: ${ipv4}`);
|
|
37
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
38
|
+
throw new Error(`bad port ${port}`);
|
|
39
|
+
const data = (BigInt(ip) << 16n) | BigInt(port); // version 0
|
|
40
|
+
let v = (data << 5n) | BigInt(crc5(data, 50));
|
|
41
|
+
let s = "";
|
|
42
|
+
for (let i = 0; i < JOIN_CODE_LENGTH; i++) {
|
|
43
|
+
s = CROCKFORD_ALPHABET[Number(v & 31n)] + s;
|
|
44
|
+
v >>= 5n;
|
|
45
|
+
}
|
|
46
|
+
return `${s.slice(0, 4)}-${s.slice(4, 8)}-${s.slice(8)}`;
|
|
47
|
+
}
|
|
48
|
+
/** Upper case, spaces and hyphens dropped, i/l -> 1, o -> 0; null unless 11 alphabet characters. */
|
|
49
|
+
export function normalizeJoinCode(input) {
|
|
50
|
+
const s = input
|
|
51
|
+
.replace(/[ \-]/g, "")
|
|
52
|
+
.replace(/[a-z]/g, (c) => c.toUpperCase())
|
|
53
|
+
.replace(/[IL]/g, "1")
|
|
54
|
+
.replace(/O/g, "0");
|
|
55
|
+
if (s.length !== JOIN_CODE_LENGTH)
|
|
56
|
+
return null;
|
|
57
|
+
for (const c of s)
|
|
58
|
+
if (CROCKFORD_ALPHABET.indexOf(c) < 0)
|
|
59
|
+
return null;
|
|
60
|
+
return s;
|
|
61
|
+
}
|
|
62
|
+
export function decodeJoinCode(input) {
|
|
63
|
+
const s = normalizeJoinCode(input);
|
|
64
|
+
if (!s)
|
|
65
|
+
return { error: "bad_code" };
|
|
66
|
+
let v = 0n;
|
|
67
|
+
for (const c of s)
|
|
68
|
+
v = (v << 5n) | BigInt(CROCKFORD_ALPHABET.indexOf(c));
|
|
69
|
+
const data = v >> 5n;
|
|
70
|
+
if (crc5(data, 50) !== Number(v & 31n))
|
|
71
|
+
return { error: "bad_code" };
|
|
72
|
+
if (data >> 48n !== 0n)
|
|
73
|
+
return { error: "unknown_version" };
|
|
74
|
+
const port = Number(data & 0xffffn);
|
|
75
|
+
if (port === 0)
|
|
76
|
+
return { error: "bad_code" };
|
|
77
|
+
const a = Number((data >> 16n) & 0xffffffffn);
|
|
78
|
+
return { ip: [a >>> 24, (a >>> 16) & 255, (a >>> 8) & 255, a & 255].join("."), port };
|
|
79
|
+
}
|
|
80
|
+
/** Shaped like a join code (no dots or colons, 11 alphabet characters): a failed decode is `bad_code`, not a host name. */
|
|
81
|
+
export function looksLikeJoinCode(input) {
|
|
82
|
+
return !/[.:]/.test(input) && normalizeJoinCode(input) !== null;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=joincode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"joincode.js","sourceRoot":"","sources":["../src/joincode.ts"],"names":[],"mappings":"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","sourcesContent":["import { CROCKFORD_ALPHABET } from \"./crockford.js\";\n\n/**\n * `dc1` join codes (docs/direct-ip.md s.2a, spec/reference/joincode.mjs): a direct-IP host's IPv4 address and port in\n * 11 Crockford characters, shown `6B01-RGFE-P2B`. The code is the address in disguise, so decoding asks nobody.\n * Bits, most significant first: version:2 (0 = IPv4) | ipv4:32 | port:16 | crc:5 (CRC-5, polynomial 0x05, over the 50\n * bits before it). One mistyped character always fails the check.\n */\nexport const JOIN_CODE_LENGTH = 11;\n\nexport type JoinCodeError = \"bad_code\" | \"unknown_version\";\nexport type DecodedJoinCode = { ip: string; port: number } | { error: JoinCodeError };\n\nfunction crc5(value: bigint, bits: number): number {\n let crc = 0;\n for (let i = bits - 1; i >= 0; i--) {\n const bit = Number((value >> BigInt(i)) & 1n);\n const top = (crc >> 4) & 1;\n crc = (crc << 1) & 0x1f;\n if (top ^ bit) crc ^= 0x05;\n }\n return crc;\n}\n\nfunction parseIPv4(ip: string): number | null {\n const parts = ip.split(\".\");\n if (parts.length !== 4) return null;\n let n = 0;\n for (const p of parts) {\n if (!/^\\d{1,3}$/.test(p) || Number(p) > 255) return null;\n n = n * 256 + Number(p);\n }\n return n;\n}\n\n/** Display form (`6B01-RGFE-P2B`) for an IPv4 address and port 1-65535. Throws on anything else. */\nexport function encodeJoinCode(ipv4: string, port: number): string {\n const ip = parseIPv4(ipv4);\n if (ip === null) throw new Error(`not an IPv4 address: ${ipv4}`);\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`bad port ${port}`);\n const data = (BigInt(ip) << 16n) | BigInt(port); // version 0\n let v = (data << 5n) | BigInt(crc5(data, 50));\n let s = \"\";\n for (let i = 0; i < JOIN_CODE_LENGTH; i++) {\n s = CROCKFORD_ALPHABET[Number(v & 31n)] + s;\n v >>= 5n;\n }\n return `${s.slice(0, 4)}-${s.slice(4, 8)}-${s.slice(8)}`;\n}\n\n/** Upper case, spaces and hyphens dropped, i/l -> 1, o -> 0; null unless 11 alphabet characters. */\nexport function normalizeJoinCode(input: string): string | null {\n const s = input\n .replace(/[ \\-]/g, \"\")\n .replace(/[a-z]/g, (c) => c.toUpperCase())\n .replace(/[IL]/g, \"1\")\n .replace(/O/g, \"0\");\n if (s.length !== JOIN_CODE_LENGTH) return null;\n for (const c of s) if (CROCKFORD_ALPHABET.indexOf(c) < 0) return null;\n return s;\n}\n\nexport function decodeJoinCode(input: string): DecodedJoinCode {\n const s = normalizeJoinCode(input);\n if (!s) return { error: \"bad_code\" };\n let v = 0n;\n for (const c of s) v = (v << 5n) | BigInt(CROCKFORD_ALPHABET.indexOf(c));\n const data = v >> 5n;\n if (crc5(data, 50) !== Number(v & 31n)) return { error: \"bad_code\" };\n if (data >> 48n !== 0n) return { error: \"unknown_version\" };\n const port = Number(data & 0xffffn);\n if (port === 0) return { error: \"bad_code\" };\n const a = Number((data >> 16n) & 0xffffffffn);\n return { ip: [a >>> 24, (a >>> 16) & 255, (a >>> 8) & 255, a & 255].join(\".\"), port };\n}\n\n/** Shaped like a join code (no dots or colons, 11 alphabet characters): a failed decode is `bad_code`, not a host name. */\nexport function looksLikeJoinCode(input: string): boolean {\n return !/[.:]/.test(input) && normalizeJoinCode(input) !== null;\n}\n"]}
|
package/dist/lobby.d.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { Chat } from "./chat.js";
|
|
2
|
+
import type { Clock } from "./clock.js";
|
|
3
|
+
import { PROTO } from "./codec.js";
|
|
4
|
+
import { Emitter } from "./emitter.js";
|
|
5
|
+
import { Lockstep } from "./lockstep.js";
|
|
6
|
+
import { type LobbyState, type PeerInfo, type SignalingClient } from "./signaling.js";
|
|
7
|
+
import type { ChannelName, IceServer, LinkFactory, PeerLink } from "./transport.js";
|
|
8
|
+
import { Voice, type VoiceDeps } from "./voice.js";
|
|
9
|
+
import { type LobbySocket, type LobbySocketOptions } from "./socket.js";
|
|
10
|
+
export type Phase = "lobby" | "in_game";
|
|
11
|
+
export type Peer = PeerInfo;
|
|
12
|
+
export type LobbyEvents = {
|
|
13
|
+
peerJoined: [Peer];
|
|
14
|
+
/** The peer's data channels are open and the handshake is done: reliable sends reach it now (earlier ones were queued and flushed). */
|
|
15
|
+
peerReady: [Peer];
|
|
16
|
+
/** Joiner: the link to the host is open (the host itself is ready from the start). Also available as `lobby.ready`. */
|
|
17
|
+
ready: [];
|
|
18
|
+
peerLeft: [Peer, reason: string];
|
|
19
|
+
joinRequested: [Peer];
|
|
20
|
+
hostChanged: [hostId: number];
|
|
21
|
+
phase: [Phase];
|
|
22
|
+
message: [from: number, bytes: Uint8Array, channel: ChannelName];
|
|
23
|
+
sessionEnded: [reason: string];
|
|
24
|
+
/** The signaling server went away. The game continues on the data channels (s.9). */
|
|
25
|
+
serverLost: [];
|
|
26
|
+
metaChanged: [Record<string, string>];
|
|
27
|
+
};
|
|
28
|
+
export interface LobbyConfig {
|
|
29
|
+
signaling: SignalingClient;
|
|
30
|
+
state: LobbyState;
|
|
31
|
+
ice: IceServer[];
|
|
32
|
+
links: LinkFactory;
|
|
33
|
+
clock: Clock;
|
|
34
|
+
wall: () => number;
|
|
35
|
+
hashEveryTicks: number;
|
|
36
|
+
relayOnly: boolean;
|
|
37
|
+
voice: VoiceDeps;
|
|
38
|
+
displayName: string;
|
|
39
|
+
/**
|
|
40
|
+
* Joiner: give up on reaching the host after this many ms (0 or absent: wait forever). When the ICE list has a
|
|
41
|
+
* TURN server and the lobby is not relay-only already, the second half of the budget is a relay-only retry.
|
|
42
|
+
* On failure `ready` rejects with OneLobbyError("ice_failed"), the error is reported and the lobby is left.
|
|
43
|
+
*/
|
|
44
|
+
readyTimeoutMs?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Direct IP (docs/direct-ip.md): the hub lives in the host, so there is no migration. A joiner that loses the host
|
|
47
|
+
* (link, peer_left, a host_changed from the hub as it shuts down, or the signaling socket) ends with "host_left".
|
|
48
|
+
*/
|
|
49
|
+
direct?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Treat a peer as gone after this many ms without hearing anything from it (0 or absent: off, and a silent peer
|
|
52
|
+
* surfaces only when its link or signaling socket times out, about 30 s). Joiners ping the host every 2 s and the
|
|
53
|
+
* host answers, so the host drops a silent joiner (`peerLeft`, "timeout") and a joiner handles a silent host as
|
|
54
|
+
* lost (a direct lobby ends with "host_left"). Values under 6000 are raised to 6000.
|
|
55
|
+
*/
|
|
56
|
+
peerTimeoutMs?: number;
|
|
57
|
+
/** Host: per-peer cap on incoming raw messages; the excess is dropped before `message` fires or is relayed. */
|
|
58
|
+
rateLimit?: RateLimit;
|
|
59
|
+
}
|
|
60
|
+
/** At most `messages` raw messages per peer in any `perMs` window (a fixed window, reset every `perMs`). */
|
|
61
|
+
export interface RateLimit {
|
|
62
|
+
messages: number;
|
|
63
|
+
perMs: number;
|
|
64
|
+
}
|
|
65
|
+
/** The smallest `peerTimeoutMs` honoured: three missed pings. */
|
|
66
|
+
export declare const MIN_PEER_TIMEOUT_MS = 6000;
|
|
67
|
+
/** The name shown for a peer that chose none: "Player N", N = its seat (peer id, join order). */
|
|
68
|
+
export declare function seatName(p: PeerInfo): PeerInfo;
|
|
69
|
+
/** One joined lobby: roster, star of peer links, and the lockstep/chat/voice engines riding on them. */
|
|
70
|
+
export declare class Lobby extends Emitter<LobbyEvents> {
|
|
71
|
+
readonly code: string;
|
|
72
|
+
readonly mode: "lockstep" | "raw";
|
|
73
|
+
readonly tickRate: number;
|
|
74
|
+
readonly lockstep: Lockstep;
|
|
75
|
+
readonly chat: Chat;
|
|
76
|
+
readonly voice: Voice;
|
|
77
|
+
localPeerId: number;
|
|
78
|
+
hostPeerId: number;
|
|
79
|
+
phase: Phase;
|
|
80
|
+
meta: Record<string, string>;
|
|
81
|
+
resumeToken: string;
|
|
82
|
+
/** Last measured round trip to the host in ms (joiners), or 0. */
|
|
83
|
+
rtt: number;
|
|
84
|
+
/**
|
|
85
|
+
* Direct-IP host only (onelobby/direct): the code friends paste to join, e.g. "6B01-RGFE-P2B" (docs/direct-ip.md
|
|
86
|
+
* s.2a). Show it with joinCodeKind. Empty when the machine has no IPv4 address, and on joiners.
|
|
87
|
+
*/
|
|
88
|
+
joinCode: string;
|
|
89
|
+
/** "internet": the router forwarded the port and the code carries its public address; "same_network": the LAN address. */
|
|
90
|
+
joinCodeKind: "internet" | "same_network" | "none";
|
|
91
|
+
/** Direct-IP host only: this machine's addresses with the port, for an "Advanced" fold-out. */
|
|
92
|
+
directAddresses: string[];
|
|
93
|
+
/** Direct-IP host only: how the router port-open went ("UPnP", "no UPnP gateway answered; ...", "off"). */
|
|
94
|
+
portMapping: string;
|
|
95
|
+
private signaling;
|
|
96
|
+
private cfg;
|
|
97
|
+
private roster;
|
|
98
|
+
private links;
|
|
99
|
+
private connected;
|
|
100
|
+
private hiSentTo;
|
|
101
|
+
private epoch;
|
|
102
|
+
private ended;
|
|
103
|
+
private serverUp;
|
|
104
|
+
private cancelWatchdog;
|
|
105
|
+
private cancelPing;
|
|
106
|
+
private pingN;
|
|
107
|
+
private chunkId;
|
|
108
|
+
private reassemblers;
|
|
109
|
+
private hub;
|
|
110
|
+
private outbox;
|
|
111
|
+
private markReady;
|
|
112
|
+
private failReady;
|
|
113
|
+
private isReady;
|
|
114
|
+
private cancelReadyTimer;
|
|
115
|
+
private relayRetry;
|
|
116
|
+
/** clock.now() of the last frame from each peer (peerTimeoutMs). */
|
|
117
|
+
private lastHeard;
|
|
118
|
+
/** Host: per-peer fixed window for rateLimit. */
|
|
119
|
+
private rate;
|
|
120
|
+
/**
|
|
121
|
+
* Resolves when this peer can exchange data: immediately for the host, once the link to the host is open for a
|
|
122
|
+
* joiner. Rejects with OneLobbyError("ice_failed") when the joiner cannot reach the host in `readyTimeoutMs`
|
|
123
|
+
* (or with "closed"/"kicked" when the lobby ends first).
|
|
124
|
+
*/
|
|
125
|
+
readonly ready: Promise<void>;
|
|
126
|
+
constructor(cfg: LobbyConfig);
|
|
127
|
+
get isHost(): boolean;
|
|
128
|
+
get peers(): Peer[];
|
|
129
|
+
peer(peerId: number): Peer | undefined;
|
|
130
|
+
/** Peers whose link is open and handshake done (host: the ready joiners; joiner: the host once ready). */
|
|
131
|
+
get connectedPeers(): number[];
|
|
132
|
+
/**
|
|
133
|
+
* Joiner: the link to the host as a WebSocket-shaped object (text by default), so WebSocket client code runs
|
|
134
|
+
* unchanged. It opens when `ready` resolves and closes when the session ends (`host_left` gives code 1001).
|
|
135
|
+
*/
|
|
136
|
+
asWebSocket(opts?: LobbySocketOptions): LobbySocket;
|
|
137
|
+
/** Host: `handler(conn)` once per joiner (a WebSocket-shaped connection), the server-side mirror of asWebSocket(). */
|
|
138
|
+
onConnection(handler: (conn: LobbySocket) => void, opts?: LobbySocketOptions): () => void;
|
|
139
|
+
set snapshotChunkSize(n: number);
|
|
140
|
+
/** Current migration epoch (0 until the first host change). */
|
|
141
|
+
get hostEpoch(): number;
|
|
142
|
+
/** @internal Host role: create the link to `peerId` and start negotiating (adapters and tests). */
|
|
143
|
+
_offer(peerId: number): PeerLink;
|
|
144
|
+
/** @internal Joiner role: create the answering link to `peerId` (adapters and tests). */
|
|
145
|
+
_answer(peerId: number): PeerLink;
|
|
146
|
+
/** @internal Called by OneLobby right after construction: the host waits for joiners, a joiner waits for the host's offer. */
|
|
147
|
+
_begin(): void;
|
|
148
|
+
start(): Promise<void>;
|
|
149
|
+
setMeta(meta: Record<string, string>): Promise<void>;
|
|
150
|
+
kick(peerId: number): Promise<void>;
|
|
151
|
+
approve(peerId: number, ok: boolean): Promise<void>;
|
|
152
|
+
leave(): Promise<void>;
|
|
153
|
+
/** raw mode (and any extra game data): to one peer. */
|
|
154
|
+
send(peerId: number, bytes: Uint8Array, opts?: {
|
|
155
|
+
channel?: "reliable" | "fast";
|
|
156
|
+
}): void;
|
|
157
|
+
/** To everyone else in the lobby. */
|
|
158
|
+
broadcast(bytes: Uint8Array, opts?: {
|
|
159
|
+
channel?: "reliable" | "fast";
|
|
160
|
+
}): void;
|
|
161
|
+
private channelFor;
|
|
162
|
+
private sendTo;
|
|
163
|
+
private connectedPeerIds;
|
|
164
|
+
private reportError;
|
|
165
|
+
/** @internal set by OneLobby so lobby errors reach `kit.on("error")`. */
|
|
166
|
+
errorSink: ((e: Error) => void) | null;
|
|
167
|
+
/** @internal set by OneLobby: the server issued a new resume token (rememberSeat). */
|
|
168
|
+
seatSink: ((token: string) => void) | null;
|
|
169
|
+
private armReadyTimer;
|
|
170
|
+
/** The direct attempt stalled: dial the host ourselves through the TURN relay (a host answers any offer). */
|
|
171
|
+
private retryRelayed;
|
|
172
|
+
private giveUpConnecting;
|
|
173
|
+
private makeLink;
|
|
174
|
+
/** Host side: offer to a joiner. */
|
|
175
|
+
private offerTo;
|
|
176
|
+
private onLinkOpen;
|
|
177
|
+
private sendHi;
|
|
178
|
+
private onLinkClosed;
|
|
179
|
+
private onData;
|
|
180
|
+
private onHi;
|
|
181
|
+
private flush;
|
|
182
|
+
private onRaw;
|
|
183
|
+
private allowRaw;
|
|
184
|
+
private setHost;
|
|
185
|
+
/** The host is gone (link closed, no frames for 3 s, or the server said so): run the s.9 rule. */
|
|
186
|
+
private handleHostLost;
|
|
187
|
+
private becomeHost;
|
|
188
|
+
private onHostClaim;
|
|
189
|
+
private onSignaling;
|
|
190
|
+
private onSignal;
|
|
191
|
+
private removePeer;
|
|
192
|
+
private dropPeer;
|
|
193
|
+
private setPhase;
|
|
194
|
+
private onServerClosed;
|
|
195
|
+
private watchdog;
|
|
196
|
+
/** peerTimeoutMs: the host drops joiners it has not heard from; a joiner handles a silent host as lost. */
|
|
197
|
+
private dropSilent;
|
|
198
|
+
private pingHost;
|
|
199
|
+
private teardown;
|
|
200
|
+
/** Total in-flight links (diagnostics and tests). */
|
|
201
|
+
get linkCount(): number;
|
|
202
|
+
}
|
|
203
|
+
export { PROTO };
|