@lagless/desync-diagnostics 0.0.48

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 (40) hide show
  1. package/LICENSE +26 -0
  2. package/out-tsc/index.d.ts +13 -0
  3. package/out-tsc/index.d.ts.map +1 -0
  4. package/out-tsc/lib/attach.d.ts +5 -0
  5. package/out-tsc/lib/attach.d.ts.map +1 -0
  6. package/out-tsc/lib/diagnostics-collector.d.ts +41 -0
  7. package/out-tsc/lib/diagnostics-collector.d.ts.map +1 -0
  8. package/out-tsc/lib/diagnostics-protocol.d.ts +52 -0
  9. package/out-tsc/lib/diagnostics-protocol.d.ts.map +1 -0
  10. package/out-tsc/lib/divergence-analysis.d.ts +21 -0
  11. package/out-tsc/lib/divergence-analysis.d.ts.map +1 -0
  12. package/out-tsc/lib/hash-bytes.d.ts +6 -0
  13. package/out-tsc/lib/hash-bytes.d.ts.map +1 -0
  14. package/out-tsc/lib/performance-profiler.d.ts +43 -0
  15. package/out-tsc/lib/performance-profiler.d.ts.map +1 -0
  16. package/out-tsc/lib/report-generator.d.ts +55 -0
  17. package/out-tsc/lib/report-generator.d.ts.map +1 -0
  18. package/out-tsc/lib/types.d.ts +39 -0
  19. package/out-tsc/lib/types.d.ts.map +1 -0
  20. package/out-tsc/lib/use-desync-diagnostics.d.ts +9 -0
  21. package/out-tsc/lib/use-desync-diagnostics.d.ts.map +1 -0
  22. package/package.json +38 -0
  23. package/src/index.ts +26 -0
  24. package/src/lib/attach.ts +10 -0
  25. package/src/lib/diagnostics-collector.spec.ts +354 -0
  26. package/src/lib/diagnostics-collector.ts +210 -0
  27. package/src/lib/diagnostics-protocol.ts +44 -0
  28. package/src/lib/divergence-analysis.spec.ts +178 -0
  29. package/src/lib/divergence-analysis.ts +179 -0
  30. package/src/lib/hash-bytes.spec.ts +29 -0
  31. package/src/lib/hash-bytes.ts +11 -0
  32. package/src/lib/performance-profiler.spec.ts +412 -0
  33. package/src/lib/performance-profiler.ts +245 -0
  34. package/src/lib/report-generator.spec.ts +147 -0
  35. package/src/lib/report-generator.ts +117 -0
  36. package/src/lib/types.ts +41 -0
  37. package/src/lib/use-desync-diagnostics.ts +101 -0
  38. package/tsconfig.json +21 -0
  39. package/tsconfig.tsbuildinfo +1 -0
  40. package/vite.config.ts +20 -0
