@series-inc/rundot-syncplay 5.23.0-beta.7 → 5.23.0
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 +116 -15
- package/dist/browser.d.ts +87 -55
- package/dist/browser.js +47 -52
- package/dist/certification.d.ts +32 -3
- package/dist/certification.js +88 -3
- package/dist/cjs/browser.js +259 -87
- package/dist/cjs/certification.js +87 -2
- package/dist/cjs/collision.js +58 -0
- package/dist/cjs/creator.js +2 -1
- package/dist/cjs/engine-complete.js +213 -3
- package/dist/cjs/engine-parity-matrix.js +19 -4
- package/dist/cjs/index.js +199 -132
- package/dist/cjs/math.js +342 -15
- package/dist/cjs/movement3d.js +230 -1
- package/dist/cjs/node.js +5 -1
- package/dist/cjs/noise.js +58 -0
- package/dist/cjs/physics-cert.js +147 -6
- package/dist/cjs/physics2d.js +10 -4
- package/dist/cjs/physics3d-joints.js +637 -0
- package/dist/cjs/physics3d-shared.js +288 -0
- package/dist/cjs/physics3d-solver.js +1351 -0
- package/dist/cjs/physics3d-vehicle.js +467 -0
- package/dist/cjs/physics3d.js +1057 -223
- package/dist/cjs/random.js +49 -0
- package/dist/cjs/replay-bundle.js +127 -20
- package/dist/cjs/sample-scenes.js +3 -0
- package/dist/cjs/sdk-offline.js +6 -32
- package/dist/cjs/sdk-session.js +156 -0
- package/dist/cjs/session-build.js +42 -0
- package/dist/cjs/testing.js +107 -0
- package/dist/cli.js +40 -12
- package/dist/collision.d.ts +12 -0
- package/dist/collision.js +52 -0
- package/dist/creator.d.ts +1 -1
- package/dist/creator.js +1 -1
- package/dist/engine-complete.js +214 -4
- package/dist/engine-parity-matrix.js +19 -4
- package/dist/index.d.ts +94 -89
- package/dist/index.js +45 -56
- package/dist/math.d.ts +59 -6
- package/dist/math.js +342 -15
- package/dist/movement3d.d.ts +73 -0
- package/dist/movement3d.js +229 -1
- package/dist/node.d.ts +4 -0
- package/dist/node.js +2 -0
- package/dist/noise.d.ts +7 -0
- package/dist/noise.js +51 -0
- package/dist/physics-cert.d.ts +1 -1
- package/dist/physics-cert.js +147 -6
- package/dist/physics2d.js +10 -4
- package/dist/physics3d-joints.d.ts +94 -0
- package/dist/physics3d-joints.js +634 -0
- package/dist/physics3d-shared.d.ts +72 -0
- package/dist/physics3d-shared.js +257 -0
- package/dist/physics3d-solver.d.ts +197 -0
- package/dist/physics3d-solver.js +1346 -0
- package/dist/physics3d-vehicle.d.ts +84 -0
- package/dist/physics3d-vehicle.js +463 -0
- package/dist/physics3d.d.ts +108 -8
- package/dist/physics3d.js +1006 -177
- package/dist/random.d.ts +7 -0
- package/dist/random.js +49 -0
- package/dist/replay-bundle.d.ts +40 -1
- package/dist/replay-bundle.js +126 -20
- package/dist/sample-scenes.js +3 -0
- package/dist/sdk-offline.js +6 -32
- package/dist/sdk-session.d.ts +47 -0
- package/dist/sdk-session.js +153 -0
- package/dist/session-build.d.ts +21 -0
- package/dist/session-build.js +39 -0
- package/dist/testing.d.ts +52 -0
- package/dist/testing.js +45 -0
- package/dist/tools/vite-plugin.d.ts +8 -0
- package/dist/tools/vite-plugin.js +63 -9
- package/package.json +11 -3
package/dist/cjs/node.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.stableEngineParityJson = exports.runEngineParitySuite = exports.explainEngineParityFixture = exports.runChaosCertificationSuite = exports.explainChaosFixture = exports.startLocalWebSocketProtocolServer = exports.runLocalWebSocketAdapterTest = exports.runNetworkedInputAuthoritySpike = exports.defaultNetworkedInputAuthoritySpikeOptions = exports.verifyReplay = exports.createOfflineSession = void 0;
|
|
17
|
+
exports.stableEngineParityJson = exports.runEngineParitySuite = exports.explainEngineParityFixture = exports.runChaosCertificationSuite = exports.explainChaosFixture = exports.startLocalWebSocketProtocolServer = exports.runLocalWebSocketAdapterTest = exports.runNetworkedInputAuthoritySpike = exports.defaultNetworkedInputAuthoritySpikeOptions = exports.verifyReplayScoreAgainstTrustedRuntime = exports.verifyReplay = exports.createSyncplaySession = exports.createOfflineSession = void 0;
|
|
18
18
|
__exportStar(require("./multiplayer-service.js"), exports);
|
|
19
19
|
__exportStar(require("./input-authority.js"), exports);
|
|
20
20
|
__exportStar(require("./local-authority-wire.js"), exports);
|
|
@@ -23,12 +23,16 @@ __exportStar(require("./session-snapshot.js"), exports);
|
|
|
23
23
|
__exportStar(require("./networked-client.js"), exports);
|
|
24
24
|
var offline_session_js_1 = require("./offline-session.js");
|
|
25
25
|
Object.defineProperty(exports, "createOfflineSession", { enumerable: true, get: function () { return offline_session_js_1.createOfflineSession; } });
|
|
26
|
+
var sdk_session_js_1 = require("./sdk-session.js");
|
|
27
|
+
Object.defineProperty(exports, "createSyncplaySession", { enumerable: true, get: function () { return sdk_session_js_1.createSyncplaySession; } });
|
|
26
28
|
__exportStar(require("./instant-replay.js"), exports);
|
|
27
29
|
__exportStar(require("./pause-resume.js"), exports);
|
|
28
30
|
__exportStar(require("./session-promotion.js"), exports);
|
|
29
31
|
var replay_js_1 = require("./replay.js");
|
|
30
32
|
Object.defineProperty(exports, "verifyReplay", { enumerable: true, get: function () { return replay_js_1.verifyReplay; } });
|
|
31
33
|
__exportStar(require("./replay-scoring.js"), exports);
|
|
34
|
+
var replay_bundle_js_1 = require("./replay-bundle.js");
|
|
35
|
+
Object.defineProperty(exports, "verifyReplayScoreAgainstTrustedRuntime", { enumerable: true, get: function () { return replay_bundle_js_1.verifyReplayScoreAgainstTrustedRuntime; } });
|
|
32
36
|
__exportStar(require("./match-log.js"), exports);
|
|
33
37
|
__exportStar(require("./authority-room.js"), exports);
|
|
34
38
|
__exportStar(require("./ws-frame.js"), exports);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashUint32 = hashUint32;
|
|
4
|
+
exports.hash2 = hash2;
|
|
5
|
+
exports.valueNoise1D = valueNoise1D;
|
|
6
|
+
exports.valueNoise2D = valueNoise2D;
|
|
7
|
+
exports.fbm2D = fbm2D;
|
|
8
|
+
/** Integer avalanche hash (Murmur-style finalizer) → uint32. */
|
|
9
|
+
function hashUint32(x) {
|
|
10
|
+
let h = x | 0;
|
|
11
|
+
h = Math.imul(h ^ (h >>> 16), 0x45d9f3b);
|
|
12
|
+
h = Math.imul(h ^ (h >>> 16), 0x45d9f3b);
|
|
13
|
+
h = (h ^ (h >>> 16)) >>> 0;
|
|
14
|
+
return h;
|
|
15
|
+
}
|
|
16
|
+
function hash2(x, y) {
|
|
17
|
+
return hashUint32((Math.imul(x | 0, 0x1f1f1f1f) ^ Math.imul(y | 0, 0x8da6b343)) >>> 0);
|
|
18
|
+
}
|
|
19
|
+
/** Hash → fixed value in [-scale, scale]. */
|
|
20
|
+
function hashToFixed(h, fixedScale) {
|
|
21
|
+
return Math.trunc((h / 4294967296) * (2 * fixedScale)) - fixedScale;
|
|
22
|
+
}
|
|
23
|
+
function valueNoise1D(math, xFixed, seed) {
|
|
24
|
+
const scale = math.fixedScale;
|
|
25
|
+
const i0 = Math.floor(xFixed / scale);
|
|
26
|
+
const frac = xFixed - i0 * scale;
|
|
27
|
+
const g0 = hashToFixed(hash2(i0, seed), scale);
|
|
28
|
+
const g1 = hashToFixed(hash2(i0 + 1, seed), scale);
|
|
29
|
+
const t = math.smoothstep(0, scale, frac);
|
|
30
|
+
return math.lerp(g0, g1, t);
|
|
31
|
+
}
|
|
32
|
+
function valueNoise2D(math, xFixed, yFixed, seed) {
|
|
33
|
+
const scale = math.fixedScale;
|
|
34
|
+
const ix = Math.floor(xFixed / scale);
|
|
35
|
+
const iy = Math.floor(yFixed / scale);
|
|
36
|
+
const fx = xFixed - ix * scale;
|
|
37
|
+
const fy = yFixed - iy * scale;
|
|
38
|
+
const c = (gx, gy) => hashToFixed(hashUint32((hash2(ix + gx, iy + gy) ^ hashUint32(seed)) >>> 0), scale);
|
|
39
|
+
const tx = math.smoothstep(0, scale, fx);
|
|
40
|
+
const ty = math.smoothstep(0, scale, fy);
|
|
41
|
+
const top = math.lerp(c(0, 0), c(1, 0), tx);
|
|
42
|
+
const bot = math.lerp(c(0, 1), c(1, 1), tx);
|
|
43
|
+
return math.lerp(top, bot, ty);
|
|
44
|
+
}
|
|
45
|
+
function fbm2D(math, xFixed, yFixed, seed, octaves) {
|
|
46
|
+
const scale = math.fixedScale;
|
|
47
|
+
let sum = 0;
|
|
48
|
+
let amp = scale;
|
|
49
|
+
let freq = scale;
|
|
50
|
+
let norm = 0;
|
|
51
|
+
for (let o = 0; o < octaves; o += 1) {
|
|
52
|
+
sum += math.mul(valueNoise2D(math, math.mul(xFixed, freq), math.mul(yFixed, freq), seed + o), amp);
|
|
53
|
+
norm += amp;
|
|
54
|
+
amp = Math.trunc(amp / 2);
|
|
55
|
+
freq = freq * 2;
|
|
56
|
+
}
|
|
57
|
+
return norm === 0 ? 0 : Math.trunc((sum * scale) / norm);
|
|
58
|
+
}
|
package/dist/cjs/physics-cert.js
CHANGED
|
@@ -37,6 +37,8 @@ exports.runPhysicsCert = runPhysicsCert;
|
|
|
37
37
|
exports.runRapierPhysicsCert = runRapierPhysicsCert;
|
|
38
38
|
const canonical_js_1 = require("./canonical.js");
|
|
39
39
|
const collision_js_1 = require("./collision.js");
|
|
40
|
+
const physics3d_js_1 = require("./physics3d.js");
|
|
41
|
+
const physics3d_vehicle_js_1 = require("./physics3d-vehicle.js");
|
|
40
42
|
const rapier2dCompatPackage = '@dimforge/rapier2d-compat';
|
|
41
43
|
const physicsCertResults = {
|
|
42
44
|
true: 'pass',
|
|
@@ -44,8 +46,9 @@ const physicsCertResults = {
|
|
|
44
46
|
};
|
|
45
47
|
function runPhysicsCert(options) {
|
|
46
48
|
validateOptions(options);
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
+
const simulate = options.engine === 'builtin-rigid' ? runRigidSimulation : runPhysicsSimulation;
|
|
50
|
+
const uninterrupted = simulate(options, false);
|
|
51
|
+
const restored = simulate(options, true);
|
|
49
52
|
const snapshotBytes = [...restored.snapshotBytes].sort((left, right) => left - right);
|
|
50
53
|
const stepTimings = [...restored.stepTimings].sort((left, right) => left - right);
|
|
51
54
|
const snapshotTimings = [...restored.snapshotTimings].sort((left, right) => left - right);
|
|
@@ -59,11 +62,11 @@ function runPhysicsCert(options) {
|
|
|
59
62
|
snapshotHashStable;
|
|
60
63
|
return {
|
|
61
64
|
engine: options.engine,
|
|
62
|
-
engineVersion: 'builtin-fixed@1',
|
|
65
|
+
engineVersion: options.engine === 'builtin-rigid' ? 'builtin-rigid@1' : 'builtin-fixed@1',
|
|
63
66
|
adapterVersion: 'physics-cert@1',
|
|
64
67
|
shape: options.shape,
|
|
65
68
|
players: options.players,
|
|
66
|
-
bodies: createInitialBodies(options).length,
|
|
69
|
+
bodies: options.engine === 'builtin-rigid' ? countRigidArenaBodies(options) : createInitialBodies(options).length,
|
|
67
70
|
frames: options.frames,
|
|
68
71
|
snapshotEvery: options.snapshotEvery,
|
|
69
72
|
seed: options.seed,
|
|
@@ -267,6 +270,144 @@ function quantizeRapierNumber(value) {
|
|
|
267
270
|
function countRapierDynamicBodies(options) {
|
|
268
271
|
return options.players * (options.shape === 'fixed-arena' ? 2 : 4);
|
|
269
272
|
}
|
|
273
|
+
function arenaVehicleWheel(localAnchor, steered) {
|
|
274
|
+
return {
|
|
275
|
+
localAnchor,
|
|
276
|
+
suspensionDir: { x: 0, y: -1, z: 0 },
|
|
277
|
+
suspensionRestLength: 0.6,
|
|
278
|
+
suspensionMaxTravel: 0.3,
|
|
279
|
+
radius: 0.3,
|
|
280
|
+
stiffness: 60,
|
|
281
|
+
damping: 220,
|
|
282
|
+
grip: 1,
|
|
283
|
+
maxSteerAngleTurns: 0.1,
|
|
284
|
+
isSteered: steered,
|
|
285
|
+
isDriven: steered,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/** Scripted arena driving: launch, weave, then brake to a stop. */
|
|
289
|
+
function arenaVehicleInput(frame) {
|
|
290
|
+
if (frame >= 400) {
|
|
291
|
+
return { throttle: 0, steer: 0, brake: 10 };
|
|
292
|
+
}
|
|
293
|
+
return { throttle: 10, steer: Math.trunc(frame / 32) % 2 === 0 ? 10 : -10, brake: 0 };
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Mixed rigid arena exercising every solver feature the determinism gates must
|
|
297
|
+
* cover: a settling stack, a hinge door with a motor, a grab-target box, and a
|
|
298
|
+
* high-speed ccd projectile per player.
|
|
299
|
+
*/
|
|
300
|
+
function buildRigidArena(options) {
|
|
301
|
+
const bodies = [{
|
|
302
|
+
id: 'arena-floor', kind: 'static', x: 0, y: 0, z: 0, vx: 0, vy: 0, vz: 0,
|
|
303
|
+
shape: { type: 'box', halfX: 400, halfY: 0.5, halfZ: 400 }, layer: 1, mask: 1,
|
|
304
|
+
material: { friction: 10, restitution: 0 },
|
|
305
|
+
}];
|
|
306
|
+
const joints = [];
|
|
307
|
+
const ident = { x: 0, y: 0, z: 0, w: 1 };
|
|
308
|
+
for (let slot = 0; slot < options.players; slot += 1) {
|
|
309
|
+
const baseX = slot * 12 + (options.seed % 3);
|
|
310
|
+
// stack of 3 boxes
|
|
311
|
+
for (let layer = 0; layer < 3; layer += 1) {
|
|
312
|
+
bodies.push({
|
|
313
|
+
id: `stack-${slot}-${layer}`, kind: 'dynamic', x: baseX, y: 0.5 + 0.5 + layer, z: 0,
|
|
314
|
+
vx: 0, vy: 0, vz: 0, mass: 1, orientation: ident, angularVel: { x: 0, y: 0, z: 0 },
|
|
315
|
+
shape: { type: 'box', halfX: 0.5, halfY: 0.5, halfZ: 0.5 }, layer: 1, mask: 1,
|
|
316
|
+
material: { friction: 8, restitution: 0 },
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
// hinge door
|
|
320
|
+
bodies.push({ id: `frame-${slot}`, kind: 'static', x: baseX + 3, y: 2, z: 0, vx: 0, vy: 0, vz: 0, shape: { type: 'box', halfX: 0.1, halfY: 0.5, halfZ: 0.5 }, layer: 1, mask: 1 });
|
|
321
|
+
bodies.push({ id: `door-${slot}`, kind: 'dynamic', x: baseX + 4, y: 2, z: 0, vx: 0, vy: 0, vz: 0, mass: 1, orientation: ident, angularVel: { x: 0, y: 0, z: 0 }, shape: { type: 'box', halfX: 1, halfY: 0.5, halfZ: 0.1 }, layer: 1, mask: 1 });
|
|
322
|
+
joints.push({ id: `hinge-${slot}`, type: 'hinge', a: `frame-${slot}`, b: `door-${slot}`, anchorA: { x: 0, y: 0, z: 0 }, anchorB: { x: -1, y: 0, z: 0 }, axisA: { x: 0, y: 1, z: 0 }, motorSpeed: 0.02, maxMotorTorque: 50 });
|
|
323
|
+
// grab-target box
|
|
324
|
+
bodies.push({ id: `grab-${slot}`, kind: 'dynamic', x: baseX + 6, y: 3, z: 0, vx: 0, vy: 0, vz: 0, mass: 1, orientation: ident, angularVel: { x: 0, y: 0, z: 0 }, shape: { type: 'box', halfX: 0.5, halfY: 0.5, halfZ: 0.5 }, layer: 1, mask: 1 });
|
|
325
|
+
joints.push({ id: `carry-${slot}`, type: 'grab', body: `grab-${slot}`, anchor: { x: 0, y: 0, z: 0 }, targetX: baseX + 6, targetY: 4, targetZ: 0, maxForce: 20 });
|
|
326
|
+
// ccd projectile toward a thin wall
|
|
327
|
+
bodies.push({ id: `wall-${slot}`, kind: 'static', x: baseX + 9, y: 2, z: 0, vx: 0, vy: 0, vz: 0, shape: { type: 'box', halfX: 0.05, halfY: 2, halfZ: 2 }, layer: 1, mask: 1 });
|
|
328
|
+
bodies.push({ id: `bullet-${slot}`, kind: 'dynamic', x: baseX - 20, y: 2, z: 0, vx: 60, vy: 0, vz: 0, mass: 1, orientation: ident, angularVel: { x: 0, y: 0, z: 0 }, ccd: true, linearDamping: 1, angularDamping: 1, shape: { type: 'box', halfX: 0.2, halfY: 0.2, halfZ: 0.2 }, layer: 1, mask: 1 });
|
|
329
|
+
// soft spring: bob hung 2 below a static anchor, restLength 1 → oscillates and settles
|
|
330
|
+
bodies.push({ id: `spring-anchor-${slot}`, kind: 'static', x: baseX + 2, y: 6, z: 3, vx: 0, vy: 0, vz: 0, shape: { type: 'box', halfX: 0.1, halfY: 0.1, halfZ: 0.1 }, layer: 2, mask: 2 });
|
|
331
|
+
bodies.push({ id: `spring-bob-${slot}`, kind: 'dynamic', x: baseX + 2, y: 4, z: 3, vx: 0, vy: 0, vz: 0, mass: 1, orientation: ident, angularVel: { x: 0, y: 0, z: 0 }, shape: { type: 'box', halfX: 0.3, halfY: 0.3, halfZ: 0.3 }, layer: 2, mask: 2 });
|
|
332
|
+
joints.push({ id: `spring-${slot}`, type: 'spring', a: `spring-anchor-${slot}`, b: `spring-bob-${slot}`, anchorA: { x: 0, y: 0, z: 0 }, anchorB: { x: 0, y: 0, z: 0 }, restLength: 1, frequencyHz: 2, dampingRatio: 0.5 });
|
|
333
|
+
}
|
|
334
|
+
// raycast-wheel vehicle: dropped onto the floor, then driven by a scripted
|
|
335
|
+
// input sequence (see arenaVehicleInput), clear of the per-slot clutter
|
|
336
|
+
bodies.push({
|
|
337
|
+
id: 'arena-car', kind: 'dynamic', x: -6, y: 2.55, z: 12,
|
|
338
|
+
vx: 0, vy: 0, vz: 0, mass: 1200, orientation: ident, angularVel: { x: 0, y: 0, z: 0 },
|
|
339
|
+
shape: { type: 'box', halfX: 1, halfY: 0.25, halfZ: 2 }, layer: 1, mask: 1,
|
|
340
|
+
});
|
|
341
|
+
const vehicle = {
|
|
342
|
+
chassisId: 'arena-car',
|
|
343
|
+
driveForce: 12,
|
|
344
|
+
brakeForce: 40,
|
|
345
|
+
wheels: [
|
|
346
|
+
arenaVehicleWheel({ x: -0.9, y: -0.25, z: 1.5 }, true),
|
|
347
|
+
arenaVehicleWheel({ x: 0.9, y: -0.25, z: 1.5 }, true),
|
|
348
|
+
arenaVehicleWheel({ x: -0.9, y: -0.25, z: -1.5 }, false),
|
|
349
|
+
arenaVehicleWheel({ x: 0.9, y: -0.25, z: -1.5 }, false),
|
|
350
|
+
],
|
|
351
|
+
};
|
|
352
|
+
return { world: (0, physics3d_js_1.createDeterministicPhysicsWorld3D)(bodies), joints, vehicle };
|
|
353
|
+
}
|
|
354
|
+
function countRigidArenaBodies(options) {
|
|
355
|
+
return 2 + options.players * 10; // floor + arena car + (3 stack + frame + door + grab + wall + bullet + spring anchor + spring bob)
|
|
356
|
+
}
|
|
357
|
+
function runRigidSimulation(options, withRestore) {
|
|
358
|
+
const { world: initialWorld, joints, vehicle } = buildRigidArena(options);
|
|
359
|
+
let world = initialWorld;
|
|
360
|
+
const collisionEvents = [];
|
|
361
|
+
const stepTimings = [];
|
|
362
|
+
const snapshotTimings = [];
|
|
363
|
+
const restoreTimings = [];
|
|
364
|
+
const snapshotBytes = [];
|
|
365
|
+
const snapshotChecksums = [];
|
|
366
|
+
let queryOrderStable = true;
|
|
367
|
+
for (let frame = 0; frame < options.frames; frame += 1) {
|
|
368
|
+
const stepStartedAt = performance.now();
|
|
369
|
+
const drive = (0, physics3d_vehicle_js_1.stepDeterministicVehicle3D)(vehicle, arenaVehicleInput(frame), world, { dtTicks: 1 });
|
|
370
|
+
world = (0, physics3d_js_1.applyDeterministicImpulses3D)(world, drive.impulses.map((impulse) => ({
|
|
371
|
+
bodyId: impulse.bodyId,
|
|
372
|
+
impulse: impulse.impulse,
|
|
373
|
+
worldPoint: impulse.point,
|
|
374
|
+
})));
|
|
375
|
+
const step = (0, physics3d_js_1.stepDeterministicPhysicsWorld3D)(world, { gravityY: -0.02, joints, computeChecksum: false });
|
|
376
|
+
world = step.world;
|
|
377
|
+
stepTimings.push(performance.now() - stepStartedAt);
|
|
378
|
+
for (const contact of step.contacts) {
|
|
379
|
+
collisionEvents.push(`${frame + 1}:${contact.pair}:${contact.type}`);
|
|
380
|
+
}
|
|
381
|
+
if (withRestore && frame % options.snapshotEvery === 0) {
|
|
382
|
+
const snapshotStartedAt = performance.now();
|
|
383
|
+
// snapshot the WHOLE world including contactCache/jointCache — dropping the
|
|
384
|
+
// cache here would diverge (the negative case the spike identified).
|
|
385
|
+
const snapshot = JSON.stringify(world);
|
|
386
|
+
snapshotTimings.push(performance.now() - snapshotStartedAt);
|
|
387
|
+
snapshotBytes.push(Buffer.byteLength(snapshot));
|
|
388
|
+
snapshotChecksums.push({ first: (0, canonical_js_1.defaultChecksum)(snapshot), second: (0, canonical_js_1.defaultChecksum)(JSON.stringify(world)) });
|
|
389
|
+
const restoreStartedAt = performance.now();
|
|
390
|
+
world = JSON.parse(snapshot);
|
|
391
|
+
restoreTimings.push(performance.now() - restoreStartedAt);
|
|
392
|
+
}
|
|
393
|
+
const hits = (0, physics3d_js_1.raycastDeterministicPhysics3D)(world, { x: -1000, y: 2, z: 0, dx: 1, dy: 0, dz: 0, maxDistance: 5000, layerMask: 1 });
|
|
394
|
+
for (let index = 1; index < hits.length; index += 1) {
|
|
395
|
+
if (hits[index].distance < hits[index - 1].distance) {
|
|
396
|
+
queryOrderStable = false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
finalChecksum: (0, canonical_js_1.defaultChecksum)(world),
|
|
402
|
+
collisionEvents,
|
|
403
|
+
queryOrderStable,
|
|
404
|
+
stepTimings,
|
|
405
|
+
snapshotTimings,
|
|
406
|
+
restoreTimings,
|
|
407
|
+
snapshotBytes,
|
|
408
|
+
snapshotChecksums,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
270
411
|
function runPhysicsSimulation(options, withRestore) {
|
|
271
412
|
let state = {
|
|
272
413
|
frame: 0,
|
|
@@ -396,8 +537,8 @@ function physicsCertResult(pass) {
|
|
|
396
537
|
return physicsCertResults[String(pass)];
|
|
397
538
|
}
|
|
398
539
|
function validateOptions(options) {
|
|
399
|
-
if (options.engine !== 'builtin-fixed') {
|
|
400
|
-
throw new Error('physics certification currently supports engine=builtin-fixed');
|
|
540
|
+
if (options.engine !== 'builtin-fixed' && options.engine !== 'builtin-rigid') {
|
|
541
|
+
throw new Error('physics certification currently supports engine=builtin-fixed or engine=builtin-rigid');
|
|
401
542
|
}
|
|
402
543
|
validateCommonOptions(options);
|
|
403
544
|
}
|
package/dist/cjs/physics2d.js
CHANGED
|
@@ -272,18 +272,22 @@ function separateDynamicPair(a, b) {
|
|
|
272
272
|
const correctionA = Math.ceil(overlapX / 2);
|
|
273
273
|
const correctionB = Math.ceil(overlapX / 2);
|
|
274
274
|
return {
|
|
275
|
-
a: { ...a, x: a.x + direction * correctionA, vx: normalizeZero(Math.trunc((-a.vx * material.restitution) / 10)) },
|
|
276
|
-
b: { ...b, x: b.x - direction * correctionB, vx: normalizeZero(Math.trunc((-b.vx * material.restitution) / 10)) },
|
|
275
|
+
a: { ...a, x: a.x + direction * correctionA, vx: normalizeZero(Math.trunc((-a.vx * material.restitution) / 10)), vy: applyTangentFriction2D(a.vy, material.friction) },
|
|
276
|
+
b: { ...b, x: b.x - direction * correctionB, vx: normalizeZero(Math.trunc((-b.vx * material.restitution) / 10)), vy: applyTangentFriction2D(b.vy, material.friction) },
|
|
277
277
|
};
|
|
278
278
|
}
|
|
279
279
|
const direction = a.y <= b.y ? -1 : 1;
|
|
280
280
|
const correctionA = Math.ceil(overlapY / 2);
|
|
281
281
|
const correctionB = Math.ceil(overlapY / 2);
|
|
282
282
|
return {
|
|
283
|
-
a: { ...a, y: a.y + direction * correctionA, vy: normalizeZero(Math.trunc((-a.vy * material.restitution) / 10)) },
|
|
284
|
-
b: { ...b, y: b.y - direction * correctionB, vy: normalizeZero(Math.trunc((-b.vy * material.restitution) / 10)) },
|
|
283
|
+
a: { ...a, y: a.y + direction * correctionA, vy: normalizeZero(Math.trunc((-a.vy * material.restitution) / 10)), vx: applyTangentFriction2D(a.vx, material.friction) },
|
|
284
|
+
b: { ...b, y: b.y - direction * correctionB, vy: normalizeZero(Math.trunc((-b.vy * material.restitution) / 10)), vx: applyTangentFriction2D(b.vx, material.friction) },
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
|
+
/** Integer tangential-friction scaling, mirroring physics3d's legacy pusher semantics. */
|
|
288
|
+
function applyTangentFriction2D(velocity, friction) {
|
|
289
|
+
return normalizeZero(Math.trunc((velocity * Math.max(0, 10 - friction)) / 10));
|
|
290
|
+
}
|
|
287
291
|
function separateFromStatic(dynamicBody, staticBody) {
|
|
288
292
|
const dynamicBounds = bodyBounds(dynamicBody);
|
|
289
293
|
const staticBounds = bodyBounds(staticBody);
|
|
@@ -299,6 +303,7 @@ function separateFromStatic(dynamicBody, staticBody) {
|
|
|
299
303
|
...dynamicBody,
|
|
300
304
|
x: dynamicBody.x + direction * overlapX,
|
|
301
305
|
vx: normalizeZero(Math.trunc((-dynamicBody.vx * material.restitution) / 10)),
|
|
306
|
+
vy: applyTangentFriction2D(dynamicBody.vy, material.friction),
|
|
302
307
|
};
|
|
303
308
|
}
|
|
304
309
|
const direction = dynamicBody.y < staticBody.y ? -1 : 1;
|
|
@@ -306,6 +311,7 @@ function separateFromStatic(dynamicBody, staticBody) {
|
|
|
306
311
|
...dynamicBody,
|
|
307
312
|
y: dynamicBody.y + direction * overlapY,
|
|
308
313
|
vy: normalizeZero(Math.trunc((-dynamicBody.vy * material.restitution) / 10)),
|
|
314
|
+
vx: applyTangentFriction2D(dynamicBody.vx, material.friction),
|
|
309
315
|
};
|
|
310
316
|
}
|
|
311
317
|
function detectContacts(bodies) {
|