@bloopjs/web 0.0.88 → 0.0.89

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
@@ -82,16 +82,21 @@ __export2(exports_engine, {
82
82
  SNAPSHOT_HEADER_ENGINE_LEN_OFFSET: () => SNAPSHOT_HEADER_ENGINE_LEN_OFFSET,
83
83
  PlayerInputContext: () => PlayerInputContext,
84
84
  PLAYER_INPUTS_SIZE: () => PLAYER_INPUTS_SIZE,
85
+ PLAYER_INPUTS_MOUSE_CTX_OFFSET: () => PLAYER_INPUTS_MOUSE_CTX_OFFSET,
86
+ PLAYER_INPUTS_KEY_CTX_OFFSET: () => PLAYER_INPUTS_KEY_CTX_OFFSET,
85
87
  NetContext: () => NetContext,
86
88
  NET_CTX_OFFSET: () => NET_CTX_OFFSET,
87
89
  MouseContext: () => MouseContext,
88
- MOUSE_OFFSET: () => MOUSE_OFFSET,
89
- MOUSE_BUTTONS_OFFSET: () => MOUSE_BUTTONS_OFFSET,
90
+ MOUSE_OFFSET: () => PLAYER_INPUTS_MOUSE_CTX_OFFSET,
91
+ MOUSE_CTX_SIZE: () => MOUSE_CTX_SIZE,
92
+ MOUSE_CTX_BUTTON_STATES_OFFSET: () => MOUSE_CTX_BUTTON_STATES_OFFSET,
93
+ MOUSE_BUTTONS_OFFSET: () => MOUSE_CTX_BUTTON_STATES_OFFSET,
90
94
  MAX_ROLLBACK_FRAMES: () => MAX_ROLLBACK_FRAMES,
91
95
  MAX_PLAYERS: () => MAX_PLAYERS,
92
96
  KeyboardContext: () => KeyboardContext,
93
- KEYBOARD_SIZE: () => KEYBOARD_SIZE,
94
- KEYBOARD_OFFSET: () => KEYBOARD_OFFSET,
97
+ KEY_CTX_SIZE: () => KEY_CTX_SIZE,
98
+ KEYBOARD_SIZE: () => KEY_CTX_SIZE,
99
+ KEYBOARD_OFFSET: () => PLAYER_INPUTS_KEY_CTX_OFFSET,
95
100
  InputContext: () => InputContext,
96
101
  INPUT_CTX_SIZE: () => INPUT_CTX_SIZE,
97
102
  INPUT_CTX_OFFSET: () => INPUT_CTX_OFFSET,
@@ -387,33 +392,38 @@ var NetJoinFailReason;
387
392
  NetJoinFailReason2[NetJoinFailReason2["room_not_found"] = 3] = "room_not_found";
388
393
  NetJoinFailReason2[NetJoinFailReason2["already_in_room"] = 4] = "already_in_room";
389
394
  })(NetJoinFailReason ||= {});
390
- var MAX_PLAYERS = 12;
391
- var KEYBOARD_OFFSET = 0;
392
- var KEYBOARD_SIZE = 256;
393
- var MOUSE_OFFSET = 256;
394
- var MOUSE_BUTTONS_OFFSET = 16;
395
+ var TIME_CTX_FRAME_OFFSET = 0;
396
+ var TIME_CTX_DT_MS_OFFSET = 4;
397
+ var TIME_CTX_TOTAL_MS_OFFSET = 8;
398
+ var PEER_CTX_CONNECTED_OFFSET = 0;
399
+ var PEER_CTX_SEQ_OFFSET = 2;
400
+ var PEER_CTX_ACK_OFFSET = 4;
401
+ var PEER_CTX_SIZE = 8;
402
+ var NET_CTX_PEER_COUNT_OFFSET = 0;
403
+ var NET_CTX_LOCAL_PEER_ID_OFFSET = 1;
404
+ var NET_CTX_IN_SESSION_OFFSET = 2;
405
+ var NET_CTX_STATUS_OFFSET = 3;
406
+ var NET_CTX_MATCH_FRAME_OFFSET = 4;
407
+ var NET_CTX_SESSION_START_FRAME_OFFSET = 8;
408
+ var NET_CTX_ROOM_CODE_OFFSET = 12;
409
+ var NET_CTX_WANTS_ROOM_CODE_OFFSET = 20;
410
+ var NET_CTX_WANTS_DISCONNECT_OFFSET = 28;
411
+ var NET_CTX_PEERS_OFFSET = 32;
412
+ var NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET = 128;
413
+ var NET_CTX_TOTAL_ROLLBACKS_OFFSET = 132;
414
+ var NET_CTX_FRAMES_RESIMULATED_OFFSET = 136;
415
+ var MOUSE_CTX_X_OFFSET = 0;
416
+ var MOUSE_CTX_Y_OFFSET = 4;
417
+ var MOUSE_CTX_WHEEL_X_OFFSET = 8;
418
+ var MOUSE_CTX_WHEEL_Y_OFFSET = 12;
419
+ var MOUSE_CTX_BUTTON_STATES_OFFSET = 16;
420
+ var MOUSE_CTX_SIZE = 24;
421
+ var KEY_CTX_SIZE = 256;
422
+ var PLAYER_INPUTS_KEY_CTX_OFFSET = 0;
423
+ var PLAYER_INPUTS_MOUSE_CTX_OFFSET = 256;
395
424
  var PLAYER_INPUTS_SIZE = 280;
396
- var INPUT_CTX_SIZE = MAX_PLAYERS * PLAYER_INPUTS_SIZE;
397
- var EVENT_PAYLOAD_SIZE = 8;
398
- var EVENT_PAYLOAD_ALIGN = 4;
399
- function keyToKeyCode(key) {
400
- return Key[key];
401
- }
402
- function keyCodeToKey(code) {
403
- return Key[code];
404
- }
405
- function mouseButtonToMouseButtonCode(button) {
406
- return MouseButton[button];
407
- }
408
- function mouseButtonCodeToMouseButton(code) {
409
- return MouseButton[code];
410
- }
411
- function inputSourceToInputSourceCode(source) {
412
- return InputSource[source];
413
- }
414
- function inputSourceCodeToInputSource(code) {
415
- return InputSource[code];
416
- }
425
+ var INPUT_CTX_SIZE = 3360;
426
+ var MAX_PLAYERS = 12;
417
427
 
