quake2ts 0.0.582 → 0.0.585

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.
@@ -22,7 +22,9 @@ var createBinaryWriterMock = () => ({
22
22
  writeInt32: vi.fn(),
23
23
  writeUint32: vi.fn(),
24
24
  writeFloat: vi.fn(),
25
- getData: vi.fn(() => new Uint8Array(0))
25
+ getData: vi.fn(() => new Uint8Array(0)),
26
+ writePos: vi.fn(),
27
+ writeDir: vi.fn()
26
28
  });
27
29
  var createNetChanMock = () => ({
28
30
  qport: 1234,
@@ -84,6 +86,37 @@ var createBinaryStreamMock = () => ({
84
86
  readPos: vi.fn(),
85
87
  readDir: vi.fn()
86
88
  });
89
+ var createMessageWriterMock = (overrides) => {
90
+ const mock = createBinaryWriterMock();
91
+ const writer = {
92
+ ...mock,
93
+ writeInt: mock.writeInt32,
94
+ // Alias writeInt to writeInt32
95
+ writeVector: mock.writePos,
96
+ // Alias writeVector to writePos
97
+ ...overrides
98
+ };
99
+ return writer;
100
+ };
101
+ var createMessageReaderMock = (data) => {
102
+ const mock = createBinaryStreamMock();
103
+ const reader = {
104
+ ...mock,
105
+ readInt: mock.readLong,
106
+ // Alias readInt to readLong (int32)
107
+ readVector: mock.readPos
108
+ // Alias readVector to readPos
109
+ };
110
+ return reader;
111
+ };
112
+ var createPacketMock = (overrides) => ({
113
+ type: "data",
114
+ sequence: 0,
115
+ ack: 0,
116
+ qport: 0,
117
+ data: new Uint8Array(0),
118
+ ...overrides
119
+ });
87
120
 
88
121
  // src/shared/bsp.ts
89
122
  import {
@@ -155,6 +188,28 @@ function makeBrushFromMinsMaxs(mins, maxs, contents = CONTENTS_SOLID) {
155
188
  };
156
189
  }
157
190
 
191
+ // src/shared/math.ts
192
+ var createVector3 = (x = 0, y = 0, z = 0) => ({
193
+ x,
194
+ y,
195
+ z
196
+ });
197
+ var createBounds = (mins = createVector3(0, 0, 0), maxs = createVector3(1, 1, 1)) => ({
198
+ mins,
199
+ maxs
200
+ });
201
+ var createTransform = (overrides) => ({
202
+ position: createVector3(),
203
+ rotation: createVector3(),
204
+ scale: createVector3(1, 1, 1),
205
+ ...overrides
206
+ });
207
+ var randomVector3 = (min2 = -100, max2 = 100) => ({
208
+ x: Math.random() * (max2 - min2) + min2,
209
+ y: Math.random() * (max2 - min2) + min2,
210
+ z: Math.random() * (max2 - min2) + min2
211
+ });
212
+
158
213
  // src/game/factories.ts
159
214
  import {
160
215
  Entity,
@@ -317,7 +372,28 @@ function createTriggerEntityFactory(classname, overrides = {}) {
317
372
  import { vi as vi2 } from "vitest";
318
373
  import { Entity as Entity2, SpawnRegistry, ScriptHookRegistry } from "@quake2ts/game";
319
374
  import { createRandomGenerator } from "@quake2ts/shared";
375
+
376
+ // src/shared/collision.ts
320
377
  import { intersects, stairTrace, ladderTrace } from "@quake2ts/shared";
378
+ var createTraceMock = (overrides) => ({
379
+ fraction: 1,
380
+ endpos: { x: 0, y: 0, z: 0 },
381
+ plane: { normal: { x: 0, y: 0, z: 0 }, dist: 0, type: 0, signbits: 0 },
382
+ surface: { flags: 0 },
383
+ contents: 0,
384
+ ent: null,
385
+ allsolid: false,
386
+ startsolid: false,
387
+ ...overrides
388
+ });
389
+ var createSurfaceMock = (overrides) => ({
390
+ flags: 0,
391
+ name: "default",
392
+ value: 0,
393
+ ...overrides
394
+ });
395
+
396
+ // src/game/helpers.ts
321
397
  var createMockEngine = () => ({
322
398
  sound: vi2.fn(),
323
399
  soundIndex: vi2.fn((sound) => 0),
@@ -354,16 +430,12 @@ function createTestContext(options) {
354
430
  const engine = createMockEngine();
355
431
  const seed = options?.seed ?? 12345;
356
432
  const { game, spawnRegistry } = createMockGame(seed);
357
- const traceFn = vi2.fn((start, end, mins, maxs) => ({
358
- fraction: 1,
359
- ent: null,
360
- allsolid: false,
361
- startsolid: false,
362
- endpos: end,
363
- plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0 },
364
- surfaceFlags: 0,
365
- contents: 0
366
- }));
433
+ const traceFn = vi2.fn(
434
+ (start, end, mins, maxs) => createTraceMock({
435
+ endpos: end,
436
+ plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0, type: 0, signbits: 0 }
437
+ })
438
+ );
367
439
  const entityList = options?.initialEntities ? [...options.initialEntities] : [];
368
440
  const hooks = game.hooks;
369
441
  const entities = {
@@ -1912,7 +1984,7 @@ function teardownBrowserEnvironment() {
1912
1984
  }
1913
1985
 
1914
1986
  // src/setup/canvas.ts
1915
- import { Canvas as Canvas2, Image as Image2, ImageData as ImageData2 } from "@napi-rs/canvas";
1987
+ import { Canvas as Canvas2, ImageData as ImageData2 } from "@napi-rs/canvas";
1916
1988
  function createMockCanvas(width = 300, height = 150) {
1917
1989
  if (typeof document !== "undefined" && document.createElement) {
1918
1990
  const canvas2 = document.createElement("canvas");
@@ -1920,75 +1992,92 @@ function createMockCanvas(width = 300, height = 150) {
1920
1992
  canvas2.height = height;
1921
1993
  return canvas2;
1922
1994
  }
1923
- const canvas = new Canvas2(width, height);
1924
- const originalGetContext = canvas.getContext.bind(canvas);
1925
- canvas.getContext = function(contextId, options) {
1926
- if (contextId === "webgl2") {
1927
- return createMockWebGL2Context(canvas);
1928
- }
1929
- if (contextId === "2d") {
1930
- return originalGetContext("2d", options);
1931
- }
1932
- return originalGetContext(contextId, options);
1995
+ const napiCanvas = new Canvas2(width, height);
1996
+ const canvas = {
1997
+ width,
1998
+ height,
1999
+ getContext: (contextId, options) => {
2000
+ if (contextId === "2d") {
2001
+ return napiCanvas.getContext("2d", options);
2002
+ }
2003
+ if (contextId === "webgl2") {
2004
+ return createMockWebGL2Context(canvas);
2005
+ }
2006
+ return null;
2007
+ },
2008
+ toDataURL: () => napiCanvas.toDataURL(),
2009
+ toBuffer: (mime) => napiCanvas.toBuffer(mime)
2010
+ // Add other properties as needed
1933
2011
  };
1934
2012
  return canvas;
1935
2013
  }
1936
2014
  function createMockCanvasContext2D(canvas) {
1937
- if (!canvas) {
1938
- canvas = createMockCanvas();
2015
+ const c = canvas || createMockCanvas();
2016
+ const ctx = c.getContext("2d");
2017
+ if (!ctx) {
2018
+ throw new Error("Failed to create 2D context");
1939
2019
  }
1940
- return canvas.getContext("2d");
2020
+ return ctx;
1941
2021
  }
1942
2022
  function captureCanvasDrawCalls(context) {
1943
- const drawCalls = [];
1944
- const methodsToSpy = [
1945
- "fillRect",
1946
- "strokeRect",
1947
- "clearRect",
1948
- "fillText",
1949
- "strokeText",
1950
- "drawImage",
1951
- "beginPath",
1952
- "closePath",
1953
- "moveTo",
1954
- "lineTo",
1955
- "arc",
1956
- "arcTo",
1957
- "bezierCurveTo",
1958
- "quadraticCurveTo",
1959
- "stroke",
1960
- "fill",
1961
- "putImageData"
1962
- ];
1963
- methodsToSpy.forEach((method) => {
1964
- const original = context[method];
1965
- if (typeof original === "function") {
1966
- context[method] = function(...args) {
1967
- drawCalls.push({ method, args });
1968
- return original.apply(this, args);
2023
+ const calls = [];
2024
+ const proto = Object.getPrototypeOf(context);
2025
+ for (const key of Object.getOwnPropertyNames(proto)) {
2026
+ const value = context[key];
2027
+ if (typeof value === "function") {
2028
+ context[key] = function(...args) {
2029
+ calls.push({ method: key, args });
2030
+ return value.apply(context, args);
1969
2031
  };
1970
2032
  }
1971
- });
1972
- return drawCalls;
2033
+ }
2034
+ return calls;
1973
2035
  }
1974
2036
  function createMockImageData(width, height, fillColor) {
1975
- const imageData = new ImageData2(width, height);
2037
+ if (typeof global.ImageData !== "undefined") {
2038
+ const data2 = new Uint8ClampedArray(width * height * 4);
2039
+ if (fillColor) {
2040
+ for (let i = 0; i < data2.length; i += 4) {
2041
+ data2[i] = fillColor[0];
2042
+ data2[i + 1] = fillColor[1];
2043
+ data2[i + 2] = fillColor[2];
2044
+ data2[i + 3] = fillColor[3];
2045
+ }
2046
+ }
2047
+ return new global.ImageData(data2, width, height);
2048
+ }
2049
+ const data = new Uint8ClampedArray(width * height * 4);
1976
2050
  if (fillColor) {
1977
- const [r, g, b, a] = fillColor;
1978
- for (let i = 0; i < imageData.data.length; i += 4) {
1979
- imageData.data[i] = r;
1980
- imageData.data[i + 1] = g;
1981
- imageData.data[i + 2] = b;
1982
- imageData.data[i + 3] = a;
2051
+ for (let i = 0; i < data.length; i += 4) {
2052
+ data[i] = fillColor[0];
2053
+ data[i + 1] = fillColor[1];
2054
+ data[i + 2] = fillColor[2];
2055
+ data[i + 3] = fillColor[3];
1983
2056
  }
1984
2057
  }
1985
- return imageData;
2058
+ return new ImageData2(data, width, height);
1986
2059
  }
1987
- function createMockImage(width, height, src) {
1988
- const img = new Image2();
1989
- if (width) img.width = width;
1990
- if (height) img.height = height;
1991
- if (src) img.src = src;
2060
+ function createMockImage(width = 100, height = 100, src = "") {
2061
+ if (typeof document !== "undefined" && document.createElement) {
2062
+ const img2 = document.createElement("img");
2063
+ img2.width = width;
2064
+ img2.height = height;
2065
+ if (src) img2.src = src;
2066
+ return img2;
2067
+ }
2068
+ const img = {
2069
+ width,
2070
+ height,
2071
+ src,
2072
+ complete: true,
2073
+ onload: null,
2074
+ onerror: null
2075
+ };
2076
+ if (src) {
2077
+ setTimeout(() => {
2078
+ if (img.onload) img.onload();
2079
+ }, 0);
2080
+ }
1992
2081
  return img;
1993
2082
  }
1994
2083
 
@@ -2056,193 +2145,89 @@ function setupWebGPUMocks() {
2056
2145
  }
2057
2146
 
2058
2147
  // src/setup/timing.ts
2059
- var activeMockRAF;
2060
2148
  function createMockRAF() {
2061
2149
  let callbacks = [];
2062
- let nextId = 1;
2150
+ let lastId = 0;
2063
2151
  let currentTime = 0;
2064
2152
  const originalRAF = global.requestAnimationFrame;
2065
2153
  const originalCancelRAF = global.cancelAnimationFrame;
2066
- const raf = (callback) => {
2067
- const id = nextId++;
2068
- callbacks.push({ id, callback });
2069
- return id;
2154
+ global.requestAnimationFrame = (callback) => {
2155
+ lastId++;
2156
+ callbacks.push({ id: lastId, callback });
2157
+ return lastId;
2070
2158
  };
2071
- const cancel = (id) => {
2159
+ global.cancelAnimationFrame = (id) => {
2072
2160
  callbacks = callbacks.filter((cb) => cb.id !== id);
2073
2161
  };
2074
- const mock = {
2075
- tick(timestamp) {
2076
- if (typeof timestamp !== "number") {
2077
- currentTime += 16.6;
2078
- } else {
2079
- currentTime = timestamp;
2080
- }
2162
+ return {
2163
+ tick(time) {
2164
+ if (time) currentTime = time;
2165
+ else currentTime += 16.66;
2081
2166
  const currentCallbacks = [...callbacks];
2082
2167
  callbacks = [];
2083
- currentCallbacks.forEach(({ callback }) => {
2084
- callback(currentTime);
2085
- });
2168
+ currentCallbacks.forEach((cb) => cb.callback(currentTime));
2086
2169
  },
2087
- advance(deltaMs = 16.6) {
2088
- this.tick(currentTime + deltaMs);
2170
+ advance(ms) {
2171
+ currentTime += ms;
2172
+ this.tick(currentTime);
2089
2173
  },
2090
2174
  getCallbacks() {
2091
- return callbacks.map((c) => c.callback);
2092
- },
2093
- reset() {
2094
- callbacks = [];
2095
- nextId = 1;
2096
- currentTime = 0;
2097
- },
2098
- enable() {
2099
- activeMockRAF = this;
2100
- global.requestAnimationFrame = raf;
2101
- global.cancelAnimationFrame = cancel;
2102
- },
2103
- disable() {
2104
- if (activeMockRAF === this) {
2105
- activeMockRAF = void 0;
2106
- }
2107
- if (originalRAF) {
2108
- global.requestAnimationFrame = originalRAF;
2109
- } else {
2110
- delete global.requestAnimationFrame;
2111
- }
2112
- if (originalCancelRAF) {
2113
- global.cancelAnimationFrame = originalCancelRAF;
2114
- } else {
2115
- delete global.cancelAnimationFrame;
2116
- }
2175
+ return callbacks;
2117
2176
  }
2118
2177
  };
2119
- return mock;
2120
2178
  }
2121
2179
  function createMockPerformance(startTime = 0) {
2122
- let currentTime = startTime;
2123
- const mockPerf = {
2124
- now: () => currentTime,
2180
+ let now = startTime;
2181
+ const mockPerformance = {
2182
+ now: () => now,
2125
2183
  timeOrigin: startTime,
2126
2184
  timing: {
2127
2185
  navigationStart: startTime
2128
2186
  },
2129
- clearMarks: () => {
2187
+ mark: (_name) => {
2130
2188
  },
2131
- clearMeasures: () => {
2132
- },
2133
- clearResourceTimings: () => {
2189
+ measure: (_name, _start, _end) => {
2134
2190
  },
2135
2191
  getEntries: () => [],
2136
- getEntriesByName: () => [],
2137
- getEntriesByType: () => [],
2138
- mark: () => {
2192
+ getEntriesByName: (_name) => [],
2193
+ getEntriesByType: (_type) => [],
2194
+ clearMarks: (_name) => {
2139
2195
  },
2140
- measure: () => {
2196
+ clearMeasures: (_name) => {
2141
2197
  },
2142
- setResourceTimingBufferSize: () => {
2143
- },
2144
- toJSON: () => ({}),
2145
- addEventListener: () => {
2198
+ clearResourceTimings: () => {
2146
2199
  },
2147
- removeEventListener: () => {
2200
+ setResourceTimingBufferSize: (_maxSize) => {
2148
2201
  },
2149
- dispatchEvent: () => true
2150
- };
2151
- mockPerf.advance = (deltaMs) => {
2152
- currentTime += deltaMs;
2202
+ onresourcetimingbufferfull: null,
2203
+ toJSON: () => ({})
2153
2204
  };
2154
- mockPerf.setTime = (time) => {
2155
- currentTime = time;
2156
- };
2157
- return mockPerf;
2205
+ if (typeof global.performance === "undefined") {
2206
+ global.performance = mockPerformance;
2207
+ }
2208
+ return mockPerformance;
2158
2209
  }
2159
2210
  function createControlledTimer() {
2160
- let currentTime = 0;
2161
- let timers = [];
2162
- let nextId = 1;
2163
- const originalSetTimeout = global.setTimeout;
2164
- const originalClearTimeout = global.clearTimeout;
2165
- const originalSetInterval = global.setInterval;
2166
- const originalClearInterval = global.clearInterval;
2167
- const mockSetTimeout = (callback, delay = 0, ...args) => {
2168
- const id = nextId++;
2169
- timers.push({ id, callback, dueTime: currentTime + delay, args });
2170
- return id;
2171
- };
2172
- const mockClearTimeout = (id) => {
2173
- timers = timers.filter((t) => t.id !== id);
2174
- };
2175
- const mockSetInterval = (callback, delay = 0, ...args) => {
2176
- const id = nextId++;
2177
- timers.push({ id, callback, dueTime: currentTime + delay, interval: delay, args });
2178
- return id;
2179
- };
2180
- const mockClearInterval = (id) => {
2181
- timers = timers.filter((t) => t.id !== id);
2182
- };
2183
- global.setTimeout = mockSetTimeout;
2184
- global.clearTimeout = mockClearTimeout;
2185
- global.setInterval = mockSetInterval;
2186
- global.clearInterval = mockClearInterval;
2211
+ console.warn("createControlledTimer: Recommend using vi.useFakeTimers() instead.");
2187
2212
  return {
2188
- tick() {
2189
- this.advanceBy(0);
2213
+ advanceBy: (ms) => {
2190
2214
  },
2191
- advanceBy(ms) {
2192
- const targetTime = currentTime + ms;
2193
- while (true) {
2194
- let earliest = null;
2195
- for (const t of timers) {
2196
- if (!earliest || t.dueTime < earliest.dueTime) {
2197
- earliest = t;
2198
- }
2199
- }
2200
- if (!earliest || earliest.dueTime > targetTime) {
2201
- break;
2202
- }
2203
- currentTime = earliest.dueTime;
2204
- const { callback, args, interval, id } = earliest;
2205
- if (interval !== void 0) {
2206
- earliest.dueTime += interval;
2207
- if (interval === 0) earliest.dueTime += 1;
2208
- } else {
2209
- timers = timers.filter((t) => t.id !== id);
2210
- }
2211
- callback(...args);
2212
- }
2213
- currentTime = targetTime;
2215
+ runAll: () => {
2214
2216
  },
2215
- clear() {
2216
- timers = [];
2217
- },
2218
- restore() {
2219
- global.setTimeout = originalSetTimeout;
2220
- global.clearTimeout = originalClearTimeout;
2221
- global.setInterval = originalSetInterval;
2222
- global.clearInterval = originalClearInterval;
2217
+ clear: () => {
2223
2218
  }
2224
2219
  };
2225
2220
  }
2226
- function simulateFrames(count, frameTimeMs = 16.6, callback) {
2227
- if (!activeMockRAF) {
2228
- throw new Error("simulateFrames requires an active MockRAF. Ensure createMockRAF().enable() is called.");
2229
- }
2230
- for (let i = 0; i < count; i++) {
2231
- if (callback) callback(i);
2232
- activeMockRAF.advance(frameTimeMs);
2233
- }
2234
- }
2235
- function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
2221
+ function simulateFrames(count, frameTime = 16, callback) {
2236
2222
  for (let i = 0; i < count; i++) {
2237
2223
  if (callback) callback(i);
2238
- mock.advance(frameTimeMs);
2239
2224
  }
2240
2225
  }
2241
2226
 
2242
2227
  // src/setup/node.ts
2243
2228
  function setupNodeEnvironment(options = {}) {
2244
- if (options.polyfillFetch && typeof global.fetch === "undefined") {
2245
- }
2229
+ }
2230
+ function teardownNodeEnvironment() {
2246
2231
  }
2247
2232
 
2248
2233
  // src/engine/rendering.ts
@@ -2304,186 +2289,141 @@ function createMockRenderingContext() {
2304
2289
  }
2305
2290
 
2306
2291
  // src/setup/storage.ts
2307
- import "fake-indexeddb/auto";
2308
- function createMockLocalStorage(initialData = {}) {
2309
- const storage = new Map(Object.entries(initialData));
2292
+ function createStorageMock(initialData = {}) {
2293
+ const store = new Map(Object.entries(initialData));
2310
2294
  return {
2311
- getItem: (key) => storage.get(key) || null,
2312
- setItem: (key, value) => storage.set(key, value),
2313
- removeItem: (key) => storage.delete(key),
2314
- clear: () => storage.clear(),
2315
- key: (index) => Array.from(storage.keys())[index] || null,
2295
+ getItem: (key) => store.get(key) || null,
2296
+ setItem: (key, value) => store.set(key, value.toString()),
2297
+ removeItem: (key) => store.delete(key),
2298
+ clear: () => store.clear(),
2299
+ key: (index) => Array.from(store.keys())[index] || null,
2316
2300
  get length() {
2317
- return storage.size;
2301
+ return store.size;
2318
2302
  }
2319
2303
  };
2320
2304
  }
2305
+ function createMockLocalStorage(initialData = {}) {
2306
+ return createStorageMock(initialData);
2307
+ }
2321
2308
  function createMockSessionStorage(initialData = {}) {
2322
- return createMockLocalStorage(initialData);
2309
+ return createStorageMock(initialData);
2323
2310
  }
2324
- function createMockIndexedDB() {
2325
- if (typeof indexedDB === "undefined") {
2326
- throw new Error("IndexedDB mock not found. Ensure fake-indexeddb is loaded.");
2327
- }
2328
- return indexedDB;
2311
+ function createMockIndexedDB(databases) {
2312
+ return global.indexedDB;
2329
2313
  }
2330
2314
  function createStorageTestScenario(storageType = "local") {
2331
- if (storageType === "indexed") {
2332
- const dbName = `test-db-${Math.random().toString(36).substring(7)}`;
2333
- const storeName = "test-store";
2334
- const storage2 = createMockIndexedDB();
2335
- return {
2336
- storage: storage2,
2337
- populate: async (data) => {
2338
- return new Promise((resolve, reject) => {
2339
- const req = storage2.open(dbName, 1);
2340
- req.onupgradeneeded = (e) => {
2341
- const db = e.target.result;
2342
- db.createObjectStore(storeName);
2343
- };
2344
- req.onsuccess = (e) => {
2345
- const db = e.target.result;
2346
- const tx = db.transaction(storeName, "readwrite");
2347
- const store = tx.objectStore(storeName);
2348
- Object.entries(data).forEach(([k, v]) => store.put(v, k));
2349
- tx.oncomplete = () => {
2350
- db.close();
2351
- resolve();
2352
- };
2353
- tx.onerror = () => reject(tx.error);
2354
- };
2355
- req.onerror = () => reject(req.error);
2356
- });
2357
- },
2358
- verify: async (key, value) => {
2359
- return new Promise((resolve, reject) => {
2360
- const req = storage2.open(dbName, 1);
2361
- req.onsuccess = (e) => {
2362
- const db = e.target.result;
2363
- if (!db.objectStoreNames.contains(storeName)) {
2364
- db.close();
2365
- resolve(false);
2366
- return;
2367
- }
2368
- const tx = db.transaction(storeName, "readonly");
2369
- const store = tx.objectStore(storeName);
2370
- const getReq = store.get(key);
2371
- getReq.onsuccess = () => {
2372
- const result = getReq.result === value;
2373
- db.close();
2374
- resolve(result);
2375
- };
2376
- getReq.onerror = () => {
2377
- db.close();
2378
- resolve(false);
2379
- };
2380
- };
2381
- req.onerror = () => reject(req.error);
2382
- });
2383
- }
2384
- };
2385
- }
2386
- const storage = storageType === "local" ? createMockLocalStorage() : createMockSessionStorage();
2387
2315
  return {
2388
- storage,
2389
- populate(data) {
2390
- Object.entries(data).forEach(([k, v]) => storage.setItem(k, v));
2391
- },
2392
- verify(key, value) {
2393
- return storage.getItem(key) === value;
2394
- }
2316
+ localStorage: createMockLocalStorage(),
2317
+ sessionStorage: createMockSessionStorage(),
2318
+ indexedDB: createMockIndexedDB()
2395
2319
  };
2396
2320
  }
2397
2321
 
2398
2322
  // src/setup/audio.ts
2399
- function createMockAudioContext() {
2400
- const context = {
2401
- createGain: () => ({
2402
- connect: () => {
2403
- },
2404
- gain: { value: 1, setValueAtTime: () => {
2405
- } }
2406
- }),
2407
- createOscillator: () => ({
2408
- connect: () => {
2409
- },
2410
- start: () => {
2411
- },
2412
- stop: () => {
2413
- },
2414
- frequency: { value: 440 }
2415
- }),
2416
- createBufferSource: () => ({
2417
- connect: () => {
2418
- },
2419
- start: () => {
2420
- },
2421
- stop: () => {
2422
- },
2423
- buffer: null,
2424
- playbackRate: { value: 1 },
2425
- loop: false
2426
- }),
2427
- destination: {},
2428
- currentTime: 0,
2429
- state: "running",
2430
- resume: async () => {
2431
- },
2432
- suspend: async () => {
2433
- },
2434
- close: async () => {
2435
- },
2436
- decodeAudioData: async (buffer) => ({
2437
- duration: 1,
2438
- length: 44100,
2439
- sampleRate: 44100,
2440
- numberOfChannels: 2,
2441
- getChannelData: () => new Float32Array(44100)
2442
- }),
2443
- createBuffer: (channels, length2, sampleRate) => ({
2444
- duration: length2 / sampleRate,
2445
- length: length2,
2446
- sampleRate,
2447
- numberOfChannels: channels,
2448
- getChannelData: () => new Float32Array(length2)
2449
- }),
2450
- // Helper to track events if needed
2451
- _events: []
2452
- };
2453
- return new Proxy(context, {
2454
- get(target, prop, receiver) {
2455
- if (prop === "_events") return target._events;
2456
- const value = Reflect.get(target, prop, receiver);
2457
- if (typeof value === "function") {
2458
- return (...args) => {
2459
- target._events.push({ type: String(prop), args });
2460
- return Reflect.apply(value, target, args);
2461
- };
2462
- }
2463
- return value;
2464
- }
2465
- });
2466
- }
2467
2323
  function setupMockAudioContext() {
2468
- if (typeof global.AudioContext === "undefined" && typeof global.window !== "undefined") {
2469
- global.AudioContext = class {
2470
- constructor() {
2471
- return createMockAudioContext();
2472
- }
2473
- };
2474
- global.window.AudioContext = global.AudioContext;
2475
- global.window.webkitAudioContext = global.AudioContext;
2324
+ class MockAudioContext {
2325
+ constructor() {
2326
+ this.state = "suspended";
2327
+ this.destination = {};
2328
+ this.currentTime = 0;
2329
+ this.listener = {
2330
+ positionX: { value: 0 },
2331
+ positionY: { value: 0 },
2332
+ positionZ: { value: 0 },
2333
+ forwardX: { value: 0 },
2334
+ forwardY: { value: 0 },
2335
+ forwardZ: { value: 0 },
2336
+ upX: { value: 0 },
2337
+ upY: { value: 0 },
2338
+ upZ: { value: 0 },
2339
+ setOrientation: () => {
2340
+ },
2341
+ setPosition: () => {
2342
+ }
2343
+ };
2344
+ }
2345
+ createGain() {
2346
+ return {
2347
+ gain: { value: 1, linearRampToValueAtTime: () => {
2348
+ } },
2349
+ connect: () => {
2350
+ },
2351
+ disconnect: () => {
2352
+ }
2353
+ };
2354
+ }
2355
+ createBufferSource() {
2356
+ return {
2357
+ buffer: null,
2358
+ loop: false,
2359
+ playbackRate: { value: 1 },
2360
+ connect: () => {
2361
+ },
2362
+ start: () => {
2363
+ },
2364
+ stop: () => {
2365
+ },
2366
+ disconnect: () => {
2367
+ },
2368
+ onended: null
2369
+ };
2370
+ }
2371
+ createPanner() {
2372
+ return {
2373
+ panningModel: "equalpower",
2374
+ distanceModel: "inverse",
2375
+ positionX: { value: 0 },
2376
+ positionY: { value: 0 },
2377
+ positionZ: { value: 0 },
2378
+ orientationX: { value: 0 },
2379
+ orientationY: { value: 0 },
2380
+ orientationZ: { value: 0 },
2381
+ coneInnerAngle: 360,
2382
+ coneOuterAngle: 360,
2383
+ coneOuterGain: 0,
2384
+ connect: () => {
2385
+ },
2386
+ disconnect: () => {
2387
+ },
2388
+ setPosition: () => {
2389
+ },
2390
+ setOrientation: () => {
2391
+ }
2392
+ };
2393
+ }
2394
+ createBuffer(numOfChannels, length2, sampleRate) {
2395
+ return {
2396
+ duration: length2 / sampleRate,
2397
+ length: length2,
2398
+ sampleRate,
2399
+ numberOfChannels: numOfChannels,
2400
+ getChannelData: () => new Float32Array(length2)
2401
+ };
2402
+ }
2403
+ decodeAudioData(data, success) {
2404
+ const buffer = this.createBuffer(2, 100, 44100);
2405
+ if (success) success(buffer);
2406
+ return Promise.resolve(buffer);
2407
+ }
2408
+ resume() {
2409
+ return Promise.resolve();
2410
+ }
2411
+ suspend() {
2412
+ return Promise.resolve();
2413
+ }
2414
+ close() {
2415
+ return Promise.resolve();
2416
+ }
2476
2417
  }
2418
+ global.AudioContext = MockAudioContext;
2419
+ global.webkitAudioContext = MockAudioContext;
2477
2420
  }
2478
2421
  function teardownMockAudioContext() {
2479
- if (global.AudioContext && global.AudioContext.toString().includes("class")) {
2480
- delete global.AudioContext;
2481
- delete global.window.AudioContext;
2482
- delete global.window.webkitAudioContext;
2483
- }
2422
+ delete global.AudioContext;
2423
+ delete global.webkitAudioContext;
2484
2424
  }
2485
2425
  function captureAudioEvents(context) {
2486
- return context._events || [];
2426
+ return [];
2487
2427
  }
2488
2428
 
2489
2429
  // ../../node_modules/.pnpm/gl-matrix@3.4.4/node_modules/gl-matrix/esm/common.js
@@ -2973,217 +2913,110 @@ function simulateCameraMovement(camera, input, deltaTime) {
2973
2913
  }
2974
2914
 
2975
2915
  // src/e2e/playwright.ts
2976
- import { chromium } from "playwright";
2977
- import { createServer } from "http";
2978
- import handler from "serve-handler";
2979
2916
  async function createPlaywrightTestClient(options = {}) {
2980
- let staticServer;
2981
- let clientUrl = options.clientUrl;
2982
- const rootPath = options.rootPath || process.cwd();
2983
- if (!clientUrl) {
2984
- staticServer = createServer((request, response) => {
2985
- return handler(request, response, {
2986
- public: rootPath,
2987
- cleanUrls: false,
2988
- headers: [
2989
- {
2990
- source: "**/*",
2991
- headers: [
2992
- { key: "Cache-Control", value: "no-cache" },
2993
- { key: "Access-Control-Allow-Origin", value: "*" },
2994
- { key: "Cross-Origin-Opener-Policy", value: "same-origin" },
2995
- { key: "Cross-Origin-Embedder-Policy", value: "require-corp" }
2996
- ]
2997
- }
2998
- ]
2999
- });
3000
- });
3001
- await new Promise((resolve) => {
3002
- if (!staticServer) return;
3003
- staticServer.listen(0, () => {
3004
- const addr = staticServer?.address();
3005
- const port = typeof addr === "object" ? addr?.port : 0;
3006
- clientUrl = `http://localhost:${port}`;
3007
- console.log(`Test client serving from ${rootPath} at ${clientUrl}`);
3008
- resolve();
3009
- });
3010
- });
2917
+ let playwright;
2918
+ try {
2919
+ playwright = await import("playwright");
2920
+ } catch (e) {
2921
+ throw new Error("Playwright is not installed. Please install it to use this utility.");
3011
2922
  }
3012
- const browser = await chromium.launch({
3013
- headless: options.headless ?? true,
3014
- args: [
3015
- "--use-gl=egl",
3016
- "--ignore-gpu-blocklist",
3017
- ...options.launchOptions?.args || []
3018
- ],
3019
- ...options.launchOptions
2923
+ const browser = await playwright.chromium.launch({
2924
+ headless: options.headless ?? true
3020
2925
  });
3021
- const width = options.width || 1280;
3022
- const height = options.height || 720;
3023
2926
  const context = await browser.newContext({
3024
- viewport: { width, height },
3025
- deviceScaleFactor: 1,
3026
- ...options.contextOptions
2927
+ viewport: options.viewport || { width: 1280, height: 720 }
3027
2928
  });
3028
2929
  const page = await context.newPage();
3029
- const close = async () => {
3030
- await browser.close();
3031
- if (staticServer) {
3032
- staticServer.close();
3033
- }
3034
- };
3035
- const navigate = async (url) => {
3036
- const targetUrl = url || clientUrl;
3037
- if (!targetUrl) throw new Error("No URL to navigate to");
3038
- let finalUrl = targetUrl;
3039
- if (options.serverUrl && !targetUrl.includes("connect=")) {
3040
- const separator = targetUrl.includes("?") ? "&" : "?";
3041
- finalUrl = `${targetUrl}${separator}connect=${encodeURIComponent(options.serverUrl)}`;
3042
- }
3043
- console.log(`Navigating to: ${finalUrl}`);
3044
- await page.goto(finalUrl, { waitUntil: "domcontentloaded" });
3045
- };
3046
2930
  return {
3047
- browser,
3048
- context,
3049
2931
  page,
3050
- server: staticServer,
3051
- close,
3052
- navigate,
3053
- waitForGame: async (timeout = 1e4) => {
3054
- await waitForGameReady(page, timeout);
2932
+ browser,
2933
+ async navigate(url) {
2934
+ await page.goto(url);
2935
+ },
2936
+ async waitForGame() {
2937
+ await waitForGameReady(page);
3055
2938
  },
3056
- injectInput: async (type, data) => {
2939
+ async injectInput(type, data) {
3057
2940
  await page.evaluate(({ type: type2, data: data2 }) => {
3058
- if (window.injectGameInput) window.injectGameInput(type2, data2);
2941
+ console.log("Injecting input", type2, data2);
3059
2942
  }, { type, data });
2943
+ },
2944
+ async screenshot(name) {
2945
+ return await page.screenshot({ path: `${name}.png` });
2946
+ },
2947
+ async close() {
2948
+ await browser.close();
3060
2949
  }
3061
2950
  };
3062
2951
  }
3063
2952
  async function waitForGameReady(page, timeout = 1e4) {
3064
- try {
3065
- await page.waitForFunction(() => {
3066
- return window.gameInstance && window.gameInstance.isReady;
3067
- }, null, { timeout });
3068
- } catch (e) {
3069
- await page.waitForSelector("canvas", { timeout });
3070
- }
2953
+ await page.waitForFunction(() => {
2954
+ return window.game && window.game.isRunning;
2955
+ }, { timeout });
3071
2956
  }
3072
2957
  async function captureGameState(page) {
3073
2958
  return await page.evaluate(() => {
3074
- if (window.gameInstance && window.gameInstance.getState) {
3075
- return window.gameInstance.getState();
3076
- }
3077
- return {};
2959
+ const game = window.game;
2960
+ return {
2961
+ time: game ? game.time : 0,
2962
+ entities: game && game.entities ? game.entities.length : 0
2963
+ };
3078
2964
  });
3079
2965
  }
3080
2966
 
3081
2967
  // src/e2e/network.ts
3082
- var CONDITIONS = {
3083
- "good": {
3084
- offline: false,
3085
- downloadThroughput: 10 * 1024 * 1024,
3086
- // 10 Mbps
3087
- uploadThroughput: 5 * 1024 * 1024,
3088
- // 5 Mbps
3089
- latency: 20
3090
- },
3091
- "slow": {
3092
- offline: false,
3093
- downloadThroughput: 500 * 1024,
3094
- // 500 Kbps
3095
- uploadThroughput: 500 * 1024,
3096
- latency: 400
3097
- },
3098
- "unstable": {
3099
- offline: false,
3100
- downloadThroughput: 1 * 1024 * 1024,
3101
- uploadThroughput: 1 * 1024 * 1024,
3102
- latency: 100
3103
- },
3104
- "offline": {
3105
- offline: true,
3106
- downloadThroughput: 0,
3107
- uploadThroughput: 0,
3108
- latency: 0
3109
- }
3110
- };
3111
2968
  function simulateNetworkCondition(condition) {
3112
- const config = CONDITIONS[condition];
3113
- return createCustomNetworkCondition(config.latency, 0, 0, config);
3114
- }
3115
- function createCustomNetworkCondition(latency, jitter = 0, packetLoss = 0, baseConfig) {
2969
+ switch (condition) {
2970
+ case "good":
2971
+ return { latency: 20, jitter: 5, packetLoss: 0, bandwidth: 10 * 1024 * 1024 };
2972
+ case "slow":
2973
+ return { latency: 150, jitter: 20, packetLoss: 0.01, bandwidth: 1 * 1024 * 1024 };
2974
+ case "unstable":
2975
+ return { latency: 100, jitter: 100, packetLoss: 0.05, bandwidth: 512 * 1024 };
2976
+ case "offline":
2977
+ return { latency: 0, jitter: 0, packetLoss: 1, bandwidth: 0 };
2978
+ case "custom":
2979
+ default:
2980
+ return { latency: 0, jitter: 0, packetLoss: 0, bandwidth: Infinity };
2981
+ }
2982
+ }
2983
+ function createCustomNetworkCondition(latency, jitter, packetLoss) {
3116
2984
  return {
3117
- async apply(page) {
3118
- const client = await page.context().newCDPSession(page);
3119
- await client.send("Network.enable");
3120
- await client.send("Network.emulateNetworkConditions", {
3121
- offline: baseConfig?.offline || false,
3122
- latency: latency + Math.random() * jitter,
3123
- downloadThroughput: baseConfig?.downloadThroughput || -1,
3124
- uploadThroughput: baseConfig?.uploadThroughput || -1
3125
- });
3126
- },
3127
- async clear(page) {
3128
- const client = await page.context().newCDPSession(page);
3129
- await client.send("Network.emulateNetworkConditions", {
3130
- offline: false,
3131
- latency: 0,
3132
- downloadThroughput: -1,
3133
- uploadThroughput: -1
3134
- });
3135
- }
2985
+ latency,
2986
+ jitter,
2987
+ packetLoss,
2988
+ bandwidth: Infinity
2989
+ // Default to unlimited unless specified
3136
2990
  };
3137
2991
  }
3138
- async function throttleBandwidth(page, bytesPerSecond) {
3139
- const simulator = createCustomNetworkCondition(0, 0, 0, {
3140
- offline: false,
3141
- latency: 0,
3142
- downloadThroughput: bytesPerSecond,
3143
- uploadThroughput: bytesPerSecond
3144
- });
3145
- await simulator.apply(page);
2992
+ function throttleBandwidth(bytesPerSecond) {
3146
2993
  }
3147
2994
 
3148
2995
  // src/e2e/visual.ts
3149
- import path from "path";
3150
- import fs from "fs/promises";
3151
- async function captureGameScreenshot(page, name, options = {}) {
3152
- const dir = options.dir || "__screenshots__";
3153
- const screenshotPath = path.join(dir, `${name}.png`);
3154
- await fs.mkdir(dir, { recursive: true });
3155
- return await page.screenshot({
3156
- path: screenshotPath,
3157
- fullPage: options.fullPage ?? false,
3158
- animations: "disabled",
3159
- caret: "hide"
3160
- });
3161
- }
3162
- async function compareScreenshots(baseline, current, threshold = 0.1) {
3163
- if (baseline.equals(current)) {
3164
- return { pixelDiff: 0, matched: true };
2996
+ async function captureGameScreenshot(page, name) {
2997
+ return await page.screenshot({ path: `${name}.png` });
2998
+ }
2999
+ function compareScreenshots(baseline, current, threshold = 0.01) {
3000
+ if (baseline.length !== current.length) {
3001
+ return { diffPercentage: 1 };
3002
+ }
3003
+ let diffPixels = 0;
3004
+ const totalPixels = baseline.length;
3005
+ for (let i = 0; i < baseline.length; i++) {
3006
+ if (baseline[i] !== current[i]) {
3007
+ diffPixels++;
3008
+ }
3165
3009
  }
3010
+ const diffPercentage = diffPixels / totalPixels;
3166
3011
  return {
3167
- pixelDiff: -1,
3168
- // Unknown magnitude
3169
- matched: false
3012
+ diffPercentage
3013
+ // Generating a diff image buffer would require a library like pixelmatch
3170
3014
  };
3171
3015
  }
3172
- function createVisualTestScenario(page, sceneName) {
3016
+ function createVisualTestScenario(sceneName) {
3173
3017
  return {
3174
- async capture(snapshotName) {
3175
- return await captureGameScreenshot(page, `${sceneName}-${snapshotName}`);
3176
- },
3177
- async compare(snapshotName, baselineDir) {
3178
- const name = `${sceneName}-${snapshotName}`;
3179
- const current = await captureGameScreenshot(page, name, { dir: "__screenshots__/current" });
3180
- try {
3181
- const baselinePath = path.join(baselineDir, `${name}.png`);
3182
- const baseline = await fs.readFile(baselinePath);
3183
- return await compareScreenshots(baseline, current);
3184
- } catch (e) {
3185
- return { pixelDiff: -1, matched: false };
3186
- }
3018
+ sceneName,
3019
+ setup: async () => {
3187
3020
  }
3188
3021
  };
3189
3022
  }
@@ -3201,6 +3034,7 @@ export {
3201
3034
  createBandwidthTestScenario,
3202
3035
  createBinaryStreamMock,
3203
3036
  createBinaryWriterMock,
3037
+ createBounds,
3204
3038
  createCombatTestContext,
3205
3039
  createControlledTimer,
3206
3040
  createCustomNetworkCondition,
@@ -3211,10 +3045,11 @@ export {
3211
3045
  createGameStateSnapshotFactory,
3212
3046
  createInputInjector,
3213
3047
  createItemEntityFactory,
3048
+ createMessageReaderMock,
3049
+ createMessageWriterMock,
3214
3050
  createMockAI,
3215
3051
  createMockAmmoItem,
3216
3052
  createMockArmorItem,
3217
- createMockAudioContext,
3218
3053
  createMockCamera,
3219
3054
  createMockCanvas,
3220
3055
  createMockCanvasContext2D,
@@ -3269,6 +3104,7 @@ export {
3269
3104
  createMonsterEntityFactory,
3270
3105
  createMultiplayerTestScenario,
3271
3106
  createNetChanMock,
3107
+ createPacketMock,
3272
3108
  createPhysicsTestContext,
3273
3109
  createPlayerEntityFactory,
3274
3110
  createPlayerStateFactory,
@@ -3277,8 +3113,12 @@ export {
3277
3113
  createServerSnapshot,
3278
3114
  createSpawnTestContext,
3279
3115
  createStorageTestScenario,
3116
+ createSurfaceMock,
3280
3117
  createTestContext,
3118
+ createTraceMock,
3119
+ createTransform,
3281
3120
  createTriggerEntityFactory,
3121
+ createVector3,
3282
3122
  createViewTestScenario,
3283
3123
  createVisualTestScenario,
3284
3124
  intersects,
@@ -3292,6 +3132,7 @@ export {
3292
3132
  makePlane,
3293
3133
  measureSnapshotSize,
3294
3134
  mockMonsterAttacks,
3135
+ randomVector3,
3295
3136
  serializeUserInfo,
3296
3137
  setupBrowserEnvironment,
3297
3138
  setupMockAudioContext,
@@ -3300,7 +3141,6 @@ export {
3300
3141
  simulateBandwidthLimit,
3301
3142
  simulateCameraMovement,
3302
3143
  simulateFrames,
3303
- simulateFramesWithMock,
3304
3144
  simulateHandshake,
3305
3145
  simulateNetworkCondition,
3306
3146
  simulatePlayerInput,
@@ -3313,6 +3153,7 @@ export {
3313
3153
  stairTrace,
3314
3154
  teardownBrowserEnvironment,
3315
3155
  teardownMockAudioContext,
3156
+ teardownNodeEnvironment,
3316
3157
  throttleBandwidth,
3317
3158
  verifySnapshotConsistency,
3318
3159
  waitForGameReady