quake2ts 0.0.511 → 0.0.513
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 +1 -1
- package/packages/client/dist/browser/index.global.js +17 -17
- package/packages/client/dist/browser/index.global.js.map +1 -1
- package/packages/client/dist/cjs/index.cjs +168 -38
- package/packages/client/dist/cjs/index.cjs.map +1 -1
- package/packages/client/dist/esm/index.js +168 -38
- package/packages/client/dist/esm/index.js.map +1 -1
- package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/client/dist/types/demo/handler.d.ts +0 -1
- package/packages/client/dist/types/demo/handler.d.ts.map +1 -1
- package/packages/engine/dist/browser/index.global.js +17 -17
- package/packages/engine/dist/browser/index.global.js.map +1 -1
- package/packages/engine/dist/cjs/index.cjs +461 -9
- package/packages/engine/dist/cjs/index.cjs.map +1 -1
- package/packages/engine/dist/esm/index.js +456 -9
- package/packages/engine/dist/esm/index.js.map +1 -1
- package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/engine/dist/types/assets/manager.d.ts +3 -0
- package/packages/engine/dist/types/assets/manager.d.ts.map +1 -1
- package/packages/engine/dist/types/assets/pakWriter.d.ts +8 -0
- package/packages/engine/dist/types/assets/pakWriter.d.ts.map +1 -0
- package/packages/engine/dist/types/assets/resourceTracker.d.ts +33 -0
- package/packages/engine/dist/types/assets/resourceTracker.d.ts.map +1 -0
- package/packages/engine/dist/types/demo/clipper.d.ts +22 -0
- package/packages/engine/dist/types/demo/clipper.d.ts.map +1 -0
- package/packages/engine/dist/types/demo/delta.d.ts +3 -0
- package/packages/engine/dist/types/demo/delta.d.ts.map +1 -0
- package/packages/engine/dist/types/demo/playback.d.ts +40 -0
- package/packages/engine/dist/types/demo/playback.d.ts.map +1 -1
- package/packages/engine/dist/types/index.d.ts +4 -0
- package/packages/engine/dist/types/index.d.ts.map +1 -1
- package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
|
@@ -1802,7 +1802,7 @@ var PakArchive = class _PakArchive {
|
|
|
1802
1802
|
if (dirOffset < HEADER_SIZE) {
|
|
1803
1803
|
throw new PakParseError(`Invalid directory offset: ${dirOffset}`);
|
|
1804
1804
|
}
|
|
1805
|
-
if (dirLength
|
|
1805
|
+
if (dirLength < 0 || dirLength % DIRECTORY_ENTRY_SIZE !== 0) {
|
|
1806
1806
|
throw new PakParseError(`Invalid directory length: ${dirLength}`);
|
|
1807
1807
|
}
|
|
1808
1808
|
const dirEnd = dirOffset + dirLength;
|
|
@@ -1856,6 +1856,136 @@ function calculatePakChecksum(buffer) {
|
|
|
1856
1856
|
return crc32(new Uint8Array(buffer));
|
|
1857
1857
|
}
|
|
1858
1858
|
|
|
1859
|
+
// src/assets/pakWriter.ts
|
|
1860
|
+
var HEADER_SIZE2 = 12;
|
|
1861
|
+
var DIRECTORY_ENTRY_SIZE2 = 64;
|
|
1862
|
+
var PakWriter = class _PakWriter {
|
|
1863
|
+
constructor() {
|
|
1864
|
+
this.files = /* @__PURE__ */ new Map();
|
|
1865
|
+
}
|
|
1866
|
+
addFile(path, data) {
|
|
1867
|
+
const normalized = normalizePath(path);
|
|
1868
|
+
if (!normalized) {
|
|
1869
|
+
throw new Error(`Invalid path: ${path}`);
|
|
1870
|
+
}
|
|
1871
|
+
if (normalized.length > 56) {
|
|
1872
|
+
throw new Error(`Path too long: ${path} (max 56 chars)`);
|
|
1873
|
+
}
|
|
1874
|
+
this.files.set(normalized, data);
|
|
1875
|
+
}
|
|
1876
|
+
removeFile(path) {
|
|
1877
|
+
const normalized = normalizePath(path);
|
|
1878
|
+
return this.files.delete(normalized);
|
|
1879
|
+
}
|
|
1880
|
+
build() {
|
|
1881
|
+
let currentOffset = HEADER_SIZE2;
|
|
1882
|
+
const sortedPaths = Array.from(this.files.keys()).sort();
|
|
1883
|
+
const directorySize = sortedPaths.length * DIRECTORY_ENTRY_SIZE2;
|
|
1884
|
+
let fileDataSize = 0;
|
|
1885
|
+
for (const path of sortedPaths) {
|
|
1886
|
+
fileDataSize += this.files.get(path).byteLength;
|
|
1887
|
+
}
|
|
1888
|
+
const totalSize = HEADER_SIZE2 + fileDataSize + directorySize;
|
|
1889
|
+
const buffer = new Uint8Array(totalSize);
|
|
1890
|
+
const view = new DataView(buffer.buffer);
|
|
1891
|
+
view.setUint8(0, "P".charCodeAt(0));
|
|
1892
|
+
view.setUint8(1, "A".charCodeAt(0));
|
|
1893
|
+
view.setUint8(2, "C".charCodeAt(0));
|
|
1894
|
+
view.setUint8(3, "K".charCodeAt(0));
|
|
1895
|
+
const directoryOffset = HEADER_SIZE2 + fileDataSize;
|
|
1896
|
+
view.setInt32(4, directoryOffset, true);
|
|
1897
|
+
view.setInt32(8, directorySize, true);
|
|
1898
|
+
let fileOffset = HEADER_SIZE2;
|
|
1899
|
+
let dirEntryOffset = directoryOffset;
|
|
1900
|
+
for (const path of sortedPaths) {
|
|
1901
|
+
const data = this.files.get(path);
|
|
1902
|
+
buffer.set(data, fileOffset);
|
|
1903
|
+
for (let i = 0; i < 56; i++) {
|
|
1904
|
+
if (i < path.length) {
|
|
1905
|
+
view.setUint8(dirEntryOffset + i, path.charCodeAt(i));
|
|
1906
|
+
} else {
|
|
1907
|
+
view.setUint8(dirEntryOffset + i, 0);
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
view.setInt32(dirEntryOffset + 56, fileOffset, true);
|
|
1911
|
+
view.setInt32(dirEntryOffset + 60, data.byteLength, true);
|
|
1912
|
+
fileOffset += data.byteLength;
|
|
1913
|
+
dirEntryOffset += DIRECTORY_ENTRY_SIZE2;
|
|
1914
|
+
}
|
|
1915
|
+
return buffer;
|
|
1916
|
+
}
|
|
1917
|
+
static buildFromEntries(entries) {
|
|
1918
|
+
const writer = new _PakWriter();
|
|
1919
|
+
for (const [path, data] of entries) {
|
|
1920
|
+
writer.addFile(path, data);
|
|
1921
|
+
}
|
|
1922
|
+
return writer.build();
|
|
1923
|
+
}
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1926
|
+
// src/assets/resourceTracker.ts
|
|
1927
|
+
var ResourceType = /* @__PURE__ */ ((ResourceType2) => {
|
|
1928
|
+
ResourceType2["Texture"] = "texture";
|
|
1929
|
+
ResourceType2["Sound"] = "sound";
|
|
1930
|
+
ResourceType2["Model"] = "model";
|
|
1931
|
+
ResourceType2["Map"] = "map";
|
|
1932
|
+
ResourceType2["Sprite"] = "sprite";
|
|
1933
|
+
ResourceType2["ConfigString"] = "configString";
|
|
1934
|
+
return ResourceType2;
|
|
1935
|
+
})(ResourceType || {});
|
|
1936
|
+
var ResourceLoadTracker = class {
|
|
1937
|
+
constructor() {
|
|
1938
|
+
this.tracking = false;
|
|
1939
|
+
this.entries = [];
|
|
1940
|
+
this.currentFrame = 0;
|
|
1941
|
+
this.currentTime = 0;
|
|
1942
|
+
}
|
|
1943
|
+
startTracking() {
|
|
1944
|
+
this.tracking = true;
|
|
1945
|
+
this.entries = [];
|
|
1946
|
+
}
|
|
1947
|
+
stopTracking() {
|
|
1948
|
+
this.tracking = false;
|
|
1949
|
+
const log = {
|
|
1950
|
+
byFrame: /* @__PURE__ */ new Map(),
|
|
1951
|
+
byTime: /* @__PURE__ */ new Map(),
|
|
1952
|
+
uniqueResources: /* @__PURE__ */ new Map()
|
|
1953
|
+
};
|
|
1954
|
+
for (const entry of this.entries) {
|
|
1955
|
+
if (!log.byFrame.has(entry.frame)) {
|
|
1956
|
+
log.byFrame.set(entry.frame, []);
|
|
1957
|
+
}
|
|
1958
|
+
log.byFrame.get(entry.frame).push(entry);
|
|
1959
|
+
if (!log.byTime.has(entry.timestamp)) {
|
|
1960
|
+
log.byTime.set(entry.timestamp, []);
|
|
1961
|
+
}
|
|
1962
|
+
log.byTime.get(entry.timestamp).push(entry);
|
|
1963
|
+
const key = `${entry.type}:${entry.path}`;
|
|
1964
|
+
if (!log.uniqueResources.has(key)) {
|
|
1965
|
+
log.uniqueResources.set(key, entry);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
return log;
|
|
1969
|
+
}
|
|
1970
|
+
recordLoad(type, path, size, pakSource) {
|
|
1971
|
+
if (!this.tracking) return;
|
|
1972
|
+
this.entries.push({
|
|
1973
|
+
type,
|
|
1974
|
+
path,
|
|
1975
|
+
timestamp: this.currentTime,
|
|
1976
|
+
frame: this.currentFrame,
|
|
1977
|
+
size,
|
|
1978
|
+
pakSource
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
setCurrentFrame(frame) {
|
|
1982
|
+
this.currentFrame = frame;
|
|
1983
|
+
}
|
|
1984
|
+
setCurrentTime(time) {
|
|
1985
|
+
this.currentTime = time;
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
|
|
1859
1989
|
// src/assets/vfs.ts
|
|
1860
1990
|
var VirtualFileSystem = class {
|
|
1861
1991
|
constructor(archives = []) {
|
|
@@ -2380,7 +2510,7 @@ function wireFileInput(input, handler) {
|
|
|
2380
2510
|
var BSP_MAGIC = "IBSP";
|
|
2381
2511
|
var BSP_VERSION = 38;
|
|
2382
2512
|
var HEADER_LUMPS = 19;
|
|
2383
|
-
var
|
|
2513
|
+
var HEADER_SIZE3 = 4 + 4 + HEADER_LUMPS * 8;
|
|
2384
2514
|
var BspParseError = class extends Error {
|
|
2385
2515
|
};
|
|
2386
2516
|
var BspLoader = class {
|
|
@@ -2395,7 +2525,7 @@ var BspLoader = class {
|
|
|
2395
2525
|
}
|
|
2396
2526
|
};
|
|
2397
2527
|
function parseBsp(buffer) {
|
|
2398
|
-
if (buffer.byteLength <
|
|
2528
|
+
if (buffer.byteLength < HEADER_SIZE3) {
|
|
2399
2529
|
throw new BspParseError("BSP too small to contain header");
|
|
2400
2530
|
}
|
|
2401
2531
|
const view = new DataView(buffer);
|
|
@@ -2848,7 +2978,7 @@ function createFaceLightmap(face, lightMaps, info) {
|
|
|
2848
2978
|
// src/assets/md2.ts
|
|
2849
2979
|
var MD2_MAGIC = 844121161;
|
|
2850
2980
|
var MD2_VERSION = 8;
|
|
2851
|
-
var
|
|
2981
|
+
var HEADER_SIZE4 = 17 * 4;
|
|
2852
2982
|
var MD2_NORMALS = [
|
|
2853
2983
|
{ x: -0.525731, y: 0, z: 0.850651 },
|
|
2854
2984
|
{ x: -0.442863, y: 0.238856, z: 0.864188 },
|
|
@@ -3046,12 +3176,12 @@ function readCString2(view, offset, maxLength) {
|
|
|
3046
3176
|
}
|
|
3047
3177
|
function validateSection(buffer, offset, length, label) {
|
|
3048
3178
|
if (length === 0) return;
|
|
3049
|
-
if (offset <
|
|
3179
|
+
if (offset < HEADER_SIZE4 || offset + length > buffer.byteLength) {
|
|
3050
3180
|
throw new Md2ParseError(`${label} section is out of bounds`);
|
|
3051
3181
|
}
|
|
3052
3182
|
}
|
|
3053
3183
|
function parseHeader(buffer) {
|
|
3054
|
-
if (buffer.byteLength <
|
|
3184
|
+
if (buffer.byteLength < HEADER_SIZE4) {
|
|
3055
3185
|
throw new Md2ParseError("MD2 buffer too small to contain header");
|
|
3056
3186
|
}
|
|
3057
3187
|
const view = new DataView(buffer);
|
|
@@ -3488,7 +3618,7 @@ var Md3Loader = class {
|
|
|
3488
3618
|
var IDSPRITEHEADER = 844317769;
|
|
3489
3619
|
var SPRITE_VERSION = 2;
|
|
3490
3620
|
var MAX_SKINNAME = 64;
|
|
3491
|
-
var
|
|
3621
|
+
var HEADER_SIZE5 = 12;
|
|
3492
3622
|
var SpriteParseError = class extends Error {
|
|
3493
3623
|
};
|
|
3494
3624
|
function readCString3(view, offset, maxLength) {
|
|
@@ -3501,7 +3631,7 @@ function readCString3(view, offset, maxLength) {
|
|
|
3501
3631
|
return String.fromCharCode(...chars);
|
|
3502
3632
|
}
|
|
3503
3633
|
function parseSprite(buffer) {
|
|
3504
|
-
if (buffer.byteLength <
|
|
3634
|
+
if (buffer.byteLength < HEADER_SIZE5) {
|
|
3505
3635
|
throw new SpriteParseError("Sprite buffer too small to contain header");
|
|
3506
3636
|
}
|
|
3507
3637
|
const view = new DataView(buffer);
|
|
@@ -3516,7 +3646,7 @@ function parseSprite(buffer) {
|
|
|
3516
3646
|
}
|
|
3517
3647
|
const frames = [];
|
|
3518
3648
|
const frameSize = 16 + MAX_SKINNAME;
|
|
3519
|
-
let offset =
|
|
3649
|
+
let offset = HEADER_SIZE5;
|
|
3520
3650
|
for (let i = 0; i < numFrames; i += 1) {
|
|
3521
3651
|
if (offset + frameSize > buffer.byteLength) {
|
|
3522
3652
|
throw new SpriteParseError("Sprite frame data exceeds buffer length");
|
|
@@ -4232,6 +4362,7 @@ var AssetManager = class {
|
|
|
4232
4362
|
this.textures = new TextureCache({ capacity: options.textureCacheCapacity ?? 128 });
|
|
4233
4363
|
this.audio = new AudioRegistry(vfs, { cacheSize: options.audioCacheSize ?? 64 });
|
|
4234
4364
|
this.dependencyTracker = options.dependencyTracker ?? new AssetDependencyTracker();
|
|
4365
|
+
this.resourceTracker = options.resourceTracker;
|
|
4235
4366
|
this.md2 = new Md2Loader(vfs);
|
|
4236
4367
|
this.md3 = new Md3Loader(vfs);
|
|
4237
4368
|
this.sprite = new SpriteLoader(vfs);
|
|
@@ -4268,6 +4399,9 @@ var AssetManager = class {
|
|
|
4268
4399
|
this.dependencyTracker.markLoaded(key);
|
|
4269
4400
|
}
|
|
4270
4401
|
async loadTexture(path) {
|
|
4402
|
+
if (this.resourceTracker) {
|
|
4403
|
+
this.resourceTracker.recordLoad("texture" /* Texture */, path);
|
|
4404
|
+
}
|
|
4271
4405
|
const cached = this.textures.get(path);
|
|
4272
4406
|
if (cached) return cached;
|
|
4273
4407
|
const buffer = await this.vfs.readFile(path);
|
|
@@ -4286,6 +4420,9 @@ var AssetManager = class {
|
|
|
4286
4420
|
return texture;
|
|
4287
4421
|
}
|
|
4288
4422
|
async loadSound(path) {
|
|
4423
|
+
if (this.resourceTracker) {
|
|
4424
|
+
this.resourceTracker.recordLoad("sound" /* Sound */, path);
|
|
4425
|
+
}
|
|
4289
4426
|
const audio = await this.audio.load(path);
|
|
4290
4427
|
const key = this.makeKey("sound", path);
|
|
4291
4428
|
this.dependencyTracker.register(key);
|
|
@@ -4293,6 +4430,9 @@ var AssetManager = class {
|
|
|
4293
4430
|
return audio;
|
|
4294
4431
|
}
|
|
4295
4432
|
async loadMd2Model(path, textureDependencies = []) {
|
|
4433
|
+
if (this.resourceTracker) {
|
|
4434
|
+
this.resourceTracker.recordLoad("model" /* Model */, path);
|
|
4435
|
+
}
|
|
4296
4436
|
const modelKey = this.makeKey("model", path);
|
|
4297
4437
|
const dependencyKeys = textureDependencies.map((dep) => this.makeKey("texture", dep));
|
|
4298
4438
|
this.dependencyTracker.register(modelKey, dependencyKeys);
|
|
@@ -4305,9 +4445,15 @@ var AssetManager = class {
|
|
|
4305
4445
|
return model;
|
|
4306
4446
|
}
|
|
4307
4447
|
getMd2Model(path) {
|
|
4448
|
+
if (this.resourceTracker) {
|
|
4449
|
+
this.resourceTracker.recordLoad("model" /* Model */, path);
|
|
4450
|
+
}
|
|
4308
4451
|
return this.md2.get(path);
|
|
4309
4452
|
}
|
|
4310
4453
|
async loadMd3Model(path, textureDependencies = []) {
|
|
4454
|
+
if (this.resourceTracker) {
|
|
4455
|
+
this.resourceTracker.recordLoad("model" /* Model */, path);
|
|
4456
|
+
}
|
|
4311
4457
|
const modelKey = this.makeKey("model", path);
|
|
4312
4458
|
const dependencyKeys = textureDependencies.map((dep) => this.makeKey("texture", dep));
|
|
4313
4459
|
this.dependencyTracker.register(modelKey, dependencyKeys);
|
|
@@ -4320,9 +4466,15 @@ var AssetManager = class {
|
|
|
4320
4466
|
return model;
|
|
4321
4467
|
}
|
|
4322
4468
|
getMd3Model(path) {
|
|
4469
|
+
if (this.resourceTracker) {
|
|
4470
|
+
this.resourceTracker.recordLoad("model" /* Model */, path);
|
|
4471
|
+
}
|
|
4323
4472
|
return this.md3.get(path);
|
|
4324
4473
|
}
|
|
4325
4474
|
async loadSprite(path) {
|
|
4475
|
+
if (this.resourceTracker) {
|
|
4476
|
+
this.resourceTracker.recordLoad("sprite" /* Sprite */, path);
|
|
4477
|
+
}
|
|
4326
4478
|
const spriteKey = this.makeKey("sprite", path);
|
|
4327
4479
|
this.dependencyTracker.register(spriteKey);
|
|
4328
4480
|
const sprite = await this.sprite.load(path);
|
|
@@ -4330,6 +4482,9 @@ var AssetManager = class {
|
|
|
4330
4482
|
return sprite;
|
|
4331
4483
|
}
|
|
4332
4484
|
async loadMap(path) {
|
|
4485
|
+
if (this.resourceTracker) {
|
|
4486
|
+
this.resourceTracker.recordLoad("map" /* Map */, path);
|
|
4487
|
+
}
|
|
4333
4488
|
const mapKey = this.makeKey("map", path);
|
|
4334
4489
|
if (this.maps.has(path)) {
|
|
4335
4490
|
return this.maps.get(path);
|
|
@@ -13397,6 +13552,8 @@ var DemoPlaybackController = class {
|
|
|
13397
13552
|
this.cachedStatistics = null;
|
|
13398
13553
|
this.cachedPlayerStats = null;
|
|
13399
13554
|
this.cachedWeaponStats = null;
|
|
13555
|
+
// Resource Tracking
|
|
13556
|
+
this.tracker = null;
|
|
13400
13557
|
// Camera State
|
|
13401
13558
|
this.cameraMode = 0 /* FirstPerson */;
|
|
13402
13559
|
this.thirdPersonDistance = 80;
|
|
@@ -13510,6 +13667,41 @@ var DemoPlaybackController = class {
|
|
|
13510
13667
|
seekToFrame(frameIndex) {
|
|
13511
13668
|
this.seek(frameIndex);
|
|
13512
13669
|
}
|
|
13670
|
+
frameToTime(frame) {
|
|
13671
|
+
return frame * this.frameDuration / 1e3;
|
|
13672
|
+
}
|
|
13673
|
+
timeToFrame(seconds) {
|
|
13674
|
+
return Math.floor(seconds * 1e3 / this.frameDuration);
|
|
13675
|
+
}
|
|
13676
|
+
playFrom(offset) {
|
|
13677
|
+
if (offset.type === "frame") {
|
|
13678
|
+
this.seek(offset.frame);
|
|
13679
|
+
} else {
|
|
13680
|
+
this.seekToTime(offset.seconds);
|
|
13681
|
+
}
|
|
13682
|
+
this.play();
|
|
13683
|
+
}
|
|
13684
|
+
playRange(start, end) {
|
|
13685
|
+
this.playFrom(start);
|
|
13686
|
+
const endFrame = end.type === "frame" ? end.frame : this.timeToFrame(end.seconds);
|
|
13687
|
+
const originalOnFrameUpdate = this.callbacks?.onFrameUpdate;
|
|
13688
|
+
const rangeCallback = {
|
|
13689
|
+
...this.callbacks,
|
|
13690
|
+
onFrameUpdate: (frame) => {
|
|
13691
|
+
if (originalOnFrameUpdate) originalOnFrameUpdate(frame);
|
|
13692
|
+
if (this.currentFrameIndex >= endFrame) {
|
|
13693
|
+
this.pause();
|
|
13694
|
+
if (this.callbacks === rangeCallback) {
|
|
13695
|
+
this.setCallbacks({ ...this.callbacks, onFrameUpdate: originalOnFrameUpdate });
|
|
13696
|
+
}
|
|
13697
|
+
if (this.callbacks?.onPlaybackComplete) {
|
|
13698
|
+
this.callbacks.onPlaybackComplete();
|
|
13699
|
+
}
|
|
13700
|
+
}
|
|
13701
|
+
}
|
|
13702
|
+
};
|
|
13703
|
+
this.setCallbacks(rangeCallback);
|
|
13704
|
+
}
|
|
13513
13705
|
/**
|
|
13514
13706
|
* Seeks to a specific frame number.
|
|
13515
13707
|
*/
|
|
@@ -13587,6 +13779,10 @@ var DemoPlaybackController = class {
|
|
|
13587
13779
|
return false;
|
|
13588
13780
|
}
|
|
13589
13781
|
this.currentFrameIndex++;
|
|
13782
|
+
if (this.tracker) {
|
|
13783
|
+
this.tracker.setCurrentFrame(this.currentFrameIndex);
|
|
13784
|
+
this.tracker.setCurrentTime(this.getCurrentTime());
|
|
13785
|
+
}
|
|
13590
13786
|
try {
|
|
13591
13787
|
const proxyHandler = {
|
|
13592
13788
|
...this.handler,
|
|
@@ -13821,6 +14017,93 @@ var DemoPlaybackController = class {
|
|
|
13821
14017
|
setThirdPersonOffset(offset) {
|
|
13822
14018
|
this.thirdPersonOffset = offset;
|
|
13823
14019
|
}
|
|
14020
|
+
async playWithTracking(tracker, options = {}) {
|
|
14021
|
+
this.tracker = tracker;
|
|
14022
|
+
tracker.startTracking();
|
|
14023
|
+
if (options.fastForward) {
|
|
14024
|
+
try {
|
|
14025
|
+
if (this.state === 0 /* Stopped */ && this.reader) {
|
|
14026
|
+
}
|
|
14027
|
+
this.transitionState(1 /* Playing */);
|
|
14028
|
+
const CHUNK_SIZE = 100;
|
|
14029
|
+
const processChunk = async () => {
|
|
14030
|
+
if (this.state !== 1 /* Playing */) {
|
|
14031
|
+
throw new Error("Playback stopped unexpectedly during fast forward");
|
|
14032
|
+
}
|
|
14033
|
+
let count = 0;
|
|
14034
|
+
while (count < CHUNK_SIZE) {
|
|
14035
|
+
if (!this.processNextFrame()) {
|
|
14036
|
+
const log = tracker.stopTracking();
|
|
14037
|
+
this.tracker = null;
|
|
14038
|
+
if (this.callbacks?.onPlaybackComplete) this.callbacks.onPlaybackComplete();
|
|
14039
|
+
return log;
|
|
14040
|
+
}
|
|
14041
|
+
count++;
|
|
14042
|
+
}
|
|
14043
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
14044
|
+
return processChunk();
|
|
14045
|
+
};
|
|
14046
|
+
return await processChunk();
|
|
14047
|
+
} catch (e) {
|
|
14048
|
+
tracker.stopTracking();
|
|
14049
|
+
this.tracker = null;
|
|
14050
|
+
throw e;
|
|
14051
|
+
}
|
|
14052
|
+
} else {
|
|
14053
|
+
return new Promise((resolve, reject) => {
|
|
14054
|
+
const originalComplete = this.callbacks?.onPlaybackComplete;
|
|
14055
|
+
const originalError = this.callbacks?.onPlaybackError;
|
|
14056
|
+
const cleanup = () => {
|
|
14057
|
+
this.setCallbacks({ ...this.callbacks, onPlaybackComplete: originalComplete, onPlaybackError: originalError });
|
|
14058
|
+
this.tracker = null;
|
|
14059
|
+
};
|
|
14060
|
+
this.setCallbacks({
|
|
14061
|
+
...this.callbacks,
|
|
14062
|
+
onPlaybackComplete: () => {
|
|
14063
|
+
const log = tracker.stopTracking();
|
|
14064
|
+
if (originalComplete) originalComplete();
|
|
14065
|
+
cleanup();
|
|
14066
|
+
resolve(log);
|
|
14067
|
+
},
|
|
14068
|
+
onPlaybackError: (err2) => {
|
|
14069
|
+
tracker.stopTracking();
|
|
14070
|
+
if (originalError) originalError(err2);
|
|
14071
|
+
cleanup();
|
|
14072
|
+
reject(err2);
|
|
14073
|
+
}
|
|
14074
|
+
});
|
|
14075
|
+
this.play();
|
|
14076
|
+
});
|
|
14077
|
+
}
|
|
14078
|
+
}
|
|
14079
|
+
async playRangeWithTracking(start, end, tracker) {
|
|
14080
|
+
this.tracker = tracker;
|
|
14081
|
+
tracker.startTracking();
|
|
14082
|
+
return new Promise((resolve, reject) => {
|
|
14083
|
+
const originalComplete = this.callbacks?.onPlaybackComplete;
|
|
14084
|
+
const originalError = this.callbacks?.onPlaybackError;
|
|
14085
|
+
const cleanup = () => {
|
|
14086
|
+
this.setCallbacks({ ...this.callbacks, onPlaybackComplete: originalComplete, onPlaybackError: originalError });
|
|
14087
|
+
this.tracker = null;
|
|
14088
|
+
};
|
|
14089
|
+
this.setCallbacks({
|
|
14090
|
+
...this.callbacks,
|
|
14091
|
+
onPlaybackComplete: () => {
|
|
14092
|
+
const log = tracker.stopTracking();
|
|
14093
|
+
if (originalComplete) originalComplete();
|
|
14094
|
+
cleanup();
|
|
14095
|
+
resolve(log);
|
|
14096
|
+
},
|
|
14097
|
+
onPlaybackError: (err2) => {
|
|
14098
|
+
tracker.stopTracking();
|
|
14099
|
+
if (originalError) originalError(err2);
|
|
14100
|
+
cleanup();
|
|
14101
|
+
reject(err2);
|
|
14102
|
+
}
|
|
14103
|
+
});
|
|
14104
|
+
this.playRange(start, end);
|
|
14105
|
+
});
|
|
14106
|
+
}
|
|
13824
14107
|
};
|
|
13825
14108
|
|
|
13826
14109
|
// src/demo/recorder.ts
|
|
@@ -13896,6 +14179,165 @@ var DemoValidator = class {
|
|
|
13896
14179
|
}
|
|
13897
14180
|
};
|
|
13898
14181
|
|
|
14182
|
+
// src/demo/delta.ts
|
|
14183
|
+
function applyEntityDelta(to, from) {
|
|
14184
|
+
const bits = from.bits;
|
|
14185
|
+
const bitsHigh = from.bitsHigh;
|
|
14186
|
+
to.number = from.number;
|
|
14187
|
+
if (bits & U_MODEL5) to.modelindex = from.modelindex;
|
|
14188
|
+
if (bits & U_MODEL22) to.modelindex2 = from.modelindex2;
|
|
14189
|
+
if (bits & U_MODEL32) to.modelindex3 = from.modelindex3;
|
|
14190
|
+
if (bits & U_MODEL42) to.modelindex4 = from.modelindex4;
|
|
14191
|
+
if (bits & U_FRAME8) to.frame = from.frame;
|
|
14192
|
+
if (bits & U_FRAME16) to.frame = from.frame;
|
|
14193
|
+
if (bits & U_SKIN8 || bits & U_SKIN16) to.skinnum = from.skinnum;
|
|
14194
|
+
if (bits & U_EFFECTS8 || bits & U_EFFECTS16) to.effects = from.effects;
|
|
14195
|
+
if (bits & U_RENDERFX8 || bits & U_RENDERFX16) to.renderfx = from.renderfx;
|
|
14196
|
+
if (bits & U_ORIGIN12) to.origin.x = from.origin.x;
|
|
14197
|
+
if (bits & U_ORIGIN22) to.origin.y = from.origin.y;
|
|
14198
|
+
if (bits & U_ORIGIN32) to.origin.z = from.origin.z;
|
|
14199
|
+
if (bits & U_ANGLE12) to.angles.x = from.angles.x;
|
|
14200
|
+
if (bits & U_ANGLE22) to.angles.y = from.angles.y;
|
|
14201
|
+
if (bits & U_ANGLE32) to.angles.z = from.angles.z;
|
|
14202
|
+
if (bits & U_OLDORIGIN) {
|
|
14203
|
+
to.old_origin.x = from.old_origin.x;
|
|
14204
|
+
to.old_origin.y = from.old_origin.y;
|
|
14205
|
+
to.old_origin.z = from.old_origin.z;
|
|
14206
|
+
}
|
|
14207
|
+
if (bits & U_SOUND2) to.sound = from.sound;
|
|
14208
|
+
if (bits & U_EVENT2) to.event = from.event;
|
|
14209
|
+
if (bits & U_SOLID2) to.solid = from.solid;
|
|
14210
|
+
if (bits & U_ALPHA) to.alpha = from.alpha;
|
|
14211
|
+
if (bits & U_SCALE) to.scale = from.scale;
|
|
14212
|
+
if (bits & U_INSTANCE_BITS) to.instanceBits = from.instanceBits;
|
|
14213
|
+
if (bits & U_LOOP_VOLUME) to.loopVolume = from.loopVolume;
|
|
14214
|
+
if (bitsHigh & U_LOOP_ATTENUATION_HIGH) to.loopAttenuation = from.loopAttenuation;
|
|
14215
|
+
if (bitsHigh & U_OWNER_HIGH) to.owner = from.owner;
|
|
14216
|
+
if (bitsHigh & U_OLD_FRAME_HIGH) to.oldFrame = from.oldFrame;
|
|
14217
|
+
}
|
|
14218
|
+
|
|
14219
|
+
// src/demo/clipper.ts
|
|
14220
|
+
var DemoClipper = class {
|
|
14221
|
+
static extractClip(demoData, startFrame, endFrame) {
|
|
14222
|
+
const reader = new DemoReader(demoData);
|
|
14223
|
+
if (!reader.seekToMessage(startFrame)) {
|
|
14224
|
+
throw new Error(`Start frame ${startFrame} out of bounds`);
|
|
14225
|
+
}
|
|
14226
|
+
const startOffset = reader.getOffset();
|
|
14227
|
+
let endOffset = demoData.byteLength;
|
|
14228
|
+
if (endFrame < reader.getMessageCount()) {
|
|
14229
|
+
if (reader.seekToMessage(endFrame)) {
|
|
14230
|
+
endOffset = reader.getOffset();
|
|
14231
|
+
}
|
|
14232
|
+
}
|
|
14233
|
+
const length = endOffset - startOffset;
|
|
14234
|
+
const result = new Uint8Array(length + 4);
|
|
14235
|
+
const sourceView = new Uint8Array(demoData);
|
|
14236
|
+
result.set(sourceView.subarray(startOffset, endOffset), 0);
|
|
14237
|
+
const view = new DataView(result.buffer);
|
|
14238
|
+
view.setInt32(length, -1, true);
|
|
14239
|
+
return result;
|
|
14240
|
+
}
|
|
14241
|
+
static async captureWorldState(demoData, atFrame) {
|
|
14242
|
+
const reader = new DemoReader(demoData);
|
|
14243
|
+
const configStrings = /* @__PURE__ */ new Map();
|
|
14244
|
+
const entityBaselines = /* @__PURE__ */ new Map();
|
|
14245
|
+
let serverData = null;
|
|
14246
|
+
let playerState = createEmptyProtocolPlayerState();
|
|
14247
|
+
const entities = /* @__PURE__ */ new Map();
|
|
14248
|
+
let currentFrame = -1;
|
|
14249
|
+
const handler = {
|
|
14250
|
+
onServerData: (protocol, serverCount, attractLoop, gameDir, playerNum, levelName, tickRate, demoType) => {
|
|
14251
|
+
serverData = {
|
|
14252
|
+
protocolVersion: protocol,
|
|
14253
|
+
serverCount,
|
|
14254
|
+
attractLoop: attractLoop !== 0,
|
|
14255
|
+
gameDirectory: gameDir,
|
|
14256
|
+
levelName
|
|
14257
|
+
};
|
|
14258
|
+
},
|
|
14259
|
+
onConfigString: (index, str) => {
|
|
14260
|
+
configStrings.set(index, str);
|
|
14261
|
+
},
|
|
14262
|
+
onSpawnBaseline: (entity) => {
|
|
14263
|
+
entityBaselines.set(entity.number, entity);
|
|
14264
|
+
},
|
|
14265
|
+
onFrame: (frameData) => {
|
|
14266
|
+
playerState = frameData.playerState;
|
|
14267
|
+
const packetEntities = frameData.packetEntities;
|
|
14268
|
+
if (!packetEntities.delta) {
|
|
14269
|
+
entities.clear();
|
|
14270
|
+
}
|
|
14271
|
+
for (const partial of packetEntities.entities) {
|
|
14272
|
+
if (partial.bits & U_REMOVE2) {
|
|
14273
|
+
entities.delete(partial.number);
|
|
14274
|
+
continue;
|
|
14275
|
+
}
|
|
14276
|
+
const number = partial.number;
|
|
14277
|
+
let source;
|
|
14278
|
+
if (packetEntities.delta && entities.has(number)) {
|
|
14279
|
+
source = entities.get(number);
|
|
14280
|
+
} else if (entityBaselines.has(number)) {
|
|
14281
|
+
source = entityBaselines.get(number);
|
|
14282
|
+
} else {
|
|
14283
|
+
source = createEmptyEntityState();
|
|
14284
|
+
}
|
|
14285
|
+
const final = structuredClone(source);
|
|
14286
|
+
applyEntityDelta(final, partial);
|
|
14287
|
+
entities.set(number, final);
|
|
14288
|
+
}
|
|
14289
|
+
},
|
|
14290
|
+
onCenterPrint: () => {
|
|
14291
|
+
},
|
|
14292
|
+
onStuffText: () => {
|
|
14293
|
+
},
|
|
14294
|
+
onSound: () => {
|
|
14295
|
+
},
|
|
14296
|
+
onPrint: () => {
|
|
14297
|
+
},
|
|
14298
|
+
onMuzzleFlash: () => {
|
|
14299
|
+
},
|
|
14300
|
+
onMuzzleFlash2: () => {
|
|
14301
|
+
},
|
|
14302
|
+
onTempEntity: () => {
|
|
14303
|
+
},
|
|
14304
|
+
onLayout: () => {
|
|
14305
|
+
},
|
|
14306
|
+
onInventory: () => {
|
|
14307
|
+
},
|
|
14308
|
+
onDisconnect: () => {
|
|
14309
|
+
},
|
|
14310
|
+
onReconnect: () => {
|
|
14311
|
+
},
|
|
14312
|
+
onDownload: () => {
|
|
14313
|
+
}
|
|
14314
|
+
};
|
|
14315
|
+
while (reader.hasMore()) {
|
|
14316
|
+
if (currentFrame >= atFrame) {
|
|
14317
|
+
break;
|
|
14318
|
+
}
|
|
14319
|
+
const block = reader.readNextBlock();
|
|
14320
|
+
if (!block) break;
|
|
14321
|
+
currentFrame++;
|
|
14322
|
+
const parser = new NetworkMessageParser(block.data, handler);
|
|
14323
|
+
const currentProtocol = serverData ? serverData.protocolVersion : 34;
|
|
14324
|
+
parser.setProtocolVersion(currentProtocol);
|
|
14325
|
+
parser.parseMessage();
|
|
14326
|
+
}
|
|
14327
|
+
return {
|
|
14328
|
+
serverData,
|
|
14329
|
+
configStrings,
|
|
14330
|
+
entityBaselines,
|
|
14331
|
+
playerState,
|
|
14332
|
+
entities,
|
|
14333
|
+
gameTime: 0
|
|
14334
|
+
};
|
|
14335
|
+
}
|
|
14336
|
+
static extractStandaloneClip(demoData, startFrame, endFrame, worldState) {
|
|
14337
|
+
throw new Error("extractStandaloneClip not fully implemented: requires NetworkMessageWriter");
|
|
14338
|
+
}
|
|
14339
|
+
};
|
|
14340
|
+
|
|
13899
14341
|
// src/assets/fileType.ts
|
|
13900
14342
|
var FileType = /* @__PURE__ */ ((FileType2) => {
|
|
13901
14343
|
FileType2["Unknown"] = "unknown";
|
|
@@ -14273,6 +14715,7 @@ export {
|
|
|
14273
14715
|
CvarRegistry,
|
|
14274
14716
|
DemoAnalyzer,
|
|
14275
14717
|
DemoCameraMode,
|
|
14718
|
+
DemoClipper,
|
|
14276
14719
|
DemoEventType,
|
|
14277
14720
|
DemoPlaybackController,
|
|
14278
14721
|
DemoReader,
|
|
@@ -14312,10 +14755,13 @@ export {
|
|
|
14312
14755
|
PakParseError,
|
|
14313
14756
|
PakValidationError,
|
|
14314
14757
|
PakValidator,
|
|
14758
|
+
PakWriter,
|
|
14315
14759
|
ParticleRenderer,
|
|
14316
14760
|
ParticleSystem,
|
|
14317
14761
|
PlaybackState,
|
|
14318
14762
|
RERELEASE_KNOWN_PAKS,
|
|
14763
|
+
ResourceLoadTracker,
|
|
14764
|
+
ResourceType,
|
|
14319
14765
|
SKYBOX_FRAGMENT_SHADER,
|
|
14320
14766
|
SKYBOX_VERTEX_SHADER,
|
|
14321
14767
|
SOUND_FULLVOLUME,
|
|
@@ -14365,6 +14811,7 @@ export {
|
|
|
14365
14811
|
VertexBuffer,
|
|
14366
14812
|
VirtualFileSystem,
|
|
14367
14813
|
advanceAnimation,
|
|
14814
|
+
applyEntityDelta,
|
|
14368
14815
|
applySurfaceState,
|
|
14369
14816
|
attenuationToDistanceMultiplier,
|
|
14370
14817
|
boxIntersectsFrustum,
|