@bloopjs/web 0.0.80 → 0.0.81

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/mod.js CHANGED
@@ -113,6 +113,7 @@ var __export22 = (target, all) => {
113
113
  };
114
114
  var exports_enums = {};
115
115
  __export22(exports_enums, {
116
+ NetJoinFailReason: () => NetJoinFailReason,
116
117
  MouseButton: () => MouseButton,
117
118
  Key: () => Key,
118
119
  InputSource: () => InputSource,
@@ -128,11 +129,13 @@ var EventType;
128
129
  EventType2[EventType2["MouseUp"] = 5] = "MouseUp";
129
130
  EventType2[EventType2["MouseWheel"] = 6] = "MouseWheel";
130
131
  EventType2[EventType2["FrameStart"] = 7] = "FrameStart";
131
- EventType2[EventType2["SessionInit"] = 8] = "SessionInit";
132
- EventType2[EventType2["SessionSetLocalPeer"] = 9] = "SessionSetLocalPeer";
133
- EventType2[EventType2["SessionConnectPeer"] = 10] = "SessionConnectPeer";
134
- EventType2[EventType2["SessionDisconnectPeer"] = 11] = "SessionDisconnectPeer";
135
- EventType2[EventType2["SessionEnd"] = 12] = "SessionEnd";
132
+ EventType2[EventType2["NetJoinOk"] = 8] = "NetJoinOk";
133
+ EventType2[EventType2["NetJoinFail"] = 9] = "NetJoinFail";
134
+ EventType2[EventType2["NetPeerJoin"] = 10] = "NetPeerJoin";
135
+ EventType2[EventType2["NetPeerLeave"] = 11] = "NetPeerLeave";
136
+ EventType2[EventType2["NetPeerAssignLocalId"] = 12] = "NetPeerAssignLocalId";
137
+ EventType2[EventType2["NetPacketReceived"] = 13] = "NetPacketReceived";
138
+ EventType2[EventType2["NetSessionInit"] = 14] = "NetSessionInit";
136
139
  })(EventType ||= {});
137
140
  var MouseButton;
138
141
  ((MouseButton2) => {
@@ -376,6 +379,14 @@ var InputSource;
376
379
  InputSource2[InputSource2["RemotePeer11"] = 139] = "RemotePeer11";
377
380
  InputSource2[InputSource2["Unmapped"] = 255] = "Unmapped";
378
381
  })(InputSource ||= {});
382
+ var NetJoinFailReason;
383
+ ((NetJoinFailReason2) => {
384
+ NetJoinFailReason2[NetJoinFailReason2["unknown"] = 0] = "unknown";
385
+ NetJoinFailReason2[NetJoinFailReason2["timeout"] = 1] = "timeout";
386
+ NetJoinFailReason2[NetJoinFailReason2["room_full"] = 2] = "room_full";
387
+ NetJoinFailReason2[NetJoinFailReason2["room_not_found"] = 3] = "room_not_found";
388
+ NetJoinFailReason2[NetJoinFailReason2["already_in_room"] = 4] = "already_in_room";
389
+ })(NetJoinFailReason ||= {});
379
390
  var MAX_PLAYERS = 12;
380
391
  var KEYBOARD_OFFSET = 0;
381
392
  var KEYBOARD_SIZE = 256;
@@ -1129,30 +1140,171 @@ class KeyboardContext {
1129
1140
  return state;
1130
1141
  }
1131
1142
  }
1143
+ var MAX_PEERS = 12;
1144
+ var PEERS_ARRAY_OFFSET = 32;
1145
+ var PEER_CTX_SIZE = 8;
1146
+ var PEER_CONNECTED_OFFSET = 0;
1147
+ var PEER_PACKET_COUNT_OFFSET = 1;
1148
+ var PEER_SEQ_OFFSET = 2;
1149
+ var PEER_ACK_OFFSET = 4;
1150
+ var PEER_ACK_COUNT_OFFSET = 6;
1151
+ var STATUS_MAP = {
1152
+ 0: "offline",
1153
+ 1: "local",
1154
+ 2: "join:pending",
1155
+ 3: "connected",
1156
+ 4: "disconnected"
1157
+ };
1132
1158
 
1133
1159
  class NetContext {
1134
1160
  dataView;
1161
+ #peers = Array.from({ length: MAX_PEERS }, () => ({
1162
+ isLocal: false,
1163
+ seq: -1,
1164
+ ack: -1
1165
+ }));
1166
+ #peersResult = [];
1135
1167
  constructor(dataView) {
1136
1168
  this.dataView = dataView;
1137
1169
  }
