@series-inc/rundot-syncplay 5.26.0-beta.8 → 5.26.0-beta.9
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 +29 -3
- package/dist/networked-client.d.ts +7 -5
- package/dist/networked-client.js +30 -3
- package/dist/node.d.ts +2 -0
- package/dist/node.js +1 -0
- package/dist/rooms-config-keys.d.ts +11 -0
- package/dist/rooms-config-keys.js +17 -0
- package/dist/tools/host-timing.d.ts +8 -0
- package/dist/tools/host-timing.js +36 -0
- package/dist/tools/module-certification.js +10 -18
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -122,9 +122,11 @@ nothing and is a command that executes on import.
|
|
|
122
122
|
`certifyModule(component, fixture)` on `./tools` is the provisional conformance
|
|
123
123
|
gate for `./modules/*`. It delegates straight, rollback, replay, and hydration
|
|
124
124
|
equivalence to `runSyncplaySynctest`, then adds module-only source rules,
|
|
125
|
-
permutation equivalence,
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
permutation equivalence, the median of five warmed p99 batches, and retained
|
|
126
|
+
heap per step. Each batch uses the declared warm-up count and sample count.
|
|
127
|
+
Certification runs the warm-up before each batch. It compares the median with
|
|
128
|
+
the unchanged step-time limit. Run it under Node with `--expose-gc`. Missing GC
|
|
129
|
+
support fails closed instead of reporting a zero allocation.
|
|
128
130
|
|
|
129
131
|
The published JavaScript graph has a committed exhaustive side-effect audit.
|
|
130
132
|
`core/scripts/preflight.mjs` and
|
|
@@ -341,6 +343,30 @@ runner without rewriting those layers:
|
|
|
341
343
|
RTT-adaptive) are forwarded verbatim, and live `netStats` (RTT, timescale, input
|
|
342
344
|
delay) is surfaced on the snapshot + runner (`null` offline) for a netcode HUD.
|
|
343
345
|
|
|
346
|
+
### Simulation, rendering, input, and confirmation
|
|
347
|
+
|
|
348
|
+
One session uses one fixed simulation rate. The rate is part of its runtime and
|
|
349
|
+
replay identity. Rendering follows the host display loop. Rendering does not
|
|
350
|
+
define the simulation rate.
|
|
351
|
+
|
|
352
|
+
A player sends control input for each simulation tick. The input can contain
|
|
353
|
+
movement, aim, and buttons. It does not contain game state or projectile state.
|
|
354
|
+
The deterministic simulation creates projectiles from confirmed fire input. The
|
|
355
|
+
authority orders one canonical input frame for each tick. It can replace late
|
|
356
|
+
input with the defined substitute. Every client receives that confirmed frame.
|
|
357
|
+
A client rolls back only when its prediction differs.
|
|
358
|
+
|
|
359
|
+
Use 30 Hz for a new general-purpose game. Use 20 Hz when simulation cost is high
|
|
360
|
+
or action is low, and 50 ms rule steps are acceptable. Use 60 Hz only when game
|
|
361
|
+
rules need 16.7 ms resolution. Input and confirmation remain per simulation tick
|
|
362
|
+
at all supported rates.
|
|
363
|
+
|
|
364
|
+
When `inputDelay` is omitted, `pumpPaced()` uses an adaptive zero-to-100-ms
|
|
365
|
+
range. It calculates the delay needed to fit RTT plus `targetLeadTicks` inside
|
|
366
|
+
`maxPredictionTicks`. The first RTT sample applies immediately. Later changes
|
|
367
|
+
use three matching samples. Equal explicit `minTicks` and `maxTicks` values
|
|
368
|
+
select a static delay.
|
|
369
|
+
|
|
344
370
|
Direct `NetworkedSyncplayClient` users get the same projection surface:
|
|
345
371
|
`getPresentationFrame()`, `renderAlpha`, `currentFrame`, `caughtUp`, and a
|
|
346
372
|
bounded confirmed-frame queue via the optional `confirmedPresentationBufferSize`
|
|
@@ -84,9 +84,11 @@ export interface NetworkedSyncplayClientOptions<State, Input, Checkpoint = unkno
|
|
|
84
84
|
* tick T is bound and sent for tick T+delay, trading a fixed sliver of local
|
|
85
85
|
* latency for inputs that arrive before the authority's deadline (fewer
|
|
86
86
|
* substitutions of THIS client's input, shallower rollbacks for peers).
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
87
|
+
* An omitted value adapts from zero through 100 ms of simulation ticks. It
|
|
88
|
+
* closes the gap between RTT plus the target lead and maxPredictionTicks.
|
|
89
|
+
* With `maxTicks > minTicks`, the supplied range adapts with the same
|
|
90
|
+
* formula. Equal bounds select a static delay. RTT measurement requires
|
|
91
|
+
* driving `pumpPaced`.
|
|
90
92
|
*/
|
|
91
93
|
readonly inputDelay?: NetworkedSyncplayClientInputDelayOptions;
|
|
92
94
|
/** Monotonic-enough clock for RTT measurement. Default Date.now. NEVER used by the sim. */
|
|
@@ -122,9 +124,9 @@ export interface NetworkedSyncplayClientPacingOptions {
|
|
|
122
124
|
readonly maxStepsPerPump?: number;
|
|
123
125
|
}
|
|
124
126
|
export interface NetworkedSyncplayClientInputDelayOptions {
|
|
125
|
-
/** Minimum
|
|
127
|
+
/** Minimum and initial own-input delay in ticks. Default 0. */
|
|
126
128
|
readonly minTicks?: number;
|
|
127
|
-
/** Maximum
|
|
129
|
+
/** Maximum adaptive delay. Equal to minTicks for a static delay. */
|
|
128
130
|
readonly maxTicks?: number;
|
|
129
131
|
}
|
|
130
132
|
/** Live netcode telemetry for the local client (G8). */
|
package/dist/networked-client.js
CHANGED
|
@@ -14,6 +14,7 @@ const DEFAULT_MIN_TIMESCALE = 0.9;
|
|
|
14
14
|
const DEFAULT_MAX_TIMESCALE = 1.1;
|
|
15
15
|
const DEFAULT_CORRECTION_PER_TICK = 0.02;
|
|
16
16
|
const DEFAULT_MAX_STEPS_PER_PUMP = 4;
|
|
17
|
+
const DEFAULT_MAX_INPUT_DELAY_MS = 100;
|
|
17
18
|
/** Frame-advantage error inside this band leaves the timescale at 1 (no hunting on jitter). */
|
|
18
19
|
const TIMESCALE_DEADBAND_TICKS = 1;
|
|
19
20
|
/** RTT smoothing factor (EWMA); ~4 samples to converge. */
|
|
@@ -193,8 +194,11 @@ export function createNetworkedSyncplayClient(options) {
|
|
|
193
194
|
const maxTimescale = options.pacing?.maxTimescale ?? DEFAULT_MAX_TIMESCALE;
|
|
194
195
|
const correctionPerTick = options.pacing?.correctionPerTick ?? DEFAULT_CORRECTION_PER_TICK;
|
|
195
196
|
const maxStepsPerPump = options.pacing?.maxStepsPerPump ?? DEFAULT_MAX_STEPS_PER_PUMP;
|
|
197
|
+
const usesDefaultInputDelayRange = options.inputDelay === undefined;
|
|
196
198
|
const inputDelayMinTicks = options.inputDelay?.minTicks ?? 0;
|
|
197
|
-
const
|
|
199
|
+
const configuredInputDelayMaxTicks = Math.max(options.inputDelay?.maxTicks ?? inputDelayMinTicks, inputDelayMinTicks);
|
|
200
|
+
let inputDelayMaxTicks = configuredInputDelayMaxTicks;
|
|
201
|
+
let firstDefaultDelaySamplePending = usesDefaultInputDelayRange;
|
|
198
202
|
const remoteInputPrediction = options.remoteInputPrediction ?? 'repeat-last';
|
|
199
203
|
if (remoteInputPrediction !== 'repeat-last' && remoteInputPrediction !== 'neutral') {
|
|
200
204
|
throw new Error('SYNCPLAY_REMOTE_INPUT_PREDICTION_INVALID');
|
|
@@ -427,6 +431,16 @@ export function createNetworkedSyncplayClient(options) {
|
|
|
427
431
|
outgoingTransferQueue.length = 0;
|
|
428
432
|
tickRateHz = message.tickRateHz;
|
|
429
433
|
frameMs = tickRateHz > 0 ? 1000 / tickRateHz : 0;
|
|
434
|
+
inputDelayMaxTicks = usesDefaultInputDelayRange
|
|
435
|
+
? Math.max(1, Math.ceil(tickRateHz * DEFAULT_MAX_INPUT_DELAY_MS / 1_000))
|
|
436
|
+
: configuredInputDelayMaxTicks;
|
|
437
|
+
inputDelayTicks = inputDelayMinTicks;
|
|
438
|
+
pendingDelayCandidate = inputDelayMinTicks;
|
|
439
|
+
pendingDelayAgreement = 0;
|
|
440
|
+
firstDefaultDelaySamplePending = usesDefaultInputDelayRange;
|
|
441
|
+
rttMs = -1;
|
|
442
|
+
lastPingSentMs = undefined;
|
|
443
|
+
pendingPings.clear();
|
|
430
444
|
ownScheduleCursor = 0;
|
|
431
445
|
ownScheduleCatchUp = true;
|
|
432
446
|
ownScheduleFreshRoom = false;
|
|
@@ -436,7 +450,6 @@ export function createNetworkedSyncplayClient(options) {
|
|
|
436
450
|
// A re-greet resets the sim; pacing estimates must not survive it either
|
|
437
451
|
// (fresh observations arrive with the very next confirmed-input).
|
|
438
452
|
authorityEpochsMs.length = 0;
|
|
439
|
-
pendingPings.clear();
|
|
440
453
|
timescale = 1;
|
|
441
454
|
const runtime = options.runtimeFactory(message.runtimeIdentity, message.sessionConfigBytes.slice());
|
|
442
455
|
if (canonicalStringify(runtime.identity)
|
|
@@ -588,11 +601,25 @@ export function createNetworkedSyncplayClient(options) {
|
|
|
588
601
|
options.transport.send(encodeDeterministicSessionMessage({ kind: 'time-sync-ping', clientSendTick: predictedThrough, nonce: pingNonce }));
|
|
589
602
|
}
|
|
590
603
|
// ── G2: ping-adaptive own-input delay ──
|
|
604
|
+
function desiredInputDelayTicks() {
|
|
605
|
+
if (rttMs < 0 || frameMs <= 0)
|
|
606
|
+
return inputDelayMinTicks;
|
|
607
|
+
const rttTicks = rttMs / frameMs;
|
|
608
|
+
const requiredPredictionSpan = Math.ceil(rttTicks + targetLeadTicks);
|
|
609
|
+
return clamp(requiredPredictionSpan - maxPredictionTicks, inputDelayMinTicks, inputDelayMaxTicks);
|
|
610
|
+
}
|
|
591
611
|
function maybeAdaptInputDelay() {
|
|
592
612
|
if (inputDelayMaxTicks <= inputDelayMinTicks) {
|
|
593
613
|
return;
|
|
594
614
|
}
|
|
595
|
-
const desired =
|
|
615
|
+
const desired = desiredInputDelayTicks();
|
|
616
|
+
if (firstDefaultDelaySamplePending) {
|
|
617
|
+
inputDelayTicks = desired;
|
|
618
|
+
pendingDelayCandidate = desired;
|
|
619
|
+
pendingDelayAgreement = 0;
|
|
620
|
+
firstDefaultDelaySamplePending = false;
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
596
623
|
if (desired === inputDelayTicks) {
|
|
597
624
|
pendingDelayAgreement = 0;
|
|
598
625
|
return;
|
package/dist/node.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export * from './secret-authority.js';
|
|
|
18
18
|
export { buildMerkleTree, bytesToHex, concatBytes, constantTimeEqual, createBrowserSyncplaySecretCrypto, hexToBytes, merkleLeafHash, merkleNodeHash, merkleProof, sha256Bytes, signedReceiptBytes, verifyHashChainReveal, verifyMerkleProof, verifySyncplaySignedReceipt, } from './secret-crypto.js';
|
|
19
19
|
export type { SyncplaySecretAuthorityCrypto, SyncplaySecretBrowserCrypto } from './secret-crypto.js';
|
|
20
20
|
export * from './ws-frame.js';
|
|
21
|
+
export { SYNCPLAY_DETERMINISTIC_ROOM_OPTION_KEYS } from './rooms-config-keys.js';
|
|
22
|
+
export type { SyncplayDeterministicRoomOptionKey } from './rooms-config-keys.js';
|
|
21
23
|
export { assertCookedColliderIsRuntimeValid, cookConvexDecomposition, } from './collider-cooking.js';
|
|
22
24
|
export type { ColliderCookOptions, ColliderSourceMesh, CookedCollider, CookedColliderHull, } from './collider-cooking.js';
|
|
23
25
|
export { DevSyncplayAuthorityRoom } from './dev-authority-room.js';
|
package/dist/node.js
CHANGED
|
@@ -15,6 +15,7 @@ export * from './secret-protocol.js';
|
|
|
15
15
|
export * from './secret-authority.js';
|
|
16
16
|
export { buildMerkleTree, bytesToHex, concatBytes, constantTimeEqual, createBrowserSyncplaySecretCrypto, hexToBytes, merkleLeafHash, merkleNodeHash, merkleProof, sha256Bytes, signedReceiptBytes, verifyHashChainReveal, verifyMerkleProof, verifySyncplaySignedReceipt, } from './secret-crypto.js';
|
|
17
17
|
export * from './ws-frame.js';
|
|
18
|
+
export { SYNCPLAY_DETERMINISTIC_ROOM_OPTION_KEYS } from './rooms-config-keys.js';
|
|
18
19
|
export { assertCookedColliderIsRuntimeValid, cookConvexDecomposition, } from './collider-cooking.js';
|
|
19
20
|
// Dev-server deterministic room class (relocated from the game SDK). A game's
|
|
20
21
|
// vite.config passes this to rundotMultiplayerPlugin({ deterministicRoomClass }).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every option key a deterministic (SyncPlay) room object may carry.
|
|
3
|
+
*
|
|
4
|
+
* This is the ONE list. `server/mp-room-server` enforces it when it loads a
|
|
5
|
+
* bundle, and `server/cloud-run` checks its upload allowlist against it. Before
|
|
6
|
+
* this list existed the two validators were hand-copied and drifted, which left
|
|
7
|
+
* `secrets`, `reconfigurePolicy`, and `commandsPerSecond` honored by the room
|
|
8
|
+
* server but impossible to publish.
|
|
9
|
+
*/
|
|
10
|
+
export declare const SYNCPLAY_DETERMINISTIC_ROOM_OPTION_KEYS: readonly ["protocol", "simulationHooks", "secrets", "runtime", "reconfigurePolicy", "commandsPerSecond"];
|
|
11
|
+
export type SyncplayDeterministicRoomOptionKey = (typeof SYNCPLAY_DETERMINISTIC_ROOM_OPTION_KEYS)[number];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every option key a deterministic (SyncPlay) room object may carry.
|
|
3
|
+
*
|
|
4
|
+
* This is the ONE list. `server/mp-room-server` enforces it when it loads a
|
|
5
|
+
* bundle, and `server/cloud-run` checks its upload allowlist against it. Before
|
|
6
|
+
* this list existed the two validators were hand-copied and drifted, which left
|
|
7
|
+
* `secrets`, `reconfigurePolicy`, and `commandsPerSecond` honored by the room
|
|
8
|
+
* server but impossible to publish.
|
|
9
|
+
*/
|
|
10
|
+
export const SYNCPLAY_DETERMINISTIC_ROOM_OPTION_KEYS = [
|
|
11
|
+
'protocol',
|
|
12
|
+
'simulationHooks',
|
|
13
|
+
'secrets',
|
|
14
|
+
'runtime',
|
|
15
|
+
'reconfigurePolicy',
|
|
16
|
+
'commandsPerSecond',
|
|
17
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const SYNCPLAY_HOST_TIMING_BATCHES = 5;
|
|
2
|
+
export interface RobustHostTimingOptions {
|
|
3
|
+
readonly warmupSteps: number;
|
|
4
|
+
readonly sampleSteps: number;
|
|
5
|
+
readonly run: (index: number) => void;
|
|
6
|
+
readonly now?: () => number;
|
|
7
|
+
}
|
|
8
|
+
export declare function measureRobustHostP99(options: Readonly<RobustHostTimingOptions>): number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const SYNCPLAY_HOST_TIMING_BATCHES = 5;
|
|
2
|
+
export function measureRobustHostP99(options) {
|
|
3
|
+
if (!Number.isSafeInteger(options.warmupSteps)
|
|
4
|
+
|| options.warmupSteps < 0
|
|
5
|
+
|| !Number.isSafeInteger(options.sampleSteps)
|
|
6
|
+
|| options.sampleSteps < 1) {
|
|
7
|
+
throw new Error('SYNCPLAY_HOST_TIMING_INVALID');
|
|
8
|
+
}
|
|
9
|
+
const now = options.now ?? (() => performance.now());
|
|
10
|
+
const batchP99 = [];
|
|
11
|
+
for (let batch = 0; batch < SYNCPLAY_HOST_TIMING_BATCHES; batch += 1) {
|
|
12
|
+
for (let index = 0; index < options.warmupSteps; index += 1) {
|
|
13
|
+
options.run(index);
|
|
14
|
+
}
|
|
15
|
+
const timings = [];
|
|
16
|
+
for (let index = 0; index < options.sampleSteps; index += 1) {
|
|
17
|
+
const startedAt = now();
|
|
18
|
+
options.run(index);
|
|
19
|
+
const elapsed = now() - startedAt;
|
|
20
|
+
if (!Number.isFinite(elapsed) || elapsed < 0) {
|
|
21
|
+
throw new Error('SYNCPLAY_HOST_TIMING_INVALID');
|
|
22
|
+
}
|
|
23
|
+
timings.push(elapsed);
|
|
24
|
+
}
|
|
25
|
+
batchP99.push(nearestRank(timings, 0.99));
|
|
26
|
+
}
|
|
27
|
+
return nearestRank(batchP99, 0.5);
|
|
28
|
+
}
|
|
29
|
+
function nearestRank(values, percentile) {
|
|
30
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
31
|
+
const index = Math.max(0, Math.ceil(percentile * sorted.length) - 1);
|
|
32
|
+
const selected = sorted[index];
|
|
33
|
+
if (selected === undefined)
|
|
34
|
+
throw new Error('SYNCPLAY_HOST_TIMING_INVALID');
|
|
35
|
+
return selected;
|
|
36
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { encodeCanonicalBytes } from '../canonical.js';
|
|
2
2
|
import { runSyncplaySynctest } from '../synctest.js';
|
|
3
|
+
import { measureRobustHostP99 } from './host-timing.js';
|
|
3
4
|
import { runDeterminismCheck } from './static-checker.js';
|
|
4
5
|
const staticFailureCodes = new Set([
|
|
5
6
|
'SYNCPLAY_MODULE_CERT_STATIC_MATH',
|
|
@@ -119,18 +120,14 @@ export async function certifyModule(component, fixture) {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
try {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
component.step(selected.slice, selected.input, selected.context);
|
|
131
|
-
timings.push(performance.now() - startedAt);
|
|
132
|
-
}
|
|
133
|
-
stepP99Ms = nearestRank(timings, 0.99);
|
|
123
|
+
stepP99Ms = measureRobustHostP99({
|
|
124
|
+
warmupSteps: fixture.profile.warmupSteps,
|
|
125
|
+
sampleSteps: fixture.profile.sampleSteps,
|
|
126
|
+
run: (index) => {
|
|
127
|
+
const selected = requireProfileCase(fixture.profile.caseAt(index));
|
|
128
|
+
component.step(selected.slice, selected.input, selected.context);
|
|
129
|
+
},
|
|
130
|
+
});
|
|
134
131
|
if (!(stepP99Ms < fixture.profile.maxStepP99Ms)) {
|
|
135
132
|
failures.push({
|
|
136
133
|
code: 'SYNCPLAY_MODULE_CERT_TIME_BUDGET',
|
|
@@ -144,7 +141,7 @@ export async function certifyModule(component, fixture) {
|
|
|
144
141
|
failures.push({
|
|
145
142
|
code: 'SYNCPLAY_MODULE_CERT_EXECUTION_ERROR',
|
|
146
143
|
check: 'profile',
|
|
147
|
-
detail: 'profile callback
|
|
144
|
+
detail: 'profile callback, module step, or host timing failed',
|
|
148
145
|
});
|
|
149
146
|
}
|
|
150
147
|
const collectGarbage = globalThis.gc;
|
|
@@ -198,11 +195,6 @@ export async function certifyModule(component, fixture) {
|
|
|
198
195
|
},
|
|
199
196
|
};
|
|
200
197
|
}
|
|
201
|
-
function nearestRank(values, percentile) {
|
|
202
|
-
const sorted = [...values].sort((left, right) => left - right);
|
|
203
|
-
const index = Math.max(0, Math.ceil(percentile * sorted.length) - 1);
|
|
204
|
-
return sorted[index] ?? 0;
|
|
205
|
-
}
|
|
206
198
|
function isStaticFailureCode(value) {
|
|
207
199
|
return staticFailureCodes.has(value);
|
|
208
200
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@series-inc/rundot-syncplay",
|
|
3
|
-
"version": "5.26.0-beta.
|
|
3
|
+
"version": "5.26.0-beta.9",
|
|
4
4
|
"description": "Kinetix orchestration, rollback, replay, late-join, and deterministic networking for RUN.game",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
161
161
|
"test": "npm run test:core && npm run test:unit-control && npm run test:entity-registry",
|
|
162
162
|
"test:module-promotion": "node --expose-gc --import tsx --test tests/module-promotion.test.ts tests/module-docs.test.ts tests/module-bundle-budget.test.ts scripts/prove-module-promotion.test.mjs",
|
|
163
|
-
"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/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/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/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",
|
|
163
|
+
"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/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
164
|
"test:soak": "tsx --test tests/runtime-soak.test.ts",
|
|
165
165
|
"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",
|
|
166
166
|
"test:ci": "npm run build && npm run test",
|
|
@@ -189,8 +189,8 @@
|
|
|
189
189
|
"test:inventory-equipment": "node --expose-gc --import tsx --test tests/inventory-equipment.test.ts tests/inventory-equipment-certification.test.ts scripts/prove-inventory-equipment.test.mjs",
|
|
190
190
|
"prove:inventory-equipment": "node --expose-gc --import tsx scripts/prove-inventory-equipment.mjs",
|
|
191
191
|
"test:platformer-2d": "node --expose-gc --import tsx --test tests/platformer-2d.test.ts tests/platformer-2d-certification.test.ts scripts/prove-platformer-2d.test.mjs",
|
|
192
|
-
"test:unit-control": "node --expose-gc --import tsx --test tests/unit-control.test.ts tests/unit-control-certification.test.ts",
|
|
193
|
-
"test:entity-registry": "node --import tsx --test tests/entity-registry.test.ts tests/entity-registry-determinism.test.ts tests/entity-registry-certification.test.ts",
|
|
192
|
+
"test:unit-control": "node --expose-gc --import tsx --test --test-concurrency=1 tests/unit-control.test.ts tests/unit-control-certification.test.ts",
|
|
193
|
+
"test:entity-registry": "node --import tsx --test --test-concurrency=1 tests/entity-registry.test.ts tests/entity-registry-determinism.test.ts tests/entity-registry-certification.test.ts",
|
|
194
194
|
"prove:unit-control": "node --expose-gc --import tsx scripts/prove-unit-control.mjs",
|
|
195
195
|
"prove:entity-registry": "node --import tsx scripts/prove-entity-registry.mjs",
|
|
196
196
|
"prove:platformer-2d": "node --expose-gc --import tsx scripts/prove-platformer-2d.mjs",
|