@minicode-dev/chat 0.1.6 → 0.1.8
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 +25 -0
- package/assets/sounds/tanks/big_explosion.mp3 +0 -0
- package/assets/sounds/tanks/big_explosion.ogg +0 -0
- package/assets/sounds/tanks/cannon_fire.mp3 +0 -0
- package/assets/sounds/tanks/cannon_fire.ogg +0 -0
- package/assets/sounds/tanks/defeat.mp3 +0 -0
- package/assets/sounds/tanks/defeat.ogg +0 -0
- package/assets/sounds/tanks/engine_loop.mp3 +0 -0
- package/assets/sounds/tanks/engine_loop.ogg +0 -0
- package/assets/sounds/tanks/engine_start.mp3 +0 -0
- package/assets/sounds/tanks/engine_start.ogg +0 -0
- package/assets/sounds/tanks/projectile_whistle.mp3 +0 -0
- package/assets/sounds/tanks/projectile_whistle.ogg +0 -0
- package/assets/sounds/tanks/tank_destroyed.mp3 +0 -0
- package/assets/sounds/tanks/tank_destroyed.ogg +0 -0
- package/assets/sounds/tanks/tank_hit.mp3 +0 -0
- package/assets/sounds/tanks/tank_hit.ogg +0 -0
- package/assets/sounds/tanks/terrain_impact.mp3 +0 -0
- package/assets/sounds/tanks/terrain_impact.ogg +0 -0
- package/assets/sounds/tanks/tracks_loop.mp3 +0 -0
- package/assets/sounds/tanks/tracks_loop.ogg +0 -0
- package/assets/sounds/tanks/turret_loop.mp3 +0 -0
- package/assets/sounds/tanks/turret_loop.ogg +0 -0
- package/assets/sounds/tanks/victory.mp3 +0 -0
- package/assets/sounds/tanks/victory.ogg +0 -0
- package/dist/chunks/{BattleshipGameCard-S-UdM6rn.js → BattleshipGameCard-BFqA0cjT.js} +67 -65
- package/dist/chunks/{BattleshipGameCard-S-UdM6rn.js.map → BattleshipGameCard-BFqA0cjT.js.map} +1 -1
- package/dist/chunks/{BattleshipPrototype-Dm6U_mCr.js → BattleshipPrototype-CmQd5AUO.js} +4 -3
- package/dist/chunks/{BattleshipPrototype-Dm6U_mCr.js.map → BattleshipPrototype-CmQd5AUO.js.map} +1 -1
- package/dist/chunks/ChatContext-dty_KPDX.js +90 -0
- package/dist/chunks/ChatContext-dty_KPDX.js.map +1 -0
- package/dist/chunks/TanksGameCard-D0n-lzTs.js +1535 -0
- package/dist/chunks/TanksGameCard-D0n-lzTs.js.map +1 -0
- package/dist/chunks/TanksLiveGameCard-BmNRXP66.js +849 -0
- package/dist/chunks/TanksLiveGameCard-BmNRXP66.js.map +1 -0
- package/dist/chunks/WebRtcLab-B7xBlJy1.js +510 -0
- package/dist/chunks/WebRtcLab-B7xBlJy1.js.map +1 -0
- package/dist/chunks/{battleship-DXKuIZ1I.js → board-DXKuIZ1I.js} +1 -1
- package/dist/chunks/board-DXKuIZ1I.js.map +1 -0
- package/dist/chunks/connect4Sounds-BN7d_TM2.js +3235 -0
- package/dist/chunks/connect4Sounds-BN7d_TM2.js.map +1 -0
- package/dist/chunks/core-geo-UJY-tH9L.js +361 -0
- package/dist/chunks/core-geo-UJY-tH9L.js.map +1 -0
- package/dist/chunks/createBattleScene-L4ZRWDED.js +1265 -0
- package/dist/chunks/createBattleScene-L4ZRWDED.js.map +1 -0
- package/dist/chunks/createLiveScene-CtYPcOPo.js +364 -0
- package/dist/chunks/createLiveScene-CtYPcOPo.js.map +1 -0
- package/dist/chunks/createPeerLink-qei05vpV.js +478 -0
- package/dist/chunks/createPeerLink-qei05vpV.js.map +1 -0
- package/dist/chunks/createTankScene-CV7Brxmt.js +685 -0
- package/dist/chunks/createTankScene-CV7Brxmt.js.map +1 -0
- package/dist/chunks/fx-BHsl4S_J.js +1470 -0
- package/dist/chunks/fx-BHsl4S_J.js.map +1 -0
- package/dist/chunks/gameStatus-Cu1Z8un9.js +69 -0
- package/dist/chunks/gameStatus-Cu1Z8un9.js.map +1 -0
- package/dist/chunks/signalFaults-Dv2YjjKy.js +36 -0
- package/dist/chunks/signalFaults-Dv2YjjKy.js.map +1 -0
- package/dist/chunks/tanksSounds-C99OD2xD.js +177 -0
- package/dist/chunks/tanksSounds-C99OD2xD.js.map +1 -0
- package/dist/index.js +2404 -2427
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/index.d.ts +63 -0
- package/package.json +1 -1
- package/dist/chunks/battleship-DXKuIZ1I.js.map +0 -1
- package/dist/chunks/connect4Sounds-9u0p-dAf.js +0 -1847
- package/dist/chunks/connect4Sounds-9u0p-dAf.js.map +0 -1
- package/dist/chunks/createBattleScene-HIAvjixn.js +0 -1621
- package/dist/chunks/createBattleScene-HIAvjixn.js.map +0 -1
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
// src/net/webrtc/protocol.js
|
|
2
|
+
var e = "LIRA_WEBRTC_V1:", t = 1e4, n = [
|
|
3
|
+
"hello",
|
|
4
|
+
"ready",
|
|
5
|
+
"offer",
|
|
6
|
+
"answer",
|
|
7
|
+
"ice",
|
|
8
|
+
"ack",
|
|
9
|
+
"bye"
|
|
10
|
+
], r = /^[A-Za-z0-9_-]{4,64}$/, i = 32, a = (e) => typeof e == "string" && e.startsWith("LIRA_WEBRTC_V1:"), o = (e, t) => {
|
|
11
|
+
if (t == null) return [
|
|
12
|
+
"hello",
|
|
13
|
+
"ready",
|
|
14
|
+
"ack",
|
|
15
|
+
"bye"
|
|
16
|
+
].includes(e);
|
|
17
|
+
if (typeof t != "object" || Array.isArray(t)) return !1;
|
|
18
|
+
switch (e) {
|
|
19
|
+
case "offer":
|
|
20
|
+
case "answer": return typeof t.sdp == "string" && t.sdp.length > 0;
|
|
21
|
+
case "ice": return Array.isArray(t.c) && t.c.length <= i && t.c.every((e) => e === null || e && typeof e.candidate == "string");
|
|
22
|
+
case "ack": return typeof t.of == "string" && r.test(t.of);
|
|
23
|
+
default: return !0;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
function s(e) {
|
|
27
|
+
return !!e && typeof e == "object" && e.v === 1 && n.includes(e.t) && typeof e.sid == "string" && r.test(e.sid) && typeof e.mid == "string" && r.test(e.mid) && typeof e.from == "string" && r.test(e.from) && (e.to == null || typeof e.to == "string" && r.test(e.to)) && Number.isInteger(e.gen ?? 0) && (e.gen ?? 0) >= 0 && (e.at == null || Number.isFinite(e.at)) && o(e.t, e.d);
|
|
28
|
+
}
|
|
29
|
+
function c(t) {
|
|
30
|
+
let n = e + JSON.stringify({
|
|
31
|
+
v: 1,
|
|
32
|
+
...t
|
|
33
|
+
});
|
|
34
|
+
return n.length <= 1e4 ? n : null;
|
|
35
|
+
}
|
|
36
|
+
function l(e) {
|
|
37
|
+
if (!a(e) || e.length > 1e4) return null;
|
|
38
|
+
try {
|
|
39
|
+
let t = JSON.parse(e.slice(15));
|
|
40
|
+
return s(t) ? t : null;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var u = () => {
|
|
46
|
+
try {
|
|
47
|
+
if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID().replace(/-/g, "").slice(0, 20);
|
|
48
|
+
} catch {}
|
|
49
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
|
|
50
|
+
}, d = [{ urls: ["stun:stun.l.google.com:19302", "stun:stun.cloudflare.com:3478"] }], f = 1200, p = 6, ee = 2e3, te = 3e4, ne = 80, m = 3e3, h = 2e4, g = 1e3, _ = 1e4, v = 2, re = 5, y = 16384, ie = 500, b = () => ({
|
|
51
|
+
sent: 0,
|
|
52
|
+
dropped: 0,
|
|
53
|
+
received: 0,
|
|
54
|
+
firstSeq: null,
|
|
55
|
+
maxSeq: 0,
|
|
56
|
+
outOfOrder: 0,
|
|
57
|
+
lossPct: 0,
|
|
58
|
+
hzIn: 0,
|
|
59
|
+
hzOut: 0,
|
|
60
|
+
windowIn: 0,
|
|
61
|
+
windowOut: 0
|
|
62
|
+
}), ae = {
|
|
63
|
+
host: "directa (red local)",
|
|
64
|
+
srflx: "directa (NAT vía STUN)",
|
|
65
|
+
prflx: "directa (NAT)",
|
|
66
|
+
relay: "TURN (relay)"
|
|
67
|
+
};
|
|
68
|
+
function x({ signaling: e, selfId: t, peerId: n, roomId: r = null, tabId: i = u(), iceServers: o = d, iceTransportPolicy: s = "all", autoReconnect: x = !0, listen: S = !0, RTCPeerConnectionImpl: C = globalThis.RTCPeerConnection, onChange: oe = () => {}, onMove: se = () => {}, onEvent: ce = () => {}, log: w = () => {} } = {}) {
|
|
69
|
+
if (!e?.send || !e?.subscribe) throw Error("createPeerLink: signaling { send, subscribe } requerido");
|
|
70
|
+
t = String(t), n = String(n);
|
|
71
|
+
let T = S, E = !1, D = null, O = 0, k = null, A = null, j = /* @__PURE__ */ new Set(), M = {
|
|
72
|
+
phase: "idle",
|
|
73
|
+
role: null,
|
|
74
|
+
sid: null,
|
|
75
|
+
gen: 0,
|
|
76
|
+
tabId: i,
|
|
77
|
+
peerTab: null,
|
|
78
|
+
ice: "new",
|
|
79
|
+
connection: "new",
|
|
80
|
+
signalingState: "stable",
|
|
81
|
+
gathering: "new",
|
|
82
|
+
route: null,
|
|
83
|
+
rtt: null,
|
|
84
|
+
statsRtt: null,
|
|
85
|
+
reconnects: 0,
|
|
86
|
+
restarts: 0,
|
|
87
|
+
error: null,
|
|
88
|
+
move: b(),
|
|
89
|
+
signals: {
|
|
90
|
+
sent: 0,
|
|
91
|
+
received: 0,
|
|
92
|
+
retransmits: 0,
|
|
93
|
+
duplicates: 0,
|
|
94
|
+
ignored: 0,
|
|
95
|
+
invalid: 0,
|
|
96
|
+
tooLarge: 0,
|
|
97
|
+
maxLength: 0
|
|
98
|
+
},
|
|
99
|
+
events: {
|
|
100
|
+
sent: 0,
|
|
101
|
+
received: 0
|
|
102
|
+
}
|
|
103
|
+
}, N = () => {
|
|
104
|
+
E || oe(P());
|
|
105
|
+
}, P = () => ({
|
|
106
|
+
...M,
|
|
107
|
+
move: { ...M.move },
|
|
108
|
+
signals: { ...M.signals },
|
|
109
|
+
events: { ...M.events }
|
|
110
|
+
}), F = (e, t = null) => {
|
|
111
|
+
M.phase = e, M.error = t, w(`fase: ${e}${t ? ` (${t})` : ""}`), N();
|
|
112
|
+
}, I = (t) => {
|
|
113
|
+
try {
|
|
114
|
+
return Promise.resolve(e.send(t)).then(() => !0, () => !1);
|
|
115
|
+
} catch {
|
|
116
|
+
return Promise.resolve(!1);
|
|
117
|
+
}
|
|
118
|
+
}, L = (e, t, n, { to: r = e?.peerTab ?? null, reliable: a = !0, gen: o = e?.gen ?? 0, sid: s = e?.sid } = {}) => {
|
|
119
|
+
let l = u(), d = c({
|
|
120
|
+
t,
|
|
121
|
+
sid: s,
|
|
122
|
+
gen: o,
|
|
123
|
+
mid: l,
|
|
124
|
+
at: Date.now(),
|
|
125
|
+
from: i,
|
|
126
|
+
to: r,
|
|
127
|
+
d: n ?? void 0
|
|
128
|
+
});
|
|
129
|
+
if (!d) return M.signals.tooLarge += 1, w(`señal ${t} supera el límite y no se envía`), e && U(e, "signal_too_large"), null;
|
|
130
|
+
if (M.signals.sent += 1, M.signals.maxLength = Math.max(M.signals.maxLength, d.length), I(d), a && e) {
|
|
131
|
+
let n = {
|
|
132
|
+
text: d,
|
|
133
|
+
tries: 1,
|
|
134
|
+
timer: null
|
|
135
|
+
}, r = () => {
|
|
136
|
+
!e.closed && e.outbox.has(l) && (n.tries >= p ? (e.outbox.delete(l), w(`señal ${t} sin ACK tras ${p} intentos`), t !== "bye" && U(e, "signal_timeout")) : (n.tries += 1, M.signals.retransmits += 1, I(d), n.timer = setTimeout(r, f)));
|
|
137
|
+
};
|
|
138
|
+
n.timer = setTimeout(r, f), e.outbox.set(l, n);
|
|
139
|
+
}
|
|
140
|
+
return l;
|
|
141
|
+
}, R = (e) => !j.has(e) && (j.add(e), j.size > ie && j.delete(j.values().next().value), !0), z = e.subscribe((e) => {
|
|
142
|
+
if (E || !e || !a(e.text) || r != null && e.roomId && String(e.roomId) !== String(r)) return;
|
|
143
|
+
let t = l(e.text);
|
|
144
|
+
if (!t) M.signals.invalid += 1;
|
|
145
|
+
else if (String(e.userId) !== n || t.from === i || t.to && t.to !== i) M.signals.ignored += 1;
|
|
146
|
+
else if (M.signals.received += 1, t.t !== "hello" && t.t !== "ack" && L(null, "ack", { of: t.mid }, {
|
|
147
|
+
to: t.from,
|
|
148
|
+
reliable: !1,
|
|
149
|
+
sid: t.sid,
|
|
150
|
+
gen: t.gen
|
|
151
|
+
}), !R(`${t.from}:${t.mid}`)) M.signals.duplicates += 1, N();
|
|
152
|
+
else {
|
|
153
|
+
switch (t.t) {
|
|
154
|
+
case "ack":
|
|
155
|
+
ge(t);
|
|
156
|
+
break;
|
|
157
|
+
case "hello":
|
|
158
|
+
_e(t);
|
|
159
|
+
break;
|
|
160
|
+
case "ready":
|
|
161
|
+
ve(t);
|
|
162
|
+
break;
|
|
163
|
+
case "offer":
|
|
164
|
+
ye(t);
|
|
165
|
+
break;
|
|
166
|
+
case "answer":
|
|
167
|
+
be(t);
|
|
168
|
+
break;
|
|
169
|
+
case "ice":
|
|
170
|
+
xe(t);
|
|
171
|
+
break;
|
|
172
|
+
case "bye": Se(t);
|
|
173
|
+
}
|
|
174
|
+
D && t.sid === D.sid && t.from === D.peerTab && t.at && (D.peerAt = Math.max(D.peerAt, t.at));
|
|
175
|
+
}
|
|
176
|
+
}), B = (e, t = u(), n = null) => ({
|
|
177
|
+
role: e,
|
|
178
|
+
sid: t,
|
|
179
|
+
peerTab: n,
|
|
180
|
+
gen: 0,
|
|
181
|
+
pc: null,
|
|
182
|
+
move: null,
|
|
183
|
+
events: null,
|
|
184
|
+
closed: !1,
|
|
185
|
+
outbox: /* @__PURE__ */ new Map(),
|
|
186
|
+
pendingIce: [],
|
|
187
|
+
localIce: [],
|
|
188
|
+
iceTimer: null,
|
|
189
|
+
helloTimer: null,
|
|
190
|
+
helloStarted: Date.now(),
|
|
191
|
+
disconnectTimer: null,
|
|
192
|
+
connectTimer: null,
|
|
193
|
+
pingTimer: null,
|
|
194
|
+
statsTimer: null,
|
|
195
|
+
tickTimer: null,
|
|
196
|
+
lastSeen: Date.now(),
|
|
197
|
+
pingSeq: 0,
|
|
198
|
+
moveSeq: 0,
|
|
199
|
+
restarts: 0,
|
|
200
|
+
open: 0,
|
|
201
|
+
peerAt: 0
|
|
202
|
+
}), V = (e, { bye: t = !1 } = {}) => {
|
|
203
|
+
if (e && !e.closed) {
|
|
204
|
+
if (t && e.peerTab && L(e, "bye", null, { reliable: !1 }), e.closed = !0, [
|
|
205
|
+
e.iceTimer,
|
|
206
|
+
e.disconnectTimer,
|
|
207
|
+
e.connectTimer
|
|
208
|
+
].forEach((e) => clearTimeout(e)), [
|
|
209
|
+
e.helloTimer,
|
|
210
|
+
e.pingTimer,
|
|
211
|
+
e.statsTimer,
|
|
212
|
+
e.tickTimer
|
|
213
|
+
].forEach((e) => clearInterval(e)), e.outbox.forEach((e) => clearTimeout(e.timer)), e.outbox.clear(), [e.move, e.events].forEach((e) => {
|
|
214
|
+
if (e) {
|
|
215
|
+
e.onopen = e.onclose = e.onmessage = e.onerror = null;
|
|
216
|
+
try {
|
|
217
|
+
e.close();
|
|
218
|
+
} catch {}
|
|
219
|
+
}
|
|
220
|
+
}), e.pc) {
|
|
221
|
+
e.pc.onicecandidate = e.pc.oniceconnectionstatechange = e.pc.onconnectionstatechange = null, e.pc.onsignalingstatechange = e.pc.onicegatheringstatechange = null;
|
|
222
|
+
try {
|
|
223
|
+
e.pc.close();
|
|
224
|
+
} catch {}
|
|
225
|
+
}
|
|
226
|
+
D === e && (D = null);
|
|
227
|
+
}
|
|
228
|
+
}, H = (e) => {
|
|
229
|
+
if (E || !x || k) return;
|
|
230
|
+
if (O >= re) {
|
|
231
|
+
F("failed", `${e}; sin más reintentos`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
let t = Math.min(8e3, 1e3 * 2 ** O);
|
|
235
|
+
O += 1, M.reconnects = O, F("reconnecting", e), k = setTimeout(() => {
|
|
236
|
+
k = null, !E && !D && $();
|
|
237
|
+
}, t);
|
|
238
|
+
}, U = (e, t) => {
|
|
239
|
+
if (e.closed) return;
|
|
240
|
+
let n = e.role === "caller";
|
|
241
|
+
A = e.sid, V(e, { bye: !0 }), n ? H(t) : F(T ? "listening" : "idle", t);
|
|
242
|
+
}, W = (e) => {
|
|
243
|
+
M.role = e.role, M.sid = e.sid, M.gen = e.gen, M.peerTab = e.peerTab, M.ice = "new", M.connection = "new", M.signalingState = "stable", M.gathering = "new", M.route = null, M.rtt = null, M.statsRtt = null, M.restarts = 0, M.move = b();
|
|
244
|
+
}, G = (e) => {
|
|
245
|
+
if (!C) return U(e, "webrtc_unavailable"), null;
|
|
246
|
+
let t = new C({
|
|
247
|
+
iceServers: o,
|
|
248
|
+
iceTransportPolicy: s
|
|
249
|
+
});
|
|
250
|
+
return e.pc = t, t.onicecandidate = ({ candidate: t }) => {
|
|
251
|
+
e.closed || (e.localIce.push(t ? t.toJSON?.() ?? t : null), e.iceTimer || (e.iceTimer = setTimeout(() => le(e), ne)));
|
|
252
|
+
}, t.onicegatheringstatechange = () => {
|
|
253
|
+
M.gathering = t.iceGatheringState, N();
|
|
254
|
+
}, t.onsignalingstatechange = () => {
|
|
255
|
+
M.signalingState = t.signalingState, N();
|
|
256
|
+
}, t.onconnectionstatechange = () => {
|
|
257
|
+
M.connection = t.connectionState, N();
|
|
258
|
+
}, t.oniceconnectionstatechange = () => ue(e), e.move = t.createDataChannel("move", {
|
|
259
|
+
negotiated: !0,
|
|
260
|
+
id: 0,
|
|
261
|
+
ordered: !1,
|
|
262
|
+
maxRetransmits: 0
|
|
263
|
+
}), e.events = t.createDataChannel("events", {
|
|
264
|
+
negotiated: !0,
|
|
265
|
+
id: 1,
|
|
266
|
+
ordered: !0
|
|
267
|
+
}), [e.move, e.events].forEach((t) => {
|
|
268
|
+
t.onopen = () => de(e), t.onclose = () => {
|
|
269
|
+
!e.closed && M.phase === "connected" && X(e, "canal cerrado");
|
|
270
|
+
};
|
|
271
|
+
}), e.move.onmessage = (t) => me(e, t.data), e.events.onmessage = (t) => pe(e, t.data), e.connectTimer = setTimeout(() => {
|
|
272
|
+
M.phase !== "connected" && U(e, "connect_timeout");
|
|
273
|
+
}, h), t;
|
|
274
|
+
}, le = (e) => {
|
|
275
|
+
if (e.iceTimer = null, e.closed || !e.localIce.length) return;
|
|
276
|
+
let t = e.localIce.splice(0, e.localIce.length);
|
|
277
|
+
for (let n = 0; n < t.length; n += 16) L(e, "ice", { c: t.slice(n, n + 16) });
|
|
278
|
+
}, K = async (e, t) => {
|
|
279
|
+
for (let n of t) try {
|
|
280
|
+
n && await e.pc.addIceCandidate(n);
|
|
281
|
+
} catch (e) {
|
|
282
|
+
w(`ICE remoto rechazado: ${e?.message ?? e}`);
|
|
283
|
+
}
|
|
284
|
+
}, q = (e) => {
|
|
285
|
+
let t = e.pendingIce.filter((t) => t.gen === e.gen);
|
|
286
|
+
e.pendingIce = e.pendingIce.filter((t) => t.gen > e.gen), t.forEach((t) => void K(e, t.c));
|
|
287
|
+
}, J = async (e, t = !1) => {
|
|
288
|
+
try {
|
|
289
|
+
let n = await e.pc.createOffer(t ? { iceRestart: !0 } : void 0);
|
|
290
|
+
if (e.closed || (await e.pc.setLocalDescription(n), e.closed)) return;
|
|
291
|
+
L(e, "offer", { sdp: e.pc.localDescription.sdp });
|
|
292
|
+
} catch (t) {
|
|
293
|
+
U(e, `offer: ${t?.message ?? t}`);
|
|
294
|
+
}
|
|
295
|
+
}, Y = (e) => {
|
|
296
|
+
e.closed || e.role !== "caller" || (e.restarts >= v ? X(e, "ICE no se recupera") : (e.restarts += 1, M.restarts = e.restarts, e.gen += 1, M.gen = e.gen, F("restarting"), J(e, !0)));
|
|
297
|
+
}, ue = (e) => {
|
|
298
|
+
if (e.closed) return;
|
|
299
|
+
let t = e.pc.iceConnectionState;
|
|
300
|
+
M.ice = t, clearTimeout(e.disconnectTimer), t === "connected" || t === "completed" ? (M.phase === "restarting" && F("connected"), Q(e)) : t === "disconnected" ? e.disconnectTimer = setTimeout(() => Y(e), m) : t === "failed" && Y(e), N();
|
|
301
|
+
}, de = (e) => {
|
|
302
|
+
e.open += 1, !(e.open < 2 || e.closed) && (clearTimeout(e.connectTimer), O = 0, e.lastSeen = Date.now(), e.pingTimer = setInterval(() => fe(e), g), e.statsTimer = setInterval(() => void Q(e), 2e3), e.tickTimer = setInterval(() => he(e), 1e3), F("connected"), Q(e));
|
|
303
|
+
}, X = (e, t) => U(e, t), fe = (e) => {
|
|
304
|
+
Date.now() - e.lastSeen > _ ? X(e, "sin latido del rival") : Z(e.events, JSON.stringify({
|
|
305
|
+
k: "ping",
|
|
306
|
+
n: ++e.pingSeq,
|
|
307
|
+
ts: performance.now()
|
|
308
|
+
}));
|
|
309
|
+
}, Z = (e, t) => {
|
|
310
|
+
if (e?.readyState !== "open") return !1;
|
|
311
|
+
try {
|
|
312
|
+
return e.send(t), !0;
|
|
313
|
+
} catch {
|
|
314
|
+
return !1;
|
|
315
|
+
}
|
|
316
|
+
}, pe = (e, t) => {
|
|
317
|
+
e.lastSeen = Date.now();
|
|
318
|
+
let n;
|
|
319
|
+
try {
|
|
320
|
+
n = JSON.parse(t);
|
|
321
|
+
} catch {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
n?.k === "ping" ? Z(e.events, JSON.stringify({
|
|
325
|
+
k: "pong",
|
|
326
|
+
ts: n.ts
|
|
327
|
+
})) : n?.k === "pong" ? M.rtt = Math.round((performance.now() - n.ts) * 10) / 10 : n?.k === "app" && (M.events.received += 1, ce(n.d));
|
|
328
|
+
}, me = (e, t) => {
|
|
329
|
+
e.lastSeen = Date.now();
|
|
330
|
+
let n;
|
|
331
|
+
try {
|
|
332
|
+
n = JSON.parse(t);
|
|
333
|
+
} catch {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
if (!Array.isArray(n) || !Number.isInteger(n[0])) return;
|
|
337
|
+
let [r, , i] = n, a = M.move;
|
|
338
|
+
a.received += 1, a.windowIn += 1, a.firstSeq ?? (a.firstSeq = r), r < a.maxSeq ? a.outOfOrder += 1 : (a.maxSeq = r, se(i, { seq: r }));
|
|
339
|
+
}, he = (e) => {
|
|
340
|
+
let t = M.move;
|
|
341
|
+
t.hzIn = t.windowIn, t.hzOut = t.windowOut, t.windowIn = 0, t.windowOut = 0;
|
|
342
|
+
let n = t.firstSeq == null ? 0 : t.maxSeq - t.firstSeq + 1;
|
|
343
|
+
t.lossPct = n > 0 ? Math.max(0, Math.round((1 - t.received / n) * 1e3) / 10) : 0, e.closed || N();
|
|
344
|
+
}, Q = async (e) => {
|
|
345
|
+
if (!e.closed && e.pc?.getStats) try {
|
|
346
|
+
let t = await e.pc.getStats(), n = /* @__PURE__ */ new Map();
|
|
347
|
+
t.forEach((e) => n.set(e.id, e));
|
|
348
|
+
let r = null;
|
|
349
|
+
if (t.forEach((e) => {
|
|
350
|
+
e.type === "transport" && e.selectedCandidatePairId && (r = n.get(e.selectedCandidatePairId));
|
|
351
|
+
}), r || t.forEach((e) => {
|
|
352
|
+
e.type === "candidate-pair" && (e.selected || e.nominated) && e.state === "succeeded" && (r = e);
|
|
353
|
+
}), !r) return;
|
|
354
|
+
let i = n.get(r.localCandidateId), a = n.get(r.remoteCandidateId), o = i?.candidateType === "relay" || a?.candidateType === "relay", s = o ? "relay" : i?.candidateType ?? "host";
|
|
355
|
+
M.route = {
|
|
356
|
+
local: i?.candidateType ?? null,
|
|
357
|
+
remote: a?.candidateType ?? null,
|
|
358
|
+
protocol: i?.protocol ?? null,
|
|
359
|
+
relay: o,
|
|
360
|
+
label: ae[s] ?? s
|
|
361
|
+
}, typeof r.currentRoundTripTime == "number" && (M.statsRtt = Math.round(r.currentRoundTripTime * 1e4) / 10), N();
|
|
362
|
+
} catch {}
|
|
363
|
+
}, ge = (e) => {
|
|
364
|
+
let t = D?.outbox.get(e.d.of);
|
|
365
|
+
t && (clearTimeout(t.timer), D.outbox.delete(e.d.of));
|
|
366
|
+
}, _e = (e) => {
|
|
367
|
+
if (D && D.sid === e.sid || D && D.peerTab === e.from && (e.at ?? 0) <= D.peerAt) return;
|
|
368
|
+
let t = D?.role === "caller" && !D.peerTab;
|
|
369
|
+
if (t && i < e.from) return;
|
|
370
|
+
if (!T && !t) {
|
|
371
|
+
M.signals.ignored += 1;
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
D && V(D, { bye: !t });
|
|
375
|
+
let n = B("answerer", e.sid, e.from);
|
|
376
|
+
D = n, W(n), G(n) && (L(n, "ready", null), F("negotiating"));
|
|
377
|
+
}, ve = (e) => {
|
|
378
|
+
let t = D;
|
|
379
|
+
t && t.role === "caller" && t.sid === e.sid && (t.peerTab && t.peerTab !== e.from ? L(t, "bye", null, {
|
|
380
|
+
to: e.from,
|
|
381
|
+
reliable: !1
|
|
382
|
+
}) : t.peerTab || (t.peerTab = e.from, M.peerTab = e.from, clearInterval(t.helloTimer), G(t) && (F("negotiating"), J(t))));
|
|
383
|
+
}, ye = async (e) => {
|
|
384
|
+
let t = D;
|
|
385
|
+
if (!(!t || t.role !== "answerer" || t.sid !== e.sid || t.peerTab !== e.from || e.gen < t.gen)) {
|
|
386
|
+
t.gen = e.gen, M.gen = t.gen;
|
|
387
|
+
try {
|
|
388
|
+
if (await t.pc.setRemoteDescription({
|
|
389
|
+
type: "offer",
|
|
390
|
+
sdp: e.d.sdp
|
|
391
|
+
}), t.closed) return;
|
|
392
|
+
q(t);
|
|
393
|
+
let n = await t.pc.createAnswer();
|
|
394
|
+
if (t.closed || (await t.pc.setLocalDescription(n), t.closed)) return;
|
|
395
|
+
L(t, "answer", { sdp: t.pc.localDescription.sdp });
|
|
396
|
+
} catch (e) {
|
|
397
|
+
U(t, `answer: ${e?.message ?? e}`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}, be = async (e) => {
|
|
401
|
+
let t = D;
|
|
402
|
+
if (t && t.role === "caller" && t.sid === e.sid && t.peerTab === e.from && e.gen === t.gen && t.pc.signalingState === "have-local-offer") try {
|
|
403
|
+
if (await t.pc.setRemoteDescription({
|
|
404
|
+
type: "answer",
|
|
405
|
+
sdp: e.d.sdp
|
|
406
|
+
}), t.closed) return;
|
|
407
|
+
q(t), M.phase === "restarting" && ["connected", "completed"].includes(t.pc.iceConnectionState) && F("connected");
|
|
408
|
+
} catch (e) {
|
|
409
|
+
U(t, `answer remota: ${e?.message ?? e}`);
|
|
410
|
+
}
|
|
411
|
+
}, xe = (e) => {
|
|
412
|
+
let t = D;
|
|
413
|
+
!t || t.sid !== e.sid || t.peerTab !== e.from || e.gen < t.gen || (e.gen > t.gen || !t.pc?.remoteDescription ? t.pendingIce.push({
|
|
414
|
+
gen: e.gen,
|
|
415
|
+
c: e.d.c
|
|
416
|
+
}) : K(t, e.d.c));
|
|
417
|
+
}, Se = (e) => {
|
|
418
|
+
if (e.sid === A && (k || D?.role === "caller" && !D.peerTab)) {
|
|
419
|
+
clearTimeout(k), k = null, O = 0, D && V(D), F(T ? "listening" : "idle", "el rival cerró la conexión");
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
let t = D;
|
|
423
|
+
t && t.sid === e.sid && (t.peerTab && t.peerTab !== e.from || (V(t), F(T ? "listening" : "idle", "el rival cerró la conexión")));
|
|
424
|
+
};
|
|
425
|
+
function $() {
|
|
426
|
+
if (E) return;
|
|
427
|
+
clearTimeout(k), k = null, D && V(D, { bye: !0 });
|
|
428
|
+
let e = B("caller");
|
|
429
|
+
D = e, W(e), F("calling");
|
|
430
|
+
let t = () => {
|
|
431
|
+
e.closed || e.peerTab || (Date.now() - e.helloStarted > te ? (V(e), F("failed", "el rival no respondió")) : L(e, "hello", null, {
|
|
432
|
+
to: null,
|
|
433
|
+
reliable: !1
|
|
434
|
+
}));
|
|
435
|
+
};
|
|
436
|
+
t(), e.helloTimer = setInterval(t, ee);
|
|
437
|
+
}
|
|
438
|
+
return T && F("listening"), {
|
|
439
|
+
tabId: i,
|
|
440
|
+
connect() {
|
|
441
|
+
O = 0, $();
|
|
442
|
+
},
|
|
443
|
+
setListening(e) {
|
|
444
|
+
T = !!e, D || F(T ? "listening" : "idle");
|
|
445
|
+
},
|
|
446
|
+
sendMove(e) {
|
|
447
|
+
let t = D;
|
|
448
|
+
if (!t || t.move?.readyState !== "open") return !1;
|
|
449
|
+
if (t.move.bufferedAmount > y) return M.move.dropped += 1, !1;
|
|
450
|
+
let n = Z(t.move, JSON.stringify([
|
|
451
|
+
++t.moveSeq,
|
|
452
|
+
Date.now(),
|
|
453
|
+
e
|
|
454
|
+
]));
|
|
455
|
+
return n && (M.move.sent += 1, M.move.windowOut += 1), n;
|
|
456
|
+
},
|
|
457
|
+
sendEvent(e) {
|
|
458
|
+
let t = Z(D?.events, JSON.stringify({
|
|
459
|
+
k: "app",
|
|
460
|
+
d: e
|
|
461
|
+
}));
|
|
462
|
+
return t && (M.events.sent += 1), t;
|
|
463
|
+
},
|
|
464
|
+
restartIce() {
|
|
465
|
+
D && Y(D);
|
|
466
|
+
},
|
|
467
|
+
getState: P,
|
|
468
|
+
hangup() {
|
|
469
|
+
clearTimeout(k), k = null, D && V(D, { bye: !0 }), F(T ? "listening" : "idle");
|
|
470
|
+
},
|
|
471
|
+
destroy() {
|
|
472
|
+
E || (clearTimeout(k), D && V(D, { bye: !0 }), E = !0, z?.());
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
export { l as a, e as i, x as n, c as o, t as r, a as s, d as t };
|
|
477
|
+
|
|
478
|
+
//# sourceMappingURL=createPeerLink-qei05vpV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createPeerLink-qei05vpV.js","names":["WEBRTC_PREFIX","MAX_SIGNAL_LENGTH","SIGNAL_TYPES","ID","MAX_CANDIDATES","isWebrtcMessage","text","validData","d","t","c","isValidSignal","signal","encodeSignal","decodeSignal","newSignalId","DEFAULT_ICE_SERVERS","RETRY_MS","MAX_TRIES","HELLO_EVERY_MS","HELLO_GIVE_UP_MS","ICE_BATCH_MS","DISCONNECTED_GRACE_MS","CONNECT_TIMEOUT_MS","PING_EVERY_MS","HEARTBEAT_TIMEOUT_MS","MAX_RESTARTS","MAX_RECONNECTS","MAX_BUFFERED","MAX_SEEN","emptyMoveStats","ROUTE_LABELS","createPeerLink","signaling","selfId","peerId","listening","listen","destroyed","session","reconnects","reconnectTimer","endedSid","seen","state","emit","onChange","snapshot","setPhase","phase","error","log","rawSend","sendSignal","s","mid","tabId","fail","reliable","entry","retry","remember","key","unsubscribe","frame","roomId","newSession","teardown","bye","timer","channel","scheduleReconnect","autoReconnect","reason","delay","call","wasCaller","bindState","createPc","RTCPeerConnectionImpl","pc","candidate","flushLocalIce","onIceState","onChannelOpen","lose","onMoveMessage","event","onEventMessage","batch","i","addRemoteIce","candidates","flushPendingIce","ready","item","makeOffer","offer","iceRestart","restartIce","ice","refreshStats","ping","tick","sendRaw","message","data","onEvent","packet","seq","payload","m","onMove","expected","report","byId","pair","local","remote","relay","type","onAck","onHello","glare","onReady","onOffer","answer","onAnswer","onIce","onBye","hello","value","sent"],"sources":["../../src/net/webrtc/protocol.js","../../src/net/webrtc/createPeerLink.js"],"sourcesContent":["// WebRTC signaling frames carried inside ordinary CHAT_MESSAGE text, the same idea\n// as LIRA_GAME_V1:. The chat backend only relays them; the sender is the\n// server-stamped user_id of the envelope, never a field of the payload.\n//\n// LIRA_WEBRTC_V1:{\"v\":1,\"t\":\"offer\",\"sid\":\"…\",\"gen\":0,\"mid\":\"…\",\"from\":\"tab\",\"to\":\"tab\",\"d\":{…}}\n//\n// t: hello (call, to = null) | ready (reply to hello) | offer | answer | ice | ack | bye\n// sid: session id chosen by the caller; gen: negotiation round (ICE restarts);\n// mid: message id for ACK/dedupe; from/to: tab ids, so several tabs of one user\n// and the server echo to the sender are told apart; at: sender clock (ms), only\n// compared with earlier frames of the same tab to spot late, stale calls.\n\nexport const WEBRTC_PREFIX = 'LIRA_WEBRTC_V1:';\n// Backend limit for CHAT_MESSAGE.message (Django audit). Frames above it are refused locally.\nexport const MAX_SIGNAL_LENGTH = 10000;\nexport const SIGNAL_TYPES = ['hello', 'ready', 'offer', 'answer', 'ice', 'ack', 'bye'];\n\nconst ID = /^[A-Za-z0-9_-]{4,64}$/;\nconst MAX_CANDIDATES = 32;\n\nexport const isWebrtcMessage = (text) => typeof text === 'string' && text.startsWith(WEBRTC_PREFIX);\n\nconst validData = (t, d) => {\n if (d == null) return ['hello', 'ready', 'ack', 'bye'].includes(t);\n if (typeof d !== 'object' || Array.isArray(d)) return false;\n switch (t) {\n case 'offer':\n case 'answer':\n return typeof d.sdp === 'string' && d.sdp.length > 0;\n case 'ice':\n return Array.isArray(d.c) && d.c.length <= MAX_CANDIDATES\n && d.c.every((c) => c === null || (c && typeof c.candidate === 'string'));\n case 'ack':\n return typeof d.of === 'string' && ID.test(d.of);\n default:\n return true;\n }\n};\n\nexport function isValidSignal(signal) {\n return Boolean(signal) && typeof signal === 'object'\n && signal.v === 1\n && SIGNAL_TYPES.includes(signal.t)\n && typeof signal.sid === 'string' && ID.test(signal.sid)\n && typeof signal.mid === 'string' && ID.test(signal.mid)\n && typeof signal.from === 'string' && ID.test(signal.from)\n && (signal.to == null || (typeof signal.to === 'string' && ID.test(signal.to)))\n && Number.isInteger(signal.gen ?? 0) && (signal.gen ?? 0) >= 0\n && (signal.at == null || Number.isFinite(signal.at))\n && validData(signal.t, signal.d);\n}\n\n/** Returns the frame text, or null when it would exceed MAX_SIGNAL_LENGTH. */\nexport function encodeSignal(signal) {\n const text = WEBRTC_PREFIX + JSON.stringify({ v: 1, ...signal });\n return text.length <= MAX_SIGNAL_LENGTH ? text : null;\n}\n\nexport function decodeSignal(text) {\n if (!isWebrtcMessage(text) || text.length > MAX_SIGNAL_LENGTH) return null;\n try {\n const signal = JSON.parse(text.slice(WEBRTC_PREFIX.length));\n return isValidSignal(signal) ? signal : null;\n } catch {\n return null;\n }\n}\n\nexport const newSignalId = () => {\n try {\n if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID().replace(/-/g, '').slice(0, 20);\n } catch { /* fallback below */ }\n return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;\n};\n\n// Normalizes a chat frame (flat or nested in data/payload) to { userId, roomId, text }.\nexport function readChatFrame(frame) {\n if (!frame || typeof frame !== 'object') return null;\n const nested = frame.data ?? frame.payload;\n const body = nested && typeof nested === 'object' ? { ...nested, ...frame } : frame;\n const text = frame.message ?? frame.text ?? nested?.message ?? nested?.text;\n if (typeof text !== 'string') return null;\n return { userId: String(body.user_id ?? ''), roomId: String(body.room_id ?? ''), text };\n}\n","// One WebRTC link between two chat users, signaled through the chat itself.\n//\n// Signaling: LIRA_WEBRTC_V1: frames (protocol.js) sent as ordinary CHAT_MESSAGE\n// text in the users' private room. The relay echoes frames to every tab of both\n// users, may duplicate them after a reconnect and gives no delivery guarantee, so:\n// - frames carry tab ids (from/to): own echoes and other tabs are ignored;\n// - every frame except hello/ack is ACKed and retransmitted until ACKed;\n// - duplicates are dropped by message id; ICE that arrives before its\n// description waits in a queue; stale sessions/generations are ignored.\n// Session: caller sends hello (repeated until answered); the first tab of the\n// peer that answers ready is bound, gets the offer and the others a bye.\n// Simultaneous calls resolve by tab id (the lower one keeps calling).\n//\n// Data: two negotiated DataChannels (no ondatachannel race):\n// move id 0, unordered, maxRetransmits 0 — latest position wins;\n// events id 1, ordered and reliable — pings, hits, game events.\n// Recovery: ICE 'failed' (or 'disconnected' for 3 s) triggers an ICE restart by\n// the caller; no traffic for HEARTBEAT_TIMEOUT_MS ends the session and the caller\n// starts a new one (autoReconnect, with backoff).\n\nimport { encodeSignal, decodeSignal, isWebrtcMessage, newSignalId } from './protocol.js';\n\nexport const DEFAULT_ICE_SERVERS = [{ urls: ['stun:stun.l.google.com:19302', 'stun:stun.cloudflare.com:3478'] }];\n\nconst RETRY_MS = 1200;\nconst MAX_TRIES = 6;\nconst HELLO_EVERY_MS = 2000;\nconst HELLO_GIVE_UP_MS = 30000;\nconst ICE_BATCH_MS = 80;\nconst DISCONNECTED_GRACE_MS = 3000;\nconst CONNECT_TIMEOUT_MS = 20000;\nconst PING_EVERY_MS = 1000;\nconst HEARTBEAT_TIMEOUT_MS = 10000;\nconst MAX_RESTARTS = 2;\nconst MAX_RECONNECTS = 5;\nconst MAX_BUFFERED = 16 * 1024;\nconst MAX_SEEN = 500;\n\nconst emptyMoveStats = () => ({ sent: 0, dropped: 0, received: 0, firstSeq: null, maxSeq: 0, outOfOrder: 0, lossPct: 0, hzIn: 0, hzOut: 0, windowIn: 0, windowOut: 0 });\n\nconst ROUTE_LABELS = { host: 'directa (red local)', srflx: 'directa (NAT vía STUN)', prflx: 'directa (NAT)', relay: 'TURN (relay)' };\n\nexport function createPeerLink({\n signaling,\n selfId,\n peerId,\n roomId = null,\n tabId = newSignalId(),\n iceServers = DEFAULT_ICE_SERVERS,\n iceTransportPolicy = 'all',\n autoReconnect = true,\n listen = true,\n RTCPeerConnectionImpl = globalThis.RTCPeerConnection,\n onChange = () => {},\n onMove = () => {},\n onEvent = () => {},\n log = () => {},\n} = {}) {\n if (!signaling?.send || !signaling?.subscribe) throw new Error('createPeerLink: signaling { send, subscribe } requerido');\n selfId = String(selfId);\n peerId = String(peerId);\n\n let listening = listen;\n let destroyed = false;\n let session = null;\n let reconnects = 0;\n let reconnectTimer = null;\n let endedSid = null; // last session that ended without the peer's bye\n const seen = new Set();\n const state = {\n phase: 'idle', role: null, sid: null, gen: 0, tabId, peerTab: null,\n ice: 'new', connection: 'new', signalingState: 'stable', gathering: 'new',\n route: null, rtt: null, statsRtt: null, reconnects: 0, restarts: 0, error: null,\n move: emptyMoveStats(),\n signals: { sent: 0, received: 0, retransmits: 0, duplicates: 0, ignored: 0, invalid: 0, tooLarge: 0, maxLength: 0 },\n events: { sent: 0, received: 0 },\n };\n\n const emit = () => { if (!destroyed) onChange(snapshot()); };\n const snapshot = () => ({ ...state, move: { ...state.move }, signals: { ...state.signals }, events: { ...state.events } });\n const setPhase = (phase, error = null) => {\n state.phase = phase;\n state.error = error;\n log(`fase: ${phase}${error ? ` (${error})` : ''}`);\n emit();\n };\n\n // ---- signaling --------------------------------------------------------\n\n const rawSend = (text) => {\n try {\n return Promise.resolve(signaling.send(text)).then(() => true, () => false);\n } catch {\n return Promise.resolve(false);\n }\n };\n\n const sendSignal = (s, t, d, { to = s?.peerTab ?? null, reliable = true, gen = s?.gen ?? 0, sid = s?.sid } = {}) => {\n const mid = newSignalId();\n const text = encodeSignal({ t, sid, gen, mid, at: Date.now(), from: tabId, to, d: d ?? undefined });\n if (!text) {\n state.signals.tooLarge += 1;\n log(`señal ${t} supera el límite y no se envía`);\n if (s) fail(s, 'signal_too_large');\n return null;\n }\n state.signals.sent += 1;\n state.signals.maxLength = Math.max(state.signals.maxLength, text.length);\n void rawSend(text);\n if (reliable && s) {\n const entry = { text, tries: 1, timer: null };\n const retry = () => {\n if (s.closed || !s.outbox.has(mid)) return;\n if (entry.tries >= MAX_TRIES) {\n s.outbox.delete(mid);\n log(`señal ${t} sin ACK tras ${MAX_TRIES} intentos`);\n if (t !== 'bye') fail(s, 'signal_timeout');\n return;\n }\n entry.tries += 1;\n state.signals.retransmits += 1;\n void rawSend(text);\n entry.timer = setTimeout(retry, RETRY_MS);\n };\n entry.timer = setTimeout(retry, RETRY_MS);\n s.outbox.set(mid, entry);\n }\n return mid;\n };\n\n const remember = (key) => {\n if (seen.has(key)) return false;\n seen.add(key);\n if (seen.size > MAX_SEEN) seen.delete(seen.values().next().value);\n return true;\n };\n\n const handleFrame = (frame) => {\n if (destroyed || !frame || !isWebrtcMessage(frame.text)) return;\n if (roomId != null && frame.roomId && String(frame.roomId) !== String(roomId)) return;\n const signal = decodeSignal(frame.text);\n if (!signal) { state.signals.invalid += 1; return; }\n // Own echoes, own other tabs and third parties never drive this link.\n if (String(frame.userId) !== peerId || signal.from === tabId || (signal.to && signal.to !== tabId)) {\n state.signals.ignored += 1;\n return;\n }\n state.signals.received += 1;\n if (signal.t !== 'hello' && signal.t !== 'ack') {\n // ACK even duplicates: the first ACK may be the one that got lost.\n sendSignal(null, 'ack', { of: signal.mid }, { to: signal.from, reliable: false, sid: signal.sid, gen: signal.gen });\n }\n if (!remember(`${signal.from}:${signal.mid}`)) { state.signals.duplicates += 1; emit(); return; }\n switch (signal.t) {\n case 'ack': onAck(signal); break;\n case 'hello': onHello(signal); break;\n case 'ready': onReady(signal); break;\n case 'offer': void onOffer(signal); break;\n case 'answer': void onAnswer(signal); break;\n case 'ice': onIce(signal); break;\n case 'bye': onBye(signal); break;\n default: break;\n }\n // After dispatch, so the frame that binds the peer tab (hello/ready) counts too.\n if (session && signal.sid === session.sid && signal.from === session.peerTab && signal.at) {\n session.peerAt = Math.max(session.peerAt, signal.at);\n }\n };\n\n const unsubscribe = signaling.subscribe(handleFrame);\n\n // ---- session lifecycle ------------------------------------------------\n\n const newSession = (role, sid = newSignalId(), peerTab = null) => ({\n role, sid, peerTab, gen: 0, pc: null, move: null, events: null, closed: false,\n outbox: new Map(), pendingIce: [], localIce: [], iceTimer: null, helloTimer: null, helloStarted: Date.now(),\n disconnectTimer: null, connectTimer: null, pingTimer: null, statsTimer: null, tickTimer: null,\n lastSeen: Date.now(), pingSeq: 0, moveSeq: 0, restarts: 0, open: 0, peerAt: 0,\n });\n\n const teardown = (s, { bye = false } = {}) => {\n if (!s || s.closed) return;\n if (bye && s.peerTab) sendSignal(s, 'bye', null, { reliable: false });\n s.closed = true;\n [s.iceTimer, s.disconnectTimer, s.connectTimer].forEach((timer) => clearTimeout(timer));\n [s.helloTimer, s.pingTimer, s.statsTimer, s.tickTimer].forEach((timer) => clearInterval(timer));\n s.outbox.forEach((entry) => clearTimeout(entry.timer));\n s.outbox.clear();\n [s.move, s.events].forEach((channel) => {\n if (!channel) return;\n channel.onopen = channel.onclose = channel.onmessage = channel.onerror = null;\n try { channel.close(); } catch { /* already closed */ }\n });\n if (s.pc) {\n s.pc.onicecandidate = s.pc.oniceconnectionstatechange = s.pc.onconnectionstatechange = null;\n s.pc.onsignalingstatechange = s.pc.onicegatheringstatechange = null;\n try { s.pc.close(); } catch { /* already closed */ }\n }\n if (session === s) session = null;\n };\n\n const scheduleReconnect = (reason) => {\n if (destroyed || !autoReconnect || reconnectTimer) return;\n if (reconnects >= MAX_RECONNECTS) { setPhase('failed', `${reason}; sin más reintentos`); return; }\n const delay = Math.min(8000, 1000 * 2 ** reconnects);\n reconnects += 1;\n state.reconnects = reconnects;\n setPhase('reconnecting', reason);\n reconnectTimer = setTimeout(() => { reconnectTimer = null; if (!destroyed && !session) call(); }, delay);\n };\n\n const fail = (s, reason) => {\n if (s.closed) return;\n const wasCaller = s.role === 'caller';\n endedSid = s.sid;\n teardown(s, { bye: true });\n if (wasCaller) scheduleReconnect(reason);\n else setPhase(listening ? 'listening' : 'idle', reason);\n };\n\n const bindState = (s) => {\n state.role = s.role;\n state.sid = s.sid;\n state.gen = s.gen;\n state.peerTab = s.peerTab;\n state.ice = 'new';\n state.connection = 'new';\n state.signalingState = 'stable';\n state.gathering = 'new';\n state.route = null;\n state.rtt = null;\n state.statsRtt = null;\n state.restarts = 0;\n state.move = emptyMoveStats();\n };\n\n // ---- peer connection --------------------------------------------------\n\n const createPc = (s) => {\n if (!RTCPeerConnectionImpl) { fail(s, 'webrtc_unavailable'); return null; }\n const pc = new RTCPeerConnectionImpl({ iceServers, iceTransportPolicy });\n s.pc = pc;\n pc.onicecandidate = ({ candidate }) => {\n if (s.closed) return;\n s.localIce.push(candidate ? candidate.toJSON?.() ?? candidate : null);\n if (!s.iceTimer) s.iceTimer = setTimeout(() => flushLocalIce(s), ICE_BATCH_MS);\n };\n pc.onicegatheringstatechange = () => { state.gathering = pc.iceGatheringState; emit(); };\n pc.onsignalingstatechange = () => { state.signalingState = pc.signalingState; emit(); };\n pc.onconnectionstatechange = () => { state.connection = pc.connectionState; emit(); };\n pc.oniceconnectionstatechange = () => onIceState(s);\n s.move = pc.createDataChannel('move', { negotiated: true, id: 0, ordered: false, maxRetransmits: 0 });\n s.events = pc.createDataChannel('events', { negotiated: true, id: 1, ordered: true });\n [s.move, s.events].forEach((channel) => {\n channel.onopen = () => onChannelOpen(s);\n channel.onclose = () => { if (!s.closed && state.phase === 'connected') lose(s, 'canal cerrado'); };\n });\n s.move.onmessage = (event) => onMoveMessage(s, event.data);\n s.events.onmessage = (event) => onEventMessage(s, event.data);\n s.connectTimer = setTimeout(() => { if (state.phase !== 'connected') fail(s, 'connect_timeout'); }, CONNECT_TIMEOUT_MS);\n return pc;\n };\n\n const flushLocalIce = (s) => {\n s.iceTimer = null;\n if (s.closed || !s.localIce.length) return;\n const batch = s.localIce.splice(0, s.localIce.length);\n for (let i = 0; i < batch.length; i += 16) sendSignal(s, 'ice', { c: batch.slice(i, i + 16) });\n };\n\n const addRemoteIce = async (s, candidates) => {\n for (const candidate of candidates) {\n try {\n if (candidate) await s.pc.addIceCandidate(candidate);\n } catch (error) {\n log(`ICE remoto rechazado: ${error?.message ?? error}`);\n }\n }\n };\n\n const flushPendingIce = (s) => {\n const ready = s.pendingIce.filter((item) => item.gen === s.gen);\n s.pendingIce = s.pendingIce.filter((item) => item.gen > s.gen);\n ready.forEach((item) => void addRemoteIce(s, item.c));\n };\n\n const makeOffer = async (s, iceRestart = false) => {\n try {\n const offer = await s.pc.createOffer(iceRestart ? { iceRestart: true } : undefined);\n if (s.closed) return;\n await s.pc.setLocalDescription(offer);\n if (s.closed) return;\n sendSignal(s, 'offer', { sdp: s.pc.localDescription.sdp });\n } catch (error) {\n fail(s, `offer: ${error?.message ?? error}`);\n }\n };\n\n const restartIce = (s) => {\n if (s.closed || s.role !== 'caller') return;\n if (s.restarts >= MAX_RESTARTS) { lose(s, 'ICE no se recupera'); return; }\n s.restarts += 1;\n state.restarts = s.restarts;\n s.gen += 1;\n state.gen = s.gen;\n setPhase('restarting');\n void makeOffer(s, true);\n };\n\n const onIceState = (s) => {\n if (s.closed) return;\n const ice = s.pc.iceConnectionState;\n state.ice = ice;\n clearTimeout(s.disconnectTimer);\n if (ice === 'connected' || ice === 'completed') {\n if (state.phase === 'restarting') setPhase('connected');\n void refreshStats(s);\n } else if (ice === 'disconnected') {\n s.disconnectTimer = setTimeout(() => restartIce(s), DISCONNECTED_GRACE_MS);\n } else if (ice === 'failed') {\n restartIce(s);\n }\n emit();\n };\n\n const onChannelOpen = (s) => {\n s.open += 1;\n if (s.open < 2 || s.closed) return;\n clearTimeout(s.connectTimer);\n reconnects = 0;\n s.lastSeen = Date.now();\n s.pingTimer = setInterval(() => ping(s), PING_EVERY_MS);\n s.statsTimer = setInterval(() => void refreshStats(s), 2000);\n s.tickTimer = setInterval(() => tick(s), 1000);\n setPhase('connected');\n void refreshStats(s);\n };\n\n // A session that stops hearing the peer is over; the caller starts a new one.\n const lose = (s, reason) => fail(s, reason);\n\n // ---- data -------------------------------------------------------------\n\n const ping = (s) => {\n if (Date.now() - s.lastSeen > HEARTBEAT_TIMEOUT_MS) { lose(s, 'sin latido del rival'); return; }\n sendRaw(s.events, JSON.stringify({ k: 'ping', n: ++s.pingSeq, ts: performance.now() }));\n };\n\n const sendRaw = (channel, text) => {\n if (channel?.readyState !== 'open') return false;\n try { channel.send(text); return true; } catch { return false; }\n };\n\n const onEventMessage = (s, data) => {\n s.lastSeen = Date.now();\n let message;\n try { message = JSON.parse(data); } catch { return; }\n if (message?.k === 'ping') sendRaw(s.events, JSON.stringify({ k: 'pong', ts: message.ts }));\n else if (message?.k === 'pong') { state.rtt = Math.round((performance.now() - message.ts) * 10) / 10; }\n else if (message?.k === 'app') { state.events.received += 1; onEvent(message.d); }\n };\n\n const onMoveMessage = (s, data) => {\n s.lastSeen = Date.now();\n let packet;\n try { packet = JSON.parse(data); } catch { return; }\n if (!Array.isArray(packet) || !Number.isInteger(packet[0])) return;\n const [seq, , payload] = packet;\n const m = state.move;\n m.received += 1;\n m.windowIn += 1;\n if (m.firstSeq == null) m.firstSeq = seq;\n if (seq < m.maxSeq) { m.outOfOrder += 1; return; } // stale position: counted, not applied\n m.maxSeq = seq;\n onMove(payload, { seq });\n };\n\n const tick = (s) => {\n const m = state.move;\n m.hzIn = m.windowIn;\n m.hzOut = m.windowOut;\n m.windowIn = 0;\n m.windowOut = 0;\n const expected = m.firstSeq == null ? 0 : m.maxSeq - m.firstSeq + 1;\n m.lossPct = expected > 0 ? Math.max(0, Math.round((1 - m.received / expected) * 1000) / 10) : 0;\n if (!s.closed) emit();\n };\n\n const refreshStats = async (s) => {\n if (s.closed || !s.pc?.getStats) return;\n try {\n const report = await s.pc.getStats();\n const byId = new Map();\n report.forEach((entry) => byId.set(entry.id, entry));\n let pair = null;\n report.forEach((entry) => {\n if (entry.type === 'transport' && entry.selectedCandidatePairId) pair = byId.get(entry.selectedCandidatePairId);\n });\n if (!pair) report.forEach((entry) => { if (entry.type === 'candidate-pair' && (entry.selected || entry.nominated) && entry.state === 'succeeded') pair = entry; });\n if (!pair) return;\n const local = byId.get(pair.localCandidateId);\n const remote = byId.get(pair.remoteCandidateId);\n const relay = local?.candidateType === 'relay' || remote?.candidateType === 'relay';\n const type = relay ? 'relay' : (local?.candidateType ?? 'host');\n state.route = {\n local: local?.candidateType ?? null,\n remote: remote?.candidateType ?? null,\n protocol: local?.protocol ?? null,\n relay,\n label: ROUTE_LABELS[type] ?? type,\n };\n if (typeof pair.currentRoundTripTime === 'number') state.statsRtt = Math.round(pair.currentRoundTripTime * 10000) / 10;\n emit();\n } catch { /* stats are optional */ }\n };\n\n // ---- signal handlers ----------------------------------------------------\n\n const onAck = (signal) => {\n const entry = session?.outbox.get(signal.d.of);\n if (!entry) return;\n clearTimeout(entry.timer);\n session.outbox.delete(signal.d.of);\n };\n\n const onHello = (signal) => {\n if (session && session.sid === signal.sid) return;\n // A late hello from a call the bound tab already dropped (glare, reorder).\n if (session && session.peerTab === signal.from && (signal.at ?? 0) <= session.peerAt) return;\n const glare = session?.role === 'caller' && !session.peerTab;\n // Both called at once: the lower tab id keeps calling, the other answers.\n if (glare && tabId < signal.from) return;\n if (!listening && !glare) { state.signals.ignored += 1; return; }\n // Otherwise the peer started over (reload, lost session): the new call replaces ours.\n if (session) teardown(session, { bye: !glare });\n const s = newSession('answerer', signal.sid, signal.from);\n session = s;\n bindState(s);\n if (!createPc(s)) return;\n sendSignal(s, 'ready', null);\n setPhase('negotiating');\n };\n\n const onReady = (signal) => {\n const s = session;\n if (!s || s.role !== 'caller' || s.sid !== signal.sid) return;\n if (s.peerTab && s.peerTab !== signal.from) {\n // Another tab of the peer answered later: release it.\n sendSignal(s, 'bye', null, { to: signal.from, reliable: false });\n return;\n }\n if (s.peerTab) return;\n s.peerTab = signal.from;\n state.peerTab = signal.from;\n clearInterval(s.helloTimer);\n if (!createPc(s)) return;\n setPhase('negotiating');\n void makeOffer(s);\n };\n\n const onOffer = async (signal) => {\n const s = session;\n if (!s || s.role !== 'answerer' || s.sid !== signal.sid || s.peerTab !== signal.from || signal.gen < s.gen) return;\n s.gen = signal.gen;\n state.gen = s.gen;\n try {\n await s.pc.setRemoteDescription({ type: 'offer', sdp: signal.d.sdp });\n if (s.closed) return;\n flushPendingIce(s);\n const answer = await s.pc.createAnswer();\n if (s.closed) return;\n await s.pc.setLocalDescription(answer);\n if (s.closed) return;\n sendSignal(s, 'answer', { sdp: s.pc.localDescription.sdp });\n } catch (error) {\n fail(s, `answer: ${error?.message ?? error}`);\n }\n };\n\n const onAnswer = async (signal) => {\n const s = session;\n if (!s || s.role !== 'caller' || s.sid !== signal.sid || s.peerTab !== signal.from || signal.gen !== s.gen) return;\n if (s.pc.signalingState !== 'have-local-offer') return;\n try {\n await s.pc.setRemoteDescription({ type: 'answer', sdp: signal.d.sdp });\n if (s.closed) return;\n flushPendingIce(s);\n // An ICE restart over a working path may never leave 'connected'.\n if (state.phase === 'restarting' && ['connected', 'completed'].includes(s.pc.iceConnectionState)) setPhase('connected');\n } catch (error) {\n fail(s, `answer remota: ${error?.message ?? error}`);\n }\n };\n\n const onIce = (signal) => {\n const s = session;\n if (!s || s.sid !== signal.sid || s.peerTab !== signal.from || signal.gen < s.gen) return;\n if (signal.gen > s.gen || !s.pc?.remoteDescription) {\n s.pendingIce.push({ gen: signal.gen, c: signal.d.c }); // arrived before its offer/answer\n return;\n }\n void addRemoteIce(s, signal.d.c);\n };\n\n const onBye = (signal) => {\n // The channel may close before the bye arrives through the chat: the peer left\n // on purpose, so the reconnection that started meanwhile is cancelled.\n if (signal.sid === endedSid && (reconnectTimer || (session?.role === 'caller' && !session.peerTab))) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n reconnects = 0;\n if (session) teardown(session);\n setPhase(listening ? 'listening' : 'idle', 'el rival cerró la conexión');\n return;\n }\n const s = session;\n if (!s || s.sid !== signal.sid) return;\n if (s.peerTab && s.peerTab !== signal.from) return;\n teardown(s);\n setPhase(listening ? 'listening' : 'idle', 'el rival cerró la conexión');\n };\n\n // ---- public API -------------------------------------------------------\n\n function call() {\n if (destroyed) return;\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n if (session) teardown(session, { bye: true });\n const s = newSession('caller');\n session = s;\n bindState(s);\n setPhase('calling');\n const hello = () => {\n if (s.closed || s.peerTab) return;\n if (Date.now() - s.helloStarted > HELLO_GIVE_UP_MS) {\n teardown(s);\n setPhase('failed', 'el rival no respondió');\n return;\n }\n sendSignal(s, 'hello', null, { to: null, reliable: false });\n };\n hello();\n s.helloTimer = setInterval(hello, HELLO_EVERY_MS);\n }\n\n if (listening) setPhase('listening');\n\n return {\n tabId,\n /** Starts a new session as caller (the peer must be listening). */\n connect() { reconnects = 0; call(); },\n /** Accept (true) or ignore (false) incoming calls. */\n setListening(value) {\n listening = Boolean(value);\n if (!session) setPhase(listening ? 'listening' : 'idle');\n },\n /** Latest-wins position update over the unreliable channel. */\n sendMove(payload) {\n const s = session;\n if (!s || s.move?.readyState !== 'open') return false;\n if (s.move.bufferedAmount > MAX_BUFFERED) { state.move.dropped += 1; return false; }\n const sent = sendRaw(s.move, JSON.stringify([++s.moveSeq, Date.now(), payload]));\n if (sent) { state.move.sent += 1; state.move.windowOut += 1; }\n return sent;\n },\n /** Reliable, ordered game event. */\n sendEvent(payload) {\n const sent = sendRaw(session?.events, JSON.stringify({ k: 'app', d: payload }));\n if (sent) state.events.sent += 1;\n return sent;\n },\n /** Forces an ICE restart (caller) — for tests. */\n restartIce() { if (session) restartIce(session); },\n getState: snapshot,\n /** Ends the session (bye to the peer) but keeps listening. */\n hangup() {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n if (session) teardown(session, { bye: true });\n setPhase(listening ? 'listening' : 'idle');\n },\n /** Full cleanup: session, timers and the signaling subscription. */\n destroy() {\n if (destroyed) return;\n clearTimeout(reconnectTimer);\n if (session) teardown(session, { bye: true });\n destroyed = true;\n unsubscribe?.();\n },\n };\n}\n\nexport default createPeerLink;\n"],"mappings":"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"}
|