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
@@ -32,14 +32,13 @@ var index_exports = {};
32
32
  __export(index_exports, {
33
33
  InputInjector: () => InputInjector,
34
34
  MockPointerLock: () => MockPointerLock,
35
+ MockTransport: () => MockTransport,
35
36
  captureAudioEvents: () => captureAudioEvents,
36
37
  captureCanvasDrawCalls: () => captureCanvasDrawCalls,
37
- captureGameScreenshot: () => captureGameScreenshot,
38
38
  captureGameState: () => captureGameState,
39
- compareScreenshots: () => compareScreenshots,
40
39
  createBinaryStreamMock: () => createBinaryStreamMock,
41
40
  createBinaryWriterMock: () => createBinaryWriterMock,
42
- createCustomNetworkCondition: () => createCustomNetworkCondition,
41
+ createControlledTimer: () => createControlledTimer,
43
42
  createEntity: () => createEntity,
44
43
  createEntityStateFactory: () => createEntityStateFactory,
45
44
  createGameStateSnapshotFactory: () => createGameStateSnapshotFactory,
@@ -48,13 +47,21 @@ __export(index_exports, {
48
47
  createMockCanvasContext2D: () => createMockCanvasContext2D,
49
48
  createMockEngine: () => createMockEngine,
50
49
  createMockGame: () => createMockGame,
50
+ createMockGameState: () => createMockGameState,
51
51
  createMockImage: () => createMockImage,
52
52
  createMockImageData: () => createMockImageData,
53
53
  createMockIndexedDB: () => createMockIndexedDB,
54
54
  createMockLocalStorage: () => createMockLocalStorage,
55
+ createMockNetworkAddress: () => createMockNetworkAddress,
55
56
  createMockPerformance: () => createMockPerformance,
56
57
  createMockRAF: () => createMockRAF,
58
+ createMockServer: () => createMockServer,
59
+ createMockServerClient: () => createMockServerClient,
60
+ createMockServerState: () => createMockServerState,
61
+ createMockServerStatic: () => createMockServerStatic,
57
62
  createMockSessionStorage: () => createMockSessionStorage,
63
+ createMockTransport: () => createMockTransport,
64
+ createMockUDPSocket: () => createMockUDPSocket,
58
65
  createMockWebGL2Context: () => createMockWebGL2Context,
59
66
  createNetChanMock: () => createNetChanMock,
60
67
  createPlayerStateFactory: () => createPlayerStateFactory,
@@ -62,7 +69,6 @@ __export(index_exports, {
62
69
  createSpawnContext: () => createSpawnContext,
63
70
  createStorageTestScenario: () => createStorageTestScenario,
64
71
  createTestContext: () => createTestContext,
65
- createVisualTestScenario: () => createVisualTestScenario,
66
72
  intersects: () => import_shared3.intersects,
67
73
  ladderTrace: () => import_shared3.ladderTrace,
68
74
  makeAxisBrush: () => makeAxisBrush,
@@ -77,11 +83,9 @@ __export(index_exports, {
77
83
  setupNodeEnvironment: () => setupNodeEnvironment,
78
84
  simulateFrames: () => simulateFrames,
79
85
  simulateFramesWithMock: () => simulateFramesWithMock,
80
- simulateNetworkCondition: () => simulateNetworkCondition,
81
86
  stairTrace: () => import_shared3.stairTrace,
82
87
  teardownBrowserEnvironment: () => teardownBrowserEnvironment,
83
88
  teardownMockAudioContext: () => teardownMockAudioContext,
84
- throttleBandwidth: () => throttleBandwidth,
85
89
  waitForGameReady: () => waitForGameReady
86
90
  });
87
91
  module.exports = __toCommonJS(index_exports);
@@ -476,6 +480,218 @@ function createEntity() {
476
480
  return new import_game.Entity(1);
477
481
  }
478
482
 
483
+ // src/game/mocks.ts
484
+ function createMockGameState(overrides) {
485
+ return {
486
+ levelName: "test_level",
487
+ time: 0,
488
+ entities: [],
489
+ clients: [],
490
+ ...overrides
491
+ };
492
+ }
493
+
494
+ // src/server/mocks/transport.ts
495
+ var import_vitest3 = require("vitest");
496
+ var MockTransport = class {
497
+ constructor() {
498
+ this.address = "127.0.0.1";
499
+ this.port = 27910;
500
+ this.sentMessages = [];
501
+ this.receivedMessages = [];
502
+ this.listening = false;
503
+ this.listenSpy = import_vitest3.vi.fn().mockImplementation(async (port) => {
504
+ this.port = port;
505
+ this.listening = true;
506
+ });
507
+ this.closeSpy = import_vitest3.vi.fn().mockImplementation(() => {
508
+ this.listening = false;
509
+ });
510
+ }
511
+ /**
512
+ * Start listening on the specified port.
513
+ */
514
+ async listen(port) {
515
+ return this.listenSpy(port);
516
+ }
517
+ /**
518
+ * Close the transport.
519
+ */
520
+ close() {
521
+ this.closeSpy();
522
+ }
523
+ /**
524
+ * Register a callback for new connections.
525
+ */
526
+ onConnection(callback) {
527
+ this.onConnectionCallback = callback;
528
+ }
529
+ /**
530
+ * Register a callback for errors.
531
+ */
532
+ onError(callback) {
533
+ this.onErrorCallback = callback;
534
+ }
535
+ /**
536
+ * Check if the transport is currently listening.
537
+ */
538
+ isListening() {
539
+ return this.listening;
540
+ }
541
+ /**
542
+ * Helper to simulate a new connection.
543
+ * @param driver The network driver for the connection.
544
+ * @param info Optional connection info.
545
+ */
546
+ simulateConnection(driver, info) {
547
+ if (this.onConnectionCallback) {
548
+ this.onConnectionCallback(driver, info);
549
+ }
550
+ }
551
+ /**
552
+ * Helper to simulate an error.
553
+ * @param error The error to simulate.
554
+ */
555
+ simulateError(error) {
556
+ if (this.onErrorCallback) {
557
+ this.onErrorCallback(error);
558
+ }
559
+ }
560
+ };
561
+ function createMockUDPSocket(overrides) {
562
+ const socket = {
563
+ send: import_vitest3.vi.fn(),
564
+ on: import_vitest3.vi.fn(),
565
+ close: import_vitest3.vi.fn(),
566
+ bind: import_vitest3.vi.fn(),
567
+ address: import_vitest3.vi.fn().mockReturnValue({ address: "127.0.0.1", family: "IPv4", port: 0 }),
568
+ ...overrides
569
+ };
570
+ return socket;
571
+ }
572
+ function createMockNetworkAddress(ip = "127.0.0.1", port = 27910) {
573
+ return { ip, port };
574
+ }
575
+ function createMockTransport(address = "127.0.0.1", port = 27910, overrides) {
576
+ const transport = new MockTransport();
577
+ transport.address = address;
578
+ transport.port = port;
579
+ Object.assign(transport, overrides);
580
+ return transport;
581
+ }
582
+
583
+ // src/server/mocks/state.ts
584
+ var import_server = require("@quake2ts/server");
585
+ var import_shared4 = require("@quake2ts/shared");
586
+ var import_vitest4 = require("vitest");
587
+ function createMockServerState(overrides) {
588
+ return {
589
+ state: import_server.ServerState.Game,
590
+ attractLoop: false,
591
+ loadGame: false,
592
+ startTime: Date.now(),
593
+ time: 0,
594
+ frame: 0,
595
+ name: "test_map",
596
+ collisionModel: null,
597
+ configStrings: new Array(import_shared4.MAX_CONFIGSTRINGS).fill(""),
598
+ baselines: new Array(import_shared4.MAX_EDICTS).fill(null),
599
+ multicastBuf: new Uint8Array(0),
600
+ ...overrides
601
+ };
602
+ }
603
+ function createMockServerStatic(maxClients = 16, overrides) {
604
+ return {
605
+ initialized: true,
606
+ realTime: Date.now(),
607
+ mapCmd: "",
608
+ spawnCount: 1,
609
+ clients: new Array(maxClients).fill(null),
610
+ lastHeartbeat: 0,
611
+ challenges: [],
612
+ ...overrides
613
+ };
614
+ }
615
+ function createMockServerClient(clientNum, overrides) {
616
+ const mockNet = {
617
+ connect: import_vitest4.vi.fn(),
618
+ disconnect: import_vitest4.vi.fn(),
619
+ send: import_vitest4.vi.fn(),
620
+ onMessage: import_vitest4.vi.fn(),
621
+ onClose: import_vitest4.vi.fn(),
622
+ onError: import_vitest4.vi.fn(),
623
+ isConnected: import_vitest4.vi.fn().mockReturnValue(true)
624
+ };
625
+ return {
626
+ index: clientNum,
627
+ state: import_server.ClientState.Connected,
628
+ edict: { index: clientNum + 1 },
629
+ net: mockNet,
630
+ netchan: {
631
+ qport: 0,
632
+ remoteAddress: "127.0.0.1",
633
+ incomingSequence: 0,
634
+ outgoingSequence: 0,
635
+ lastReceived: 0,
636
+ process: import_vitest4.vi.fn(),
637
+ transmit: import_vitest4.vi.fn(),
638
+ writeReliableByte: import_vitest4.vi.fn(),
639
+ writeReliableShort: import_vitest4.vi.fn(),
640
+ writeReliableLong: import_vitest4.vi.fn(),
641
+ writeReliableString: import_vitest4.vi.fn(),
642
+ writeReliableData: import_vitest4.vi.fn()
643
+ },
644
+ // Cast as any because NetChan might be complex to fully mock here
645
+ userInfo: "",
646
+ lastMessage: 0,
647
+ lastCommandTime: 0,
648
+ commandCount: 0,
649
+ messageQueue: [],
650
+ frames: [],
651
+ lastFrame: 0,
652
+ lastPacketEntities: [],
653
+ challenge: 0,
654
+ lastConnect: 0,
655
+ ping: 0,
656
+ rate: 0,
657
+ name: `Client${clientNum}`,
658
+ messageLevel: 0,
659
+ datagram: new Uint8Array(0),
660
+ downloadSize: 0,
661
+ downloadCount: 0,
662
+ commandMsec: 0,
663
+ frameLatency: [],
664
+ messageSize: [],
665
+ suppressCount: 0,
666
+ commandQueue: [],
667
+ lastCmd: {
668
+ msec: 0,
669
+ buttons: 0,
670
+ angles: { x: 0, y: 0, z: 0 },
671
+ forwardmove: 0,
672
+ sidemove: 0,
673
+ upmove: 0,
674
+ sequence: 0,
675
+ lightlevel: 0,
676
+ impulse: 0,
677
+ serverFrame: 0
678
+ },
679
+ ...overrides
680
+ };
681
+ }
682
+ function createMockServer(overrides) {
683
+ return {
684
+ start: import_vitest4.vi.fn().mockResolvedValue(void 0),
685
+ stop: import_vitest4.vi.fn(),
686
+ multicast: import_vitest4.vi.fn(),
687
+ unicast: import_vitest4.vi.fn(),
688
+ configstring: import_vitest4.vi.fn(),
689
+ kickPlayer: import_vitest4.vi.fn(),
690
+ changeMap: import_vitest4.vi.fn().mockResolvedValue(void 0),
691
+ ...overrides
692
+ };
693
+ }
694
+
479
695
  // src/setup/browser.ts
480
696
  var import_jsdom = require("jsdom");
481
697
  var import_canvas = require("@napi-rs/canvas");
@@ -871,12 +1087,6 @@ function teardownBrowserEnvironment() {
871
1087
  delete global.UIEvent;
872
1088
  }
873
1089
 
874
- // src/setup/node.ts
875
- function setupNodeEnvironment() {
876
- if (typeof global.fetch === "undefined") {
877
- }
878
- }
879
-
880
1090
  // src/setup/canvas.ts
881
1091
  var import_canvas2 = require("@napi-rs/canvas");
882
1092
  function createMockCanvas(width = 300, height = 150) {
@@ -958,111 +1168,9 @@ function createMockImage(width, height, src) {
958
1168
  return img;
959
1169
  }
960
1170
 
961
- // src/setup/timing.ts
962
- function createMockRAF() {
963
- let callbacks = [];
964
- let nextId = 1;
965
- let currentTime = 0;
966
- const originalRAF = global.requestAnimationFrame;
967
- const originalCancelRAF = global.cancelAnimationFrame;
968
- const raf = (callback) => {
969
- const id = nextId++;
970
- callbacks.push({ id, callback });
971
- return id;
972
- };
973
- const cancel = (id) => {
974
- callbacks = callbacks.filter((cb) => cb.id !== id);
975
- };
976
- const mock = {
977
- tick(timestamp) {
978
- if (typeof timestamp !== "number") {
979
- currentTime += 16.6;
980
- } else {
981
- currentTime = timestamp;
982
- }
983
- const currentCallbacks = [...callbacks];
984
- callbacks = [];
985
- currentCallbacks.forEach(({ callback }) => {
986
- callback(currentTime);
987
- });
988
- },
989
- advance(deltaMs = 16.6) {
990
- this.tick(currentTime + deltaMs);
991
- },
992
- getCallbacks() {
993
- return callbacks.map((c) => c.callback);
994
- },
995
- reset() {
996
- callbacks = [];
997
- nextId = 1;
998
- currentTime = 0;
999
- },
1000
- enable() {
1001
- global.requestAnimationFrame = raf;
1002
- global.cancelAnimationFrame = cancel;
1003
- },
1004
- disable() {
1005
- if (originalRAF) {
1006
- global.requestAnimationFrame = originalRAF;
1007
- } else {
1008
- delete global.requestAnimationFrame;
1009
- }
1010
- if (originalCancelRAF) {
1011
- global.cancelAnimationFrame = originalCancelRAF;
1012
- } else {
1013
- delete global.cancelAnimationFrame;
1014
- }
1015
- }
1016
- };
1017
- return mock;
1018
- }
1019
- function createMockPerformance(startTime = 0) {
1020
- let currentTime = startTime;
1021
- const mockPerf = {
1022
- now: () => currentTime,
1023
- timeOrigin: startTime,
1024
- timing: {
1025
- navigationStart: startTime
1026
- },
1027
- // Add minimal navigation/resource timing interfaces to satisfy types if needed
1028
- clearMarks: () => {
1029
- },
1030
- clearMeasures: () => {
1031
- },
1032
- clearResourceTimings: () => {
1033
- },
1034
- getEntries: () => [],
1035
- getEntriesByName: () => [],
1036
- getEntriesByType: () => [],
1037
- mark: () => {
1038
- },
1039
- measure: () => {
1040
- },
1041
- setResourceTimingBufferSize: () => {
1042
- },
1043
- toJSON: () => ({}),
1044
- addEventListener: () => {
1045
- },
1046
- removeEventListener: () => {
1047
- },
1048
- dispatchEvent: () => true
1049
- };
1050
- mockPerf.advance = (deltaMs) => {
1051
- currentTime += deltaMs;
1052
- };
1053
- mockPerf.setTime = (time) => {
1054
- currentTime = time;
1055
- };
1056
- return mockPerf;
1057
- }
1058
- function simulateFrames(count, frameTimeMs = 16.6, callback) {
1059
- const raf = global.requestAnimationFrame;
1060
- if (!raf) return;
1061
- }
1062
- function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
1063
- for (let i = 0; i < count; i++) {
1064
- if (callback) callback(i);
1065
- mock.advance(frameTimeMs);
1171
+ // src/setup/node.ts
1172
+ function setupNodeEnvironment() {
1173
+ if (typeof global.fetch === "undefined") {
1066
1174
  }
1067
1175
  }
1068
1176
 
@@ -1179,205 +1287,306 @@ function captureAudioEvents(context) {
1179
1287
  return [];
1180
1288
  }
1181
1289
 
1290
+ // src/setup/timing.ts
1291
+ var activeMockRAF;
1292
+ function createMockRAF() {
1293
+ let callbacks = [];
1294
+ let nextId = 1;
1295
+ let currentTime = 0;
1296
+ const originalRAF = global.requestAnimationFrame;
1297
+ const originalCancelRAF = global.cancelAnimationFrame;
1298
+ const raf = (callback) => {
1299
+ const id = nextId++;
1300
+ callbacks.push({ id, callback });
1301
+ return id;
1302
+ };
1303
+ const cancel = (id) => {
1304
+ callbacks = callbacks.filter((cb) => cb.id !== id);
1305
+ };
1306
+ const mock = {
1307
+ tick(timestamp) {
1308
+ if (typeof timestamp !== "number") {
1309
+ currentTime += 16.6;
1310
+ } else {
1311
+ currentTime = timestamp;
1312
+ }
1313
+ const currentCallbacks = [...callbacks];
1314
+ callbacks = [];
1315
+ currentCallbacks.forEach(({ callback }) => {
1316
+ callback(currentTime);
1317
+ });
1318
+ },
1319
+ advance(deltaMs = 16.6) {
1320
+ this.tick(currentTime + deltaMs);
1321
+ },
1322
+ getCallbacks() {
1323
+ return callbacks.map((c) => c.callback);
1324
+ },
1325
+ reset() {
1326
+ callbacks = [];
1327
+ nextId = 1;
1328
+ currentTime = 0;
1329
+ },
1330
+ enable() {
1331
+ activeMockRAF = this;
1332
+ global.requestAnimationFrame = raf;
1333
+ global.cancelAnimationFrame = cancel;
1334
+ },
1335
+ disable() {
1336
+ if (activeMockRAF === this) {
1337
+ activeMockRAF = void 0;
1338
+ }
1339
+ if (originalRAF) {
1340
+ global.requestAnimationFrame = originalRAF;
1341
+ } else {
1342
+ delete global.requestAnimationFrame;
1343
+ }
1344
+ if (originalCancelRAF) {
1345
+ global.cancelAnimationFrame = originalCancelRAF;
1346
+ } else {
1347
+ delete global.cancelAnimationFrame;
1348
+ }
1349
+ }
1350
+ };
1351
+ return mock;
1352
+ }
1353
+ function createMockPerformance(startTime = 0) {
1354
+ let currentTime = startTime;
1355
+ const mockPerf = {
1356
+ now: () => currentTime,
1357
+ timeOrigin: startTime,
1358
+ timing: {
1359
+ navigationStart: startTime
1360
+ },
1361
+ clearMarks: () => {
1362
+ },
1363
+ clearMeasures: () => {
1364
+ },
1365
+ clearResourceTimings: () => {
1366
+ },
1367
+ getEntries: () => [],
1368
+ getEntriesByName: () => [],
1369
+ getEntriesByType: () => [],
1370
+ mark: () => {
1371
+ },
1372
+ measure: () => {
1373
+ },
1374
+ setResourceTimingBufferSize: () => {
1375
+ },
1376
+ toJSON: () => ({}),
1377
+ addEventListener: () => {
1378
+ },
1379
+ removeEventListener: () => {
1380
+ },
1381
+ dispatchEvent: () => true
1382
+ };
1383
+ mockPerf.advance = (deltaMs) => {
1384
+ currentTime += deltaMs;
1385
+ };
1386
+ mockPerf.setTime = (time) => {
1387
+ currentTime = time;
1388
+ };
1389
+ return mockPerf;
1390
+ }
1391
+ function createControlledTimer() {
1392
+ let currentTime = 0;
1393
+ let timers = [];
1394
+ let nextId = 1;
1395
+ const originalSetTimeout = global.setTimeout;
1396
+ const originalClearTimeout = global.clearTimeout;
1397
+ const originalSetInterval = global.setInterval;
1398
+ const originalClearInterval = global.clearInterval;
1399
+ const mockSetTimeout = (callback, delay = 0, ...args) => {
1400
+ const id = nextId++;
1401
+ timers.push({ id, callback, dueTime: currentTime + delay, args });
1402
+ return id;
1403
+ };
1404
+ const mockClearTimeout = (id) => {
1405
+ timers = timers.filter((t) => t.id !== id);
1406
+ };
1407
+ const mockSetInterval = (callback, delay = 0, ...args) => {
1408
+ const id = nextId++;
1409
+ timers.push({ id, callback, dueTime: currentTime + delay, interval: delay, args });
1410
+ return id;
1411
+ };
1412
+ const mockClearInterval = (id) => {
1413
+ timers = timers.filter((t) => t.id !== id);
1414
+ };
1415
+ global.setTimeout = mockSetTimeout;
1416
+ global.clearTimeout = mockClearTimeout;
1417
+ global.setInterval = mockSetInterval;
1418
+ global.clearInterval = mockClearInterval;
1419
+ return {
1420
+ tick() {
1421
+ this.advanceBy(0);
1422
+ },
1423
+ advanceBy(ms) {
1424
+ const targetTime = currentTime + ms;
1425
+ while (true) {
1426
+ let earliest = null;
1427
+ for (const t of timers) {
1428
+ if (!earliest || t.dueTime < earliest.dueTime) {
1429
+ earliest = t;
1430
+ }
1431
+ }
1432
+ if (!earliest || earliest.dueTime > targetTime) {
1433
+ break;
1434
+ }
1435
+ currentTime = earliest.dueTime;
1436
+ const { callback, args, interval, id } = earliest;
1437
+ if (interval !== void 0) {
1438
+ earliest.dueTime += interval;
1439
+ if (interval === 0) earliest.dueTime += 1;
1440
+ } else {
1441
+ timers = timers.filter((t) => t.id !== id);
1442
+ }
1443
+ callback(...args);
1444
+ }
1445
+ currentTime = targetTime;
1446
+ },
1447
+ clear() {
1448
+ timers = [];
1449
+ },
1450
+ restore() {
1451
+ global.setTimeout = originalSetTimeout;
1452
+ global.clearTimeout = originalClearTimeout;
1453
+ global.setInterval = originalSetInterval;
1454
+ global.clearInterval = originalClearInterval;
1455
+ }
1456
+ };
1457
+ }
1458
+ function simulateFrames(count, frameTimeMs = 16.6, callback) {
1459
+ if (!activeMockRAF) {
1460
+ throw new Error("simulateFrames requires an active MockRAF. Ensure createMockRAF().enable() is called.");
1461
+ }
1462
+ for (let i = 0; i < count; i++) {
1463
+ if (callback) callback(i);
1464
+ activeMockRAF.advance(frameTimeMs);
1465
+ }
1466
+ }
1467
+ function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
1468
+ for (let i = 0; i < count; i++) {
1469
+ if (callback) callback(i);
1470
+ mock.advance(frameTimeMs);
1471
+ }
1472
+ }
1473
+
1182
1474
  // src/e2e/playwright.ts
1183
1475
  var import_playwright = require("playwright");
1476
+ var import_http = require("http");
1477
+ var import_serve_handler = __toESM(require("serve-handler"), 1);
1184
1478
  async function createPlaywrightTestClient(options = {}) {
1479
+ let staticServer;
1480
+ let clientUrl = options.clientUrl;
1481
+ const rootPath = options.rootPath || process.cwd();
1482
+ if (!clientUrl) {
1483
+ staticServer = (0, import_http.createServer)((request, response) => {
1484
+ return (0, import_serve_handler.default)(request, response, {
1485
+ public: rootPath,
1486
+ cleanUrls: false,
1487
+ headers: [
1488
+ {
1489
+ source: "**/*",
1490
+ headers: [
1491
+ { key: "Cache-Control", value: "no-cache" },
1492
+ { key: "Access-Control-Allow-Origin", value: "*" },
1493
+ { key: "Cross-Origin-Opener-Policy", value: "same-origin" },
1494
+ { key: "Cross-Origin-Embedder-Policy", value: "require-corp" }
1495
+ ]
1496
+ }
1497
+ ]
1498
+ });
1499
+ });
1500
+ await new Promise((resolve) => {
1501
+ if (!staticServer) return;
1502
+ staticServer.listen(0, () => {
1503
+ const addr = staticServer?.address();
1504
+ const port = typeof addr === "object" ? addr?.port : 0;
1505
+ clientUrl = `http://localhost:${port}`;
1506
+ console.log(`Test client serving from ${rootPath} at ${clientUrl}`);
1507
+ resolve();
1508
+ });
1509
+ });
1510
+ }
1185
1511
  const browser = await import_playwright.chromium.launch({
1186
1512
  headless: options.headless ?? true,
1187
- args: options.args || [
1513
+ args: [
1188
1514
  "--use-gl=egl",
1189
1515
  "--ignore-gpu-blocklist",
1190
- "--no-sandbox",
1191
- "--disable-setuid-sandbox"
1192
- ]
1516
+ ...options.launchOptions?.args || []
1517
+ ],
1518
+ ...options.launchOptions
1193
1519
  });
1520
+ const width = options.width || 1280;
1521
+ const height = options.height || 720;
1194
1522
  const context = await browser.newContext({
1195
- viewport: options.viewport || { width: 1280, height: 720 },
1196
- recordVideo: options.recordVideo,
1197
- deviceScaleFactor: 1
1523
+ viewport: { width, height },
1524
+ deviceScaleFactor: 1,
1525
+ ...options.contextOptions
1198
1526
  });
1199
1527
  const page = await context.newPage();
1200
- const waitForGame = async (timeout = 1e4) => {
1201
- try {
1202
- await page.waitForFunction(() => {
1203
- return window.game || document.querySelector("canvas");
1204
- }, null, { timeout });
1205
- } catch (e) {
1206
- throw new Error(`Game did not initialize within ${timeout}ms`);
1528
+ const close = async () => {
1529
+ await browser.close();
1530
+ if (staticServer) {
1531
+ staticServer.close();
1207
1532
  }
1208
1533
  };
1209
- const client = {
1534
+ const navigate = async (url) => {
1535
+ const targetUrl = url || clientUrl;
1536
+ if (!targetUrl) throw new Error("No URL to navigate to");
1537
+ let finalUrl = targetUrl;
1538
+ if (options.serverUrl && !targetUrl.includes("connect=")) {
1539
+ const separator = targetUrl.includes("?") ? "&" : "?";
1540
+ finalUrl = `${targetUrl}${separator}connect=${encodeURIComponent(options.serverUrl)}`;
1541
+ }
1542
+ console.log(`Navigating to: ${finalUrl}`);
1543
+ await page.goto(finalUrl, { waitUntil: "domcontentloaded" });
1544
+ };
1545
+ return {
1210
1546
  browser,
1211
1547
  context,
1212
1548
  page,
1213
- async navigate(url) {
1214
- await page.goto(url, { waitUntil: "domcontentloaded" });
1215
- },
1216
- async waitForGame(timeout) {
1217
- await waitForGame(timeout);
1218
- },
1219
- async injectInput(type, key) {
1220
- if (type === "keydown") {
1221
- await page.keyboard.down(key);
1222
- } else {
1223
- await page.keyboard.up(key);
1224
- }
1225
- },
1226
- async injectMouse(type, x = 0, y = 0, button = 0) {
1227
- if (type === "move") {
1228
- await page.mouse.move(x, y);
1229
- } else if (type === "down") {
1230
- await page.mouse.down({ button: button === 0 ? "left" : button === 2 ? "right" : "middle" });
1231
- } else if (type === "up") {
1232
- await page.mouse.up({ button: button === 0 ? "left" : button === 2 ? "right" : "middle" });
1233
- }
1234
- },
1235
- async screenshot(path2) {
1236
- await page.screenshot({ path: path2 });
1237
- },
1238
- async close() {
1239
- await browser.close();
1549
+ server: staticServer,
1550
+ close,
1551
+ navigate,
1552
+ waitForGame: async (timeout = 1e4) => {
1553
+ await waitForGameReady(page, timeout);
1554
+ },
1555
+ injectInput: async (type, data) => {
1556
+ await page.evaluate(({ type: type2, data: data2 }) => {
1557
+ if (window.injectGameInput) window.injectGameInput(type2, data2);
1558
+ }, { type, data });
1240
1559
  }
1241
1560
  };
1242
- return client;
1243
1561
  }
1244
1562
  async function waitForGameReady(page, timeout = 1e4) {
1245
- await page.waitForFunction(() => {
1246
- return window.game || document.querySelector("canvas");
1247
- }, null, { timeout });
1563
+ try {
1564
+ await page.waitForFunction(() => {
1565
+ return window.gameInstance && window.gameInstance.isReady;
1566
+ }, null, { timeout });
1567
+ } catch (e) {
1568
+ await page.waitForSelector("canvas", { timeout });
1569
+ }
1248
1570
  }
1249
1571
  async function captureGameState(page) {
1250
1572
  return await page.evaluate(() => {
1251
- const game = window.game;
1252
- if (!game) return {};
1253
- return {};
1254
- });
1255
- }
1256
-
1257
- // src/e2e/network.ts
1258
- var CONDITIONS = {
1259
- "good": {
1260
- offline: false,
1261
- downloadThroughput: 10 * 1024 * 1024,
1262
- // 10 Mbps
1263
- uploadThroughput: 5 * 1024 * 1024,
1264
- // 5 Mbps
1265
- latency: 20
1266
- },
1267
- "slow": {
1268
- offline: false,
1269
- downloadThroughput: 500 * 1024,
1270
- // 500 Kbps
1271
- uploadThroughput: 500 * 1024,
1272
- latency: 400
1273
- },
1274
- "unstable": {
1275
- offline: false,
1276
- downloadThroughput: 1 * 1024 * 1024,
1277
- uploadThroughput: 1 * 1024 * 1024,
1278
- latency: 100
1279
- // Jitter needs logic not simple preset
1280
- },
1281
- "offline": {
1282
- offline: true,
1283
- downloadThroughput: 0,
1284
- uploadThroughput: 0,
1285
- latency: 0
1286
- }
1287
- };
1288
- function simulateNetworkCondition(condition) {
1289
- const config = CONDITIONS[condition];
1290
- return createCustomNetworkCondition(config.latency, 0, 0, config);
1291
- }
1292
- function createCustomNetworkCondition(latency, jitter = 0, packetLoss = 0, baseConfig) {
1293
- return {
1294
- async apply(page) {
1295
- const client = await page.context().newCDPSession(page);
1296
- await client.send("Network.enable");
1297
- await client.send("Network.emulateNetworkConditions", {
1298
- offline: baseConfig?.offline || false,
1299
- latency: latency + Math.random() * jitter,
1300
- // Basic jitter application on setup (static)
1301
- downloadThroughput: baseConfig?.downloadThroughput || -1,
1302
- uploadThroughput: baseConfig?.uploadThroughput || -1
1303
- });
1304
- },
1305
- async clear(page) {
1306
- const client = await page.context().newCDPSession(page);
1307
- await client.send("Network.emulateNetworkConditions", {
1308
- offline: false,
1309
- latency: 0,
1310
- downloadThroughput: -1,
1311
- uploadThroughput: -1
1312
- });
1573
+ if (window.gameInstance && window.gameInstance.getState) {
1574
+ return window.gameInstance.getState();
1313
1575
  }
1314
- };
1315
- }
1316
- async function throttleBandwidth(page, bytesPerSecond) {
1317
- const simulator = createCustomNetworkCondition(0, 0, 0, {
1318
- offline: false,
1319
- latency: 0,
1320
- downloadThroughput: bytesPerSecond,
1321
- uploadThroughput: bytesPerSecond
1322
- });
1323
- await simulator.apply(page);
1324
- }
1325
-
1326
- // src/e2e/visual.ts
1327
- var import_path = __toESM(require("path"), 1);
1328
- var import_promises = __toESM(require("fs/promises"), 1);
1329
- async function captureGameScreenshot(page, name, options = {}) {
1330
- const dir = options.dir || "__screenshots__";
1331
- const screenshotPath = import_path.default.join(dir, `${name}.png`);
1332
- await import_promises.default.mkdir(dir, { recursive: true });
1333
- return await page.screenshot({
1334
- path: screenshotPath,
1335
- fullPage: options.fullPage ?? false,
1336
- animations: "disabled",
1337
- // Try to freeze animations if possible
1338
- caret: "hide"
1576
+ return {};
1339
1577
  });
1340
1578
  }
1341
- async function compareScreenshots(baseline, current, threshold = 0.1) {
1342
- if (baseline.equals(current)) {
1343
- return { pixelDiff: 0, matched: true };
1344
- }
1345
- return {
1346
- pixelDiff: -1,
1347
- // Unknown magnitude
1348
- matched: false
1349
- };
1350
- }
1351
- function createVisualTestScenario(page, sceneName) {
1352
- return {
1353
- async capture(snapshotName) {
1354
- return await captureGameScreenshot(page, `${sceneName}-${snapshotName}`);
1355
- },
1356
- async compare(snapshotName, baselineDir) {
1357
- const name = `${sceneName}-${snapshotName}`;
1358
- const current = await captureGameScreenshot(page, name, { dir: "__screenshots__/current" });
1359
- try {
1360
- const baselinePath = import_path.default.join(baselineDir, `${name}.png`);
1361
- const baseline = await import_promises.default.readFile(baselinePath);
1362
- return await compareScreenshots(baseline, current);
1363
- } catch (e) {
1364
- return { pixelDiff: -1, matched: false };
1365
- }
1366
- }
1367
- };
1368
- }
1369
1579
  // Annotate the CommonJS export names for ESM import in node:
1370
1580
  0 && (module.exports = {
1371
1581
  InputInjector,
1372
1582
  MockPointerLock,
1583
+ MockTransport,
1373
1584
  captureAudioEvents,
1374
1585
  captureCanvasDrawCalls,
1375
- captureGameScreenshot,
1376
1586
  captureGameState,
1377
- compareScreenshots,
1378
1587
  createBinaryStreamMock,
1379
1588
  createBinaryWriterMock,
1380
- createCustomNetworkCondition,
1589
+ createControlledTimer,
1381
1590
  createEntity,
1382
1591
  createEntityStateFactory,
1383
1592
  createGameStateSnapshotFactory,
@@ -1386,13 +1595,21 @@ function createVisualTestScenario(page, sceneName) {
1386
1595
  createMockCanvasContext2D,
1387
1596
  createMockEngine,
1388
1597
  createMockGame,
1598
+ createMockGameState,
1389
1599
  createMockImage,
1390
1600
  createMockImageData,
1391
1601
  createMockIndexedDB,
1392
1602
  createMockLocalStorage,
1603
+ createMockNetworkAddress,
1393
1604
  createMockPerformance,
1394
1605
  createMockRAF,
1606
+ createMockServer,
1607
+ createMockServerClient,
1608
+ createMockServerState,
1609
+ createMockServerStatic,
1395
1610
  createMockSessionStorage,
1611
+ createMockTransport,
1612
+ createMockUDPSocket,
1396
1613
  createMockWebGL2Context,
1397
1614
  createNetChanMock,
1398
1615
  createPlayerStateFactory,
@@ -1400,7 +1617,6 @@ function createVisualTestScenario(page, sceneName) {
1400
1617
  createSpawnContext,
1401
1618
  createStorageTestScenario,
1402
1619
  createTestContext,
1403
- createVisualTestScenario,
1404
1620
  intersects,
1405
1621
  ladderTrace,
1406
1622
  makeAxisBrush,
@@ -1415,11 +1631,9 @@ function createVisualTestScenario(page, sceneName) {
1415
1631
  setupNodeEnvironment,
1416
1632
  simulateFrames,
1417
1633
  simulateFramesWithMock,
1418
- simulateNetworkCondition,
1419
1634
  stairTrace,
1420
1635
  teardownBrowserEnvironment,
1421
1636
  teardownMockAudioContext,
1422
- throttleBandwidth,
1423
1637
  waitForGameReady
1424
1638
  });
1425
1639
  //# sourceMappingURL=index.cjs.map