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.
- package/package.json +6 -4
- package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/engine/dist/types/render/__mocks__/frame.d.ts +31 -0
- package/packages/engine/dist/types/render/__mocks__/frame.d.ts.map +1 -0
- package/packages/test-utils/dist/index.cjs +404 -555
- package/packages/test-utils/dist/index.cjs.map +1 -1
- package/packages/test-utils/dist/index.d.cts +174 -109
- package/packages/test-utils/dist/index.d.ts +174 -109
- package/packages/test-utils/dist/index.js +389 -548
- package/packages/test-utils/dist/index.js.map +1 -1
|
@@ -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(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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,
|
|
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
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
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
|
-
|
|
1938
|
-
|
|
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
|
|
2020
|
+
return ctx;
|
|
1941
2021
|
}
|
|
1942
2022
|
function captureCanvasDrawCalls(context) {
|
|
1943
|
-
const
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
"
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
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
|
|
2033
|
+
}
|
|
2034
|
+
return calls;
|
|
1973
2035
|
}
|
|
1974
2036
|
function createMockImageData(width, height, fillColor) {
|
|
1975
|
-
|
|
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
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
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
|
|
2058
|
+
return new ImageData2(data, width, height);
|
|
1986
2059
|
}
|
|
1987
|
-
function createMockImage(width, height, src) {
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
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
|
|
2150
|
+
let lastId = 0;
|
|
2063
2151
|
let currentTime = 0;
|
|
2064
2152
|
const originalRAF = global.requestAnimationFrame;
|
|
2065
2153
|
const originalCancelRAF = global.cancelAnimationFrame;
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
callbacks.push({ id, callback });
|
|
2069
|
-
return
|
|
2154
|
+
global.requestAnimationFrame = (callback) => {
|
|
2155
|
+
lastId++;
|
|
2156
|
+
callbacks.push({ id: lastId, callback });
|
|
2157
|
+
return lastId;
|
|
2070
2158
|
};
|
|
2071
|
-
|
|
2159
|
+
global.cancelAnimationFrame = (id) => {
|
|
2072
2160
|
callbacks = callbacks.filter((cb) => cb.id !== id);
|
|
2073
2161
|
};
|
|
2074
|
-
|
|
2075
|
-
tick(
|
|
2076
|
-
if (
|
|
2077
|
-
|
|
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((
|
|
2084
|
-
callback(currentTime);
|
|
2085
|
-
});
|
|
2168
|
+
currentCallbacks.forEach((cb) => cb.callback(currentTime));
|
|
2086
2169
|
},
|
|
2087
|
-
advance(
|
|
2088
|
-
|
|
2170
|
+
advance(ms) {
|
|
2171
|
+
currentTime += ms;
|
|
2172
|
+
this.tick(currentTime);
|
|
2089
2173
|
},
|
|
2090
2174
|
getCallbacks() {
|
|
2091
|
-
return callbacks
|
|
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
|
|
2123
|
-
const
|
|
2124
|
-
now: () =>
|
|
2180
|
+
let now = startTime;
|
|
2181
|
+
const mockPerformance = {
|
|
2182
|
+
now: () => now,
|
|
2125
2183
|
timeOrigin: startTime,
|
|
2126
2184
|
timing: {
|
|
2127
2185
|
navigationStart: startTime
|
|
2128
2186
|
},
|
|
2129
|
-
|
|
2187
|
+
mark: (_name) => {
|
|
2130
2188
|
},
|
|
2131
|
-
|
|
2132
|
-
},
|
|
2133
|
-
clearResourceTimings: () => {
|
|
2189
|
+
measure: (_name, _start, _end) => {
|
|
2134
2190
|
},
|
|
2135
2191
|
getEntries: () => [],
|
|
2136
|
-
getEntriesByName: () => [],
|
|
2137
|
-
getEntriesByType: () => [],
|
|
2138
|
-
|
|
2192
|
+
getEntriesByName: (_name) => [],
|
|
2193
|
+
getEntriesByType: (_type) => [],
|
|
2194
|
+
clearMarks: (_name) => {
|
|
2139
2195
|
},
|
|
2140
|
-
|
|
2196
|
+
clearMeasures: (_name) => {
|
|
2141
2197
|
},
|
|
2142
|
-
|
|
2143
|
-
},
|
|
2144
|
-
toJSON: () => ({}),
|
|
2145
|
-
addEventListener: () => {
|
|
2198
|
+
clearResourceTimings: () => {
|
|
2146
2199
|
},
|
|
2147
|
-
|
|
2200
|
+
setResourceTimingBufferSize: (_maxSize) => {
|
|
2148
2201
|
},
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
mockPerf.advance = (deltaMs) => {
|
|
2152
|
-
currentTime += deltaMs;
|
|
2202
|
+
onresourcetimingbufferfull: null,
|
|
2203
|
+
toJSON: () => ({})
|
|
2153
2204
|
};
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
}
|
|
2157
|
-
return
|
|
2205
|
+
if (typeof global.performance === "undefined") {
|
|
2206
|
+
global.performance = mockPerformance;
|
|
2207
|
+
}
|
|
2208
|
+
return mockPerformance;
|
|
2158
2209
|
}
|
|
2159
2210
|
function createControlledTimer() {
|
|
2160
|
-
|
|
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
|
-
|
|
2189
|
-
this.advanceBy(0);
|
|
2213
|
+
advanceBy: (ms) => {
|
|
2190
2214
|
},
|
|
2191
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
2308
|
-
|
|
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) =>
|
|
2312
|
-
setItem: (key, value) =>
|
|
2313
|
-
removeItem: (key) =>
|
|
2314
|
-
clear: () =>
|
|
2315
|
-
key: (index) => Array.from(
|
|
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
|
|
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
|
|
2309
|
+
return createStorageMock(initialData);
|
|
2323
2310
|
}
|
|
2324
|
-
function createMockIndexedDB() {
|
|
2325
|
-
|
|
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
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
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
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
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
|
-
|
|
2480
|
-
|
|
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
|
|
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
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
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
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
2932
|
+
browser,
|
|
2933
|
+
async navigate(url) {
|
|
2934
|
+
await page.goto(url);
|
|
2935
|
+
},
|
|
2936
|
+
async waitForGame() {
|
|
2937
|
+
await waitForGameReady(page);
|
|
3055
2938
|
},
|
|
3056
|
-
|
|
2939
|
+
async injectInput(type, data) {
|
|
3057
2940
|
await page.evaluate(({ type: type2, data: data2 }) => {
|
|
3058
|
-
|
|
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
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
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
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
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
|
-
|
|
3113
|
-
|
|
3114
|
-
}
|
|
3115
|
-
|
|
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
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
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
|
-
|
|
3168
|
-
//
|
|
3169
|
-
matched: false
|
|
3012
|
+
diffPercentage
|
|
3013
|
+
// Generating a diff image buffer would require a library like pixelmatch
|
|
3170
3014
|
};
|
|
3171
3015
|
}
|
|
3172
|
-
function createVisualTestScenario(
|
|
3016
|
+
function createVisualTestScenario(sceneName) {
|
|
3173
3017
|
return {
|
|
3174
|
-
|
|
3175
|
-
|
|
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
|