@bloopjs/web 0.0.43 → 0.0.45
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/dist/App.d.ts +30 -1
- package/dist/App.d.ts.map +1 -1
- package/dist/mod.d.ts +1 -0
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +834 -26
- package/dist/mod.js.map +10 -5
- package/dist/netcode/broker.d.ts +12 -0
- package/dist/netcode/broker.d.ts.map +1 -0
- package/dist/netcode/logs.d.ts +37 -0
- package/dist/netcode/logs.d.ts.map +1 -0
- package/dist/netcode/mod.d.ts +7 -0
- package/dist/netcode/mod.d.ts.map +1 -0
- package/dist/netcode/protocol.d.ts +42 -0
- package/dist/netcode/protocol.d.ts.map +1 -0
- package/dist/netcode/transport.d.ts +16 -0
- package/dist/netcode/transport.d.ts.map +1 -0
- package/package.json +4 -3
- package/src/App.ts +48 -3
- package/src/mod.ts +1 -0
- package/src/netcode/broker.ts +136 -0
- package/src/netcode/logs.ts +81 -0
- package/src/netcode/mod.ts +14 -0
- package/src/netcode/protocol.ts +56 -0
- package/src/netcode/transport.ts +237 -0
package/dist/mod.js
CHANGED
|
@@ -1141,6 +1141,63 @@ var EVENTS_OFFSET = INPUT_CTX_OFFSET + 4;
|
|
|
1141
1141
|
var SNAPSHOT_HEADER_LEN = 16;
|
|
1142
1142
|
var SNAPSHOT_HEADER_USER_LEN_OFFSET = 4;
|
|
1143
1143
|
var SNAPSHOT_HEADER_ENGINE_LEN_OFFSET = 8;
|
|
1144
|
+
|
|
1145
|
+
class Net {
|
|
1146
|
+
#wasm;
|
|
1147
|
+
#memory;
|
|
1148
|
+
constructor(wasm, memory) {
|
|
1149
|
+
this.#wasm = wasm;
|
|
1150
|
+
this.#memory = memory;
|
|
1151
|
+
}
|
|
1152
|
+
setLocalPeer(peerId) {
|
|
1153
|
+
this.#wasm.session_set_local_peer(peerId);
|
|
1154
|
+
}
|
|
1155
|
+
connectPeer(peerId) {
|
|
1156
|
+
this.#wasm.session_peer_connect(peerId);
|
|
1157
|
+
}
|
|
1158
|
+
disconnectPeer(peerId) {
|
|
1159
|
+
this.#wasm.session_peer_disconnect(peerId);
|
|
1160
|
+
}
|
|
1161
|
+
getOutboundPacket(targetPeer) {
|
|
1162
|
+
this.#wasm.build_outbound_packet(targetPeer);
|
|
1163
|
+
const len = this.#wasm.get_outbound_packet_len();
|
|
1164
|
+
if (len === 0) {
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
const ptr = this.#wasm.get_outbound_packet();
|
|
1168
|
+
assert(ptr > 0, `Invalid outbound packet pointer: ${ptr}`);
|
|
1169
|
+
const memoryView = new Uint8Array(this.#memory.buffer, ptr, len);
|
|
1170
|
+
const copy = new Uint8Array(len);
|
|
1171
|
+
copy.set(memoryView);
|
|
1172
|
+
return copy;
|
|
1173
|
+
}
|
|
1174
|
+
receivePacket(data) {
|
|
1175
|
+
if (data.length === 0) {
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
const ptr = this.#wasm.alloc(data.byteLength);
|
|
1179
|
+
assert(ptr > 0, `Failed to allocate ${data.byteLength} bytes for received packet`);
|
|
1180
|
+
const memoryView = new Uint8Array(this.#memory.buffer, ptr, data.byteLength);
|
|
1181
|
+
memoryView.set(data);
|
|
1182
|
+
const result = this.#wasm.receive_packet(ptr, data.byteLength);
|
|
1183
|
+
this.#wasm.free(ptr, data.byteLength);
|
|
1184
|
+
if (result !== 0) {
|
|
1185
|
+
const errorMessages = {
|
|
1186
|
+
1: "No active session",
|
|
1187
|
+
2: "Buffer too small",
|
|
1188
|
+
3: "Unsupported packet version",
|
|
1189
|
+
4: "Invalid event count"
|
|
1190
|
+
};
|
|
1191
|
+
throw new Error(errorMessages[result] ?? `Unknown packet error: ${result}`);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
getPeerState(peer) {
|
|
1195
|
+
return {
|
|
1196
|
+
seq: this.#wasm.get_peer_seq(peer),
|
|
1197
|
+
ack: this.#wasm.get_peer_ack(peer)
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1144
1201
|
var originalConsole = globalThis.console;
|
|
1145
1202
|
var noop = () => {};
|
|
1146
1203
|
var stubConsole = Object.fromEntries(Object.keys(originalConsole).map((key) => [key, noop]));
|
|
@@ -1158,12 +1215,14 @@ class Sim {
|
|
|
1158
1215
|
#time;
|
|
1159
1216
|
#serialize;
|
|
1160
1217
|
#isPaused = false;
|
|
1218
|
+
net;
|
|
1161
1219
|
constructor(wasm, memory, opts) {
|
|
1162
1220
|
this.wasm = wasm;
|
|
1163
1221
|
this.#memory = memory;
|
|
1164
1222
|
this.#time = new TimeContext(new DataView(this.#memory.buffer, this.wasm.get_time_ctx()));
|
|
1165
1223
|
this.id = `${Math.floor(Math.random() * 1e6)}`;
|
|
1166
1224
|
this.#serialize = opts?.serialize;
|
|
1225
|
+
this.net = new Net(wasm, memory);
|
|
1167
1226
|
}
|
|
1168
1227
|
step(ms) {
|
|
1169
1228
|
if (this.#isPaused && !this.isReplaying) {
|
|
@@ -1276,23 +1335,23 @@ class Sim {
|
|
|
1276
1335
|
return this.isRecording || this.isReplaying;
|
|
1277
1336
|
}
|
|
1278
1337
|
emit = {
|
|
1279
|
-
keydown: (key,
|
|
1280
|
-
this.wasm.emit_keydown(keyToKeyCode(key),
|
|
1338
|
+
keydown: (key, peerId = 0) => {
|
|
1339
|
+
this.wasm.emit_keydown(keyToKeyCode(key), peerId);
|
|
1281
1340
|
},
|
|
1282
|
-
keyup: (key,
|
|
1283
|
-
this.wasm.emit_keyup(keyToKeyCode(key),
|
|
1341
|
+
keyup: (key, peerId = 0) => {
|
|
1342
|
+
this.wasm.emit_keyup(keyToKeyCode(key), peerId);
|
|
1284
1343
|
},
|
|
1285
|
-
mousemove: (x, y,
|
|
1286
|
-
this.wasm.emit_mousemove(x, y,
|
|
1344
|
+
mousemove: (x, y, peerId = 0) => {
|
|
1345
|
+
this.wasm.emit_mousemove(x, y, peerId);
|
|
1287
1346
|
},
|
|
1288
|
-
mousedown: (button,
|
|
1289
|
-
this.wasm.emit_mousedown(mouseButtonToMouseButtonCode(button),
|
|
1347
|
+
mousedown: (button, peerId = 0) => {
|
|
1348
|
+
this.wasm.emit_mousedown(mouseButtonToMouseButtonCode(button), peerId);
|
|
1290
1349
|
},
|
|
1291
|
-
mouseup: (button,
|
|
1292
|
-
this.wasm.emit_mouseup(mouseButtonToMouseButtonCode(button),
|
|
1350
|
+
mouseup: (button, peerId = 0) => {
|
|
1351
|
+
this.wasm.emit_mouseup(mouseButtonToMouseButtonCode(button), peerId);
|
|
1293
1352
|
},
|
|
1294
|
-
mousewheel: (x, y,
|
|
1295
|
-
this.wasm.emit_mousewheel(x, y,
|
|
1353
|
+
mousewheel: (x, y, peerId = 0) => {
|
|
1354
|
+
this.wasm.emit_mousewheel(x, y, peerId);
|
|
1296
1355
|
}
|
|
1297
1356
|
};
|
|
1298
1357
|
sessionInit(peerCount) {
|
|
@@ -1317,23 +1376,13 @@ class Sim {
|
|
|
1317
1376
|
this.wasm.session_emit_inputs(peer, matchFrame, ptr, eventCount);
|
|
1318
1377
|
this.wasm.free(ptr, events.byteLength);
|
|
1319
1378
|
}
|
|
1320
|
-
getSessionStats() {
|
|
1321
|
-
return {
|
|
1322
|
-
matchFrame: this.wasm.get_match_frame(),
|
|
1323
|
-
confirmedFrame: this.wasm.get_confirmed_frame(),
|
|
1324
|
-
rollbackDepth: this.wasm.get_rollback_depth()
|
|
1325
|
-
};
|
|
1326
|
-
}
|
|
1327
|
-
getPeerFrame(peer) {
|
|
1328
|
-
return this.wasm.get_peer_frame(peer);
|
|
1329
|
-
}
|
|
1330
1379
|
}
|
|
1331
1380
|
async function mount(opts) {
|
|
1332
1381
|
const bytes = await fetch(opts.wasmUrl ?? DEFAULT_WASM_URL).then((res) => res.arrayBuffer()).catch((e) => {
|
|
1333
1382
|
console.error(`Failed to fetch wasm at ${opts.wasmUrl ?? DEFAULT_WASM_URL}`, e);
|
|
1334
1383
|
throw e;
|
|
1335
1384
|
});
|
|
1336
|
-
const memory = new WebAssembly.Memory({ initial:
|
|
1385
|
+
const memory = new WebAssembly.Memory({ initial: 30, maximum: 1000 });
|
|
1337
1386
|
const wasmInstantiatedSource = await WebAssembly.instantiate(bytes, {
|
|
1338
1387
|
env: {
|
|
1339
1388
|
memory,
|
|
@@ -2529,7 +2578,751 @@ var TIME_CTX_OFFSET2 = 0;
|
|
|
2529
2578
|
var INPUT_CTX_OFFSET2 = TIME_CTX_OFFSET2 + 4;
|
|
2530
2579
|
var EVENTS_OFFSET2 = INPUT_CTX_OFFSET2 + 4;
|
|
2531
2580
|
|
|
2581
|
+
// ../../node_modules/partysocket/dist/chunk-V6LO7DXK.mjs
|
|
2582
|
+
if (!globalThis.EventTarget || !globalThis.Event) {
|
|
2583
|
+
console.error(`
|
|
2584
|
+
PartySocket requires a global 'EventTarget' class to be available!
|
|
2585
|
+
You can polyfill this global by adding this to your code before any partysocket imports:
|
|
2586
|
+
|
|
2587
|
+
\`\`\`
|
|
2588
|
+
import 'partysocket/event-target-polyfill';
|
|
2589
|
+
\`\`\`
|
|
2590
|
+
Please file an issue at https://github.com/partykit/partykit if you're still having trouble.
|
|
2591
|
+
`);
|
|
2592
|
+
}
|
|
2593
|
+
var ErrorEvent = class extends Event {
|
|
2594
|
+
message;
|
|
2595
|
+
error;
|
|
2596
|
+
constructor(error, target) {
|
|
2597
|
+
super("error", target);
|
|
2598
|
+
this.message = error.message;
|
|
2599
|
+
this.error = error;
|
|
2600
|
+
}
|
|
2601
|
+
};
|
|
2602
|
+
var CloseEvent = class extends Event {
|
|
2603
|
+
code;
|
|
2604
|
+
reason;
|
|
2605
|
+
wasClean = true;
|
|
2606
|
+
constructor(code = 1000, reason = "", target) {
|
|
2607
|
+
super("close", target);
|
|
2608
|
+
this.code = code;
|
|
2609
|
+
this.reason = reason;
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
var Events = {
|
|
2613
|
+
Event,
|
|
2614
|
+
ErrorEvent,
|
|
2615
|
+
CloseEvent
|
|
2616
|
+
};
|
|
2617
|
+
function assert2(condition, msg) {
|
|
2618
|
+
if (!condition) {
|
|
2619
|
+
throw new Error(msg);
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
function cloneEventBrowser(e) {
|
|
2623
|
+
return new e.constructor(e.type, e);
|
|
2624
|
+
}
|
|
2625
|
+
function cloneEventNode(e) {
|
|
2626
|
+
if ("data" in e) {
|
|
2627
|
+
const evt2 = new MessageEvent(e.type, e);
|
|
2628
|
+
return evt2;
|
|
2629
|
+
}
|
|
2630
|
+
if ("code" in e || "reason" in e) {
|
|
2631
|
+
const evt2 = new CloseEvent(e.code || 1999, e.reason || "unknown reason", e);
|
|
2632
|
+
return evt2;
|
|
2633
|
+
}
|
|
2634
|
+
if ("error" in e) {
|
|
2635
|
+
const evt2 = new ErrorEvent(e.error, e);
|
|
2636
|
+
return evt2;
|
|
2637
|
+
}
|
|
2638
|
+
const evt = new Event(e.type, e);
|
|
2639
|
+
return evt;
|
|
2640
|
+
}
|
|
2641
|
+
var _a;
|
|
2642
|
+
var isNode = typeof process !== "undefined" && typeof ((_a = process.versions) == null ? undefined : _a.node) !== "undefined" && typeof document === "undefined";
|
|
2643
|
+
var cloneEvent = isNode ? cloneEventNode : cloneEventBrowser;
|
|
2644
|
+
var DEFAULT = {
|
|
2645
|
+
maxReconnectionDelay: 1e4,
|
|
2646
|
+
minReconnectionDelay: 1000 + Math.random() * 4000,
|
|
2647
|
+
minUptime: 5000,
|
|
2648
|
+
reconnectionDelayGrowFactor: 1.3,
|
|
2649
|
+
connectionTimeout: 4000,
|
|
2650
|
+
maxRetries: Number.POSITIVE_INFINITY,
|
|
2651
|
+
maxEnqueuedMessages: Number.POSITIVE_INFINITY,
|
|
2652
|
+
startClosed: false,
|
|
2653
|
+
debug: false
|
|
2654
|
+
};
|
|
2655
|
+
var didWarnAboutMissingWebSocket = false;
|
|
2656
|
+
var ReconnectingWebSocket = class _ReconnectingWebSocket extends EventTarget {
|
|
2657
|
+
_ws;
|
|
2658
|
+
_retryCount = -1;
|
|
2659
|
+
_uptimeTimeout;
|
|
2660
|
+
_connectTimeout;
|
|
2661
|
+
_shouldReconnect = true;
|
|
2662
|
+
_connectLock = false;
|
|
2663
|
+
_binaryType = "blob";
|
|
2664
|
+
_closeCalled = false;
|
|
2665
|
+
_messageQueue = [];
|
|
2666
|
+
_debugLogger = console.log.bind(console);
|
|
2667
|
+
_url;
|
|
2668
|
+
_protocols;
|
|
2669
|
+
_options;
|
|
2670
|
+
constructor(url, protocols, options = {}) {
|
|
2671
|
+
super();
|
|
2672
|
+
this._url = url;
|
|
2673
|
+
this._protocols = protocols;
|
|
2674
|
+
this._options = options;
|
|
2675
|
+
if (this._options.startClosed) {
|
|
2676
|
+
this._shouldReconnect = false;
|
|
2677
|
+
}
|
|
2678
|
+
if (this._options.debugLogger) {
|
|
2679
|
+
this._debugLogger = this._options.debugLogger;
|
|
2680
|
+
}
|
|
2681
|
+
this._connect();
|
|
2682
|
+
}
|
|
2683
|
+
static get CONNECTING() {
|
|
2684
|
+
return 0;
|
|
2685
|
+
}
|
|
2686
|
+
static get OPEN() {
|
|
2687
|
+
return 1;
|
|
2688
|
+
}
|
|
2689
|
+
static get CLOSING() {
|
|
2690
|
+
return 2;
|
|
2691
|
+
}
|
|
2692
|
+
static get CLOSED() {
|
|
2693
|
+
return 3;
|
|
2694
|
+
}
|
|
2695
|
+
get CONNECTING() {
|
|
2696
|
+
return _ReconnectingWebSocket.CONNECTING;
|
|
2697
|
+
}
|
|
2698
|
+
get OPEN() {
|
|
2699
|
+
return _ReconnectingWebSocket.OPEN;
|
|
2700
|
+
}
|
|
2701
|
+
get CLOSING() {
|
|
2702
|
+
return _ReconnectingWebSocket.CLOSING;
|
|
2703
|
+
}
|
|
2704
|
+
get CLOSED() {
|
|
2705
|
+
return _ReconnectingWebSocket.CLOSED;
|
|
2706
|
+
}
|
|
2707
|
+
get binaryType() {
|
|
2708
|
+
return this._ws ? this._ws.binaryType : this._binaryType;
|
|
2709
|
+
}
|
|
2710
|
+
set binaryType(value) {
|
|
2711
|
+
this._binaryType = value;
|
|
2712
|
+
if (this._ws) {
|
|
2713
|
+
this._ws.binaryType = value;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
get retryCount() {
|
|
2717
|
+
return Math.max(this._retryCount, 0);
|
|
2718
|
+
}
|
|
2719
|
+
get bufferedAmount() {
|
|
2720
|
+
const bytes = this._messageQueue.reduce((acc, message) => {
|
|
2721
|
+
if (typeof message === "string") {
|
|
2722
|
+
acc += message.length;
|
|
2723
|
+
} else if (message instanceof Blob) {
|
|
2724
|
+
acc += message.size;
|
|
2725
|
+
} else {
|
|
2726
|
+
acc += message.byteLength;
|
|
2727
|
+
}
|
|
2728
|
+
return acc;
|
|
2729
|
+
}, 0);
|
|
2730
|
+
return bytes + (this._ws ? this._ws.bufferedAmount : 0);
|
|
2731
|
+
}
|
|
2732
|
+
get extensions() {
|
|
2733
|
+
return this._ws ? this._ws.extensions : "";
|
|
2734
|
+
}
|
|
2735
|
+
get protocol() {
|
|
2736
|
+
return this._ws ? this._ws.protocol : "";
|
|
2737
|
+
}
|
|
2738
|
+
get readyState() {
|
|
2739
|
+
if (this._ws) {
|
|
2740
|
+
return this._ws.readyState;
|
|
2741
|
+
}
|
|
2742
|
+
return this._options.startClosed ? _ReconnectingWebSocket.CLOSED : _ReconnectingWebSocket.CONNECTING;
|
|
2743
|
+
}
|
|
2744
|
+
get url() {
|
|
2745
|
+
return this._ws ? this._ws.url : "";
|
|
2746
|
+
}
|
|
2747
|
+
get shouldReconnect() {
|
|
2748
|
+
return this._shouldReconnect;
|
|
2749
|
+
}
|
|
2750
|
+
onclose = null;
|
|
2751
|
+
onerror = null;
|
|
2752
|
+
onmessage = null;
|
|
2753
|
+
onopen = null;
|
|
2754
|
+
close(code = 1000, reason) {
|
|
2755
|
+
this._closeCalled = true;
|
|
2756
|
+
this._shouldReconnect = false;
|
|
2757
|
+
this._clearTimeouts();
|
|
2758
|
+
if (!this._ws) {
|
|
2759
|
+
this._debug("close enqueued: no ws instance");
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
if (this._ws.readyState === this.CLOSED) {
|
|
2763
|
+
this._debug("close: already closed");
|
|
2764
|
+
return;
|
|
2765
|
+
}
|
|
2766
|
+
this._ws.close(code, reason);
|
|
2767
|
+
}
|
|
2768
|
+
reconnect(code, reason) {
|
|
2769
|
+
this._shouldReconnect = true;
|
|
2770
|
+
this._closeCalled = false;
|
|
2771
|
+
this._retryCount = -1;
|
|
2772
|
+
if (!this._ws || this._ws.readyState === this.CLOSED) {
|
|
2773
|
+
this._connect();
|
|
2774
|
+
} else {
|
|
2775
|
+
this._disconnect(code, reason);
|
|
2776
|
+
this._connect();
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
send(data) {
|
|
2780
|
+
if (this._ws && this._ws.readyState === this.OPEN) {
|
|
2781
|
+
this._debug("send", data);
|
|
2782
|
+
this._ws.send(data);
|
|
2783
|
+
} else {
|
|
2784
|
+
const { maxEnqueuedMessages = DEFAULT.maxEnqueuedMessages } = this._options;
|
|
2785
|
+
if (this._messageQueue.length < maxEnqueuedMessages) {
|
|
2786
|
+
this._debug("enqueue", data);
|
|
2787
|
+
this._messageQueue.push(data);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
_debug(...args) {
|
|
2792
|
+
if (this._options.debug) {
|
|
2793
|
+
this._debugLogger("RWS>", ...args);
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
_getNextDelay() {
|
|
2797
|
+
const {
|
|
2798
|
+
reconnectionDelayGrowFactor = DEFAULT.reconnectionDelayGrowFactor,
|
|
2799
|
+
minReconnectionDelay = DEFAULT.minReconnectionDelay,
|
|
2800
|
+
maxReconnectionDelay = DEFAULT.maxReconnectionDelay
|
|
2801
|
+
} = this._options;
|
|
2802
|
+
let delay = 0;
|
|
2803
|
+
if (this._retryCount > 0) {
|
|
2804
|
+
delay = minReconnectionDelay * reconnectionDelayGrowFactor ** (this._retryCount - 1);
|
|
2805
|
+
if (delay > maxReconnectionDelay) {
|
|
2806
|
+
delay = maxReconnectionDelay;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
this._debug("next delay", delay);
|
|
2810
|
+
return delay;
|
|
2811
|
+
}
|
|
2812
|
+
_wait() {
|
|
2813
|
+
return new Promise((resolve) => {
|
|
2814
|
+
setTimeout(resolve, this._getNextDelay());
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2817
|
+
_getNextProtocols(protocolsProvider) {
|
|
2818
|
+
if (!protocolsProvider)
|
|
2819
|
+
return Promise.resolve(null);
|
|
2820
|
+
if (typeof protocolsProvider === "string" || Array.isArray(protocolsProvider)) {
|
|
2821
|
+
return Promise.resolve(protocolsProvider);
|
|
2822
|
+
}
|
|
2823
|
+
if (typeof protocolsProvider === "function") {
|
|
2824
|
+
const protocols = protocolsProvider();
|
|
2825
|
+
if (!protocols)
|
|
2826
|
+
return Promise.resolve(null);
|
|
2827
|
+
if (typeof protocols === "string" || Array.isArray(protocols)) {
|
|
2828
|
+
return Promise.resolve(protocols);
|
|
2829
|
+
}
|
|
2830
|
+
if (protocols.then) {
|
|
2831
|
+
return protocols;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
throw Error("Invalid protocols");
|
|
2835
|
+
}
|
|
2836
|
+
_getNextUrl(urlProvider) {
|
|
2837
|
+
if (typeof urlProvider === "string") {
|
|
2838
|
+
return Promise.resolve(urlProvider);
|
|
2839
|
+
}
|
|
2840
|
+
if (typeof urlProvider === "function") {
|
|
2841
|
+
const url = urlProvider();
|
|
2842
|
+
if (typeof url === "string") {
|
|
2843
|
+
return Promise.resolve(url);
|
|
2844
|
+
}
|
|
2845
|
+
if (url.then) {
|
|
2846
|
+
return url;
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
throw Error("Invalid URL");
|
|
2850
|
+
}
|
|
2851
|
+
_connect() {
|
|
2852
|
+
if (this._connectLock || !this._shouldReconnect) {
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
this._connectLock = true;
|
|
2856
|
+
const {
|
|
2857
|
+
maxRetries = DEFAULT.maxRetries,
|
|
2858
|
+
connectionTimeout = DEFAULT.connectionTimeout
|
|
2859
|
+
} = this._options;
|
|
2860
|
+
if (this._retryCount >= maxRetries) {
|
|
2861
|
+
this._debug("max retries reached", this._retryCount, ">=", maxRetries);
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
this._retryCount++;
|
|
2865
|
+
this._debug("connect", this._retryCount);
|
|
2866
|
+
this._removeListeners();
|
|
2867
|
+
this._wait().then(() => Promise.all([
|
|
2868
|
+
this._getNextUrl(this._url),
|
|
2869
|
+
this._getNextProtocols(this._protocols || null)
|
|
2870
|
+
])).then(([url, protocols]) => {
|
|
2871
|
+
if (this._closeCalled) {
|
|
2872
|
+
this._connectLock = false;
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2875
|
+
if (!this._options.WebSocket && typeof WebSocket === "undefined" && !didWarnAboutMissingWebSocket) {
|
|
2876
|
+
console.error(`‼️ No WebSocket implementation available. You should define options.WebSocket.
|
|
2877
|
+
|
|
2878
|
+
For example, if you're using node.js, run \`npm install ws\`, and then in your code:
|
|
2879
|
+
|
|
2880
|
+
import PartySocket from 'partysocket';
|
|
2881
|
+
import WS from 'ws';
|
|
2882
|
+
|
|
2883
|
+
const partysocket = new PartySocket({
|
|
2884
|
+
host: "127.0.0.1:1999",
|
|
2885
|
+
room: "test-room",
|
|
2886
|
+
WebSocket: WS
|
|
2887
|
+
});
|
|
2888
|
+
|
|
2889
|
+
`);
|
|
2890
|
+
didWarnAboutMissingWebSocket = true;
|
|
2891
|
+
}
|
|
2892
|
+
const WS = this._options.WebSocket || WebSocket;
|
|
2893
|
+
this._debug("connect", { url, protocols });
|
|
2894
|
+
this._ws = protocols ? new WS(url, protocols) : new WS(url);
|
|
2895
|
+
this._ws.binaryType = this._binaryType;
|
|
2896
|
+
this._connectLock = false;
|
|
2897
|
+
this._addListeners();
|
|
2898
|
+
this._connectTimeout = setTimeout(() => this._handleTimeout(), connectionTimeout);
|
|
2899
|
+
}).catch((err) => {
|
|
2900
|
+
this._connectLock = false;
|
|
2901
|
+
this._handleError(new Events.ErrorEvent(Error(err.message), this));
|
|
2902
|
+
});
|
|
2903
|
+
}
|
|
2904
|
+
_handleTimeout() {
|
|
2905
|
+
this._debug("timeout event");
|
|
2906
|
+
this._handleError(new Events.ErrorEvent(Error("TIMEOUT"), this));
|
|
2907
|
+
}
|
|
2908
|
+
_disconnect(code = 1000, reason) {
|
|
2909
|
+
this._clearTimeouts();
|
|
2910
|
+
if (!this._ws) {
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
this._removeListeners();
|
|
2914
|
+
try {
|
|
2915
|
+
if (this._ws.readyState === this.OPEN || this._ws.readyState === this.CONNECTING) {
|
|
2916
|
+
this._ws.close(code, reason);
|
|
2917
|
+
}
|
|
2918
|
+
this._handleClose(new Events.CloseEvent(code, reason, this));
|
|
2919
|
+
} catch (_error) {}
|
|
2920
|
+
}
|
|
2921
|
+
_acceptOpen() {
|
|
2922
|
+
this._debug("accept open");
|
|
2923
|
+
this._retryCount = 0;
|
|
2924
|
+
}
|
|
2925
|
+
_handleOpen = (event) => {
|
|
2926
|
+
this._debug("open event");
|
|
2927
|
+
const { minUptime = DEFAULT.minUptime } = this._options;
|
|
2928
|
+
clearTimeout(this._connectTimeout);
|
|
2929
|
+
this._uptimeTimeout = setTimeout(() => this._acceptOpen(), minUptime);
|
|
2930
|
+
assert2(this._ws, "WebSocket is not defined");
|
|
2931
|
+
this._ws.binaryType = this._binaryType;
|
|
2932
|
+
this._messageQueue.forEach((message) => {
|
|
2933
|
+
var _a2;
|
|
2934
|
+
(_a2 = this._ws) == null || _a2.send(message);
|
|
2935
|
+
});
|
|
2936
|
+
this._messageQueue = [];
|
|
2937
|
+
if (this.onopen) {
|
|
2938
|
+
this.onopen(event);
|
|
2939
|
+
}
|
|
2940
|
+
this.dispatchEvent(cloneEvent(event));
|
|
2941
|
+
};
|
|
2942
|
+
_handleMessage = (event) => {
|
|
2943
|
+
this._debug("message event");
|
|
2944
|
+
if (this.onmessage) {
|
|
2945
|
+
this.onmessage(event);
|
|
2946
|
+
}
|
|
2947
|
+
this.dispatchEvent(cloneEvent(event));
|
|
2948
|
+
};
|
|
2949
|
+
_handleError = (event) => {
|
|
2950
|
+
this._debug("error event", event.message);
|
|
2951
|
+
this._disconnect(undefined, event.message === "TIMEOUT" ? "timeout" : undefined);
|
|
2952
|
+
if (this.onerror) {
|
|
2953
|
+
this.onerror(event);
|
|
2954
|
+
}
|
|
2955
|
+
this._debug("exec error listeners");
|
|
2956
|
+
this.dispatchEvent(cloneEvent(event));
|
|
2957
|
+
this._connect();
|
|
2958
|
+
};
|
|
2959
|
+
_handleClose = (event) => {
|
|
2960
|
+
this._debug("close event");
|
|
2961
|
+
this._clearTimeouts();
|
|
2962
|
+
if (this._shouldReconnect) {
|
|
2963
|
+
this._connect();
|
|
2964
|
+
}
|
|
2965
|
+
if (this.onclose) {
|
|
2966
|
+
this.onclose(event);
|
|
2967
|
+
}
|
|
2968
|
+
this.dispatchEvent(cloneEvent(event));
|
|
2969
|
+
};
|
|
2970
|
+
_removeListeners() {
|
|
2971
|
+
if (!this._ws) {
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
this._debug("removeListeners");
|
|
2975
|
+
this._ws.removeEventListener("open", this._handleOpen);
|
|
2976
|
+
this._ws.removeEventListener("close", this._handleClose);
|
|
2977
|
+
this._ws.removeEventListener("message", this._handleMessage);
|
|
2978
|
+
this._ws.removeEventListener("error", this._handleError);
|
|
2979
|
+
}
|
|
2980
|
+
_addListeners() {
|
|
2981
|
+
if (!this._ws) {
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
this._debug("addListeners");
|
|
2985
|
+
this._ws.addEventListener("open", this._handleOpen);
|
|
2986
|
+
this._ws.addEventListener("close", this._handleClose);
|
|
2987
|
+
this._ws.addEventListener("message", this._handleMessage);
|
|
2988
|
+
this._ws.addEventListener("error", this._handleError);
|
|
2989
|
+
}
|
|
2990
|
+
_clearTimeouts() {
|
|
2991
|
+
clearTimeout(this._connectTimeout);
|
|
2992
|
+
clearTimeout(this._uptimeTimeout);
|
|
2993
|
+
}
|
|
2994
|
+
};
|
|
2995
|
+
/*!
|
|
2996
|
+
* Reconnecting WebSocket
|
|
2997
|
+
* by Pedro Ladaria <pedro.ladaria@gmail.com>
|
|
2998
|
+
* https://github.com/pladaria/reconnecting-websocket
|
|
2999
|
+
* License MIT
|
|
3000
|
+
*/
|
|
3001
|
+
|
|
3002
|
+
// src/netcode/logs.ts
|
|
3003
|
+
var logger = {
|
|
3004
|
+
onLog: null,
|
|
3005
|
+
matchFrame: -1,
|
|
3006
|
+
frameNumber: -1,
|
|
3007
|
+
log(opts) {
|
|
3008
|
+
this.onLog?.({
|
|
3009
|
+
...opts,
|
|
3010
|
+
frame_number: this.frameNumber,
|
|
3011
|
+
match_frame: this.matchFrame >= 0 ? this.matchFrame : null,
|
|
3012
|
+
timestamp: Date.now(),
|
|
3013
|
+
severity: "log"
|
|
3014
|
+
});
|
|
3015
|
+
},
|
|
3016
|
+
warn(opts) {
|
|
3017
|
+
this.onLog?.({
|
|
3018
|
+
...opts,
|
|
3019
|
+
frame_number: this.frameNumber,
|
|
3020
|
+
match_frame: this.matchFrame >= 0 ? this.matchFrame : null,
|
|
3021
|
+
timestamp: Date.now(),
|
|
3022
|
+
severity: "warn"
|
|
3023
|
+
});
|
|
3024
|
+
},
|
|
3025
|
+
error(opts) {
|
|
3026
|
+
this.onLog?.({
|
|
3027
|
+
...opts,
|
|
3028
|
+
frame_number: this.frameNumber,
|
|
3029
|
+
match_frame: this.matchFrame >= 0 ? this.matchFrame : null,
|
|
3030
|
+
timestamp: Date.now(),
|
|
3031
|
+
severity: "error"
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
};
|
|
3035
|
+
|
|
3036
|
+
// src/netcode/transport.ts
|
|
3037
|
+
var iceServers = [
|
|
3038
|
+
{ urls: "stun:stun.cloudflare.com:3478" }
|
|
3039
|
+
];
|
|
3040
|
+
async function connect(ws, peerId, timeoutMs = 1e4) {
|
|
3041
|
+
const pc = new RTCPeerConnection({ iceServers });
|
|
3042
|
+
const reliable = pc.createDataChannel("reliable", {});
|
|
3043
|
+
const unreliable = pc.createDataChannel("unreliable", {
|
|
3044
|
+
ordered: false,
|
|
3045
|
+
maxRetransmits: 0
|
|
3046
|
+
});
|
|
3047
|
+
const offer = await pc.createOffer();
|
|
3048
|
+
await pc.setLocalDescription(offer);
|
|
3049
|
+
logger.log({ source: "webrtc", label: "set offer", json: offer });
|
|
3050
|
+
await gatherIce(pc, timeoutMs);
|
|
3051
|
+
logger.log({ source: "webrtc", label: "gathered ICE candidates" });
|
|
3052
|
+
logger.log({
|
|
3053
|
+
source: "ws",
|
|
3054
|
+
label: "sending local description",
|
|
3055
|
+
json: pc.localDescription,
|
|
3056
|
+
to: peerId
|
|
3057
|
+
});
|
|
3058
|
+
send(ws, {
|
|
3059
|
+
type: "offer",
|
|
3060
|
+
payload: btoa(JSON.stringify(pc.localDescription)),
|
|
3061
|
+
target: peerId
|
|
3062
|
+
});
|
|
3063
|
+
await waitForAnswer(ws, pc, timeoutMs);
|
|
3064
|
+
return {
|
|
3065
|
+
peerConnection: pc,
|
|
3066
|
+
reliable,
|
|
3067
|
+
unreliable,
|
|
3068
|
+
peerId
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
async function logErrors(dc) {
|
|
3072
|
+
dc.onerror = (event) => {
|
|
3073
|
+
logger.error({
|
|
3074
|
+
source: "webrtc",
|
|
3075
|
+
label: `error on ${dc.label} channel`,
|
|
3076
|
+
json: event.error
|
|
3077
|
+
});
|
|
3078
|
+
};
|
|
3079
|
+
dc.onclosing = (_event) => {
|
|
3080
|
+
logger.log({
|
|
3081
|
+
source: "webrtc",
|
|
3082
|
+
label: `closing ${dc.label} channel`
|
|
3083
|
+
});
|
|
3084
|
+
};
|
|
3085
|
+
dc.onopen = (_event) => {
|
|
3086
|
+
logger.log({
|
|
3087
|
+
source: "webrtc",
|
|
3088
|
+
label: `opened ${dc.label} channel`
|
|
3089
|
+
});
|
|
3090
|
+
};
|
|
3091
|
+
dc.onclose = (_event) => {
|
|
3092
|
+
logger.log({
|
|
3093
|
+
source: "webrtc",
|
|
3094
|
+
label: `closed ${dc.label} channel`
|
|
3095
|
+
});
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
async function logPeerConnection(pc, peerId) {
|
|
3099
|
+
pc.onconnectionstatechange = () => {
|
|
3100
|
+
logger.log({
|
|
3101
|
+
source: "webrtc",
|
|
3102
|
+
label: `[${peerId.substring(0, 6)}] connectionState = ${pc.connectionState}`
|
|
3103
|
+
});
|
|
3104
|
+
};
|
|
3105
|
+
pc.onsignalingstatechange = () => {
|
|
3106
|
+
logger.log({
|
|
3107
|
+
source: "webrtc",
|
|
3108
|
+
label: `[${peerId.substring(0, 6)}] signalingState = ${pc.signalingState}`
|
|
3109
|
+
});
|
|
3110
|
+
};
|
|
3111
|
+
}
|
|
3112
|
+
async function gatherIce(pc, timeoutMs) {
|
|
3113
|
+
return new Promise((yes, no) => {
|
|
3114
|
+
setTimeout(() => no(new Error("Timed out waiting for completion")), timeoutMs);
|
|
3115
|
+
pc.onicegatheringstatechange = () => {
|
|
3116
|
+
logger.log({
|
|
3117
|
+
source: "webrtc",
|
|
3118
|
+
label: `icegatheringstatechange: ${pc.iceGatheringState}`
|
|
3119
|
+
});
|
|
3120
|
+
if (pc.iceGatheringState === "complete") {
|
|
3121
|
+
yes(true);
|
|
3122
|
+
}
|
|
3123
|
+
};
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
async function waitForAnswer(ws, pc, timeoutMs) {
|
|
3127
|
+
return new Promise((yes, no) => {
|
|
3128
|
+
const timeoutId = setTimeout(no, timeoutMs);
|
|
3129
|
+
const messageListener = async (event) => {
|
|
3130
|
+
const serverMsg = JSON.parse(event.data);
|
|
3131
|
+
if (serverMsg.type !== "message:json") {
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
const peerMsg = serverMsg.message;
|
|
3135
|
+
if (peerMsg.type !== "answer") {
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
const answerDesc = JSON.parse(atob(peerMsg.payload));
|
|
3139
|
+
logger.log({
|
|
3140
|
+
source: "webrtc",
|
|
3141
|
+
label: "received answer",
|
|
3142
|
+
json: answerDesc
|
|
3143
|
+
});
|
|
3144
|
+
await pc.setRemoteDescription(new RTCSessionDescription(answerDesc));
|
|
3145
|
+
logger.log({
|
|
3146
|
+
source: "webrtc",
|
|
3147
|
+
label: "set remote description with answer"
|
|
3148
|
+
});
|
|
3149
|
+
clearTimeout(timeoutId);
|
|
3150
|
+
yes();
|
|
3151
|
+
};
|
|
3152
|
+
ws.addEventListener("message", messageListener);
|
|
3153
|
+
});
|
|
3154
|
+
}
|
|
3155
|
+
function listenForOffers(ws, cb) {
|
|
3156
|
+
const messageListener = async (event) => {
|
|
3157
|
+
const envelope = JSON.parse(event.data);
|
|
3158
|
+
if (envelope.type !== "message:json") {
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
const msg = envelope.message;
|
|
3162
|
+
if (msg.type !== "offer") {
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
3165
|
+
logger.log({ source: "webrtc", label: "received offer" });
|
|
3166
|
+
const offer = JSON.parse(atob(msg.payload));
|
|
3167
|
+
const pc = new RTCPeerConnection({ iceServers });
|
|
3168
|
+
await pc.setRemoteDescription(offer);
|
|
3169
|
+
logger.log({
|
|
3170
|
+
source: "webrtc",
|
|
3171
|
+
label: "set remote description",
|
|
3172
|
+
json: { offer, remoteDescription: pc.remoteDescription }
|
|
3173
|
+
});
|
|
3174
|
+
const answer = await pc.createAnswer();
|
|
3175
|
+
await pc.setLocalDescription(answer);
|
|
3176
|
+
logger.log({
|
|
3177
|
+
source: "webrtc",
|
|
3178
|
+
label: "set local description",
|
|
3179
|
+
json: pc.localDescription
|
|
3180
|
+
});
|
|
3181
|
+
await gatherIce(pc, 1e4);
|
|
3182
|
+
const channels = {
|
|
3183
|
+
reliable: null,
|
|
3184
|
+
unreliable: null
|
|
3185
|
+
};
|
|
3186
|
+
pc.ondatachannel = (event2) => {
|
|
3187
|
+
logger.log({
|
|
3188
|
+
source: "webrtc",
|
|
3189
|
+
label: `received datachannel ${event2.channel.label}`
|
|
3190
|
+
});
|
|
3191
|
+
switch (event2.channel.label) {
|
|
3192
|
+
case "reliable":
|
|
3193
|
+
channels.reliable = event2.channel;
|
|
3194
|
+
break;
|
|
3195
|
+
case "unreliable":
|
|
3196
|
+
channels.unreliable = event2.channel;
|
|
3197
|
+
break;
|
|
3198
|
+
}
|
|
3199
|
+
if (channels.reliable && channels.unreliable) {
|
|
3200
|
+
pc.ondatachannel = null;
|
|
3201
|
+
cb({
|
|
3202
|
+
peerConnection: pc,
|
|
3203
|
+
reliable: channels.reliable,
|
|
3204
|
+
unreliable: channels.unreliable,
|
|
3205
|
+
peerId: envelope.peerId
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
3208
|
+
};
|
|
3209
|
+
logger.log({
|
|
3210
|
+
source: "webrtc",
|
|
3211
|
+
label: "sending answer",
|
|
3212
|
+
json: pc.localDescription
|
|
3213
|
+
});
|
|
3214
|
+
send(ws, {
|
|
3215
|
+
type: "answer",
|
|
3216
|
+
payload: btoa(JSON.stringify(pc.localDescription)),
|
|
3217
|
+
target: envelope.peerId
|
|
3218
|
+
});
|
|
3219
|
+
};
|
|
3220
|
+
ws.addEventListener("message", messageListener);
|
|
3221
|
+
}
|
|
3222
|
+
function send(ws, msg) {
|
|
3223
|
+
ws.send(JSON.stringify(msg));
|
|
3224
|
+
logger.log({ source: "ws", direction: "outbound", json: msg });
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
// src/netcode/broker.ts
|
|
3228
|
+
function joinRoom(brokerUrl, _roomId, cbs) {
|
|
3229
|
+
const broker = new ReconnectingWebSocket(brokerUrl);
|
|
3230
|
+
broker.addEventListener("open", () => {
|
|
3231
|
+
logger.log({
|
|
3232
|
+
source: "ws",
|
|
3233
|
+
label: "Connection opened"
|
|
3234
|
+
});
|
|
3235
|
+
});
|
|
3236
|
+
broker.addEventListener("close", (event) => {
|
|
3237
|
+
logger.warn({
|
|
3238
|
+
source: "ws",
|
|
3239
|
+
label: "Connection closed",
|
|
3240
|
+
json: event
|
|
3241
|
+
});
|
|
3242
|
+
});
|
|
3243
|
+
broker.addEventListener("error", (event) => {
|
|
3244
|
+
logger.error({
|
|
3245
|
+
source: "ws",
|
|
3246
|
+
label: "Connection error",
|
|
3247
|
+
json: event
|
|
3248
|
+
});
|
|
3249
|
+
});
|
|
3250
|
+
const pipes = new Map;
|
|
3251
|
+
let ourId = "";
|
|
3252
|
+
broker.addEventListener("message", async (event) => {
|
|
3253
|
+
try {
|
|
3254
|
+
const envelope = JSON.parse(event.data);
|
|
3255
|
+
logger.log({
|
|
3256
|
+
source: "ws",
|
|
3257
|
+
direction: "inbound",
|
|
3258
|
+
json: envelope
|
|
3259
|
+
});
|
|
3260
|
+
switch (envelope.type) {
|
|
3261
|
+
case "welcome":
|
|
3262
|
+
ourId = envelope.yourId;
|
|
3263
|
+
cbs.onPeerIdAssign(envelope.yourId);
|
|
3264
|
+
for (const peerId of envelope.peerIds) {
|
|
3265
|
+
if (peerId === ourId)
|
|
3266
|
+
continue;
|
|
3267
|
+
cbs.onPeerConnected(peerId);
|
|
3268
|
+
}
|
|
3269
|
+
break;
|
|
3270
|
+
case "message:json":
|
|
3271
|
+
break;
|
|
3272
|
+
case "peer:connect": {
|
|
3273
|
+
const pipe = await connect(broker, envelope.peerId);
|
|
3274
|
+
registerPipe(pipe, cbs);
|
|
3275
|
+
cbs.onPeerConnected(envelope.peerId);
|
|
3276
|
+
break;
|
|
3277
|
+
}
|
|
3278
|
+
case "peer:disconnect":
|
|
3279
|
+
cbs.onPeerDisconnected(envelope.peerId);
|
|
3280
|
+
break;
|
|
3281
|
+
default:
|
|
3282
|
+
logger.warn({
|
|
3283
|
+
source: "ws",
|
|
3284
|
+
label: `Unknown message type: ${envelope.type}`,
|
|
3285
|
+
json: envelope
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
} catch (e) {
|
|
3289
|
+
logger.error({
|
|
3290
|
+
source: "ws",
|
|
3291
|
+
label: "Failed to parse json",
|
|
3292
|
+
json: {
|
|
3293
|
+
data: event.data,
|
|
3294
|
+
error: e
|
|
3295
|
+
}
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
});
|
|
3299
|
+
listenForOffers(broker, (pipe) => {
|
|
3300
|
+
registerPipe(pipe, cbs);
|
|
3301
|
+
});
|
|
3302
|
+
function registerPipe(pipe, cbs2) {
|
|
3303
|
+
logErrors(pipe.reliable);
|
|
3304
|
+
logErrors(pipe.unreliable);
|
|
3305
|
+
logPeerConnection(pipe.peerConnection, ourId);
|
|
3306
|
+
cbs2.onDataChannelOpen(pipe.peerId, true, pipe.reliable);
|
|
3307
|
+
cbs2.onDataChannelOpen(pipe.peerId, false, pipe.unreliable);
|
|
3308
|
+
pipe.reliable.onmessage = (event) => {
|
|
3309
|
+
cbs2.onMessage(pipe.peerId, event.data, true);
|
|
3310
|
+
};
|
|
3311
|
+
pipe.reliable.onclose = () => {
|
|
3312
|
+
cbs2.onDataChannelClose(pipe.peerId, true);
|
|
3313
|
+
};
|
|
3314
|
+
pipe.unreliable.onmessage = (event) => {
|
|
3315
|
+
cbs2.onMessage(pipe.peerId, event.data, false);
|
|
3316
|
+
};
|
|
3317
|
+
pipe.unreliable.onclose = () => {
|
|
3318
|
+
cbs2.onDataChannelClose(pipe.peerId, false);
|
|
3319
|
+
};
|
|
3320
|
+
pipes.set(pipe.peerId, pipe);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
|
|
2532
3324
|
// src/App.ts
|
|
3325
|
+
var DEFAULT_BROKER_URL = "wss://webrtc-divine-glade-8064.fly.dev/ws";
|
|
2533
3326
|
async function start(opts) {
|
|
2534
3327
|
if (!opts.sim) {
|
|
2535
3328
|
const { sim } = await mount({
|
|
@@ -2539,20 +3332,24 @@ async function start(opts) {
|
|
|
2539
3332
|
});
|
|
2540
3333
|
opts.sim = sim;
|
|
2541
3334
|
}
|
|
2542
|
-
const app = new App(opts.sim, opts.game);
|
|
3335
|
+
const app = new App(opts.sim, opts.game, opts.brokerUrl ?? DEFAULT_BROKER_URL);
|
|
2543
3336
|
return app;
|
|
2544
3337
|
}
|
|
2545
3338
|
|
|
2546
3339
|
class App {
|
|
2547
3340
|
#sim;
|
|
2548
3341
|
game;
|
|
3342
|
+
brokerUrl;
|
|
2549
3343
|
#rafHandle = null;
|
|
2550
3344
|
#unsubscribe = null;
|
|
2551
3345
|
#now = performance.now();
|
|
2552
|
-
constructor(sim, game) {
|
|
3346
|
+
constructor(sim, game, brokerUrl) {
|
|
2553
3347
|
this.#sim = sim;
|
|
2554
3348
|
this.game = game;
|
|
3349
|
+
this.brokerUrl = brokerUrl;
|
|
2555
3350
|
this.game.hooks.beforeFrame = (frame) => {
|
|
3351
|
+
logger.frameNumber = this.#sim.time.frame;
|
|
3352
|
+
logger.matchFrame = this.#sim.wasm.get_match_frame();
|
|
2556
3353
|
this.beforeFrame.notify(frame);
|
|
2557
3354
|
};
|
|
2558
3355
|
this.subscribe();
|
|
@@ -2563,6 +3360,9 @@ class App {
|
|
|
2563
3360
|
set sim(sim) {
|
|
2564
3361
|
this.#sim = sim;
|
|
2565
3362
|
}
|
|
3363
|
+
joinRoom(roomId, callbacks) {
|
|
3364
|
+
joinRoom(this.brokerUrl, roomId, callbacks);
|
|
3365
|
+
}
|
|
2566
3366
|
beforeFrame = createListener();
|
|
2567
3367
|
afterFrame = createListener();
|
|
2568
3368
|
subscribe() {
|
|
@@ -2699,10 +3499,18 @@ function createListener() {
|
|
|
2699
3499
|
unsubscribeAll: () => listeners.clear()
|
|
2700
3500
|
};
|
|
2701
3501
|
}
|
|
3502
|
+
// src/netcode/protocol.ts
|
|
3503
|
+
var PacketType;
|
|
3504
|
+
((PacketType2) => {
|
|
3505
|
+
PacketType2[PacketType2["None"] = 0] = "None";
|
|
3506
|
+
PacketType2[PacketType2["Inputs"] = 1] = "Inputs";
|
|
3507
|
+
})(PacketType ||= {});
|
|
2702
3508
|
export {
|
|
2703
3509
|
start,
|
|
3510
|
+
logger,
|
|
3511
|
+
PacketType,
|
|
2704
3512
|
App
|
|
2705
3513
|
};
|
|
2706
3514
|
|
|
2707
|
-
//# debugId=
|
|
3515
|
+
//# debugId=2419B8104EB1627464756E2164756E21
|
|
2708
3516
|
//# sourceMappingURL=mod.js.map
|