quake2ts 0.0.557 → 0.0.562

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.
Files changed (51) hide show
  1. package/package.json +3 -1
  2. package/packages/client/dist/browser/index.global.js +15 -15
  3. package/packages/client/dist/browser/index.global.js.map +1 -1
  4. package/packages/client/dist/cjs/index.cjs +343 -1
  5. package/packages/client/dist/cjs/index.cjs.map +1 -1
  6. package/packages/client/dist/esm/index.js +343 -1
  7. package/packages/client/dist/esm/index.js.map +1 -1
  8. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  9. package/packages/client/dist/types/index.d.ts.map +1 -1
  10. package/packages/client/dist/types/net/connection.d.ts +2 -0
  11. package/packages/client/dist/types/net/connection.d.ts.map +1 -1
  12. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  13. package/packages/engine/dist/types/render/bloom.d.ts +19 -0
  14. package/packages/engine/dist/types/render/bloom.d.ts.map +1 -0
  15. package/packages/engine/dist/types/render/frame.d.ts +2 -0
  16. package/packages/engine/dist/types/render/frame.d.ts.map +1 -1
  17. package/packages/engine/dist/types/render/renderer.d.ts +2 -0
  18. package/packages/engine/dist/types/render/renderer.d.ts.map +1 -1
  19. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
  20. package/packages/server/dist/client.d.ts +51 -0
  21. package/packages/server/dist/client.js +100 -0
  22. package/packages/server/dist/dedicated.d.ts +69 -0
  23. package/packages/server/dist/dedicated.js +1013 -0
  24. package/packages/server/dist/index.cjs +27 -2
  25. package/packages/server/dist/index.d.ts +7 -161
  26. package/packages/server/dist/index.js +26 -2
  27. package/packages/server/dist/net/nodeWsDriver.d.ts +16 -0
  28. package/packages/server/dist/net/nodeWsDriver.js +122 -0
  29. package/packages/server/dist/protocol/player.d.ts +23 -0
  30. package/packages/server/dist/protocol/player.js +137 -0
  31. package/packages/server/dist/protocol/write.d.ts +7 -0
  32. package/packages/server/dist/protocol/write.js +167 -0
  33. package/packages/server/dist/protocol.d.ts +17 -0
  34. package/packages/server/dist/protocol.js +71 -0
  35. package/packages/server/dist/server.d.ts +50 -0
  36. package/packages/server/dist/server.js +12 -0
  37. package/packages/server/dist/server.test.d.ts +1 -0
  38. package/packages/server/dist/server.test.js +69 -0
  39. package/packages/server/dist/transport.d.ts +7 -0
  40. package/packages/server/dist/transport.js +1 -0
  41. package/packages/server/dist/transports/websocket.d.ts +11 -0
  42. package/packages/server/dist/transports/websocket.js +38 -0
  43. package/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
  44. package/packages/test-utils/dist/index.cjs +498 -284
  45. package/packages/test-utils/dist/index.cjs.map +1 -1
  46. package/packages/test-utils/dist/index.d.cts +215 -146
  47. package/packages/test-utils/dist/index.d.ts +215 -146
  48. package/packages/test-utils/dist/index.js +488 -278
  49. package/packages/test-utils/dist/index.js.map +1 -1
  50. package/packages/tools/dist/tsconfig.tsbuildinfo +1 -1
  51. package/packages/server/dist/index.d.cts +0 -161
@@ -391,6 +391,218 @@ function createEntity() {
391
391
  return new Entity(1);
392
392
  }
393
393
 
