@series-inc/rundot-syncplay 5.26.0 → 5.28.0-beta.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/dist/physics3d.js CHANGED
@@ -7,6 +7,73 @@ import { buildSolverHull, rayHullIntervalLocal, } from './physics3d-hull.js';
7
7
  import { computeShapeMassProperties } from './physics3d-mass.js';
8
8
  import { heightfieldLocalBounds, heightfieldLocalTriangles } from './physics3d-heightfield.js';
9
9
  import { conservativeToi, toiProxiesOf, toiProxyOf, } from './physics3d-toi.js';
10
+ /**
11
+ * Creates a centered axis-aligned box shape from full dimensional sizes (sizeX, sizeY, sizeZ).
12
+ */
13
+ export function createBoxShapeFromExtents(sizeX, sizeY, sizeZ) {
14
+ if (!Number.isFinite(sizeX) || sizeX <= 0 || !Number.isFinite(sizeY) || sizeY <= 0 || !Number.isFinite(sizeZ) || sizeZ <= 0) {
15
+ throw new Error(`createBoxShapeFromExtents: sizeX, sizeY, sizeZ must be positive finite numbers, received (${sizeX}, ${sizeY}, ${sizeZ})`);
16
+ }
17
+ return {
18
+ type: 'box',
19
+ halfX: sizeX / 2,
20
+ halfY: sizeY / 2,
21
+ halfZ: sizeZ / 2,
22
+ };
23
+ }
24
+ /**
25
+ * Creates a box shape from min and max bounding coordinates.
26
+ * Returns a simple box if centered at (0,0,0), or a single-child compound with offset if not centered.
27
+ */
28
+ export function createBoxShapeFromMinMax(min, max) {
29
+ if (!Number.isFinite(min.x) || !Number.isFinite(min.y) || !Number.isFinite(min.z)
30
+ || !Number.isFinite(max.x) || !Number.isFinite(max.y) || !Number.isFinite(max.z)) {
31
+ throw new Error('createBoxShapeFromMinMax: min and max coordinates must be finite numbers');
32
+ }
33
+ if (max.x <= min.x || max.y <= min.y || max.z <= min.z) {
34
+ throw new Error(`createBoxShapeFromMinMax: max must be strictly greater than min on all axes, received min (${min.x},${min.y},${min.z}) max (${max.x},${max.y},${max.z})`);
35
+ }
36
+ const halfX = (max.x - min.x) / 2;
37
+ const halfY = (max.y - min.y) / 2;
38
+ const halfZ = (max.z - min.z) / 2;
39
+ const cx = (min.x + max.x) / 2;
40
+ const cy = (min.y + max.y) / 2;
41
+ const cz = (min.z + max.z) / 2;
42
+ if (cx === 0 && cy === 0 && cz === 0) {
43
+ return { type: 'box', halfX, halfY, halfZ };
44
+ }
45
+ return {
46
+ type: 'compound',
47
+ children: [
48
+ {
49
+ shape: { type: 'box', halfX, halfY, halfZ },
50
+ offset: { x: cx, y: cy, z: cz },
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ /**
56
+ * Creates an upright capsule shape anchored at its base (y = 0), matching character controller conventions.
57
+ * Encapsulates the offset into a single-child compound shape centered at y = halfHeight + radius.
58
+ */
59
+ export function createCapsuleShapeFromBase(radius, totalHeight) {
60
+ if (!Number.isFinite(radius) || radius <= 0 || !Number.isFinite(totalHeight) || totalHeight <= 0) {
61
+ throw new Error(`createCapsuleShapeFromBase: radius and totalHeight must be positive finite numbers, received radius ${radius}, totalHeight ${totalHeight}`);
62
+ }
63
+ if (totalHeight <= 2 * radius) {
64
+ throw new Error(`createCapsuleShapeFromBase: totalHeight must be strictly greater than 2 * radius (${2 * radius}), received ${totalHeight}`);
65
+ }
66
+ const halfHeight = (totalHeight - 2 * radius) / 2;
67
+ return {
68
+ type: 'compound',
69
+ children: [
70
+ {
71
+ shape: { type: 'capsule', radius, halfHeight },
72
+ offset: { x: 0, y: halfHeight + radius, z: 0 },
73
+ },
74
+ ],
75
+ };
76
+ }
10
77
  export const deterministicPhysicsCallback3D = {
11
78
  collisionEnter: 1,
12
79
  collisionStay: 2,
@@ -281,6 +348,145 @@ export function applyDeterministicWorldEdit3D(world, edit) {
281
348
  : world.jointCache.filter((entry) => !removeJointIds.has(entry.jointId)),
282
349
  };
283
350
  }
351
+ function applyKinematicUpdatesInternal(world, poseById, dt, callerName) {
352
+ const updatedIds = new Set();
353
+ const movedBodies = new Map();
354
+ const newBodies = world.bodies.map((body) => {
355
+ const pose = poseById.get(body.id);
356
+ if (pose === undefined) {
357
+ return body;
358
+ }
359
+ if (body.kind !== 'kinematic') {
360
+ throw new Error(`${callerName}: body ${body.id} is ${body.kind}, expected kinematic`);
361
+ }
362
+ updatedIds.add(body.id);
363
+ let vx = pose.vx;
364
+ let vy = pose.vy;
365
+ let vz = pose.vz;
366
+ if (vx === undefined || vy === undefined || vz === undefined) {
367
+ if (dt !== undefined) {
368
+ vx = vx ?? (pose.x - body.x) / dt;
369
+ vy = vy ?? (pose.y - body.y) / dt;
370
+ vz = vz ?? (pose.z - body.z) / dt;
371
+ }
372
+ else {
373
+ vx = vx ?? body.vx;
374
+ vy = vy ?? body.vy;
375
+ vz = vz ?? body.vz;
376
+ }
377
+ }
378
+ let angularVel = body.angularVel;
379
+ if (pose.angularVelX !== undefined || pose.angularVelY !== undefined || pose.angularVelZ !== undefined) {
380
+ angularVel = {
381
+ x: pose.angularVelX ?? body.angularVel?.x ?? 0,
382
+ y: pose.angularVelY ?? body.angularVel?.y ?? 0,
383
+ z: pose.angularVelZ ?? body.angularVel?.z ?? 0,
384
+ };
385
+ }
386
+ const updated = {
387
+ ...body,
388
+ x: pose.x,
389
+ y: pose.y,
390
+ z: pose.z,
391
+ vx,
392
+ vy,
393
+ vz,
394
+ rx: pose.rx ?? body.rx,
395
+ ry: pose.ry ?? body.ry,
396
+ rz: pose.rz ?? body.rz,
397
+ orientation: pose.orientation ?? body.orientation,
398
+ angularVel,
399
+ };
400
+ movedBodies.set(body.id, updated);
401
+ return updated;
402
+ });
403
+ for (const id of poseById.keys()) {
404
+ if (!updatedIds.has(id)) {
405
+ throw new Error(`${callerName}: cannot update unknown kinematic body ${id}`);
406
+ }
407
+ }
408
+ // Wake any dynamic bodies that share active pairs with updated kinematics
409
+ const wokenDynamicIds = new Set();
410
+ for (const pair of world.activePairMetadata) {
411
+ if (movedBodies.has(pair.a)) {
412
+ for (const member of contactIslandFor(world, pair.b)) {
413
+ wokenDynamicIds.add(member);
414
+ }
415
+ }
416
+ else if (movedBodies.has(pair.b)) {
417
+ for (const member of contactIslandFor(world, pair.a)) {
418
+ wokenDynamicIds.add(member);
419
+ }
420
+ }
421
+ }
422
+ const finalBodies = wokenDynamicIds.size === 0
423
+ ? newBodies
424
+ : newBodies.map((body) => (wokenDynamicIds.has(body.id) && body.kind === 'dynamic' && body.sleeping
425
+ ? { ...body, sleeping: false, lowVelFrames: 0 }
426
+ : body));
427
+ return {
428
+ ...world,
429
+ bodies: finalBodies,
430
+ };
431
+ }
432
+ /**
433
+ * Fast batch update of kinematic body transforms and velocities without
434
+ * allocating Set instances or re-sorting world bodies. Wakes touching dynamic
435
+ * bodies across active contact pairs. If linear velocity (vx, vy, vz) is omitted
436
+ * and dtTicks (> 0) is supplied, linear velocity is derived as (newPos - oldPos) / dtTicks.
437
+ */
438
+ export function setDeterministicKinematicPoses3D(world, poses, options = {}) {
439
+ if (poses.length === 0) {
440
+ return world;
441
+ }
442
+ const dt = options.dtTicks !== undefined && options.dtTicks > 0 ? options.dtTicks : undefined;
443
+ const poseById = new Map();
444
+ for (const pose of poses) {
445
+ if (!Number.isFinite(pose.x) || !Number.isFinite(pose.y) || !Number.isFinite(pose.z)) {
446
+ throw new Error(`setDeterministicKinematicPoses3D: body ${pose.id} position must be finite numbers`);
447
+ }
448
+ poseById.set(pose.id, pose);
449
+ }
450
+ return applyKinematicUpdatesInternal(world, poseById, dt, 'setDeterministicKinematicPoses3D');
451
+ }
452
+ /**
453
+ * Allocation-free flat buffer batch update of kinematic body transforms.
454
+ * Accepts flat [x, y, z, qx, qy, qz, qw] arrays with stride 7 per bodyId,
455
+ * eliminating caller-side object allocations on high-frequency animation loops.
456
+ */
457
+ export function setDeterministicKinematicTransforms3D(world, buffer, options = {}) {
458
+ const { bodyIds, transforms } = buffer;
459
+ if (transforms.length !== bodyIds.length * 7) {
460
+ throw new Error(`setDeterministicKinematicTransforms3D: transforms length must equal bodyIds.length * 7 (${bodyIds.length * 7}), received ${transforms.length}`);
461
+ }
462
+ if (bodyIds.length === 0) {
463
+ return world;
464
+ }
465
+ const dt = options.dtTicks !== undefined && options.dtTicks > 0 ? options.dtTicks : undefined;
466
+ const poseById = new Map();
467
+ for (let i = 0; i < bodyIds.length; i += 1) {
468
+ const id = bodyIds[i];
469
+ const offset = i * 7;
470
+ const x = transforms[offset];
471
+ const y = transforms[offset + 1];
472
+ const z = transforms[offset + 2];
473
+ const qx = transforms[offset + 3];
474
+ const qy = transforms[offset + 4];
475
+ const qz = transforms[offset + 5];
476
+ const qw = transforms[offset + 6];
477
+ if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)
478
+ || !Number.isFinite(qx) || !Number.isFinite(qy) || !Number.isFinite(qz) || !Number.isFinite(qw)) {
479
+ throw new Error(`setDeterministicKinematicTransforms3D: body ${id} transform contains non-finite numbers`);
480
+ }
481
+ poseById.set(id, {
482
+ x,
483
+ y,
484
+ z,
485
+ orientation: { x: qx, y: qy, z: qz, w: qw },
486
+ });
487
+ }
488
+ return applyKinematicUpdatesInternal(world, poseById, dt, 'setDeterministicKinematicTransforms3D');
489
+ }
284
490
  /**
285
491
  * Apply a deterministic impulse to a body (linear + angular via lever arm),
286
492
  * waking the body and its contact island. Returns a new canonical world.
@@ -3112,6 +3318,27 @@ function rayBodyDistance(body, query) {
3112
3318
  if (body.shape.type === 'hull') {
3113
3319
  return rayHullBodyDistance(body, body.shape, query);
3114
3320
  }
3321
+ if (body.shape.type === 'compound') {
3322
+ let closestDistance;
3323
+ for (let index = 0; index < body.shape.children.length; index += 1) {
3324
+ const child = body.shape.children[index];
3325
+ const childBody = {
3326
+ ...body,
3327
+ x: (body.x ?? 0) + (child.offset?.x ?? 0),
3328
+ y: (body.y ?? 0) + (child.offset?.y ?? 0),
3329
+ z: (body.z ?? 0) + (child.offset?.z ?? 0),
3330
+ shape: child.shape,
3331
+ rx: (body.rx ?? 0) + canonicalQuarterTurns(child.quarterTurns?.x ?? 0) / 4,
3332
+ ry: (body.ry ?? 0) + canonicalQuarterTurns(child.quarterTurns?.y ?? 0) / 4,
3333
+ rz: (body.rz ?? 0) + canonicalQuarterTurns(child.quarterTurns?.z ?? 0) / 4,
3334
+ };
3335
+ const childDistance = rayBodyDistance(childBody, query);
3336
+ if (childDistance !== undefined && (closestDistance === undefined || childDistance < closestDistance)) {
3337
+ closestDistance = childDistance;
3338
+ }
3339
+ }
3340
+ return closestDistance;
3341
+ }
3115
3342
  return rayAabbDistance(toBounds(body), query);
3116
3343
  }
3117
3344
  function rayHullBodyDistance(body, shape, query) {
@@ -3425,13 +3652,61 @@ function countCandidatePairs(bodies) {
3425
3652
  countCandidatePairsCalls += 1;
3426
3653
  return broadphaseCandidatePairs(bodies).length;
3427
3654
  }
3655
+ function compoundChildDetailedHit(body, query, distance) {
3656
+ if (body.shape.type !== 'compound') {
3657
+ return undefined;
3658
+ }
3659
+ let bestChildIndex;
3660
+ let bestNormal;
3661
+ let bestDistDiff = Infinity;
3662
+ for (let index = 0; index < body.shape.children.length; index += 1) {
3663
+ const child = body.shape.children[index];
3664
+ const childBody = {
3665
+ ...body,
3666
+ x: (body.x ?? 0) + (child.offset?.x ?? 0),
3667
+ y: (body.y ?? 0) + (child.offset?.y ?? 0),
3668
+ z: (body.z ?? 0) + (child.offset?.z ?? 0),
3669
+ shape: child.shape,
3670
+ rx: (body.rx ?? 0) + canonicalQuarterTurns(child.quarterTurns?.x ?? 0) / 4,
3671
+ ry: (body.ry ?? 0) + canonicalQuarterTurns(child.quarterTurns?.y ?? 0) / 4,
3672
+ rz: (body.rz ?? 0) + canonicalQuarterTurns(child.quarterTurns?.z ?? 0) / 4,
3673
+ };
3674
+ const childDist = 'dx' in query ? rayBodyDistance(childBody, query) : undefined;
3675
+ if (childDist !== undefined) {
3676
+ const diff = Math.abs(childDist - distance);
3677
+ if (diff < bestDistDiff) {
3678
+ bestDistDiff = diff;
3679
+ bestChildIndex = index;
3680
+ const center = quantizePoint({
3681
+ x: query.x + query.dx * childDist,
3682
+ y: query.y + query.dy * childDist,
3683
+ z: query.z + query.dz * childDist,
3684
+ });
3685
+ bestNormal = hitNormalForBody(childBody, center);
3686
+ }
3687
+ }
3688
+ }
3689
+ if (bestChildIndex !== undefined && bestNormal !== undefined) {
3690
+ return { childIndex: bestChildIndex, normal: bestNormal };
3691
+ }
3692
+ return undefined;
3693
+ }
3428
3694
  function detailedHitForBody(body, query, distance, castRadius) {
3429
3695
  const center = quantizePoint({
3430
3696
  x: query.x + query.dx * distance,
3431
3697
  y: query.y + query.dy * distance,
3432
3698
  z: query.z + query.dz * distance,
3433
3699
  });
3434
- const normal = hitNormalForBody(body, center);
3700
+ let normal;
3701
+ let childIndex;
3702
+ if (body.shape.type === 'compound') {
3703
+ const compoundHit = compoundChildDetailedHit(body, query, distance);
3704
+ childIndex = compoundHit?.childIndex;
3705
+ normal = compoundHit?.normal ?? hitNormalForBody(body, center);
3706
+ }
3707
+ else {
3708
+ normal = hitNormalForBody(body, center);
3709
+ }
3435
3710
  const point = quantizePoint({
3436
3711
  x: center.x - normal.x * castRadius,
3437
3712
  y: center.y - normal.y * castRadius,
@@ -3443,6 +3718,7 @@ function detailedHitForBody(body, query, distance, castRadius) {
3443
3718
  point,
3444
3719
  normal,
3445
3720
  penetration: distance === 0 ? penetrationAtPoint(body, center, castRadius) : 0,
3721
+ childIndex,
3446
3722
  };
3447
3723
  }
3448
3724
  function hitNormalForBody(body, point) {
@@ -3453,6 +3729,24 @@ function hitNormalForBody(body, point) {
3453
3729
  z: point.z - body.z,
3454
3730
  });
3455
3731
  }
3732
+ if (body.shape.type === 'capsule') {
3733
+ const halfHeight = body.shape.halfHeight;
3734
+ let localPoint = { x: point.x - body.x, y: point.y - body.y, z: point.z - body.z };
3735
+ if (body.orientation !== undefined) {
3736
+ localPoint = rotatePointByQuatInverse(body.orientation, localPoint);
3737
+ }
3738
+ const clampedY = Math.max(-halfHeight, Math.min(halfHeight, localPoint.y));
3739
+ const localDelta = {
3740
+ x: localPoint.x,
3741
+ y: localPoint.y - clampedY,
3742
+ z: localPoint.z,
3743
+ };
3744
+ const localNormal = normalizePoint(localDelta);
3745
+ if (body.orientation !== undefined) {
3746
+ return normalizePoint(rotatePointByQuat(body.orientation, localNormal));
3747
+ }
3748
+ return localNormal;
3749
+ }
3456
3750
  const bounds = toBounds(body);
3457
3751
  const candidates = [
3458
3752
  { normal: { x: -1, y: 0, z: 0 }, distance: Math.abs(point.x - bounds.minX), axis: 'x-' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@series-inc/rundot-syncplay",
3
- "version": "5.26.0",
3
+ "version": "5.28.0-beta.0",
4
4
  "description": "Kinetix orchestration, rollback, replay, late-join, and deterministic networking for RUN.game",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,6 +42,11 @@
42
42
  "import": "./dist/tools/index.js",
43
43
  "default": "./dist/tools/index.js"
44
44
  },
45
+ "./debug": {
46
+ "types": "./dist/debug-wireframe.d.ts",
47
+ "import": "./dist/debug-wireframe.js",
48
+ "default": "./dist/debug-wireframe.js"
49
+ },
45
50
  "./modules/component-model": {
46
51
  "types": "./dist/modules/component-model.d.ts",
47
52
  "import": "./dist/modules/component-model.js",
@@ -163,7 +168,7 @@
163
168
  "test:core": "node --expose-gc --import tsx --test --test-concurrency=1 tests/canonical.test.ts tests/canonical-portability.test.ts tests/runtime-session.test.ts tests/kinetix-runtime-session.test.ts tests/runtime-support.test.ts tests/runtime-guards.test.ts tests/r3f-render-adapter.test.ts tests/snapshot-cost.test.ts tests/sdkSession.test.ts tests/input-authority.test.ts tests/session-wire.test.ts tests/runtime-room-metadata.test.ts tests/session-snapshot.test.ts tests/authority-room.test.ts tests/networked-client-snapshot.test.ts tests/runner.test.ts tests/testing.test.ts tests/browser-session.test.ts tests/secret-config.test.ts tests/secret-protocol.test.ts tests/secret-authority.test.ts tests/secret-client.test.ts tests/late-join-checksum.test.ts tests/late-join-input-backfill.test.ts tests/replay-scoring.test.ts tests/bot-backfill.test.ts tests/latency-simulation.test.ts tests/production-input-budget.test.ts tests/time-sync.test.ts tests/match-log.test.ts tests/spectator.test.ts tests/synctest.test.ts tests/instant-replay.test.ts tests/session-promotion.test.ts tests/physics3d-broadphase-stats.test.ts tests/entrypoints.test.ts tests/export-surface.test.ts tests/module-seam.test.ts tests/module-contract-portability.test.ts tests/module-identity.test.ts tests/module-corpus.test.ts tests/component-model.test.ts tests/component-model-rollback.test.ts tests/component-model-integration.test.ts tests/actors-ai.test.ts tests/character-3d.test.ts tests/character-3d-certification.test.ts tests/combat-modules.test.ts tests/combat-modules-integration.test.ts tests/firearm.test.ts tests/gameplay-effects.test.ts tests/spatial-interaction.test.ts tests/stats-abilities.test.ts tests/inventory-equipment.test.ts tests/inventory-equipment-certification.test.ts tests/platformer-2d.test.ts tests/platformer-2d-certification.test.ts tests/module-promotion.test.ts tests/module-docs.test.ts tests/module-bundle-budget.test.ts tests/published-side-effects.test.ts tests/host-timing.test.ts tests/test-manifest.test.ts tests/coarse-region.test.ts tests/input-codec.test.ts tests/command-ingress.test.ts tests/session-reconfigure.test.ts tests/prediction-mode.test.ts tests/ephemeral-channel.test.ts tests/runner-surface.test.ts tests/runner-harness.test.ts tests/effects-adapter-bounds.test.ts tests/simulated-match-secrets.test.ts tests/run-glue-surface.test.mjs core/tests/core-boundary.test.mjs tests/math-core-parity.test.ts tests/math-profile.test.ts tests/movement3d-kcc-unit-contract.test.ts tests/movement3d-static-ground.test.ts",
164
169
  "test:sdk-contract": "node --import tsx --test tests/sdk-runtime-contract.test.ts",
165
170
  "test:soak": "tsx --test tests/runtime-soak.test.ts",
166
- "test:extended": "tsx --test tests/kcc-vehicle-characterization.test.ts tests/animation.test.ts tests/asset-cooking.test.ts tests/backend-proof-checksum-vectors.test.ts tests/bot-documents.test.ts tests/bot-primitives.test.ts tests/collider-cooking.test.ts tests/collision.test.ts tests/command-timeline.test.ts tests/config-bundle.test.ts tests/cosmetic-physics3d.test.ts tests/deterministic-primitives.test.ts tests/ecs-lite.test.ts tests/effects-adapter.test.ts tests/engine-runtime.test.ts tests/errors.test.ts tests/events.test.ts tests/input-button.test.ts tests/load-128-players.test.ts tests/math.test.ts tests/movement.test.ts tests/movement3d.test.ts tests/multiclient-authority.test.ts tests/navigation.test.ts tests/noise.test.ts tests/physics-cert.test.ts tests/physics2d.test.ts tests/physics3d-ccd.test.ts tests/physics3d-coverage.test.ts tests/physics3d-destruction-cert.test.ts tests/physics3d-geometry-limits.test.ts tests/physics3d-heightfield.test.ts tests/physics3d-hull.test.ts tests/physics3d-joints.test.ts tests/physics3d-kinematic-mesh.test.ts tests/physics3d-mass.test.ts tests/physics3d-materials.test.ts tests/physics3d-mesh-onesided.test.ts tests/physics3d-query-index.test.ts tests/physics3d-recipes.test.ts tests/physics3d-rotation-convention.test.ts tests/physics3d-shared.test.ts tests/physics3d-sleeping-manifold-reuse.test.ts tests/physics3d-solver.test.ts tests/physics3d-static-memoization.test.ts tests/physics3d-static-overlap-memo.test.ts tests/physics3d-cylinder-hull.test.ts tests/physics3d-stacking.test.ts tests/physics3d-toi.test.ts tests/physics3d-vehicle.test.ts tests/physics3d-voxel.test.ts tests/physics3d-world-edit.test.ts tests/physics3d.test.ts tests/physics-vector-generator.test.ts tests/random.test.ts tests/rollback-history.test.ts tests/schema-artifacts.test.ts tests/schema-codegen.test.ts tests/schema-dsl-binary.test.ts tests/schema.test.ts tests/signals.test.ts tests/sparse-set.test.ts tests/static-checker.test.ts tests/system-lifecycle.test.ts tests/wire-canonical-sentinels.test.ts tests/ws-frame.test.ts",
171
+ "test:extended": "tsx --test tests/kcc-vehicle-characterization.test.ts tests/animation.test.ts tests/asset-cooking.test.ts tests/backend-proof-checksum-vectors.test.ts tests/bot-documents.test.ts tests/bot-primitives.test.ts tests/collider-cooking.test.ts tests/collision.test.ts tests/command-timeline.test.ts tests/config-bundle.test.ts tests/cosmetic-physics3d.test.ts tests/deterministic-primitives.test.ts tests/ecs-lite.test.ts tests/effects-adapter.test.ts tests/engine-runtime.test.ts tests/errors.test.ts tests/events.test.ts tests/input-button.test.ts tests/load-128-players.test.ts tests/math.test.ts tests/movement.test.ts tests/movement3d.test.ts tests/multiclient-authority.test.ts tests/navigation.test.ts tests/noise.test.ts tests/physics-cert.test.ts tests/physics2d.test.ts tests/physics3d-ccd.test.ts tests/physics3d-coverage.test.ts tests/physics3d-debug-wireframe.test.ts tests/physics3d-destruction-cert.test.ts tests/physics3d-geometry-limits.test.ts tests/physics3d-heightfield.test.ts tests/physics3d-hull.test.ts tests/physics3d-joints.test.ts tests/physics3d-kinematic-mesh.test.ts tests/physics3d-kinematic-poses.test.ts tests/physics3d-kinematic-transforms.test.ts tests/physics3d-mass.test.ts tests/physics3d-materials.test.ts tests/physics3d-mesh-onesided.test.ts tests/physics3d-queries-capsule-childindex.test.ts tests/physics3d-query-index.test.ts tests/physics3d-recipes.test.ts tests/physics3d-rotation-convention.test.ts tests/physics3d-shape-builders.test.ts tests/physics3d-shared.test.ts tests/physics3d-sleeping-manifold-reuse.test.ts tests/physics3d-solver.test.ts tests/physics3d-static-memoization.test.ts tests/physics3d-static-overlap-memo.test.ts tests/physics3d-cylinder-hull.test.ts tests/physics3d-stacking.test.ts tests/physics3d-toi.test.ts tests/physics3d-vehicle.test.ts tests/physics3d-voxel-carving.test.ts tests/physics3d-voxel-dda.test.ts tests/physics3d-voxel.test.ts tests/physics3d-world-edit.test.ts tests/physics3d.test.ts tests/physics-vector-generator.test.ts tests/random.test.ts tests/rollback-history.test.ts tests/schema-artifacts.test.ts tests/schema-codegen.test.ts tests/schema-dsl-binary.test.ts tests/schema.test.ts tests/signals.test.ts tests/sparse-set.test.ts tests/static-checker.test.ts tests/system-lifecycle.test.ts tests/wire-canonical-sentinels.test.ts tests/ws-frame.test.ts",
167
172
  "test:ci": "npm run build && npm run test",
168
173
  "build:native": "node core/scripts/build-native.mjs",
169
174
  "build:mobile-native": "node core/scripts/build-mobile-native.mjs",