onelobby 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/direct/hub-host.js +23 -0
- package/dist/direct/hub-host.js.map +1 -1
- package/dist/direct/hub.js +23 -5
- package/dist/direct/hub.js.map +1 -1
- package/dist/direct/index.d.ts +8 -1
- package/dist/direct/index.js +17 -8
- package/dist/direct/index.js.map +1 -1
- package/dist/direct/portmap.d.ts +12 -1
- package/dist/direct/portmap.js +61 -14
- package/dist/direct/portmap.js.map +1 -1
- package/dist/lobby.d.ts +43 -4
- package/dist/lobby.js +98 -21
- package/dist/lobby.js.map +1 -1
- package/dist/onelobby.bundle.mjs +5 -5
- package/dist/onelobby.bundle.mjs.map +3 -3
- package/dist/onelobby.d.ts +1 -1
- package/dist/onelobby.global.js +186 -32
- package/dist/onelobby.global.js.map +2 -2
- package/dist/onelobby.global.min.js +5 -5
- package/dist/onelobby.global.min.js.map +3 -3
- package/dist/onelobby.js +17 -3
- package/dist/onelobby.js.map +1 -1
- package/dist/signaling.d.ts +5 -1
- package/dist/signaling.js +17 -2
- package/dist/signaling.js.map +1 -1
- package/dist/socket.d.ts +11 -5
- package/dist/socket.js +34 -10
- package/dist/socket.js.map +1 -1
- package/dist/testing.js +13 -3
- package/dist/testing.js.map +1 -1
- package/dist/webrtc.d.ts +12 -0
- package/dist/webrtc.js +30 -1
- package/dist/webrtc.js.map +1 -1
- package/package.json +1 -1
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Test doubles: a manual clock, an in-memory lobby server speaking the lk1 protocol (spec/signaling.md, the parts the\n * client uses) and an in-memory peer network. Exported as `onelobby/testing`.\n */\nimport type { Clock } from \"./clock.js\";\nimport { CROCKFORD_ALPHABET, normalizeCode } from \"./crockford.js\";\nimport type {\n ChannelName,\n LinkFactory,\n LinkOptions,\n PeerLink,\n SignalPayload,\n SocketFactory,\n SocketLike,\n} from \"./transport.js\";\n\n// ---- clock ------------------------------------------------------------------------------------------------------\n\ninterface Timer {\n at: number;\n seq: number;\n fn: () => void;\n every?: number;\n dead?: boolean;\n}\n\n/** Deterministic clock: time only moves through `advance()`. Timers fire in (time, creation) order. */\nexport class ManualClock implements Clock {\n private t = 0;\n private seq = 0;\n private timers: Timer[] = [];\n\n now(): number {\n return this.t;\n }\n\n setInterval(fn: () => void, ms: number): () => void {\n const timer: Timer = { at: this.t + ms, seq: this.seq++, fn, every: Math.max(1, ms) };\n this.timers.push(timer);\n return () => {\n timer.dead = true;\n };\n }\n\n setTimeout(fn: () => void, ms: number): () => void {\n const timer: Timer = { at: this.t + ms, seq: this.seq++, fn };\n this.timers.push(timer);\n return () => {\n timer.dead = true;\n };\n }\n\n /** Advance time, firing due timers one at a time (microtasks flush between them when awaited via `run`). */\n advance(ms: number): void {\n const end = this.t + ms;\n for (;;) {\n this.timers = this.timers.filter((x) => !x.dead);\n let next: Timer | undefined;\n for (const x of this.timers) if (x.at <= end && (!next || x.at < next.at || (x.at === next.at && x.seq < next.seq))) next = x;\n if (!next) break;\n this.t = Math.max(this.t, next.at);\n if (next.every) {\n next.at += next.every;\n next.seq = this.seq++;\n } else {\n next.dead = true;\n }\n next.fn();\n }\n this.t = end;\n }\n\n /** Advance in `step` ms slices, letting promises and queued microtasks settle between slices. */\n async run(ms: number, step = 10): Promise<void> {\n let left = ms;\n await settle();\n while (left > 0) {\n const d = Math.min(step, left);\n this.advance(d);\n left -= d;\n await settle();\n }\n }\n}\n\n/** Let all pending microtasks (and chained promise continuations) run. */\nexport async function settle(rounds = 8): Promise<void> {\n for (let i = 0; i < rounds; i++) await Promise.resolve();\n // A macrotask boundary guarantees the whole microtask queue (delivery chains) has drained.\n await new Promise<void>((r) => ((globalThis as { setImmediate?: (f: () => void) => void }).setImmediate?.(r) ?? setTimeout(r, 0)));\n}\n\n// ---- in-memory peer network ---------------------------------------------------------------------------------------\n\nlet linkSerial = 0;\n\n/**\n * Pairs `PeerLink`s through the signaling path like WebRTC would: the offerer's SDP string names it, the answerer\n * resolves that name, and both open one microtask later. Messages are delivered FIFO, one microtask later.\n */\nexport class MemoryNetwork {\n private offers = new Map<string, MemoryLink>();\n /** Every link ever created (for assertions and fault injection). */\n readonly links: MemoryLink[] = [];\n /** Set to drop all traffic in one direction pair for fault injection: key `${from}->${to}` is not tracked; use link.mute(). */\n readonly factory: LinkFactory = { create: (opts) => new MemoryLink(this, opts) };\n\n /** @internal */\n register(offer: string, link: MemoryLink): void {\n this.offers.set(offer, link);\n this.links.push(link);\n }\n /** @internal */\n lookup(offer: string): MemoryLink | undefined {\n return this.offers.get(offer);\n }\n\n /** Kill every link whose far end is this identity-tagged factory owner (set `tag` via MemoryLink.owner). */\n killOwner(owner: string): void {\n for (const l of this.links) if (l.owner === owner) l.close();\n }\n\n /** Per-owner factory so tests can tell whose link is whose and cut one peer off. */\n factoryFor(owner: string): LinkFactory {\n return {\n create: (opts) => {\n const l = new MemoryLink(this, opts);\n l.owner = owner;\n return l;\n },\n };\n }\n}\n\nexport class MemoryLink implements PeerLink {\n readonly remoteId: number;\n isOpen = false;\n owner = \"\";\n onSignal: PeerLink[\"onSignal\"] = null;\n onOpen: PeerLink[\"onOpen\"] = null;\n onClose: PeerLink[\"onClose\"] = null;\n onMessage: PeerLink[\"onMessage\"] = null;\n onPath: PeerLink[\"onPath\"] = null;\n /** Bytes sent per channel, for assertions. */\n readonly sent: Record<ChannelName, number> = { reliable: 0, fast: 0, voice: 0 };\n /** While true, outgoing messages vanish (a frozen or partitioned peer). */\n blackhole = false;\n private peer: MemoryLink | null = null;\n private closed = false;\n private id = `mem-${++linkSerial}`;\n\n constructor(private net: MemoryNetwork, opts: LinkOptions) {\n this.remoteId = opts.remoteId;\n }\n\n startOffer(): void {\n this.net.register(this.id, this);\n queueMicrotask(() => this.onSignal?.({ sdp: { type: \"offer\", sdp: this.id } }));\n }\n\n handleSignal(s: SignalPayload): void {\n if (s.sdp?.type === \"offer\") {\n const offerer = this.net.lookup(s.sdp.sdp);\n this.net.links.push(this);\n if (!offerer) return;\n this.peer = offerer;\n queueMicrotask(() => this.onSignal?.({ sdp: { type: \"answer\", sdp: this.id } }));\n queueMicrotask(() => offerer.accept(this));\n queueMicrotask(() => this.open());\n }\n }\n\n /** @internal */\n accept(answerer: MemoryLink): void {\n this.peer = answerer;\n this.open();\n }\n\n private open(): void {\n if (this.closed || this.isOpen) return;\n this.isOpen = true;\n this.onOpen?.();\n this.onPath?.(\"direct\");\n }\n\n send(channel: ChannelName, bytes: Uint8Array): void {\n if (!this.isOpen || this.closed) return;\n this.sent[channel] += bytes.length;\n if (this.blackhole) return;\n const peer = this.peer;\n const copy = bytes.slice();\n queueMicrotask(() => {\n if (peer && peer.isOpen && !peer.closed && !this.closed) peer.onMessage?.(channel, copy);\n });\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.isOpen = false;\n const peer = this.peer;\n queueMicrotask(() => {\n this.onClose?.(\"closed\");\n if (peer && !peer.closed) peer.closeFromRemote();\n });\n }\n\n private closeFromRemote(): void {\n if (this.closed) return;\n this.closed = true;\n this.isOpen = false;\n this.onClose?.(\"remote closed\");\n }\n}\n\n// ---- in-memory lobby server ---------------------------------------------------------------------------------------\n\ntype Json = Record<string, unknown>;\n\ninterface MPeer {\n peerId: number;\n name: string;\n joinedAt: number;\n sock: MemorySocket | null;\n token: string;\n}\ninterface MLobby {\n code: string;\n gameId: string;\n netVersion: string;\n name: string;\n hostId: number;\n epoch: number;\n mode: string;\n tickRate: number;\n lateJoin: boolean;\n phase: string;\n meta: Record<string, string>;\n visibility: string;\n maxPlayers: number;\n peers: Map<number, MPeer>;\n next: number;\n kicked: Set<number>;\n reserved: Map<string, MPeer>;\n}\n\n/** Minimal onelobby-server (lk1) for client tests. Not a conformance target: stream A owns the real server. */\nexport class MemoryServer {\n readonly lobbies = new Map<string, MLobby>();\n /** Every socket ever opened (to drop one from a test). */\n readonly sockets: MemorySocket[] = [];\n readonly socketFactory: SocketFactory = () => this.connect();\n private n = 0;\n iceServers = [{ urls: [\"stun:stun.example:3478\"] }];\n /** Log of every client message, for assertions. */\n readonly received: Json[] = [];\n\n connect(): MemorySocket {\n const s = new MemorySocket(this);\n this.sockets.push(s);\n return s;\n }\n\n /** @internal */\n newCode(): string {\n let x = 0x9e3779b1 * (++this.n + 17);\n let s = \"\";\n for (let i = 0; i < 6; i++) {\n x = (Math.imul(x, 1103515245) + 12345) >>> 0;\n s += CROCKFORD_ALPHABET[(x >>> 16) & 31];\n }\n return this.lobbies.has(s) ? this.newCode() : s;\n }\n}\n\nexport class MemorySocket implements SocketLike {\n readyState = 0;\n onopen: SocketLike[\"onopen\"] = null;\n onmessage: SocketLike[\"onmessage\"] = null;\n onclose: SocketLike[\"onclose\"] = null;\n onerror: SocketLike[\"onerror\"] = null;\n\n private hello: Json | null = null;\n private lobby: MLobby | null = null;\n private peer: MPeer | null = null;\n private closed = false;\n\n constructor(private srv: MemoryServer) {\n queueMicrotask(() => {\n this.readyState = 1;\n this.onopen?.();\n });\n }\n\n // ---- socket API\n send(data: string): void {\n if (this.closed) return;\n const m = JSON.parse(data) as Json;\n queueMicrotask(() => this.handle(m));\n }\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.readyState = 3;\n this.leaveLobby(\"timeout\");\n queueMicrotask(() => this.onclose?.());\n }\n\n /** @internal server -> this client */\n push(m: Json): void {\n if (this.closed) return;\n queueMicrotask(() => this.onmessage?.({ data: JSON.stringify(m) }));\n }\n\n private err(code: string, msg = \"\", data?: Json) {\n this.push({ t: \"error\", code, msg, ...(data ? { data } : {}) });\n }\n\n private lobbyMsg(l: MLobby, p: MPeer): Json {\n return {\n t: \"lobby\",\n code: l.code,\n gameId: l.gameId,\n netVersion: l.netVersion,\n hostId: l.hostId,\n you: p.peerId,\n mode: l.mode,\n tickRate: l.tickRate,\n lateJoin: l.lateJoin,\n phase: l.phase,\n meta: l.meta,\n peers: [...l.peers.values()].map((q) => ({ peerId: q.peerId, name: q.name, joinedAt: q.joinedAt })),\n resumeToken: p.token,\n name: l.name,\n maxPlayers: l.maxPlayers,\n visibility: l.visibility,\n };\n }\n\n private broadcast(l: MLobby, m: Json, except?: number) {\n for (const q of l.peers.values()) if (q.peerId !== except) q.sock?.push(m);\n }\n\n private leaveLobby(reason: string) {\n const l = this.lobby;\n const p = this.peer;\n if (!l || !p) return;\n this.lobby = null;\n this.peer = null;\n p.sock = null;\n l.peers.delete(p.peerId);\n if (reason === \"timeout\") l.reserved.set(p.token, p); // slot kept for resume\n if (l.peers.size === 0) {\n this.srv.lobbies.delete(l.code);\n return;\n }\n this.broadcast(l, { t: \"peer_left\", peerId: p.peerId, reason });\n if (l.hostId === p.peerId) {\n l.hostId = Math.min(...l.peers.keys());\n l.epoch++;\n this.broadcast(l, { t: \"host_changed\", hostId: l.hostId, epoch: l.epoch });\n }\n }\n\n private handle(m: Json) {\n if (this.closed) return;\n this.srv.received.push(m);\n const t = String(m.t);\n if (t === \"ping\") return this.push({ t: \"pong\", n: m.n });\n if (t === \"hello\") {\n if (this.hello) return this.err(\"bad_request\", \"second hello\");\n this.hello = m;\n const tok = typeof m.resumeToken === \"string\" ? m.resumeToken : \"\";\n for (const l of this.srv.lobbies.values()) {\n const rp = l.reserved.get(tok);\n if (!rp) continue;\n l.reserved.delete(tok);\n rp.sock = this;\n rp.token = tok + \"+\";\n l.peers.set(rp.peerId, rp);\n this.lobby = l;\n this.peer = rp;\n this.push({ t: \"welcome\", peerId: rp.peerId, ice: this.srv.iceServers, serverTime: Date.now(), limits: { maxMsg: 16384, maxPlayers: 64 } });\n this.push(this.lobbyMsg(l, rp));\n return this.broadcast(l, { t: \"peer_joined\", peer: { peerId: rp.peerId, name: rp.name, joinedAt: rp.joinedAt } }, rp.peerId);\n }\n return this.push({ t: \"welcome\", ice: this.srv.iceServers, serverTime: Date.now(), limits: { maxMsg: 16384, maxPlayers: 64 } });\n }\n if (!this.hello) return this.err(\"bad_request\", \"hello first\");\n const gameId = String(this.hello.gameId);\n const netVersion = String(this.hello.netVersion);\n switch (t) {\n case \"create\": {\n const code = this.srv.newCode();\n const p: MPeer = { peerId: 1, name: String(this.hello.name), joinedAt: Date.now(), sock: this, token: `tok-${code}-1` };\n const l: MLobby = {\n code, gameId, netVersion, name: String(m.name), hostId: 1, epoch: 0, mode: String(m.mode), tickRate: Number(m.tickRate),\n lateJoin: !!m.lateJoin, phase: \"lobby\", meta: (m.meta as Record<string, string>) ?? {}, visibility: String(m.visibility),\n maxPlayers: Number(m.maxPlayers), peers: new Map([[1, p]]), next: 2, kicked: new Set(), reserved: new Map(),\n };\n this.srv.lobbies.set(code, l);\n this.lobby = l;\n this.peer = p;\n return this.push(this.lobbyMsg(l, p));\n }\n case \"join\": {\n const code = normalizeCode(String(m.code));\n if (!code) return this.err(\"bad_request\", \"bad code\");\n const l = this.srv.lobbies.get(code);\n if (!l || l.gameId !== gameId) return this.err(\"unknown_code\");\n if (l.netVersion !== netVersion) return this.err(\"version_mismatch\", \"\", { hostNetVersion: l.netVersion, yourNetVersion: netVersion });\n if (l.phase === \"in_game\" && !l.lateJoin) return this.err(\"in_game\");\n if (l.peers.size >= l.maxPlayers) return this.err(\"lobby_full\");\n const id = l.next++;\n const p: MPeer = { peerId: id, name: String(this.hello.name), joinedAt: Date.now(), sock: this, token: `tok-${code}-${id}` };\n l.peers.set(id, p);\n this.lobby = l;\n this.peer = p;\n this.push(this.lobbyMsg(l, p));\n return this.broadcast(l, { t: \"peer_joined\", peer: { peerId: id, name: p.name, joinedAt: p.joinedAt } }, id);\n }\n case \"list\": {\n const items = [...this.srv.lobbies.values()]\n .filter((l) => l.gameId === gameId && l.netVersion === netVersion && l.visibility === \"public\")\n .map((l) => ({\n code: l.code, gameId: l.gameId, netVersion: l.netVersion, name: l.name, hostName: l.peers.get(l.hostId)?.name ?? \"\",\n players: l.peers.size, maxPlayers: l.maxPlayers, phase: l.phase, meta: l.meta, region: \"mem\", createdAt: 0,\n }));\n return this.push({ t: \"lobbies\", items });\n }\n case \"leave\":\n return this.leaveLobby(\"left\");\n case \"signal\": {\n const l = this.lobby;\n if (!l || !this.peer) return this.err(\"bad_request\");\n const to = l.peers.get(Number(m.to));\n if (!to) return this.err(\"bad_request\", \"no such peer\");\n return to.sock?.push({ t: \"signal\", from: this.peer.peerId, ...(m.sdp ? { sdp: m.sdp } : {}), ...(m.ice ? { ice: m.ice } : {}) });\n }\n case \"meta\":\n case \"phase\": {\n const l = this.lobby;\n if (!l || !this.peer) return;\n if (l.hostId !== this.peer.peerId) return this.err(\"not_host\");\n if (t === \"meta\") l.meta = m.meta as Record<string, string>;\n else l.phase = String(m.phase);\n for (const q of l.peers.values()) q.sock?.push(this.lobbyMsg(l, q));\n return;\n }\n case \"kick\": {\n const l = this.lobby;\n if (!l || !this.peer) return;\n if (l.hostId !== this.peer.peerId) return this.err(\"not_host\");\n const victim = l.peers.get(Number(m.peerId));\n if (!victim || victim === this.peer) return this.err(\"bad_request\");\n l.kicked.add(victim.peerId);\n l.peers.delete(victim.peerId);\n victim.sock?.push({ t: \"error\", code: \"kicked\", msg: \"kicked\" });\n this.broadcast(l, { t: \"peer_left\", peerId: victim.peerId, reason: \"kicked\" });\n return;\n }\n case \"host_changed\": {\n const l = this.lobby;\n if (!l || !this.peer) return;\n if (Number(m.hostId) === this.peer.peerId && Number(m.epoch) > l.epoch) {\n l.hostId = this.peer.peerId;\n l.epoch = Number(m.epoch);\n this.broadcast(l, { t: \"host_changed\", hostId: l.hostId, epoch: l.epoch });\n }\n return;\n }\n default:\n return;\n }\n }\n}\n\n/** Convenience: kill a socket as if the network dropped (server sees a timeout, others get peer_left). */\nexport function dropSocket(s: SocketLike): void {\n s.close();\n}\n"]}
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Test doubles: a manual clock, an in-memory lobby server speaking the lk1 protocol (spec/signaling.md, the parts the\n * client uses) and an in-memory peer network. Exported as `onelobby/testing`.\n */\nimport type { Clock } from \"./clock.js\";\nimport { CROCKFORD_ALPHABET, normalizeCode } from \"./crockford.js\";\nimport type {\n ChannelName,\n LinkFactory,\n LinkOptions,\n PeerLink,\n SignalPayload,\n SocketFactory,\n SocketLike,\n} from \"./transport.js\";\n\n// ---- clock ------------------------------------------------------------------------------------------------------\n\ninterface Timer {\n at: number;\n seq: number;\n fn: () => void;\n every?: number;\n dead?: boolean;\n}\n\n/** Deterministic clock: time only moves through `advance()`. Timers fire in (time, creation) order. */\nexport class ManualClock implements Clock {\n private t = 0;\n private seq = 0;\n private timers: Timer[] = [];\n\n now(): number {\n return this.t;\n }\n\n setInterval(fn: () => void, ms: number): () => void {\n const timer: Timer = { at: this.t + ms, seq: this.seq++, fn, every: Math.max(1, ms) };\n this.timers.push(timer);\n return () => {\n timer.dead = true;\n };\n }\n\n setTimeout(fn: () => void, ms: number): () => void {\n const timer: Timer = { at: this.t + ms, seq: this.seq++, fn };\n this.timers.push(timer);\n return () => {\n timer.dead = true;\n };\n }\n\n /** Advance time, firing due timers one at a time (microtasks flush between them when awaited via `run`). */\n advance(ms: number): void {\n const end = this.t + ms;\n for (;;) {\n this.timers = this.timers.filter((x) => !x.dead);\n let next: Timer | undefined;\n for (const x of this.timers) if (x.at <= end && (!next || x.at < next.at || (x.at === next.at && x.seq < next.seq))) next = x;\n if (!next) break;\n this.t = Math.max(this.t, next.at);\n if (next.every) {\n next.at += next.every;\n next.seq = this.seq++;\n } else {\n next.dead = true;\n }\n next.fn();\n }\n this.t = end;\n }\n\n /** Advance in `step` ms slices, letting promises and queued microtasks settle between slices. */\n async run(ms: number, step = 10): Promise<void> {\n let left = ms;\n await settle();\n while (left > 0) {\n const d = Math.min(step, left);\n this.advance(d);\n left -= d;\n await settle();\n }\n }\n}\n\n/** Let all pending microtasks (and chained promise continuations) run. */\nexport async function settle(rounds = 8): Promise<void> {\n for (let i = 0; i < rounds; i++) await Promise.resolve();\n // A macrotask boundary guarantees the whole microtask queue (delivery chains) has drained.\n await new Promise<void>((r) => ((globalThis as { setImmediate?: (f: () => void) => void }).setImmediate?.(r) ?? setTimeout(r, 0)));}\n\n// ---- in-memory peer network ---------------------------------------------------------------------------------------\n\nlet linkSerial = 0;\n\n/**\n * Pairs `PeerLink`s through the signaling path like WebRTC would: the offerer's SDP string names it, the answerer\n * resolves that name, and both open one microtask later. Messages are delivered FIFO, one microtask later.\n */\nexport class MemoryNetwork {\n private offers = new Map<string, MemoryLink>();\n /** Every link ever created (for assertions and fault injection). */\n readonly links: MemoryLink[] = [];\n /** Set to drop all traffic in one direction pair for fault injection: key `${from}->${to}` is not tracked; use link.mute(). */\n readonly factory: LinkFactory = { create: (opts) => new MemoryLink(this, opts) };\n\n /** @internal */\n register(offer: string, link: MemoryLink): void {\n this.offers.set(offer, link);\n this.links.push(link);\n }\n /** @internal */\n lookup(offer: string): MemoryLink | undefined {\n return this.offers.get(offer);\n }\n\n /** Kill every link whose far end is this identity-tagged factory owner (set `tag` via MemoryLink.owner). */\n killOwner(owner: string): void {\n for (const l of this.links) if (l.owner === owner) l.close();\n }\n\n /** Per-owner factory so tests can tell whose link is whose and cut one peer off. */\n factoryFor(owner: string): LinkFactory {\n return {\n create: (opts) => {\n const l = new MemoryLink(this, opts);\n l.owner = owner;\n return l;\n },\n };\n }\n}\n\nexport class MemoryLink implements PeerLink {\n readonly remoteId: number;\n isOpen = false;\n owner = \"\";\n onSignal: PeerLink[\"onSignal\"] = null;\n onOpen: PeerLink[\"onOpen\"] = null;\n onClose: PeerLink[\"onClose\"] = null;\n onMessage: PeerLink[\"onMessage\"] = null;\n onPath: PeerLink[\"onPath\"] = null;\n /** Bytes sent per channel, for assertions. */\n readonly sent: Record<ChannelName, number> = { reliable: 0, fast: 0, voice: 0 };\n /** While true, outgoing messages vanish (a frozen or partitioned peer). */\n blackhole = false;\n private peer: MemoryLink | null = null;\n private closed = false;\n private id = `mem-${++linkSerial}`;\n\n constructor(private net: MemoryNetwork, opts: LinkOptions) {\n this.remoteId = opts.remoteId;\n }\n\n startOffer(): void {\n this.net.register(this.id, this);\n queueMicrotask(() => this.onSignal?.({ sdp: { type: \"offer\", sdp: this.id } }));\n }\n\n handleSignal(s: SignalPayload): void {\n if (s.sdp?.type === \"offer\") {\n const offerer = this.net.lookup(s.sdp.sdp);\n this.net.links.push(this);\n if (!offerer) return;\n this.peer = offerer;\n queueMicrotask(() => this.onSignal?.({ sdp: { type: \"answer\", sdp: this.id } }));\n queueMicrotask(() => offerer.accept(this));\n queueMicrotask(() => this.open());\n }\n }\n\n /** @internal */\n accept(answerer: MemoryLink): void {\n this.peer = answerer;\n this.open();\n }\n\n private open(): void {\n if (this.closed || this.isOpen) return;\n this.isOpen = true;\n this.onOpen?.();\n this.onPath?.(\"direct\");\n }\n\n send(channel: ChannelName, bytes: Uint8Array): void {\n if (!this.isOpen || this.closed) return;\n this.sent[channel] += bytes.length;\n if (this.blackhole) return;\n const peer = this.peer;\n const copy = bytes.slice();\n queueMicrotask(() => {\n if (peer && peer.isOpen && !peer.closed && !this.closed) peer.onMessage?.(channel, copy);\n });\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.isOpen = false;\n const peer = this.peer;\n queueMicrotask(() => {\n this.onClose?.(\"closed\");\n if (peer && !peer.closed) peer.closeFromRemote();\n });\n }\n\n private closeFromRemote(): void {\n if (this.closed) return;\n this.closed = true;\n this.isOpen = false;\n this.onClose?.(\"remote closed\");\n }\n}\n\n// ---- in-memory lobby server ---------------------------------------------------------------------------------------\n\ntype Json = Record<string, unknown>;\n\ninterface MPeer {\n peerId: number;\n name: string;\n joinedAt: number;\n sock: MemorySocket | null;\n token: string;\n}\ninterface MLobby {\n code: string;\n gameId: string;\n netVersion: string;\n name: string;\n hostId: number;\n epoch: number;\n mode: string;\n tickRate: number;\n lateJoin: boolean;\n phase: string;\n meta: Record<string, string>;\n visibility: string;\n maxPlayers: number;\n peers: Map<number, MPeer>;\n next: number;\n kicked: Set<number>;\n reserved: Map<string, MPeer>;\n}\n\n/** Minimal onelobby-server (lk1) for client tests. Not a conformance target: stream A owns the real server. */\nexport class MemoryServer {\n readonly lobbies = new Map<string, MLobby>();\n /** Every socket ever opened (to drop one from a test). */\n readonly sockets: MemorySocket[] = [];\n readonly socketFactory: SocketFactory = () => this.connect();\n private n = 0;\n iceServers = [{ urls: [\"stun:stun.example:3478\"] }];\n /** Log of every client message, for assertions. */\n readonly received: Json[] = [];\n\n connect(): MemorySocket {\n const s = new MemorySocket(this);\n this.sockets.push(s);\n return s;\n }\n\n /** @internal */\n newCode(): string {\n let x = 0x9e3779b1 * (++this.n + 17);\n let s = \"\";\n for (let i = 0; i < 6; i++) {\n x = (Math.imul(x, 1103515245) + 12345) >>> 0;\n s += CROCKFORD_ALPHABET[(x >>> 16) & 31];\n }\n return this.lobbies.has(s) ? this.newCode() : s;\n }\n}\n\nexport class MemorySocket implements SocketLike {\n readyState = 0;\n onopen: SocketLike[\"onopen\"] = null;\n onmessage: SocketLike[\"onmessage\"] = null;\n onclose: SocketLike[\"onclose\"] = null;\n onerror: SocketLike[\"onerror\"] = null;\n\n private hello: Json | null = null;\n private lobby: MLobby | null = null;\n private peer: MPeer | null = null;\n private closed = false;\n\n constructor(private srv: MemoryServer) {\n queueMicrotask(() => {\n this.readyState = 1;\n this.onopen?.();\n });\n }\n\n // ---- socket API\n send(data: string): void {\n if (this.closed) return;\n const m = JSON.parse(data) as Json;\n queueMicrotask(() => this.handle(m));\n }\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.readyState = 3;\n this.leaveLobby(\"timeout\");\n queueMicrotask(() => this.onclose?.());\n }\n\n /** @internal server -> this client */\n push(m: Json): void {\n if (this.closed) return;\n queueMicrotask(() => this.onmessage?.({ data: JSON.stringify(m) }));\n }\n\n private err(code: string, msg = \"\", data?: Json) {\n this.push({ t: \"error\", code, msg, ...(data ? { data } : {}) });\n }\n\n private lobbyMsg(l: MLobby, p: MPeer): Json {\n return {\n t: \"lobby\",\n code: l.code,\n gameId: l.gameId,\n netVersion: l.netVersion,\n hostId: l.hostId,\n you: p.peerId,\n mode: l.mode,\n tickRate: l.tickRate,\n lateJoin: l.lateJoin,\n phase: l.phase,\n meta: l.meta,\n peers: [...l.peers.values()].map((q) => ({ peerId: q.peerId, name: q.name, joinedAt: q.joinedAt })),\n resumeToken: p.token,\n name: l.name,\n maxPlayers: l.maxPlayers,\n visibility: l.visibility,\n };\n }\n\n private broadcast(l: MLobby, m: Json, except?: number) {\n for (const q of l.peers.values()) if (q.peerId !== except) q.sock?.push(m);\n }\n\n private leaveLobby(reason: string) {\n const l = this.lobby;\n const p = this.peer;\n if (!l || !p) return;\n this.lobby = null;\n this.peer = null;\n p.sock = null;\n l.peers.delete(p.peerId);\n if (reason === \"timeout\") l.reserved.set(p.token, p); // slot kept for resume\n if (l.peers.size === 0) {\n this.srv.lobbies.delete(l.code);\n return;\n }\n this.broadcast(l, { t: \"peer_left\", peerId: p.peerId, reason });\n if (l.hostId === p.peerId) {\n l.hostId = Math.min(...l.peers.keys());\n l.epoch++;\n this.broadcast(l, { t: \"host_changed\", hostId: l.hostId, epoch: l.epoch });\n }\n }\n\n private handle(m: Json) {\n if (this.closed) return;\n this.srv.received.push(m);\n const t = String(m.t);\n if (t === \"ping\") return this.push({ t: \"pong\", n: m.n });\n if (t === \"hello\") {\n if (this.hello) return this.err(\"bad_request\", \"second hello\");\n this.hello = m;\n const tok = typeof m.resumeToken === \"string\" ? m.resumeToken : \"\";\n for (const l of this.srv.lobbies.values()) {\n const rp = l.reserved.get(tok);\n if (!rp) continue;\n l.reserved.delete(tok);\n rp.sock = this;\n rp.token = tok + \"+\";\n l.peers.set(rp.peerId, rp);\n this.lobby = l;\n this.peer = rp;\n this.push({ t: \"welcome\", peerId: rp.peerId, ice: this.srv.iceServers, serverTime: Date.now(), limits: { maxMsg: 16384, maxPlayers: 64 } });\n this.push(this.lobbyMsg(l, rp));\n return this.broadcast(l, { t: \"peer_joined\", peer: { peerId: rp.peerId, name: rp.name, joinedAt: rp.joinedAt } }, rp.peerId);\n }\n return this.push({ t: \"welcome\", ice: this.srv.iceServers, serverTime: Date.now(), limits: { maxMsg: 16384, maxPlayers: 64 } });\n }\n if (!this.hello) return this.err(\"bad_request\", \"hello first\");\n const gameId = String(this.hello.gameId);\n const netVersion = String(this.hello.netVersion);\n switch (t) {\n case \"create\": {\n const code = this.srv.newCode();\n const p: MPeer = { peerId: 1, name: String(this.hello.name), joinedAt: Date.now(), sock: this, token: `tok-${code}-1` };\n const l: MLobby = {\n code, gameId, netVersion, name: String(m.name), hostId: 1, epoch: 0, mode: String(m.mode), tickRate: Number(m.tickRate),\n lateJoin: !!m.lateJoin, phase: \"lobby\", meta: (m.meta as Record<string, string>) ?? {}, visibility: String(m.visibility),\n maxPlayers: Number(m.maxPlayers), peers: new Map([[1, p]]), next: 2, kicked: new Set(), reserved: new Map(),\n };\n this.srv.lobbies.set(code, l);\n this.lobby = l;\n this.peer = p;\n return this.push(this.lobbyMsg(l, p));\n }\n case \"join\": {\n const code = normalizeCode(String(m.code));\n if (!code) return this.err(\"bad_request\", \"bad code\");\n const l = this.srv.lobbies.get(code);\n if (!l || l.gameId !== gameId) return this.err(\"unknown_code\");\n if (l.netVersion !== netVersion) return this.err(\"version_mismatch\", \"\", { hostNetVersion: l.netVersion, yourNetVersion: netVersion });\n if (l.phase === \"in_game\" && !l.lateJoin) return this.err(\"in_game\");\n if (l.peers.size >= l.maxPlayers) return this.err(\"lobby_full\");\n const id = l.next++;\n const p: MPeer = { peerId: id, name: String(this.hello.name), joinedAt: Date.now(), sock: this, token: `tok-${code}-${id}` };\n l.peers.set(id, p);\n this.lobby = l;\n this.peer = p;\n this.push(this.lobbyMsg(l, p));\n return this.broadcast(l, { t: \"peer_joined\", peer: { peerId: id, name: p.name, joinedAt: p.joinedAt } }, id);\n }\n case \"list\": {\n const items = [...this.srv.lobbies.values()]\n .filter((l) => l.gameId === gameId && l.netVersion === netVersion && l.visibility === \"public\")\n .map((l) => ({\n code: l.code, gameId: l.gameId, netVersion: l.netVersion, name: l.name, hostName: l.peers.get(l.hostId)?.name ?? \"\",\n players: l.peers.size, maxPlayers: l.maxPlayers, phase: l.phase, meta: l.meta, region: \"mem\", createdAt: 0,\n }));\n return this.push({ t: \"lobbies\", items });\n }\n case \"leave\":\n return this.leaveLobby(\"left\");\n case \"signal\": {\n const l = this.lobby;\n if (!l || !this.peer) return this.err(\"bad_request\");\n const to = l.peers.get(Number(m.to));\n if (!to) return this.err(\"bad_request\", \"no such peer\");\n return to.sock?.push({ t: \"signal\", from: this.peer.peerId, ...(m.sdp ? { sdp: m.sdp } : {}), ...(m.ice ? { ice: m.ice } : {}) });\n }\n case \"meta\":\n case \"phase\": {\n const l = this.lobby;\n if (!l || !this.peer) return;\n if (l.hostId !== this.peer.peerId) return this.err(\"not_host\");\n if (t === \"meta\") l.meta = m.meta as Record<string, string>;\n else l.phase = String(m.phase);\n for (const q of l.peers.values()) q.sock?.push(this.lobbyMsg(l, q));\n return;\n }\n case \"kick\": {\n const l = this.lobby;\n if (!l || !this.peer) return;\n if (l.hostId !== this.peer.peerId) return this.err(\"not_host\");\n const victim = l.peers.get(Number(m.peerId));\n if (!victim || victim === this.peer) return this.err(\"bad_request\");\n // ban:false disconnects without the ban; closeCode / closeReason go to the removed peer (spec/signaling.md)\n const ban = m.ban !== false;\n const data: Json = {};\n if (!ban) data.ban = false;\n if (typeof m.closeCode === \"number\") data.closeCode = m.closeCode;\n if (typeof m.closeReason === \"string\") data.closeReason = m.closeReason;\n if (ban) l.kicked.add(victim.peerId);\n l.peers.delete(victim.peerId);\n victim.sock?.push({ t: \"error\", code: \"kicked\", msg: \"kicked\", ...(Object.keys(data).length ? { data } : {}) });\n this.broadcast(l, { t: \"peer_left\", peerId: victim.peerId, reason: ban ? \"kicked\" : \"disconnected\" });\n return;\n }\n case \"host_changed\": {\n const l = this.lobby;\n if (!l || !this.peer) return;\n if (Number(m.hostId) === this.peer.peerId && Number(m.epoch) > l.epoch) {\n l.hostId = this.peer.peerId;\n l.epoch = Number(m.epoch);\n this.broadcast(l, { t: \"host_changed\", hostId: l.hostId, epoch: l.epoch });\n }\n return;\n }\n default:\n return;\n }\n }\n}\n\n/** Convenience: kill a socket as if the network dropped (server sees a timeout, others get peer_left). */\nexport function dropSocket(s: SocketLike): void {\n s.close();\n}\n"]}
|
package/dist/webrtc.d.ts
CHANGED
|
@@ -11,11 +11,23 @@ export declare class WebRtcLink implements PeerLink {
|
|
|
11
11
|
private pc;
|
|
12
12
|
private channels;
|
|
13
13
|
private pendingIce;
|
|
14
|
+
/**
|
|
15
|
+
* Set once setRemoteDescription resolves. Not `pc.remoteDescription`: node-datachannel's polyfill returns an object
|
|
16
|
+
* there before any is set, and its addIceCandidate throws until one is.
|
|
17
|
+
*/
|
|
18
|
+
private haveRemote;
|
|
14
19
|
private closed;
|
|
15
20
|
private opened;
|
|
21
|
+
private early;
|
|
16
22
|
private extra;
|
|
17
23
|
/** `rtc`: the RTCPeerConnection class to use (default the global one; in Node, node-datachannel's polyfill). */
|
|
18
24
|
constructor(opts: LinkOptions, rtc?: typeof RTCPeerConnection);
|
|
25
|
+
/**
|
|
26
|
+
* The offerer creates the channels just before its offer, the answerer once the remote offer is set. Creating them
|
|
27
|
+
* any earlier makes libdatachannel (node-datachannel) start an offer of its own on the answering side, which then
|
|
28
|
+
* collides with the real one (both ICE agents controlling, the link never opens).
|
|
29
|
+
*/
|
|
30
|
+
private openChannels;
|
|
19
31
|
startOffer(): void;
|
|
20
32
|
handleSignal(s: SignalPayload): Promise<void>;
|
|
21
33
|
/** Selected candidate pair has a relay candidate -> "relayed", else "direct". Retries while stats settle. */
|
package/dist/webrtc.js
CHANGED
|
@@ -16,8 +16,14 @@ export class WebRtcLink {
|
|
|
16
16
|
pc;
|
|
17
17
|
channels = {};
|
|
18
18
|
pendingIce = [];
|
|
19
|
+
/**
|
|
20
|
+
* Set once setRemoteDescription resolves. Not `pc.remoteDescription`: node-datachannel's polyfill returns an object
|
|
21
|
+
* there before any is set, and its addIceCandidate throws until one is.
|
|
22
|
+
*/
|
|
23
|
+
haveRemote = false;
|
|
19
24
|
closed = false;
|
|
20
25
|
opened = 0;
|
|
26
|
+
early = [];
|
|
21
27
|
extra;
|
|
22
28
|
/** `rtc`: the RTCPeerConnection class to use (default the global one; in Node, node-datachannel's polyfill). */
|
|
23
29
|
constructor(opts, rtc = globalThis.RTCPeerConnection) {
|
|
@@ -49,6 +55,15 @@ export class WebRtcLink {
|
|
|
49
55
|
this.fail(s);
|
|
50
56
|
}
|
|
51
57
|
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The offerer creates the channels just before its offer, the answerer once the remote offer is set. Creating them
|
|
61
|
+
* any earlier makes libdatachannel (node-datachannel) start an offer of its own on the answering side, which then
|
|
62
|
+
* collides with the real one (both ICE agents controlling, the link never opens).
|
|
63
|
+
*/
|
|
64
|
+
openChannels() {
|
|
65
|
+
if (this.channels.reliable)
|
|
66
|
+
return;
|
|
52
67
|
for (const name of ["reliable", "fast", "voice"]) {
|
|
53
68
|
const ch = this.pc.createDataChannel(name, CHANNEL_INIT[name]);
|
|
54
69
|
ch.binaryType = "arraybuffer";
|
|
@@ -56,6 +71,9 @@ export class WebRtcLink {
|
|
|
56
71
|
if (++this.opened === 3 && !this.closed) {
|
|
57
72
|
this.isOpen = true;
|
|
58
73
|
this.onOpen?.();
|
|
74
|
+
for (const [n, data] of this.early.splice(0))
|
|
75
|
+
if (!this.closed)
|
|
76
|
+
this.onMessage?.(n, data);
|
|
59
77
|
void this.reportPath();
|
|
60
78
|
}
|
|
61
79
|
};
|
|
@@ -63,6 +81,13 @@ export class WebRtcLink {
|
|
|
63
81
|
ch.onmessage = (e) => {
|
|
64
82
|
const d = e.data;
|
|
65
83
|
const data = d instanceof ArrayBuffer ? new Uint8Array(d) : ArrayBuffer.isView(d) ? new Uint8Array(d.buffer, d.byteOffset, d.byteLength) : new Uint8Array(d);
|
|
84
|
+
// The far side can open first and speak at once; hold its data until this side is open too, so whoever
|
|
85
|
+
// reads it can already answer (otherwise the host queues or drops its replies to a link it thinks is down).
|
|
86
|
+
if (!this.isOpen) {
|
|
87
|
+
if (this.early.length < 1000)
|
|
88
|
+
this.early.push([name, data]);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
66
91
|
this.onMessage?.(name, data);
|
|
67
92
|
};
|
|
68
93
|
this.channels[name] = ch;
|
|
@@ -71,6 +96,7 @@ export class WebRtcLink {
|
|
|
71
96
|
startOffer() {
|
|
72
97
|
void (async () => {
|
|
73
98
|
try {
|
|
99
|
+
this.openChannels();
|
|
74
100
|
const offer = await this.pc.createOffer();
|
|
75
101
|
await this.pc.setLocalDescription(offer);
|
|
76
102
|
this.onSignal?.({ sdp: { type: "offer", sdp: offer.sdp ?? "" } });
|
|
@@ -84,6 +110,7 @@ export class WebRtcLink {
|
|
|
84
110
|
try {
|
|
85
111
|
if (s.sdp) {
|
|
86
112
|
await this.pc.setRemoteDescription({ type: s.sdp.type, sdp: s.sdp.sdp });
|
|
113
|
+
this.haveRemote = true;
|
|
87
114
|
for (const c of this.pendingIce.splice(0))
|
|
88
115
|
await this.pc.addIceCandidate(c);
|
|
89
116
|
if (this.extra.length) {
|
|
@@ -93,13 +120,15 @@ export class WebRtcLink {
|
|
|
93
120
|
await this.pc.addIceCandidate({ candidate, sdpMid: mid, sdpMLineIndex: 0 });
|
|
94
121
|
}
|
|
95
122
|
if (s.sdp.type === "offer") {
|
|
123
|
+
this.openChannels();
|
|
96
124
|
const answer = await this.pc.createAnswer();
|
|
97
125
|
await this.pc.setLocalDescription(answer);
|
|
98
126
|
this.onSignal?.({ sdp: { type: "answer", sdp: answer.sdp ?? "" } });
|
|
99
127
|
}
|
|
100
128
|
}
|
|
101
129
|
if (s.ice) {
|
|
102
|
-
|
|
130
|
+
// a candidate can come before the description it belongs to (the far side's native callbacks are unordered)
|
|
131
|
+
if (this.haveRemote)
|
|
103
132
|
await this.pc.addIceCandidate(s.ice);
|
|
104
133
|
else
|
|
105
134
|
this.pendingIce.push(s.ice);
|
package/dist/webrtc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webrtc.js","sourceRoot":"","sources":["../src/webrtc.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CHANNEL_IDS,\n type ChannelName,\n type LinkFactory,\n type LinkOptions,\n type PeerLink,\n type SignalPayload,\n} from \"./transport.js\";\n\nconst CHANNEL_INIT: Record<ChannelName, RTCDataChannelInit> = {\n reliable: { negotiated: true, id: CHANNEL_IDS.reliable, ordered: true },\n fast: { negotiated: true, id: CHANNEL_IDS.fast, ordered: false, maxRetransmits: 0 },\n voice: { negotiated: true, id: CHANNEL_IDS.voice, ordered: false, maxRetransmits: 0 },\n};\n\n/** Browser (or any WebRTC-capable runtime) PeerLink over RTCPeerConnection with three negotiated channels. */\nexport class WebRtcLink implements PeerLink {\n readonly remoteId: number;\n isOpen = false;\n onSignal: PeerLink[\"onSignal\"] = null;\n onOpen: PeerLink[\"onOpen\"] = null;\n onClose: PeerLink[\"onClose\"] = null;\n onMessage: PeerLink[\"onMessage\"] = null;\n onPath: PeerLink[\"onPath\"] = null;\n\n private pc: RTCPeerConnection;\n private channels = {} as Record<ChannelName, RTCDataChannel>;\n private pendingIce: RTCIceCandidateInit[] = [];\n private closed = false;\n private opened = 0;\n private extra: string[];\n\n /** `rtc`: the RTCPeerConnection class to use (default the global one; in Node, node-datachannel's polyfill). */\n constructor(opts: LinkOptions, rtc: typeof RTCPeerConnection = globalThis.RTCPeerConnection) {\n this.remoteId = opts.remoteId;\n this.extra = opts.extraRemoteCandidates ?? [];\n const config: RTCConfiguration & Record<string, unknown> = {\n iceServers: opts.iceServers as RTCIceServer[],\n iceTransportPolicy: opts.relayOnly ? \"relay\" : \"all\",\n };\n if (opts.portRange) {\n // libdatachannel names (node-datachannel passes its config through); one pinned port = every peer on it\n config.portRangeBegin = opts.portRange.begin;\n config.portRangeEnd = opts.portRange.end;\n if (opts.portRange.begin === opts.portRange.end) config.enableIceUdpMux = true;\n }\n this.pc = new rtc(config);\n this.pc.onicecandidate = (e) => {\n if (e.candidate) this.onSignal?.({ ice: e.candidate.toJSON() as { candidate: string } });\n };\n this.pc.onconnectionstatechange = () => {\n const s = this.pc.connectionState;\n if (s === \"failed\" || s === \"closed\" || s === \"disconnected\") {\n // \"disconnected\" can recover; give ICE a moment before declaring the link dead.\n if (s === \"disconnected\") setTimeout(() => this.pc.connectionState === \"disconnected\" && this.fail(\"disconnected\"), 4000);\n else this.fail(s);\n }\n };\n for (const name of [\"reliable\", \"fast\", \"voice\"] as ChannelName[]) {\n const ch = this.pc.createDataChannel(name, CHANNEL_INIT[name]);\n ch.binaryType = \"arraybuffer\";\n ch.onopen = () => {\n if (++this.opened === 3 && !this.closed) {\n this.isOpen = true;\n this.onOpen?.();\n void this.reportPath();\n }\n };\n ch.onclose = () => this.fail(`channel ${name} closed`);\n ch.onmessage = (e) => {\n const d = e.data as unknown;\n const data = d instanceof ArrayBuffer ? new Uint8Array(d) : ArrayBuffer.isView(d) ? new Uint8Array(d.buffer, d.byteOffset, d.byteLength) : new Uint8Array(d as never);\n this.onMessage?.(name, data);\n };\n this.channels[name] = ch;\n }\n }\n\n startOffer(): void {\n void (async () => {\n try {\n const offer = await this.pc.createOffer();\n await this.pc.setLocalDescription(offer);\n this.onSignal?.({ sdp: { type: \"offer\", sdp: offer.sdp ?? \"\" } });\n } catch (e) {\n this.fail(`offer failed: ${String(e)}`);\n }\n })();\n }\n\n async handleSignal(s: SignalPayload): Promise<void> {\n try {\n if (s.sdp) {\n await this.pc.setRemoteDescription({ type: s.sdp.type, sdp: s.sdp.sdp });\n for (const c of this.pendingIce.splice(0)) await this.pc.addIceCandidate(c);\n if (this.extra.length) {\n // docs/direct-ip.md s.2: the address the joiner typed is the host's reachable candidate\n const mid = /^a=mid:(\\S+)/m.exec(s.sdp.sdp)?.[1] ?? \"0\";\n for (const candidate of this.extra.splice(0)) await this.pc.addIceCandidate({ candidate, sdpMid: mid, sdpMLineIndex: 0 });\n }\n if (s.sdp.type === \"offer\") {\n const answer = await this.pc.createAnswer();\n await this.pc.setLocalDescription(answer);\n this.onSignal?.({ sdp: { type: \"answer\", sdp: answer.sdp ?? \"\" } });\n }\n }\n if (s.ice) {\n if (this.pc.remoteDescription) await this.pc.addIceCandidate(s.ice as RTCIceCandidateInit);\n else this.pendingIce.push(s.ice as RTCIceCandidateInit);\n }\n } catch (e) {\n this.fail(`signal failed: ${String(e)}`);\n }\n }\n\n /** Selected candidate pair has a relay candidate -> \"relayed\", else \"direct\". Retries while stats settle. */\n private async reportPath(attempt = 0): Promise<void> {\n if (this.closed || attempt > 10) return;\n try {\n const stats = await this.pc.getStats();\n const byId = new Map<string, any>();\n stats.forEach((v) => byId.set(v.id, v));\n let pair: any;\n stats.forEach((v) => {\n if (v.type === \"transport\" && v.selectedCandidatePairId) pair = byId.get(v.selectedCandidatePairId);\n });\n if (!pair) stats.forEach((v) => { if (v.type === \"candidate-pair\" && (v.selected || (v.nominated && v.state === \"succeeded\"))) pair = v; });\n const l = pair && byId.get(pair.localCandidateId);\n const r = pair && byId.get(pair.remoteCandidateId);\n if (l && r) {\n this.onPath?.(l.candidateType === \"relay\" || r.candidateType === \"relay\" ? \"relayed\" : \"direct\");\n return;\n }\n } catch {\n /* stats unavailable: retry */\n }\n setTimeout(() => void this.reportPath(attempt + 1), 300);\n }\n\n send(channel: ChannelName, bytes: Uint8Array): void {\n const ch = this.channels[channel];\n if (ch && ch.readyState === \"open\") ch.send(bytes as unknown as ArrayBuffer);\n }\n\n close(): void {\n this.fail(\"closed locally\");\n }\n\n private fail(reason: string): void {\n if (this.closed) return;\n this.closed = true;\n const wasOpen = this.isOpen;\n this.isOpen = false;\n try {\n this.pc.close();\n } catch {\n /* ignore */\n }\n void wasOpen;\n this.onClose?.(reason);\n }\n}\n\nexport const webRtcLinkFactory: LinkFactory = { create: (opts) => new WebRtcLink(opts) };\n\n/** A link factory over a given RTCPeerConnection class, e.g. `import { RTCPeerConnection } from \"node-datachannel/polyfill\"`. */\nexport function webRtcLinkFactoryFor(rtc: typeof RTCPeerConnection): LinkFactory {\n return { create: (opts) => new WebRtcLink(opts, rtc) };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"webrtc.js","sourceRoot":"","sources":["../src/webrtc.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n CHANNEL_IDS,\n type ChannelName,\n type LinkFactory,\n type LinkOptions,\n type PeerLink,\n type SignalPayload,\n} from \"./transport.js\";\n\nconst CHANNEL_INIT: Record<ChannelName, RTCDataChannelInit> = {\n reliable: { negotiated: true, id: CHANNEL_IDS.reliable, ordered: true },\n fast: { negotiated: true, id: CHANNEL_IDS.fast, ordered: false, maxRetransmits: 0 },\n voice: { negotiated: true, id: CHANNEL_IDS.voice, ordered: false, maxRetransmits: 0 },\n};\n\n/** Browser (or any WebRTC-capable runtime) PeerLink over RTCPeerConnection with three negotiated channels. */\nexport class WebRtcLink implements PeerLink {\n readonly remoteId: number;\n isOpen = false;\n onSignal: PeerLink[\"onSignal\"] = null;\n onOpen: PeerLink[\"onOpen\"] = null;\n onClose: PeerLink[\"onClose\"] = null;\n onMessage: PeerLink[\"onMessage\"] = null;\n onPath: PeerLink[\"onPath\"] = null;\n\n private pc: RTCPeerConnection;\n private channels = {} as Record<ChannelName, RTCDataChannel>;\n private pendingIce: RTCIceCandidateInit[] = [];\n /**\n * Set once setRemoteDescription resolves. Not `pc.remoteDescription`: node-datachannel's polyfill returns an object\n * there before any is set, and its addIceCandidate throws until one is.\n */\n private haveRemote = false;\n private closed = false;\n private opened = 0;\n private early: [ChannelName, Uint8Array][] = [];\n private extra: string[];\n\n /** `rtc`: the RTCPeerConnection class to use (default the global one; in Node, node-datachannel's polyfill). */\n constructor(opts: LinkOptions, rtc: typeof RTCPeerConnection = globalThis.RTCPeerConnection) {\n this.remoteId = opts.remoteId;\n this.extra = opts.extraRemoteCandidates ?? [];\n const config: RTCConfiguration & Record<string, unknown> = {\n iceServers: opts.iceServers as RTCIceServer[],\n iceTransportPolicy: opts.relayOnly ? \"relay\" : \"all\",\n };\n if (opts.portRange) {\n // libdatachannel names (node-datachannel passes its config through); one pinned port = every peer on it\n config.portRangeBegin = opts.portRange.begin;\n config.portRangeEnd = opts.portRange.end;\n if (opts.portRange.begin === opts.portRange.end) config.enableIceUdpMux = true;\n }\n this.pc = new rtc(config);\n this.pc.onicecandidate = (e) => {\n if (e.candidate) this.onSignal?.({ ice: e.candidate.toJSON() as { candidate: string } });\n };\n this.pc.onconnectionstatechange = () => {\n const s = this.pc.connectionState;\n if (s === \"failed\" || s === \"closed\" || s === \"disconnected\") {\n // \"disconnected\" can recover; give ICE a moment before declaring the link dead.\n if (s === \"disconnected\") setTimeout(() => this.pc.connectionState === \"disconnected\" && this.fail(\"disconnected\"), 4000);\n else this.fail(s);\n }\n };\n }\n\n /**\n * The offerer creates the channels just before its offer, the answerer once the remote offer is set. Creating them\n * any earlier makes libdatachannel (node-datachannel) start an offer of its own on the answering side, which then\n * collides with the real one (both ICE agents controlling, the link never opens).\n */\n private openChannels(): void {\n if (this.channels.reliable) return;\n for (const name of [\"reliable\", \"fast\", \"voice\"] as ChannelName[]) {\n const ch = this.pc.createDataChannel(name, CHANNEL_INIT[name]);\n ch.binaryType = \"arraybuffer\";\n ch.onopen = () => {\n if (++this.opened === 3 && !this.closed) {\n this.isOpen = true;\n this.onOpen?.();\n for (const [n, data] of this.early.splice(0)) if (!this.closed) this.onMessage?.(n, data);\n void this.reportPath();\n }\n };\n ch.onclose = () => this.fail(`channel ${name} closed`);\n ch.onmessage = (e) => {\n const d = e.data as unknown;\n const data = d instanceof ArrayBuffer ? new Uint8Array(d) : ArrayBuffer.isView(d) ? new Uint8Array(d.buffer, d.byteOffset, d.byteLength) : new Uint8Array(d as never);\n // The far side can open first and speak at once; hold its data until this side is open too, so whoever\n // reads it can already answer (otherwise the host queues or drops its replies to a link it thinks is down).\n if (!this.isOpen) {\n if (this.early.length < 1000) this.early.push([name, data]);\n return;\n }\n this.onMessage?.(name, data);\n };\n this.channels[name] = ch;\n }\n }\n\n startOffer(): void {\n void (async () => {\n try {\n this.openChannels();\n const offer = await this.pc.createOffer();\n await this.pc.setLocalDescription(offer);\n this.onSignal?.({ sdp: { type: \"offer\", sdp: offer.sdp ?? \"\" } });\n } catch (e) {\n this.fail(`offer failed: ${String(e)}`);\n }\n })();\n }\n\n async handleSignal(s: SignalPayload): Promise<void> {\n try {\n if (s.sdp) {\n await this.pc.setRemoteDescription({ type: s.sdp.type, sdp: s.sdp.sdp });\n this.haveRemote = true;\n for (const c of this.pendingIce.splice(0)) await this.pc.addIceCandidate(c);\n if (this.extra.length) {\n // docs/direct-ip.md s.2: the address the joiner typed is the host's reachable candidate\n const mid = /^a=mid:(\\S+)/m.exec(s.sdp.sdp)?.[1] ?? \"0\";\n for (const candidate of this.extra.splice(0)) await this.pc.addIceCandidate({ candidate, sdpMid: mid, sdpMLineIndex: 0 });\n }\n if (s.sdp.type === \"offer\") {\n this.openChannels();\n const answer = await this.pc.createAnswer();\n await this.pc.setLocalDescription(answer);\n this.onSignal?.({ sdp: { type: \"answer\", sdp: answer.sdp ?? \"\" } });\n }\n }\n if (s.ice) {\n // a candidate can come before the description it belongs to (the far side's native callbacks are unordered)\n if (this.haveRemote) await this.pc.addIceCandidate(s.ice as RTCIceCandidateInit);\n else this.pendingIce.push(s.ice as RTCIceCandidateInit);\n }\n } catch (e) {\n this.fail(`signal failed: ${String(e)}`);\n }\n }\n\n /** Selected candidate pair has a relay candidate -> \"relayed\", else \"direct\". Retries while stats settle. */\n private async reportPath(attempt = 0): Promise<void> {\n if (this.closed || attempt > 10) return;\n try {\n const stats = await this.pc.getStats();\n const byId = new Map<string, any>();\n stats.forEach((v) => byId.set(v.id, v));\n let pair: any;\n stats.forEach((v) => {\n if (v.type === \"transport\" && v.selectedCandidatePairId) pair = byId.get(v.selectedCandidatePairId);\n });\n if (!pair) stats.forEach((v) => { if (v.type === \"candidate-pair\" && (v.selected || (v.nominated && v.state === \"succeeded\"))) pair = v; });\n const l = pair && byId.get(pair.localCandidateId);\n const r = pair && byId.get(pair.remoteCandidateId);\n if (l && r) {\n this.onPath?.(l.candidateType === \"relay\" || r.candidateType === \"relay\" ? \"relayed\" : \"direct\");\n return;\n }\n } catch {\n /* stats unavailable: retry */\n }\n setTimeout(() => void this.reportPath(attempt + 1), 300);\n }\n\n send(channel: ChannelName, bytes: Uint8Array): void {\n const ch = this.channels[channel];\n if (ch && ch.readyState === \"open\") ch.send(bytes as unknown as ArrayBuffer);\n }\n\n close(): void {\n this.fail(\"closed locally\");\n }\n\n private fail(reason: string): void {\n if (this.closed) return;\n this.closed = true;\n const wasOpen = this.isOpen;\n this.isOpen = false;\n try {\n this.pc.close();\n } catch {\n /* ignore */\n }\n void wasOpen;\n this.onClose?.(reason);\n }\n}\n\nexport const webRtcLinkFactory: LinkFactory = { create: (opts) => new WebRtcLink(opts) };\n\n/** A link factory over a given RTCPeerConnection class, e.g. `import { RTCPeerConnection } from \"node-datachannel/polyfill\"`. */\nexport function webRtcLinkFactoryFor(rtc: typeof RTCPeerConnection): LinkFactory {\n return { create: (opts) => new WebRtcLink(opts, rtc) };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onelobby",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Multiplayer for browser games: host/join lobbies by short code, peer-to-peer WebRTC, lockstep sync, text and voice chat. Zero runtime dependencies; works from a plain <script> tag.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"multiplayer",
|