package/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Creative Commons Attribution-NonCommercial 4.0 International
2
+
3
+ Copyright (c) 2025 Lagless
4
+
5
+ This work is licensed under the Creative Commons
6
+ Attribution-NonCommercial 4.0 International License.
7
+
8
+ You are free to:
9
+
10
+ Share — copy and redistribute the material in any medium or format
11
+ Adapt — remix, transform, and build upon the material
12
+
13
+ Under the following terms:
14
+
15
+ Attribution — You must give appropriate credit, provide a link to
16
+ the license, and indicate if changes were made. You may do so in
17
+ any reasonable manner, but not in any way that suggests the licensor
18
+ endorses you or your use.
19
+
20
+ NonCommercial — You may not use the material for commercial purposes.
21
+
22
+ No additional restrictions — You may not apply legal terms or
23
+ technological measures that legally restrict others from doing
24
+ anything the license permits.
25
+
26
+ Full license text: https://creativecommons.org/licenses/by-nc/4.0/legalcode
@@ -0,0 +1,13 @@
1
+ export { DiagnosticsCollector } from './lib/diagnostics-collector.js';
2
+ export { hashBytes } from './lib/hash-bytes.js';
3
+ export { analyzeDivergence } from './lib/divergence-analysis.js';
4
+ export type { DivergenceAnalysis, CheckpointComparison, RollbackOverlapWindow } from './lib/divergence-analysis.js';
5
+ export { attachDesyncDiagnostics } from './lib/attach.js';
6
+ export { generateReport } from './lib/report-generator.js';
7
+ export type { DiagnosticsConfig, TickRecord, RollbackEvent, DiagnosticsStats } from './lib/types.js';
8
+ export type { DiagnosticsReport, CombinedDiagnosticsReport, DiagnosticsReportConfig, DiagnosticsReportSummary, DiagnosticsReportTickRecord, DiagnosticsReportRPC, } from './lib/report-generator.js';
9
+ export { useDesyncDiagnostics, type UseDesyncDiagnosticsOptions } from './lib/use-desync-diagnostics.js';
10
+ export { PerformanceProfiler } from './lib/performance-profiler.js';
11
+ export type { PerformanceStats, SystemTimingStats, TimingStats } from './lib/performance-profiler.js';
12
+ export type { DiagnosticsSummaryMessage, DiagnosticsReportMessage, PerformanceStatsMessage, DiagnosticsChildMessage, RequestDiagnosticsReportMessage, DiagnosticsParentMessage, } from './lib/diagnostics-protocol.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACpH,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACrG,YAAY,EACV,iBAAiB,EACjB,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC3B,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,KAAK,2BAA2B,EAAE,MAAM,iCAAiC,CAAC;AACzG,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACtG,YAAY,EACV,yBAAyB,EACzB,wBAAwB,EACxB,uBAAuB,EACvB,uBAAuB,EACvB,+BAA+B,EAC/B,wBAAwB,GACzB,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ECSRunner } from '@lagless/core';
2
+ import { DiagnosticsCollector } from './diagnostics-collector.js';
3
+ import type { DiagnosticsConfig } from './types.js';
4
+ export declare function attachDesyncDiagnostics(runner: ECSRunner, config?: DiagnosticsConfig): DiagnosticsCollector;
5
+ //# sourceMappingURL=attach.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/lib/attach.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,iBAAiB,GACzB,oBAAoB,CAEtB"}
@@ -0,0 +1,41 @@
1
+ import type { ECSRunner } from '@lagless/core';
2
+ import type { DiagnosticsConfig, TickRecord, RollbackEvent, DiagnosticsStats } from './types.js';
3
+ export declare class DiagnosticsCollector {
4
+ private readonly _runner;
5
+ private readonly _maxPlayers;
6
+ private readonly _bufferSize;
7
+ private readonly _ticks;
8
+ private readonly _hashes;
9
+ private readonly _physicsHashes;
10
+ private readonly _velocityHashes;
11
+ private readonly _verifiedTicks;
12
+ private readonly _wasRollback;
13
+ private readonly _inputCounts;
14
+ private readonly _physicsHashFn;
15
+ private readonly _velocityHashFn;
16
+ private _head;
17
+ private _count;
18
+ private readonly _maxRollbackEvents;
19
+ private readonly _rollbackEvents;
20
+ private _totalRollbacks;
21
+ private _lastRollbackTick;
22
+ private _isResimulating;
23
+ private _preRollbackTick;
24
+ private _lastTickSeen;
25
+ private _prevVerifiedTick;
26
+ private _verifiedTickGapCount;
27
+ private _removeTickHandler;
28
+ private _removeRollbackHandler;
29
+ private _disposed;
30
+ constructor(runner: ECSRunner, config?: DiagnosticsConfig);
31
+ get runner(): ECSRunner;
32
+ get bufferSize(): number;
33
+ get count(): number;
34
+ getTimeline(): TickRecord[];
35
+ getRollbacks(): ReadonlyArray<RollbackEvent>;
36
+ getStats(): DiagnosticsStats;
37
+ dispose(): void;
38
+ private _onRollback;
39
+ private _onTick;
40
+ }
41
+ //# sourceMappingURL=diagnostics-collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics-collector.d.ts","sourceRoot":"","sources":["../../src/lib/diagnostics-collector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAKjG,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAGrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAa;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAa;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAa;IAE1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IACvD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwB;IAExD,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,MAAM,CAAK;IAGnB,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuB;IACvD,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,iBAAiB,CAAK;IAG9B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,aAAa,CAAK;IAG1B,OAAO,CAAC,iBAAiB,CAAM;IAC/B,OAAO,CAAC,qBAAqB,CAAK;IAGlC,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,sBAAsB,CAA6B;IAC3D,OAAO,CAAC,SAAS,CAAS;gBAEd,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,iBAAiB;IA4BzD,IAAW,MAAM,IAAI,SAAS,CAE7B;IAED,IAAW,UAAU,IAAI,MAAM,CAE9B;IAED,IAAW,KAAK,IAAI,MAAM,CAEzB;IAEM,WAAW,IAAI,UAAU,EAAE;IAqB3B,YAAY,IAAI,aAAa,CAAC,aAAa,CAAC;IAI5C,QAAQ,IAAI,gBAAgB;IAiB5B,OAAO,IAAI,IAAI;IAWtB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,OAAO;CAkDhB"}
@@ -0,0 +1,52 @@
1
+ import type { DiagnosticsReport } from './report-generator.js';
2
+ export interface DiagnosticsSummaryMessage {
3
+ type: 'dev-bridge:diagnostics-summary';
4
+ instanceId: string;
5
+ rollbackCount: number;
6
+ lastRollbackTick: number;
7
+ verifiedTickGapCount: number;
8
+ ticksRecorded: number;
9
+ latestHash: number;
10
+ latestPhysicsHash: number;
11
+ latestVelocityHash: number;
12
+ }
13
+ export interface DiagnosticsReportMessage {
14
+ type: 'dev-bridge:diagnostics-report';
15
+ instanceId: string;
16
+ report: DiagnosticsReport;
17
+ }
18
+ export interface PerformanceStatsMessage {
19
+ type: 'dev-bridge:performance-stats';
20
+ instanceId: string;
21
+ tickTime: {
22
+ latest: number;
23
+ min: number;
24
+ max: number;
25
+ avg: number;
26
+ };
27
+ snapshotTime: {
28
+ latest: number;
29
+ min: number;
30
+ max: number;
31
+ avg: number;
32
+ };
33
+ overheadTime: {
34
+ latest: number;
35
+ min: number;
36
+ max: number;
37
+ avg: number;
38
+ };
39
+ systems: Array<{
40
+ name: string;
41
+ latest: number;
42
+ min: number;
43
+ max: number;
44
+ avg: number;
45
+ }>;
46
+ }
47
+ export type DiagnosticsChildMessage = DiagnosticsSummaryMessage | DiagnosticsReportMessage | PerformanceStatsMessage;
48
+ export interface RequestDiagnosticsReportMessage {
49
+ type: 'dev-bridge:request-diagnostics-report';
50
+ }
51
+ export type DiagnosticsParentMessage = RequestDiagnosticsReportMessage;
52
+ //# sourceMappingURL=diagnostics-protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics-protocol.d.ts","sourceRoot":"","sources":["../../src/lib/diagnostics-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI/D,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,gCAAgC,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,+BAA+B,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,8BAA8B,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACxE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzF;AAED,MAAM,MAAM,uBAAuB,GAC/B,yBAAyB,GACzB,wBAAwB,GACxB,uBAAuB,CAAC;AAI5B,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,uCAAuC,CAAC;CAC/C;AAED,MAAM,MAAM,wBAAwB,GAChC,+BAA+B,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { DiagnosticsReport } from './report-generator.js';
2
+ export interface CheckpointComparison {
3
+ tick: number;
4
+ ecsHashes: Record<number, number>;
5
+ physicsHashes: Record<number, number>;
6
+ ecsMatch: boolean;
7
+ physicsMatch: boolean;
8
+ }
9
+ export interface RollbackOverlapWindow {
10
+ startTick: number;
11
+ endTick: number;
12
+ affectedSlots: number[];
13
+ }
14
+ export interface DivergenceAnalysis {
15
+ firstEcsDivergenceTick: number | null;
16
+ firstPhysicsDivergenceTick: number | null;
17
+ checkpointComparison: CheckpointComparison[];
18
+ rollbackOverlapWindows: RollbackOverlapWindow[];
19
+ }
20
+ export declare function analyzeDivergence(clients: DiagnosticsReport[], checkpointInterval?: number): DivergenceAnalysis;
21
+ //# sourceMappingURL=divergence-analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"divergence-analysis.d.ts","sourceRoot":"","sources":["../../src/lib/divergence-analysis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE/D,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;IAC7C,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;CACjD;AAsBD,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAAE,EAC5B,kBAAkB,SAA8B,GAC/C,kBAAkB,CA8EpB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Polynomial hash (same algorithm as Mem.getHash) but using direct Uint8Array indexing
3
+ * instead of DataView for better performance on large buffers (e.g. Rapier snapshots).
4
+ */
5
+ export declare function hashBytes(data: Uint8Array): number;
6
+ //# sourceMappingURL=hash-bytes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash-bytes.d.ts","sourceRoot":"","sources":["../../src/lib/hash-bytes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAMlD"}
@@ -0,0 +1,43 @@
1
+ import type { ECSRunner } from '@lagless/core';
2
+ export interface TimingStats {
3
+ latest: number;
4
+ min: number;
5
+ max: number;
6
+ avg: number;
7
+ }
8
+ export interface SystemTimingStats extends TimingStats {
9
+ name: string;
10
+ }
11
+ export interface PerformanceStats {
12
+ tickTime: TimingStats;
13
+ snapshotTime: TimingStats;
14
+ overheadTime: TimingStats;
15
+ systems: SystemTimingStats[];
16
+ }
17
+ export declare class PerformanceProfiler {
18
+ private readonly _windowSize;
19
+ private _entries;
20
+ private _attached;
21
+ private _tickTimeBuffer;
22
+ private _tickTimeWriteIndex;
23
+ private _tickTimeCount;
24
+ private _tickStartTime;
25
+ private _snapshotTimeBuffer;
26
+ private _snapshotTimeWriteIndex;
27
+ private _snapshotTimeCount;
28
+ private _overheadTimeBuffer;
29
+ private _overheadTimeWriteIndex;
30
+ private _overheadTimeCount;
31
+ private _lastSimulateElapsed;
32
+ private _lastSnapshotElapsed;
33
+ private _originalSimulate;
34
+ private _originalSaveSnapshot;
35
+ private _removeTickHandler;
36
+ private _simulation;
37
+ constructor(windowSize?: number);
38
+ attach(runner: ECSRunner): void;
39
+ detach(): void;
40
+ getStats(): PerformanceStats;
41
+ dispose(): void;
42
+ }
43
+ //# sourceMappingURL=performance-profiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-profiler.d.ts","sourceRoot":"","sources":["../../src/lib/performance-profiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAA6B,MAAM,eAAe,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,CAAC;IACtB,YAAY,EAAE,WAAW,CAAC;IAC1B,YAAY,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAyCD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,cAAc,CAAK;IAG3B,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,uBAAuB,CAAK;IACpC,OAAO,CAAC,kBAAkB,CAAK;IAG/B,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,uBAAuB,CAAK;IACpC,OAAO,CAAC,kBAAkB,CAAK;IAG/B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,oBAAoB,CAAK;IAGjC,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,qBAAqB,CAAyC;IACtE,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,WAAW,CAA8B;gBAErC,UAAU,SAAsB;IAIrC,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAwF/B,MAAM,IAAI,IAAI;IAsCd,QAAQ,IAAI,gBAAgB;IAqB5B,OAAO,IAAI,IAAI;CAGvB"}
@@ -0,0 +1,55 @@
1
+ import type { DiagnosticsCollector } from './diagnostics-collector.js';
2
+ export interface DiagnosticsReportConfig {
3
+ fps: number;
4
+ maxPlayers: number;
5
+ frameLength: number;
6
+ snapshotRate: number;
7
+ maxEntities: number;
8
+ }
9
+ export interface DiagnosticsReportSummary {
10
+ totalTicks: number;
11
+ totalRollbacks: number;
12
+ firstDivergenceTick: number | null;
13
+ verifiedTickGapCount: number;
14
+ latestPhysicsHash: number;
15
+ oldestTick: number;
16
+ newestTick: number;
17
+ }
18
+ export interface DiagnosticsReportTickRecord {
19
+ tick: number;
20
+ hash: number;
21
+ physicsHash: number;
22
+ velocityHash: number;
23
+ verifiedTick: number;
24
+ wasRollback: boolean;
25
+ inputCountBySlot: number[];
26
+ }
27
+ export interface DiagnosticsReportRPC {
28
+ inputId: number;
29
+ seq: number;
30
+ playerSlot: number;
31
+ ordinal: number;
32
+ data: unknown;
33
+ }
34
+ export interface DiagnosticsReport {
35
+ version: number;
36
+ generatedAt: string;
37
+ playerSlot: number;
38
+ config: DiagnosticsReportConfig;
39
+ summary: DiagnosticsReportSummary;
40
+ timeline: DiagnosticsReportTickRecord[];
41
+ rollbacks: Array<{
42
+ atSimTick: number;
43
+ rollbackToTick: number;
44
+ timestamp: number;
45
+ }>;
46
+ inputHistory: Record<string, DiagnosticsReportRPC[]>;
47
+ }
48
+ export interface CombinedDiagnosticsReport {
49
+ version: number;
50
+ generatedAt: string;
51
+ clients: DiagnosticsReport[];
52
+ divergenceAnalysis?: import('./divergence-analysis.js').DivergenceAnalysis;
53
+ }
54
+ export declare function generateReport(collector: DiagnosticsCollector): DiagnosticsReport;
55
+ //# sourceMappingURL=report-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-generator.d.ts","sourceRoot":"","sources":["../../src/lib/report-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAIvE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,uBAAuB,CAAC;IAChC,OAAO,EAAE,wBAAwB,CAAC;IAClC,QAAQ,EAAE,2BAA2B,EAAE,CAAC;IACxC,SAAS,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnF,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,0BAA0B,EAAE,kBAAkB,CAAC;CAC5E;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,oBAAoB,GAAG,iBAAiB,CA0DjF"}
@@ -0,0 +1,39 @@
1
+ export interface DiagnosticsConfig {
2
+ /** Number of ticks to keep in the ring buffer. Default: 18000 (5 min at 60fps). */
3
+ bufferSize?: number;
4
+ /** Max rollback events to keep. Default: 1000. */
5
+ maxRollbackEvents?: number;
6
+ /** Optional callback that returns a hash of physics state (e.g. Rapier snapshot). Called every tick. */
7
+ physicsHashFn?: () => number;
8
+ /** Optional callback that returns a hash of velocity state (e.g. all body velocities). Called every tick. */
9
+ velocityHashFn?: () => number;
10
+ }
11
+ export interface TickRecord {
12
+ tick: number;
13
+ hash: number;
14
+ physicsHash: number;
15
+ velocityHash: number;
16
+ verifiedTick: number;
17
+ wasRollback: boolean;
18
+ inputCountBySlot: Uint8Array;
19
+ }
20
+ export interface RollbackEvent {
21
+ /** Simulation tick when rollback was triggered. */
22
+ atSimTick: number;
23
+ /** Tick we rolled back to. */
24
+ rollbackToTick: number;
25
+ /** Performance.now() timestamp. */
26
+ timestamp: number;
27
+ }
28
+ export interface DiagnosticsStats {
29
+ ticksRecorded: number;
30
+ totalRollbacks: number;
31
+ lastRollbackTick: number;
32
+ verifiedTickGapCount: number;
33
+ latestHash: number;
34
+ latestPhysicsHash: number;
35
+ latestVelocityHash: number;
36
+ oldestTick: number;
37
+ newestTick: number;
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wGAAwG;IACxG,aAAa,CAAC,EAAE,MAAM,MAAM,CAAC;IAC7B,6GAA6G;IAC7G,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,UAAU,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,9 @@
1
+ import type { ECSRunner } from '@lagless/core';
2
+ export interface UseDesyncDiagnosticsOptions {
3
+ physicsHashFn?: () => number;
4
+ velocityHashFn?: () => number;
5
+ /** When false, don't create collector or stream diagnostics. Defaults to true. */
6
+ enabled?: boolean;
7
+ }
8
+ export declare function useDesyncDiagnostics(runner: ECSRunner | null, options?: UseDesyncDiagnosticsOptions): void;
9
+ //# sourceMappingURL=use-desync-diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-desync-diagnostics.d.ts","sourceRoot":"","sources":["../../src/lib/use-desync-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAU/C,MAAM,WAAW,2BAA2B;IAC1C,aAAa,CAAC,EAAE,MAAM,MAAM,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,GAAG,IAAI,EACxB,OAAO,CAAC,EAAE,2BAA2B,GACpC,IAAI,CA+EN"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@lagless/desync-diagnostics",
3
+ "version": "0.0.48",
4
+ "license": "CC-BY-NC-4.0",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/GbGr/lagless.git",
9
+ "directory": "libs/desync-diagnostics"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "main": "./src/index.ts",
15
+ "types": "./src/index.ts",
16
+ "exports": {
17
+ "./package.json": "./package.json",
18
+ ".": {
19
+ "@lagless/source": "./src/index.ts",
20
+ "types": "./src/index.ts",
21
+ "import": "./src/index.ts",
22
+ "default": "./src/index.ts"
23
+ }
24
+ },
25
+ "peerDependencies": {
26
+ "react": "^18.0.0 || ^19.0.0",
27
+ "@lagless/core": "0.0.48",
28
+ "@lagless/react": "0.0.48"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "@lagless/react": {
32
+ "optional": true
33
+ },
34
+ "react": {
35
+ "optional": true
36
+ }
37
+ }
38
+ }
package/src/index.ts ADDED
@@ -0,0 +1,26 @@
1
+ export { DiagnosticsCollector } from './lib/diagnostics-collector.js';
2
+ export { hashBytes } from './lib/hash-bytes.js';
3
+ export { analyzeDivergence } from './lib/divergence-analysis.js';
4
+ export type { DivergenceAnalysis, CheckpointComparison, RollbackOverlapWindow } from './lib/divergence-analysis.js';
5
+ export { attachDesyncDiagnostics } from './lib/attach.js';
6
+ export { generateReport } from './lib/report-generator.js';
7
+ export type { DiagnosticsConfig, TickRecord, RollbackEvent, DiagnosticsStats } from './lib/types.js';
8
+ export type {
9
+ DiagnosticsReport,
10
+ CombinedDiagnosticsReport,
11
+ DiagnosticsReportConfig,
12
+ DiagnosticsReportSummary,
13
+ DiagnosticsReportTickRecord,
14
+ DiagnosticsReportRPC,
15
+ } from './lib/report-generator.js';
16
+ export { useDesyncDiagnostics, type UseDesyncDiagnosticsOptions } from './lib/use-desync-diagnostics.js';
17
+ export { PerformanceProfiler } from './lib/performance-profiler.js';
18
+ export type { PerformanceStats, SystemTimingStats, TimingStats } from './lib/performance-profiler.js';
19
+ export type {
20
+ DiagnosticsSummaryMessage,
21
+ DiagnosticsReportMessage,
22
+ PerformanceStatsMessage,
23
+ DiagnosticsChildMessage,
24
+ RequestDiagnosticsReportMessage,
25
+ DiagnosticsParentMessage,
26
+ } from './lib/diagnostics-protocol.js';
@@ -0,0 +1,10 @@
1
+ import type { ECSRunner } from '@lagless/core';
2
+ import { DiagnosticsCollector } from './diagnostics-collector.js';
3
+ import type { DiagnosticsConfig } from './types.js';
4
+
5
+ export function attachDesyncDiagnostics(
6
+ runner: ECSRunner,
7
+ config?: DiagnosticsConfig,
8
+ ): DiagnosticsCollector {
9
+ return new DiagnosticsCollector(runner, config);
10
+ }