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
@@ -2445,11 +2445,33 @@ var createEmptyEntityState = () => ({
2445
2445
  angles: { x: 0, y: 0, z: 0 },
2446
2446
  sound: 0,
2447
2447
  event: 0,
2448
- solid: 0
2448
+ solid: 0,
2449
+ bits: 0
2450
+ });
2451
+ var createEmptyProtocolPlayerState = () => ({
2452
+ pm_type: 0,
2453
+ origin: { x: 0, y: 0, z: 0 },
2454
+ velocity: { x: 0, y: 0, z: 0 },
2455
+ pm_time: 0,
2456
+ pm_flags: 0,
2457
+ gravity: 0,
2458
+ delta_angles: { x: 0, y: 0, z: 0 },
2459
+ viewoffset: { x: 0, y: 0, z: 0 },
2460
+ viewangles: { x: 0, y: 0, z: 0 },
2461
+ kick_angles: { x: 0, y: 0, z: 0 },
2462
+ gun_index: 0,
2463
+ gun_frame: 0,
2464
+ gun_offset: { x: 0, y: 0, z: 0 },
2465
+ gun_angles: { x: 0, y: 0, z: 0 },
2466
+ blend: [0, 0, 0, 0],
2467
+ fov: 0,
2468
+ rdflags: 0,
2469
+ stats: new Array(32).fill(0)
2449
2470
  });
