@bloopjs/web 0.0.88 → 0.0.90
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.map +1 -1
- package/dist/mod.js +467 -251
- package/dist/mod.js.map +8 -8
- package/dist/netcode/mod.d.ts +0 -2
- package/dist/netcode/mod.d.ts.map +1 -1
- package/dist/netcode/reconcile.d.ts +3 -0
- package/dist/netcode/reconcile.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/App.ts +68 -0
- package/src/netcode/mod.ts +0 -2
- package/src/netcode/reconcile.ts +171 -0
- package/dist/netcode/scaffold.d.ts +0 -13
- package/dist/netcode/scaffold.d.ts.map +0 -1
- package/src/netcode/scaffold.ts +0 -168
package/dist/mod.js
CHANGED
|
@@ -77,21 +77,28 @@ __export2(exports_engine, {
|
|
|
77
77
|
inputSourceCodeToInputSource: () => inputSourceCodeToInputSource,
|
|
78
78
|
TimeContext: () => TimeContext,
|
|
79
79
|
TIME_CTX_OFFSET: () => TIME_CTX_OFFSET,
|
|
80
|
+
ScreenContext: () => ScreenContext,
|
|
80
81
|
SNAPSHOT_HEADER_USER_LEN_OFFSET: () => SNAPSHOT_HEADER_USER_LEN_OFFSET,
|
|
81
82
|
SNAPSHOT_HEADER_LEN: () => SNAPSHOT_HEADER_LEN,
|
|
82
83
|
SNAPSHOT_HEADER_ENGINE_LEN_OFFSET: () => SNAPSHOT_HEADER_ENGINE_LEN_OFFSET,
|
|
84
|
+
SCREEN_CTX_OFFSET: () => SCREEN_CTX_OFFSET,
|
|
83
85
|
PlayerInputContext: () => PlayerInputContext,
|
|
84
86
|
PLAYER_INPUTS_SIZE: () => PLAYER_INPUTS_SIZE,
|
|
87
|
+
PLAYER_INPUTS_MOUSE_CTX_OFFSET: () => PLAYER_INPUTS_MOUSE_CTX_OFFSET,
|
|
88
|
+
PLAYER_INPUTS_KEY_CTX_OFFSET: () => PLAYER_INPUTS_KEY_CTX_OFFSET,
|
|
85
89
|
NetContext: () => NetContext,
|
|
86
90
|
NET_CTX_OFFSET: () => NET_CTX_OFFSET,
|
|
87
91
|
MouseContext: () => MouseContext,
|
|
88
|
-
MOUSE_OFFSET: () =>
|
|
89
|
-
|
|
92
|
+
MOUSE_OFFSET: () => PLAYER_INPUTS_MOUSE_CTX_OFFSET,
|
|
93
|
+
MOUSE_CTX_SIZE: () => MOUSE_CTX_SIZE,
|
|
94
|
+
MOUSE_CTX_BUTTON_STATES_OFFSET: () => MOUSE_CTX_BUTTON_STATES_OFFSET,
|
|
95
|
+
MOUSE_BUTTONS_OFFSET: () => MOUSE_CTX_BUTTON_STATES_OFFSET,
|
|
90
96
|
MAX_ROLLBACK_FRAMES: () => MAX_ROLLBACK_FRAMES,
|
|
91
97
|
MAX_PLAYERS: () => MAX_PLAYERS,
|
|
92
98
|
KeyboardContext: () => KeyboardContext,
|
|
93
|
-
|
|
94
|
-
|
|
99
|
+
KEY_CTX_SIZE: () => KEY_CTX_SIZE,
|
|
100
|
+
KEYBOARD_SIZE: () => KEY_CTX_SIZE,
|
|
101
|
+
KEYBOARD_OFFSET: () => PLAYER_INPUTS_KEY_CTX_OFFSET,
|
|
95
102
|
InputContext: () => InputContext,
|
|
96
103
|
INPUT_CTX_SIZE: () => INPUT_CTX_SIZE,
|
|
97
104
|
INPUT_CTX_OFFSET: () => INPUT_CTX_OFFSET,
|
|
@@ -136,6 +143,7 @@ var EventType;
|
|
|
136
143
|
EventType2[EventType2["NetPeerAssignLocalId"] = 12] = "NetPeerAssignLocalId";
|
|
137
144
|
EventType2[EventType2["NetPacketReceived"] = 13] = "NetPacketReceived";
|
|
138
145
|
EventType2[EventType2["NetSessionInit"] = 14] = "NetSessionInit";
|
|
146
|
+
EventType2[EventType2["Resize"] = 15] = "Resize";
|
|
139
147
|
})(EventType ||= {});
|
|
140
148
|
var MouseButton;
|
|
141
149
|
((MouseButton2) => {
|
|
@@ -387,33 +395,43 @@ var NetJoinFailReason;
|
|
|
387
395
|
NetJoinFailReason2[NetJoinFailReason2["room_not_found"] = 3] = "room_not_found";
|
|
388
396
|
NetJoinFailReason2[NetJoinFailReason2["already_in_room"] = 4] = "already_in_room";
|
|
389
397
|
})(NetJoinFailReason ||= {});
|
|
390
|
-
var
|
|
391
|
-
var
|
|
392
|
-
var
|
|
393
|
-
var
|
|
394
|
-
var
|
|
398
|
+
var TIME_CTX_FRAME_OFFSET = 0;
|
|
399
|
+
var TIME_CTX_DT_MS_OFFSET = 4;
|
|
400
|
+
var TIME_CTX_TOTAL_MS_OFFSET = 8;
|
|
401
|
+
var PEER_CTX_CONNECTED_OFFSET = 0;
|
|
402
|
+
var PEER_CTX_SEQ_OFFSET = 2;
|
|
403
|
+
var PEER_CTX_ACK_OFFSET = 4;
|
|
404
|
+
var PEER_CTX_SIZE = 8;
|
|
405
|
+
var NET_CTX_PEER_COUNT_OFFSET = 0;
|
|
406
|
+
var NET_CTX_LOCAL_PEER_ID_OFFSET = 1;
|
|
407
|
+
var NET_CTX_IN_SESSION_OFFSET = 2;
|
|
408
|
+
var NET_CTX_STATUS_OFFSET = 3;
|
|
409
|
+
var NET_CTX_MATCH_FRAME_OFFSET = 4;
|
|
410
|
+
var NET_CTX_SESSION_START_FRAME_OFFSET = 8;
|
|
411
|
+
var NET_CTX_ROOM_CODE_OFFSET = 12;
|
|
412
|
+
var NET_CTX_WANTS_ROOM_CODE_OFFSET = 20;
|
|
413
|
+
var NET_CTX_WANTS_DISCONNECT_OFFSET = 28;
|
|
414
|
+
var NET_CTX_PEERS_OFFSET = 32;
|
|
415
|
+
var NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET = 128;
|
|
416
|
+
var NET_CTX_TOTAL_ROLLBACKS_OFFSET = 132;
|
|
417
|
+
var NET_CTX_FRAMES_RESIMULATED_OFFSET = 136;
|
|
418
|
+
var SCREEN_CTX_WIDTH_OFFSET = 0;
|
|
419
|
+
var SCREEN_CTX_HEIGHT_OFFSET = 4;
|
|
420
|
+
var SCREEN_CTX_PHYSICAL_WIDTH_OFFSET = 8;
|
|
421
|
+
var SCREEN_CTX_PHYSICAL_HEIGHT_OFFSET = 12;
|
|
422
|
+
var SCREEN_CTX_PIXEL_RATIO_OFFSET = 16;
|
|
423
|
+
var MOUSE_CTX_X_OFFSET = 0;
|
|
424
|
+
var MOUSE_CTX_Y_OFFSET = 4;
|
|
425
|
+
var MOUSE_CTX_WHEEL_X_OFFSET = 8;
|
|
426
|
+
var MOUSE_CTX_WHEEL_Y_OFFSET = 12;
|
|
427
|
+
var MOUSE_CTX_BUTTON_STATES_OFFSET = 16;
|
|
428
|
+
var MOUSE_CTX_SIZE = 24;
|
|
429
|
+
var KEY_CTX_SIZE = 256;
|
|
430
|
+
var PLAYER_INPUTS_KEY_CTX_OFFSET = 0;
|
|
431
|
+
var PLAYER_INPUTS_MOUSE_CTX_OFFSET = 256;
|
|
395
432
|
var PLAYER_INPUTS_SIZE = 280;
|
|
396
|
-
var INPUT_CTX_SIZE =
|
|
397
|
-
var
|
|
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
|
-
}
|
|
433
|
+
var INPUT_CTX_SIZE = 3360;
|
|
434
|
+
var MAX_PLAYERS = 12;
|
|
417
435
|
|
|
418
436
|
class PlayerInputContext {
|
|
419
437
|
#dataView;
|
|
@@ -424,13 +442,13 @@ class PlayerInputContext {
|
|
|
424
442
|
}
|
|
425
443
|
get keys() {
|
|
426
444
|
if (!this.#keys) {
|
|
427
|
-
this.#keys = new KeyboardContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset +
|
|
445
|
+
this.#keys = new KeyboardContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_KEY_CTX_OFFSET));
|
|
428
446
|
}
|
|
429
447
|
return this.#keys;
|
|
430
448
|
}
|
|
431
449
|
get mouse() {
|
|
432
450
|
if (!this.#mouse) {
|
|
433
|
-
this.#mouse = new MouseContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset +
|
|
451
|
+
this.#mouse = new MouseContext(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_MOUSE_CTX_OFFSET));
|
|
434
452
|
}
|
|
435
453
|
return this.#mouse;
|
|
436
454
|
}
|
|
@@ -455,6 +473,9 @@ class InputContext {
|
|
|
455
473
|
this.#dataView = dataView;
|
|
456
474
|
this.#buildPlayers();
|
|
457
475
|
}
|
|
476
|
+
hasDataView() {
|
|
477
|
+
return !!this.#dataView;
|
|
478
|
+
}
|
|
458
479
|
#buildPlayers() {
|
|
459
480
|
this.#players = [];
|
|
460
481
|
for (let i = 0;i < MAX_PLAYERS; i++) {
|
|
@@ -487,19 +508,19 @@ class MouseContext {
|
|
|
487
508
|
this.#dataView = dataView;
|
|
488
509
|
}
|
|
489
510
|
get x() {
|
|
490
|
-
return this.#dataView.getFloat32(
|
|
511
|
+
return this.#dataView.getFloat32(MOUSE_CTX_X_OFFSET, true);
|
|
491
512
|
}
|
|
492
513
|
get y() {
|
|
493
|
-
return this.#dataView.getFloat32(
|
|
514
|
+
return this.#dataView.getFloat32(MOUSE_CTX_Y_OFFSET, true);
|
|
494
515
|
}
|
|
495
516
|
get wheel() {
|
|
496
517
|
return { x: this.wheelX, y: this.wheelY };
|
|
497
518
|
}
|
|
498
519
|
get wheelX() {
|
|
499
|
-
return this.#dataView.getFloat32(
|
|
520
|
+
return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_X_OFFSET, true);
|
|
500
521
|
}
|
|
501
522
|
get wheelY() {
|
|
502
|
-
return this.#dataView.getFloat32(
|
|
523
|
+
return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_Y_OFFSET, true);
|
|
503
524
|
}
|
|
504
525
|
get left() {
|
|
505
526
|
return this.#buttonState(1);
|
|
@@ -518,7 +539,7 @@ class MouseContext {
|
|
|
518
539
|
state = { down: false, held: false, up: false };
|
|
519
540
|
this.#buttonStates.set(code, state);
|
|
520
541
|
}
|
|
521
|
-
const offset =
|
|
542
|
+
const offset = MOUSE_CTX_BUTTON_STATES_OFFSET;
|
|
522
543
|
state.held = !!(this.#dataView.getUint8(offset + code) & 1);
|
|
523
544
|
state.down = state.held && !(this.#dataView.getUint8(offset + code) & 2);
|
|
524
545
|
state.up = !state.held && !!(this.#dataView.getUint8(offset + code) & 2);
|
|
@@ -1140,15 +1161,6 @@ class KeyboardContext {
|
|
|
1140
1161
|
return state;
|
|
1141
1162
|
}
|
|
1142
1163
|
}
|
|
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
1164
|
var STATUS_MAP = {
|
|
1153
1165
|
0: "offline",
|
|
1154
1166
|
1: "local",
|
|
@@ -1159,7 +1171,7 @@ var STATUS_MAP = {
|
|
|
1159
1171
|
|
|
1160
1172
|
class NetContext {
|
|
1161
1173
|
dataView;
|
|
1162
|
-
#peers = Array.from({ length:
|
|
1174
|
+
#peers = Array.from({ length: MAX_PLAYERS }, () => ({
|
|
1163
1175
|
isLocal: false,
|
|
1164
1176
|
seq: -1,
|
|
1165
1177
|
ack: -1
|
|
@@ -1177,38 +1189,38 @@ class NetContext {
|
|
|
1177
1189
|
if (!this.#hasValidBuffer()) {
|
|
1178
1190
|
throw new Error("NetContext dataView is not valid");
|
|
1179
1191
|
}
|
|
1180
|
-
return this.dataView.getUint8(
|
|
1192
|
+
return this.dataView.getUint8(NET_CTX_PEER_COUNT_OFFSET);
|
|
1181
1193
|
}
|
|
1182
1194
|
get localPeerId() {
|
|
1183
1195
|
if (!this.#hasValidBuffer()) {
|
|
1184
1196
|
throw new Error("NetContext dataView is not valid");
|
|
1185
1197
|
}
|
|
1186
|
-
return this.dataView.getUint8(
|
|
1198
|
+
return this.dataView.getUint8(NET_CTX_LOCAL_PEER_ID_OFFSET);
|
|
1187
1199
|
}
|
|
1188
1200
|
get isInSession() {
|
|
1189
1201
|
if (!this.#hasValidBuffer()) {
|
|
1190
1202
|
throw new Error("NetContext dataView is not valid");
|
|
1191
1203
|
}
|
|
1192
|
-
return this.dataView.getUint8(
|
|
1204
|
+
return this.dataView.getUint8(NET_CTX_IN_SESSION_OFFSET) !== 0;
|
|
1193
1205
|
}
|
|
1194
1206
|
get status() {
|
|
1195
1207
|
if (!this.#hasValidBuffer()) {
|
|
1196
1208
|
throw new Error("NetContext dataView is not valid");
|
|
1197
1209
|
}
|
|
1198
|
-
const statusByte = this.dataView.getUint8(
|
|
1210
|
+
const statusByte = this.dataView.getUint8(NET_CTX_STATUS_OFFSET);
|
|
1199
1211
|
return STATUS_MAP[statusByte] ?? "local";
|
|
1200
1212
|
}
|
|
1201
1213
|
get matchFrame() {
|
|
1202
1214
|
if (!this.#hasValidBuffer()) {
|
|
1203
1215
|
throw new Error("NetContext dataView is not valid");
|
|
1204
1216
|
}
|
|
1205
|
-
return this.dataView.getUint32(
|
|
1217
|
+
return this.dataView.getUint32(NET_CTX_MATCH_FRAME_OFFSET, true);
|
|
1206
1218
|
}
|
|
1207
1219
|
get sessionStartFrame() {
|
|
1208
1220
|
if (!this.#hasValidBuffer()) {
|
|
1209
1221
|
throw new Error("NetContext dataView is not valid");
|
|
1210
1222
|
}
|
|
1211
|
-
return this.dataView.getUint32(
|
|
1223
|
+
return this.dataView.getUint32(NET_CTX_SESSION_START_FRAME_OFFSET, true);
|
|
1212
1224
|
}
|
|
1213
1225
|
get roomCode() {
|
|
1214
1226
|
if (!this.#hasValidBuffer()) {
|
|
@@ -1216,7 +1228,7 @@ class NetContext {
|
|
|
1216
1228
|
}
|
|
1217
1229
|
const bytes = [];
|
|
1218
1230
|
for (let i = 0;i < 8; i++) {
|
|
1219
|
-
const byte = this.dataView.getUint8(
|
|
1231
|
+
const byte = this.dataView.getUint8(NET_CTX_ROOM_CODE_OFFSET + i);
|
|
1220
1232
|
if (byte === 0)
|
|
1221
1233
|
break;
|
|
1222
1234
|
bytes.push(byte);
|
|
@@ -1229,7 +1241,7 @@ class NetContext {
|
|
|
1229
1241
|
}
|
|
1230
1242
|
const bytes = [];
|
|
1231
1243
|
for (let i = 0;i < 8; i++) {
|
|
1232
|
-
const byte = this.dataView.getUint8(
|
|
1244
|
+
const byte = this.dataView.getUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i);
|
|
1233
1245
|
if (byte === 0)
|
|
1234
1246
|
break;
|
|
1235
1247
|
bytes.push(byte);
|
|
@@ -1241,11 +1253,11 @@ class NetContext {
|
|
|
1241
1253
|
throw new Error("NetContext dataView is not valid");
|
|
1242
1254
|
}
|
|
1243
1255
|
for (let i = 0;i < 8; i++) {
|
|
1244
|
-
this.dataView.setUint8(
|
|
1256
|
+
this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i, 0);
|
|
1245
1257
|
}
|
|
1246
1258
|
if (code) {
|
|
1247
1259
|
for (let i = 0;i < Math.min(code.length, 7); i++) {
|
|
1248
|
-
this.dataView.setUint8(
|
|
1260
|
+
this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET + i, code.charCodeAt(i));
|
|
1249
1261
|
}
|
|
1250
1262
|
}
|
|
1251
1263
|
}
|
|
@@ -1253,13 +1265,13 @@ class NetContext {
|
|
|
1253
1265
|
if (!this.#hasValidBuffer()) {
|
|
1254
1266
|
return false;
|
|
1255
1267
|
}
|
|
1256
|
-
return this.dataView.getUint8(
|
|
1268
|
+
return this.dataView.getUint8(NET_CTX_WANTS_DISCONNECT_OFFSET) !== 0;
|
|
1257
1269
|
}
|
|
1258
1270
|
set wantsDisconnect(value) {
|
|
1259
1271
|
if (!this.#hasValidBuffer()) {
|
|
1260
1272
|
throw new Error("NetContext dataView is not valid");
|
|
1261
1273
|
}
|
|
1262
|
-
this.dataView.setUint8(
|
|
1274
|
+
this.dataView.setUint8(NET_CTX_WANTS_DISCONNECT_OFFSET, value ? 1 : 0);
|
|
1263
1275
|
}
|
|
1264
1276
|
get peers() {
|
|
1265
1277
|
if (!this.#hasValidBuffer()) {
|
|
@@ -1269,13 +1281,13 @@ class NetContext {
|
|
|
1269
1281
|
const localPeerId = this.localPeerId;
|
|
1270
1282
|
const matchFrame = this.matchFrame;
|
|
1271
1283
|
let minRemoteSeq = -1;
|
|
1272
|
-
for (let i = 0;i <
|
|
1284
|
+
for (let i = 0;i < MAX_PLAYERS; i++) {
|
|
1273
1285
|
if (i === localPeerId)
|
|
1274
1286
|
continue;
|
|
1275
|
-
const peerOffset =
|
|
1276
|
-
if (dv.getUint8(peerOffset +
|
|
1287
|
+
const peerOffset = NET_CTX_PEERS_OFFSET + i * PEER_CTX_SIZE;
|
|
1288
|
+
if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET) !== 1)
|
|
1277
1289
|
continue;
|
|
1278
|
-
const seq = dv.getInt16(peerOffset +
|
|
1290
|
+
const seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET, true);
|
|
1279
1291
|
if (seq < 0)
|
|
1280
1292
|
continue;
|
|
1281
1293
|
if (minRemoteSeq === -1 || seq < minRemoteSeq) {
|
|
@@ -1283,9 +1295,9 @@ class NetContext {
|
|
|
1283
1295
|
}
|
|
1284
1296
|
}
|
|
1285
1297
|
this.#peersResult.length = 0;
|
|
1286
|
-
for (let i = 0;i <
|
|
1287
|
-
const peerOffset =
|
|
1288
|
-
if (dv.getUint8(peerOffset +
|
|
1298
|
+
for (let i = 0;i < MAX_PLAYERS; i++) {
|
|
1299
|
+
const peerOffset = NET_CTX_PEERS_OFFSET + i * PEER_CTX_SIZE;
|
|
1300
|
+
if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET) !== 1)
|
|
1289
1301
|
continue;
|
|
1290
1302
|
const peer = this.#peers[i];
|
|
1291
1303
|
if (!peer) {
|
|
@@ -1297,8 +1309,8 @@ class NetContext {
|
|
|
1297
1309
|
peer.seq = matchFrame;
|
|
1298
1310
|
peer.ack = minRemoteSeq;
|
|
1299
1311
|
} else {
|
|
1300
|
-
peer.seq = dv.getInt16(peerOffset +
|
|
1301
|
-
peer.ack = dv.getInt16(peerOffset +
|
|
1312
|
+
peer.seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET, true);
|
|
1313
|
+
peer.ack = dv.getInt16(peerOffset + PEER_CTX_ACK_OFFSET, true);
|
|
1302
1314
|
}
|
|
1303
1315
|
this.#peersResult.push(peer);
|
|
1304
1316
|
}
|
|
@@ -1308,19 +1320,56 @@ class NetContext {
|
|
|
1308
1320
|
if (!this.#hasValidBuffer()) {
|
|
1309
1321
|
throw new Error("NetContext dataView is not valid");
|
|
1310
1322
|
}
|
|
1311
|
-
return this.dataView.getUint32(
|
|
1323
|
+
return this.dataView.getUint32(NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET, true);
|
|
1312
1324
|
}
|
|
1313
1325
|
get totalRollbacks() {
|
|
1314
1326
|
if (!this.#hasValidBuffer()) {
|
|
1315
1327
|
throw new Error("NetContext dataView is not valid");
|
|
1316
1328
|
}
|
|
1317
|
-
return this.dataView.getUint32(
|
|
1329
|
+
return this.dataView.getUint32(NET_CTX_TOTAL_ROLLBACKS_OFFSET, true);
|
|
1318
1330
|
}
|
|
1319
1331
|
get framesResimulated() {
|
|
1320
1332
|
if (!this.#hasValidBuffer()) {
|
|
1321
1333
|
throw new Error("NetContext dataView is not valid");
|
|
1322
1334
|
}
|
|
1323
|
-
return Number(this.dataView.getBigUint64(
|
|
1335
|
+
return Number(this.dataView.getBigUint64(NET_CTX_FRAMES_RESIMULATED_OFFSET, true));
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
class ScreenContext {
|
|
1340
|
+
dataView;
|
|
1341
|
+
constructor(dataView) {
|
|
1342
|
+
this.dataView = dataView;
|
|
1343
|
+
}
|
|
1344
|
+
get width() {
|
|
1345
|
+
if (!this.dataView) {
|
|
1346
|
+
throw new Error("ScreenContext DataView is not initialized");
|
|
1347
|
+
}
|
|
1348
|
+
return this.dataView.getUint32(SCREEN_CTX_WIDTH_OFFSET, true);
|
|
1349
|
+
}
|
|
1350
|
+
get height() {
|
|
1351
|
+
if (!this.dataView) {
|
|
1352
|
+
throw new Error("ScreenContext DataView is not initialized");
|
|
1353
|
+
}
|
|
1354
|
+
return this.dataView.getUint32(SCREEN_CTX_HEIGHT_OFFSET, true);
|
|
1355
|
+
}
|
|
1356
|
+
get physicalWidth() {
|
|
1357
|
+
if (!this.dataView) {
|
|
1358
|
+
throw new Error("ScreenContext DataView is not initialized");
|
|
1359
|
+
}
|
|
1360
|
+
return this.dataView.getUint32(SCREEN_CTX_PHYSICAL_WIDTH_OFFSET, true);
|
|
1361
|
+
}
|
|
1362
|
+
get physicalHeight() {
|
|
1363
|
+
if (!this.dataView) {
|
|
1364
|
+
throw new Error("ScreenContext DataView is not initialized");
|
|
1365
|
+
}
|
|
1366
|
+
return this.dataView.getUint32(SCREEN_CTX_PHYSICAL_HEIGHT_OFFSET, true);
|
|
1367
|
+
}
|
|
1368
|
+
get pixelRatio() {
|
|
1369
|
+
if (!this.dataView) {
|
|
1370
|
+
throw new Error("ScreenContext DataView is not initialized");
|
|
1371
|
+
}
|
|
1372
|
+
return this.dataView.getFloat32(SCREEN_CTX_PIXEL_RATIO_OFFSET, true);
|
|
1324
1373
|
}
|
|
1325
1374
|
}
|
|
1326
1375
|
|
|
@@ -1333,21 +1382,41 @@ class TimeContext {
|
|
|
1333
1382
|
if (!this.dataView) {
|
|
1334
1383
|
throw new Error("TimeContext DataView is not initialized");
|
|
1335
1384
|
}
|
|
1336
|
-
return this.dataView.getUint32(
|
|
1385
|
+
return this.dataView.getUint32(TIME_CTX_FRAME_OFFSET, true);
|
|
1337
1386
|
}
|
|
1338
1387
|
get dt() {
|
|
1339
1388
|
if (!this.dataView) {
|
|
1340
1389
|
throw new Error("TimeContext DataView is not initialized");
|
|
1341
1390
|
}
|
|
1342
|
-
return this.dataView.getUint32(
|
|
1391
|
+
return this.dataView.getUint32(TIME_CTX_DT_MS_OFFSET, true) / 1000;
|
|
1343
1392
|
}
|
|
1344
1393
|
get time() {
|
|
1345
1394
|
if (!this.dataView) {
|
|
1346
1395
|
throw new Error("TimeContext DataView is not initialized");
|
|
1347
1396
|
}
|
|
1348
|
-
return this.dataView.getUint32(
|
|
1397
|
+
return this.dataView.getUint32(TIME_CTX_TOTAL_MS_OFFSET, true) / 1000;
|
|
1349
1398
|
}
|
|
1350
1399
|
}
|
|
1400
|
+
var EVENT_PAYLOAD_SIZE = 8;
|
|
1401
|
+
var EVENT_PAYLOAD_ALIGN = 4;
|
|
1402
|
+
function keyToKeyCode(key) {
|
|
1403
|
+
return Key[key];
|
|
1404
|
+
}
|
|
1405
|
+
function keyCodeToKey(code) {
|
|
1406
|
+
return Key[code];
|
|
1407
|
+
}
|
|
1408
|
+
function mouseButtonToMouseButtonCode(button) {
|
|
1409
|
+
return MouseButton[button];
|
|
1410
|
+
}
|
|
1411
|
+
function mouseButtonCodeToMouseButton(code) {
|
|
1412
|
+
return MouseButton[code];
|
|
1413
|
+
}
|
|
1414
|
+
function inputSourceToInputSourceCode(source) {
|
|
1415
|
+
return InputSource[source];
|
|
1416
|
+
}
|
|
1417
|
+
function inputSourceCodeToInputSource(code) {
|
|
1418
|
+
return InputSource[code];
|
|
1419
|
+
}
|
|
1351
1420
|
var TAPE_MAGIC = 1413566533;
|
|
1352
1421
|
function readTapeHeader(tape) {
|
|
1353
1422
|
const view = new DataView(tape.buffer, tape.byteOffset, tape.byteLength);
|
|
@@ -1363,12 +1432,13 @@ function readTapeHeader(tape) {
|
|
|
1363
1432
|
eventCount: view.getUint16(14, true)
|
|
1364
1433
|
};
|
|
1365
1434
|
}
|
|
1366
|
-
var DEFAULT_WASM_URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.
|
|
1435
|
+
var DEFAULT_WASM_URL = new URL("https://unpkg.com/@bloopjs/engine@0.0.90/wasm/bloop.wasm");
|
|
1367
1436
|
var MAX_ROLLBACK_FRAMES = 500;
|
|
1368
1437
|
var TIME_CTX_OFFSET = 0;
|
|
1369
1438
|
var INPUT_CTX_OFFSET = TIME_CTX_OFFSET + 4;
|
|
1370
1439
|
var EVENTS_OFFSET = INPUT_CTX_OFFSET + 4;
|
|
1371
1440
|
var NET_CTX_OFFSET = EVENTS_OFFSET + 4;
|
|
1441
|
+
var SCREEN_CTX_OFFSET = NET_CTX_OFFSET + 4;
|
|
1372
1442
|
var SNAPSHOT_HEADER_LEN = 16;
|
|
1373
1443
|
var SNAPSHOT_HEADER_USER_LEN_OFFSET = 4;
|
|
1374
1444
|
var SNAPSHOT_HEADER_ENGINE_LEN_OFFSET = 8;
|
|
@@ -1390,6 +1460,7 @@ class Sim {
|
|
|
1390
1460
|
#serialize;
|
|
1391
1461
|
#isPaused = false;
|
|
1392
1462
|
#net;
|
|
1463
|
+
#screen;
|
|
1393
1464
|
onTapeFull;
|
|
1394
1465
|
constructor(wasm, memory, opts) {
|
|
1395
1466
|
this.wasm = wasm;
|
|
@@ -1398,6 +1469,7 @@ class Sim {
|
|
|
1398
1469
|
this.id = `${Math.floor(Math.random() * 1e6)}`;
|
|
1399
1470
|
this.#serialize = opts?.serialize;
|
|
1400
1471
|
this.#net = new NetContext;
|
|
1472
|
+
this.#screen = new ScreenContext;
|
|
1401
1473
|
}
|
|
1402
1474
|
step(ms) {
|
|
1403
1475
|
if (this.#isPaused) {
|
|
@@ -1510,6 +1582,12 @@ class Sim {
|
|
|
1510
1582
|
}
|
|
1511
1583
|
return this.#net;
|
|
1512
1584
|
}
|
|
1585
|
+
get screen() {
|
|
1586
|
+
if (!this.#screen.dataView || this.#screen.dataView.buffer !== this.#memory.buffer) {
|
|
1587
|
+
this.#screen.dataView = new DataView(this.#memory.buffer, this.wasm.get_screen_ctx());
|
|
1588
|
+
}
|
|
1589
|
+
return this.#screen;
|
|
1590
|
+
}
|
|
1513
1591
|
get buffer() {
|
|
1514
1592
|
return this.#memory.buffer;
|
|
1515
1593
|
}
|
|
@@ -1541,6 +1619,9 @@ class Sim {
|
|
|
1541
1619
|
mousewheel: (x, y, peerId = 0) => {
|
|
1542
1620
|
this.wasm.emit_mousewheel(x, y, peerId);
|
|
1543
1621
|
},
|
|
1622
|
+
resize: (width, height, physicalWidth, physicalHeight, pixelRatio) => {
|
|
1623
|
+
this.wasm.emit_resize(width, height, physicalWidth, physicalHeight, pixelRatio);
|
|
1624
|
+
},
|
|
1544
1625
|
network: (type, data) => {
|
|
1545
1626
|
switch (type) {
|
|
1546
1627
|
case "join:ok": {
|
|
@@ -1628,6 +1709,7 @@ async function mount(mountable, options) {
|
|
|
1628
1709
|
memory,
|
|
1629
1710
|
__systems: function(system_handle, ptr) {
|
|
1630
1711
|
mountable.hooks.setBuffer(memory.buffer);
|
|
1712
|
+
mountable.hooks.setContext(ptr);
|
|
1631
1713
|
mountable.hooks.systemsCallback(system_handle, ptr);
|
|
1632
1714
|
},
|
|
1633
1715
|
__before_frame: function(ptr, frame) {
|
|
@@ -1705,6 +1787,39 @@ function calculateTapeConfig(tape) {
|
|
|
1705
1787
|
return { maxEvents, maxPacketBytes };
|
|
1706
1788
|
}
|
|
1707
1789
|
|
|
1790
|
+
class Players {
|
|
1791
|
+
#inputs;
|
|
1792
|
+
#net;
|
|
1793
|
+
constructor(inputs, net) {
|
|
1794
|
+
this.#inputs = inputs;
|
|
1795
|
+
this.#net = net;
|
|
1796
|
+
}
|
|
1797
|
+
get(index) {
|
|
1798
|
+
const players = this.#inputs.players;
|
|
1799
|
+
if (index < 0 || index >= players.length) {
|
|
1800
|
+
throw new RangeError(`Player index ${index} out of bounds (0-${players.length - 1})`);
|
|
1801
|
+
}
|
|
1802
|
+
return players[index];
|
|
1803
|
+
}
|
|
1804
|
+
get count() {
|
|
1805
|
+
return this.#net.isInSession ? this.#net.peerCount : 1;
|
|
1806
|
+
}
|
|
1807
|
+
*[Symbol.iterator]() {
|
|
1808
|
+
const players = this.#inputs.players;
|
|
1809
|
+
const count = this.count;
|
|
1810
|
+
for (let i = 0;i < count; i++) {
|
|
1811
|
+
yield players[i];
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
*entries() {
|
|
1815
|
+
const players = this.#inputs.players;
|
|
1816
|
+
const count = this.count;
|
|
1817
|
+
for (let i = 0;i < count; i++) {
|
|
1818
|
+
yield [i, players[i]];
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1708
1823
|
class Bloop {
|
|
1709
1824
|
#systems = [];
|
|
1710
1825
|
#context;
|
|
@@ -1717,15 +1832,16 @@ class Bloop {
|
|
|
1717
1832
|
throw new Error("Bloop constructor is private. Use Bloop.create() to create a new game instance.");
|
|
1718
1833
|
}
|
|
1719
1834
|
const inputs = new InputContext;
|
|
1835
|
+
const net = new NetContext;
|
|
1836
|
+
const screen = new ScreenContext;
|
|
1720
1837
|
this.#context = {
|
|
1721
1838
|
bag: opts.bag ?? {},
|
|
1722
1839
|
time: new TimeContext,
|
|
1723
1840
|
inputs,
|
|
1724
|
-
|
|
1725
|
-
return inputs.players;
|
|
1726
|
-
},
|
|
1841
|
+
players: new Players(inputs, net),
|
|
1727
1842
|
rawPointer: -1,
|
|
1728
|
-
net
|
|
1843
|
+
net,
|
|
1844
|
+
screen
|
|
1729
1845
|
};
|
|
1730
1846
|
}
|
|
1731
1847
|
get bag() {
|
|
@@ -1773,10 +1889,20 @@ class Bloop {
|
|
|
1773
1889
|
const timeCtxPtr = dv.getUint32(TIME_CTX_OFFSET, true);
|
|
1774
1890
|
const inputCtxPtr = dv.getUint32(INPUT_CTX_OFFSET, true);
|
|
1775
1891
|
const netCtxPtr = dv.getUint32(NET_CTX_OFFSET, true);
|
|
1892
|
+
const screenCtxPtr = dv.getUint32(SCREEN_CTX_OFFSET, true);
|
|
1776
1893
|
this.#context.rawPointer = ptr;
|
|
1777
|
-
this.#context.inputs.dataView
|
|
1778
|
-
|
|
1779
|
-
|
|
1894
|
+
if (!this.#context.inputs.hasDataView() || this.#context.inputs.dataView.buffer !== this.#engineBuffer || this.#context.inputs.dataView.byteOffset !== inputCtxPtr) {
|
|
1895
|
+
this.#context.inputs.dataView = new DataView(this.#engineBuffer, inputCtxPtr);
|
|
1896
|
+
}
|
|
1897
|
+
if (this.#context.time.dataView?.buffer !== this.#engineBuffer || this.#context.time.dataView?.byteOffset !== timeCtxPtr) {
|
|
1898
|
+
this.#context.time.dataView = new DataView(this.#engineBuffer, timeCtxPtr);
|
|
1899
|
+
}
|
|
1900
|
+
if (this.#context.net.dataView?.buffer !== this.#engineBuffer || this.#context.net.dataView?.byteOffset !== netCtxPtr) {
|
|
1901
|
+
this.#context.net.dataView = new DataView(this.#engineBuffer, netCtxPtr);
|
|
1902
|
+
}
|
|
1903
|
+
if (this.#context.screen.dataView?.buffer !== this.#engineBuffer || this.#context.screen.dataView?.byteOffset !== screenCtxPtr) {
|
|
1904
|
+
this.#context.screen.dataView = new DataView(this.#engineBuffer, screenCtxPtr);
|
|
1905
|
+
}
|
|
1780
1906
|
},
|
|
1781
1907
|
systemsCallback: (system_handle, ptr) => {
|
|
1782
1908
|
this.hooks.setContext(ptr);
|
|
@@ -1881,6 +2007,22 @@ class Bloop {
|
|
|
1881
2007
|
system.netcode?.(this.#context);
|
|
1882
2008
|
break;
|
|
1883
2009
|
}
|
|
2010
|
+
case exports_enums.EventType.NetSessionInit:
|
|
2011
|
+
console.log("[bloop] NetSessionInit event received");
|
|
2012
|
+
this.#context.event = {
|
|
2013
|
+
type: "session:start"
|
|
2014
|
+
};
|
|
2015
|
+
system.netcode?.(this.#context);
|
|
2016
|
+
break;
|
|
2017
|
+
case exports_enums.EventType.Resize:
|
|
2018
|
+
system.resize?.(attachEvent(this.#context, {
|
|
2019
|
+
width: this.#context.screen.width,
|
|
2020
|
+
height: this.#context.screen.height,
|
|
2021
|
+
physicalWidth: this.#context.screen.physicalWidth,
|
|
2022
|
+
physicalHeight: this.#context.screen.physicalHeight,
|
|
2023
|
+
pixelRatio: this.#context.screen.pixelRatio
|
|
2024
|
+
}));
|
|
2025
|
+
break;
|
|
1884
2026
|
default:
|
|
1885
2027
|
break;
|
|
1886
2028
|
}
|
|
@@ -1932,6 +2074,7 @@ var EventType2;
|
|
|
1932
2074
|
EventType22[EventType22["NetPeerAssignLocalId"] = 12] = "NetPeerAssignLocalId";
|
|
1933
2075
|
EventType22[EventType22["NetPacketReceived"] = 13] = "NetPacketReceived";
|
|
1934
2076
|
EventType22[EventType22["NetSessionInit"] = 14] = "NetSessionInit";
|
|
2077
|
+
EventType22[EventType22["Resize"] = 15] = "Resize";
|
|
1935
2078
|
})(EventType2 ||= {});
|
|
1936
2079
|
var MouseButton2;
|
|
1937
2080
|
((MouseButton22) => {
|
|
@@ -2183,15 +2326,33 @@ var NetJoinFailReason2;
|
|
|
2183
2326
|
NetJoinFailReason22[NetJoinFailReason22["room_not_found"] = 3] = "room_not_found";
|
|
2184
2327
|
NetJoinFailReason22[NetJoinFailReason22["already_in_room"] = 4] = "already_in_room";
|
|
2185
2328
|
})(NetJoinFailReason2 ||= {});
|
|
2186
|
-
var
|
|
2187
|
-
var
|
|
2188
|
-
var
|
|
2189
|
-
var
|
|
2329
|
+
var PEER_CTX_CONNECTED_OFFSET2 = 0;
|
|
2330
|
+
var PEER_CTX_SEQ_OFFSET2 = 2;
|
|
2331
|
+
var PEER_CTX_ACK_OFFSET2 = 4;
|
|
2332
|
+
var PEER_CTX_SIZE2 = 8;
|
|
2333
|
+
var NET_CTX_PEER_COUNT_OFFSET2 = 0;
|
|
2334
|
+
var NET_CTX_LOCAL_PEER_ID_OFFSET2 = 1;
|
|
2335
|
+
var NET_CTX_IN_SESSION_OFFSET2 = 2;
|
|
2336
|
+
var NET_CTX_STATUS_OFFSET2 = 3;
|
|
2337
|
+
var NET_CTX_MATCH_FRAME_OFFSET2 = 4;
|
|
2338
|
+
var NET_CTX_SESSION_START_FRAME_OFFSET2 = 8;
|
|
2339
|
+
var NET_CTX_ROOM_CODE_OFFSET2 = 12;
|
|
2340
|
+
var NET_CTX_WANTS_ROOM_CODE_OFFSET2 = 20;
|
|
2341
|
+
var NET_CTX_WANTS_DISCONNECT_OFFSET2 = 28;
|
|
2342
|
+
var NET_CTX_PEERS_OFFSET2 = 32;
|
|
2343
|
+
var NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET2 = 128;
|
|
2344
|
+
var NET_CTX_TOTAL_ROLLBACKS_OFFSET2 = 132;
|
|
2345
|
+
var NET_CTX_FRAMES_RESIMULATED_OFFSET2 = 136;
|
|
2346
|
+
var MOUSE_CTX_X_OFFSET2 = 0;
|
|
2347
|
+
var MOUSE_CTX_Y_OFFSET2 = 4;
|
|
2348
|
+
var MOUSE_CTX_WHEEL_X_OFFSET2 = 8;
|
|
2349
|
+
var MOUSE_CTX_WHEEL_Y_OFFSET2 = 12;
|
|
2350
|
+
var MOUSE_CTX_BUTTON_STATES_OFFSET2 = 16;
|
|
2351
|
+
var PLAYER_INPUTS_KEY_CTX_OFFSET2 = 0;
|
|
2352
|
+
var PLAYER_INPUTS_MOUSE_CTX_OFFSET2 = 256;
|
|
2190
2353
|
var PLAYER_INPUTS_SIZE2 = 280;
|
|
2191
|
-
var
|
|
2192
|
-
|
|
2193
|
-
return MouseButton2[code];
|
|
2194
|
-
}
|
|
2354
|
+
var MAX_PLAYERS2 = 12;
|
|
2355
|
+
|
|
2195
2356
|
class PlayerInputContext2 {
|
|
2196
2357
|
#dataView;
|
|
2197
2358
|
#keys;
|
|
@@ -2201,13 +2362,13 @@ class PlayerInputContext2 {
|
|
|
2201
2362
|
}
|
|
2202
2363
|
get keys() {
|
|
2203
2364
|
if (!this.#keys) {
|
|
2204
|
-
this.#keys = new KeyboardContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset +
|
|
2365
|
+
this.#keys = new KeyboardContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_KEY_CTX_OFFSET2));
|
|
2205
2366
|
}
|
|
2206
2367
|
return this.#keys;
|
|
2207
2368
|
}
|
|
2208
2369
|
get mouse() {
|
|
2209
2370
|
if (!this.#mouse) {
|
|
2210
|
-
this.#mouse = new MouseContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset +
|
|
2371
|
+
this.#mouse = new MouseContext2(new DataView(this.#dataView.buffer, this.#dataView.byteOffset + PLAYER_INPUTS_MOUSE_CTX_OFFSET2));
|
|
2211
2372
|
}
|
|
2212
2373
|
return this.#mouse;
|
|
2213
2374
|
}
|
|
@@ -2232,6 +2393,9 @@ class InputContext2 {
|
|
|
2232
2393
|
this.#dataView = dataView;
|
|
2233
2394
|
this.#buildPlayers();
|
|
2234
2395
|
}
|
|
2396
|
+
hasDataView() {
|
|
2397
|
+
return !!this.#dataView;
|
|
2398
|
+
}
|
|
2235
2399
|
#buildPlayers() {
|
|
2236
2400
|
this.#players = [];
|
|
2237
2401
|
for (let i = 0;i < MAX_PLAYERS2; i++) {
|
|
@@ -2264,19 +2428,19 @@ class MouseContext2 {
|
|
|
2264
2428
|
this.#dataView = dataView;
|
|
2265
2429
|
}
|
|
2266
2430
|
get x() {
|
|
2267
|
-
return this.#dataView.getFloat32(
|
|
2431
|
+
return this.#dataView.getFloat32(MOUSE_CTX_X_OFFSET2, true);
|
|
2268
2432
|
}
|
|
2269
2433
|
get y() {
|
|
2270
|
-
return this.#dataView.getFloat32(
|
|
2434
|
+
return this.#dataView.getFloat32(MOUSE_CTX_Y_OFFSET2, true);
|
|
2271
2435
|
}
|
|
2272
2436
|
get wheel() {
|
|
2273
2437
|
return { x: this.wheelX, y: this.wheelY };
|
|
2274
2438
|
}
|
|
2275
2439
|
get wheelX() {
|
|
2276
|
-
return this.#dataView.getFloat32(
|
|
2440
|
+
return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_X_OFFSET2, true);
|
|
2277
2441
|
}
|
|
2278
2442
|
get wheelY() {
|
|
2279
|
-
return this.#dataView.getFloat32(
|
|
2443
|
+
return this.#dataView.getFloat32(MOUSE_CTX_WHEEL_Y_OFFSET2, true);
|
|
2280
2444
|
}
|
|
2281
2445
|
get left() {
|
|
2282
2446
|
return this.#buttonState(1);
|
|
@@ -2295,7 +2459,7 @@ class MouseContext2 {
|
|
|
2295
2459
|
state = { down: false, held: false, up: false };
|
|
2296
2460
|
this.#buttonStates.set(code, state);
|
|
2297
2461
|
}
|
|
2298
|
-
const offset =
|
|
2462
|
+
const offset = MOUSE_CTX_BUTTON_STATES_OFFSET2;
|
|
2299
2463
|
state.held = !!(this.#dataView.getUint8(offset + code) & 1);
|
|
2300
2464
|
state.down = state.held && !(this.#dataView.getUint8(offset + code) & 2);
|
|
2301
2465
|
state.up = !state.held && !!(this.#dataView.getUint8(offset + code) & 2);
|
|
@@ -2917,15 +3081,6 @@ class KeyboardContext2 {
|
|
|
2917
3081
|
return state;
|
|
2918
3082
|
}
|
|
2919
3083
|
}
|
|
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
3084
|
var STATUS_MAP2 = {
|
|
2930
3085
|
0: "offline",
|
|
2931
3086
|
1: "local",
|
|
@@ -2936,7 +3091,7 @@ var STATUS_MAP2 = {
|
|
|
2936
3091
|
|
|
2937
3092
|
class NetContext2 {
|
|
2938
3093
|
dataView;
|
|
2939
|
-
#peers = Array.from({ length:
|
|
3094
|
+
#peers = Array.from({ length: MAX_PLAYERS2 }, () => ({
|
|
2940
3095
|
isLocal: false,
|
|
2941
3096
|
seq: -1,
|
|
2942
3097
|
ack: -1
|
|
@@ -2954,38 +3109,38 @@ class NetContext2 {
|
|
|
2954
3109
|
if (!this.#hasValidBuffer()) {
|
|
2955
3110
|
throw new Error("NetContext dataView is not valid");
|
|
2956
3111
|
}
|
|
2957
|
-
return this.dataView.getUint8(
|
|
3112
|
+
return this.dataView.getUint8(NET_CTX_PEER_COUNT_OFFSET2);
|
|
2958
3113
|
}
|
|
2959
3114
|
get localPeerId() {
|
|
2960
3115
|
if (!this.#hasValidBuffer()) {
|
|
2961
3116
|
throw new Error("NetContext dataView is not valid");
|
|
2962
3117
|
}
|
|
2963
|
-
return this.dataView.getUint8(
|
|
3118
|
+
return this.dataView.getUint8(NET_CTX_LOCAL_PEER_ID_OFFSET2);
|
|
2964
3119
|
}
|
|
2965
3120
|
get isInSession() {
|
|
2966
3121
|
if (!this.#hasValidBuffer()) {
|
|
2967
3122
|
throw new Error("NetContext dataView is not valid");
|
|
2968
3123
|
}
|
|
2969
|
-
return this.dataView.getUint8(
|
|
3124
|
+
return this.dataView.getUint8(NET_CTX_IN_SESSION_OFFSET2) !== 0;
|
|
2970
3125
|
}
|
|
2971
3126
|
get status() {
|
|
2972
3127
|
if (!this.#hasValidBuffer()) {
|
|
2973
3128
|
throw new Error("NetContext dataView is not valid");
|
|
2974
3129
|
}
|
|
2975
|
-
const statusByte = this.dataView.getUint8(
|
|
3130
|
+
const statusByte = this.dataView.getUint8(NET_CTX_STATUS_OFFSET2);
|
|
2976
3131
|
return STATUS_MAP2[statusByte] ?? "local";
|
|
2977
3132
|
}
|
|
2978
3133
|
get matchFrame() {
|
|
2979
3134
|
if (!this.#hasValidBuffer()) {
|
|
2980
3135
|
throw new Error("NetContext dataView is not valid");
|
|
2981
3136
|
}
|
|
2982
|
-
return this.dataView.getUint32(
|
|
3137
|
+
return this.dataView.getUint32(NET_CTX_MATCH_FRAME_OFFSET2, true);
|
|
2983
3138
|
}
|
|
2984
3139
|
get sessionStartFrame() {
|
|
2985
3140
|
if (!this.#hasValidBuffer()) {
|
|
2986
3141
|
throw new Error("NetContext dataView is not valid");
|
|
2987
3142
|
}
|
|
2988
|
-
return this.dataView.getUint32(
|
|
3143
|
+
return this.dataView.getUint32(NET_CTX_SESSION_START_FRAME_OFFSET2, true);
|
|
2989
3144
|
}
|
|
2990
3145
|
get roomCode() {
|
|
2991
3146
|
if (!this.#hasValidBuffer()) {
|
|
@@ -2993,7 +3148,7 @@ class NetContext2 {
|
|
|
2993
3148
|
}
|
|
2994
3149
|
const bytes = [];
|
|
2995
3150
|
for (let i = 0;i < 8; i++) {
|
|
2996
|
-
const byte = this.dataView.getUint8(
|
|
3151
|
+
const byte = this.dataView.getUint8(NET_CTX_ROOM_CODE_OFFSET2 + i);
|
|
2997
3152
|
if (byte === 0)
|
|
2998
3153
|
break;
|
|
2999
3154
|
bytes.push(byte);
|
|
@@ -3006,7 +3161,7 @@ class NetContext2 {
|
|
|
3006
3161
|
}
|
|
3007
3162
|
const bytes = [];
|
|
3008
3163
|
for (let i = 0;i < 8; i++) {
|
|
3009
|
-
const byte = this.dataView.getUint8(
|
|
3164
|
+
const byte = this.dataView.getUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET2 + i);
|
|
3010
3165
|
if (byte === 0)
|
|
3011
3166
|
break;
|
|
3012
3167
|
bytes.push(byte);
|
|
@@ -3018,11 +3173,11 @@ class NetContext2 {
|
|
|
3018
3173
|
throw new Error("NetContext dataView is not valid");
|
|
3019
3174
|
}
|
|
3020
3175
|
for (let i = 0;i < 8; i++) {
|
|
3021
|
-
this.dataView.setUint8(
|
|
3176
|
+
this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET2 + i, 0);
|
|
3022
3177
|
}
|
|
3023
3178
|
if (code) {
|
|
3024
3179
|
for (let i = 0;i < Math.min(code.length, 7); i++) {
|
|
3025
|
-
this.dataView.setUint8(
|
|
3180
|
+
this.dataView.setUint8(NET_CTX_WANTS_ROOM_CODE_OFFSET2 + i, code.charCodeAt(i));
|
|
3026
3181
|
}
|
|
3027
3182
|
}
|
|
3028
3183
|
}
|
|
@@ -3030,13 +3185,13 @@ class NetContext2 {
|
|
|
3030
3185
|
if (!this.#hasValidBuffer()) {
|
|
3031
3186
|
return false;
|
|
3032
3187
|
}
|
|
3033
|
-
return this.dataView.getUint8(
|
|
3188
|
+
return this.dataView.getUint8(NET_CTX_WANTS_DISCONNECT_OFFSET2) !== 0;
|
|
3034
3189
|
}
|
|
3035
3190
|
set wantsDisconnect(value) {
|
|
3036
3191
|
if (!this.#hasValidBuffer()) {
|
|
3037
3192
|
throw new Error("NetContext dataView is not valid");
|
|
3038
3193
|
}
|
|
3039
|
-
this.dataView.setUint8(
|
|
3194
|
+
this.dataView.setUint8(NET_CTX_WANTS_DISCONNECT_OFFSET2, value ? 1 : 0);
|
|
3040
3195
|
}
|
|
3041
3196
|
get peers() {
|
|
3042
3197
|
if (!this.#hasValidBuffer()) {
|
|
@@ -3046,13 +3201,13 @@ class NetContext2 {
|
|
|
3046
3201
|
const localPeerId = this.localPeerId;
|
|
3047
3202
|
const matchFrame = this.matchFrame;
|
|
3048
3203
|
let minRemoteSeq = -1;
|
|
3049
|
-
for (let i = 0;i <
|
|
3204
|
+
for (let i = 0;i < MAX_PLAYERS2; i++) {
|
|
3050
3205
|
if (i === localPeerId)
|
|
3051
3206
|
continue;
|
|
3052
|
-
const peerOffset =
|
|
3053
|
-
if (dv.getUint8(peerOffset +
|
|
3207
|
+
const peerOffset = NET_CTX_PEERS_OFFSET2 + i * PEER_CTX_SIZE2;
|
|
3208
|
+
if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET2) !== 1)
|
|
3054
3209
|
continue;
|
|
3055
|
-
const seq = dv.getInt16(peerOffset +
|
|
3210
|
+
const seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET2, true);
|
|
3056
3211
|
if (seq < 0)
|
|
3057
3212
|
continue;
|
|
3058
3213
|
if (minRemoteSeq === -1 || seq < minRemoteSeq) {
|
|
@@ -3060,9 +3215,9 @@ class NetContext2 {
|
|
|
3060
3215
|
}
|
|
3061
3216
|
}
|
|
3062
3217
|
this.#peersResult.length = 0;
|
|
3063
|
-
for (let i = 0;i <
|
|
3064
|
-
const peerOffset =
|
|
3065
|
-
if (dv.getUint8(peerOffset +
|
|
3218
|
+
for (let i = 0;i < MAX_PLAYERS2; i++) {
|
|
3219
|
+
const peerOffset = NET_CTX_PEERS_OFFSET2 + i * PEER_CTX_SIZE2;
|
|
3220
|
+
if (dv.getUint8(peerOffset + PEER_CTX_CONNECTED_OFFSET2) !== 1)
|
|
3066
3221
|
continue;
|
|
3067
3222
|
const peer = this.#peers[i];
|
|
3068
3223
|
if (!peer) {
|
|
@@ -3074,8 +3229,8 @@ class NetContext2 {
|
|
|
3074
3229
|
peer.seq = matchFrame;
|
|
3075
3230
|
peer.ack = minRemoteSeq;
|
|
3076
3231
|
} else {
|
|
3077
|
-
peer.seq = dv.getInt16(peerOffset +
|
|
3078
|
-
peer.ack = dv.getInt16(peerOffset +
|
|
3232
|
+
peer.seq = dv.getInt16(peerOffset + PEER_CTX_SEQ_OFFSET2, true);
|
|
3233
|
+
peer.ack = dv.getInt16(peerOffset + PEER_CTX_ACK_OFFSET2, true);
|
|
3079
3234
|
}
|
|
3080
3235
|
this.#peersResult.push(peer);
|
|
3081
3236
|
}
|
|
@@ -3085,21 +3240,24 @@ class NetContext2 {
|
|
|
3085
3240
|
if (!this.#hasValidBuffer()) {
|
|
3086
3241
|
throw new Error("NetContext dataView is not valid");
|
|
3087
3242
|
}
|
|
3088
|
-
return this.dataView.getUint32(
|
|
3243
|
+
return this.dataView.getUint32(NET_CTX_LAST_ROLLBACK_DEPTH_OFFSET2, true);
|
|
3089
3244
|
}
|
|
3090
3245
|
get totalRollbacks() {
|
|
3091
3246
|
if (!this.#hasValidBuffer()) {
|
|
3092
3247
|
throw new Error("NetContext dataView is not valid");
|
|
3093
3248
|
}
|
|
3094
|
-
return this.dataView.getUint32(
|
|
3249
|
+
return this.dataView.getUint32(NET_CTX_TOTAL_ROLLBACKS_OFFSET2, true);
|
|
3095
3250
|
}
|
|
3096
3251
|
get framesResimulated() {
|
|
3097
3252
|
if (!this.#hasValidBuffer()) {
|
|
3098
3253
|
throw new Error("NetContext dataView is not valid");
|
|
3099
3254
|
}
|
|
3100
|
-
return Number(this.dataView.getBigUint64(
|
|
3255
|
+
return Number(this.dataView.getBigUint64(NET_CTX_FRAMES_RESIMULATED_OFFSET2, true));
|
|
3101
3256
|
}
|
|
3102
3257
|
}
|
|
3258
|
+
function mouseButtonCodeToMouseButton2(code) {
|
|
3259
|
+
return MouseButton2[code];
|
|
3260
|
+
}
|
|
3103
3261
|
var TAPE_MAGIC2 = 1413566533;
|
|
3104
3262
|
function readTapeHeader2(tape) {
|
|
3105
3263
|
const view = new DataView(tape.buffer, tape.byteOffset, tape.byteLength);
|
|
@@ -3115,11 +3273,12 @@ function readTapeHeader2(tape) {
|
|
|
3115
3273
|
eventCount: view.getUint16(14, true)
|
|
3116
3274
|
};
|
|
3117
3275
|
}
|
|
3118
|
-
var DEFAULT_WASM_URL2 = new URL("https://unpkg.com/@bloopjs/engine@0.0.
|
|
3276
|
+
var DEFAULT_WASM_URL2 = new URL("https://unpkg.com/@bloopjs/engine@0.0.90/wasm/bloop.wasm");
|
|
3119
3277
|
var TIME_CTX_OFFSET2 = 0;
|
|
3120
3278
|
var INPUT_CTX_OFFSET2 = TIME_CTX_OFFSET2 + 4;
|
|
3121
3279
|
var EVENTS_OFFSET2 = INPUT_CTX_OFFSET2 + 4;
|
|
3122
3280
|
var NET_CTX_OFFSET2 = EVENTS_OFFSET2 + 4;
|
|
3281
|
+
var SCREEN_CTX_OFFSET2 = NET_CTX_OFFSET2 + 4;
|
|
3123
3282
|
|
|
3124
3283
|
// src/debugui/mod.ts
|
|
3125
3284
|
var exports_mod = {};
|
|
@@ -6327,6 +6486,138 @@ function joinRoom(brokerUrl, _roomId, cbs) {
|
|
|
6327
6486
|
}
|
|
6328
6487
|
}
|
|
6329
6488
|
|
|
6489
|
+
// src/netcode/reconcile.ts
|
|
6490
|
+
var udp = null;
|
|
6491
|
+
var localPeerId = null;
|
|
6492
|
+
var remotePeerId = null;
|
|
6493
|
+
var localStringPeerId = null;
|
|
6494
|
+
var remoteStringPeerId = null;
|
|
6495
|
+
var incomingPackets = [];
|
|
6496
|
+
var actual = {
|
|
6497
|
+
roomCode: ""
|
|
6498
|
+
};
|
|
6499
|
+
async function reconcile(app, signal) {
|
|
6500
|
+
app.beforeFrame.subscribe((_frame) => {
|
|
6501
|
+
if (!app.game.context.net.isInSession) {
|
|
6502
|
+
return;
|
|
6503
|
+
}
|
|
6504
|
+
try {
|
|
6505
|
+
receivePackets(app);
|
|
6506
|
+
sendPacket(app);
|
|
6507
|
+
} catch (e4) {
|
|
6508
|
+
console.error("Error in beforeFrame:", e4);
|
|
6509
|
+
}
|
|
6510
|
+
});
|
|
6511
|
+
logger.onLog = (log) => {
|
|
6512
|
+
addLog(log);
|
|
6513
|
+
};
|
|
6514
|
+
while (!signal.aborted) {
|
|
6515
|
+
const { net } = app.game.context;
|
|
6516
|
+
if (net.wantsRoomCode && actual.roomCode !== net.wantsRoomCode) {
|
|
6517
|
+
console.log("[netcode] wants a room code", {
|
|
6518
|
+
actual: actual.roomCode,
|
|
6519
|
+
wants: net.wantsRoomCode
|
|
6520
|
+
});
|
|
6521
|
+
actual.roomCode = net.wantsRoomCode;
|
|
6522
|
+
joinRollbackRoom(net.wantsRoomCode, app);
|
|
6523
|
+
}
|
|
6524
|
+
await sleep(150);
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
async function sleep(ms) {
|
|
6528
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
6529
|
+
}
|
|
6530
|
+
function joinRollbackRoom(roomId, app) {
|
|
6531
|
+
app.joinRoom(roomId, {
|
|
6532
|
+
onPeerIdAssign: (peerId) => {
|
|
6533
|
+
localStringPeerId = peerId;
|
|
6534
|
+
},
|
|
6535
|
+
onBrokerMessage: (_message) => {},
|
|
6536
|
+
onMessage(_peerId, data, _reliable) {
|
|
6537
|
+
incomingPackets.push(new Uint8Array(data));
|
|
6538
|
+
},
|
|
6539
|
+
onDataChannelClose(_peerId, reliable) {
|
|
6540
|
+
if (!reliable && remotePeerId !== null) {
|
|
6541
|
+
app.sim.emit.network("peer:leave", { peerId: remotePeerId });
|
|
6542
|
+
}
|
|
6543
|
+
},
|
|
6544
|
+
onDataChannelOpen(peerId, reliable, channel) {
|
|
6545
|
+
if (!reliable) {
|
|
6546
|
+
udp = channel;
|
|
6547
|
+
if (localStringPeerId === null) {
|
|
6548
|
+
console.error("[netcode] Local peer ID not assigned yet!");
|
|
6549
|
+
return;
|
|
6550
|
+
}
|
|
6551
|
+
const ids = assignPeerIds(localStringPeerId, peerId);
|
|
6552
|
+
localPeerId = ids.local;
|
|
6553
|
+
setLocalId(localPeerId);
|
|
6554
|
+
remotePeerId = ids.remote;
|
|
6555
|
+
remoteStringPeerId = peerId;
|
|
6556
|
+
setRemoteId(remotePeerId);
|
|
6557
|
+
app.sim.emit.network("peer:join", { peerId: localPeerId });
|
|
6558
|
+
app.sim.emit.network("peer:join", { peerId: remotePeerId });
|
|
6559
|
+
app.sim.emit.network("peer:assign_local_id", { peerId: localPeerId });
|
|
6560
|
+
app.sim.emit.network("session:start", {});
|
|
6561
|
+
}
|
|
6562
|
+
},
|
|
6563
|
+
onPeerConnected(peerId) {
|
|
6564
|
+
addPeer({
|
|
6565
|
+
id: peerId,
|
|
6566
|
+
nickname: peerId.substring(0, 6),
|
|
6567
|
+
ack: -1,
|
|
6568
|
+
seq: -1,
|
|
6569
|
+
lastPacketTime: performance.now()
|
|
6570
|
+
});
|
|
6571
|
+
console.log(`[netcode] Peer connected: ${peerId}. Total peers: ${debugState.netStatus.value.peers.length}`);
|
|
6572
|
+
},
|
|
6573
|
+
onPeerDisconnected(peerId) {
|
|
6574
|
+
removePeer(peerId);
|
|
6575
|
+
if (remotePeerId !== null && peerId === remoteStringPeerId) {
|
|
6576
|
+
app.sim.emit.network("peer:leave", { peerId: remotePeerId });
|
|
6577
|
+
app.sim.emit.network("session:end", {});
|
|
6578
|
+
}
|
|
6579
|
+
}
|
|
6580
|
+
});
|
|
6581
|
+
}
|
|
6582
|
+
function assignPeerIds(localId, remoteId) {
|
|
6583
|
+
if (localId < remoteId) {
|
|
6584
|
+
return { local: 0, remote: 1 };
|
|
6585
|
+
} else {
|
|
6586
|
+
return { local: 1, remote: 0 };
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
function receivePackets(app) {
|
|
6590
|
+
for (const packetData of incomingPackets) {
|
|
6591
|
+
app.sim.emit.packet(packetData);
|
|
6592
|
+
if (remotePeerId == null) {
|
|
6593
|
+
return;
|
|
6594
|
+
}
|
|
6595
|
+
const peerState = unwrap(app.sim.net.peers[remotePeerId], `Remote peer state not found for peerId ${remotePeerId}`);
|
|
6596
|
+
updatePeer(remoteStringPeerId, {
|
|
6597
|
+
ack: peerState.ack,
|
|
6598
|
+
seq: peerState.seq,
|
|
6599
|
+
lastPacketTime: performance.now()
|
|
6600
|
+
});
|
|
6601
|
+
}
|
|
6602
|
+
incomingPackets.length = 0;
|
|
6603
|
+
}
|
|
6604
|
+
function sendPacket(app) {
|
|
6605
|
+
if (!udp || remotePeerId === null) {
|
|
6606
|
+
console.warn("[netcode] Cannot send packet, udp or remotePeerId is null");
|
|
6607
|
+
return;
|
|
6608
|
+
}
|
|
6609
|
+
if (udp.readyState !== "open") {
|
|
6610
|
+
console.warn("[netcode] Data channel not open, cannot send packet. readyState=", udp.readyState);
|
|
6611
|
+
return;
|
|
6612
|
+
}
|
|
6613
|
+
const packet = app.sim.getOutboundPacket(remotePeerId);
|
|
6614
|
+
if (!packet) {
|
|
6615
|
+
console.warn("[netcode] No packet to send");
|
|
6616
|
+
return;
|
|
6617
|
+
}
|
|
6618
|
+
udp.send(packet);
|
|
6619
|
+
}
|
|
6620
|
+
|
|
6330
6621
|
// src/App.ts
|
|
6331
6622
|
var DEFAULT_BROKER_URL = "wss://webrtc-divine-glade-8064.fly.dev/ws";
|
|
6332
6623
|
async function start(opts) {
|
|
@@ -6350,6 +6641,7 @@ class App {
|
|
|
6350
6641
|
#unsubscribe = null;
|
|
6351
6642
|
#now = performance.now();
|
|
6352
6643
|
#debugUi = null;
|
|
6644
|
+
#abortController = new AbortController;
|
|
6353
6645
|
constructor(sim, game, brokerUrl, debugUiOpts) {
|
|
6354
6646
|
this.#sim = sim;
|
|
6355
6647
|
this.game = game;
|
|
@@ -6363,6 +6655,9 @@ class App {
|
|
|
6363
6655
|
this.beforeFrame.notify(frame);
|
|
6364
6656
|
};
|
|
6365
6657
|
this.subscribe();
|
|
6658
|
+
reconcile(this, this.#abortController.signal).catch((err) => {
|
|
6659
|
+
console.error("Error in lemmyloop:", err);
|
|
6660
|
+
});
|
|
6366
6661
|
}
|
|
6367
6662
|
get sim() {
|
|
6368
6663
|
return this.#sim;
|
|
@@ -6403,6 +6698,41 @@ class App {
|
|
|
6403
6698
|
onHmr = createListener();
|
|
6404
6699
|
subscribe() {
|
|
6405
6700
|
const shouldEmitInputs = () => !this.sim.isReplaying;
|
|
6701
|
+
let resizeObserver = null;
|
|
6702
|
+
let cleanupPixelRatio = null;
|
|
6703
|
+
const emitResize = () => {
|
|
6704
|
+
const canvas2 = this.canvas;
|
|
6705
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
6706
|
+
let width;
|
|
6707
|
+
let height;
|
|
6708
|
+
if (canvas2) {
|
|
6709
|
+
const rect = canvas2.getBoundingClientRect();
|
|
6710
|
+
width = Math.round(rect.width);
|
|
6711
|
+
height = Math.round(rect.height);
|
|
6712
|
+
} else {
|
|
6713
|
+
width = window.innerWidth;
|
|
6714
|
+
height = window.innerHeight;
|
|
6715
|
+
}
|
|
6716
|
+
this.sim.emit.resize(width, height, Math.round(width * pixelRatio), Math.round(height * pixelRatio), pixelRatio);
|
|
6717
|
+
};
|
|
6718
|
+
const canvas = this.canvas;
|
|
6719
|
+
if (canvas) {
|
|
6720
|
+
resizeObserver = new ResizeObserver(() => emitResize());
|
|
6721
|
+
resizeObserver.observe(canvas);
|
|
6722
|
+
} else {
|
|
6723
|
+
window.addEventListener("resize", emitResize);
|
|
6724
|
+
}
|
|
6725
|
+
const updatePixelRatioListener = () => {
|
|
6726
|
+
const mediaQuery = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
|
|
6727
|
+
const handler = () => {
|
|
6728
|
+
emitResize();
|
|
6729
|
+
updatePixelRatioListener();
|
|
6730
|
+
};
|
|
6731
|
+
mediaQuery.addEventListener("change", handler, { once: true });
|
|
6732
|
+
return () => mediaQuery.removeEventListener("change", handler);
|
|
6733
|
+
};
|
|
6734
|
+
cleanupPixelRatio = updatePixelRatioListener();
|
|
6735
|
+
emitResize();
|
|
6406
6736
|
const handleKeydown = (event) => {
|
|
6407
6737
|
if (shouldEmitInputs())
|
|
6408
6738
|
this.sim.emit.keydown(event.code);
|
|
@@ -6556,6 +6886,9 @@ class App {
|
|
|
6556
6886
|
window.removeEventListener("touchstart", handleTouchstart);
|
|
6557
6887
|
window.removeEventListener("touchend", handleTouchend);
|
|
6558
6888
|
window.removeEventListener("touchmove", handleTouchmove);
|
|
6889
|
+
window.removeEventListener("resize", emitResize);
|
|
6890
|
+
resizeObserver?.disconnect();
|
|
6891
|
+
cleanupPixelRatio?.();
|
|
6559
6892
|
if (this.#rafHandle != null) {
|
|
6560
6893
|
cancelAnimationFrame(this.#rafHandle);
|
|
6561
6894
|
}
|
|
@@ -6568,6 +6901,7 @@ class App {
|
|
|
6568
6901
|
this.afterFrame.unsubscribeAll();
|
|
6569
6902
|
this.onHmr.unsubscribeAll();
|
|
6570
6903
|
this.#debugUi?.unmount();
|
|
6904
|
+
this.#abortController.abort();
|
|
6571
6905
|
}
|
|
6572
6906
|
async acceptHmr(module, opts) {
|
|
6573
6907
|
const game = module.game ?? module;
|
|
@@ -6612,131 +6946,13 @@ var PacketType;
|
|
|
6612
6946
|
PacketType2[PacketType2["None"] = 0] = "None";
|
|
6613
6947
|
PacketType2[PacketType2["Inputs"] = 1] = "Inputs";
|
|
6614
6948
|
})(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
6949
|
export {
|
|
6733
6950
|
start,
|
|
6734
6951
|
logger,
|
|
6735
|
-
joinRollbackRoom,
|
|
6736
6952
|
PacketType,
|
|
6737
6953
|
exports_mod as Debug,
|
|
6738
6954
|
App
|
|
6739
6955
|
};
|
|
6740
6956
|
|
|
6741
|
-
//# debugId=
|
|
6957
|
+
//# debugId=669ECA7E8ACBF62064756E2164756E21
|
|
6742
6958
|
//# sourceMappingURL=mod.js.map
|