quake2ts 0.0.563 → 0.0.565

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.
@@ -1,9 +1,9 @@
1
1
  import * as vitest from 'vitest';
2
2
  import { Mock } from 'vitest';
3
- import { Vec3, CollisionPlane, CollisionBrush, CollisionNode, CollisionLeaf, CollisionModel, PlayerState, EntityState, createRandomGenerator, NetDriver } from '@quake2ts/shared';
3
+ import { Vec3, CollisionPlane, CollisionBrush, CollisionNode, CollisionLeaf, CollisionModel, PlayerState, EntityState, createRandomGenerator, NetDriver, UserCommand } from '@quake2ts/shared';
4
4
  export { intersects, ladderTrace, stairTrace } from '@quake2ts/shared';
5
5
  import { GameStateSnapshot, Entity, ScriptHookRegistry, SpawnContext, EntitySystem, SpawnRegistry } from '@quake2ts/game';
6
- import { NetworkTransport, Server, ServerStatic, Client } from '@quake2ts/server';
6
+ import { NetworkTransport, Server, ServerStatic, Client, ClientState } from '@quake2ts/server';
7
7
  import { ImageData } from '@napi-rs/canvas';
8
8
  import { LaunchOptions, BrowserContextOptions, Browser, BrowserContext, Page } from 'playwright';
9
9
  import { Server as Server$1 } from 'http';
