quake2ts 0.0.84 → 0.0.85

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 (28) hide show
  1. package/package.json +1 -1
  2. package/packages/client/dist/browser/index.global.js +1 -1
  3. package/packages/client/dist/browser/index.global.js.map +1 -1
  4. package/packages/client/dist/cjs/index.cjs +382 -127
  5. package/packages/client/dist/cjs/index.cjs.map +1 -1
  6. package/packages/client/dist/esm/index.js +382 -127
  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 +30 -0
  10. package/packages/client/dist/types/demo/handler.d.ts.map +1 -0
  11. package/packages/client/dist/types/index.d.ts +2 -0
  12. package/packages/client/dist/types/index.d.ts.map +1 -1
  13. package/packages/engine/dist/browser/index.global.js +14 -14
  14. package/packages/engine/dist/browser/index.global.js.map +1 -1
  15. package/packages/engine/dist/cjs/index.cjs +235 -97
  16. package/packages/engine/dist/cjs/index.cjs.map +1 -1
  17. package/packages/engine/dist/esm/index.js +210 -97
  18. package/packages/engine/dist/esm/index.js.map +1 -1
  19. package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
  20. package/packages/engine/dist/types/demo/index.d.ts +1 -1
  21. package/packages/engine/dist/types/demo/index.d.ts.map +1 -1
  22. package/packages/engine/dist/types/demo/parser.d.ts +84 -3
  23. package/packages/engine/dist/types/demo/parser.d.ts.map +1 -1
  24. package/packages/engine/dist/types/demo/playback.d.ts +3 -0
  25. package/packages/engine/dist/types/demo/playback.d.ts.map +1 -1
  26. package/packages/engine/dist/types/index.d.ts +2 -0
  27. package/packages/engine/dist/types/index.d.ts.map +1 -1
  28. package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
@@ -2414,11 +2414,33 @@ var createEmptyEntityState = () => ({
2414
2414
  angles: { x: 0, y: 0, z: 0 },
2415
2415
  sound: 0,
2416
2416
  event: 0,
2417
- solid: 0
2417
+ solid: 0,
2418
+ bits: 0
2419
+ });
2420
+ var createEmptyProtocolPlayerState = () => ({
2421
+ pm_type: 0,
2422
+ origin: { x: 0, y: 0, z: 0 },
2423
+ velocity: { x: 0, y: 0, z: 0 },
2424
+ pm_time: 0,
2425
+ pm_flags: 0,
2426
+ gravity: 0,
2427
+ delta_angles: { x: 0, y: 0, z: 0 },
2428
+ viewoffset: { x: 0, y: 0, z: 0 },
2429
+ viewangles: { x: 0, y: 0, z: 0 },
2430
+ kick_angles: { x: 0, y: 0, z: 0 },
2431
+ gun_index: 0,
2432
+ gun_frame: 0,
2433
+ gun_offset: { x: 0, y: 0, z: 0 },
2434
+ gun_angles: { x: 0, y: 0, z: 0 },
2435
+ blend: [0, 0, 0, 0],
2436
+ fov: 0,
2437
+ rdflags: 0,
2438
+ stats: new Array(32).fill(0)
2418
2439
  });