418
428
  class PlayerInputContext {
419
429
  #dataView;
@@ -424,13 +434,13 @@ class PlayerInputContext {
424
434
  }
425
435
  get keys() {
426
436
  if (!this.#keys) {
427
- this.#keys = new KeyboardContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + KEYBOARD_OFFSET));
437
+ this.#keys = new KeyboardContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_KEY_CTX_OFFSET));
428
438
  }
429
439
  return this.#keys;
430
440
  }
431
441
  get mouse() {
432
442
  if (!this.#mouse) {
433
- this.#mouse = new MouseContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + MOUSE_OFFSET));
443
+ this.#mouse = new MouseContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_MOUSE_CTX_OFFSET));
434
444
  }
435
445
  return this.#mouse;
436
446
  }
@@ -455,6 +465,9 @@ class InputContext {
455
465
  this.#dataView = dataView;
456
466
  this.#buildPlayers();
457
467
  }
468
+ hasDataView() {
469
+ return !!this.#dataView;
470
+ }
458
471
  #buildPlayers() {
459
472
  this.#players = [];
460
473
  for (let i = 0;i < MAX_PLAYERS; i++) {
@@ -487,19 +500,19 @@ class MouseContext {
487
500
  this.#dataView = dataView;
488
501
  }
489
502
  get x() {
490
- return this.#dataView.getFloat32(0, true);
503
+ return this.#dataView.getFloat32(MOUSE_CTX_X_OFFSET, true);
491
504
  }
492
505
  get y() {
493
- return this.#dataView.getFloat32(4, true);
506
+ return this.#dataView.getFloat32(MOUSE_CTX_Y_OFFSET, true);
494
507
  }
495
508
  get wheel() {
496
509
  return { x: this.wheelX, y: this.wheelY };
497
510
  }
498
511
  get wheelX() {
499
- return this.#dataView.getFloat32(8, true);
512
+ return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_X_OFFSET, true);
500
513
  }
501
514
  get wheelY() {
502
- return this.#dataView.getFloat32(12, true);
515
+ return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_Y_OFFSET, true);
503
516
  }