394
+ // src/game/mocks.ts
395
+ function createMockGameState(overrides) {
396
+ return {
397
+ levelName: "test_level",
398
+ time: 0,
399
+ entities: [],
400
+ clients: [],
401
+ ...overrides
402
+ };
403
+ }
404
+
405
+ // src/server/mocks/transport.ts
406
+ import { vi as vi3 } from "vitest";
407
+ var MockTransport = class {
408
+ constructor() {
409
+ this.address = "127.0.0.1";
410
+ this.port = 27910;
411
+ this.sentMessages = [];
412
+ this.receivedMessages = [];
413
+ this.listening = false;
414
+ this.listenSpy = vi3.fn().mockImplementation(async (port) => {
415
+ this.port = port;
416
+ this.listening = true;
417
+ });
418
+ this.closeSpy = vi3.fn().mockImplementation(() => {
419
+ this.listening = false;
420
+ });
421
+ }
422
+ /**
423
+ * Start listening on the specified port.
424
+ */
425
+ async listen(port) {
426
+ return this.listenSpy(port);
427
+ }
428
+ /**
429
+ * Close the transport.
430
+ */
431
+ close() {
432
+ this.closeSpy();
433
+ }
434
+ /**
435
+ * Register a callback for new connections.
436
+ */
437
+ onConnection(callback) {
438
+ this.onConnectionCallback = callback;
439
+ }
440
+ /**
441
+ * Register a callback for errors.
442
+ */
443
+ onError(callback) {
444
+ this.onErrorCallback = callback;
445
+ }
446
+ /**
447
+ * Check if the transport is currently listening.
448
+ */
449
+ isListening() {
450
+ return this.listening;
451
+ }
452
+ /**
453
+ * Helper to simulate a new connection.
454
+ * @param driver The network driver for the connection.
455
+ * @param info Optional connection info.
456
+ */
457
+ simulateConnection(driver, info) {
458
+ if (this.onConnectionCallback) {
459
+ this.onConnectionCallback(driver, info);
460
+ }
461
+ }
462
+ /**
463
+ * Helper to simulate an error.
464
+ * @param error The error to simulate.
465
+ */
466
+ simulateError(error) {
467
+ if (this.onErrorCallback) {
468
+ this.onErrorCallback(error);
469
+ }
470
+ }
471
+ };
472
+ function createMockUDPSocket(overrides) {
473
+ const socket = {
474
+ send: vi3.fn(),
475
+ on: vi3.fn(),
476
+ close: vi3.fn(),
477
+ bind: vi3.fn(),
478
+ address: vi3.fn().mockReturnValue({ address: "127.0.0.1", family: "IPv4", port: 0 }),
479
+ ...overrides
480
+ };
481
+ return socket;
482
+ }
483
+ function createMockNetworkAddress(ip = "127.0.0.1", port = 27910) {
484
+ return { ip, port };
485
+ }
486
+ function createMockTransport(address = "127.0.0.1", port = 27910, overrides) {
487
+ const transport = new MockTransport();
488
+ transport.address = address;
489
+ transport.port = port;
490
+ Object.assign(transport, overrides);
491
+ return transport;
492
+ }
493
+
494
+ // src/server/mocks/state.ts
495
+ import { ServerState, ClientState } from "@quake2ts/server";
496
+ import { MAX_CONFIGSTRINGS, MAX_EDICTS } from "@quake2ts/shared";
497
+ import { vi as vi4 } from "vitest";
498
+ function createMockServerState(overrides) {
499
+ return {
500
+ state: ServerState.Game,
501
+ attractLoop: false,
502
+ loadGame: false,
503
+ startTime: Date.now(),
504
+ time: 0,
505
+ frame: 0,
506
+ name: "test_map",
507
+ collisionModel: null,
508
+ configStrings: new Array(MAX_CONFIGSTRINGS).fill(""),
509
+ baselines: new Array(MAX_EDICTS).fill(null),
510
+ multicastBuf: new Uint8Array(0),
511
+ ...overrides
512
+ };
513
+ }
514
+ function createMockServerStatic(maxClients = 16, overrides) {
515
+ return {
516
+ initialized: true,
517
+ realTime: Date.now(),
518
+ mapCmd: "",
519
+ spawnCount: 1,
520
+ clients: new Array(maxClients).fill(null),
521
+ lastHeartbeat: 0,
522
+ challenges: [],
523
+ ...overrides
524
+ };
525
+ }
526
+ function createMockServerClient(clientNum, overrides) {
527
+ const mockNet = {
528
+ connect: vi4.fn(),
529
+ disconnect: vi4.fn(),
530
+ send: vi4.fn(),
531
+ onMessage: vi4.fn(),
532
+ onClose: vi4.fn(),
533
+ onError: vi4.fn(),
534
+ isConnected: vi4.fn().mockReturnValue(true)
535
+ };
536
+ return {
537
+ index: clientNum,
538
+ state: ClientState.Connected,
539
+ edict: { index: clientNum + 1 },
540
+ net: mockNet,
541
+ netchan: {
542
+ qport: 0,
543
+ remoteAddress: "127.0.0.1",
544
+ incomingSequence: 0,
545
+ outgoingSequence: 0,
546
+ lastReceived: 0,
547
+ process: vi4.fn(),
548
+ transmit: vi4.fn(),
549
+ writeReliableByte: vi4.fn(),
550
+ writeReliableShort: vi4.fn(),
551
+ writeReliableLong: vi4.fn(),
552
+ writeReliableString: vi4.fn(),
553
+ writeReliableData: vi4.fn()
554
+ },
555
+ // Cast as any because NetChan might be complex to fully mock here
556
+ userInfo: "",
557
+ lastMessage: 0,
558
+ lastCommandTime: 0,
559
+ commandCount: 0,
560
+ messageQueue: [],
561
+ frames: [],
562
+ lastFrame: 0,
563
+ lastPacketEntities: [],
564
+ challenge: 0,
565
+ lastConnect: 0,
566
+ ping: 0,
567
+ rate: 0,
568
+ name: `Client${clientNum}`,
569
+ messageLevel: 0,
570
+ datagram: new Uint8Array(0),
571
+ downloadSize: 0,
572
+ downloadCount: 0,
573
+ commandMsec: 0,
574
+ frameLatency: [],
575
+ messageSize: [],
576
+ suppressCount: 0,
577
+ commandQueue: [],
578
+ lastCmd: {
579
+ msec: 0,
580
+ buttons: 0,
581
+ angles: { x: 0, y: 0, z: 0 },
582
+ forwardmove: 0,
583
+ sidemove: 0,
584
+ upmove: 0,
585
+ sequence: 0,
586
+ lightlevel: 0,
587
+ impulse: 0,
588
+ serverFrame: 0
589
+ },
590
+ ...overrides
591
+ };
592
+ }
593
+ function createMockServer(overrides) {
594
+ return {
595
+ start: vi4.fn().mockResolvedValue(void 0),
596
+ stop: vi4.fn(),
597
+ multicast: vi4.fn(),
598
+ unicast: vi4.fn(),
599
+ configstring: vi4.fn(),
600
+ kickPlayer: vi4.fn(),
601
+ changeMap: vi4.fn().mockResolvedValue(void 0),
602
+ ...overrides
603
+ };
604
+ }
605
+
394
606
  // src/setup/browser.ts