1170
+ #hasValidBuffer() {
1171
+ if (!this.dataView)
1172
+ return false;
1173
+ return this.dataView.buffer.byteLength > 0;
1174
+ }
1138
1175
  get peerCount() {
1139
- if (!this.dataView) {
1140
- throw new Error("NetContext DataView is not initialized");
1176
+ if (!this.#hasValidBuffer()) {
1177
+ throw new Error("NetContext dataView is not valid");
1141
1178
  }
1142
1179
  return this.dataView.getUint8(0);
1143
1180
  }
1181
+ get localPeerId() {
1182
+ if (!this.#hasValidBuffer()) {
1183
+ throw new Error("NetContext dataView is not valid");
1184
+ }
1185
+ return this.dataView.getUint8(1);
1186
+ }
1144
1187
  get isInSession() {
1145
- if (!this.dataView) {
1146
- throw new Error("NetContext DataView is not initialized");
1188
+ if (!this.#hasValidBuffer()) {
1189
+ throw new Error("NetContext dataView is not valid");
1147
1190
  }
1148
1191
  return this.dataView.getUint8(2) !== 0;
1149
1192
  }
1193
+ get status() {
1194
+ if (!this.#hasValidBuffer()) {
1195
+ throw new Error("NetContext dataView is not valid");
1196
+ }
1197
+ const statusByte = this.dataView.getUint8(3);
1198
+ return STATUS_MAP[statusByte] ?? "local";
1199
+ }
1150
1200
  get matchFrame() {
1151
- if (!this.dataView) {
1152
- throw new Error("NetContext DataView is not initialized");
1201
+ if (!this.#hasValidBuffer()) {
1202
+ throw new Error("NetContext dataView is not valid");
1153
1203
  }
1154
1204
  return this.dataView.getUint32(4, true);
1155
1205
  }
1206
+ get sessionStartFrame() {
1207
+ if (!this.#hasValidBuffer()) {
1208
+ throw new Error("NetContext dataView is not valid");
1209
+ }
1210
+ return this.dataView.getUint32(8, true);
1211
+ }
1212
+ get roomCode() {
1213
+ if (!this.#hasValidBuffer()) {
1214
+ throw new Error("NetContext dataView is not valid");
1215
+ }
1216
+ const bytes = [];
1217
+ for (let i = 0;i < 8; i++) {
1218
+ const byte = this.dataView.getUint8(12 + i);
1219
+ if (byte === 0)
1220
+ break;
1221
+ bytes.push(byte);
1222
+ }
1223
+ return String.fromCharCode(...bytes);
1224
+ }
1225
+ get wantsRoomCode() {
1226
+ if (!this.#hasValidBuffer()) {
1227
+ return;
1228
+ }
1229
+ const bytes = [];
1230
+ for (let i = 0;i < 8; i++) {
1231
+ const byte = this.dataView.getUint8(20 + i);
1232
+ if (byte === 0)
1233
+ break;
1234
+ bytes.push(byte);
1235
+ }
1236
+ return bytes.length > 0 ? String.fromCharCode(...bytes) : undefined;
1237
+ }
1238
+ set wantsRoomCode(code) {
1239
+ if (!this.#hasValidBuffer()) {
1240
+ throw new Error("NetContext dataView is not valid");
1241
+ }
1242
+ for (let i = 0;i < 8; i++) {
1243
+ this.dataView.setUint8(20 + i, 0);
1244
+ }
1245
+ if (code) {
1246
+ for (let i = 0;i < Math.min(code.length, 7); i++) {
1247
+ this.dataView.setUint8(20 + i, code.charCodeAt(i));
1248
+ }
1249
+ }
1250
+ }
1251
+ get wantsDisconnect() {
1252
+ if (!this.#hasValidBuffer()) {
1253
+ return false;
1254
+ }
1255
+ return this.dataView.getUint8(28) !== 0;
1256
+ }
1257
+ set wantsDisconnect(value) {
1258
+ if (!this.#hasValidBuffer()) {
1259
+ throw new Error("NetContext dataView is not valid");
1260
+ }
1261
+ this.dataView.setUint8(28, value ? 1 : 0);
1262
+ }
1263
+ get peers() {
1264
+ if (!this.#hasValidBuffer()) {
1265
+ throw new Error("NetContext dataView is not valid");
1266
+ }
1267
+ const dv = this.dataView;
1268
+ const localPeerId = this.localPeerId;
1269
+ const matchFrame = this.matchFrame;
1270
+ let minRemoteSeq = -1;
1271
+ for (let i = 0;i < MAX_PEERS; i++) {
1272
+ if (i === localPeerId)
1273
+ continue;
1274
+ const peerOffset = PEERS_ARRAY_OFFSET + i * PEER_CTX_SIZE;
1275
+ if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET) !== 1)
1276
+ continue;
1277
+ if (dv.getUint8(peerOffset + PEER_PACKET_COUNT_OFFSET) === 0)
1278
+ continue;
1279
+ const seq = dv.getUint16(peerOffset + PEER_SEQ_OFFSET, true);
1280
+ if (minRemoteSeq === -1 || seq < minRemoteSeq) {
1281
+ minRemoteSeq = seq;
1282
+ }
1283
+ }
1284
+ this.#peersResult.length = 0;
1285
+ for (let i = 0;i < MAX_PEERS; i++) {
1286
+ const peerOffset = PEERS_ARRAY_OFFSET + i * PEER_CTX_SIZE;
1287
+ if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET) !== 1)
1288
+ continue;
1289
+ const peer = this.#peers[i];
1290
+ if (!peer) {
1291
+ throw new Error(`Unexpected missing peer object at index ${i}`);
1292
+ }
1293
+ const isLocal = i === localPeerId;
1294
+ peer.isLocal = isLocal;
1295
+ if (isLocal) {
1296
+ peer.seq = matchFrame;
1297
+ peer.ack = minRemoteSeq;
1298
+ } else {
1299
+ const packetCount = dv.getUint8(peerOffset + PEER_PACKET_COUNT_OFFSET);
1300
+ const ackCount = dv.getUint8(peerOffset + PEER_ACK_COUNT_OFFSET);
1301
+ peer.seq = packetCount === 0 ? -1 : dv.getUint16(peerOffset + PEER_SEQ_OFFSET, true);
1302
+ peer.ack = ackCount === 0 ? -1 : dv.getUint16(peerOffset + PEER_ACK_OFFSET, true);
1303
+ }
1304
+ this.#peersResult.push(peer);
1305
+ }
1306
+ return this.#peersResult;
1307
+ }
1156
1308
  }