2419
2440
  var NetworkMessageParser = class {
2420
- constructor(stream) {
2441
+ constructor(stream, handler) {
2421
2442
  this.stream = stream;
2443
+ this.handler = handler;
2422
2444
  }
2423
2445
  parseMessage() {
2424
2446
  while (this.stream.hasMore()) {
@@ -2430,15 +2452,18 @@ var NetworkMessageParser = class {
2430
2452
  case ServerCommand.nop:
2431
2453
  break;
2432
2454
  case ServerCommand.disconnect:
2433
- console.log("Server disconnected");
2455
+ if (this.handler) this.handler.onDisconnect();
2456
+ else console.log("Server disconnected");
2434
2457
  break;
2435
2458
  case ServerCommand.reconnect:
2436
- console.log("Server reconnect");
2459
+ if (this.handler) this.handler.onReconnect();
2460
+ else console.log("Server reconnect");
2437
2461
  break;
2438
2462
  case ServerCommand.print:
2439
2463
  const printId = this.stream.readByte();
2440
2464
  const printMsg = this.stream.readString();
2441
- console.log(`[Server Print ${printId}]: ${printMsg}`);
2465
+ if (this.handler) this.handler.onPrint(printId, printMsg);
2466
+ else console.log(`[Server Print ${printId}]: ${printMsg}`);
2442
2467
  break;
2443
2468
  case ServerCommand.serverdata:
2444
2469
  this.parseServerData();
@@ -2451,7 +2476,8 @@ var NetworkMessageParser = class {
2451
2476
  break;
2452
2477
  case ServerCommand.centerprint:
2453
2478
  const centerMsg = this.stream.readString();
2454
- console.log(`[Center Print]: ${centerMsg}`);
2479
+ if (this.handler) this.handler.onCenterPrint(centerMsg);
2480
+ else console.log(`[Center Print]: ${centerMsg}`);
2455
2481
  break;
2456
2482
  case ServerCommand.download:
2457
2483
  this.parseDownload();
@@ -2467,10 +2493,12 @@ var NetworkMessageParser = class {
2467
2493
  break;
2468
2494
  case ServerCommand.stufftext:
2469
2495
  const text = this.stream.readString();
2470
- console.log(`[StuffText]: ${text}`);
2496
+ if (this.handler) this.handler.onStuffText(text);
2497
+ else console.log(`[StuffText]: ${text}`);
2471
2498
  break;
2472
2499
  case ServerCommand.layout:
2473
2500
  const layout = this.stream.readString();
2501
+ if (this.handler) this.handler.onLayout(layout);
2474
2502
  break;
2475
2503
  case ServerCommand.inventory:
2476
2504
  this.parseInventory();
@@ -2500,58 +2528,89 @@ var NetworkMessageParser = class {
2500
2528
  const gameDir = this.stream.readString();
2501
2529
  const playerNum = this.stream.readShort();
2502
2530
  const levelName = this.stream.readString();
2503
- console.log(`Server Data: Protocol ${protocol}, Level ${levelName}, GameDir ${gameDir}`);
2531
+ if (this.handler) {
2532
+ this.handler.onServerData(protocol, serverCount, attractLoop, gameDir, playerNum, levelName);
2533
+ } else {
2534
+ console.log(`Server Data: Protocol ${protocol}, Level ${levelName}, GameDir ${gameDir}`);
2535
+ }
2504
2536
  }
2505
2537
  parseConfigString() {
2506
2538
  const index = this.stream.readShort();
2507
2539
  const str3 = this.stream.readString();
2540
+ if (this.handler) {
2541
+ this.handler.onConfigString(index, str3);
2542
+ }
2508
2543
  }
2509
2544
  parseDownload() {
2510
2545
  const size = this.stream.readShort();
2511
2546
  const percent = this.stream.readByte();
2547
+ let data;
2512
2548
  if (size > 0) {
2513
- this.stream.readData(size);
2549
+ data = this.stream.readData(size);
2550
+ }
2551
+ if (this.handler) {
2552
+ this.handler.onDownload(size, percent, data);
2514
2553
  }
2515
2554
  }
2516
2555
  parseInventory() {
2517
2556
  const MAX_ITEMS22 = 256;
2557
+ const inventory = new Array(MAX_ITEMS22);
2518
2558
  for (let i = 0; i < MAX_ITEMS22; i++) {
2519
- this.stream.readShort();
2559
+ inventory[i] = this.stream.readShort();
2560
+ }
2561
+ if (this.handler) {
2562
+ this.handler.onInventory(inventory);
2520
2563
  }
2521
2564
  }
2522
2565
  parseSound() {
2523
2566
  const flags = this.stream.readByte();
2524
2567
  const soundNum = this.stream.readByte();
2568
+ let volume;
2569
+ let attenuation;
2570
+ let offset;
2571
+ let ent;
2572
+ let pos;
2525
2573
  if (flags & 1) {
2526
- this.stream.readByte();
2574
+ volume = this.stream.readByte();
2527
2575
  }
2528
2576
  if (flags & 2) {
2529
- this.stream.readByte();
2577
+ attenuation = this.stream.readByte();
2530
2578
  }
2531
2579
  if (flags & 16) {
2532
- this.stream.readByte();
2580
+ offset = this.stream.readByte();
2533
2581
  }
2534
2582
  if (flags & 8) {
2535
- this.stream.readShort();
2583
+ ent = this.stream.readShort();
2536
2584
  }
2537
2585
  if (flags & 4) {
2538
- const pos = { x: 0, y: 0, z: 0 };
2539
- this.stream.readPos(pos);
2586
+ const p = { x: 0, y: 0, z: 0 };
2587
+ this.stream.readPos(p);
2588
+ pos = p;
2589
+ }
2590
+ if (this.handler) {
2591
+ this.handler.onSound(flags, soundNum, volume, attenuation, offset, ent, pos);
2540
2592
  }
2541
2593
  }
2542
2594
  parseMuzzleFlash() {
2543
2595
  const ent = this.stream.readShort();
2544
2596
  const weapon = this.stream.readByte();
2597
+ if (this.handler) this.handler.onMuzzleFlash(ent, weapon);
2545
2598
  }
2546
2599
  parseMuzzleFlash2() {
2547
2600
  const ent = this.stream.readShort();
2548
2601
  const weapon = this.stream.readByte();
2602
+ if (this.handler) this.handler.onMuzzleFlash2(ent, weapon);
2549
2603
  }
2550
2604
  parseTempEntity() {
2551
2605
  const type = this.stream.readByte();
2552
2606
  const pos = { x: 0, y: 0, z: 0 };
2553
2607
  const pos2 = { x: 0, y: 0, z: 0 };
2554
2608
  const dir = { x: 0, y: 0, z: 0 };
2609
+ let cnt;
2610
+ let color;
2611
+ let ent;
2612
+ let srcEnt;
2613
+ let destEnt;
2555
2614
  switch (type) {
2556
2615
  case TempEntity.BLOOD:
2557
2616
  this.stream.readPos(pos);
@@ -2560,29 +2619,23 @@ var NetworkMessageParser = class {
2560
2619
  case TempEntity.GUNSHOT:
2561
2620
  case TempEntity.SPARKS:
2562
2621
  case TempEntity.BULLET_SPARKS:
2563
- this.stream.readPos(pos);
2564
- this.stream.readDir(dir);
2565
- break;
2566
2622
  case TempEntity.SCREEN_SPARKS:
2567
2623
  case TempEntity.SHIELD_SPARKS:
2568
- this.stream.readPos(pos);
2569
- this.stream.readDir(dir);
2570
- break;
2571
2624
  case TempEntity.SHOTGUN:
2572
2625
  this.stream.readPos(pos);
2573
2626
  this.stream.readDir(dir);
2574
2627
  break;
2575
2628
  case TempEntity.SPLASH:
2576
- this.stream.readByte();
2629
+ cnt = this.stream.readByte();
2577
2630
  this.stream.readPos(pos);
2578
2631
  this.stream.readDir(dir);
2579
- this.stream.readByte();
2632
+ color = this.stream.readByte();
2580
2633
  break;
2581
2634
  case TempEntity.LASER_SPARKS:
2582
- this.stream.readByte();
2635
+ cnt = this.stream.readByte();
2583
2636
  this.stream.readPos(pos);
2584
2637
  this.stream.readDir(dir);
2585
- this.stream.readByte();
2638
+ color = this.stream.readByte();
2586
2639
  break;
2587
2640
  case TempEntity.BLUEHYPERBLASTER:
2588
2641
  this.stream.readPos(pos);
@@ -2599,21 +2652,13 @@ var NetworkMessageParser = class {
2599
2652
  case TempEntity.EXPLOSION2:
2600
2653
  case TempEntity.GRENADE_EXPLOSION:
2601
2654
  case TempEntity.GRENADE_EXPLOSION_WATER:
2602
- this.stream.readPos(pos);
2603
- break;
2604
2655
  case TempEntity.PLASMA_EXPLOSION:
2605
- this.stream.readPos(pos);
2606
- break;
2607
2656
  case TempEntity.EXPLOSION1:
2608
2657
  case TempEntity.EXPLOSION1_BIG:
2609
2658
  case TempEntity.ROCKET_EXPLOSION:
2610
2659
  case TempEntity.ROCKET_EXPLOSION_WATER:
2611
2660
  case TempEntity.EXPLOSION1_NP:
2612
- this.stream.readPos(pos);
2613
- break;
2614
2661
  case TempEntity.BFG_EXPLOSION:
2615
- this.stream.readPos(pos);
2616
- break;
2617
2662
  case TempEntity.BFG_BIGEXPLOSION:
2618
2663
  this.stream.readPos(pos);
2619
2664
  break;
@@ -2627,7 +2672,7 @@ var NetworkMessageParser = class {
2627
2672
  break;
2628
2673
  case TempEntity.PARASITE_ATTACK:
2629
2674
  case TempEntity.MEDIC_CABLE_ATTACK:
2630
- this.stream.readShort();
2675
+ ent = this.stream.readShort();
2631
2676
  this.stream.readPos(pos);
2632
2677
  this.stream.readPos(pos2);
2633
2678
  break;
@@ -2635,26 +2680,26 @@ var NetworkMessageParser = class {
2635
2680
  this.stream.readPos(pos);
2636
2681
  break;
2637
2682
  case TempEntity.GRAPPLE_CABLE:
2638
- this.stream.readShort();
2683
+ ent = this.stream.readShort();
2639
2684
  this.stream.readPos(pos);
2640
2685
  this.stream.readPos(pos2);
2641
2686
  this.stream.readPos(dir);
2642
2687
  break;
2643
2688
  case TempEntity.WELDING_SPARKS:
2644
- this.stream.readByte();
2689
+ cnt = this.stream.readByte();
2645
2690
  this.stream.readPos(pos);
2646
2691
  this.stream.readDir(dir);
2647
- this.stream.readByte();
2692
+ color = this.stream.readByte();
2648
2693
  break;
2649
2694
  case TempEntity.GREENBLOOD:
2650
2695
  this.stream.readPos(pos);
2651
2696
  this.stream.readDir(dir);
2652
2697
  break;
2653
2698
  case TempEntity.TUNNEL_SPARKS:
2654
- this.stream.readByte();
2699
+ cnt = this.stream.readByte();
2655
2700
  this.stream.readPos(pos);
2656
2701
  this.stream.readDir(dir);
2657
- this.stream.readByte();
2702
+ color = this.stream.readByte();
2658
2703
  break;
2659
2704
  case TempEntity.BLASTER2:
2660
2705
  case TempEntity.FLECHETTE:
@@ -2662,8 +2707,8 @@ var NetworkMessageParser = class {
2662
2707
  this.stream.readDir(dir);
2663
2708
  break;
2664
2709
  case TempEntity.LIGHTNING:
2665
- this.stream.readShort();
2666
- this.stream.readShort();
2710
+ srcEnt = this.stream.readShort();
2711
+ destEnt = this.stream.readShort();
2667
2712
  this.stream.readPos(pos);
2668
2713
  this.stream.readPos(pos2);
2669
2714
  break;
@@ -2676,29 +2721,26 @@ var NetworkMessageParser = class {
2676
2721
  break;
2677
2722
  case TempEntity.FLASHLIGHT:
2678
2723
  this.stream.readPos(pos);
2679
- this.stream.readShort();
2724
+ ent = this.stream.readShort();
2680
2725
  break;
2681
2726
  case TempEntity.FORCEWALL:
2682
2727
  this.stream.readPos(pos);
2683
2728
  this.stream.readPos(pos2);
2684
- this.stream.readByte();
2729
+ color = this.stream.readByte();
2685
2730
  break;
2686
2731
  case TempEntity.HEATBEAM:
2687
- this.stream.readShort();
2732
+ ent = this.stream.readShort();
2688
2733
  this.stream.readPos(pos);
2689
2734
  this.stream.readPos(pos2);
2690
2735
  this.stream.readPos(dir);
2691
2736
  break;
2692
2737
  case TempEntity.MONSTER_HEATBEAM:
2693
- this.stream.readShort();
2738
+ ent = this.stream.readShort();
2694
2739
  this.stream.readPos(pos);
2695
2740
  this.stream.readPos(pos2);
2696
2741
  this.stream.readPos(dir);
2697
2742
  break;
2698
2743
  case TempEntity.HEATBEAM_SPARKS:
2699
- this.stream.readPos(pos);
2700
- this.stream.readDir(dir);
2701
- break;
2702
2744
  case TempEntity.HEATBEAM_STEAM:
2703
2745
  this.stream.readPos(pos);
2704
2746
  this.stream.readDir(dir);
@@ -2706,17 +2748,17 @@ var NetworkMessageParser = class {
2706
2748
  case TempEntity.STEAM:
2707
2749
  const steamId = this.stream.readShort();
2708
2750
  if (steamId !== -1) {
2709
- this.stream.readByte();
2751
+ cnt = this.stream.readByte();
2710
2752
  this.stream.readPos(pos);
2711
2753
  this.stream.readDir(dir);
2712
- this.stream.readByte();
2754
+ color = this.stream.readByte();
2713
2755
  this.stream.readShort();
2714
2756
  this.stream.readLong();
2715
2757
  } else {
2716
- this.stream.readByte();
2758
+ cnt = this.stream.readByte();
2717
2759
  this.stream.readPos(pos);
2718
2760
  this.stream.readDir(dir);
2719
- this.stream.readByte();
2761
+ color = this.stream.readByte();
2720
2762
  this.stream.readShort();
2721
2763
  }
2722
2764
  break;
@@ -2743,7 +2785,7 @@ var NetworkMessageParser = class {
2743
2785
  this.stream.readPos(pos);
2744
2786
  break;
2745
2787
  case TempEntity.WIDOWBEAMOUT:
2746
- const wbId = this.stream.readShort();
2788
+ this.stream.readShort();
2747
2789
  this.stream.readPos(pos);
2748
2790
  break;
2749
2791
  case TempEntity.NUKEBLAST:
@@ -2756,97 +2798,139 @@ var NetworkMessageParser = class {
2756
2798
  console.warn(`CL_ParseTEnt: bad type ${type}`);
2757
2799
  break;
2758
2800
  }
2801
+ if (this.handler) {
2802
+ this.handler.onTempEntity(type, pos, pos2, dir, cnt, color, ent, srcEnt, destEnt);
2803
+ }
2759
2804
  }
2760
2805
  parseSpawnBaseline() {
2761
2806
  const bits = this.parseEntityBits();
2762
- this.parseDelta(createEmptyEntityState(), createEmptyEntityState(), bits.number, bits.bits);
2807
+ const entity = createEmptyEntityState();
2808
+ this.parseDelta(createEmptyEntityState(), entity, bits.number, bits.bits);
2809
+ if (this.handler) {
2810
+ this.handler.onSpawnBaseline(entity);
2811
+ }
2763
2812
  }
2764
2813
  parseFrame() {
2765
2814
  const serverFrame = this.stream.readLong();
2766
2815
  const deltaFrame = this.stream.readLong();
2767
2816
  const surpressCount = this.stream.readByte();
2768
2817
  const areaBytes = this.stream.readByte();
2769
- this.stream.readData(areaBytes);
2818
+ const areaBits = this.stream.readData(areaBytes);
2770
2819
  const piCmd = this.stream.readByte();
2771
2820
  if (piCmd !== ServerCommand.playerinfo) {
2772
2821
  throw new Error(`Expected svc_playerinfo after svc_frame, got ${piCmd}`);
2773
2822
  }
2774
- this.parsePlayerState();
2823
+ const playerState = this.parsePlayerState();
2775
2824
  const peCmd = this.stream.readByte();
2776
2825
  if (peCmd !== ServerCommand.packetentities && peCmd !== ServerCommand.deltapacketentities) {
2777
2826
  throw new Error(`Expected svc_packetentities after svc_playerinfo, got ${peCmd}`);
2778
2827
  }
2779
- this.parsePacketEntities(peCmd === ServerCommand.deltapacketentities);
2828
+ const isDelta = peCmd === ServerCommand.deltapacketentities;
2829
+ const entities = this.collectPacketEntities();
2830
+ if (this.handler) {
2831
+ this.handler.onFrame({
2832
+ serverFrame,
2833
+ deltaFrame,
2834
+ surpressCount,
2835
+ areaBytes,
2836
+ areaBits,
2837
+ playerState,
2838
+ packetEntities: {
2839
+ delta: isDelta,
2840
+ entities
2841
+ }
2842
+ });
2843
+ }
2780
2844
  }
2781
2845
  parsePlayerState() {
2846
+ const ps = createEmptyProtocolPlayerState();
2782
2847
  const flags = this.stream.readShort();
2783
- if (flags & 1) this.stream.readByte();
2848
+ if (flags & 1) ps.pm_type = this.stream.readByte();
2784
2849
  if (flags & 2) {
2785
- this.stream.readShort();
2786
- this.stream.readShort();
2787
- this.stream.readShort();
2850
+ ps.origin.x = this.stream.readShort() * 0.125;
2851
+ ps.origin.y = this.stream.readShort() * 0.125;
2852
+ ps.origin.z = this.stream.readShort() * 0.125;
2788
2853
  }
2789
2854
  if (flags & 4) {
2790
- this.stream.readShort();
2791
- this.stream.readShort();
2792
- this.stream.readShort();
2855
+ ps.velocity.x = this.stream.readShort() * 0.125;
2856
+ ps.velocity.y = this.stream.readShort() * 0.125;
2857
+ ps.velocity.z = this.stream.readShort() * 0.125;
2793
2858
  }
2794
- if (flags & 8) this.stream.readByte();
2795
- if (flags & 16) this.stream.readByte();
2796
- if (flags & 32) this.stream.readShort();
2859
+ if (flags & 8) ps.pm_time = this.stream.readByte();
2860
+ if (flags & 16) ps.pm_flags = this.stream.readByte();
2861
+ if (flags & 32) ps.gravity = this.stream.readShort();
2797
2862
  if (flags & 64) {
2798
- this.stream.readShort();
2799
- this.stream.readShort();
2800
- this.stream.readShort();
2863
+ ps.delta_angles.x = this.stream.readShort() * (180 / 32768);
2864
+ ps.delta_angles.y = this.stream.readShort() * (180 / 32768);
2865
+ ps.delta_angles.z = this.stream.readShort() * (180 / 32768);
2801
2866
  }
2802
2867
  if (flags & 128) {
2803
- this.stream.readChar();
2804
- this.stream.readChar();
2805
- this.stream.readChar();
2868
+ ps.viewoffset.x = this.stream.readChar() * 0.25;
2869
+ ps.viewoffset.y = this.stream.readChar() * 0.25;
2870
+ ps.viewoffset.z = this.stream.readChar() * 0.25;
2806
2871
  }
2807
2872
  if (flags & 256) {
2808
- this.stream.readAngle16();
2809
- this.stream.readAngle16();
2810
- this.stream.readAngle16();
2873
+ ps.viewangles.x = this.stream.readAngle16();
2874
+ ps.viewangles.y = this.stream.readAngle16();
2875
+ ps.viewangles.z = this.stream.readAngle16();
2811
2876
  }
2812
2877
  if (flags & 512) {
2813
- this.stream.readChar();
2814
- this.stream.readChar();
2815
- this.stream.readChar();
2878
+ ps.kick_angles.x = this.stream.readChar() * 0.25;
2879
+ ps.kick_angles.y = this.stream.readChar() * 0.25;
2880
+ ps.kick_angles.z = this.stream.readChar() * 0.25;
2816
2881
  }
2817
- if (flags & 4096) this.stream.readByte();
2882
+ if (flags & 4096) ps.gun_index = this.stream.readByte();
2818
2883
  if (flags & 8192) {
2819
- this.stream.readByte();
2820
- this.stream.readChar();
2821
- this.stream.readChar();
2822
- this.stream.readChar();
2823
- this.stream.readChar();
2824
- this.stream.readChar();
2825
- this.stream.readChar();
2884
+ ps.gun_frame = this.stream.readByte();
2885
+ ps.gun_offset.x = this.stream.readChar() * 0.25;
2886
+ ps.gun_offset.y = this.stream.readChar() * 0.25;
2887
+ ps.gun_offset.z = this.stream.readChar() * 0.25;
2888
+ ps.gun_angles.x = this.stream.readChar() * 0.25;
2889
+ ps.gun_angles.y = this.stream.readChar() * 0.25;
2890
+ ps.gun_angles.z = this.stream.readChar() * 0.25;
2826
2891
  }
2827
2892
  if (flags & 1024) {
2828
- this.stream.readByte();
2829
- this.stream.readByte();
2830
- this.stream.readByte();
2831
- this.stream.readByte();
2893
+ ps.blend[0] = this.stream.readByte();
2894
+ ps.blend[1] = this.stream.readByte();
2895
+ ps.blend[2] = this.stream.readByte();
2896
+ ps.blend[3] = this.stream.readByte();
2832
2897
  }
2833
- if (flags & 2048) this.stream.readByte();
2834
- if (flags & 16384) this.stream.readByte();
2898
+ if (flags & 2048) ps.fov = this.stream.readByte();
2899
+ if (flags & 16384) ps.rdflags = this.stream.readByte();
2835
2900
  const statbits = this.stream.readLong();
2836
2901
  for (let i = 0; i < 32; i++) {
2837
2902
  if (statbits & 1 << i) {
2838
- this.stream.readShort();
2903
+ ps.stats[i] = this.stream.readShort();
2839
2904
  }
2840
2905
  }
2906
+ return ps;
2841
2907
  }
2842
2908
  parsePacketEntities(delta) {
2909
+ const entities = this.collectPacketEntities();
2910
+ if (this.handler) {
2911
+ this.handler.onFrame({
2912
+ serverFrame: 0,
2913
+ deltaFrame: 0,
2914
+ surpressCount: 0,
2915
+ areaBytes: 0,
2916
+ areaBits: new Uint8Array(),
2917
+ playerState: createEmptyProtocolPlayerState(),
2918
+ packetEntities: { delta, entities }
2919
+ });
2920
+ }
2921
+ }
2922
+ collectPacketEntities() {
2923
+ const entities = [];
2843
2924
  while (true) {
2844
2925
  const bits = this.parseEntityBits();
2845
2926
  if (bits.number === 0) {
2846
2927
  break;
2847
2928
  }
2848
- this.parseDelta(createEmptyEntityState(), createEmptyEntityState(), bits.number, bits.bits);
2929
+ const entity = createEmptyEntityState();
2930
+ this.parseDelta(createEmptyEntityState(), entity, bits.number, bits.bits);
2931
+ entities.push(entity);
2849
2932
  }
2933
+ return entities;
2850
2934
  }
2851
2935
  parseEntityBits() {
2852
2936
  let total = this.stream.readByte();
@@ -2890,6 +2974,7 @@ var NetworkMessageParser = class {
2890
2974
  to.event = from.event;
2891
2975
  to.solid = from.solid;
2892
2976
  to.number = number;
2977
+ to.bits = bits;
2893
2978
  if (bits & U_MODEL) to.modelindex = this.stream.readByte();
2894
2979
  if (bits & U_MODEL2) to.modelindex2 = this.stream.readByte();
2895
2980
  if (bits & U_MODEL3) to.modelindex3 = this.stream.readByte();
@@ -2935,6 +3020,13 @@ var NetworkMessageParser = class {
2935
3020
  if (bits & U_SOLID) to.solid = this.stream.readShort();
2936
3021
  }
2937
3022
  };
3023
+ var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
3024
+ PlaybackState2[PlaybackState2["Stopped"] = 0] = "Stopped";
3025
+ PlaybackState2[PlaybackState2["Playing"] = 1] = "Playing";
3026
+ PlaybackState2[PlaybackState2["Paused"] = 2] = "Paused";
3027
+ PlaybackState2[PlaybackState2["Finished"] = 3] = "Finished";
3028
+ return PlaybackState2;
3029
+ })(PlaybackState || {});
2938
3030
  var DemoPlaybackController = class {
2939
3031
  // ms (10Hz default)
2940
3032
  constructor() {
@@ -2944,6 +3036,9 @@ var DemoPlaybackController = class {
2944
3036
  this.accumulatedTime = 0;
2945
3037
  this.frameDuration = 100;
2946
3038
  }
3039
+ setHandler(handler) {
3040
+ this.handler = handler;
3041
+ }
2947
3042
  loadDemo(buffer) {
2948
3043
  this.reader = new DemoReader(buffer);
2949
3044
  this.state = 0;
@@ -2981,7 +3076,7 @@ var DemoPlaybackController = class {
2981
3076
  this.state = 3;
2982
3077
  return;
2983
3078
  }
2984
- const parser = new NetworkMessageParser(block.data);
3079
+ const parser = new NetworkMessageParser(block.data, this.handler);
2985
3080
  parser.parseMessage();
2986
3081
  this.accumulatedTime -= this.frameDuration;
2987
3082
  }
@@ -3159,32 +3254,32 @@ var WaterLevel = /* @__PURE__ */ ((WaterLevel3) => {
3159
3254
  WaterLevel3[WaterLevel3["Under"] = 3] = "Under";
3160
3255
  return WaterLevel3;
3161
3256
  })(WaterLevel || {});
3162
- var PmFlag = /* @__PURE__ */ ((PmFlag2) => {
3163
- PmFlag2[PmFlag2["Ducked"] = 1] = "Ducked";
3164
- PmFlag2[PmFlag2["JumpHeld"] = 2] = "JumpHeld";
3165
- PmFlag2[PmFlag2["OnGround"] = 4] = "OnGround";
3166
- PmFlag2[PmFlag2["TimeWaterJump"] = 8] = "TimeWaterJump";
3167
- PmFlag2[PmFlag2["TimeLand"] = 16] = "TimeLand";
3168
- PmFlag2[PmFlag2["TimeTeleport"] = 32] = "TimeTeleport";
3169
- PmFlag2[PmFlag2["NoPositionalPrediction"] = 64] = "NoPositionalPrediction";
3170
- PmFlag2[PmFlag2["OnLadder"] = 128] = "OnLadder";
3171
- PmFlag2[PmFlag2["NoAngularPrediction"] = 256] = "NoAngularPrediction";
3172
- PmFlag2[PmFlag2["IgnorePlayerCollision"] = 512] = "IgnorePlayerCollision";
3173
- PmFlag2[PmFlag2["TimeTrick"] = 1024] = "TimeTrick";
3174
- return PmFlag2;
3257
+ var PmFlag = /* @__PURE__ */ ((PmFlag22) => {
3258
+ PmFlag22[PmFlag22["Ducked"] = 1] = "Ducked";
3259
+ PmFlag22[PmFlag22["JumpHeld"] = 2] = "JumpHeld";
3260
+ PmFlag22[PmFlag22["OnGround"] = 4] = "OnGround";
3261
+ PmFlag22[PmFlag22["TimeWaterJump"] = 8] = "TimeWaterJump";
3262
+ PmFlag22[PmFlag22["TimeLand"] = 16] = "TimeLand";
3263
+ PmFlag22[PmFlag22["TimeTeleport"] = 32] = "TimeTeleport";
3264
+ PmFlag22[PmFlag22["NoPositionalPrediction"] = 64] = "NoPositionalPrediction";
3265
+ PmFlag22[PmFlag22["OnLadder"] = 128] = "OnLadder";
3266
+ PmFlag22[PmFlag22["NoAngularPrediction"] = 256] = "NoAngularPrediction";
3267
+ PmFlag22[PmFlag22["IgnorePlayerCollision"] = 512] = "IgnorePlayerCollision";
3268
+ PmFlag22[PmFlag22["TimeTrick"] = 1024] = "TimeTrick";
3269
+ return PmFlag22;
3175
3270
  })(PmFlag || {});
3176
3271
  function hasPmFlag(flags, flag) {
3177
3272
  return (flags & flag) !== 0;
3178
3273
  }
3179
- var PmType = /* @__PURE__ */ ((PmType2) => {
3180
- PmType2[PmType2["Normal"] = 0] = "Normal";
3181
- PmType2[PmType2["Grapple"] = 1] = "Grapple";
3182
- PmType2[PmType2["NoClip"] = 2] = "NoClip";
3183
- PmType2[PmType2["Spectator"] = 3] = "Spectator";
3184
- PmType2[PmType2["Dead"] = 4] = "Dead";
3185
- PmType2[PmType2["Gib"] = 5] = "Gib";
3186
- PmType2[PmType2["Freeze"] = 6] = "Freeze";
3187
- return PmType2;
3274
+ var PmType = /* @__PURE__ */ ((PmType22) => {
3275
+ PmType22[PmType22["Normal"] = 0] = "Normal";
3276
+ PmType22[PmType22["Grapple"] = 1] = "Grapple";
3277
+ PmType22[PmType22["NoClip"] = 2] = "NoClip";
3278
+ PmType22[PmType22["Spectator"] = 3] = "Spectator";
3279
+ PmType22[PmType22["Dead"] = 4] = "Dead";
3280
+ PmType22[PmType22["Gib"] = 5] = "Gib";
3281
+ PmType22[PmType22["Freeze"] = 6] = "Freeze";
3282
+ return PmType22;
3188
3283
  })(PmType || {});
3189
3284
  var PlayerButton = /* @__PURE__ */ ((PlayerButton2) => {
3190
3285
  PlayerButton2[PlayerButton2["None"] = 0] = "None";
@@ -3814,6 +3909,158 @@ var Draw_Hud = (renderer, ps, client, health, armor, ammo, stats) => {
3814
3909
  renderer.end2D();
3815
3910
  };
3816
3911
 
3912
+ // src/demo/handler.ts
3913
+ var MAX_CONFIGSTRINGS3 = 2048;
3914
+ var ClientNetworkHandler = class {
3915
+ constructor() {
3916
+ this.configstrings = new Array(MAX_CONFIGSTRINGS3).fill("");
3917
+ this.entities = /* @__PURE__ */ new Map();
3918
+ // Current frame entities
3919
+ this.baselines = /* @__PURE__ */ new Map();
3920
+ this.latestFrame = null;
3921
+ // Stats for HUD
3922
+ this.stats = new Array(32).fill(0);
3923
+ this.inventory = new Array(256).fill(0);
3924
+ }
3925
+ onServerData(protocol, serverCount, attractLoop, gameDir, playerNum, levelName) {
3926
+ console.log(`Demo: Server Data - Protocol: ${protocol}, Level: ${levelName}`);
3927
+ this.configstrings.fill("");
3928
+ this.entities.clear();
3929
+ this.baselines.clear();
3930
+ this.latestFrame = null;
3931
+ }
3932
+ onConfigString(index, str3) {
3933
+ this.configstrings[index] = str3;
3934
+ }
3935
+ onSpawnBaseline(entity) {
3936
+ this.baselines.set(entity.number, structuredClone(entity));
3937
+ }
3938
+ onFrame(frame) {
3939
+ this.latestFrame = frame;
3940
+ this.stats = [...frame.playerState.stats];
3941
+ const packetEntities = frame.packetEntities;
3942
+ const newEntities = /* @__PURE__ */ new Map();
3943
+ if (packetEntities.delta) {
3944
+ for (const [num, ent] of this.entities) {
3945
+ newEntities.set(num, structuredClone(ent));
3946
+ }
3947
+ } else {
3948
+ }
3949
+ for (const partial of packetEntities.entities) {
3950
+ if (partial.bits & U_REMOVE) {
3951
+ newEntities.delete(partial.number);
3952
+ continue;
3953
+ }
3954
+ const number = partial.number;
3955
+ let source;
3956
+ if (packetEntities.delta && this.entities.has(number)) {
3957
+ source = this.entities.get(number);
3958
+ } else if (this.baselines.has(number)) {
3959
+ source = this.baselines.get(number);
3960
+ } else {
3961
+ source = createEmptyEntityState();
3962
+ }
3963
+ const final = structuredClone(source);
3964
+ this.applyDelta(final, partial);
3965
+ newEntities.set(number, final);
3966
+ }
3967
+ this.entities = newEntities;
3968
+ }
3969
+ applyDelta(to, from) {
3970
+ const bits = from.bits;
3971
+ to.number = from.number;
3972
+ if (bits & U_MODEL) to.modelindex = from.modelindex;
3973
+ if (bits & U_MODEL2) to.modelindex2 = from.modelindex2;
3974
+ if (bits & U_MODEL3) to.modelindex3 = from.modelindex3;
3975
+ if (bits & U_MODEL4) to.modelindex4 = from.modelindex4;
3976
+ if (bits & U_FRAME8) to.frame = from.frame;
3977
+ if (bits & U_FRAME16) to.frame = from.frame;
3978
+ if (bits & U_SKIN8 || bits & U_SKIN16) to.skinnum = from.skinnum;
3979
+ if (bits & U_EFFECTS8 || bits & U_EFFECTS16) to.effects = from.effects;
3980
+ if (bits & U_RENDERFX8 || bits & U_RENDERFX16) to.renderfx = from.renderfx;
3981
+ if (bits & U_ORIGIN1) to.origin.x = from.origin.x;
3982
+ if (bits & U_ORIGIN2) to.origin.y = from.origin.y;
3983
+ if (bits & U_ORIGIN3) to.origin.z = from.origin.z;
3984
+ if (bits & U_ANGLE1) to.angles.x = from.angles.x;
3985
+ if (bits & U_ANGLE2) to.angles.y = from.angles.y;
3986
+ if (bits & U_ANGLE3) to.angles.z = from.angles.z;
3987
+ if (bits & U_OLDORIGIN) {
3988
+ to.old_origin.x = from.old_origin.x;
3989
+ to.old_origin.y = from.old_origin.y;
3990
+ to.old_origin.z = from.old_origin.z;
3991
+ }
3992
+ if (bits & U_SOUND) to.sound = from.sound;
3993
+ if (bits & U_EVENT) to.event = from.event;
3994
+ if (bits & U_SOLID) to.solid = from.solid;
3995
+ }
3996
+ onCenterPrint(msg) {
3997
+ console.log(`[Center]: ${msg}`);
3998
+ }
3999
+ onStuffText(msg) {
4000
+ }
4001
+ onPrint(level, msg) {
4002
+ }
4003
+ onSound(flags, soundNum, volume, attenuation, offset, ent, pos) {
4004
+ }
4005
+ onTempEntity(type, pos, pos2, dir, cnt, color, ent, srcEnt, destEnt) {
4006
+ }
4007
+ onLayout(layout) {
4008
+ }
4009
+ onInventory(inventory) {
4010
+ this.inventory = [...inventory];
4011
+ }
4012
+ onMuzzleFlash(ent, weapon) {
4013
+ }
4014
+ onMuzzleFlash2(ent, weapon) {
4015
+ }
4016
+ onDisconnect() {
4017
+ }
4018
+ onReconnect() {
4019
+ }
4020
+ onDownload(size, percent, data) {
4021
+ }
4022
+ getPredictionState() {
4023
+ if (!this.latestFrame) return defaultPredictionState();
4024
+ const ps = this.latestFrame.playerState;
4025
+ const inventory = {
4026
+ ammo: {
4027
+ caps: [],
4028
+ counts: []
4029
+ },
4030
+ ownedWeapons: /* @__PURE__ */ new Set(),
4031
+ armor: null,
4032
+ powerups: /* @__PURE__ */ new Map(),
4033
+ keys: /* @__PURE__ */ new Set()
4034
+ };
4035
+ const origin = { ...ps.origin };
4036
+ const velocity = { ...ps.velocity };
4037
+ const viewangles = { ...ps.viewangles };
4038
+ const deltaAngles = { ...ps.delta_angles };
4039
+ return {
4040
+ origin,
4041
+ velocity,
4042
+ viewangles,
4043
+ pmFlags: ps.pm_flags,
4044
+ pmType: ps.pm_type,
4045
+ waterlevel: WaterLevel.None,
4046
+ gravity: ps.gravity,
4047
+ deltaAngles,
4048
+ client: {
4049
+ inventory,
4050
+ weaponStates: {
4051
+ states: /* @__PURE__ */ new Map()
4052
+ }
4053
+ },
4054
+ health: ps.stats[1],
4055
+ // STAT_HEALTH
4056
+ armor: ps.stats[4],
4057
+ // STAT_ARMOR
4058
+ ammo: ps.stats[2]
4059
+ // STAT_AMMO
4060
+ };
4061
+ }
4062
+ };
4063
+
3817
4064
  // src/input/bindings.ts
3818
4065
  var DEFAULT_BINDINGS = [
3819
4066
  { code: "KeyW", command: "+forward" },
@@ -4307,6 +4554,8 @@ function createClient(imports) {
4307
4554
  const prediction = new ClientPrediction(imports.engine.trace);
4308
4555
  const view = new ViewEffects();
4309
4556
  const demoPlayback = new DemoPlaybackController();
4557
+ const demoHandler = new ClientNetworkHandler();
4558
+ demoPlayback.setHandler(demoHandler);
4310
4559
  let latestFrame;
4311
4560
  let lastRendered;
4312
4561
  let lastView;
@@ -4323,14 +4572,19 @@ function createClient(imports) {
4323
4572
  return prediction.enqueueCommand(command);
4324
4573
  },
4325
4574
  render(sample) {
4326
- if (sample.latest?.state) {
4327
- prediction.setAuthoritative(sample.latest);
4328
- latestFrame = sample.latest;
4329
- }
4330
- if (sample.previous?.state && sample.latest?.state) {
4331
- lastRendered = interpolatePredictionState(sample.previous.state, sample.latest.state, sample.alpha);
4575
+ const playbackState = demoPlayback.getState();
4576
+ if (playbackState === PlaybackState.Playing) {
4577
+ lastRendered = demoHandler.getPredictionState();
4332
4578
  } else {
4333
- lastRendered = sample.latest?.state ?? sample.previous?.state ?? prediction.getPredictedState();
4579
+ if (sample.latest?.state) {
4580
+ prediction.setAuthoritative(sample.latest);
4581
+ latestFrame = sample.latest;
4582
+ }
4583
+ if (sample.previous?.state && sample.latest?.state) {
4584
+ lastRendered = interpolatePredictionState(sample.previous.state, sample.latest.state, sample.alpha);
4585
+ } else {
4586
+ lastRendered = sample.latest?.state ?? sample.previous?.state ?? prediction.getPredictedState();
4587
+ }
4334
4588
  }
4335
4589
  const frameTimeMs = sample.latest && sample.previous ? Math.max(0, sample.latest.timeMs - sample.previous.timeMs) : 0;
4336
4590
  lastView = view.sample(lastRendered, frameTimeMs);
@@ -4389,7 +4643,8 @@ function createClient(imports) {
4389
4643
  get camera() {
4390
4644
  return camera;
4391
4645
  },
4392
- demoPlayback
4646
+ demoPlayback,
4647
+ demoHandler
4393
4648
  };
4394
4649
  }
4395
4650
  export {