395
607
  import { JSDOM } from "jsdom";
396
608
  import { Canvas, Image, ImageData } from "@napi-rs/canvas";
@@ -786,12 +998,6 @@ function teardownBrowserEnvironment() {
786
998
  delete global.UIEvent;
787
999
  }
788
1000
 
789
- // src/setup/node.ts
790
- function setupNodeEnvironment() {
791
- if (typeof global.fetch === "undefined") {
792
- }
793
- }
794
-
795
1001
  // src/setup/canvas.ts
796
1002
  import { Canvas as Canvas2, Image as Image2, ImageData as ImageData2 } from "@napi-rs/canvas";
797
1003
  function createMockCanvas(width = 300, height = 150) {
@@ -873,111 +1079,9 @@ function createMockImage(width, height, src) {
873
1079
  return img;
874
1080
  }
875
1081
 
876
- // src/setup/timing.ts
877
- function createMockRAF() {
878
- let callbacks = [];
879
- let nextId = 1;
880
- let currentTime = 0;
881
- const originalRAF = global.requestAnimationFrame;
882
- const originalCancelRAF = global.cancelAnimationFrame;
883
- const raf = (callback) => {
884
- const id = nextId++;
885
- callbacks.push({ id, callback });
886
- return id;
887
- };
888
- const cancel = (id) => {
889
- callbacks = callbacks.filter((cb) => cb.id !== id);
890
- };
891
- const mock = {
892
- tick(timestamp) {
893
- if (typeof timestamp !== "number") {
894
- currentTime += 16.6;
895
- } else {
896
- currentTime = timestamp;
897
- }
898
- const currentCallbacks = [...callbacks];
899
- callbacks = [];
900
- currentCallbacks.forEach(({ callback }) => {
901
- callback(currentTime);
902
- });
903
- },
904
- advance(deltaMs = 16.6) {
905
- this.tick(currentTime + deltaMs);
906
- },
907
- getCallbacks() {
908
- return callbacks.map((c) => c.callback);
909
- },
910
- reset() {
911
- callbacks = [];
912
- nextId = 1;
913
- currentTime = 0;
914
- },
915
- enable() {
916
- global.requestAnimationFrame = raf;
917
- global.cancelAnimationFrame = cancel;
918
- },
919
- disable() {
920
- if (originalRAF) {
921
- global.requestAnimationFrame = originalRAF;
922
- } else {
923
- delete global.requestAnimationFrame;
924
- }
925
- if (originalCancelRAF) {
926
- global.cancelAnimationFrame = originalCancelRAF;
927
- } else {
928
- delete global.cancelAnimationFrame;
929
- }
930
- }
931
- };
932
- return mock;
933
- }
934
- function createMockPerformance(startTime = 0) {
935
- let currentTime = startTime;
936
- const mockPerf = {
937
- now: () => currentTime,
938
- timeOrigin: startTime,
939
- timing: {
940
- navigationStart: startTime
941
- },
942
- // Add minimal navigation/resource timing interfaces to satisfy types if needed
943
- clearMarks: () => {
944
- },
945
- clearMeasures: () => {
946
- },
947
- clearResourceTimings: () => {
948
- },
949
- getEntries: () => [],
950
- getEntriesByName: () => [],
951
- getEntriesByType: () => [],
952
- mark: () => {
953
- },
954
- measure: () => {
955
- },
956
- setResourceTimingBufferSize: () => {
957
- },
958
- toJSON: () => ({}),
959
- addEventListener: () => {
960
- },
961
- removeEventListener: () => {
962
- },
963
- dispatchEvent: () => true
964
- };
965
- mockPerf.advance = (deltaMs) => {
966
- currentTime += deltaMs;
967
- };
968
- mockPerf.setTime = (time) => {
969
- currentTime = time;
970
- };
971
- return mockPerf;
972
- }
973
- function simulateFrames(count, frameTimeMs = 16.6, callback) {
974
- const raf = global.requestAnimationFrame;
975
- if (!raf) return;
976
- }
977
- function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
978
- for (let i = 0; i < count; i++) {
979
- if (callback) callback(i);
980
- mock.advance(frameTimeMs);
1082
+ // src/setup/node.ts
1083
+ function setupNodeEnvironment() {
1084
+ if (typeof global.fetch === "undefined") {
981
1085
  }
982
1086
  }
983
1087
 
@@ -1094,204 +1198,305 @@ function captureAudioEvents(context) {
1094
1198
  return [];
1095
1199
  }
1096
1200
 
1201
+ // src/setup/timing.ts
1202
+ var activeMockRAF;
1203
+ function createMockRAF() {
1204
+ let callbacks = [];
1205
+ let nextId = 1;
1206
+ let currentTime = 0;
1207
+ const originalRAF = global.requestAnimationFrame;
1208
+ const originalCancelRAF = global.cancelAnimationFrame;
1209
+ const raf = (callback) => {
1210
+ const id = nextId++;
1211
+ callbacks.push({ id, callback });
1212
+ return id;
1213
+ };
1214
+ const cancel = (id) => {
1215
+ callbacks = callbacks.filter((cb) => cb.id !== id);
1216
+ };
1217
+ const mock = {
1218
+ tick(timestamp) {
1219
+ if (typeof timestamp !== "number") {
1220
+ currentTime += 16.6;
1221
+ } else {
1222
+ currentTime = timestamp;
1223
+ }
1224
+ const currentCallbacks = [...callbacks];
1225
+ callbacks = [];
1226
+ currentCallbacks.forEach(({ callback }) => {
1227
+ callback(currentTime);
1228
+ });
1229
+ },
1230
+ advance(deltaMs = 16.6) {
1231
+ this.tick(currentTime + deltaMs);
1232
+ },
1233
+ getCallbacks() {
1234
+ return callbacks.map((c) => c.callback);
1235
+ },
1236
+ reset() {
1237
+ callbacks = [];
1238
+ nextId = 1;
1239
+ currentTime = 0;
1240
+ },
1241
+ enable() {
1242
+ activeMockRAF = this;
1243
+ global.requestAnimationFrame = raf;
1244
+ global.cancelAnimationFrame = cancel;
1245
+ },
1246
+ disable() {
1247
+ if (activeMockRAF === this) {
1248
+ activeMockRAF = void 0;
1249
+ }
1250
+ if (originalRAF) {
1251
+ global.requestAnimationFrame = originalRAF;
1252
+ } else {
1253
+ delete global.requestAnimationFrame;
1254
+ }
1255
+ if (originalCancelRAF) {
1256
+ global.cancelAnimationFrame = originalCancelRAF;
1257
+ } else {
1258
+ delete global.cancelAnimationFrame;
1259
+ }
1260
+ }
1261
+ };
1262
+ return mock;
1263
+ }
1264
+ function createMockPerformance(startTime = 0) {
1265
+ let currentTime = startTime;
1266
+ const mockPerf = {
1267
+ now: () => currentTime,
1268
+ timeOrigin: startTime,
1269
+ timing: {
1270
+ navigationStart: startTime
1271
+ },
1272
+ clearMarks: () => {
1273
+ },
1274
+ clearMeasures: () => {
1275
+ },
1276
+ clearResourceTimings: () => {
1277
+ },
1278
+ getEntries: () => [],
1279
+ getEntriesByName: () => [],
1280
+ getEntriesByType: () => [],
1281
+ mark: () => {
1282
+ },
1283
+ measure: () => {
1284
+ },
1285
+ setResourceTimingBufferSize: () => {
1286
+ },
1287
+ toJSON: () => ({}),
1288
+ addEventListener: () => {
1289
+ },
1290
+ removeEventListener: () => {
1291
+ },
1292
+ dispatchEvent: () => true
1293
+ };
1294
+ mockPerf.advance = (deltaMs) => {
1295
+ currentTime += deltaMs;
1296
+ };
1297
+ mockPerf.setTime = (time) => {
1298
+ currentTime = time;
1299
+ };
1300
+ return mockPerf;
1301
+ }
1302
+ function createControlledTimer() {
1303
+ let currentTime = 0;
1304
+ let timers = [];
1305
+ let nextId = 1;
1306
+ const originalSetTimeout = global.setTimeout;
1307
+ const originalClearTimeout = global.clearTimeout;
1308
+ const originalSetInterval = global.setInterval;
1309
+ const originalClearInterval = global.clearInterval;
1310
+ const mockSetTimeout = (callback, delay = 0, ...args) => {
1311
+ const id = nextId++;
1312
+ timers.push({ id, callback, dueTime: currentTime + delay, args });
1313
+ return id;
1314
+ };
1315
+ const mockClearTimeout = (id) => {
1316
+ timers = timers.filter((t) => t.id !== id);
1317
+ };
1318
+ const mockSetInterval = (callback, delay = 0, ...args) => {
1319
+ const id = nextId++;
1320
+ timers.push({ id, callback, dueTime: currentTime + delay, interval: delay, args });
1321
+ return id;
1322
+ };
1323
+ const mockClearInterval = (id) => {
1324
+ timers = timers.filter((t) => t.id !== id);
1325
+ };
1326
+ global.setTimeout = mockSetTimeout;
1327
+ global.clearTimeout = mockClearTimeout;
1328
+ global.setInterval = mockSetInterval;
1329
+ global.clearInterval = mockClearInterval;
1330
+ return {
1331
+ tick() {
1332
+ this.advanceBy(0);
1333
+ },
1334
+ advanceBy(ms) {
1335
+ const targetTime = currentTime + ms;
1336
+ while (true) {
1337
+ let earliest = null;
1338
+ for (const t of timers) {
1339
+ if (!earliest || t.dueTime < earliest.dueTime) {
1340
+ earliest = t;
1341
+ }
1342
+ }
1343
+ if (!earliest || earliest.dueTime > targetTime) {
1344
+ break;
1345
+ }
1346
+ currentTime = earliest.dueTime;
1347
+ const { callback, args, interval, id } = earliest;
1348
+ if (interval !== void 0) {
1349
+ earliest.dueTime += interval;
1350
+ if (interval === 0) earliest.dueTime += 1;
1351
+ } else {
1352
+ timers = timers.filter((t) => t.id !== id);
1353
+ }
1354
+ callback(...args);
1355
+ }
1356
+ currentTime = targetTime;
1357
+ },
1358
+ clear() {
1359
+ timers = [];
1360
+ },
1361
+ restore() {
1362
+ global.setTimeout = originalSetTimeout;
1363
+ global.clearTimeout = originalClearTimeout;
1364
+ global.setInterval = originalSetInterval;
1365
+ global.clearInterval = originalClearInterval;
1366
+ }
1367
+ };
1368
+ }
1369
+ function simulateFrames(count, frameTimeMs = 16.6, callback) {
1370
+ if (!activeMockRAF) {
1371
+ throw new Error("simulateFrames requires an active MockRAF. Ensure createMockRAF().enable() is called.");
1372
+ }
1373
+ for (let i = 0; i < count; i++) {
1374
+ if (callback) callback(i);
1375
+ activeMockRAF.advance(frameTimeMs);
1376
+ }
1377
+ }
1378
+ function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
1379
+ for (let i = 0; i < count; i++) {
1380
+ if (callback) callback(i);
1381
+ mock.advance(frameTimeMs);
1382
+ }
1383
+ }
1384
+
1097
1385
  // src/e2e/playwright.ts
1098
1386
  import { chromium } from "playwright";
1387
+ import { createServer } from "http";
1388
+ import handler from "serve-handler";
1099
1389
  async function createPlaywrightTestClient(options = {}) {
1390
+ let staticServer;
1391
+ let clientUrl = options.clientUrl;
1392
+ const rootPath = options.rootPath || process.cwd();
1393
+ if (!clientUrl) {
1394
+ staticServer = createServer((request, response) => {
1395
+ return handler(request, response, {
1396
+ public: rootPath,
1397
+ cleanUrls: false,
1398
+ headers: [
1399
+ {
1400
+ source: "**/*",
1401
+ headers: [
1402
+ { key: "Cache-Control", value: "no-cache" },
1403
+ { key: "Access-Control-Allow-Origin", value: "*" },
1404
+ { key: "Cross-Origin-Opener-Policy", value: "same-origin" },
1405
+ { key: "Cross-Origin-Embedder-Policy", value: "require-corp" }
1406
+ ]
1407
+ }
1408
+ ]
1409
+ });
1410
+ });
1411
+ await new Promise((resolve) => {
1412
+ if (!staticServer) return;
1413
+ staticServer.listen(0, () => {
1414
+ const addr = staticServer?.address();
1415
+ const port = typeof addr === "object" ? addr?.port : 0;
1416
+ clientUrl = `http://localhost:${port}`;
1417
+ console.log(`Test client serving from ${rootPath} at ${clientUrl}`);
1418
+ resolve();
1419
+ });
1420
+ });
1421
+ }
1100
1422
  const browser = await chromium.launch({
1101
1423
  headless: options.headless ?? true,
1102
- args: options.args || [
1424
+ args: [
1103
1425
  "--use-gl=egl",
1104
1426
  "--ignore-gpu-blocklist",
1105
- "--no-sandbox",
1106
- "--disable-setuid-sandbox"
1107
- ]
1427
+ ...options.launchOptions?.args || []
1428
+ ],
1429
+ ...options.launchOptions
1108
1430
  });
1431
+ const width = options.width || 1280;
1432
+ const height = options.height || 720;
1109
1433
  const context = await browser.newContext({
1110
- viewport: options.viewport || { width: 1280, height: 720 },
1111
- recordVideo: options.recordVideo,
1112
- deviceScaleFactor: 1
1434
+ viewport: { width, height },
1435
+ deviceScaleFactor: 1,
1436
+ ...options.contextOptions
1113
1437
  });
1114
1438
  const page = await context.newPage();
1115
- const waitForGame = async (timeout = 1e4) => {
1116
- try {
1117
- await page.waitForFunction(() => {
1118
- return window.game || document.querySelector("canvas");
1119
- }, null, { timeout });
1120
- } catch (e) {
1121
- throw new Error(`Game did not initialize within ${timeout}ms`);
1439
+ const close = async () => {
1440
+ await browser.close();
1441
+ if (staticServer) {
1442
+ staticServer.close();
1122
1443
  }
1123
1444
  };
1124
- const client = {
1445
+ const navigate = async (url) => {
1446
+ const targetUrl = url || clientUrl;
1447
+ if (!targetUrl) throw new Error("No URL to navigate to");
1448
+ let finalUrl = targetUrl;
1449
+ if (options.serverUrl && !targetUrl.includes("connect=")) {
1450
+ const separator = targetUrl.includes("?") ? "&" : "?";
1451
+ finalUrl = `${targetUrl}${separator}connect=${encodeURIComponent(options.serverUrl)}`;
1452
+ }
1453
+ console.log(`Navigating to: ${finalUrl}`);
1454
+ await page.goto(finalUrl, { waitUntil: "domcontentloaded" });
1455
+ };
1456
+ return {
1125
1457
  browser,
1126
1458
  context,
1127
1459
  page,
1128
- async navigate(url) {
1129
- await page.goto(url, { waitUntil: "domcontentloaded" });
1130
- },
1131
- async waitForGame(timeout) {
1132
- await waitForGame(timeout);
1133
- },
1134
- async injectInput(type, key) {
1135
- if (type === "keydown") {
1136
- await page.keyboard.down(key);
1137
- } else {
1138
- await page.keyboard.up(key);
1139
- }
1140
- },
1141
- async injectMouse(type, x = 0, y = 0, button = 0) {
1142
- if (type === "move") {
1143
- await page.mouse.move(x, y);
1144
- } else if (type === "down") {
1145
- await page.mouse.down({ button: button === 0 ? "left" : button === 2 ? "right" : "middle" });
1146
- } else if (type === "up") {
1147
- await page.mouse.up({ button: button === 0 ? "left" : button === 2 ? "right" : "middle" });
1148
- }
1149
- },
1150
- async screenshot(path2) {
1151
- await page.screenshot({ path: path2 });
1152
- },
1153
- async close() {
1154
- await browser.close();
1460
+ server: staticServer,
1461
+ close,
1462
+ navigate,
1463
+ waitForGame: async (timeout = 1e4) => {
1464
+ await waitForGameReady(page, timeout);
1465
+ },
1466
+ injectInput: async (type, data) => {
1467
+ await page.evaluate(({ type: type2, data: data2 }) => {
1468
+ if (window.injectGameInput) window.injectGameInput(type2, data2);
1469
+ }, { type, data });
1155
1470
  }
1156
1471
  };
1157
- return client;
1158
1472
  }
1159
1473
  async function waitForGameReady(page, timeout = 1e4) {
1160
- await page.waitForFunction(() => {
1161
- return window.game || document.querySelector("canvas");
1162
- }, null, { timeout });
1474
+ try {
1475
+ await page.waitForFunction(() => {
1476
+ return window.gameInstance && window.gameInstance.isReady;
1477
+ }, null, { timeout });
1478
+ } catch (e) {
1479
+ await page.waitForSelector("canvas", { timeout });
1480
+ }
1163
1481
  }
1164
1482
  async function captureGameState(page) {
1165
1483
  return await page.evaluate(() => {
1166
- const game = window.game;
1167
- if (!game) return {};
1168
- return {};
1169
- });
1170
- }
1171
-
1172
- // src/e2e/network.ts
1173
- var CONDITIONS = {
1174
- "good": {
1175
- offline: false,
1176
- downloadThroughput: 10 * 1024 * 1024,
1177
- // 10 Mbps
1178
- uploadThroughput: 5 * 1024 * 1024,
1179
- // 5 Mbps
1180
- latency: 20
1181
- },
1182
- "slow": {
1183
- offline: false,
1184
- downloadThroughput: 500 * 1024,
1185
- // 500 Kbps
1186
- uploadThroughput: 500 * 1024,
1187
- latency: 400
1188
- },
1189
- "unstable": {
1190
- offline: false,
1191
- downloadThroughput: 1 * 1024 * 1024,
1192
- uploadThroughput: 1 * 1024 * 1024,
1193
- latency: 100
1194
- // Jitter needs logic not simple preset
1195
- },
1196
- "offline": {
1197
- offline: true,
1198
- downloadThroughput: 0,
1199
- uploadThroughput: 0,
1200
- latency: 0
1201
- }
1202
- };
1203
- function simulateNetworkCondition(condition) {
1204
- const config = CONDITIONS[condition];
1205
- return createCustomNetworkCondition(config.latency, 0, 0, config);
1206
- }
1207
- function createCustomNetworkCondition(latency, jitter = 0, packetLoss = 0, baseConfig) {
1208
- return {
1209
- async apply(page) {
1210
- const client = await page.context().newCDPSession(page);
1211
- await client.send("Network.enable");
1212
- await client.send("Network.emulateNetworkConditions", {
1213
- offline: baseConfig?.offline || false,
1214
- latency: latency + Math.random() * jitter,
1215
- // Basic jitter application on setup (static)
1216
- downloadThroughput: baseConfig?.downloadThroughput || -1,
1217
- uploadThroughput: baseConfig?.uploadThroughput || -1
1218
- });
1219
- },
1220
- async clear(page) {
1221
- const client = await page.context().newCDPSession(page);
1222
- await client.send("Network.emulateNetworkConditions", {
1223
- offline: false,
1224
- latency: 0,
1225
- downloadThroughput: -1,
1226
- uploadThroughput: -1
1227
- });
1484
+ if (window.gameInstance && window.gameInstance.getState) {
1485
+ return window.gameInstance.getState();
1228
1486
  }
1229
- };
1230
- }
1231
- async function throttleBandwidth(page, bytesPerSecond) {
1232
- const simulator = createCustomNetworkCondition(0, 0, 0, {
1233
- offline: false,
1234
- latency: 0,
1235
- downloadThroughput: bytesPerSecond,
1236
- uploadThroughput: bytesPerSecond
1237
- });
1238
- await simulator.apply(page);
1239
- }
1240
-
1241
- // src/e2e/visual.ts
1242
- import path from "path";
1243
- import fs from "fs/promises";
1244
- async function captureGameScreenshot(page, name, options = {}) {
1245
- const dir = options.dir || "__screenshots__";
1246
- const screenshotPath = path.join(dir, `${name}.png`);
1247
- await fs.mkdir(dir, { recursive: true });
1248
- return await page.screenshot({
1249
- path: screenshotPath,
1250
- fullPage: options.fullPage ?? false,
1251
- animations: "disabled",
1252
- // Try to freeze animations if possible
1253
- caret: "hide"
1487
+ return {};
1254
1488
  });
1255
1489
  }
1256
- async function compareScreenshots(baseline, current, threshold = 0.1) {
1257
- if (baseline.equals(current)) {
1258
- return { pixelDiff: 0, matched: true };
1259
- }
1260
- return {
1261
- pixelDiff: -1,
1262
- // Unknown magnitude
1263
- matched: false
1264
- };
1265
- }
1266
- function createVisualTestScenario(page, sceneName) {
1267
- return {
1268
- async capture(snapshotName) {
1269
- return await captureGameScreenshot(page, `${sceneName}-${snapshotName}`);
1270
- },
1271
- async compare(snapshotName, baselineDir) {
1272
- const name = `${sceneName}-${snapshotName}`;
1273
- const current = await captureGameScreenshot(page, name, { dir: "__screenshots__/current" });
1274
- try {
1275
- const baselinePath = path.join(baselineDir, `${name}.png`);
1276
- const baseline = await fs.readFile(baselinePath);
1277
- return await compareScreenshots(baseline, current);
1278
- } catch (e) {
1279
- return { pixelDiff: -1, matched: false };
1280
- }
1281
- }
1282
- };
1283
- }
1284
1490
  export {
1285
1491
  InputInjector,
1286
1492
  MockPointerLock,
1493
+ MockTransport,
1287
1494
  captureAudioEvents,
1288
1495
  captureCanvasDrawCalls,
1289
- captureGameScreenshot,
1290
1496
  captureGameState,
1291
- compareScreenshots,
1292
1497
  createBinaryStreamMock,
1293
1498
  createBinaryWriterMock,
1294
- createCustomNetworkCondition,
1499
+ createControlledTimer,
1295
1500
  createEntity,
1296
1501
  createEntityStateFactory,
1297
1502
  createGameStateSnapshotFactory,
@@ -1300,13 +1505,21 @@ export {
1300
1505
  createMockCanvasContext2D,
1301
1506
  createMockEngine,
1302
1507
  createMockGame,
1508
+ createMockGameState,
1303
1509
  createMockImage,
1304
1510
  createMockImageData,
1305
1511
  createMockIndexedDB,
1306
1512
  createMockLocalStorage,
1513
+ createMockNetworkAddress,
1307
1514
  createMockPerformance,
1308
1515
  createMockRAF,
1516
+ createMockServer,
1517
+ createMockServerClient,
1518
+ createMockServerState,
1519
+ createMockServerStatic,
1309
1520
  createMockSessionStorage,
1521
+ createMockTransport,
1522
+ createMockUDPSocket,
1310
1523
  createMockWebGL2Context,
1311
1524
  createNetChanMock,
1312
1525
  createPlayerStateFactory,
@@ -1314,7 +1527,6 @@ export {
1314
1527
  createSpawnContext,
1315
1528
  createStorageTestScenario,
1316
1529
  createTestContext,
1317
- createVisualTestScenario,
1318
1530
  intersects,
1319
1531
  ladderTrace,
1320
1532
  makeAxisBrush,
@@ -1329,11 +1541,9 @@ export {
1329
1541
  setupNodeEnvironment,
1330
1542
  simulateFrames,
1331
1543
  simulateFramesWithMock,
1332
- simulateNetworkCondition,
1333
1544
  stairTrace,
1334
1545
  teardownBrowserEnvironment,
1335
1546
  teardownMockAudioContext,
1336
- throttleBandwidth,
1337
1547
  waitForGameReady
1338
1548
  };
1339
1549
  //# sourceMappingURL=index.js.map