@@ -267,6 +267,155 @@ interface MockServer {
267
267
  */
268
268
  declare function createMockServer(overrides?: Partial<MockServer>): MockServer;
269
269
 
270
+ /**
271
+ * Interface representing connection state for testing.
272
+ */
273
+ interface Connection {
274
+ state: ClientState;
275
+ address: string;
276
+ challenge: number;
277
+ userInfo: UserInfo;
278
+ }
279
+ /**
280
+ * Stages of the client connection handshake.
281
+ */
282
+ declare enum HandshakeStage {
283
+ None = 0,
284
+ Challenge = 1,
285
+ Connect = 2,
286
+ Info = 3,
287
+ Active = 4
288
+ }
289
+ /**
290
+ * Interface representing a handshake state.
291
+ */
292
+ interface Handshake {
293
+ stage: HandshakeStage;
294
+ clientNum: number;
295
+ challenge: number;
296
+ qport: number;
297
+ }
298
+ /**
299
+ * Interface for UserInfo structure.
300
+ */
301
+ interface UserInfo {
302
+ name: string;
303
+ skin: string;
304
+ model: string;
305
+ fov: number;
306
+ hand: number;
307
+ rate: number;
308
+ msg: number;
309
+ spectator?: number;
310
+ [key: string]: string | number | undefined;
311
+ }
312
+ /**
313
+ * Helper to serialize UserInfo to Quake 2 info string format.
314
+ * Format: \key\value\key2\value2
315
+ */
316
+ declare function serializeUserInfo(info: UserInfo): string;
317
+ /**
318
+ * Creates a mock UserInfo object.
319
+ * @param overrides Optional overrides for the user info.
320
+ */
321
+ declare function createMockUserInfo(overrides?: Partial<UserInfo>): UserInfo;
322
+ /**
323
+ * Creates a mock connection object (Client) with specific state.
324
+ * @param state The client state (default: Connected).
325
+ * @param overrides Optional overrides for the client.
326
+ */
327
+ declare function createMockConnection(state?: ClientState, overrides?: Partial<Client>): Client;
328
+ /**
329
+ * Creates a mock handshake object.
330
+ * @param stage The stage of the handshake (default: None).
331
+ */
332
+ declare function createMockHandshake(stage?: HandshakeStage): Handshake;
333
+ /**
334
+ * Simulates a handshake between a mock client and server.
335
+ * Note: This is a high-level simulation helper.
336
+ * @param client The mock client connection.
337
+ * @param server The mock server instance.
338
+ * @returns Promise that resolves to true if handshake succeeded.
339
+ */
340
+ declare function simulateHandshake(client: Client, server: any): Promise<boolean>;
341
+
342
+ type MockServerContext = Server & {
343
+ clients: (Client | null)[];
344
+ entities?: Entity[];
345
+ };
346
+ interface MultiplayerScenario {
347
+ server: MockServerContext;
348
+ clients: Client[];
349
+ entities: Entity[];
350
+ }
351
+ /**
352
+ * Creates a multiplayer test scenario with a mock server and a number of clients.
353
+ * @param numPlayers Number of players to simulate.
354
+ */
355
+ declare function createMultiplayerTestScenario(numPlayers?: number): MultiplayerScenario;
356
+ /**
357
+ * Simulates a player joining the server.
358
+ * @param server The mock server instance.
359
+ * @param userInfo Optional user info overrides.
360
+ */
361
+ declare function simulatePlayerJoin(server: MockServerContext, userInfo?: Partial<UserInfo>): Promise<Client>;
362
+ /**
363
+ * Simulates a player leaving the server.
364
+ * @param server The mock server instance.
365
+ * @param clientNum The client number to disconnect.
366
+ */
367
+ declare function simulatePlayerLeave(server: MockServerContext, clientNum: number): void;
368
+ /**
369
+ * Simulates a single server frame update.
370
+ * @param server The mock server instance.
371
+ * @param deltaTime Time step in seconds (default: 0.1).
372
+ */
373
+ declare function simulateServerTick(server: MockServerContext, deltaTime?: number): void;
374
+ /**
375
+ * Simulates player input for a specific client.
376
+ * @param client The server client.
377
+ * @param input The input command.
378
+ */
379
+ declare function simulatePlayerInput(client: Client, input: Partial<UserCommand>): void;
380
+
381
+ interface Snapshot {
382
+ serverTime: number;
383
+ playerState: any;
384
+ entities: EntityState[];
385
+ }
386
+ interface DeltaSnapshot {
387
+ snapshot: Snapshot;
388
+ deltaEntities: EntityState[];
389
+ removedEntities: number[];
390
+ }
391
+ interface ConsistencyReport {
392
+ valid: boolean;
393
+ errors: string[];
394
+ }
395
+ /**
396
+ * Creates a client-specific snapshot from the server state.
397
+ * @param serverState The current server state.
398
+ * @param clientNum The client number to generate snapshot for.
399
+ */
400
+ declare function createServerSnapshot(serverState: Server, clientNum: number): Snapshot;
401
+ /**
402
+ * Calculates the delta between two snapshots.
403
+ * @param oldSnapshot The baseline snapshot.
404
+ * @param newSnapshot The current snapshot.
405
+ */
406
+ declare function createDeltaSnapshot(oldSnapshot: Snapshot, newSnapshot: Snapshot): DeltaSnapshot;
407
+ /**
408
+ * Verifies the consistency of a sequence of snapshots.
409
+ * @param snapshots Array of snapshots ordered by time.
410
+ */
411
+ declare function verifySnapshotConsistency(snapshots: Snapshot[]): ConsistencyReport;
412
+ /**
413
+ * Simulates network delivery of a snapshot with potential packet loss.
414
+ * @param snapshot The snapshot to deliver.
415
+ * @param reliability Probability of successful delivery (0.0 to 1.0).
416
+ */
417
+ declare function simulateSnapshotDelivery(snapshot: Snapshot, reliability?: number): Promise<Snapshot | null>;
418
+
270
419
  interface BrowserSetupOptions {
271
420
  url?: string;
272
421
  pretendToBeVisual?: boolean;
@@ -509,4 +658,4 @@ interface VisualScenario {
509
658
  */
510
659
  declare function createVisualTestScenario(page: Page, sceneName: string): VisualScenario;
511
660
 
512
- export { type AudioEvent, type BinaryStreamMock, type BinaryWriterMock, type BrowserSetupOptions, type ControlledTimer, type DrawCall, type GameState, type GameStateCapture, InputInjector, type MockEngine, type MockGame, MockPointerLock, type MockRAF, type MockServer, MockTransport, type MockUDPSocket, type NetworkAddress, type NetworkCondition, type NetworkConfig, type NetworkSimulator, type NodeSetupOptions, type PlaywrightOptions, type PlaywrightTestClient, type StorageScenario, type TestContext, type VisualDiff, type VisualScenario, captureAudioEvents, captureCanvasDrawCalls, captureGameScreenshot, captureGameState, compareScreenshots, createBinaryStreamMock, createBinaryWriterMock, createControlledTimer, createCustomNetworkCondition, createEntity, createEntityStateFactory, createGameStateSnapshotFactory, createMockAudioContext, createMockCanvas, createMockCanvasContext2D, createMockEngine, createMockGame, createMockGameState, createMockImage, createMockImageData, createMockIndexedDB, createMockLocalStorage, createMockNetworkAddress, createMockPerformance, createMockRAF, createMockServer, createMockServerClient, createMockServerState, createMockServerStatic, createMockSessionStorage, createMockTransport, createMockUDPSocket, createMockWebGL2Context, createNetChanMock, createPlayerStateFactory, createPlaywrightTestClient, createSpawnContext, createStorageTestScenario, createTestContext, createVisualTestScenario, makeAxisBrush, makeBrushFromMinsMaxs, makeBspModel, makeLeaf, makeLeafModel, makeNode, makePlane, setupBrowserEnvironment, setupMockAudioContext, setupNodeEnvironment, simulateFrames, simulateFramesWithMock, simulateNetworkCondition, teardownBrowserEnvironment, teardownMockAudioContext, throttleBandwidth, waitForGameReady };
661
+ export { type AudioEvent, type BinaryStreamMock, type BinaryWriterMock, type BrowserSetupOptions, type Connection, type ConsistencyReport, type ControlledTimer, type DeltaSnapshot, type DrawCall, type GameState, type GameStateCapture, type Handshake, HandshakeStage, InputInjector, type MockEngine, type MockGame, MockPointerLock, type MockRAF, type MockServer, type MockServerContext, MockTransport, type MockUDPSocket, type MultiplayerScenario, type NetworkAddress, type NetworkCondition, type NetworkConfig, type NetworkSimulator, type NodeSetupOptions, type PlaywrightOptions, type PlaywrightTestClient, type Snapshot, type StorageScenario, type TestContext, type UserInfo, type VisualDiff, type VisualScenario, captureAudioEvents, captureCanvasDrawCalls, captureGameScreenshot, captureGameState, compareScreenshots, createBinaryStreamMock, createBinaryWriterMock, createControlledTimer, createCustomNetworkCondition, createDeltaSnapshot, createEntity, createEntityStateFactory, createGameStateSnapshotFactory, createMockAudioContext, createMockCanvas, createMockCanvasContext2D, createMockConnection, createMockEngine, createMockGame, createMockGameState, createMockHandshake, createMockImage, createMockImageData, createMockIndexedDB, createMockLocalStorage, createMockNetworkAddress, createMockPerformance, createMockRAF, createMockServer, createMockServerClient, createMockServerState, createMockServerStatic, createMockSessionStorage, createMockTransport, createMockUDPSocket, createMockUserInfo, createMockWebGL2Context, createMultiplayerTestScenario, createNetChanMock, createPlayerStateFactory, createPlaywrightTestClient, createServerSnapshot, createSpawnContext, createStorageTestScenario, createTestContext, createVisualTestScenario, makeAxisBrush, makeBrushFromMinsMaxs, makeBspModel, makeLeaf, makeLeafModel, makeNode, makePlane, serializeUserInfo, setupBrowserEnvironment, setupMockAudioContext, setupNodeEnvironment, simulateFrames, simulateFramesWithMock, simulateHandshake, simulateNetworkCondition, simulatePlayerInput, simulatePlayerJoin, simulatePlayerLeave, simulateServerTick, simulateSnapshotDelivery, teardownBrowserEnvironment, teardownMockAudioContext, throttleBandwidth, verifySnapshotConsistency, waitForGameReady };
@@ -1,9 +1,9 @@
1
1
  import * as vitest from 'vitest';
2
2
  import { Mock } from 'vitest';
3
- import { Vec3, CollisionPlane, CollisionBrush, CollisionNode, CollisionLeaf, CollisionModel, PlayerState, EntityState, createRandomGenerator, NetDriver } from '@quake2ts/shared';
3
+ import { Vec3, CollisionPlane, CollisionBrush, CollisionNode, CollisionLeaf, CollisionModel, PlayerState, EntityState, createRandomGenerator, NetDriver, UserCommand } from '@quake2ts/shared';
4
4
  export { intersects, ladderTrace, stairTrace } from '@quake2ts/shared';
5
5
  import { GameStateSnapshot, Entity, ScriptHookRegistry, SpawnContext, EntitySystem, SpawnRegistry } from '@quake2ts/game';
6
- import { NetworkTransport, Server, ServerStatic, Client } from '@quake2ts/server';
6
+ import { NetworkTransport, Server, ServerStatic, Client, ClientState } from '@quake2ts/server';
7
7
  import { ImageData } from '@napi-rs/canvas';
8
8
  import { LaunchOptions, BrowserContextOptions, Browser, BrowserContext, Page } from 'playwright';
9
9
  import { Server as Server$1 } from 'http';
@@ -267,6 +267,155 @@ interface MockServer {
267
267
  */
268
268
  declare function createMockServer(overrides?: Partial<MockServer>): MockServer;
269
269
 
270
+ /**
271
+ * Interface representing connection state for testing.
272
+ */
273
+ interface Connection {
274
+ state: ClientState;
275
+ address: string;
276
+ challenge: number;
277
+ userInfo: UserInfo;
278
+ }
279
+ /**
280
+ * Stages of the client connection handshake.
281
+ */
282
+ declare enum HandshakeStage {
283
+ None = 0,
284
+ Challenge = 1,
285
+ Connect = 2,
286
+ Info = 3,
287
+ Active = 4
288
+ }
289
+ /**
290
+ * Interface representing a handshake state.
291
+ */
292
+ interface Handshake {
293
+ stage: HandshakeStage;
294
+ clientNum: number;
295
+ challenge: number;
296
+ qport: number;
297
+ }
298
+ /**
299
+ * Interface for UserInfo structure.
300
+ */
301
+ interface UserInfo {
302
+ name: string;
303
+ skin: string;
304
+ model: string;
305
+ fov: number;
306
+ hand: number;
307
+ rate: number;
308
+ msg: number;
309
+ spectator?: number;
310
+ [key: string]: string | number | undefined;
311
+ }
312
+ /**
313
+ * Helper to serialize UserInfo to Quake 2 info string format.
314
+ * Format: \key\value\key2\value2
315
+ */
316
+ declare function serializeUserInfo(info: UserInfo): string;
317
+ /**
318
+ * Creates a mock UserInfo object.
319
+ * @param overrides Optional overrides for the user info.
320
+ */
321
+ declare function createMockUserInfo(overrides?: Partial<UserInfo>): UserInfo;
322
+ /**
323
+ * Creates a mock connection object (Client) with specific state.
324
+ * @param state The client state (default: Connected).
325
+ * @param overrides Optional overrides for the client.
326
+ */
327
+ declare function createMockConnection(state?: ClientState, overrides?: Partial<Client>): Client;
328
+ /**
329
+ * Creates a mock handshake object.
330
+ * @param stage The stage of the handshake (default: None).
331
+ */
332
+ declare function createMockHandshake(stage?: HandshakeStage): Handshake;
333
+ /**
334
+ * Simulates a handshake between a mock client and server.
335
+ * Note: This is a high-level simulation helper.
336
+ * @param client The mock client connection.
337
+ * @param server The mock server instance.
338
+ * @returns Promise that resolves to true if handshake succeeded.
339
+ */
340
+ declare function simulateHandshake(client: Client, server: any): Promise<boolean>;
341
+
342
+ type MockServerContext = Server & {
343
+ clients: (Client | null)[];
344
+ entities?: Entity[];
345
+ };
346
+ interface MultiplayerScenario {
347
+ server: MockServerContext;
348
+ clients: Client[];
349
+ entities: Entity[];
350
+ }
351
+ /**
352
+ * Creates a multiplayer test scenario with a mock server and a number of clients.
353
+ * @param numPlayers Number of players to simulate.
354
+ */
355
+ declare function createMultiplayerTestScenario(numPlayers?: number): MultiplayerScenario;
356
+ /**
357
+ * Simulates a player joining the server.
358
+ * @param server The mock server instance.
359
+ * @param userInfo Optional user info overrides.
360
+ */
361
+ declare function simulatePlayerJoin(server: MockServerContext, userInfo?: Partial<UserInfo>): Promise<Client>;
362
+ /**
363
+ * Simulates a player leaving the server.
364
+ * @param server The mock server instance.
365
+ * @param clientNum The client number to disconnect.
366
+ */
367
+ declare function simulatePlayerLeave(server: MockServerContext, clientNum: number): void;
368
+ /**
369
+ * Simulates a single server frame update.
370
+ * @param server The mock server instance.
371
+ * @param deltaTime Time step in seconds (default: 0.1).
372
+ */
373
+ declare function simulateServerTick(server: MockServerContext, deltaTime?: number): void;
374
+ /**
375
+ * Simulates player input for a specific client.
376
+ * @param client The server client.
377
+ * @param input The input command.
378
+ */
379
+ declare function simulatePlayerInput(client: Client, input: Partial<UserCommand>): void;
380
+
381
+ interface Snapshot {
382
+ serverTime: number;
383
+ playerState: any;
384
+ entities: EntityState[];
385
+ }
386
+ interface DeltaSnapshot {
387
+ snapshot: Snapshot;
388
+ deltaEntities: EntityState[];
389
+ removedEntities: number[];
390
+ }
391
+ interface ConsistencyReport {
392
+ valid: boolean;
393
+ errors: string[];
394
+ }
395
+ /**
396
+ * Creates a client-specific snapshot from the server state.
397
+ * @param serverState The current server state.
398
+ * @param clientNum The client number to generate snapshot for.
399
+ */
400
+ declare function createServerSnapshot(serverState: Server, clientNum: number): Snapshot;
401
+ /**
402
+ * Calculates the delta between two snapshots.
403
+ * @param oldSnapshot The baseline snapshot.
404
+ * @param newSnapshot The current snapshot.
405
+ */
406
+ declare function createDeltaSnapshot(oldSnapshot: Snapshot, newSnapshot: Snapshot): DeltaSnapshot;
407
+ /**
408
+ * Verifies the consistency of a sequence of snapshots.
409
+ * @param snapshots Array of snapshots ordered by time.
410
+ */
411
+ declare function verifySnapshotConsistency(snapshots: Snapshot[]): ConsistencyReport;
412
+ /**
413
+ * Simulates network delivery of a snapshot with potential packet loss.
414
+ * @param snapshot The snapshot to deliver.
415
+ * @param reliability Probability of successful delivery (0.0 to 1.0).
416
+ */
417
+ declare function simulateSnapshotDelivery(snapshot: Snapshot, reliability?: number): Promise<Snapshot | null>;
418
+
270
419
  interface BrowserSetupOptions {
271
420
  url?: string;
272
421
  pretendToBeVisual?: boolean;
@@ -509,4 +658,4 @@ interface VisualScenario {
509
658
  */
510
659
  declare function createVisualTestScenario(page: Page, sceneName: string): VisualScenario;
511
660
 
512
- export { type AudioEvent, type BinaryStreamMock, type BinaryWriterMock, type BrowserSetupOptions, type ControlledTimer, type DrawCall, type GameState, type GameStateCapture, InputInjector, type MockEngine, type MockGame, MockPointerLock, type MockRAF, type MockServer, MockTransport, type MockUDPSocket, type NetworkAddress, type NetworkCondition, type NetworkConfig, type NetworkSimulator, type NodeSetupOptions, type PlaywrightOptions, type PlaywrightTestClient, type StorageScenario, type TestContext, type VisualDiff, type VisualScenario, captureAudioEvents, captureCanvasDrawCalls, captureGameScreenshot, captureGameState, compareScreenshots, createBinaryStreamMock, createBinaryWriterMock, createControlledTimer, createCustomNetworkCondition, createEntity, createEntityStateFactory, createGameStateSnapshotFactory, createMockAudioContext, createMockCanvas, createMockCanvasContext2D, createMockEngine, createMockGame, createMockGameState, createMockImage, createMockImageData, createMockIndexedDB, createMockLocalStorage, createMockNetworkAddress, createMockPerformance, createMockRAF, createMockServer, createMockServerClient, createMockServerState, createMockServerStatic, createMockSessionStorage, createMockTransport, createMockUDPSocket, createMockWebGL2Context, createNetChanMock, createPlayerStateFactory, createPlaywrightTestClient, createSpawnContext, createStorageTestScenario, createTestContext, createVisualTestScenario, makeAxisBrush, makeBrushFromMinsMaxs, makeBspModel, makeLeaf, makeLeafModel, makeNode, makePlane, setupBrowserEnvironment, setupMockAudioContext, setupNodeEnvironment, simulateFrames, simulateFramesWithMock, simulateNetworkCondition, teardownBrowserEnvironment, teardownMockAudioContext, throttleBandwidth, waitForGameReady };
661
+ export { type AudioEvent, type BinaryStreamMock, type BinaryWriterMock, type BrowserSetupOptions, type Connection, type ConsistencyReport, type ControlledTimer, type DeltaSnapshot, type DrawCall, type GameState, type GameStateCapture, type Handshake, HandshakeStage, InputInjector, type MockEngine, type MockGame, MockPointerLock, type MockRAF, type MockServer, type MockServerContext, MockTransport, type MockUDPSocket, type MultiplayerScenario, type NetworkAddress, type NetworkCondition, type NetworkConfig, type NetworkSimulator, type NodeSetupOptions, type PlaywrightOptions, type PlaywrightTestClient, type Snapshot, type StorageScenario, type TestContext, type UserInfo, type VisualDiff, type VisualScenario, captureAudioEvents, captureCanvasDrawCalls, captureGameScreenshot, captureGameState, compareScreenshots, createBinaryStreamMock, createBinaryWriterMock, createControlledTimer, createCustomNetworkCondition, createDeltaSnapshot, createEntity, createEntityStateFactory, createGameStateSnapshotFactory, createMockAudioContext, createMockCanvas, createMockCanvasContext2D, createMockConnection, createMockEngine, createMockGame, createMockGameState, createMockHandshake, createMockImage, createMockImageData, createMockIndexedDB, createMockLocalStorage, createMockNetworkAddress, createMockPerformance, createMockRAF, createMockServer, createMockServerClient, createMockServerState, createMockServerStatic, createMockSessionStorage, createMockTransport, createMockUDPSocket, createMockUserInfo, createMockWebGL2Context, createMultiplayerTestScenario, createNetChanMock, createPlayerStateFactory, createPlaywrightTestClient, createServerSnapshot, createSpawnContext, createStorageTestScenario, createTestContext, createVisualTestScenario, makeAxisBrush, makeBrushFromMinsMaxs, makeBspModel, makeLeaf, makeLeafModel, makeNode, makePlane, serializeUserInfo, setupBrowserEnvironment, setupMockAudioContext, setupNodeEnvironment, simulateFrames, simulateFramesWithMock, simulateHandshake, simulateNetworkCondition, simulatePlayerInput, simulatePlayerJoin, simulatePlayerLeave, simulateServerTick, simulateSnapshotDelivery, teardownBrowserEnvironment, teardownMockAudioContext, throttleBandwidth, verifySnapshotConsistency, waitForGameReady };
@@ -603,6 +603,214 @@ function createMockServer(overrides) {
603
603
  };
604
604
  }
605
605
 
606
+ // src/server/mocks/connection.ts
607
+ import { ClientState as ClientState2 } from "@quake2ts/server";
608
+ var HandshakeStage = /* @__PURE__ */ ((HandshakeStage2) => {
609
+ HandshakeStage2[HandshakeStage2["None"] = 0] = "None";
610
+ HandshakeStage2[HandshakeStage2["Challenge"] = 1] = "Challenge";
611
+ HandshakeStage2[HandshakeStage2["Connect"] = 2] = "Connect";
612
+ HandshakeStage2[HandshakeStage2["Info"] = 3] = "Info";
613
+ HandshakeStage2[HandshakeStage2["Active"] = 4] = "Active";
614
+ return HandshakeStage2;
615
+ })(HandshakeStage || {});
616
+ function serializeUserInfo(info) {
617
+ return Object.entries(info).reduce((acc, [key, value]) => {
618
+ if (value !== void 0) {
619
+ return `${acc}\\${key}\\${value}`;
620
+ }
621
+ return acc;
622
+ }, "");
623
+ }
624
+ function createMockUserInfo(overrides) {
625
+ return {
626
+ name: "Player",
627
+ skin: "male/grunt",
628
+ model: "male/grunt",
629
+ fov: 90,
630
+ hand: 0,
631
+ rate: 25e3,
632
+ msg: 1,
633
+ ...overrides
634
+ };
635
+ }
636
+ function createMockConnection(state = ClientState2.Connected, overrides) {
637
+ return createMockServerClient(0, {
638
+ state,
639
+ userInfo: serializeUserInfo(createMockUserInfo()),
640
+ challenge: Math.floor(Math.random() * 1e5),
641
+ ...overrides
642
+ });
643
+ }
644
+ function createMockHandshake(stage = 0 /* None */) {
645
+ return {
646
+ stage,
647
+ clientNum: -1,
648
+ challenge: 0,
649
+ qport: Math.floor(Math.random() * 65536)
650
+ };
651
+ }
652
+ async function simulateHandshake(client, server) {
653
+ const challenge = Math.floor(Math.random() * 1e5);
654
+ client.challenge = challenge;
655
+ if (server && server.clients) {
656
+ }
657
+ client.state = ClientState2.Connected;
658
+ client.state = ClientState2.Active;
659
+ return true;
660
+ }
661
+
662
+ // src/server/helpers/multiplayer.ts
663
+ import { ClientState as ClientState3 } from "@quake2ts/server";
664
+ function createMultiplayerTestScenario(numPlayers = 2) {
665
+ const baseServer = createMockServerState();
666
+ const clients = [];
667
+ const entities = [];
668
+ const server = {
669
+ ...baseServer,
670
+ clients: new Array(numPlayers).fill(null),
671
+ entities: []
672
+ };
673
+ for (let i = 0; i < numPlayers; i++) {
674
+ const client = createMockServerClient(i, {
675
+ state: ClientState3.Active,
676
+ userInfo: serializeUserInfo(createMockUserInfo({ name: `Player${i}` }))
677
+ });
678
+ const entity = {
679
+ classname: "player",
680
+ s: { origin: { x: 0, y: 0, z: 0 }, number: i + 1 },
681
+ client
682
+ };
683
+ client.edict = entity;
684
+ server.clients[i] = client;
685
+ clients.push(client);
686
+ entities.push(entity);
687
+ }
688
+ server.entities = entities;
689
+ return {
690
+ server,
691
+ clients,
692
+ entities
693
+ };
694
+ }
695
+ async function simulatePlayerJoin(server, userInfo) {
696
+ const index = server.clients.findIndex((c) => !c || c.state === ClientState3.Free);
697
+ if (index === -1) {
698
+ throw new Error("Server full");
699
+ }
700
+ const client = createMockServerClient(index, {
701
+ state: ClientState3.Connected,
702
+ userInfo: serializeUserInfo(createMockUserInfo(userInfo))
703
+ });
704
+ server.clients[index] = client;
705
+ client.state = ClientState3.Active;
706
+ const entity = {
707
+ classname: "player",
708
+ s: { origin: { x: 0, y: 0, z: 0 }, number: index + 1 },
709
+ client
710
+ };
711
+ client.edict = entity;
712
+ if (server.entities && Array.isArray(server.entities)) {
713
+ server.entities[index + 1] = entity;
714
+ }
715
+ return client;
716
+ }
717
+ function simulatePlayerLeave(server, clientNum) {
718
+ const client = server.clients[clientNum];
719
+ if (client) {
720
+ client.state = ClientState3.Free;
721
+ client.edict = null;
722
+ }
723
+ }
724
+ function simulateServerTick(server, deltaTime = 0.1) {
725
+ server.time += deltaTime;
726
+ server.frame++;
727
+ server.clients.forEach((client) => {
728
+ if (client && client.state === ClientState3.Active) {
729
+ }
730
+ });
731
+ }
732
+ function simulatePlayerInput(client, input) {
733
+ const cmd = {
734
+ msec: 100,
735
+ buttons: 0,
736
+ angles: { x: 0, y: 0, z: 0 },
737
+ forwardmove: 0,
738
+ sidemove: 0,
739
+ upmove: 0,
740
+ sequence: client.lastCmd.sequence + 1,
741
+ lightlevel: 0,
742
+ impulse: 0,
743
+ ...input
744
+ };
745
+ client.lastCmd = cmd;
746
+ client.commandQueue.push(cmd);
747
+ client.commandCount++;
748
+ }
749
+
750
+ // src/server/helpers/snapshot.ts
751
+ function createServerSnapshot(serverState, clientNum) {
752
+ const visibleEntities = [];
753
+ if (serverState.baselines) {
754
+ serverState.baselines.forEach((ent, index) => {
755
+ if (ent && index !== clientNum + 1) {
756
+ visibleEntities.push({ ...ent });
757
+ }
758
+ });
759
+ }
760
+ return {
761
+ serverTime: serverState.time,
762
+ playerState: {
763
+ origin: { x: 0, y: 0, z: 0 },
764
+ viewangles: { x: 0, y: 0, z: 0 },
765
+ pm_type: 0
766
+ },
767
+ entities: visibleEntities
768
+ };
769
+ }
770
+ function createDeltaSnapshot(oldSnapshot, newSnapshot) {
771
+ const deltaEntities = [];
772
+ const removedEntities = [];
773
+ const oldMap = new Map(oldSnapshot.entities.map((e) => [e.number, e]));
774
+ const newMap = new Map(newSnapshot.entities.map((e) => [e.number, e]));
775
+ newSnapshot.entities.forEach((newEnt) => {
776
+ const oldEnt = oldMap.get(newEnt.number);
777
+ if (!oldEnt) {
778
+ deltaEntities.push(newEnt);
779
+ } else if (JSON.stringify(newEnt) !== JSON.stringify(oldEnt)) {
780
+ deltaEntities.push(newEnt);
781
+ }
782
+ });
783
+ oldSnapshot.entities.forEach((oldEnt) => {
784
+ if (!newMap.has(oldEnt.number)) {
785
+ removedEntities.push(oldEnt.number);
786
+ }
787
+ });
788
+ return {
789
+ snapshot: newSnapshot,
790
+ deltaEntities,
791
+ removedEntities
792
+ };
793
+ }
794
+ function verifySnapshotConsistency(snapshots) {
795
+ const report = { valid: true, errors: [] };
796
+ if (snapshots.length < 2) return report;
797
+ for (let i = 1; i < snapshots.length; i++) {
798
+ const prev = snapshots[i - 1];
799
+ const curr = snapshots[i];
800
+ if (curr.serverTime <= prev.serverTime) {
801
+ report.valid = false;
802
+ report.errors.push(`Snapshot ${i} has time ${curr.serverTime} <= prev ${prev.serverTime}`);
803
+ }
804
+ }
805
+ return report;
806
+ }
807
+ async function simulateSnapshotDelivery(snapshot, reliability = 1) {
808
+ if (Math.random() > reliability) {
809
+ return null;
810
+ }
811
+ return snapshot;
812
+ }
813
+
606
814
  // src/setup/browser.ts
607
815
  import { JSDOM } from "jsdom";
608
816
  import { Canvas, Image, ImageData } from "@napi-rs/canvas";
@@ -1668,6 +1876,7 @@ function createVisualTestScenario(page, sceneName) {
1668
1876
  };
1669
1877
  }
1670
1878
  export {
1879
+ HandshakeStage,
1671
1880
  InputInjector,
1672
1881
  MockPointerLock,
1673
1882
  MockTransport,
@@ -1680,15 +1889,18 @@ export {
1680
1889
  createBinaryWriterMock,
1681
1890
  createControlledTimer,
1682
1891
  createCustomNetworkCondition,
1892
+ createDeltaSnapshot,
1683
1893
  createEntity,
1684
1894
  createEntityStateFactory,
1685
1895
  createGameStateSnapshotFactory,
1686
1896
  createMockAudioContext,
1687
1897
  createMockCanvas,
1688
1898
  createMockCanvasContext2D,
1899
+ createMockConnection,
1689
1900
  createMockEngine,
1690
1901
  createMockGame,
1691
1902
  createMockGameState,
1903
+ createMockHandshake,
1692
1904
  createMockImage,
1693
1905
  createMockImageData,
1694
1906
  createMockIndexedDB,
@@ -1703,10 +1915,13 @@ export {
1703
1915
  createMockSessionStorage,
1704
1916
  createMockTransport,
1705
1917
  createMockUDPSocket,
1918
+ createMockUserInfo,
1706
1919
  createMockWebGL2Context,
1920
+ createMultiplayerTestScenario,
1707
1921
  createNetChanMock,
1708
1922
  createPlayerStateFactory,
1709
1923
  createPlaywrightTestClient,
1924
+ createServerSnapshot,
1710
1925
  createSpawnContext,
1711
1926
  createStorageTestScenario,
1712
1927
  createTestContext,
@@ -1720,16 +1935,24 @@ export {
1720
1935
  makeLeafModel,
1721
1936
  makeNode,
1722
1937
  makePlane,
1938
+ serializeUserInfo,
1723
1939
  setupBrowserEnvironment,
1724
1940
  setupMockAudioContext,
1725
1941
  setupNodeEnvironment,
1726
1942
  simulateFrames,
1727
1943
  simulateFramesWithMock,
1944
+ simulateHandshake,
1728
1945
  simulateNetworkCondition,
1946
+ simulatePlayerInput,
1947
+ simulatePlayerJoin,
1948
+ simulatePlayerLeave,
1949
+ simulateServerTick,
1950
+ simulateSnapshotDelivery,
1729
1951
  stairTrace,
1730
1952
  teardownBrowserEnvironment,
1731
1953
  teardownMockAudioContext,
1732
1954
  throttleBandwidth,
1955
+ verifySnapshotConsistency,
1733
1956
  waitForGameReady
1734
1957
  };
1735
1958
  //# sourceMappingURL=index.js.map