@series-inc/rundot-syncplay 5.23.0 → 5.24.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -12
- package/dist/authority-room.d.ts +11 -0
- package/dist/authority-room.js +43 -0
- package/dist/browser.d.ts +11 -1
- package/dist/browser.js +5 -0
- package/dist/checksum-basis.d.ts +4 -0
- package/dist/checksum-basis.js +17 -0
- package/dist/cjs/authority-room.js +43 -0
- package/dist/cjs/browser.js +18 -2
- package/dist/cjs/checksum-basis.js +22 -0
- package/dist/cjs/collider-cooking.js +834 -0
- package/dist/cjs/deterministic-session.js +2 -0
- package/dist/cjs/engine-complete.js +1 -4
- package/dist/cjs/engine-parity-matrix.js +41 -9
- package/dist/cjs/engine-parity.js +2 -2
- package/dist/cjs/index.js +24 -2
- package/dist/cjs/input-authority.js +34 -2
- package/dist/cjs/instant-replay.js +4 -9
- package/dist/cjs/local-authority-server-runner.js +3 -1
- package/dist/cjs/local-authority-wire.js +20 -1
- package/dist/cjs/match-log.js +27 -0
- package/dist/cjs/movement3d.js +17 -1
- package/dist/cjs/networked-client.js +70 -20
- package/dist/cjs/node.js +23 -1
- package/dist/cjs/offline-session.js +52 -24
- package/dist/cjs/physics-cert.js +8 -3
- package/dist/cjs/physics3d-heightfield.js +51 -0
- package/dist/cjs/physics3d-hull.js +381 -0
- package/dist/cjs/physics3d-joints.js +255 -49
- package/dist/cjs/physics3d-mass.js +131 -0
- package/dist/cjs/physics3d-recipes.js +197 -0
- package/dist/cjs/physics3d-shared.js +11 -1
- package/dist/cjs/physics3d-solver.js +845 -98
- package/dist/cjs/physics3d-stacking.js +154 -0
- package/dist/cjs/physics3d-toi.js +274 -0
- package/dist/cjs/physics3d-vehicle.js +35 -10
- package/dist/cjs/physics3d.js +601 -38
- package/dist/cjs/replay.js +25 -1
- package/dist/cjs/runtime-support.js +71 -0
- package/dist/cjs/sdk-offline.js +9 -1
- package/dist/cjs/sdk-session.js +8 -0
- package/dist/cjs/secret-authority.js +647 -0
- package/dist/cjs/secret-client.js +615 -0
- package/dist/cjs/secret-command-classification.js +8 -0
- package/dist/cjs/secret-config.js +211 -0
- package/dist/cjs/secret-crypto.js +208 -0
- package/dist/cjs/secret-protocol.js +238 -0
- package/dist/cjs/session-build.js +7 -4
- package/dist/cjs/session-promotion.js +5 -5
- package/dist/cjs/synctest.js +49 -0
- package/dist/cjs/testing.js +4 -1
- package/dist/cjs/tools/static-checker.js +61 -13
- package/dist/cli.js +193 -48
- package/dist/collider-cooking.d.ts +141 -0
- package/dist/collider-cooking.js +829 -0
- package/dist/deterministic-primitives.d.ts +1 -0
- package/dist/deterministic-session.js +2 -0
- package/dist/engine-complete.js +1 -4
- package/dist/engine-parity-matrix.js +41 -9
- package/dist/engine-parity.js +2 -2
- package/dist/index.d.ts +12 -1
- package/dist/index.js +6 -0
- package/dist/input-authority.d.ts +3 -0
- package/dist/input-authority.js +35 -3
- package/dist/instant-replay.d.ts +2 -2
- package/dist/instant-replay.js +4 -9
- package/dist/local-authority-server-runner.d.ts +1 -0
- package/dist/local-authority-server-runner.js +2 -1
- package/dist/local-authority-wire.js +20 -1
- package/dist/match-log.d.ts +4 -1
- package/dist/match-log.js +27 -0
- package/dist/movement3d.js +18 -2
- package/dist/networked-client.d.ts +8 -0
- package/dist/networked-client.js +70 -20
- package/dist/node.d.ts +7 -0
- package/dist/node.js +6 -0
- package/dist/offline-session.js +53 -25
- package/dist/physics-cert.js +8 -3
- package/dist/physics3d-heightfield.d.ts +17 -0
- package/dist/physics3d-heightfield.js +47 -0
- package/dist/physics3d-hull.d.ts +53 -0
- package/dist/physics3d-hull.js +376 -0
- package/dist/physics3d-joints.d.ts +13 -1
- package/dist/physics3d-joints.js +256 -50
- package/dist/physics3d-mass.d.ts +11 -0
- package/dist/physics3d-mass.js +126 -0
- package/dist/physics3d-recipes.d.ts +29 -0
- package/dist/physics3d-recipes.js +193 -0
- package/dist/physics3d-shared.d.ts +1 -0
- package/dist/physics3d-shared.js +10 -1
- package/dist/physics3d-solver.d.ts +78 -2
- package/dist/physics3d-solver.js +842 -98
- package/dist/physics3d-stacking.d.ts +26 -0
- package/dist/physics3d-stacking.js +147 -0
- package/dist/physics3d-toi.d.ts +79 -0
- package/dist/physics3d-toi.js +267 -0
- package/dist/physics3d-vehicle.js +35 -10
- package/dist/physics3d.d.ts +28 -3
- package/dist/physics3d.js +601 -40
- package/dist/replay.d.ts +5 -0
- package/dist/replay.js +25 -1
- package/dist/reports.d.ts +6 -1
- package/dist/runtime-support.d.ts +60 -0
- package/dist/runtime-support.js +66 -0
- package/dist/sdk-offline.d.ts +4 -0
- package/dist/sdk-offline.js +9 -1
- package/dist/sdk-session.d.ts +4 -0
- package/dist/sdk-session.js +8 -0
- package/dist/secret-authority.d.ts +90 -0
- package/dist/secret-authority.js +643 -0
- package/dist/secret-client.d.ts +89 -0
- package/dist/secret-client.js +611 -0
- package/dist/secret-command-classification.d.ts +1 -0
- package/dist/secret-command-classification.js +5 -0
- package/dist/secret-config.d.ts +45 -0
- package/dist/secret-config.js +206 -0
- package/dist/secret-crypto.d.ts +29 -0
- package/dist/secret-crypto.js +190 -0
- package/dist/secret-protocol.d.ts +181 -0
- package/dist/secret-protocol.js +230 -0
- package/dist/session-build.d.ts +16 -0
- package/dist/session-build.js +7 -4
- package/dist/session-promotion.js +5 -5
- package/dist/synctest.d.ts +1 -1
- package/dist/synctest.js +49 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.js +1 -0
- package/dist/tools/static-checker.d.ts +11 -0
- package/dist/tools/static-checker.js +60 -13
- package/dist/types.d.ts +21 -2
- package/package.json +8 -3
|
@@ -7,11 +7,13 @@ exports.runDeterministicSessionCoreFixture = runDeterministicSessionCoreFixture;
|
|
|
7
7
|
exports.runDeterministicStructuredSessionInputFixture = runDeterministicStructuredSessionInputFixture;
|
|
8
8
|
exports.runDeterministicSessionAuthorityFixture = runDeterministicSessionAuthorityFixture;
|
|
9
9
|
exports.runDeterministicSessionLifecycleProtocolFixture = runDeterministicSessionLifecycleProtocolFixture;
|
|
10
|
+
const runtime_support_js_1 = require("./runtime-support.js");
|
|
10
11
|
const canonical_js_1 = require("./canonical.js");
|
|
11
12
|
const commands_js_1 = require("./commands.js");
|
|
12
13
|
const engine_runtime_js_1 = require("./engine-runtime.js");
|
|
13
14
|
const random_js_1 = require("./random.js");
|
|
14
15
|
function createDeterministicSessionCore(options) {
|
|
16
|
+
(0, runtime_support_js_1.assertSupportedJsRuntime)();
|
|
15
17
|
assertPositiveInteger(options.players, 'players');
|
|
16
18
|
if (options.reconnectGraceFrames !== undefined) {
|
|
17
19
|
assertNonNegativeInteger(options.reconnectGraceFrames, 'reconnectGraceFrames');
|
|
@@ -1276,10 +1276,7 @@ function physicsCategory(bodies, requestedFrames, executedFrames, enforceThresho
|
|
|
1276
1276
|
&& stack.settledDynamicBodies === stack.stackLayers,
|
|
1277
1277
|
stackRollbackEquivalent: stack.rollbackEquivalent,
|
|
1278
1278
|
stackAuthoringOrderStable: stack.stableUnderAuthoringOrderPermutation,
|
|
1279
|
-
|
|
1280
|
-
// (floor-average friction, max restitution); the bounce/friction velocity
|
|
1281
|
-
// magnitudes come from the sequential-impulse solver, not the legacy pusher.
|
|
1282
|
-
materialResponseCovered: materials.combinedFriction === 3
|
|
1279
|
+
materialResponseCovered: Math.abs(materials.combinedFriction - 0.28284271) < 1e-6
|
|
1283
1280
|
&& materials.combinedRestitution === 7
|
|
1284
1281
|
&& materials.normalVelocityAfterBounce > 0
|
|
1285
1282
|
&& materials.tangentVelocityAfterFrictionY < 10
|
|
@@ -623,30 +623,56 @@ const engineParityMatrixEntries = [
|
|
|
623
623
|
'packages/syncplay/src/physics2d.ts',
|
|
624
624
|
'packages/syncplay/src/physics3d.ts',
|
|
625
625
|
'packages/syncplay/src/physics3d-solver.ts',
|
|
626
|
+
'packages/syncplay/src/physics3d-toi.ts',
|
|
626
627
|
'packages/syncplay/src/physics3d-joints.ts',
|
|
628
|
+
'packages/syncplay/src/physics3d-mass.ts',
|
|
629
|
+
'packages/syncplay/src/physics3d-heightfield.ts',
|
|
627
630
|
'packages/syncplay/tests/physics2d.test.ts',
|
|
628
631
|
'packages/syncplay/tests/physics3d-solver.test.ts',
|
|
629
632
|
'packages/syncplay/tests/physics3d-joints.test.ts',
|
|
630
633
|
'packages/syncplay/tests/physics3d-ccd.test.ts',
|
|
634
|
+
'packages/syncplay/tests/physics3d-toi.test.ts',
|
|
635
|
+
'packages/syncplay/tests/physics3d-mass.test.ts',
|
|
636
|
+
'packages/syncplay/tests/physics3d-materials.test.ts',
|
|
637
|
+
'packages/syncplay/tests/physics3d-heightfield.test.ts',
|
|
638
|
+
'packages/syncplay/tests/physics3d-rotation-convention.test.ts',
|
|
639
|
+
'packages/syncplay/tests/physics3d-mesh-onesided.test.ts',
|
|
631
640
|
'packages/syncplay/tests/engine-complete.test.ts',
|
|
632
641
|
'packages/syncplay/src/physics3d-vehicle.ts',
|
|
633
642
|
'packages/syncplay/tests/physics3d-vehicle.test.ts',
|
|
634
|
-
'
|
|
643
|
+
'packages/syncplay/src/physics3d-stacking.ts',
|
|
644
|
+
'packages/syncplay/tests/physics3d-stacking.test.ts',
|
|
645
|
+
'packages/syncplay/src/physics3d-recipes.ts',
|
|
646
|
+
'packages/syncplay/tests/physics3d-recipes.test.ts',
|
|
647
|
+
'packages/syncplay/src/physics3d-hull.ts',
|
|
648
|
+
'packages/syncplay/tests/physics3d-hull.test.ts',
|
|
649
|
+
'TGS-Soft (sub-stepped soft-constraint) rigid solver: box3d-class stable stacking, tumbling, sleeping, axis locks, joints (ball/hinge/weld/rope/prismatic/spring/grab), opt-in TOI-bound CCD, compound colliders, orientation-aware queries',
|
|
650
|
+
'extreme-speed CCD (box3d model, src/physics3d-toi.ts): an opt-in ccd body whose per-tick motion exceeds half its smallest extent has its exact first time-of-impact found by GJK conservative advancement against convex shapes and culled heightfield cell triangles; it is advanced there and the impact resolved as a perfectly-inelastic collision with momentum transfer and a TOI position clamp. physics3d-ccd.test.ts and physics3d-heightfield.test.ts cover finite-at-vx60, heightfield-cliff no-tunnel, rollback, and permutation equivalence',
|
|
651
|
+
'stacking stability gated in CI: tower-15 (<1° tilt), 50:1 mass-ratio column, pyramid, kick-recovery, and a 20-layer stack all meet drift/penetration/up-alignment thresholds with rollback + authoring-order-permutation equivalence',
|
|
652
|
+
'convex-hull collision shape (4–16 verts): deterministic supporting-plane preprocessing, SAT+clip hull-vs-hull/box, closest-point hull-vs-sphere/capsule, support-sample hull-vs-static-mesh, exact ray/overlap/shape-cast queries; golden hull-pair battery pinned',
|
|
653
|
+
'cone-twist ball joint (swing cone + twist limits via fixed-point cosine) drives a 6-body ragdoll that settles kinetically and stays articulated; rigid-wheel vehicle recipe (sphere wheels + hinge motors) certified alongside the raycast controller',
|
|
635
654
|
'builtin-rigid physics-cert lane passes all 4 determinism gates; contact/joint caches are checksummed rollback state',
|
|
636
|
-
'
|
|
637
|
-
'
|
|
638
|
-
'
|
|
655
|
+
'builtin-rigid@2 certifies true hull/compound inertia and center-of-mass integration, geometric-mean friction, rolling resistance, static heightfields with CCD/query parity, and active spherical motor/orientation-spring rows',
|
|
656
|
+
'raycast-wheel vehicle controller (stepDeterministicVehicle3D): spring-damper suspension, friction-circle drive/steer/brake, fixed-point steering trig, moving-ground reaction (slip relative to the contact body; equal-and-opposite reaction on dynamic ground)',
|
|
657
|
+
'per-world static AABB-tree query index: raycast/overlap/shape-cast are O(log n + hits) with bit-identical results; batched applyDeterministicImpulses3D applies a frame of controller impulses in one canonical rebuild',
|
|
658
|
+
'pooled narrow-phase/constraint scratch (scalarized box face-clip over ping-pong clip slots, step-scoped constraint pool); prismatic/breakable/collision-suppressed joints, a soft frequencyHz/dampingRatio spring joint (breakable/disable-collision), and per-body gravityScale — joint breaks are sticky checksummed cache state',
|
|
659
|
+
'kinematic bodies integrate their authored velocity (linear + angular) inside the solver, drive the dynamic bodies they touch through the contact relative-velocity term, absorb the equal-and-opposite reaction (zero inverse mass), and wake the sleeping piles they push; a kinematic mesh is therefore a moving platform / conveyor collider. A dynamic mesh is rejected at world creation',
|
|
660
|
+
'one-sided mesh and heightfield contacts resolve penetration for zero-radius shapes while rejecting genuine back-face approaches; a shallow solver-creep recovery band keeps boxes/hulls resting without ejecting bodies from below',
|
|
661
|
+
'compound colliders accept hull children (packages/syncplay/src/collider-cooking.ts): an offline V-HACD-shaped cooker voxelizes a concave triangle soup, splits it on the axis-aligned plane that best reduces concavity, and emits a compound of ≤16-vert convex hulls that the runtime validates with buildSolverHull. The cook is order-invariant and byte-reproducible (exact integer-lattice convex hull, lexicographic tie-breaks); no decomposition code runs in a simulation step',
|
|
639
662
|
],
|
|
640
663
|
cliProofs: [
|
|
641
664
|
'npm run lab:physics3d -- --bodies 128 --frames 1000 --no-fast-stress --json artifacts/reports/physics3d-no-fast.json',
|
|
665
|
+
'npm run lab:physics3d-stacks -- --json artifacts/reports/physics3d-stacks.json',
|
|
642
666
|
'npm run certify:engine-complete -- --filter physics3d --players 128 --frames 5000 --no-fast-stress',
|
|
643
667
|
'npm run lab:physics-cert -- --engine builtin-rigid --shape fixed-arena --players 4 --frames 600 --seed 7 --snapshot-every 8',
|
|
644
668
|
'npm run coverage:package:physics-vehicle',
|
|
669
|
+
'npm run collider:validate -- <mesh.json> --json artifacts/reports/collider-validate.json',
|
|
645
670
|
],
|
|
646
671
|
gaps: [
|
|
647
|
-
'
|
|
648
|
-
'
|
|
649
|
-
'
|
|
672
|
+
'Mesh-vs-mesh collision remains unsupported by design: a mesh collider is static or kinematic, and a dynamic concave body must be cooked to a compound of hulls (collider:cook).',
|
|
673
|
+
'Kinematic bodies resolve contacts discretely — a kinematic mesh driven faster than its own extent per tick can pass through a thin body (no CCD for kinematic; ccd is a dynamic-body opt-in).',
|
|
674
|
+
'Dynamic ccd sweeps box, sphere, capsule, hull, and heightfield targets; triangle-soup mesh and compound targets remain discrete-only.',
|
|
675
|
+
'Convex decomposition is offline only: the cooked hull set is conservative (it contains the source, inflated by up to one voxel of the chosen resolution) and hulls stay capped at 16 verts.',
|
|
650
676
|
],
|
|
651
677
|
nextCliWork: [
|
|
652
678
|
'Profile the remaining burst cost before further pooling; no known single lever.',
|
|
@@ -829,20 +855,26 @@ const engineParityMatrixEntries = [
|
|
|
829
855
|
canCloseWithCli: true,
|
|
830
856
|
currentEvidence: [
|
|
831
857
|
'packages/syncplay/src/browser.ts',
|
|
858
|
+
'packages/syncplay/src/runtime-support.ts',
|
|
859
|
+
'packages/syncplay/tests/runtime-support.test.ts',
|
|
832
860
|
'packages/sdk/src/syncplay/index.ts',
|
|
833
861
|
'packages/sdk/src/syncplay/browser.ts',
|
|
834
862
|
'packages/sdk/src/syncplay/node.ts',
|
|
835
863
|
'packages/sdk/tests/deterministicMultiplayer.test.ts',
|
|
864
|
+
'cross-VM certification: Chromium (V8) AND WebKit (JavaScriptCore, the executable proxy for iOS WKWebView) are blocking gates on every engine PR (.github/workflows/ci-syncplay.yml cross-vm-browser). Each runs the replay proof, the number lab, and the 128-player/1000-frame engine-core numeric proof, and each must reproduce Node\'s checksums bit-for-bit; static:determinism requires a passing proof artifact per certified runtime, not just chromium',
|
|
865
|
+
'Hermes is an explicit non-goal with an enforced runtime guard: H5 games run in the platform WebView (V8 on Android, JavaScriptCore on iOS), never in React Native\'s VM, and no lane can execute Hermes — so createDeterministicSessionCore refuses to start there rather than emit checksums the authority server would silently reject (src/runtime-support.ts)',
|
|
836
866
|
],
|
|
837
867
|
cliProofs: [
|
|
838
868
|
'npm run lab:replay-verify-browser -- artifacts/replays/browser-proof-current.json --runtime chromium --json artifacts/certification/browser-replay-chromium-current.json',
|
|
869
|
+
'npm run lab:replay-verify-browser -- artifacts/replays/browser-proof-current.json --runtime webkit --json artifacts/certification/browser-replay-webkit-current.json',
|
|
870
|
+
'npm run lab:engine-core-browser -- --runtime webkit --json artifacts/certification/engine-core-numeric-webkit-current.json',
|
|
839
871
|
'cd packages/sdk && npm test -- deterministicMultiplayer.test.ts --runInBand && npm run build:sdk',
|
|
840
872
|
],
|
|
841
873
|
gaps: [
|
|
842
|
-
'
|
|
874
|
+
'Android WebView is proven only by proxy (Chromium/V8 on Linux); the on-device APK replay harness stays a manual per-release step.',
|
|
843
875
|
],
|
|
844
876
|
nextCliWork: [
|
|
845
|
-
'
|
|
877
|
+
'Automate the Android WebView APK replay harness so the on-device proof stops being manual.',
|
|
846
878
|
],
|
|
847
879
|
referenceDocs: [
|
|
848
880
|
'external-deterministic-engine-reference:manual/replay',
|
|
@@ -767,7 +767,7 @@ function runPhysicsCategory(config) {
|
|
|
767
767
|
&& physics3DQueries.overlapCovered
|
|
768
768
|
&& physics3DQueries.shapeCastCovered,
|
|
769
769
|
physics3DMaterialResponse: physics3DMaterials.rollbackEquivalent
|
|
770
|
-
&& physics3DMaterials.combinedFriction
|
|
770
|
+
&& Math.abs(physics3DMaterials.combinedFriction - 0.28284271) < 1e-6
|
|
771
771
|
&& physics3DMaterials.combinedRestitution === 7
|
|
772
772
|
&& physics3DMaterials.normalVelocityAfterBounce < 0,
|
|
773
773
|
physics3DDetailedQueriesAndContacts: physics3DDetails.rollbackEquivalent
|
|
@@ -2531,7 +2531,7 @@ function runReplayWorkload128(frames, seed) {
|
|
|
2531
2531
|
mismatches: verification.mismatches,
|
|
2532
2532
|
finalChecksum: verification.finalChecksum,
|
|
2533
2533
|
replayBytes: JSON.stringify(replay).length,
|
|
2534
|
-
snapshotBytes: session.
|
|
2534
|
+
snapshotBytes: session.getSnapshotBytes(session.currentFrame).byteLength,
|
|
2535
2535
|
};
|
|
2536
2536
|
}
|
|
2537
2537
|
function runSecurityHostileClientCategory(config) {
|
package/dist/cjs/index.js
CHANGED
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.createDeterministicCommandTimelineSnapshot = exports.createDeterministicCommandTimeline = exports.assertDeterministicPayloadFields = exports.translateAabb = exports.sortAabbsByStableId = exports.segmentsIntersect = exports.segmentSegmentDistance = exports.segmentPointDistance = exports.pointOnSegment = exports.orientation = exports.fixedVec2 = exports.fixedCircle = exports.fixedAabb = exports.closestPointOnSegment = exports.circleIntersectsAabb = exports.circleIntersects = exports.aabbIntersects = exports.aabbContainsPoint = exports.stableSortedKeys = exports.stableEntries = exports.deterministicShuffle = exports.createStableIndexedMap = exports.encodeUtf8 = exports.defaultChecksum = exports.decodeUtf8 = exports.decodeCanonicalValue = exports.cloneCanonical = exports.canonicalStringify = exports.canonicalize = exports.tickDeterministicHfsm = exports.tickDeterministicBehaviorTreeWithMemory = exports.tickDeterministicBehaviorTree = exports.selectDeterministicUtility = exports.runDeterministicBotSimulation = exports.getBlackboardValue = exports.createDeterministicBotBlackboard = exports.cookDeterministicBotAsset = exports.traceDeterministicBotDocument = exports.summarizeDeterministicBotDocumentTraceCoverage = exports.summarizeDeterministicBotDocumentTraceCounterDelta = exports.compileDeterministicBotDocument = exports.createDeterministicAuthorityRoom = exports.stableAssetCookJson = exports.materializePrototypeEntities = exports.cookDeterministicAssets = exports.assertAssetManifestMatches = exports.stepDeterministicAnimation = exports.reconstructDeterministicAnimationEvents = exports.createDeterministicAnimationRuntimeState = exports.cookDeterministicAnimation = void 0;
|
|
5
5
|
exports.createDeterministicLocalAuthorityCorrectionEnvelope = exports.createDeterministicLocalAuthorityCommandBundleEnvelope = exports.createDeterministicLocalAuthorityAckEnvelope = exports.applyDeterministicLocalAuthorityInputBundleEnvelope = exports.applyDeterministicLocalAuthorityCommandBundleEnvelope = exports.createDeterministicLocalAuthorityRuntime = exports.validateDeterministicLagCompensatedShot3D = exports.validateDeterministicAuthoritativeLagCompensatedShot3D = exports.rewindDeterministicLagCompensatedTargets3D = exports.createDeterministicLagCompensation3DHistory = exports.buildDeterministicLagCompensatedHitboxProxies3D = exports.sortDeterministicInputTimelineFlags = exports.evaluateDeterministicInputTimelineResult = exports.accumulateDeterministicInputTimelineState = exports.createInputAuthority = exports.filterDeterministicEventsForPlayer = exports.createDeterministicEventTimeline = exports.SyncplayError = exports.validateDeterministicFrameSystemsStateless = exports.runDeterministicFrameRuntimeMutationCorpus = exports.isDeterministicFrameHeapStaleHandleError = exports.generateDeterministicFrameRuntimeSource = exports.createDeterministicSparseSetFrameRuntime = exports.createDeterministicSlotMap = exports.createDeterministicFrameRuntimeAccessors = exports.createDeterministicFrameRuntime = exports.createDeterministicBitset = exports.compileDeterministicDslRuntimeDescriptor = exports.assertDeterministicFrameSystemsStateless = exports.assertDeterministicFrameHeapStaleHandleError = exports.stableDeterministicEngineCompleteJson = exports.runDeterministicEngineCompleteSuite = exports.engineCompleteExecutionProofHash = exports.createDeterministicEventEffectsAdapter = exports.createEcsLiteWorld = exports.verifyDeterministicSessionRuntimeReplay = exports.createDeterministicSessionReplayBundle = exports.createDeterministicSessionCore = exports.stepTopDownBody = exports.stepKccLiteBody = exports.quantizeFpsAim = exports.detectVerticalCcdHit = exports.cookStaticColliders = exports.combineDeterministicPhysicsMaterial = exports.verifyDeterministicConfigBundle = exports.summarizeDeterministicConfigBundleFailureReasons = exports.evaluateDeterministicConfigBundleParityResult = exports.cookDeterministicConfigBundle = exports.evaluateDeterministicCommandTimelineMigrationResult = exports.deterministicPayloadMatchesFields = void 0;
|
|
6
6
|
exports.queryDeterministicPhysicsShapeOverlap2D = exports.queryDeterministicPhysicsAabb2D = exports.nearestDeterministicPhysicsHit2D = exports.createDeterministicPhysicsWorld2D = exports.circleCastDeterministicPhysics2D = exports.createOfflineSession = exports.valueNoise2D = exports.valueNoise1D = exports.hashUint32 = exports.hash2 = exports.fbm2D = exports.createNetworkedSyncplayClient = exports.stepDeterministicNavmeshOffMeshTraversal = exports.stepDeterministicNavmeshAgents = exports.smoothDeterministicNavmeshPath = exports.runDeterministicNavmeshStress = exports.locateDeterministicNavmeshPolygon = exports.findDeterministicNavmeshPointPath = exports.findDeterministicNavmeshPath = exports.cookImportedDeterministicNavmesh = exports.cookDeterministicNavmesh = exports.stepDeterministicNavigationAgents = exports.smoothDeterministicPath = exports.findDeterministicPath = exports.createDeterministicFlowField = exports.cookTilemapNavigationGraph = exports.cookDeterministicNavigationGraph = exports.applyDeterministicReciprocalAvoidance = exports.stepDeterministicKcc3D = exports.stepDeterministicCarry3D = exports.validateDeterministicLagCompensatedHit = exports.stepDeterministicTopDownMover = exports.stepDeterministicKcc2D = exports.stepDeterministicFpsAim = exports.runDeterministicMovementSimulation = exports.runDeterministicMovementCrowdSimulation = exports.rewindDeterministicTargets = exports.createDeterministicMath = exports.encodeDeterministicLocalAuthorityWireEnvelope = exports.deterministicLocalAuthorityWireProtocol = exports.decodeDeterministicLocalAuthorityWireEnvelope = exports.createDeterministicLocalAuthorityWireReceiver = exports.createDeterministicLocalAuthorityWireAuth = exports.createDeterministicLocalAuthoritySnapshotTransfer = exports.createDeterministicLocalAuthoritySnapshotRequestEnvelope = exports.createDeterministicLocalAuthoritySnapshotOfferEnvelope = exports.createDeterministicLocalAuthoritySnapshotEnvelope = exports.createDeterministicLocalAuthoritySnapshotCompleteEnvelope = exports.createDeterministicLocalAuthoritySnapshotChunkEnvelope = exports.createDeterministicLocalAuthorityInputBundleEnvelope = void 0;
|
|
7
|
-
exports.
|
|
8
|
-
exports.tickCooldown = exports.scheduleFrameEvent = exports.popDueFrameEvents = exports.millisecondsToFrames = exports.framesToMilliseconds = exports.createCooldown = exports.summarizeDeterministicLifecycleCounters = exports.createDeterministicLifecycleRunner = exports.createDeterministicSparseSet = exports.createDeterministicSignalBus = exports.encodeDeterministicSessionMessage = exports.deterministicSessionWireVersion = exports.decodeDeterministicSessionMessage = exports.encodeDeterministicStateSnapshot = exports.deterministicRoomDescriptorHash = exports.DEFAULT_SNAPSHOT_CHUNK_SIZE = exports.DEFAULT_MAX_SNAPSHOT_BYTES = exports.decodeDeterministicStateSnapshot = exports.createSnapshotTransferFromBytes = exports.createSnapshotTransferCollector = exports.bytesToBase64 = exports.base64ToBytes = exports.createSyncplayOfflineRuntime = exports.stableDeterministicDslSchemaJson = exports.parseDeterministicSchemaDsl = exports.deterministicDslMaxPlayers = exports.createDeterministicBinarySerializer = exports.proveSchemaBinaryRoundTrip = exports.generateSchemaTypes = exports.generateSchemaBinarySource = exports.createDeterministicDescriptorBinarySchema = exports.createSchemaArtifacts = exports.stableDescriptorId = exports.defineSyncplayGame = void 0;
|
|
7
|
+
exports.diffDeterministicReports = exports.createProtocolRollbackReport = exports.verifyScoredReplay = exports.stableFrameDumpJson = exports.stableFrameDiffJson = exports.inspectReplayFrameWithDescriptor = exports.inspectReplayFrame = exports.explainReplayChecksum = exports.diffReplayFrames = exports.verifyReplayScoreAgainstTrustedRuntime = exports.verifyDeterministicSelfContainedFrameRuntimeReplayBundle = exports.verifyDeterministicReplayBundle = exports.verifyDeterministicFrameRuntimeReplayBundle = exports.sliceDeterministicReplayBundle = exports.minimizeDeterministicReplayDesyncBundle = exports.minimizeDeterministicReplayDesync = exports.createDeterministicReplayRuntimeManifest = exports.createDeterministicReplayDebugTables = exports.createDeterministicReplayBundle = exports.verifyReplayWithDescriptor = exports.verifyReplay = exports.DeterministicRandom = exports.interpolateDeterministicRenderVec2 = exports.interpolateDeterministicRenderNumber = exports.createDeterministicR3fRenderAdapter = exports.createDeterministicR3fDebugOverlay = exports.stepDeterministicVehicle3D = exports.runDeterministicTower3DScenario = exports.runDeterministicStackingSuite3D = exports.runDeterministicPyramid3DScenario = exports.runDeterministicMassRatio3DScenario = exports.runDeterministicKickRecovery3DScenario = exports.stepDeterministicPhysicsWorld3D = exports.shapeCastDeterministicPhysics3DDetailed = exports.shapeCastDeterministicPhysics3D = exports.setDeterministicBodyVelocity3D = exports.runDeterministicPhysics3DStress = exports.raycastDeterministicPhysics3DDetailed = exports.raycastDeterministicPhysics3D = exports.overlapDeterministicPhysics3D = exports.deterministicPhysicsCallback3D = exports.deterministicPhysicsBodyById3D = exports.deriveInverseInertia = exports.createDeterministicPhysicsWorld3D = exports.contactDetailsDeterministicPhysics3D = exports.applyDeterministicImpulses3D = exports.applyDeterministicImpulse3D = exports.stepDeterministicPhysicsWorld2D = exports.raycastDeterministicPhysics2D = exports.raycastAllDeterministicPhysics2D = void 0;
|
|
8
|
+
exports.createSyncplaySecretsClient = exports.createSyncplaySecretEventReader = exports.createSyncplaySecretAuthority = exports.verifySyncplaySignedReceipt = exports.createBrowserSyncplaySecretCrypto = exports.syncplaySecretProtocol = exports.isSyncplaySecretCommand = exports.decodeSyncplaySecretPrivateMessage = exports.validateSyncplaySecretSystemsConfig = exports.syncplaySecretLimits = exports.syncplaySecretConfigHash = exports.tickCooldown = exports.scheduleFrameEvent = exports.popDueFrameEvents = exports.millisecondsToFrames = exports.framesToMilliseconds = exports.createCooldown = exports.summarizeDeterministicLifecycleCounters = exports.createDeterministicLifecycleRunner = exports.createDeterministicSparseSet = exports.createDeterministicSignalBus = exports.encodeDeterministicSessionMessage = exports.deterministicSessionWireVersion = exports.decodeDeterministicSessionMessage = exports.encodeDeterministicStateSnapshot = exports.deterministicRoomDescriptorHash = exports.DEFAULT_SNAPSHOT_CHUNK_SIZE = exports.DEFAULT_MAX_SNAPSHOT_BYTES = exports.decodeDeterministicStateSnapshot = exports.createSnapshotTransferFromBytes = exports.createSnapshotTransferCollector = exports.bytesToBase64 = exports.base64ToBytes = exports.createSyncplayOfflineRuntime = exports.stableDeterministicDslSchemaJson = exports.parseDeterministicSchemaDsl = exports.deterministicDslMaxPlayers = exports.createDeterministicBinarySerializer = exports.proveSchemaBinaryRoundTrip = exports.generateSchemaTypes = exports.generateSchemaBinarySource = exports.createDeterministicDescriptorBinarySchema = exports.createSchemaArtifacts = exports.stableDescriptorId = exports.defineSyncplayGame = exports.withRuntimeDeterminismGuards = exports.createDeterministicRollbackHistory = exports.stableReportJson = exports.stableReportDiffJson = exports.renderDeterministicReportHtml = void 0;
|
|
9
9
|
var animation_js_1 = require("./animation.js");
|
|
10
10
|
Object.defineProperty(exports, "cookDeterministicAnimation", { enumerable: true, get: function () { return animation_js_1.cookDeterministicAnimation; } });
|
|
11
11
|
Object.defineProperty(exports, "createDeterministicAnimationRuntimeState", { enumerable: true, get: function () { return animation_js_1.createDeterministicAnimationRuntimeState; } });
|
|
@@ -207,6 +207,12 @@ Object.defineProperty(exports, "setDeterministicBodyVelocity3D", { enumerable: t
|
|
|
207
207
|
Object.defineProperty(exports, "shapeCastDeterministicPhysics3D", { enumerable: true, get: function () { return physics3d_js_1.shapeCastDeterministicPhysics3D; } });
|
|
208
208
|
Object.defineProperty(exports, "shapeCastDeterministicPhysics3DDetailed", { enumerable: true, get: function () { return physics3d_js_1.shapeCastDeterministicPhysics3DDetailed; } });
|
|
209
209
|
Object.defineProperty(exports, "stepDeterministicPhysicsWorld3D", { enumerable: true, get: function () { return physics3d_js_1.stepDeterministicPhysicsWorld3D; } });
|
|
210
|
+
var physics3d_stacking_js_1 = require("./physics3d-stacking.js");
|
|
211
|
+
Object.defineProperty(exports, "runDeterministicKickRecovery3DScenario", { enumerable: true, get: function () { return physics3d_stacking_js_1.runDeterministicKickRecovery3DScenario; } });
|
|
212
|
+
Object.defineProperty(exports, "runDeterministicMassRatio3DScenario", { enumerable: true, get: function () { return physics3d_stacking_js_1.runDeterministicMassRatio3DScenario; } });
|
|
213
|
+
Object.defineProperty(exports, "runDeterministicPyramid3DScenario", { enumerable: true, get: function () { return physics3d_stacking_js_1.runDeterministicPyramid3DScenario; } });
|
|
214
|
+
Object.defineProperty(exports, "runDeterministicStackingSuite3D", { enumerable: true, get: function () { return physics3d_stacking_js_1.runDeterministicStackingSuite3D; } });
|
|
215
|
+
Object.defineProperty(exports, "runDeterministicTower3DScenario", { enumerable: true, get: function () { return physics3d_stacking_js_1.runDeterministicTower3DScenario; } });
|
|
210
216
|
var physics3d_vehicle_js_1 = require("./physics3d-vehicle.js");
|
|
211
217
|
Object.defineProperty(exports, "stepDeterministicVehicle3D", { enumerable: true, get: function () { return physics3d_vehicle_js_1.stepDeterministicVehicle3D; } });
|
|
212
218
|
var r3f_render_adapter_js_1 = require("./r3f-render-adapter.js");
|
|
@@ -294,3 +300,19 @@ Object.defineProperty(exports, "millisecondsToFrames", { enumerable: true, get:
|
|
|
294
300
|
Object.defineProperty(exports, "popDueFrameEvents", { enumerable: true, get: function () { return timers_js_1.popDueFrameEvents; } });
|
|
295
301
|
Object.defineProperty(exports, "scheduleFrameEvent", { enumerable: true, get: function () { return timers_js_1.scheduleFrameEvent; } });
|
|
296
302
|
Object.defineProperty(exports, "tickCooldown", { enumerable: true, get: function () { return timers_js_1.tickCooldown; } });
|
|
303
|
+
var secret_config_js_1 = require("./secret-config.js");
|
|
304
|
+
Object.defineProperty(exports, "syncplaySecretConfigHash", { enumerable: true, get: function () { return secret_config_js_1.syncplaySecretConfigHash; } });
|
|
305
|
+
Object.defineProperty(exports, "syncplaySecretLimits", { enumerable: true, get: function () { return secret_config_js_1.syncplaySecretLimits; } });
|
|
306
|
+
Object.defineProperty(exports, "validateSyncplaySecretSystemsConfig", { enumerable: true, get: function () { return secret_config_js_1.validateSyncplaySecretSystemsConfig; } });
|
|
307
|
+
var secret_protocol_js_1 = require("./secret-protocol.js");
|
|
308
|
+
Object.defineProperty(exports, "decodeSyncplaySecretPrivateMessage", { enumerable: true, get: function () { return secret_protocol_js_1.decodeSyncplaySecretPrivateMessage; } });
|
|
309
|
+
Object.defineProperty(exports, "isSyncplaySecretCommand", { enumerable: true, get: function () { return secret_protocol_js_1.isSyncplaySecretCommand; } });
|
|
310
|
+
Object.defineProperty(exports, "syncplaySecretProtocol", { enumerable: true, get: function () { return secret_protocol_js_1.syncplaySecretProtocol; } });
|
|
311
|
+
var secret_crypto_js_1 = require("./secret-crypto.js");
|
|
312
|
+
Object.defineProperty(exports, "createBrowserSyncplaySecretCrypto", { enumerable: true, get: function () { return secret_crypto_js_1.createBrowserSyncplaySecretCrypto; } });
|
|
313
|
+
Object.defineProperty(exports, "verifySyncplaySignedReceipt", { enumerable: true, get: function () { return secret_crypto_js_1.verifySyncplaySignedReceipt; } });
|
|
314
|
+
var secret_authority_js_1 = require("./secret-authority.js");
|
|
315
|
+
Object.defineProperty(exports, "createSyncplaySecretAuthority", { enumerable: true, get: function () { return secret_authority_js_1.createSyncplaySecretAuthority; } });
|
|
316
|
+
var secret_client_js_1 = require("./secret-client.js");
|
|
317
|
+
Object.defineProperty(exports, "createSyncplaySecretEventReader", { enumerable: true, get: function () { return secret_client_js_1.createSyncplaySecretEventReader; } });
|
|
318
|
+
Object.defineProperty(exports, "createSyncplaySecretsClient", { enumerable: true, get: function () { return secret_client_js_1.createSyncplaySecretsClient; } });
|
|
@@ -10,6 +10,7 @@ function createInputAuthority(config) {
|
|
|
10
10
|
const maxFutureTicks = config.maxFutureTicks ?? Number.POSITIVE_INFINITY;
|
|
11
11
|
const retainedConfirmedFrames = config.retainedConfirmedFrames ?? 512;
|
|
12
12
|
const pending = new Map();
|
|
13
|
+
const pendingCommands = new Map();
|
|
13
14
|
const lastConfirmedInput = Array.from({ length: playerCount }, () => config.neutralInput);
|
|
14
15
|
const retained = new Map();
|
|
15
16
|
let confirmedThrough = -1;
|
|
@@ -22,7 +23,7 @@ function createInputAuthority(config) {
|
|
|
22
23
|
const frame = frames[index];
|
|
23
24
|
(0, errors_js_1.invariant)(Number.isInteger(frame.tick) && (index === 0 || frame.tick === frames[index - 1].tick + 1), 'restoreFrom frames must be tick-contiguous', 'input-authority.restore-gap');
|
|
24
25
|
(0, errors_js_1.invariant)(frame.inputs.length === playerCount, 'restoreFrom frame inputs must cover every player slot', 'input-authority.restore-inputs');
|
|
25
|
-
retained.set(frame.tick, frame);
|
|
26
|
+
retained.set(frame.tick, (0, canonical_js_1.cloneCanonical)(frame));
|
|
26
27
|
}
|
|
27
28
|
(0, errors_js_1.invariant)(frames[frames.length - 1].tick === restoredThrough, 'restoreFrom frames must end at the restored frontier', 'input-authority.restore-frontier');
|
|
28
29
|
earliestRetained = frames[0].tick;
|
|
@@ -75,11 +76,16 @@ function createInputAuthority(config) {
|
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
78
|
pending.delete(tick);
|
|
79
|
+
const commands = (pendingCommands.get(tick) ?? [])
|
|
80
|
+
.map((command) => (0, canonical_js_1.cloneCanonical)(command))
|
|
81
|
+
.sort(compareAuthorityCommands);
|
|
82
|
+
pendingCommands.delete(tick);
|
|
78
83
|
const frame = {
|
|
79
84
|
tick,
|
|
80
85
|
inputs,
|
|
81
86
|
substitutedSlots,
|
|
82
|
-
|
|
87
|
+
...(commands.length === 0 ? {} : { commands }),
|
|
88
|
+
checksum: String((0, canonical_js_1.defaultChecksum)(commands.length === 0 ? { tick, inputs } : { tick, inputs, commands })),
|
|
83
89
|
};
|
|
84
90
|
retained.set(tick, frame);
|
|
85
91
|
if (earliestRetained === -1) {
|
|
@@ -116,6 +122,13 @@ function createInputAuthority(config) {
|
|
|
116
122
|
return confirmedThrough;
|
|
117
123
|
},
|
|
118
124
|
receiveInput,
|
|
125
|
+
queueCommand(tick, command) {
|
|
126
|
+
(0, errors_js_1.invariant)(Number.isInteger(tick) && tick >= 0, 'command tick must be a non-negative integer', 'input-authority.command-tick');
|
|
127
|
+
(0, errors_js_1.invariant)(tick > confirmedThrough, `command tick ${tick} is already confirmed`, 'input-authority.command-confirmed');
|
|
128
|
+
const commands = pendingCommands.get(tick) ?? [];
|
|
129
|
+
commands.push((0, canonical_js_1.cloneCanonical)(command));
|
|
130
|
+
pendingCommands.set(tick, commands);
|
|
131
|
+
},
|
|
119
132
|
advance,
|
|
120
133
|
get earliestRetainedTick() {
|
|
121
134
|
return earliestRetained;
|
|
@@ -148,3 +161,22 @@ function createInputAuthority(config) {
|
|
|
148
161
|
},
|
|
149
162
|
};
|
|
150
163
|
}
|
|
164
|
+
function compareAuthorityCommands(left, right) {
|
|
165
|
+
const leftRecord = isCanonicalRecord(left) ? left : undefined;
|
|
166
|
+
const rightRecord = isCanonicalRecord(right) ? right : undefined;
|
|
167
|
+
const leftSystem = typeof leftRecord?.systemId === 'string' ? leftRecord.systemId : '';
|
|
168
|
+
const rightSystem = typeof rightRecord?.systemId === 'string' ? rightRecord.systemId : '';
|
|
169
|
+
const systemOrder = compareCanonicalText(leftSystem, rightSystem);
|
|
170
|
+
if (systemOrder !== 0)
|
|
171
|
+
return systemOrder;
|
|
172
|
+
const leftRequest = typeof leftRecord?.requestId === 'string' ? leftRecord.requestId : '';
|
|
173
|
+
const rightRequest = typeof rightRecord?.requestId === 'string' ? rightRecord.requestId : '';
|
|
174
|
+
const requestOrder = compareCanonicalText(leftRequest, rightRequest);
|
|
175
|
+
return requestOrder !== 0 ? requestOrder : compareCanonicalText((0, canonical_js_1.canonicalStringify)(left), (0, canonical_js_1.canonicalStringify)(right));
|
|
176
|
+
}
|
|
177
|
+
function compareCanonicalText(left, right) {
|
|
178
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
179
|
+
}
|
|
180
|
+
function isCanonicalRecord(value) {
|
|
181
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
182
|
+
}
|
|
@@ -22,15 +22,10 @@ function runInstantReplay(options) {
|
|
|
22
22
|
options.onFrame(session.getState(), session.currentFrame);
|
|
23
23
|
expectedFrame += 1;
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
code: 'instant-replay.missing-final-snapshot',
|
|
30
|
-
frame: session.currentFrame,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
return { framesPlayed: options.inputs.length, finalChecksum: finalSnapshot.checksum };
|
|
25
|
+
return {
|
|
26
|
+
framesPlayed: options.inputs.length,
|
|
27
|
+
finalChecksum: session.computeRecordedChecksum(session.currentFrame),
|
|
28
|
+
};
|
|
34
29
|
}
|
|
35
30
|
/**
|
|
36
31
|
* Binds a session config to a replay window. Each `run` simulates on a fresh
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LOCAL_AUTHORITY_SNAPSHOT_CHUNK_BYTES = void 0;
|
|
3
4
|
exports.runDeterministicLocalAuthorityServerRunner = runDeterministicLocalAuthorityServerRunner;
|
|
4
5
|
const node_http_1 = require("node:http");
|
|
5
6
|
const canonical_js_1 = require("./canonical.js");
|
|
@@ -8,6 +9,7 @@ const commands_js_1 = require("./commands.js");
|
|
|
8
9
|
const local_authority_wire_js_1 = require("./local-authority-wire.js");
|
|
9
10
|
const lag_compensation3d_js_1 = require("./lag-compensation3d.js");
|
|
10
11
|
const LOCAL_AUTHORITY_MAX_BODY_BYTES = 256 * 1024;
|
|
12
|
+
exports.LOCAL_AUTHORITY_SNAPSHOT_CHUNK_BYTES = 32 * 1024;
|
|
11
13
|
async function runDeterministicLocalAuthorityServerRunner(options = {}) {
|
|
12
14
|
const players = options.players ?? 4;
|
|
13
15
|
const seed = options.seed ?? 71;
|
|
@@ -550,7 +552,7 @@ async function driveAuthorityWireResync(baseUrl, runtime, players, seed, sequenc
|
|
|
550
552
|
wireRequests += 1;
|
|
551
553
|
wireSnapshotRequestEnvelopes += 1;
|
|
552
554
|
wireAuthRejected += request.wire?.rejectionReasons.includes('missing-wire-auth') === true ? 1 : 0;
|
|
553
|
-
const transfer = (0, local_authority_wire_js_1.createDeterministicLocalAuthoritySnapshotTransfer)(runtime, identity, `runner-transfer-${seed}-${sequenceBase}`,
|
|
555
|
+
const transfer = (0, local_authority_wire_js_1.createDeterministicLocalAuthoritySnapshotTransfer)(runtime, identity, `runner-transfer-${seed}-${sequenceBase}`, exports.LOCAL_AUTHORITY_SNAPSHOT_CHUNK_BYTES);
|
|
554
556
|
const offer = await postWire(baseUrl, 0, (0, local_authority_wire_js_1.createDeterministicLocalAuthoritySnapshotOfferEnvelope)(sequence, transfer, authoritySign));
|
|
555
557
|
sequence += 1;
|
|
556
558
|
requests += 1;
|
|
@@ -37,10 +37,12 @@ function encodeDeterministicLocalAuthorityWireEnvelope(envelope, sign) {
|
|
|
37
37
|
};
|
|
38
38
|
const auth = sign === undefined ? undefined : createDeterministicLocalAuthorityWireAuth(withoutChecksum, sign);
|
|
39
39
|
const signedEnvelope = auth === undefined ? withoutChecksum : { ...withoutChecksum, auth };
|
|
40
|
-
|
|
40
|
+
const encoded = (0, canonical_js_1.canonicalStringify)({
|
|
41
41
|
...signedEnvelope,
|
|
42
42
|
checksum: (0, canonical_js_1.defaultChecksum)(signedEnvelope),
|
|
43
43
|
});
|
|
44
|
+
assertWireEnvelopeWithinBudget(envelope.kind, encoded);
|
|
45
|
+
return encoded;
|
|
44
46
|
}
|
|
45
47
|
function decodeDeterministicLocalAuthorityWireEnvelope(encoded) {
|
|
46
48
|
// Bound parse cost BEFORE JSON.parse. The kind-specific budget is only known
|
|
@@ -1267,6 +1269,23 @@ const absoluteMaxWireEnvelopeBytes = 2_097_152;
|
|
|
1267
1269
|
function maxWireEnvelopeBytes(kind) {
|
|
1268
1270
|
return kind === 'input-bundle' || kind === 'command-bundle' ? 65_536 : 2_097_152;
|
|
1269
1271
|
}
|
|
1272
|
+
// UTF-8 byte length is at most 3× the UTF-16 length, so every small envelope
|
|
1273
|
+
// (per-frame input/command bundles) skips the exact byte measurement.
|
|
1274
|
+
function assertWireEnvelopeWithinBudget(kind, encoded) {
|
|
1275
|
+
const budget = maxWireEnvelopeBytes(kind);
|
|
1276
|
+
if (encoded.length * 3 <= budget) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
if (encoded.length > budget || (0, canonical_js_1.encodeUtf8)(encoded).byteLength > budget) {
|
|
1280
|
+
throw new Error(wireEnvelopeBudgetError(kind, budget));
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
function wireEnvelopeBudgetError(kind, budget) {
|
|
1284
|
+
if (kind === 'snapshot' || kind === 'correction') {
|
|
1285
|
+
return `Wire ${kind} envelope exceeds the ${budget} byte monolithic budget. A state this large must ride the chunked snapshot-offer/chunk/complete transfer (createSnapshotTransferFromBytes + createDeterministicLocalAuthoritySnapshotOfferEnvelope), which carries snapshots up to the collector's maxSnapshotBytes (default 4194304).`;
|
|
1286
|
+
}
|
|
1287
|
+
return `Wire envelope exceeds ${budget} byte payload budget`;
|
|
1288
|
+
}
|
|
1270
1289
|
function parseWireAuth(value) {
|
|
1271
1290
|
if (!isRecord(value)) {
|
|
1272
1291
|
throw new Error('Wire envelope auth must be an object');
|
package/dist/cjs/match-log.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.verifyScoredMatchLog = verifyScoredMatchLog;
|
|
|
4
4
|
exports.matchLogSessionFactory = matchLogSessionFactory;
|
|
5
5
|
const offline_session_js_1 = require("./offline-session.js");
|
|
6
6
|
const canonical_js_1 = require("./canonical.js");
|
|
7
|
+
const secret_command_classification_js_1 = require("./secret-command-classification.js");
|
|
8
|
+
const secret_protocol_js_1 = require("./secret-protocol.js");
|
|
7
9
|
function verifyScoredMatchLog(config) {
|
|
8
10
|
const { log } = config;
|
|
9
11
|
// The log must be the complete, gap-free tick 0..confirmedThrough sequence —
|
|
@@ -13,9 +15,34 @@ function verifyScoredMatchLog(config) {
|
|
|
13
15
|
}
|
|
14
16
|
const session = config.createSession({ seed: log.seed, playerCount: log.playerCount });
|
|
15
17
|
for (const frame of log.frames) {
|
|
18
|
+
const commands = frame.commands ?? [];
|
|
19
|
+
for (const command of commands) {
|
|
20
|
+
if (!(0, secret_protocol_js_1.isSyncplaySecretCommand)(command)) {
|
|
21
|
+
if ((0, secret_command_classification_js_1.hasSyncplaySecretCommandPrefix)(command)) {
|
|
22
|
+
return {
|
|
23
|
+
ok: false,
|
|
24
|
+
reason: 'authority-command-invalid',
|
|
25
|
+
submittedScore: config.submittedScore,
|
|
26
|
+
framesReplayed: frame.tick,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (config.expectedAuthorityId === undefined || config.verifyAuthorityCommand === undefined
|
|
32
|
+
|| command.authorityId !== config.expectedAuthorityId
|
|
33
|
+
|| !config.verifyAuthorityCommand(command, config.expectedAuthorityId)) {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
reason: 'authority-command-invalid',
|
|
37
|
+
submittedScore: config.submittedScore,
|
|
38
|
+
framesReplayed: frame.tick,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
16
42
|
for (let slot = 0; slot < log.playerCount; slot += 1) {
|
|
17
43
|
session.setInput(slot, config.decodeInput(frame.inputs[slot]));
|
|
18
44
|
}
|
|
45
|
+
session.recordCommands(commands);
|
|
19
46
|
session.stepFrames(1);
|
|
20
47
|
}
|
|
21
48
|
const finalChecksum = String((0, canonical_js_1.defaultChecksum)(session.getState()));
|
package/dist/cjs/movement3d.js
CHANGED
|
@@ -188,8 +188,11 @@ function computeKccPushImpulses(physics, x, y, z, velocity, geometry, maxPushImp
|
|
|
188
188
|
const magnitude = Math.min(speedIntoBody * KCC_PUSH_MASS, maxPushImpulse);
|
|
189
189
|
impulses.push({
|
|
190
190
|
bodyId: body.id,
|
|
191
|
+
// Apply through the body's center of mass so the horizontal push slides the
|
|
192
|
+
// body rather than torquing it — the soft-step solver's livelier angular
|
|
193
|
+
// response otherwise lets an above-COM push tumble a light crate.
|
|
191
194
|
impulse: { x: dirX * magnitude, y: 0, z: dirZ * magnitude },
|
|
192
|
-
point: { x: body.x, y, z: body.z },
|
|
195
|
+
point: { x: body.x, y: body.y, z: body.z },
|
|
193
196
|
});
|
|
194
197
|
}
|
|
195
198
|
return impulses;
|
|
@@ -260,6 +263,19 @@ function primitiveHalfExtent(shape, m) {
|
|
|
260
263
|
if (shape.type === 'sphere') {
|
|
261
264
|
return { x: shape.radius, y: shape.radius, z: shape.radius };
|
|
262
265
|
}
|
|
266
|
+
if (shape.type === 'hull') {
|
|
267
|
+
// Same symmetric bound the solver uses (physics3d-solver shapeHalfExtent).
|
|
268
|
+
let ext = { x: 0, y: 0, z: 0 };
|
|
269
|
+
for (const vertex of shape.vertices) {
|
|
270
|
+
const rotated = (0, physics3d_shared_js_1.mulMat3Vec)(m, vertex);
|
|
271
|
+
ext = {
|
|
272
|
+
x: Math.max(ext.x, Math.abs(rotated.x)),
|
|
273
|
+
y: Math.max(ext.y, Math.abs(rotated.y)),
|
|
274
|
+
z: Math.max(ext.z, Math.abs(rotated.z)),
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
return ext;
|
|
278
|
+
}
|
|
263
279
|
if (shape.type === 'box') {
|
|
264
280
|
return {
|
|
265
281
|
x: Math.abs(m[0]) * shape.halfX + Math.abs(m[1]) * shape.halfY + Math.abs(m[2]) * shape.halfZ,
|