504
517
  get left() {
505
518
  return this.#buttonState(1);
@@ -518,7 +531,7 @@ class MouseContext {
518
531
  state = { down: false, held: false, up: false };
519
532
  this.#buttonStates.set(code, state);
520
533
  }
521
- const offset = MOUSE_BUTTONS_OFFSET;
534
+ const offset = MOUSE_CTX_BUTTON_STATES_OFFSET;
522
535
  state.held = !!(this.#dataView.getUint8(offset + code) & 1);
523
536
  state.down = state.held && !(this.#dataView.getUint8(offset + code) & 2);
524
537
  state.up = !state.held && !!(this.#dataView.getUint8(offset + code) & 2);
@@ -1140,15 +1153,6 @@ class KeyboardContext {
1140
1153
  return state;
1141
1154
  }
1142
1155
  }
1143
- var MAX_PEERS = 12;
1144
- var PEERS_ARRAY_OFFSET = 32;
1145
- var PEER_CTX_SIZE = 8;
1146
- var LAST_ROLLBACK_DEPTH_OFFSET = 128;
1147
- var TOTAL_ROLLBACKS_OFFSET = 132;
1148
- var FRAMES_RESIMULATED_OFFSET = 136;
1149
- var PEER_CONNECTED_OFFSET = 0;
1150
- var PEER_SEQ_OFFSET = 2;
1151
- var PEER_ACK_OFFSET = 4;
1152
1156
  var STATUS_MAP = {
1153
1157
  0: "offline",
1154
1158
  1: "local",
@@ -1159,7 +1163,7 @@ var STATUS_MAP = {
1159
1163
 
1160
1164
  class NetContext {
1161
1165
  dataView;
1162
- #peers = Array.from({ length: MAX_PEERS }, () => ({
1166
+ #peers = Array.from({ length: MAX_PLAYERS }, () => ({
1163
1167
  isLocal: false,
1164
1168
  seq: -1,
1165
1169
  ack: -1
@@ -1177,38 +1181,38 @@ class NetContext {
1177
1181
  if (!this.#hasValidBuffer()) {
1178
1182
  throw new Error("NetContext dataView is not valid");
1179
1183
  }
1180
- return this.dataView.getUint8(0);
1184
+ return this.dataView.getUint8(NET_CTX_PEER_COUNT_OFFSET);
1181
1185
  }
1182
1186
  get localPeerId() {
1183
1187
  if (!this.#hasValidBuffer()) {
1184
1188
  throw new Error("NetContext dataView is not valid");
1185
1189
  }
1186
- return this.dataView.getUint8(1);
1190
+ return this.dataView.getUint8(NET_CTX_LOCAL_PEER_ID_OFFSET);
1187
1191
  }
1188
1192
  get isInSession() {
1189
1193
  if (!this.#hasValidBuffer()) {
1190
1194
  throw new Error("NetContext dataView is not valid");
1191
1195
  }
1192
- return this.dataView.getUint8(2) !== 0;
1196
+ return this.dataView.getUint8(NET_CTX_IN_SESSION_OFFSET) !== 0;
1193
1197
  }
1194
1198
  get status() {
1195
1199
  if (!this.#hasValidBuffer()) {
1196
1200
  throw new Error("NetContext dataView is not valid");
1197
1201
  }
1198
- const statusByte = this.dataView.getUint8(3);
1202
+ const statusByte = this.dataView.getUint8(NET_CTX_STATUS_OFFSET);
1199
1203
  return STATUS_MAP[statusByte] ?? "local";
1200
1204
  }
1201
1205
  get matchFrame() {
1202
1206
  if (!this.#hasValidBuffer()) {
1203
1207
  throw new Error("NetContext dataView is not valid");
1204
1208
  }
1205
- return this.dataView.getUint32(4, true);
1209
+ return this.dataView.getUint32(NET_CTX_MATCH_FRAME_OFFSET, true);
1206
1210
  }
1207
1211
  get sessionStartFrame() {
1208
1212
  if (!this.#hasValidBuffer()) {
1209
1213
  throw new Error("NetContext dataView is not valid");
1210
1214
  }
1211
- return this.dataView.getUint32(8, true);
1215
+ return this.dataView.getUint32(NET_CTX_SESSION_START_FRAME_OFFSET, true);
1212
1216
  }
1213
1217
  get roomCode() {
1214
1218
  if (!this.#hasValidBuffer()) {
@@ -1216,7 +1220,7 @@ class NetContext {
1216
1220
  }
1217
1221
  const bytes = [];
1218
1222
  for (let i = 0;i < 8; i++) {
1219
- const byte = this.dataView.getUint8(12 + i);
1223
+ const byte = this.dataView.getUint8(NET_CTX_ROOM_CODE_OFFSET + i);
1220
1224
  if (byte === 0)
1221
1225
  break;
1222
1226
  bytes.push(byte);
@@ -1229,7 +1233,7 @@ class NetContext {
1229
1233
  }
1230
1234
  const bytes = [];
1231
1235
  for (let i = 0;i < 8; i++) {
1232
- const byte = this.dataView.getUint8(20 + i);
1236
+ const byte = this.dataView.getUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i);
1233
1237
  if (byte === 0)
1234
1238
  break;
1235
1239
  bytes.push(byte);
@@ -1241,11 +1245,11 @@ class NetContext {
1241
1245
  throw new Error("NetContext dataView is not valid");
1242
1246
  }
1243
1247
  for (let i = 0;i < 8; i++) {
1244
- this.dataView.setUint8(20 + i, 0);
1248
+ this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i, 0);
1245
1249
  }
1246
1250
  if (code) {
1247
1251
  for (let i = 0;i < Math.min(code.length, 7); i++) {
1248
- this.dataView.setUint8(20 + i, code.charCodeAt(i));
1252
+ this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i, code.charCodeAt(i));
1249
1253
  }
1250
1254
  }
1251
1255
  }
@@ -1253,13 +1257,13 @@ class NetContext {
1253
1257
  if (!this.#hasValidBuffer()) {
1254
1258
  return false;
1255
1259
  }
1256
- return this.dataView.getUint8(28) !== 0;
1260
+ return this.dataView.getUint8(NET_CTX_WANTS_DISCONNECT_OFFSET) !== 0;
1257
1261
  }
1258
1262
  set wantsDisconnect(value) {
1259
1263
  if (!this.#hasValidBuffer()) {
1260
1264
  throw new Error("NetContext dataView is not valid");
1261
1265
  }
1262
- this.dataView.setUint8(28, value ? 1 : 0);
1266
+ this.dataView.setUint8(NET_CTX_WANTS_DISCONNECT_OFFSET, value ? 1 : 0);
1263
1267
  }
1264
1268
  get peers() {
1265
1269
  if (!this.#hasValidBuffer()) {
@@ -1269,13 +1273,13 @@ class NetContext {
1269
1273
  const localPeerId = this.localPeerId;
1270
1274
  const matchFrame = this.matchFrame;
1271
1275
  let minRemoteSeq = -1;
1272
- for (let i = 0;i < MAX_PEERS; i++) {
1276
+ for (let i = 0;i < MAX_PLAYERS; i++) {
1273
1277
  if (i === localPeerId)
1274
1278
  continue;
1275
- const peerOffset = PEERS_ARRAY_OFFSET + i * PEER_CTX_SIZE;
1276
- if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET) !== 1)
1279
+ const peerOffset = NET_CTX_PEERS_OFFSET + i * PEER_CTX_SIZE;
1280
+ if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET) !== 1)
1277
1281
  continue;
1278
- const seq = dv.getInt16(peerOffset + PEER_SEQ_OFFSET, true);
1282
+ const seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET, true);
1279
1283
  if (seq < 0)
1280
1284
  continue;
1281
1285
  if (minRemoteSeq === -1 || seq < minRemoteSeq) {
@@ -1283,9 +1287,9 @@ class NetContext {
1283
1287
  }
1284
1288
  }
1285
1289
  this.#peersResult.length = 0;
1286
- for (let i = 0;i < MAX_PEERS; i++) {
1287
- const peerOffset = PEERS_ARRAY_OFFSET + i * PEER_CTX_SIZE;
1288
- if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET) !== 1)
1290
+ for (let i = 0;i < MAX_PLAYERS; i++) {
1291
+ const peerOffset = NET_CTX_PEERS_OFFSET + i * PEER_CTX_SIZE;
1292
+ if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET) !== 1)
1289
1293
  continue;
1290
1294
  const peer = this.#peers[i];
1291
1295
  if (!peer) {
@@ -1297,8 +1301,8 @@ class NetContext {
1297
1301
  peer.seq = matchFrame;
1298
1302
  peer.ack = minRemoteSeq;
1299
1303
  } else {
1300
- peer.seq = dv.getInt16(peerOffset + PEER_SEQ_OFFSET, true);
1301
- peer.ack = dv.getInt16(peerOffset + PEER_ACK_OFFSET, true);
1304
+ peer.seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET, true);
1305
+ peer.ack = dv.getInt16(peerOffset + PEER_CTX_ACK_OFFSET, true);
1302
1306
  }
1303
1307
  this.#peersResult.push(peer);
1304
1308
  }
@@ -1308,19 +1312,19 @@ class NetContext {
1308
1312
  if (!this.#hasValidBuffer()) {
1309
1313
  throw new Error("NetContext dataView is not valid");
1310
1314
  }
1311
- return this.dataView.getUint32(LAST_ROLLBACK_DEPTH_OFFSET, true);
1315
+ return this.dataView.getUint32(NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET, true);
1312
1316
  }
1313
1317
  get totalRollbacks() {
1314
1318
  if (!this.#hasValidBuffer()) {
1315
1319
  throw new Error("NetContext dataView is not valid");
1316
1320
  }
1317
- return this.dataView.getUint32(TOTAL_ROLLBACKS_OFFSET, true);
1321
+ return this.dataView.getUint32(NET_CTX_TOTAL_ROLLBACKS_OFFSET, true);
1318
1322
  }
1319
1323
  get framesResimulated() {
1320
1324
  if (!this.#hasValidBuffer()) {
1321
1325
  throw new Error("NetContext dataView is not valid");
1322
1326
  }
1323
- return Number(this.dataView.getBigUint64(FRAMES_RESIMULATED_OFFSET, true));
1327
+ return Number(this.dataView.getBigUint64(NET_CTX_FRAMES_RESIMULATED_OFFSET, true));
1324
1328
  }
1325
1329
  }
1326
1330
 
@@ -1333,21 +1337,41 @@ class TimeContext {
1333
1337
  if (!this.dataView) {
1334
1338
  throw new Error("TimeContext DataView is not initialized");
1335
1339
  }
1336
- return this.dataView.getUint32(0, true);
1340
+ return this.dataView.getUint32(TIME_CTX_FRAME_OFFSET, true);
1337
1341
  }
1338
1342
  get dt() {
1339
1343
  if (!this.dataView) {
1340
1344
  throw new Error("TimeContext DataView is not initialized");
1341
1345
  }
1342
- return this.dataView.getUint32(4, true) / 1000;
1346
+ return this.dataView.getUint32(TIME_CTX_DT_MS_OFFSET, true) / 1000;
1343
1347
  }
1344
1348
  get time() {
1345
1349
  if (!this.dataView) {
1346
1350
  throw new Error("TimeContext DataView is not initialized");
1347
1351
  }
1348
- return this.dataView.getUint32(8, true) / 1000;
1352
+ return this.dataView.getUint32(TIME_CTX_TOTAL_MS_OFFSET, true) / 1000;
1349
1353
  }
1350
1354
  }
1355
+ var EVENT_PAYLOAD_SIZE = 8;
1356
+ var EVENT_PAYLOAD_ALIGN = 4;
1357
+ function keyToKeyCode(key) {
1358
+ return Key[key];
1359
+ }
1360
+ function keyCodeToKey(code) {
1361
+ return Key[code];
1362
+ }
1363
+ function mouseButtonToMouseButtonCode(button) {
1364
+ return MouseButton[button];
1365
+ }
1366
+ function mouseButtonCodeToMouseButton(code) {
1367
+ return MouseButton[code];
1368
+ }
1369
+ function inputSourceToInputSourceCode(source) {
1370
+ return InputSource[source];
1371
+ }
1372
+ function inputSourceCodeToInputSource(code) {
1373
+ return InputSource[code];
1374
+ }
1351
1375
  var TAPE_MAGIC = 1413566533;
1352
1376
  function readTapeHeader(tape) {
1353
1377
  const view = new DataView(tape.buffer, tape.byteOffset, tape.byteLength);
@@ -1363,7 +1387,7 @@ function readTapeHeader(tape) {
1363
1387
  eventCount: view.getUint16(14, true)
1364
1388
  };
1365
1389
  }
1366
- var DEFAULT_WASM_URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.88/wasm/bloop.wasm");
1390
+ var DEFAULT_WASM_URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.89/wasm/bloop.wasm");
1367
1391
  var MAX_ROLLBACK_FRAMES = 500;
1368
1392
  var TIME_CTX_OFFSET = 0;
1369
1393
  var INPUT_CTX_OFFSET = TIME_CTX_OFFSET + 4;
@@ -1628,6 +1652,7 @@ async function mount(mountable, options) {
1628
1652
  memory,
1629
1653
  __systems: function(system_handle, ptr) {
1630
1654
  mountable.hooks.setBuffer(memory.buffer);
1655
+ mountable.hooks.setContext(ptr);
1631
1656
  mountable.hooks.systemsCallback(system_handle, ptr);
1632
1657
  },
1633
1658
  __before_frame: function(ptr, frame) {
@@ -1774,9 +1799,15 @@ class Bloop {
1774
1799
  const inputCtxPtr = dv.getUint32(INPUT_CTX_OFFSET, true);
1775
1800
  const netCtxPtr = dv.getUint32(NET_CTX_OFFSET, true);
1776
1801
  this.#context.rawPointer = ptr;
1777
- this.#context.inputs.dataView = new DataView(this.#engineBuffer, inputCtxPtr);
1778
- this.#context.time.dataView = new DataView(this.#engineBuffer, timeCtxPtr);
1779
- this.#context.net.dataView = new DataView(this.#engineBuffer, netCtxPtr);
1802
+ if (!this.#context.inputs.hasDataView() || this.#context.inputs.dataView.buffer !== this.#engineBuffer || this.#context.inputs.dataView.byteOffset !== inputCtxPtr) {
1803
+ this.#context.inputs.dataView = new DataView(this.#engineBuffer, inputCtxPtr);
1804
+ }
1805
+ if (this.#context.time.dataView?.buffer !== this.#engineBuffer || this.#context.time.dataView?.byteOffset !== timeCtxPtr) {
1806
+ this.#context.time.dataView = new DataView(this.#engineBuffer, timeCtxPtr);
1807
+ }
1808
+ if (this.#context.net.dataView?.buffer !== this.#engineBuffer || this.#context.net.dataView?.byteOffset !== netCtxPtr) {
1809
+ this.#context.net.dataView = new DataView(this.#engineBuffer, netCtxPtr);
1810
+ }
1780
1811
  },
1781
1812
  systemsCallback: (system_handle, ptr) => {
1782
1813
  this.hooks.setContext(ptr);
@@ -1881,6 +1912,13 @@ class Bloop {
1881
1912
  system.netcode?.(this.#context);
1882
1913
  break;
1883
1914
  }
1915
+ case exports_enums.EventType.NetSessionInit:
1916
+ console.log("[bloop] NetSessionInit event received");
1917
+ this.#context.event = {
1918
+ type: "session:start"
1919
+ };
1920
+ system.netcode?.(this.#context);
1921
+ break;
1884
1922
  default:
1885
1923
  break;
1886
1924
  }
@@ -2183,15 +2221,33 @@ var NetJoinFailReason2;
2183
2221
  NetJoinFailReason22[NetJoinFailReason22["room_not_found"] = 3] = "room_not_found";
2184
2222
  NetJoinFailReason22[NetJoinFailReason22["already_in_room"] = 4] = "already_in_room";
2185
2223
  })(NetJoinFailReason2 ||= {});
2186
- var MAX_PLAYERS2 = 12;
2187
- var KEYBOARD_OFFSET2 = 0;
2188
- var MOUSE_OFFSET2 = 256;
2189
- var MOUSE_BUTTONS_OFFSET2 = 16;
2224
+ var PEER_CTX_CONNECTED_OFFSET2 = 0;
2225
+ var PEER_CTX_SEQ_OFFSET2 = 2;
2226
+ var PEER_CTX_ACK_OFFSET2 = 4;
2227
+ var PEER_CTX_SIZE2 = 8;
2228
+ var NET_CTX_PEER_COUNT_OFFSET2 = 0;
2229
+ var NET_CTX_LOCAL_PEER_ID_OFFSET2 = 1;
2230
+ var NET_CTX_IN_SESSION_OFFSET2 = 2;
2231
+ var NET_CTX_STATUS_OFFSET2 = 3;
2232
+ var NET_CTX_MATCH_FRAME_OFFSET2 = 4;
2233
+ var NET_CTX_SESSION_START_FRAME_OFFSET2 = 8;
2234
+ var NET_CTX_ROOM_CODE_OFFSET2 = 12;
2235
+ var NET_CTX_WANTS_ROOM_CODE_OFFSET2 = 20;
2236
+ var NET_CTX_WANTS_DISCONNECT_OFFSET2 = 28;
2237
+ var NET_CTX_PEERS_OFFSET2 = 32;
2238
+ var NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET2 = 128;
2239
+ var NET_CTX_TOTAL_ROLLBACKS_OFFSET2 = 132;
2240
+ var NET_CTX_FRAMES_RESIMULATED_OFFSET2 = 136;
2241
+ var MOUSE_CTX_X_OFFSET2 = 0;
2242
+ var MOUSE_CTX_Y_OFFSET2 = 4;
2243
+ var MOUSE_CTX_WHEEL_X_OFFSET2 = 8;
2244
+ var MOUSE_CTX_WHEEL_Y_OFFSET2 = 12;
2245
+ var MOUSE_CTX_BUTTON_STATES_OFFSET2 = 16;
2246
+ var PLAYER_INPUTS_KEY_CTX_OFFSET2 = 0;
2247
+ var PLAYER_INPUTS_MOUSE_CTX_OFFSET2 = 256;
2190
2248
  var PLAYER_INPUTS_SIZE2 = 280;
2191
- var INPUT_CTX_SIZE2 = MAX_PLAYERS2 * PLAYER_INPUTS_SIZE2;
2192
- function mouseButtonCodeToMouseButton2(code) {
2193
- return MouseButton2[code];
2194
- }
2249
+ var MAX_PLAYERS2 = 12;
2250
+
2195
2251
  class PlayerInputContext2 {
2196
2252
  #dataView;
2197
2253
  #keys;
@@ -2201,13 +2257,13 @@ class PlayerInputContext2 {
2201
2257
  }
2202
2258
  get keys() {
2203
2259
  if (!this.#keys) {
2204
- this.#keys = new KeyboardContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + KEYBOARD_OFFSET2));
2260
+ this.#keys = new KeyboardContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_KEY_CTX_OFFSET2));
2205
2261
  }
2206
2262
  return this.#keys;
2207
2263
  }
2208
2264
  get mouse() {
2209
2265
  if (!this.#mouse) {
2210
- this.#mouse = new MouseContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + MOUSE_OFFSET2));
2266
+ this.#mouse = new MouseContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_MOUSE_CTX_OFFSET2));
2211
2267
  }
2212
2268
  return this.#mouse;
2213
2269
  }
@@ -2232,6 +2288,9 @@ class InputContext2 {
2232
2288
  this.#dataView = dataView;
2233
2289
  this.#buildPlayers();
2234
2290
  }
2291
+ hasDataView() {
2292
+ return !!this.#dataView;
2293
+ }
2235
2294
  #buildPlayers() {
2236
2295
  this.#players = [];
2237
2296
  for (let i = 0;i < MAX_PLAYERS2; i++) {
@@ -2264,19 +2323,19 @@ class MouseContext2 {
2264
2323
  this.#dataView = dataView;
2265
2324
  }
2266
2325
  get x() {
2267
- return this.#dataView.getFloat32(0, true);
2326
+ return this.#dataView.getFloat32(MOUSE_CTX_X_OFFSET2, true);
2268
2327
  }
2269
2328
  get y() {
2270
- return this.#dataView.getFloat32(4, true);
2329
+ return this.#dataView.getFloat32(MOUSE_CTX_Y_OFFSET2, true);
2271
2330
  }
2272
2331
  get wheel() {
2273
2332
  return { x: this.wheelX, y: this.wheelY };
2274
2333
  }
2275
2334
  get wheelX() {
2276
- return this.#dataView.getFloat32(8, true);
2335
+ return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_X_OFFSET2, true);
2277
2336
  }
2278
2337
  get wheelY() {
2279
- return this.#dataView.getFloat32(12, true);
2338
+ return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_Y_OFFSET2, true);
2280
2339
  }
2281
2340
  get left() {
2282
2341
  return this.#buttonState(1);
@@ -2295,7 +2354,7 @@ class MouseContext2 {
2295
2354
  state = { down: false, held: false, up: false };
2296
2355
  this.#buttonStates.set(code, state);
2297
2356
  }
2298
- const offset = MOUSE_BUTTONS_OFFSET2;
2357
+ const offset = MOUSE_CTX_BUTTON_STATES_OFFSET2;
2299
2358
  state.held = !!(this.#dataView.getUint8(offset + code) & 1);
2300
2359
  state.down = state.held && !(this.#dataView.getUint8(offset + code) & 2);
2301
2360
  state.up = !state.held && !!(this.#dataView.getUint8(offset + code) & 2);
@@ -2917,15 +2976,6 @@ class KeyboardContext2 {
2917
2976
  return state;
2918
2977
  }
2919
2978
  }
2920
- var MAX_PEERS2 = 12;
2921
- var PEERS_ARRAY_OFFSET2 = 32;
2922
- var PEER_CTX_SIZE2 = 8;
2923
- var LAST_ROLLBACK_DEPTH_OFFSET2 = 128;
2924
- var TOTAL_ROLLBACKS_OFFSET2 = 132;
2925
- var FRAMES_RESIMULATED_OFFSET2 = 136;
2926
- var PEER_CONNECTED_OFFSET2 = 0;
2927
- var PEER_SEQ_OFFSET2 = 2;
2928
- var PEER_ACK_OFFSET2 = 4;
2929
2979
  var STATUS_MAP2 = {
2930
2980
  0: "offline",
2931
2981
  1: "local",
@@ -2936,7 +2986,7 @@ var STATUS_MAP2 = {
2936
2986
 
2937
2987
  class NetContext2 {
2938
2988
  dataView;
2939
- #peers = Array.from({ length: MAX_PEERS2 }, () => ({
2989
+ #peers = Array.from({ length: MAX_PLAYERS2 }, () => ({
2940
2990
  isLocal: false,
2941
2991
  seq: -1,
2942
2992
  ack: -1
@@ -2954,38 +3004,38 @@ class NetContext2 {
2954
3004
  if (!this.#hasValidBuffer()) {
2955
3005
  throw new Error("NetContext dataView is not valid");
2956
3006
  }
2957
- return this.dataView.getUint8(0);
3007
+ return this.dataView.getUint8(NET_CTX_PEER_COUNT_OFFSET2);
2958
3008
  }
2959
3009
  get localPeerId() {
2960
3010
  if (!this.#hasValidBuffer()) {
2961
3011
  throw new Error("NetContext dataView is not valid");
2962
3012
  }
2963
- return this.dataView.getUint8(1);
3013
+ return this.dataView.getUint8(NET_CTX_LOCAL_PEER_ID_OFFSET2);
2964
3014
  }
2965
3015
  get isInSession() {
2966
3016
  if (!this.#hasValidBuffer()) {
2967
3017
  throw new Error("NetContext dataView is not valid");
2968
3018
  }
2969
- return this.dataView.getUint8(2) !== 0;
3019
+ return this.dataView.getUint8(NET_CTX_IN_SESSION_OFFSET2) !== 0;
2970
3020
  }
2971
3021
  get status() {
2972
3022
  if (!this.#hasValidBuffer()) {
2973
3023
  throw new Error("NetContext dataView is not valid");
2974
3024
  }
2975
- const statusByte = this.dataView.getUint8(3);
3025
+ const statusByte = this.dataView.getUint8(NET_CTX_STATUS_OFFSET2);
2976
3026
  return STATUS_MAP2[statusByte] ?? "local";
2977
3027
  }
2978
3028
  get matchFrame() {
2979
3029
  if (!this.#hasValidBuffer()) {
2980
3030
  throw new Error("NetContext dataView is not valid");
2981
3031
  }
2982
- return this.dataView.getUint32(4, true);
3032
+ return this.dataView.getUint32(NET_CTX_MATCH_FRAME_OFFSET2, true);
2983
3033
  }
2984
3034
  get sessionStartFrame() {
2985
3035
  if (!this.#hasValidBuffer()) {
2986
3036
  throw new Error("NetContext dataView is not valid");
2987
3037
  }
2988
- return this.dataView.getUint32(8, true);
3038
+ return this.dataView.getUint32(NET_CTX_SESSION_START_FRAME_OFFSET2, true);
2989
3039
  }
2990
3040
  get roomCode() {
2991
3041
  if (!this.#hasValidBuffer()) {
@@ -2993,7 +3043,7 @@ class NetContext2 {
2993
3043
  }
2994
3044
  const bytes = [];
2995
3045
  for (let i = 0;i < 8; i++) {
2996
- const byte = this.dataView.getUint8(12 + i);
3046
+ const byte = this.dataView.getUint8(NET_CTX_ROOM_CODE_OFFSET2 + i);
2997
3047
  if (byte === 0)
2998
3048
  break;
2999
3049
  bytes.push(byte);
@@ -3006,7 +3056,7 @@ class NetContext2 {
3006
3056
  }
3007
3057
  const bytes = [];
3008
3058
  for (let i = 0;i < 8; i++) {
3009
- const byte = this.dataView.getUint8(20 + i);
3059
+ const byte = this.dataView.getUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET2 + i);
3010
3060
  if (byte === 0)
3011
3061
  break;
3012
3062
  bytes.push(byte);
@@ -3018,11 +3068,11 @@ class NetContext2 {
3018
3068
  throw new Error("NetContext dataView is not valid");
3019
3069
  }
3020
3070
  for (let i = 0;i < 8; i++) {
3021
- this.dataView.setUint8(20 + i, 0);
3071
+ this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET2 + i, 0);
3022
3072
  }
3023
3073
  if (code) {
3024
3074
  for (let i = 0;i < Math.min(code.length, 7); i++) {
3025
- this.dataView.setUint8(20 + i, code.charCodeAt(i));
3075
+ this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET2 + i, code.charCodeAt(i));
3026
3076
  }
3027
3077
  }
3028
3078
  }
@@ -3030,13 +3080,13 @@ class NetContext2 {
3030
3080
  if (!this.#hasValidBuffer()) {
3031
3081
  return false;
3032
3082
  }
3033
- return this.dataView.getUint8(28) !== 0;
3083
+ return this.dataView.getUint8(NET_CTX_WANTS_DISCONNECT_OFFSET2) !== 0;
3034
3084
  }
3035
3085
  set wantsDisconnect(value) {
3036
3086
  if (!this.#hasValidBuffer()) {
3037
3087
  throw new Error("NetContext dataView is not valid");
3038
3088
  }
3039
- this.dataView.setUint8(28, value ? 1 : 0);
3089
+ this.dataView.setUint8(NET_CTX_WANTS_DISCONNECT_OFFSET2, value ? 1 : 0);
3040
3090
  }
3041
3091
  get peers() {
3042
3092
  if (!this.#hasValidBuffer()) {
@@ -3046,13 +3096,13 @@ class NetContext2 {
3046
3096
  const localPeerId = this.localPeerId;
3047
3097
  const matchFrame = this.matchFrame;
3048
3098
  let minRemoteSeq = -1;
3049
- for (let i = 0;i < MAX_PEERS2; i++) {
3099
+ for (let i = 0;i < MAX_PLAYERS2; i++) {
3050
3100
  if (i === localPeerId)
3051
3101
  continue;
3052
- const peerOffset = PEERS_ARRAY_OFFSET2 + i * PEER_CTX_SIZE2;
3053
- if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET2) !== 1)
3102
+ const peerOffset = NET_CTX_PEERS_OFFSET2 + i * PEER_CTX_SIZE2;
3103
+ if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET2) !== 1)
3054
3104
  continue;
3055
- const seq = dv.getInt16(peerOffset + PEER_SEQ_OFFSET2, true);
3105
+ const seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET2, true);
3056
3106
  if (seq < 0)
3057
3107
  continue;
3058
3108
  if (minRemoteSeq === -1 || seq < minRemoteSeq) {
@@ -3060,9 +3110,9 @@ class NetContext2 {
3060
3110
  }
3061
3111
  }
3062
3112
  this.#peersResult.length = 0;
3063
- for (let i = 0;i < MAX_PEERS2; i++) {
3064
- const peerOffset = PEERS_ARRAY_OFFSET2 + i * PEER_CTX_SIZE2;
3065
- if (dv.getUint8(peerOffset + PEER_CONNECTED_OFFSET2) !== 1)
3113
+ for (let i = 0;i < MAX_PLAYERS2; i++) {
3114
+ const peerOffset = NET_CTX_PEERS_OFFSET2 + i * PEER_CTX_SIZE2;
3115
+ if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET2) !== 1)
3066
3116
  continue;
3067
3117
  const peer = this.#peers[i];
3068
3118
  if (!peer) {
@@ -3074,8 +3124,8 @@ class NetContext2 {
3074
3124
  peer.seq = matchFrame;
3075
3125
  peer.ack = minRemoteSeq;
3076
3126
  } else {
3077
- peer.seq = dv.getInt16(peerOffset + PEER_SEQ_OFFSET2, true);
3078
- peer.ack = dv.getInt16(peerOffset + PEER_ACK_OFFSET2, true);
3127
+ peer.seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET2, true);
3128
+ peer.ack = dv.getInt16(peerOffset + PEER_CTX_ACK_OFFSET2, true);
3079
3129
  }
3080
3130
  this.#peersResult.push(peer);
3081
3131
  }
@@ -3085,21 +3135,24 @@ class NetContext2 {
3085
3135
  if (!this.#hasValidBuffer()) {
3086
3136
  throw new Error("NetContext dataView is not valid");
3087
3137
  }
3088
- return this.dataView.getUint32(LAST_ROLLBACK_DEPTH_OFFSET2, true);
3138
+ return this.dataView.getUint32(NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET2, true);
3089
3139
  }
3090
3140
  get totalRollbacks() {
3091
3141
  if (!this.#hasValidBuffer()) {
3092
3142
  throw new Error("NetContext dataView is not valid");
3093
3143
  }
3094
- return this.dataView.getUint32(TOTAL_ROLLBACKS_OFFSET2, true);
3144
+ return this.dataView.getUint32(NET_CTX_TOTAL_ROLLBACKS_OFFSET2, true);
3095
3145
  }
3096
3146
  get framesResimulated() {
3097
3147
  if (!this.#hasValidBuffer()) {
3098
3148
  throw new Error("NetContext dataView is not valid");
3099
3149
  }
3100
- return Number(this.dataView.getBigUint64(FRAMES_RESIMULATED_OFFSET2, true));
3150
+ return Number(this.dataView.getBigUint64(NET_CTX_FRAMES_RESIMULATED_OFFSET2, true));
3101
3151
  }
3102
3152
  }
3153
+ function mouseButtonCodeToMouseButton2(code) {
3154
+ return MouseButton2[code];
3155
+ }
3103
3156
  var TAPE_MAGIC2 = 1413566533;
3104
3157
  function readTapeHeader2(tape) {
3105
3158
  const view = new DataView(tape.buffer, tape.byteOffset, tape.byteLength);
@@ -3115,7 +3168,7 @@ function readTapeHeader2(tape) {
3115
3168
  eventCount: view.getUint16(14, true)
3116
3169
  };
3117
3170
  }
3118
- var DEFAULT_WASM_URL2 = new URL("https://unpkg.com/@bloopjs/engine@0.0.88/wasm/bloop.wasm");
3171
+ var DEFAULT_WASM_URL2 = new URL("https://unpkg.com/@bloopjs/engine@0.0.89/wasm/bloop.wasm");
3119
3172
  var TIME_CTX_OFFSET2 = 0;
3120
3173
  var INPUT_CTX_OFFSET2 = TIME_CTX_OFFSET2 + 4;
3121
3174
  var EVENTS_OFFSET2 = INPUT_CTX_OFFSET2 + 4;
@@ -6327,6 +6380,138 @@ function joinRoom(brokerUrl, _roomId, cbs) {
6327
6380
  }
6328
6381
  }
6329
6382
 
6383
+ // src/netcode/reconcile.ts
6384
+ var udp = null;
6385
+ var localPeerId = null;
6386
+ var remotePeerId = null;
6387
+ var localStringPeerId = null;
6388
+ var remoteStringPeerId = null;
6389
+ var incomingPackets = [];
6390
+ var actual = {
6391
+ roomCode: ""
6392
+ };
6393
+ async function reconcile(app, signal) {
6394
+ app.beforeFrame.subscribe((_frame) => {
6395
+ if (!app.game.context.net.isInSession) {
6396
+ return;
6397
+ }
6398
+ try {
6399
+ receivePackets(app);
6400
+ sendPacket(app);
6401
+ } catch (e4) {
6402
+ console.error("Error in beforeFrame:", e4);
6403
+ }
6404
+ });
6405
+ logger.onLog = (log) => {
6406
+ addLog(log);
6407
+ };
6408
+ while (!signal.aborted) {
6409
+ const { net } = app.game.context;
6410
+ if (net.wantsRoomCode && actual.roomCode !== net.wantsRoomCode) {
6411
+ console.log("[netcode] wants a room code", {
6412
+ actual: actual.roomCode,
6413
+ wants: net.wantsRoomCode
6414
+ });
6415
+ actual.roomCode = net.wantsRoomCode;
6416
+ joinRollbackRoom(net.wantsRoomCode, app);
6417
+ }
6418
+ await sleep(150);
6419
+ }
6420
+ }
6421
+ async function sleep(ms) {
6422
+ return new Promise((resolve) => setTimeout(resolve, ms));
6423
+ }
6424
+ function joinRollbackRoom(roomId, app) {
6425
+ app.joinRoom(roomId, {
6426
+ onPeerIdAssign: (peerId) => {
6427
+ localStringPeerId = peerId;
6428
+ },
6429
+ onBrokerMessage: (_message) => {},
6430
+ onMessage(_peerId, data, _reliable) {
6431
+ incomingPackets.push(new Uint8Array(data));
6432
+ },
6433
+ onDataChannelClose(_peerId, reliable) {
6434
+ if (!reliable && remotePeerId !== null) {
6435
+ app.sim.emit.network("peer:leave", { peerId: remotePeerId });
6436
+ }
6437
+ },
6438
+ onDataChannelOpen(peerId, reliable, channel) {
6439
+ if (!reliable) {
6440
+ udp = channel;
6441
+ if (localStringPeerId === null) {
6442
+ console.error("[netcode] Local peer ID not assigned yet!");
6443
+ return;
6444
+ }
6445
+ const ids = assignPeerIds(localStringPeerId, peerId);
6446
+ localPeerId = ids.local;
6447
+ setLocalId(localPeerId);
6448
+ remotePeerId = ids.remote;
6449
+ remoteStringPeerId = peerId;
6450
+ setRemoteId(remotePeerId);
6451
+ app.sim.emit.network("peer:join", { peerId: localPeerId });
6452
+ app.sim.emit.network("peer:join", { peerId: remotePeerId });
6453
+ app.sim.emit.network("peer:assign_local_id", { peerId: localPeerId });
6454
+ app.sim.emit.network("session:start", {});
6455
+ }
6456
+ },
6457
+ onPeerConnected(peerId) {
6458
+ addPeer({
6459
+ id: peerId,
6460
+ nickname: peerId.substring(0, 6),
6461
+ ack: -1,
6462
+ seq: -1,
6463
+ lastPacketTime: performance.now()
6464
+ });
6465
+ console.log(`[netcode] Peer connected: ${peerId}. Total peers: ${debugState.netStatus.value.peers.length}`);
6466
+ },
6467
+ onPeerDisconnected(peerId) {
6468
+ removePeer(peerId);
6469
+ if (remotePeerId !== null && peerId === remoteStringPeerId) {
6470
+ app.sim.emit.network("peer:leave", { peerId: remotePeerId });
6471
+ app.sim.emit.network("session:end", {});
6472
+ }
6473
+ }
6474
+ });
6475
+ }
6476
+ function assignPeerIds(localId, remoteId) {
6477
+ if (localId < remoteId) {
6478
+ return { local: 0, remote: 1 };
6479
+ } else {
6480
+ return { local: 1, remote: 0 };
6481
+ }
6482
+ }
6483
+ function receivePackets(app) {
6484
+ for (const packetData of incomingPackets) {
6485
+ app.sim.emit.packet(packetData);
6486
+ if (remotePeerId == null) {
6487
+ return;
6488
+ }
6489
+ const peerState = unwrap(app.sim.net.peers[remotePeerId], `Remote peer state not found for peerId ${remotePeerId}`);
6490
+ updatePeer(remoteStringPeerId, {
6491
+ ack: peerState.ack,
6492
+ seq: peerState.seq,
6493
+ lastPacketTime: performance.now()
6494
+ });
6495
+ }
6496
+ incomingPackets.length = 0;
6497
+ }
6498
+ function sendPacket(app) {
6499
+ if (!udp || remotePeerId === null) {
6500
+ console.warn("[netcode] Cannot send packet, udp or remotePeerId is null");
6501
+ return;
6502
+ }
6503
+ if (udp.readyState !== "open") {
6504
+ console.warn("[netcode] Data channel not open, cannot send packet. readyState=", udp.readyState);
6505
+ return;
6506
+ }
6507
+ const packet = app.sim.getOutboundPacket(remotePeerId);
6508
+ if (!packet) {
6509
+ console.warn("[netcode] No packet to send");
6510
+ return;
6511
+ }
6512
+ udp.send(packet);
6513
+ }
6514
+
6330
6515
  // src/App.ts
6331
6516
  var DEFAULT_BROKER_URL = "wss://webrtc-divine-glade-8064.fly.dev/ws";
6332
6517
  async function start(opts) {
@@ -6350,6 +6535,7 @@ class App {
6350
6535
  #unsubscribe = null;
6351
6536
  #now = performance.now();
6352
6537
  #debugUi = null;
6538
+ #abortController = new AbortController;
6353
6539
  constructor(sim, game, brokerUrl, debugUiOpts) {
6354
6540
  this.#sim = sim;
6355
6541
  this.game = game;
@@ -6363,6 +6549,9 @@ class App {
6363
6549
  this.beforeFrame.notify(frame);
6364
6550
  };
6365
6551
  this.subscribe();
6552
+ reconcile(this, this.#abortController.signal).catch((err) => {
6553
+ console.error("Error in lemmyloop:", err);
6554
+ });
6366
6555
  }
6367
6556
  get sim() {
6368
6557
  return this.#sim;
@@ -6568,6 +6757,7 @@ class App {
6568
6757
  this.afterFrame.unsubscribeAll();
6569
6758
  this.onHmr.unsubscribeAll();
6570
6759
  this.#debugUi?.unmount();
6760
+ this.#abortController.abort();
6571
6761
  }
6572
6762
  async acceptHmr(module, opts) {
6573
6763
  const game = module.game ?? module;
@@ -6612,131 +6802,13 @@ var PacketType;
6612
6802
  PacketType2[PacketType2["None"] = 0] = "None";
6613
6803
  PacketType2[PacketType2["Inputs"] = 1] = "Inputs";
6614
6804
  })(PacketType ||= {});
6615
- // src/netcode/scaffold.ts
6616
- function joinRollbackRoom(roomId, app, opts) {
6617
- let udp = null;
6618
- let sessionActive = false;
6619
- let localPeerId = null;
6620
- let remotePeerId = null;
6621
- let localStringPeerId = null;
6622
- let remoteStringPeerId = null;
6623
- const incomingPackets = [];
6624
- function assignPeerIds(localId, remoteId) {
6625
- if (localId < remoteId) {
6626
- return { local: 0, remote: 1 };
6627
- } else {
6628
- return { local: 1, remote: 0 };
6629
- }
6630
- }
6631
- function receivePackets() {
6632
- for (const packetData of incomingPackets) {
6633
- app.sim.emit.packet(packetData);
6634
- if (remotePeerId == null) {
6635
- return;
6636
- }
6637
- const peerState = unwrap(app.sim.net.peers[remotePeerId], `Remote peer state not found for peerId ${remotePeerId}`);
6638
- updatePeer(remoteStringPeerId, {
6639
- ack: peerState.ack,
6640
- seq: peerState.seq,
6641
- lastPacketTime: performance.now()
6642
- });
6643
- }
6644
- incomingPackets.length = 0;
6645
- }
6646
- function sendPacket() {
6647
- if (!udp || remotePeerId === null) {
6648
- console.warn("[netcode] Cannot send packet, udp or remotePeerId is null");
6649
- return;
6650
- }
6651
- if (udp.readyState !== "open") {
6652
- console.warn("[netcode] Data channel not open, cannot send packet. readyState=", udp.readyState);
6653
- return;
6654
- }
6655
- const packet = app.sim.getOutboundPacket(remotePeerId);
6656
- if (!packet) {
6657
- console.warn("[netcode] No packet to send");
6658
- return;
6659
- }
6660
- udp.send(packet);
6661
- }
6662
- logger.onLog = (log) => {
6663
- addLog(log);
6664
- };
6665
- app.joinRoom(roomId, {
6666
- onPeerIdAssign: (peerId) => {
6667
- localStringPeerId = peerId;
6668
- },
6669
- onBrokerMessage: (_message) => {},
6670
- onMessage(_peerId, data, _reliable) {
6671
- incomingPackets.push(new Uint8Array(data));
6672
- },
6673
- onDataChannelClose(peerId, reliable) {
6674
- if (!reliable && remotePeerId !== null) {
6675
- app.sim.emit.network("peer:leave", { peerId: remotePeerId });
6676
- sessionActive = false;
6677
- opts?.onSessionEnd?.();
6678
- }
6679
- },
6680
- onDataChannelOpen(peerId, reliable, channel) {
6681
- if (!reliable) {
6682
- udp = channel;
6683
- if (localStringPeerId === null) {
6684
- console.error("[netcode] Local peer ID not assigned yet!");
6685
- return;
6686
- }
6687
- const ids = assignPeerIds(localStringPeerId, peerId);
6688
- localPeerId = ids.local;
6689
- setLocalId(localPeerId);
6690
- remotePeerId = ids.remote;
6691
- remoteStringPeerId = peerId;
6692
- setRemoteId(remotePeerId);
6693
- app.sim.emit.network("peer:join", { peerId: localPeerId });
6694
- app.sim.emit.network("peer:join", { peerId: remotePeerId });
6695
- app.sim.emit.network("peer:assign_local_id", { peerId: localPeerId });
6696
- app.sim.emit.network("session:start", {});
6697
- sessionActive = true;
6698
- opts?.onSessionStart?.();
6699
- }
6700
- },
6701
- onPeerConnected(peerId) {
6702
- addPeer({
6703
- id: peerId,
6704
- nickname: peerId.substring(0, 6),
6705
- ack: -1,
6706
- seq: -1,
6707
- lastPacketTime: performance.now()
6708
- });
6709
- console.log(`[netcode] Peer connected: ${peerId}. Total peers: ${debugState.netStatus.value.peers.length}`);
6710
- },
6711
- onPeerDisconnected(peerId) {
6712
- removePeer(peerId);
6713
- if (remotePeerId !== null && peerId === remoteStringPeerId) {
6714
- app.sim.emit.network("peer:leave", { peerId: remotePeerId });
6715
- sessionActive = false;
6716
- opts?.onSessionEnd?.();
6717
- }
6718
- }
6719
- });
6720
- app.beforeFrame.subscribe((_frame) => {
6721
- if (!sessionActive || !udp || remotePeerId === null) {
6722
- return;
6723
- }
6724
- try {
6725
- receivePackets();
6726
- sendPacket();
6727
- } catch (e4) {
6728
- console.error("Error in beforeFrame:", e4);
6729
- }
6730
- });
6731
- }
6732
6805
  export {
6733
6806
  start,
6734
6807
  logger,
6735
- joinRollbackRoom,
6736
6808
  PacketType,
6737
6809
  exports_mod as Debug,
6738
6810
  App
6739
6811
  };
6740
6812
 
6741
- //# debugId=1A548992A4233E8464756E2164756E21
6813
+ //# debugId=164BA35FD7424E1864756E2164756E21
6742
6814
  //# sourceMappingURL=mod.js.map