1157
1309
 
1158
1310
  class TimeContext {
@@ -1194,7 +1346,7 @@ function readTapeHeader(tape) {
1194
1346
  eventCount: view.getUint16(14, true)
1195
1347
  };
1196
1348
  }
1197
- var DEFAULT_WASM_URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.80/wasm/bloop.wasm");
1349
+ var DEFAULT_WASM_URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.81/wasm/bloop.wasm");
1198
1350
  var MAX_ROLLBACK_FRAMES = 500;
1199
1351
  var TIME_CTX_OFFSET = 0;
1200
1352
  var INPUT_CTX_OFFSET = TIME_CTX_OFFSET + 4;
@@ -1203,63 +1355,6 @@ var NET_CTX_OFFSET = EVENTS_OFFSET + 4;
1203
1355
  var SNAPSHOT_HEADER_LEN = 16;
1204
1356
  var SNAPSHOT_HEADER_USER_LEN_OFFSET = 4;
1205
1357
  var SNAPSHOT_HEADER_ENGINE_LEN_OFFSET = 8;
1206
-
1207
- class Net {
1208
- #wasm;
1209
- #memory;
1210
- constructor(wasm, memory) {
1211
- this.#wasm = wasm;
1212
- this.#memory = memory;
1213
- }
1214
- setLocalPeer(peerId) {
1215
- this.#wasm.session_set_local_peer(peerId);
1216
- }
1217
- connectPeer(peerId) {
1218
- this.#wasm.session_peer_connect(peerId);
1219
- }
1220
- disconnectPeer(peerId) {
1221
- this.#wasm.session_peer_disconnect(peerId);
1222
- }
1223
- getOutboundPacket(targetPeer) {
1224
- this.#wasm.build_outbound_packet(targetPeer);
1225
- const len = this.#wasm.get_outbound_packet_len();
1226
- if (len === 0) {
1227
- return null;
1228
- }
1229
- const ptr = this.#wasm.get_outbound_packet();
1230
- assert(ptr > 0, `Invalid outbound packet pointer: ${ptr}`);
1231
- const memoryView = new Uint8Array(this.#memory.buffer, ptr, len);
1232
- const copy = new Uint8Array(len);
1233
- copy.set(memoryView);
1234
- return copy;
1235
- }
1236
- receivePacket(data) {
1237
- if (data.length === 0) {
1238
- return;
1239
- }
1240
- const ptr = this.#wasm.alloc(data.byteLength);
1241
- assert(ptr > 0, `Failed to allocate ${data.byteLength} bytes for received packet`);
1242
- const memoryView = new Uint8Array(this.#memory.buffer, ptr, data.byteLength);
1243
- memoryView.set(data);
1244
- const result = this.#wasm.receive_packet(ptr, data.byteLength);
1245
- this.#wasm.free(ptr, data.byteLength);
1246
- if (result !== 0) {
1247
- const errorMessages = {
1248
- 1: "No active session",
1249
- 2: "Buffer too small",
1250
- 3: "Unsupported packet version",
1251
- 4: "Invalid event count"
1252
- };
1253
- throw new Error(errorMessages[result] ?? `Unknown packet error: ${result}`);
1254
- }
1255
- }
1256
- getPeerState(peer) {
1257
- return {
1258
- seq: this.#wasm.get_peer_seq(peer),
1259
- ack: this.#wasm.get_peer_ack(peer)
1260
- };
1261
- }
1262
- }
1263
1358
  var originalConsole = globalThis.console;
1264
1359
  var noop = () => {};
1265
1360
  var stubConsole = Object.fromEntries(Object.keys(originalConsole).map((key) => [key, noop]));
@@ -1277,7 +1372,7 @@ class Sim {
1277
1372
  #time;
1278
1373
  #serialize;
1279
1374
  #isPaused = false;
1280
- net;
1375
+ #net;
1281
1376
  onTapeFull;
1282
1377
  constructor(wasm, memory, opts) {
1283
1378
  this.wasm = wasm;
@@ -1285,7 +1380,7 @@ class Sim {
1285
1380
  this.#time = new TimeContext(new DataView(this.#memory.buffer, this.wasm.get_time_ctx()));
1286
1381
  this.id = `${Math.floor(Math.random() * 1e6)}`;
1287
1382
  this.#serialize = opts?.serialize;
1288
- this.net = new Net(wasm, memory);
1383
+ this.#net = new NetContext;
1289
1384
  }
1290
1385
  step(ms) {
1291
1386
  if (this.#isPaused) {
@@ -1366,7 +1461,9 @@ class Sim {
1366
1461
  assert(tapePtr > 0, `failed to allocate ${tape.byteLength} bytes for tape load, pointer=${tapePtr}`);
1367
1462
  const memoryView = new Uint8Array(this.#memory.buffer, tapePtr, tape.byteLength);
1368
1463
  memoryView.set(tape);
1369
- this.wasm.stop_recording();
1464
+ if (this.isRecording) {
1465
+ this.wasm.stop_recording();
1466
+ }
1370
1467
  const result = this.wasm.load_tape(tapePtr, tape.byteLength);
1371
1468
  assert(result === 0, `failed to load tape, error code=${result}`);
1372
1469
  this.wasm.free(tapePtr, tape.byteLength);
@@ -1388,6 +1485,12 @@ class Sim {
1388
1485
  }
1389
1486
  return this.#time;
1390
1487
  }
1488
+ get net() {
1489
+ if (!this.#net.dataView || this.#net.dataView.buffer !== this.#memory.buffer) {
1490
+ this.#net.dataView = new DataView(this.#memory.buffer, this.wasm.get_net_ctx());
1491
+ }
1492
+ return this.#net;
1493
+ }
1391
1494
  get buffer() {
1392
1495
  return this.#memory.buffer;
1393
1496
  }
@@ -1418,16 +1521,77 @@ class Sim {
1418
1521
  },
1419
1522
  mousewheel: (x, y, peerId = 0) => {
1420
1523
  this.wasm.emit_mousewheel(x, y, peerId);
1524
+ },
1525
+ network: (type, data) => {
1526
+ switch (type) {
1527
+ case "join:ok": {
1528
+ const roomCode = data.roomCode;
1529
+ const encoded = new TextEncoder().encode(roomCode);
1530
+ const ptr = this.wasm.alloc(encoded.length);
1531
+ new Uint8Array(this.#memory.buffer, ptr, encoded.length).set(encoded);
1532
+ this.wasm.emit_net_join_ok(ptr, encoded.length);
1533
+ this.wasm.free(ptr, encoded.length);
1534
+ break;
1535
+ }
1536
+ case "join:fail":
1537
+ this.wasm.emit_net_join_fail(0);
1538
+ break;
1539
+ case "peer:join": {
1540
+ const peerId = data.peerId;
1541
+ this.wasm.emit_net_peer_join(peerId);
1542
+ break;
1543
+ }
1544
+ case "peer:leave": {
1545
+ const peerId = data.peerId;
1546
+ this.wasm.emit_net_peer_leave(peerId);
1547
+ break;
1548
+ }
1549
+ case "peer:assign_local_id": {
1550
+ const peerId = data.peerId;
1551
+ this.wasm.emit_net_peer_assign_local_id(peerId);
1552
+ break;
1553
+ }
1554
+ case "session:start":
1555
+ this.wasm.emit_net_session_init();
1556
+ break;
1557
+ case "session:end":
1558
+ this.wasm.emit_net_session_end();
1559
+ break;
1560
+ }
1561
+ },
1562
+ packet: (data) => {
1563
+ if (data.length === 0) {
1564
+ return;
1565
+ }
1566
+ const ptr = this.wasm.alloc(data.byteLength);
1567
+ assert(ptr > 0, `Failed to allocate ${data.byteLength} bytes for received packet`);
1568
+ const memoryView = new Uint8Array(this.#memory.buffer, ptr, data.byteLength);
1569
+ memoryView.set(data);
1570
+ const result = this.wasm.emit_receive_packet(ptr, data.byteLength);
1571
+ this.wasm.free(ptr, data.byteLength);
1572
+ if (result !== 0) {
1573
+ const errorMessages = {
1574
+ 1: "No active session",
1575
+ 2: "Buffer too small",
1576
+ 3: "Unsupported packet version",
1577
+ 4: "Invalid event count"
1578
+ };
1579
+ throw new Error(errorMessages[result] ?? `Unknown packet error: ${result}`);
1580
+ }
1421
1581
  }
1422
1582
  };
1423
- sessionInit(peerCount) {
1424
- const serializer = this.#serialize ? this.#serialize() : null;
1425
- const size = serializer ? serializer.size : 0;
1426
- const result = this.wasm.session_init(peerCount, size);
1427
- assert(result === 0, `failed to initialize session, error code=${result}`);
1428
- }
1429
- sessionEnd() {
1430
- this.wasm.session_end();
1583
+ getOutboundPacket(targetPeer) {
1584
+ this.wasm.build_outbound_packet(targetPeer);
1585
+ const len = this.wasm.get_outbound_packet_len();
1586
+ if (len === 0) {
1587
+ throw new Error(`No outbound packet available for peer ${targetPeer}`);
1588
+ }
1589
+ const ptr = this.wasm.get_outbound_packet();
1590
+ assert(ptr > 0, `Invalid outbound packet pointer: ${ptr}`);
1591
+ const memoryView = new Uint8Array(this.#memory.buffer, ptr, len);
1592
+ const copy = new Uint8Array(len);
1593
+ copy.set(memoryView);
1594
+ return copy;
1431
1595
  }
1432
1596
  }
1433
1597
  async function mount(mountable, options) {
@@ -1646,8 +1810,59 @@ class Bloop {
1646
1810
  y: payloadVec2.y
1647
1811
  }));
1648
1812
  break;
1813
+ case exports_enums.EventType.NetJoinOk: {
1814
+ const roomCodeBytes = [];
1815
+ for (let j = 0;j < 8; j++) {
1816
+ const byte = eventsDataView.getUint8(payloadOffset + j);
1817
+ if (byte === 0)
1818
+ break;
1819
+ roomCodeBytes.push(byte);
1820
+ }
1821
+ const roomCode = String.fromCharCode(...roomCodeBytes);
1822
+ this.#context.event = {
1823
+ type: "join:ok",
1824
+ data: { roomCode }
1825
+ };
1826
+ system.netcode?.(this.#context);
1827
+ break;
1828
+ }
1829
+ case exports_enums.EventType.NetJoinFail: {
1830
+ const reasonCode = eventsDataView.getUint8(payloadOffset);
1831
+ const reasons = [
1832
+ "unknown",
1833
+ "timeout",
1834
+ "room_full",
1835
+ "room_not_found",
1836
+ "already_in_room"
1837
+ ];
1838
+ const reason = reasons[reasonCode] ?? "unknown";
1839
+ this.#context.event = {
1840
+ type: "join:fail",
1841
+ data: { reason }
1842
+ };
1843
+ system.netcode?.(this.#context);
1844
+ break;
1845
+ }
1846
+ case exports_enums.EventType.NetPeerJoin: {
1847
+ const peerId = eventsDataView.getUint8(payloadOffset);
1848
+ this.#context.event = {
1849
+ type: "peer:join",
1850
+ data: { peerId }
1851
+ };
1852
+ system.netcode?.(this.#context);
1853
+ break;
1854
+ }
1855
+ case exports_enums.EventType.NetPeerLeave: {
1856
+ const peerId = eventsDataView.getUint8(payloadOffset);
1857
+ this.#context.event = {
1858
+ type: "peer:leave",
1859
+ data: { peerId }
1860
+ };
1861
+ system.netcode?.(this.#context);
1862
+ break;
1863
+ }
1649
1864
  default:
1650
- throw new Error(`Unknown event type: ${eventType}`);
1865
+ break;
1651
1866
  }
1652
1867
  offset += 12;
1653
1868
  }
@@ -1674,6 +1889,7 @@ var __export3 = (target, all) => {
1674
1889
  };
1675
1890
  var exports_enums2 = {};
1676
1891
  __export3(exports_enums2, {
1892
+ NetJoinFailReason: () => NetJoinFailReason2,
1677
1893
  MouseButton: () => MouseButton2,
1678
1894
  Key: () => Key2,
1679
1895
  InputSource: () => InputSource2,
@@ -1689,11 +1905,13 @@ var EventType2;
1689
1905
  EventType22[EventType22["MouseUp"] = 5] = "MouseUp";
1690
1906
  EventType22[EventType22["MouseWheel"] = 6] = "MouseWheel";
1691
1907
  EventType22[EventType22["FrameStart"] = 7] = "FrameStart";
1692
- EventType22[EventType22["SessionInit"] = 8] = "SessionInit";
1693
- EventType22[EventType22["SessionSetLocalPeer"] = 9] = "SessionSetLocalPeer";
1694
- EventType22[EventType22["SessionConnectPeer"] = 10] = "SessionConnectPeer";
1695
- EventType22[EventType22["SessionDisconnectPeer"] = 11] = "SessionDisconnectPeer";
1696
- EventType22[EventType22["SessionEnd"] = 12] = "SessionEnd";
1908
+ EventType22[EventType22["NetJoinOk"] = 8] = "NetJoinOk";
1909
+ EventType22[EventType22["NetJoinFail"] = 9] = "NetJoinFail";
1910
+ EventType22[EventType22["NetPeerJoin"] = 10] = "NetPeerJoin";
1911
+ EventType22[EventType22["NetPeerLeave"] = 11] = "NetPeerLeave";
1912
+ EventType22[EventType22["NetPeerAssignLocalId"] = 12] = "NetPeerAssignLocalId";
1913
+ EventType22[EventType22["NetPacketReceived"] = 13] = "NetPacketReceived";
1914
+ EventType22[EventType22["NetSessionInit"] = 14] = "NetSessionInit";
1697
1915
  })(EventType2 ||= {});
1698
1916
  var MouseButton2;
1699
1917
  ((MouseButton22) => {
@@ -1937,6 +2155,14 @@ var InputSource2;
1937
2155
  InputSource22[InputSource22["RemotePeer11"] = 139] = "RemotePeer11";
1938
2156
  InputSource22[InputSource22["Unmapped"] = 255] = "Unmapped";
1939
2157
  })(InputSource2 ||= {});
2158
+ var NetJoinFailReason2;
2159
+ ((NetJoinFailReason22) => {
2160
+ NetJoinFailReason22[NetJoinFailReason22["unknown"] = 0] = "unknown";
2161
+ NetJoinFailReason22[NetJoinFailReason22["timeout"] = 1] = "timeout";
2162
+ NetJoinFailReason22[NetJoinFailReason22["room_full"] = 2] = "room_full";
2163
+ NetJoinFailReason22[NetJoinFailReason22["room_not_found"] = 3] = "room_not_found";
2164
+ NetJoinFailReason22[NetJoinFailReason22["already_in_room"] = 4] = "already_in_room";
2165
+ })(NetJoinFailReason2 ||= {});
1940
2166
  var MAX_PLAYERS2 = 12;
1941
2167
  var KEYBOARD_OFFSET2 = 0;
1942
2168
  var MOUSE_OFFSET2 = 256;
@@ -2671,6 +2897,172 @@ class KeyboardContext2 {
2671
2897
  return state;
2672
2898
  }
2673
2899
  }
2900
+ var MAX_PEERS2 = 12;
2901
+ var PEERS_ARRAY_OFFSET2 = 32;
2902
+ var PEER_CTX_SIZE2 = 8;
2903
+ var PEER_CONNECTED_OFFSET2 = 0;
2904
+ var PEER_PACKET_COUNT_OFFSET2 = 1;
2905
+ var PEER_SEQ_OFFSET2 = 2;
2906
+ var PEER_ACK_OFFSET2 = 4;
2907
+ var PEER_ACK_COUNT_OFFSET2 = 6;
2908
+ var STATUS_MAP2 = {
2909
+ 0: "offline",
2910
+ 1: "local",
2911
+ 2: "join:pending",
2912
+ 3: "connected",
2913
+ 4: "disconnected"
2914
+ };
2915
+
2916
+ class NetContext2 {
2917
+ dataView;
2918
+ #peers = Array.from({ length: MAX_PEERS2 }, () => ({
2919
+ isLocal: false,
2920
+ seq: -1,
2921
+ ack: -1
2922
+ }));
2923
+ #peersResult = [];
2924
+ constructor(dataView) {
2925
+ this.dataView = dataView;
2926
+ }
2927
+ #hasValidBuffer() {
2928
+ if (!this.dataView)
2929
+ return false;
2930
+ return this.dataView.buffer.byteLength > 0;
2931
+ }
2932
+ get peerCount() {
2933
+ if (!this.#hasValidBuffer()) {
2934
+ throw new Error("NetContext dataView is not valid");
2935
+ }
2936
+ return this.dataView.getUint8(0);
2937
+ }
2938
+ get localPeerId() {
2939
+ if (!this.#hasValidBuffer()) {
2940
+ throw new Error("NetContext dataView is not valid");
2941
+ }
2942
+ return this.dataView.getUint8(1);
2943
+ }
2944
+ get isInSession() {
2945
+ if (!this.#hasValidBuffer()) {
2946
+ throw new Error("NetContext dataView is not valid");
2947
+ }
2948
+ return this.dataView.getUint8(2) !== 0;
2949
+ }
2950
+ get status() {
2951
+ if (!this.#hasValidBuffer()) {
2952
+ throw new Error("NetContext dataView is not valid");
2953
+ }
2954
+ const statusByte = this.dataView.getUint8(3);
2955
+ return STATUS_MAP2[statusByte] ?? "local";
2956
+ }
2957
+ get matchFrame() {
2958
+ if (!this.#hasValidBuffer()) {
2959
+ throw new Error("NetContext dataView is not valid");
2960
+ }
2961
+ return this.dataView.getUint32(4, true);
2962
+ }
2963
+ get sessionStartFrame() {
2964
+ if (!this.#hasValidBuffer()) {
2965
+ throw new Error("NetContext dataView is not valid");
2966
+ }
2967
+ return this.dataView.getUint32(8, true);
2968
+ }
2969
+ get roomCode() {
2970
+ if (!this.#hasValidBuffer()) {
2971
+ throw new Error("NetContext dataView is not valid");
2972
+ }
2973
+ const bytes = [];
2974
+ for (let i = 0;i < 8; i++) {
2975
+ const byte = this.dataView.getUint8(12 + i);
2976
+ if (byte === 0)
2977
+ break;
2978
+ bytes.push(byte);
2979
+ }
2980
+ return String.fromCharCode(...bytes);
2981
+ }
2982
+ get wantsRoomCode() {
2983
+ if (!this.#hasValidBuffer()) {
2984
+ return;
2985
+ }
2986
+ const bytes = [];
2987
+ for (let i = 0;i < 8; i++) {
2988
+ const byte = this.dataView.getUint8(20 + i);
2989
+ if (byte === 0)
2990
+ break;
2991
+ bytes.push(byte);
2992
+ }
2993
+ return bytes.length > 0 ? String.fromCharCode(...bytes) : undefined;
2994
+ }
2995
+ set wantsRoomCode(code) {
2996
+ if (!this.#hasValidBuffer()) {
2997
+ throw new Error("NetContext dataView is not valid");
2998
+ }
2999
+ for (let i = 0;i < 8; i++) {
3000
+ this.dataView.setUint8(20 + i, 0);
3001
+ }
3002
+ if (code) {
3003
+ for (let i = 0;i < Math.min(code.length, 7); i++) {
3004
+ this.dataView.setUint8(20 + i, code.charCodeAt(i));
3005
+ }
3006
+ }
3007
+ }
3008
+ get wantsDisconnect() {
3009
+ if (!this.#hasValidBuffer()) {
3010
+ return false;
3011
+ }
3012
+ return this.dataView.getUint8(28) !== 0;
3013
+ }
3014
+ set wantsDisconnect(value) {
3015
+ if (!this.#hasValidBuffer()) {
3016
+ throw new Error("NetContext dataView is not valid");
3017
+ }
3018
+ this.dataView.setUint8(28, value ? 1 : 0);
3019
+ }
3020
+ get peers() {
3021
+ if (!this.#hasValidBuffer()) {
3022
+ throw new Error("NetContext dataView is not valid");
3023
+ }
3024
+ const dv = this.dataView;
3025
+ const localPeerId = this.localPeerId;
3026
+ const matchFrame = this.matchFrame;
3027
+ let minRemoteSeq = -1;
3028
+ for (let i = 0;i < MAX_PEERS2; i++) {
3029
+ if (i === localPeerId)
3030
+ continue;
3031
+ const peerOffset = PEERS_ARRAY_OFFSET2 + i * PEER_CTX_SIZE2;
3032
+ if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET2) !== 1)
3033
+ continue;
3034
+ if (dv.getUint8(peerOffset + PEER_PACKET_COUNT_OFFSET2) === 0)
3035
+ continue;
3036
+ const seq = dv.getUint16(peerOffset + PEER_SEQ_OFFSET2, true);
3037
+ if (minRemoteSeq === -1 || seq < minRemoteSeq) {
3038
+ minRemoteSeq = seq;
3039
+ }
3040
+ }
3041
+ this.#peersResult.length = 0;
3042
+ for (let i = 0;i < MAX_PEERS2; i++) {
3043
+ const peerOffset = PEERS_ARRAY_OFFSET2 + i * PEER_CTX_SIZE2;
3044
+ if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET2) !== 1)
3045
+ continue;
3046
+ const peer = this.#peers[i];
3047
+ if (!peer) {
3048
+ throw new Error(`Unexpected missing peer object at index ${i}`);
3049
+ }
3050
+ const isLocal = i === localPeerId;
3051
+ peer.isLocal = isLocal;
3052
+ if (isLocal) {
3053
+ peer.seq = matchFrame;
3054
+ peer.ack = minRemoteSeq;
3055
+ } else {
3056
+ const packetCount = dv.getUint8(peerOffset + PEER_PACKET_COUNT_OFFSET2);
3057
+ const ackCount = dv.getUint8(peerOffset + PEER_ACK_COUNT_OFFSET2);
3058
+ peer.seq = packetCount === 0 ? -1 : dv.getUint16(peerOffset + PEER_SEQ_OFFSET2, true);
3059
+ peer.ack = ackCount === 0 ? -1 : dv.getUint16(peerOffset + PEER_ACK_OFFSET2, true);
3060
+ }
3061
+ this.#peersResult.push(peer);
3062
+ }
3063
+ return this.#peersResult;
3064
+ }
3065
+ }
2674
3066
  var TAPE_MAGIC2 = 1413566533;
2675
3067
  function readTapeHeader2(tape) {
2676
3068
  const view = new DataView(tape.buffer, tape.byteOffset, tape.byteLength);
@@ -2686,7 +3078,7 @@ function readTapeHeader2(tape) {
2686
3078
  eventCount: view.getUint16(14, true)
2687
3079
  };
2688
3080
  }
2689
- var DEFAULT_WASM_URL2 = new URL("https://unpkg.com/@bloopjs/engine@0.0.80/wasm/bloop.wasm");
3081
+ var DEFAULT_WASM_URL2 = new URL("https://unpkg.com/@bloopjs/engine@0.0.81/wasm/bloop.wasm");
2690
3082
  var TIME_CTX_OFFSET2 = 0;
2691
3083
  var INPUT_CTX_OFFSET2 = TIME_CTX_OFFSET2 + 4;
2692
3084
  var EVENTS_OFFSET2 = INPUT_CTX_OFFSET2 + 4;
@@ -6201,11 +6593,11 @@ function joinRollbackRoom(roomId, app, opts) {
6201
6593
  }
6202
6594
  function receivePackets() {
6203
6595
  for (const packetData of incomingPackets) {
6204
- app.sim.net.receivePacket(packetData);
6596
+ app.sim.emit.packet(packetData);
6205
6597
  if (remotePeerId == null) {
6206
6598
  return;
6207
6599
  }
6208
- const peerState = app.sim.net.getPeerState(remotePeerId);
6600
+ const peerState = unwrap(app.sim.net.peers[remotePeerId], `Remote peer state not found for peerId ${remotePeerId}`);
6209
6601
  updatePeer(remoteStringPeerId, {
6210
6602
  ack: peerState.ack,
6211
6603
  seq: peerState.seq,
@@ -6223,7 +6615,7 @@ function joinRollbackRoom(roomId, app, opts) {
6223
6615
  console.warn("[netcode] Data channel not open, cannot send packet. readyState=", udp.readyState);
6224
6616
  return;
6225
6617
  }
6226
- const packet = app.sim.net.getOutboundPacket(remotePeerId);
6618
+ const packet = app.sim.getOutboundPacket(remotePeerId);
6227
6619
  if (!packet) {
6228
6620
  console.warn("[netcode] No packet to send");
6229
6621
  return;
@@ -6245,7 +6637,7 @@ function joinRollbackRoom(roomId, app, opts) {
6245
6637
  onDataChannelClose(peerId, reliable) {
6246
6638
  console.log(`Data channel closed: ${peerId} (reliable: ${reliable})`);
6247
6639
  if (!reliable && remotePeerId !== null) {
6248
- app.sim.net.disconnectPeer(remotePeerId);
6640
+ app.sim.emit.network("peer:leave", { peerId: remotePeerId });
6249
6641
  sessionActive = false;
6250
6642
  opts?.onSessionEnd?.();
6251
6643
  }
@@ -6264,9 +6656,10 @@ function joinRollbackRoom(roomId, app, opts) {
6264
6656
  remotePeerId = ids.remote;
6265
6657
  remoteStringPeerId = peerId;
6266
6658
  setRemoteId(remotePeerId);
6267
- app.sim.sessionInit(2);
6268
- app.sim.net.setLocalPeer(localPeerId);
6269
- app.sim.net.connectPeer(remotePeerId);
6659
+ app.sim.emit.network("peer:join", { peerId: localPeerId });
6660
+ app.sim.emit.network("peer:join", { peerId: remotePeerId });
6661
+ app.sim.emit.network("peer:assign_local_id", { peerId: localPeerId });
6662
+ app.sim.emit.network("session:start", {});
6270
6663
  sessionActive = true;
6271
6664
  console.log(`[netcode] Session started at frame ${app.sim.time.frame}`);
6272
6665
  opts?.onSessionStart?.();
@@ -6285,7 +6678,7 @@ function joinRollbackRoom(roomId, app, opts) {
6285
6678
  onPeerDisconnected(peerId) {
6286
6679
  removePeer(peerId);
6287
6680
  if (remotePeerId !== null && peerId === remoteStringPeerId) {
6288
- app.sim.net.disconnectPeer(remotePeerId);
6681
+ app.sim.emit.network("peer:leave", { peerId: remotePeerId });
6289
6682
  sessionActive = false;
6290
6683
  opts?.onSessionEnd?.();
6291
6684
  }
@@ -6312,5 +6705,5 @@ export {
6312
6705
  App
6313
6706
  };
6314
6707
 
6315
- //# debugId=7095338EC2A575A064756E2164756E21
6708
+ //# debugId=A73DA4EECDE34BD764756E2164756E21
6316
6709
  //# sourceMappingURL=mod.js.map