quake2ts 0.0.512 → 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.
Files changed (32) hide show
  1. package/package.json +1 -1
  2. package/packages/client/dist/browser/index.global.js +17 -17
  3. package/packages/client/dist/browser/index.global.js.map +1 -1
  4. package/packages/client/dist/cjs/index.cjs +168 -38
  5. package/packages/client/dist/cjs/index.cjs.map +1 -1
  6. package/packages/client/dist/esm/index.js +168 -38
  7. package/packages/client/dist/esm/index.js.map +1 -1
  8. package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
  9. package/packages/client/dist/types/demo/handler.d.ts +0 -1
  10. package/packages/client/dist/types/demo/handler.d.ts.map +1 -1
  11. package/packages/engine/dist/browser/index.global.js +17 -17
  12. package/packages/engine/dist/browser/index.global.js.map +1 -1
  13. package/packages/engine/dist/cjs/index.cjs +461 -9
  14. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  15. package/packages/engine/dist/esm/index.js +456 -9
  16. package/packages/engine/dist/esm/index.js.map +1 -1
  17. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  18. package/packages/engine/dist/types/assets/manager.d.ts +3 -0
  19. package/packages/engine/dist/types/assets/manager.d.ts.map +1 -1
  20. package/packages/engine/dist/types/assets/pakWriter.d.ts +8 -0
  21. package/packages/engine/dist/types/assets/pakWriter.d.ts.map +1 -0
  22. package/packages/engine/dist/types/assets/resourceTracker.d.ts +33 -0
  23. package/packages/engine/dist/types/assets/resourceTracker.d.ts.map +1 -0
  24. package/packages/engine/dist/types/demo/clipper.d.ts +22 -0
  25. package/packages/engine/dist/types/demo/clipper.d.ts.map +1 -0
  26. package/packages/engine/dist/types/demo/delta.d.ts +3 -0
  27. package/packages/engine/dist/types/demo/delta.d.ts.map +1 -0
  28. package/packages/engine/dist/types/demo/playback.d.ts +40 -0
  29. package/packages/engine/dist/types/demo/playback.d.ts.map +1 -1
  30. package/packages/engine/dist/types/index.d.ts +4 -0
  31. package/packages/engine/dist/types/index.d.ts.map +1 -1
  32. 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 <= 0 || dirLength % DIRECTORY_ENTRY_SIZE !== 0) {
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 HEADER_SIZE2 = 4 + 4 + HEADER_LUMPS * 8;
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 < HEADER_SIZE2) {
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 HEADER_SIZE3 = 17 * 4;
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 < HEADER_SIZE3 || offset + length > buffer.byteLength) {
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 < HEADER_SIZE3) {
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 HEADER_SIZE4 = 12;
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 < HEADER_SIZE4) {
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 = HEADER_SIZE4;
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,