2450
2471
  var NetworkMessageParser = class {
2451
- constructor(stream) {
2472
+ constructor(stream, handler) {
2452
2473
  this.stream = stream;
2474
+ this.handler = handler;
2453
2475
  }
2454
2476
  parseMessage() {
2455
2477
  while (this.stream.hasMore()) {
@@ -2461,15 +2483,18 @@ var NetworkMessageParser = class {
2461
2483
  case ServerCommand.nop:
2462
2484
  break;
2463
2485
  case ServerCommand.disconnect:
2464
- console.log("Server disconnected");
2486
+ if (this.handler) this.handler.onDisconnect();
2487
+ else console.log("Server disconnected");
2465
2488
  break;
2466
2489
  case ServerCommand.reconnect:
2467
- console.log("Server reconnect");
2490
+ if (this.handler) this.handler.onReconnect();
2491
+ else console.log("Server reconnect");
2468
2492
  break;
2469
2493
  case ServerCommand.print:
2470
2494
  const printId = this.stream.readByte();
2471
2495
  const printMsg = this.stream.readString();
2472
- console.log(`[Server Print ${printId}]: ${printMsg}`);
2496
+ if (this.handler) this.handler.onPrint(printId, printMsg);
2497
+ else console.log(`[Server Print ${printId}]: ${printMsg}`);
2473
2498
  break;
2474
2499
  case ServerCommand.serverdata:
2475
2500
  this.parseServerData();
@@ -2482,7 +2507,8 @@ var NetworkMessageParser = class {
2482
2507
  break;
2483
2508
  case ServerCommand.centerprint:
2484
2509
  const centerMsg = this.stream.readString();
2485
- console.log(`[Center Print]: ${centerMsg}`);
2510
+ if (this.handler) this.handler.onCenterPrint(centerMsg);
2511
+ else console.log(`[Center Print]: ${centerMsg}`);
2486
2512
  break;
2487
2513
  case ServerCommand.download:
2488
2514
  this.parseDownload();
@@ -2498,10 +2524,12 @@ var NetworkMessageParser = class {
2498
2524
  break;
2499
2525
  case ServerCommand.stufftext:
2500
2526
  const text = this.stream.readString();
2501
- console.log(`[StuffText]: ${text}`);
2527
+ if (this.handler) this.handler.onStuffText(text);
2528
+ else console.log(`[StuffText]: ${text}`);
2502
2529
  break;
2503
2530
  case ServerCommand.layout:
2504
2531
  const layout = this.stream.readString();
2532
+ if (this.handler) this.handler.onLayout(layout);
2505
2533
  break;
2506
2534
  case ServerCommand.inventory:
2507
2535
  this.parseInventory();
@@ -2531,58 +2559,89 @@ var NetworkMessageParser = class {
2531
2559
  const gameDir = this.stream.readString();
2532
2560
  const playerNum = this.stream.readShort();
2533
2561
  const levelName = this.stream.readString();
2534
- console.log(`Server Data: Protocol ${protocol}, Level ${levelName}, GameDir ${gameDir}`);
2562
+ if (this.handler) {
2563
+ this.handler.onServerData(protocol, serverCount, attractLoop, gameDir, playerNum, levelName);
2564
+ } else {
2565
+ console.log(`Server Data: Protocol ${protocol}, Level ${levelName}, GameDir ${gameDir}`);
2566
+ }
2535
2567
  }
2536
2568
  parseConfigString() {
2537
2569
  const index = this.stream.readShort();
2538
2570
  const str3 = this.stream.readString();
2571
+ if (this.handler) {
2572
+ this.handler.onConfigString(index, str3);
2573
+ }
2539
2574
  }
2540
2575
  parseDownload() {
2541
2576
  const size = this.stream.readShort();
2542
2577
  const percent = this.stream.readByte();
2578
+ let data;
2543
2579
  if (size > 0) {
2544
- this.stream.readData(size);
2580
+ data = this.stream.readData(size);
2581
+ }
2582
+ if (this.handler) {
2583
+ this.handler.onDownload(size, percent, data);
2545
2584
  }
2546
2585
  }
2547
2586
  parseInventory() {
2548
2587
  const MAX_ITEMS22 = 256;
2588
+ const inventory = new Array(MAX_ITEMS22);
2549
2589
  for (let i = 0; i < MAX_ITEMS22; i++) {
2550
- this.stream.readShort();
2590
+ inventory[i] = this.stream.readShort();
2591
+ }
2592
+ if (this.handler) {
2593
+ this.handler.onInventory(inventory);
2551
2594
  }
2552
2595
  }
2553
2596
  parseSound() {
2554
2597
  const flags = this.stream.readByte();
2555
2598
  const soundNum = this.stream.readByte();
2599
+ let volume;
2600
+ let attenuation;
2601
+ let offset;
2602
+ let ent;
2603
+ let pos;
2556
2604
  if (flags & 1) {
2557
- this.stream.readByte();
2605
+ volume = this.stream.readByte();
2558
2606
  }
2559
2607
  if (flags & 2) {
2560
- this.stream.readByte();
2608
+ attenuation = this.stream.readByte();
2561
2609
  }
2562
2610
  if (flags & 16) {
2563
- this.stream.readByte();
2611
+ offset = this.stream.readByte();
2564
2612
  }
2565
2613
  if (flags & 8) {
2566
- this.stream.readShort();
2614
+ ent = this.stream.readShort();
2567
2615
  }
2568
2616
  if (flags & 4) {
2569
- const pos = { x: 0, y: 0, z: 0 };
2570
- this.stream.readPos(pos);
2617
+ const p = { x: 0, y: 0, z: 0 };
2618
+ this.stream.readPos(p);
2619
+ pos = p;
2620
+ }
2621
+ if (this.handler) {
2622
+ this.handler.onSound(flags, soundNum, volume, attenuation, offset, ent, pos);
2571
2623
  }
2572
2624
  }
2573
2625
  parseMuzzleFlash() {
2574
2626
  const ent = this.stream.readShort();
2575
2627
  const weapon = this.stream.readByte();
2628
+ if (this.handler) this.handler.onMuzzleFlash(ent, weapon);
2576
2629
  }
2577
2630
  parseMuzzleFlash2() {
2578
2631
  const ent = this.stream.readShort();
2579
2632
  const weapon = this.stream.readByte();
2633
+ if (this.handler) this.handler.onMuzzleFlash2(ent, weapon);
2580
2634
  }
2581
2635
  parseTempEntity() {
2582
2636
  const type = this.stream.readByte();
2583
2637
  const pos = { x: 0, y: 0, z: 0 };
2584
2638
  const pos2 = { x: 0, y: 0, z: 0 };
2585
2639
  const dir = { x: 0, y: 0, z: 0 };
2640
+ let cnt;
2641
+ let color;
2642
+ let ent;
2643
+ let srcEnt;
2644
+ let destEnt;
2586
2645
  switch (type) {
2587
2646
  case TempEntity.BLOOD:
2588
2647
  this.stream.readPos(pos);
@@ -2591,29 +2650,23 @@ var NetworkMessageParser = class {
2591
2650
  case TempEntity.GUNSHOT:
2592
2651
  case TempEntity.SPARKS:
2593
2652
  case TempEntity.BULLET_SPARKS:
2594
- this.stream.readPos(pos);
2595
- this.stream.readDir(dir);
2596
- break;
2597
2653
  case TempEntity.SCREEN_SPARKS:
2598
2654
  case TempEntity.SHIELD_SPARKS:
2599
- this.stream.readPos(pos);
2600
- this.stream.readDir(dir);
2601
- break;
2602
2655
  case TempEntity.SHOTGUN:
2603
2656
  this.stream.readPos(pos);
2604
2657
  this.stream.readDir(dir);
2605
2658
  break;
2606
2659
  case TempEntity.SPLASH:
2607
- this.stream.readByte();
2660
+ cnt = this.stream.readByte();
2608
2661
  this.stream.readPos(pos);
2609
2662
  this.stream.readDir(dir);
2610
- this.stream.readByte();
2663
+ color = this.stream.readByte();
2611
2664
  break;
2612
2665
  case TempEntity.LASER_SPARKS:
2613
- this.stream.readByte();
2666
+ cnt = this.stream.readByte();
2614
2667
  this.stream.readPos(pos);
2615
2668
  this.stream.readDir(dir);
2616
- this.stream.readByte();
2669
+ color = this.stream.readByte();
2617
2670
  break;
2618
2671
  case TempEntity.BLUEHYPERBLASTER:
2619
2672
  this.stream.readPos(pos);
@@ -2630,21 +2683,13 @@ var NetworkMessageParser = class {
2630
2683
  case TempEntity.EXPLOSION2:
2631
2684
  case TempEntity.GRENADE_EXPLOSION:
2632
2685
  case TempEntity.GRENADE_EXPLOSION_WATER:
2633
- this.stream.readPos(pos);
2634
- break;
2635
2686
  case TempEntity.PLASMA_EXPLOSION:
2636
- this.stream.readPos(pos);
2637
- break;
2638
2687
  case TempEntity.EXPLOSION1:
2639
2688
  case TempEntity.EXPLOSION1_BIG:
2640
2689
  case TempEntity.ROCKET_EXPLOSION:
2641
2690
  case TempEntity.ROCKET_EXPLOSION_WATER:
2642
2691
  case TempEntity.EXPLOSION1_NP:
2643
- this.stream.readPos(pos);
2644
- break;
2645
2692
  case TempEntity.BFG_EXPLOSION:
2646
- this.stream.readPos(pos);
2647
- break;
2648
2693
  case TempEntity.BFG_BIGEXPLOSION:
2649
2694
  this.stream.readPos(pos);
2650
2695
  break;
@@ -2658,7 +2703,7 @@ var NetworkMessageParser = class {
2658
2703
  break;
2659
2704
  case TempEntity.PARASITE_ATTACK:
2660
2705
  case TempEntity.MEDIC_CABLE_ATTACK:
2661
- this.stream.readShort();
2706
+ ent = this.stream.readShort();
2662
2707
  this.stream.readPos(pos);
2663
2708
  this.stream.readPos(pos2);
2664
2709
  break;
@@ -2666,26 +2711,26 @@ var NetworkMessageParser = class {
2666
2711
  this.stream.readPos(pos);
2667
2712
  break;
2668
2713
  case TempEntity.GRAPPLE_CABLE:
2669
- this.stream.readShort();
2714
+ ent = this.stream.readShort();
2670
2715
  this.stream.readPos(pos);
2671
2716
  this.stream.readPos(pos2);
2672
2717
  this.stream.readPos(dir);
2673
2718
  break;
2674
2719
  case TempEntity.WELDING_SPARKS:
2675
- this.stream.readByte();
2720
+ cnt = this.stream.readByte();
2676
2721
  this.stream.readPos(pos);
2677
2722
  this.stream.readDir(dir);
2678
- this.stream.readByte();
2723
+ color = this.stream.readByte();
2679
2724
  break;
2680
2725
  case TempEntity.GREENBLOOD:
2681
2726
  this.stream.readPos(pos);
2682
2727
  this.stream.readDir(dir);
2683
2728
  break;
2684
2729
  case TempEntity.TUNNEL_SPARKS:
2685
- this.stream.readByte();
2730
+ cnt = this.stream.readByte();
2686
2731
  this.stream.readPos(pos);
2687
2732
  this.stream.readDir(dir);
2688
- this.stream.readByte();
2733
+ color = this.stream.readByte();
2689
2734
  break;
2690
2735
  case TempEntity.BLASTER2:
2691
2736
  case TempEntity.FLECHETTE:
@@ -2693,8 +2738,8 @@ var NetworkMessageParser = class {
2693
2738
  this.stream.readDir(dir);
2694
2739
  break;
2695
2740
  case TempEntity.LIGHTNING:
2696
- this.stream.readShort();
2697
- this.stream.readShort();
2741
+ srcEnt = this.stream.readShort();
2742
+ destEnt = this.stream.readShort();
2698
2743
  this.stream.readPos(pos);
2699
2744
  this.stream.readPos(pos2);
2700
2745
  break;
@@ -2707,29 +2752,26 @@ var NetworkMessageParser = class {
2707
2752
  break;
2708
2753
  case TempEntity.FLASHLIGHT:
2709
2754
  this.stream.readPos(pos);
2710
- this.stream.readShort();
2755
+ ent = this.stream.readShort();
2711
2756
  break;
2712
2757
  case TempEntity.FORCEWALL:
2713
2758
  this.stream.readPos(pos);
2714
2759
  this.stream.readPos(pos2);
2715
- this.stream.readByte();
2760
+ color = this.stream.readByte();
2716
2761
  break;
2717
2762
  case TempEntity.HEATBEAM:
2718
- this.stream.readShort();
2763
+ ent = this.stream.readShort();
2719
2764
  this.stream.readPos(pos);
2720
2765
  this.stream.readPos(pos2);
2721
2766
  this.stream.readPos(dir);
2722
2767
  break;
2723
2768
  case TempEntity.MONSTER_HEATBEAM:
2724
- this.stream.readShort();
2769
+ ent = this.stream.readShort();
2725
2770
  this.stream.readPos(pos);
2726
2771
  this.stream.readPos(pos2);
2727
2772
  this.stream.readPos(dir);
2728
2773
  break;
2729
2774
  case TempEntity.HEATBEAM_SPARKS:
2730
- this.stream.readPos(pos);
2731
- this.stream.readDir(dir);
2732
- break;
2733
2775
  case TempEntity.HEATBEAM_STEAM:
2734
2776
  this.stream.readPos(pos);
2735
2777
  this.stream.readDir(dir);
@@ -2737,17 +2779,17 @@ var NetworkMessageParser = class {
2737
2779
  case TempEntity.STEAM:
2738
2780
  const steamId = this.stream.readShort();
2739
2781
  if (steamId !== -1) {
2740
- this.stream.readByte();
2782
+ cnt = this.stream.readByte();
2741
2783
  this.stream.readPos(pos);
2742
2784
  this.stream.readDir(dir);
2743
- this.stream.readByte();
2785
+ color = this.stream.readByte();
2744
2786
  this.stream.readShort();
2745
2787
  this.stream.readLong();
2746
2788
  } else {
2747
- this.stream.readByte();
2789
+ cnt = this.stream.readByte();
2748
2790
  this.stream.readPos(pos);
2749
2791
  this.stream.readDir(dir);
2750
- this.stream.readByte();
2792
+ color = this.stream.readByte();
2751
2793
  this.stream.readShort();
2752
2794
  }
2753
2795
  break;
@@ -2774,7 +2816,7 @@ var NetworkMessageParser = class {
2774
2816
  this.stream.readPos(pos);
2775
2817
  break;
2776
2818
  case TempEntity.WIDOWBEAMOUT:
2777
- const wbId = this.stream.readShort();
2819
+ this.stream.readShort();
2778
2820
  this.stream.readPos(pos);
2779
2821
  break;
2780
2822
  case TempEntity.NUKEBLAST:
@@ -2787,97 +2829,139 @@ var NetworkMessageParser = class {
2787
2829
  console.warn(`CL_ParseTEnt: bad type ${type}`);
2788
2830
  break;
2789
2831
  }
2832
+ if (this.handler) {
2833
+ this.handler.onTempEntity(type, pos, pos2, dir, cnt, color, ent, srcEnt, destEnt);
2834
+ }
2790
2835
  }
2791
2836
  parseSpawnBaseline() {
2792
2837
  const bits = this.parseEntityBits();
2793
- this.parseDelta(createEmptyEntityState(), createEmptyEntityState(), bits.number, bits.bits);
2838
+ const entity = createEmptyEntityState();
2839
+ this.parseDelta(createEmptyEntityState(), entity, bits.number, bits.bits);
2840
+ if (this.handler) {
2841
+ this.handler.onSpawnBaseline(entity);
2842
+ }
2794
2843
  }
2795
2844
  parseFrame() {
2796
2845
  const serverFrame = this.stream.readLong();
2797
2846
  const deltaFrame = this.stream.readLong();
2798
2847
  const surpressCount = this.stream.readByte();
2799
2848
  const areaBytes = this.stream.readByte();
2800
- this.stream.readData(areaBytes);
2849
+ const areaBits = this.stream.readData(areaBytes);
2801
2850
  const piCmd = this.stream.readByte();
2802
2851
  if (piCmd !== ServerCommand.playerinfo) {
2803
2852
  throw new Error(`Expected svc_playerinfo after svc_frame, got ${piCmd}`);
2804
2853
  }
2805
- this.parsePlayerState();
2854
+ const playerState = this.parsePlayerState();
2806
2855
  const peCmd = this.stream.readByte();
2807
2856
  if (peCmd !== ServerCommand.packetentities && peCmd !== ServerCommand.deltapacketentities) {
2808
2857
  throw new Error(`Expected svc_packetentities after svc_playerinfo, got ${peCmd}`);
2809
2858
  }
2810
- this.parsePacketEntities(peCmd === ServerCommand.deltapacketentities);
2859
+ const isDelta = peCmd === ServerCommand.deltapacketentities;
2860
+ const entities = this.collectPacketEntities();
2861
+ if (this.handler) {
2862
+ this.handler.onFrame({
2863
+ serverFrame,
2864
+ deltaFrame,
2865
+ surpressCount,
2866
+ areaBytes,
2867
+ areaBits,
2868
+ playerState,
2869
+ packetEntities: {
2870
+ delta: isDelta,
2871
+ entities
2872
+ }
2873
+ });
2874
+ }
2811
2875
  }
2812
2876
  parsePlayerState() {
2877
+ const ps = createEmptyProtocolPlayerState();
2813
2878
  const flags = this.stream.readShort();
2814
- if (flags & 1) this.stream.readByte();
2879
+ if (flags & 1) ps.pm_type = this.stream.readByte();
2815
2880
  if (flags & 2) {
2816
- this.stream.readShort();
2817
- this.stream.readShort();
2818
- this.stream.readShort();
2881
+ ps.origin.x = this.stream.readShort() * 0.125;
2882
+ ps.origin.y = this.stream.readShort() * 0.125;
2883
+ ps.origin.z = this.stream.readShort() * 0.125;
2819
2884
  }
2820
2885
  if (flags & 4) {
2821
- this.stream.readShort();
2822
- this.stream.readShort();
2823
- this.stream.readShort();
2886
+ ps.velocity.x = this.stream.readShort() * 0.125;
2887
+ ps.velocity.y = this.stream.readShort() * 0.125;
2888
+ ps.velocity.z = this.stream.readShort() * 0.125;
2824
2889
  }
2825
- if (flags & 8) this.stream.readByte();
2826
- if (flags & 16) this.stream.readByte();
2827
- if (flags & 32) this.stream.readShort();
2890
+ if (flags & 8) ps.pm_time = this.stream.readByte();
2891
+ if (flags & 16) ps.pm_flags = this.stream.readByte();
2892
+ if (flags & 32) ps.gravity = this.stream.readShort();
2828
2893
  if (flags & 64) {
2829
- this.stream.readShort();
2830
- this.stream.readShort();
2831
- this.stream.readShort();
2894
+ ps.delta_angles.x = this.stream.readShort() * (180 / 32768);
2895
+ ps.delta_angles.y = this.stream.readShort() * (180 / 32768);
2896
+ ps.delta_angles.z = this.stream.readShort() * (180 / 32768);
2832
2897
  }
2833
2898
  if (flags & 128) {
2834
- this.stream.readChar();
2835
- this.stream.readChar();
2836
- this.stream.readChar();
2899
+ ps.viewoffset.x = this.stream.readChar() * 0.25;
2900
+ ps.viewoffset.y = this.stream.readChar() * 0.25;
2901
+ ps.viewoffset.z = this.stream.readChar() * 0.25;
2837
2902
  }
2838
2903
  if (flags & 256) {
2839
- this.stream.readAngle16();
2840
- this.stream.readAngle16();
2841
- this.stream.readAngle16();
2904
+ ps.viewangles.x = this.stream.readAngle16();
2905
+ ps.viewangles.y = this.stream.readAngle16();
2906
+ ps.viewangles.z = this.stream.readAngle16();
2842
2907
  }
2843
2908
  if (flags & 512) {
2844
- this.stream.readChar();
2845
- this.stream.readChar();
2846
- this.stream.readChar();
2909
+ ps.kick_angles.x = this.stream.readChar() * 0.25;
2910
+ ps.kick_angles.y = this.stream.readChar() * 0.25;
2911
+ ps.kick_angles.z = this.stream.readChar() * 0.25;
2847
2912
  }
2848
- if (flags & 4096) this.stream.readByte();
2913
+ if (flags & 4096) ps.gun_index = this.stream.readByte();
2849
2914
  if (flags & 8192) {
2850
- this.stream.readByte();
2851
- this.stream.readChar();
2852
- this.stream.readChar();
2853
- this.stream.readChar();
2854
- this.stream.readChar();
2855
- this.stream.readChar();
2856
- this.stream.readChar();
2915
+ ps.gun_frame = this.stream.readByte();
2916
+ ps.gun_offset.x = this.stream.readChar() * 0.25;
2917
+ ps.gun_offset.y = this.stream.readChar() * 0.25;
2918
+ ps.gun_offset.z = this.stream.readChar() * 0.25;
2919
+ ps.gun_angles.x = this.stream.readChar() * 0.25;
2920
+ ps.gun_angles.y = this.stream.readChar() * 0.25;
2921
+ ps.gun_angles.z = this.stream.readChar() * 0.25;
2857
2922
  }
2858
2923
  if (flags & 1024) {
2859
- this.stream.readByte();
2860
- this.stream.readByte();
2861
- this.stream.readByte();
2862
- this.stream.readByte();
2924
+ ps.blend[0] = this.stream.readByte();
2925
+ ps.blend[1] = this.stream.readByte();
2926
+ ps.blend[2] = this.stream.readByte();
2927
+ ps.blend[3] = this.stream.readByte();
2863
2928
  }
2864
- if (flags & 2048) this.stream.readByte();
2865
- if (flags & 16384) this.stream.readByte();
2929
+ if (flags & 2048) ps.fov = this.stream.readByte();
2930
+ if (flags & 16384) ps.rdflags = this.stream.readByte();
2866
2931
  const statbits = this.stream.readLong();
2867
2932
  for (let i = 0; i < 32; i++) {
2868
2933
  if (statbits & 1 << i) {
2869
- this.stream.readShort();
2934
+ ps.stats[i] = this.stream.readShort();
2870
2935
  }
2871
2936
  }
2937
+ return ps;
2872
2938
  }
2873
2939
  parsePacketEntities(delta) {
2940
+ const entities = this.collectPacketEntities();
2941
+ if (this.handler) {
2942
+ this.handler.onFrame({
2943
+ serverFrame: 0,
2944
+ deltaFrame: 0,
2945
+ surpressCount: 0,
2946
+ areaBytes: 0,
2947
+ areaBits: new Uint8Array(),
2948
+ playerState: createEmptyProtocolPlayerState(),
2949
+ packetEntities: { delta, entities }
2950
+ });
2951
+ }
2952
+ }
2953
+ collectPacketEntities() {
2954
+ const entities = [];
2874
2955
  while (true) {
2875
2956
  const bits = this.parseEntityBits();
2876
2957
  if (bits.number === 0) {
2877
2958
  break;
2878
2959
  }
2879
- this.parseDelta(createEmptyEntityState(), createEmptyEntityState(), bits.number, bits.bits);
2960
+ const entity = createEmptyEntityState();
2961
+ this.parseDelta(createEmptyEntityState(), entity, bits.number, bits.bits);
2962
+ entities.push(entity);
2880
2963
  }
2964
+ return entities;
2881
2965
  }
2882
2966
  parseEntityBits() {
2883
2967
  let total = this.stream.readByte();
@@ -2921,6 +3005,7 @@ var NetworkMessageParser = class {
2921
3005
  to.event = from.event;
2922
3006
  to.solid = from.solid;
2923
3007
  to.number = number;
3008
+ to.bits = bits;
2924
3009
  if (bits & U_MODEL) to.modelindex = this.stream.readByte();
2925
3010
  if (bits & U_MODEL2) to.modelindex2 = this.stream.readByte();
2926
3011
  if (bits & U_MODEL3) to.modelindex3 = this.stream.readByte();
@@ -2966,6 +3051,13 @@ var NetworkMessageParser = class {
2966
3051
  if (bits & U_SOLID) to.solid = this.stream.readShort();
2967
3052
  }
2968
3053
  };
3054
+ var PlaybackState = /* @__PURE__ */ ((PlaybackState2) => {
3055
+ PlaybackState2[PlaybackState2["Stopped"] = 0] = "Stopped";
3056
+ PlaybackState2[PlaybackState2["Playing"] = 1] = "Playing";
3057
+ PlaybackState2[PlaybackState2["Paused"] = 2] = "Paused";
3058
+ PlaybackState2[PlaybackState2["Finished"] = 3] = "Finished";
3059
+ return PlaybackState2;
3060
+ })(PlaybackState || {});
2969
3061
  var DemoPlaybackController = class {
2970
3062
  // ms (10Hz default)
2971
3063
  constructor() {
@@ -2975,6 +3067,9 @@ var DemoPlaybackController = class {
2975
3067
  this.accumulatedTime = 0;
2976
3068
  this.frameDuration = 100;
2977
3069
  }
3070
+ setHandler(handler) {
3071
+ this.handler = handler;
3072
+ }
2978
3073
  loadDemo(buffer) {
2979
3074
  this.reader = new DemoReader(buffer);
2980
3075
  this.state = 0;
@@ -3012,7 +3107,7 @@ var DemoPlaybackController = class {
3012
3107
  this.state = 3;
3013
3108
  return;
3014
3109
  }
3015
- const parser = new NetworkMessageParser(block.data);
3110
+ const parser = new NetworkMessageParser(block.data, this.handler);
3016
3111
  parser.parseMessage();
3017
3112
  this.accumulatedTime -= this.frameDuration;
3018
3113
  }
@@ -3190,32 +3285,32 @@ var WaterLevel = /* @__PURE__ */ ((WaterLevel3) => {
3190
3285
  WaterLevel3[WaterLevel3["Under"] = 3] = "Under";
3191
3286
  return WaterLevel3;
3192
3287
  })(WaterLevel || {});
3193
- var PmFlag = /* @__PURE__ */ ((PmFlag2) => {
3194
- PmFlag2[PmFlag2["Ducked"] = 1] = "Ducked";
3195
- PmFlag2[PmFlag2["JumpHeld"] = 2] = "JumpHeld";
3196
- PmFlag2[PmFlag2["OnGround"] = 4] = "OnGround";
3197
- PmFlag2[PmFlag2["TimeWaterJump"] = 8] = "TimeWaterJump";
3198
- PmFlag2[PmFlag2["TimeLand"] = 16] = "TimeLand";
3199
- PmFlag2[PmFlag2["TimeTeleport"] = 32] = "TimeTeleport";
3200
- PmFlag2[PmFlag2["NoPositionalPrediction"] = 64] = "NoPositionalPrediction";
3201
- PmFlag2[PmFlag2["OnLadder"] = 128] = "OnLadder";
3202
- PmFlag2[PmFlag2["NoAngularPrediction"] = 256] = "NoAngularPrediction";
3203
- PmFlag2[PmFlag2["IgnorePlayerCollision"] = 512] = "IgnorePlayerCollision";
3204
- PmFlag2[PmFlag2["TimeTrick"] = 1024] = "TimeTrick";
3205
- return PmFlag2;
3288
+ var PmFlag = /* @__PURE__ */ ((PmFlag22) => {
3289
+ PmFlag22[PmFlag22["Ducked"] = 1] = "Ducked";
3290
+ PmFlag22[PmFlag22["JumpHeld"] = 2] = "JumpHeld";
3291
+ PmFlag22[PmFlag22["OnGround"] = 4] = "OnGround";
3292
+ PmFlag22[PmFlag22["TimeWaterJump"] = 8] = "TimeWaterJump";
3293
+ PmFlag22[PmFlag22["TimeLand"] = 16] = "TimeLand";
3294
+ PmFlag22[PmFlag22["TimeTeleport"] = 32] = "TimeTeleport";
3295
+ PmFlag22[PmFlag22["NoPositionalPrediction"] = 64] = "NoPositionalPrediction";
3296
+ PmFlag22[PmFlag22["OnLadder"] = 128] = "OnLadder";
3297
+ PmFlag22[PmFlag22["NoAngularPrediction"] = 256] = "NoAngularPrediction";
3298
+ PmFlag22[PmFlag22["IgnorePlayerCollision"] = 512] = "IgnorePlayerCollision";
3299
+ PmFlag22[PmFlag22["TimeTrick"] = 1024] = "TimeTrick";
3300
+ return PmFlag22;
3206
3301
  })(PmFlag || {});
3207
3302
  function hasPmFlag(flags, flag) {
3208
3303
  return (flags & flag) !== 0;
3209
3304
  }
3210
- var PmType = /* @__PURE__ */ ((PmType2) => {
3211
- PmType2[PmType2["Normal"] = 0] = "Normal";
3212
- PmType2[PmType2["Grapple"] = 1] = "Grapple";
3213
- PmType2[PmType2["NoClip"] = 2] = "NoClip";
3214
- PmType2[PmType2["Spectator"] = 3] = "Spectator";
3215
- PmType2[PmType2["Dead"] = 4] = "Dead";
3216
- PmType2[PmType2["Gib"] = 5] = "Gib";
3217
- PmType2[PmType2["Freeze"] = 6] = "Freeze";
3218
- return PmType2;
3305
+ var PmType = /* @__PURE__ */ ((PmType22) => {
3306
+ PmType22[PmType22["Normal"] = 0] = "Normal";
3307
+ PmType22[PmType22["Grapple"] = 1] = "Grapple";
3308
+ PmType22[PmType22["NoClip"] = 2] = "NoClip";
3309
+ PmType22[PmType22["Spectator"] = 3] = "Spectator";
3310
+ PmType22[PmType22["Dead"] = 4] = "Dead";
3311
+ PmType22[PmType22["Gib"] = 5] = "Gib";
3312
+ PmType22[PmType22["Freeze"] = 6] = "Freeze";
3313
+ return PmType22;
3219
3314
  })(PmType || {});
3220
3315
  var PlayerButton = /* @__PURE__ */ ((PlayerButton2) => {
3221
3316
  PlayerButton2[PlayerButton2["None"] = 0] = "None";
@@ -3845,6 +3940,158 @@ var Draw_Hud = (renderer, ps, client, health, armor, ammo, stats) => {
3845
3940
  renderer.end2D();
3846
3941
  };
3847
3942
 
3943
+ // src/demo/handler.ts
3944
+ var MAX_CONFIGSTRINGS3 = 2048;
3945
+ var ClientNetworkHandler = class {
3946
+ constructor() {
3947
+ this.configstrings = new Array(MAX_CONFIGSTRINGS3).fill("");
3948
+ this.entities = /* @__PURE__ */ new Map();
3949
+ // Current frame entities
3950
+ this.baselines = /* @__PURE__ */ new Map();
3951
+ this.latestFrame = null;
3952
+ // Stats for HUD
3953
+ this.stats = new Array(32).fill(0);
3954
+ this.inventory = new Array(256).fill(0);
3955
+ }
3956
+ onServerData(protocol, serverCount, attractLoop, gameDir, playerNum, levelName) {
3957
+ console.log(`Demo: Server Data - Protocol: ${protocol}, Level: ${levelName}`);
3958
+ this.configstrings.fill("");
3959
+ this.entities.clear();
3960
+ this.baselines.clear();
3961
+ this.latestFrame = null;
3962
+ }
3963
+ onConfigString(index, str3) {
3964
+ this.configstrings[index] = str3;
3965
+ }
3966
+ onSpawnBaseline(entity) {
3967
+ this.baselines.set(entity.number, structuredClone(entity));
3968
+ }
3969
+ onFrame(frame) {
3970
+ this.latestFrame = frame;
3971
+ this.stats = [...frame.playerState.stats];
3972
+ const packetEntities = frame.packetEntities;
3973
+ const newEntities = /* @__PURE__ */ new Map();
3974
+ if (packetEntities.delta) {
3975
+ for (const [num, ent] of this.entities) {
3976
+ newEntities.set(num, structuredClone(ent));
3977
+ }
3978
+ } else {
3979
+ }
3980
+ for (const partial of packetEntities.entities) {
3981
+ if (partial.bits & U_REMOVE) {
3982
+ newEntities.delete(partial.number);
3983
+ continue;
3984
+ }
3985
+ const number = partial.number;
3986
+ let source;
3987
+ if (packetEntities.delta && this.entities.has(number)) {
3988
+ source = this.entities.get(number);
3989
+ } else if (this.baselines.has(number)) {
3990
+ source = this.baselines.get(number);
3991
+ } else {
3992
+ source = createEmptyEntityState();
3993
+ }
3994
+ const final = structuredClone(source);
3995
+ this.applyDelta(final, partial);
3996
+ newEntities.set(number, final);
3997
+ }
3998
+ this.entities = newEntities;
3999
+ }
4000
+ applyDelta(to, from) {
4001
+ const bits = from.bits;
4002
+ to.number = from.number;
4003
+ if (bits & U_MODEL) to.modelindex = from.modelindex;
4004
+ if (bits & U_MODEL2) to.modelindex2 = from.modelindex2;
4005
+ if (bits & U_MODEL3) to.modelindex3 = from.modelindex3;
4006
+ if (bits & U_MODEL4) to.modelindex4 = from.modelindex4;
4007
+ if (bits & U_FRAME8) to.frame = from.frame;
4008
+ if (bits & U_FRAME16) to.frame = from.frame;
4009
+ if (bits & U_SKIN8 || bits & U_SKIN16) to.skinnum = from.skinnum;
4010
+ if (bits & U_EFFECTS8 || bits & U_EFFECTS16) to.effects = from.effects;
4011
+ if (bits & U_RENDERFX8 || bits & U_RENDERFX16) to.renderfx = from.renderfx;
4012
+ if (bits & U_ORIGIN1) to.origin.x = from.origin.x;
4013
+ if (bits & U_ORIGIN2) to.origin.y = from.origin.y;
4014
+ if (bits & U_ORIGIN3) to.origin.z = from.origin.z;
4015
+ if (bits & U_ANGLE1) to.angles.x = from.angles.x;
4016
+ if (bits & U_ANGLE2) to.angles.y = from.angles.y;
4017
+ if (bits & U_ANGLE3) to.angles.z = from.angles.z;
4018
+ if (bits & U_OLDORIGIN) {
4019
+ to.old_origin.x = from.old_origin.x;
4020
+ to.old_origin.y = from.old_origin.y;
4021
+ to.old_origin.z = from.old_origin.z;
4022
+ }
4023
+ if (bits & U_SOUND) to.sound = from.sound;
4024
+ if (bits & U_EVENT) to.event = from.event;
4025
+ if (bits & U_SOLID) to.solid = from.solid;
4026
+ }
4027
+ onCenterPrint(msg) {
4028
+ console.log(`[Center]: ${msg}`);
4029
+ }
4030
+ onStuffText(msg) {
4031
+ }
4032
+ onPrint(level, msg) {
4033
+ }
4034
+ onSound(flags, soundNum, volume, attenuation, offset, ent, pos) {
4035
+ }
4036
+ onTempEntity(type, pos, pos2, dir, cnt, color, ent, srcEnt, destEnt) {
4037
+ }
4038
+ onLayout(layout) {
4039
+ }
4040
+ onInventory(inventory) {
4041
+ this.inventory = [...inventory];
4042
+ }
4043
+ onMuzzleFlash(ent, weapon) {
4044
+ }
4045
+ onMuzzleFlash2(ent, weapon) {
4046
+ }
4047
+ onDisconnect() {
4048
+ }
4049
+ onReconnect() {
4050
+ }
4051
+ onDownload(size, percent, data) {
4052
+ }
4053
+ getPredictionState() {
4054
+ if (!this.latestFrame) return defaultPredictionState();
4055
+ const ps = this.latestFrame.playerState;
4056
+ const inventory = {
4057
+ ammo: {
4058
+ caps: [],
4059
+ counts: []
4060
+ },
4061
+ ownedWeapons: /* @__PURE__ */ new Set(),
4062
+ armor: null,
4063
+ powerups: /* @__PURE__ */ new Map(),
4064
+ keys: /* @__PURE__ */ new Set()
4065
+ };
4066
+ const origin = { ...ps.origin };
4067
+ const velocity = { ...ps.velocity };
4068
+ const viewangles = { ...ps.viewangles };
4069
+ const deltaAngles = { ...ps.delta_angles };
4070
+ return {
4071
+ origin,
4072
+ velocity,
4073
+ viewangles,
4074
+ pmFlags: ps.pm_flags,
4075
+ pmType: ps.pm_type,
4076
+ waterlevel: WaterLevel.None,
4077
+ gravity: ps.gravity,
4078
+ deltaAngles,
4079
+ client: {
4080
+ inventory,
4081
+ weaponStates: {
4082
+ states: /* @__PURE__ */ new Map()
4083
+ }
4084
+ },
4085
+ health: ps.stats[1],
4086
+ // STAT_HEALTH
4087
+ armor: ps.stats[4],
4088
+ // STAT_ARMOR
4089
+ ammo: ps.stats[2]
4090
+ // STAT_AMMO
4091
+ };
4092
+ }
4093
+ };
4094
+
3848
4095
  // src/input/bindings.ts
3849
4096
  var DEFAULT_BINDINGS = [
3850
4097
  { code: "KeyW", command: "+forward" },
@@ -4338,6 +4585,8 @@ function createClient(imports) {
4338
4585
  const prediction = new ClientPrediction(imports.engine.trace);
4339
4586
  const view = new ViewEffects();
4340
4587
  const demoPlayback = new DemoPlaybackController();
4588
+ const demoHandler = new ClientNetworkHandler();
4589
+ demoPlayback.setHandler(demoHandler);
4341
4590
  let latestFrame;
4342
4591
  let lastRendered;
4343
4592
  let lastView;
@@ -4354,14 +4603,19 @@ function createClient(imports) {
4354
4603
  return prediction.enqueueCommand(command);
4355
4604
  },
4356
4605
  render(sample) {
4357
- if (sample.latest?.state) {
4358
- prediction.setAuthoritative(sample.latest);
4359
- latestFrame = sample.latest;
4360
- }
4361
- if (sample.previous?.state && sample.latest?.state) {
4362
- lastRendered = interpolatePredictionState(sample.previous.state, sample.latest.state, sample.alpha);
4606
+ const playbackState = demoPlayback.getState();
4607
+ if (playbackState === PlaybackState.Playing) {
4608
+ lastRendered = demoHandler.getPredictionState();
4363
4609
  } else {
4364
- lastRendered = sample.latest?.state ?? sample.previous?.state ?? prediction.getPredictedState();
4610
+ if (sample.latest?.state) {
4611
+ prediction.setAuthoritative(sample.latest);
4612
+ latestFrame = sample.latest;
4613
+ }
4614
+ if (sample.previous?.state && sample.latest?.state) {
4615
+ lastRendered = interpolatePredictionState(sample.previous.state, sample.latest.state, sample.alpha);
4616
+ } else {
4617
+ lastRendered = sample.latest?.state ?? sample.previous?.state ?? prediction.getPredictedState();
4618
+ }
4365
4619
  }
4366
4620
  const frameTimeMs = sample.latest && sample.previous ? Math.max(0, sample.latest.timeMs - sample.previous.timeMs) : 0;
4367
4621
  lastView = view.sample(lastRendered, frameTimeMs);
@@ -4420,7 +4674,8 @@ function createClient(imports) {
4420
4674
  get camera() {
4421
4675
  return camera;
4422
4676
  },
4423
- demoPlayback
4677
+ demoPlayback,
4678
+ demoHandler
4424
4679
  };
4425
4680
  }
4426
4681
  // Annotate the CommonJS export names for ESM import in node: