@series-inc/rundot-syncplay 5.26.0-beta.1 → 5.26.0-beta.2
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/core/authoring.d.ts +5 -17
- package/core/deterministic-stdlib.d.ts +0 -15
- package/core/index.d.ts +4 -33
- package/core/scripts/preflight.mjs +4 -9
- package/core/src/index.mjs +0 -3
- package/core/src/kinetix-baker.mjs +0 -92
- package/core/src/kinetix-deterministic-stdlib.mjs +0 -36
- package/core/src/kinetix-native-product.mjs +5 -3
- package/core/src/kinetix-product-contract.mjs +4 -3
- package/core/src/kinetix-project-compiler.mjs +2 -7
- package/dist/tools/index.d.ts +0 -2
- package/dist/tools/index.js +0 -1
- package/package.json +3 -4
- package/core/src/kinetix-authoring.mjs +0 -69
- package/core/src/kinetix-legacy-profile-contracts.mjs +0 -69
package/core/authoring.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
// Types-only entrypoint. The declarative `defineKinetixProject` lane it used to
|
|
2
|
+
// carry was removed with the engine-owned numeric profiles it validated; games
|
|
3
|
+
// build simulations with `createInstalledRuntimeAdapter` +
|
|
4
|
+
// `deriveCustomRuntimeIdentity` instead. Session-config schema types stay here
|
|
5
|
+
// because game runtimes declare their match configuration with them.
|
|
2
6
|
|
|
3
7
|
export type KinetixSessionField =
|
|
4
8
|
| { type: 'string'; maxBytes: number; required?: boolean }
|
|
@@ -12,19 +16,3 @@ export interface KinetixSessionConfigSchema {
|
|
|
12
16
|
maxBytes: number;
|
|
13
17
|
fields: Readonly<Record<string, KinetixSessionField>>;
|
|
14
18
|
}
|
|
15
|
-
|
|
16
|
-
export interface KinetixProjectDeclaration {
|
|
17
|
-
id: string;
|
|
18
|
-
installedProfile: KinetixInstalledProfileName;
|
|
19
|
-
tickRate: 10 | 20 | 30 | 60;
|
|
20
|
-
inputSchema: Readonly<Record<string, unknown>>;
|
|
21
|
-
sessionConfigSchema: KinetixSessionConfigSchema;
|
|
22
|
-
mechanics: readonly Readonly<Record<string, unknown>>[];
|
|
23
|
-
components: readonly Readonly<Record<string, unknown>>[];
|
|
24
|
-
systems: readonly Readonly<Record<string, unknown>>[];
|
|
25
|
-
presentationBindings: readonly Readonly<Record<string, unknown>>[];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function defineKinetixProject<const Project extends KinetixProjectDeclaration>(
|
|
29
|
-
project: Project,
|
|
30
|
-
): Readonly<Project>;
|
|
@@ -84,19 +84,6 @@ export interface DeterministicMathApi {
|
|
|
84
84
|
clamp(value: number, min: number, max: number): number;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
export interface FixedTurnsMathApi {
|
|
88
|
-
readonly ONE: number;
|
|
89
|
-
F(value: number): number;
|
|
90
|
-
mul(a: number, b: number): number;
|
|
91
|
-
div(a: number, b: number): number;
|
|
92
|
-
clamp(value: number, min: number, max: number): number;
|
|
93
|
-
fabs(value: number): number;
|
|
94
|
-
fsign(value: number): number;
|
|
95
|
-
lerp(a: number, b: number, t: number): number;
|
|
96
|
-
fsin(turns: number): number;
|
|
97
|
-
fcos(turns: number): number;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
87
|
export interface DeterministicRandomSnapshot {
|
|
101
88
|
readonly state: number;
|
|
102
89
|
}
|
|
@@ -107,8 +94,6 @@ export function createDeterministicMath(
|
|
|
107
94
|
maxFixed?: number,
|
|
108
95
|
): DeterministicMathApi;
|
|
109
96
|
|
|
110
|
-
export function createFixedTurnsMath(scale: number): FixedTurnsMathApi;
|
|
111
|
-
|
|
112
97
|
export class DeterministicRandom {
|
|
113
98
|
state: number;
|
|
114
99
|
constructor(seed: number);
|
package/core/index.d.ts
CHANGED
|
@@ -80,11 +80,9 @@ export type {
|
|
|
80
80
|
KinetixPlatformerLevelErrorCode,
|
|
81
81
|
KinetixRuntimeModuleManifestEntry,
|
|
82
82
|
} from './runtime';
|
|
83
|
-
export {
|
|
84
|
-
export type { KinetixProjectDeclaration, KinetixSessionConfigSchema } from './authoring';
|
|
83
|
+
export type { KinetixSessionConfigSchema } from './authoring';
|
|
85
84
|
export {
|
|
86
85
|
createDeterministicMath,
|
|
87
|
-
createFixedTurnsMath,
|
|
88
86
|
DeterministicRandom,
|
|
89
87
|
} from './deterministic-stdlib';
|
|
90
88
|
export {
|
|
@@ -145,15 +143,11 @@ export type {
|
|
|
145
143
|
DeterministicRandomSnapshot,
|
|
146
144
|
DeterministicVec2,
|
|
147
145
|
DeterministicVec3,
|
|
148
|
-
FixedTurnsMathApi,
|
|
149
146
|
} from './deterministic-stdlib';
|
|
150
147
|
|
|
151
|
-
export type KinetixTargetProfile = 'deterministic-f64' | 'fixed-1000-3d';
|
|
152
|
-
|
|
153
148
|
export interface KinetixCompileInput {
|
|
154
149
|
projectRoot: string;
|
|
155
150
|
expectedCommit: string;
|
|
156
|
-
targetProfile: KinetixTargetProfile;
|
|
157
151
|
}
|
|
158
152
|
|
|
159
153
|
export interface KinetixDiagnostic {
|
|
@@ -180,7 +174,7 @@ export interface KinetixCompiled {
|
|
|
180
174
|
|
|
181
175
|
export interface KinetixRuntimeContract {
|
|
182
176
|
readonly tickRate: 10 | 20 | 30 | 60;
|
|
183
|
-
readonly targetProfile:
|
|
177
|
+
readonly targetProfile: string;
|
|
184
178
|
readonly numericProfile: string;
|
|
185
179
|
readonly deterministicAbi: string;
|
|
186
180
|
readonly deterministicVersion: string;
|
|
@@ -263,7 +257,6 @@ export function compileKinetixProject(input: KinetixCompileInput): KinetixCompil
|
|
|
263
257
|
export function compileTrustedKinetixSource(input: {
|
|
264
258
|
sourceRoot: string;
|
|
265
259
|
expectedCommit: string;
|
|
266
|
-
targetProfile: KinetixTargetProfile;
|
|
267
260
|
classifyInstalledBranches?: boolean;
|
|
268
261
|
}): KinetixCompiled;
|
|
269
262
|
export function kinetixCanonicalSourceDigest(sourceRoot: string): string;
|
|
@@ -274,27 +267,6 @@ export function bakeKinetixEnvelope(
|
|
|
274
267
|
): KinetixBakeResult;
|
|
275
268
|
export type KinetixRuntimeModuleDeclaration = import('./runtime').KinetixRuntimeModuleManifestEntry;
|
|
276
269
|
|
|
277
|
-
export interface KinetixBakeOptions {
|
|
278
|
-
/**
|
|
279
|
-
* Canonical manifest of the installed runtime's implementation modules,
|
|
280
|
-
* sorted by path. Folded into the simulation payload (and so into
|
|
281
|
-
* engineIdentityHash), binding the custom step implementation to the signed
|
|
282
|
-
* product. Build with kinetixRuntimeModuleManifest.
|
|
283
|
-
*/
|
|
284
|
-
runtimeModules?: readonly KinetixRuntimeModuleDeclaration[];
|
|
285
|
-
/** Emitted as manifest.auxiliaryAssets; bound into engineIdentityHash when non-empty. */
|
|
286
|
-
auxiliaryAssets?: readonly { name: string; digest: string; byteLength: number }[];
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
export function bakeKinetixProjectDefinition(
|
|
290
|
-
project: import('./authoring').KinetixProjectDeclaration,
|
|
291
|
-
options?: KinetixBakeOptions,
|
|
292
|
-
): {
|
|
293
|
-
products: KinetixProducts;
|
|
294
|
-
runtimeIdentity: import('./runtime').KinetixRuntimeIdentity;
|
|
295
|
-
contentDigest: string;
|
|
296
|
-
};
|
|
297
|
-
|
|
298
270
|
/** Node-only build-time helper: hashes module files into a sorted canonical manifest. */
|
|
299
271
|
export function kinetixRuntimeModuleManifest(
|
|
300
272
|
sourceRoot: string,
|
|
@@ -351,8 +323,8 @@ export interface CustomRuntimeIdentityOptions {
|
|
|
351
323
|
* folding its `runtimeModules` manifest (and optional `auxiliaryAssets` /
|
|
352
324
|
* `sessionConfigSchema` / `deterministicAbi`) into `engineIdentityHash` the same
|
|
353
325
|
* canonical way `deriveKinetixRuntimeIdentity` does. The supported path for
|
|
354
|
-
* custom `createInstalledRuntimeAdapter` runtimes
|
|
355
|
-
* profile-locked
|
|
326
|
+
* custom `createInstalledRuntimeAdapter` runtimes. It replaces the removed
|
|
327
|
+
* profile-locked declarative baker.
|
|
356
328
|
*/
|
|
357
329
|
export function deriveCustomRuntimeIdentity(
|
|
358
330
|
options: CustomRuntimeIdentityOptions,
|
|
@@ -383,7 +355,6 @@ export function compileStableKinetixProducts(input: KinetixCompileInput): Kineti
|
|
|
383
355
|
export function compileStableKinetixProductsFromTrustedSource(input: {
|
|
384
356
|
sourceRoot: string;
|
|
385
357
|
expectedCommit: string;
|
|
386
|
-
targetProfile: KinetixTargetProfile;
|
|
387
358
|
}): KinetixCompilation;
|
|
388
359
|
|
|
389
360
|
/** Builds the portable C++ core and returns the binary path. */
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// `rundot pack preflight` shells out to (the compiler is JS; the CLI is .NET).
|
|
5
5
|
//
|
|
6
6
|
// Usage:
|
|
7
|
-
// node preflight.mjs --project-root <dir> --commit <sha>
|
|
7
|
+
// node preflight.mjs --project-root <dir> --commit <sha>
|
|
8
8
|
//
|
|
9
9
|
// Prints a JSON report to stdout and exits non-zero when the game does not
|
|
10
10
|
// compile, so the CLI can format it and set the exit code without re-parsing.
|
|
@@ -22,30 +22,26 @@ function parseArgs(argv) {
|
|
|
22
22
|
return args;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const PROFILES = new Set(['deterministic-f64', 'fixed-1000-3d']);
|
|
26
|
-
|
|
27
25
|
function main() {
|
|
28
26
|
const args = parseArgs(process.argv.slice(2));
|
|
29
27
|
const projectRoot = args['project-root'];
|
|
30
28
|
const commit = args.commit;
|
|
31
|
-
|
|
32
|
-
if (!projectRoot || !commit || !PROFILES.has(profile)) {
|
|
29
|
+
if (!projectRoot || !commit) {
|
|
33
30
|
process.stderr.write(
|
|
34
|
-
'KINETIX_PREFLIGHT_USAGE --project-root <dir> --commit <sha
|
|
31
|
+
'KINETIX_PREFLIGHT_USAGE --project-root <dir> --commit <sha>\n',
|
|
35
32
|
);
|
|
36
33
|
process.exit(2);
|
|
37
34
|
}
|
|
38
35
|
|
|
39
36
|
let result;
|
|
40
37
|
try {
|
|
41
|
-
result = compileKinetixProject({ projectRoot, expectedCommit: commit
|
|
38
|
+
result = compileKinetixProject({ projectRoot, expectedCommit: commit });
|
|
42
39
|
} catch (err) {
|
|
43
40
|
process.stdout.write(
|
|
44
41
|
JSON.stringify({
|
|
45
42
|
format: 'kinetix-preflight-v1',
|
|
46
43
|
ok: false,
|
|
47
44
|
commitSha: commit,
|
|
48
|
-
numericProfile: profile,
|
|
49
45
|
error: err instanceof Error ? err.message : String(err),
|
|
50
46
|
diagnostics: [],
|
|
51
47
|
unmatchedCount: 0,
|
|
@@ -66,7 +62,6 @@ function main() {
|
|
|
66
62
|
format: 'kinetix-preflight-v1',
|
|
67
63
|
ok,
|
|
68
64
|
commitSha: commit,
|
|
69
|
-
numericProfile: profile,
|
|
70
65
|
diagnostics,
|
|
71
66
|
unmatchedCount: diagnostics.length,
|
|
72
67
|
}) + '\n');
|
package/core/src/index.mjs
CHANGED
|
@@ -6,7 +6,6 @@ export { compileKinetixProject, compileTrustedKinetixSource } from './kinetix-pr
|
|
|
6
6
|
export { kinetixCanonicalSourceDigest, kinetixRuntimeModuleManifest } from './kinetix-source-snapshot.mjs';
|
|
7
7
|
export {
|
|
8
8
|
bakeKinetixEnvelope,
|
|
9
|
-
bakeKinetixProjectDefinition,
|
|
10
9
|
kinetixBakerVersion,
|
|
11
10
|
kinetixPhases,
|
|
12
11
|
scanKinetixPayloadTree,
|
|
@@ -14,13 +13,11 @@ export {
|
|
|
14
13
|
stableKinetixSourceBytes,
|
|
15
14
|
validateInstalledKinetixProfile,
|
|
16
15
|
} from './kinetix-baker.mjs';
|
|
17
|
-
export { defineKinetixProject } from './kinetix-authoring.mjs';
|
|
18
16
|
export { createKinetixWorld } from './kinetix-world-kernel.mjs';
|
|
19
17
|
export { createKinetixWorldRuntime } from './kinetix-world-runtime.mjs';
|
|
20
18
|
export { createInstalledRuntimeAdapter } from './kinetix-runtime-adapter-utils.mjs';
|
|
21
19
|
export {
|
|
22
20
|
createDeterministicMath,
|
|
23
|
-
createFixedTurnsMath,
|
|
24
21
|
DeterministicRandom,
|
|
25
22
|
} from './kinetix-deterministic-stdlib.mjs';
|
|
26
23
|
export {
|
|
@@ -10,8 +10,6 @@ import {
|
|
|
10
10
|
digestKinetixBytes,
|
|
11
11
|
stableKinetixContractBytes,
|
|
12
12
|
} from './kinetix-product-contract.mjs';
|
|
13
|
-
import { kinetixRuntimeContractForProfile } from './kinetix-legacy-profile-contracts.mjs';
|
|
14
|
-
import { normalizeKinetixRuntimeModuleManifest } from './kinetix-source-snapshot.mjs';
|
|
15
13
|
|
|
16
14
|
export const kinetixBakerVersion = 'rundot.kinetix-baker.v1';
|
|
17
15
|
|
|
@@ -517,96 +515,6 @@ export function stableKinetixSourceBytes(envelope) {
|
|
|
517
515
|
return stableKinetixEnvelopeBytes(envelope);
|
|
518
516
|
}
|
|
519
517
|
|
|
520
|
-
const bakeOptionKeys = new Set(['runtimeModules', 'auxiliaryAssets']);
|
|
521
|
-
|
|
522
|
-
export function bakeKinetixProjectDefinition(project, options = {}) {
|
|
523
|
-
if (typeof options !== 'object' || options === null || Array.isArray(options)
|
|
524
|
-
|| Object.keys(options).some((key) => !bakeOptionKeys.has(key))) {
|
|
525
|
-
throw new Error('KINETIX_BAKE_OPTIONS_INVALID');
|
|
526
|
-
}
|
|
527
|
-
const runtimeModules = normalizeKinetixRuntimeModuleManifest(options.runtimeModules);
|
|
528
|
-
// A present-but-non-array value would otherwise skip the manifest spread below
|
|
529
|
-
// and bake a product that silently declares no assets. Element-level and
|
|
530
|
-
// ordering validation stays in assertKinetixProductContract; empty stays
|
|
531
|
-
// omitted for digest stability.
|
|
532
|
-
if (options.auxiliaryAssets !== undefined && !Array.isArray(options.auxiliaryAssets)) {
|
|
533
|
-
throw new Error('KINETIX_PRODUCT_AUX_ASSETS_INVALID');
|
|
534
|
-
}
|
|
535
|
-
const auxiliaryAssets = options.auxiliaryAssets === undefined ? [] : options.auxiliaryAssets;
|
|
536
|
-
const baseRuntime = kinetixRuntimeContractForProfile(project.installedProfile);
|
|
537
|
-
const sessionSchemaDigest = digestKinetixBytes(stableKinetixContractBytes(project.sessionConfigSchema));
|
|
538
|
-
const runtime = {
|
|
539
|
-
...baseRuntime,
|
|
540
|
-
tickRate: project.tickRate,
|
|
541
|
-
inputSchema: structuredClone(project.inputSchema),
|
|
542
|
-
};
|
|
543
|
-
const simulation = {
|
|
544
|
-
format: 'rundot.kinetix-authored-simulation.v1',
|
|
545
|
-
installedProfile: project.installedProfile,
|
|
546
|
-
mechanics: structuredClone(project.mechanics),
|
|
547
|
-
components: structuredClone(project.components),
|
|
548
|
-
systems: structuredClone(project.systems),
|
|
549
|
-
// The installed runtime's implementation manifest lives in the simulation
|
|
550
|
-
// payload so engineIdentityHash covers it through simulationPayloadDigest —
|
|
551
|
-
// a changed step implementation is a different signed identity. Spread only
|
|
552
|
-
// when non-empty so module-free products keep byte-identical digests.
|
|
553
|
-
...(runtimeModules.length > 0 ? { runtimeModules } : {}),
|
|
554
|
-
};
|
|
555
|
-
const presentation = {
|
|
556
|
-
format: 'rundot.kinetix-authored-presentation.v1',
|
|
557
|
-
bindings: structuredClone(project.presentationBindings),
|
|
558
|
-
};
|
|
559
|
-
const binding = {
|
|
560
|
-
format: 'rundot.kinetix-authored-binding.v1',
|
|
561
|
-
projectId: project.id,
|
|
562
|
-
};
|
|
563
|
-
const payloads = { simulation, presentation, binding };
|
|
564
|
-
const payloadTable = ['simulation', 'presentation', 'binding'].map((name) => {
|
|
565
|
-
const bytes = stableKinetixContractBytes(payloads[name]);
|
|
566
|
-
return { name, digest: digestKinetixBytes(bytes), byteLength: bytes.length };
|
|
567
|
-
});
|
|
568
|
-
const sourceContract = {
|
|
569
|
-
format: 'rundot.kinetix-authoring-source.v1',
|
|
570
|
-
projectId: project.id,
|
|
571
|
-
installedProfile: project.installedProfile,
|
|
572
|
-
tickRate: project.tickRate,
|
|
573
|
-
inputSchema: project.inputSchema,
|
|
574
|
-
sessionConfigSchemaId: project.sessionConfigSchema.id,
|
|
575
|
-
sessionConfigSchemaDigest: sessionSchemaDigest,
|
|
576
|
-
mechanics: project.mechanics,
|
|
577
|
-
components: project.components,
|
|
578
|
-
systems: project.systems,
|
|
579
|
-
presentationBindings: project.presentationBindings,
|
|
580
|
-
...(runtimeModules.length > 0 ? { runtimeModules } : {}),
|
|
581
|
-
};
|
|
582
|
-
const products = {
|
|
583
|
-
sourceDigest: digestKinetixBytes(stableKinetixContractBytes(sourceContract)),
|
|
584
|
-
manifest: {
|
|
585
|
-
sceneId: project.id,
|
|
586
|
-
runtime,
|
|
587
|
-
sessionConfig: {
|
|
588
|
-
schemaId: project.sessionConfigSchema.id,
|
|
589
|
-
schemaDigest: sessionSchemaDigest,
|
|
590
|
-
maxBytes: project.sessionConfigSchema.maxBytes,
|
|
591
|
-
},
|
|
592
|
-
payloads: payloadTable,
|
|
593
|
-
// Content, not source: bound into engineIdentityHash by the product
|
|
594
|
-
// contract, deliberately outside sourceDigest.
|
|
595
|
-
...(Array.isArray(auxiliaryAssets) && auxiliaryAssets.length > 0
|
|
596
|
-
? { auxiliaryAssets: structuredClone(auxiliaryAssets) }
|
|
597
|
-
: {}),
|
|
598
|
-
},
|
|
599
|
-
...payloads,
|
|
600
|
-
};
|
|
601
|
-
assertKinetixProductContract(products);
|
|
602
|
-
const bytes = stableKinetixContractBytes(products);
|
|
603
|
-
return {
|
|
604
|
-
products,
|
|
605
|
-
runtimeIdentity: deriveKinetixRuntimeIdentity(products, runtime),
|
|
606
|
-
contentDigest: digestKinetixBytes(bytes),
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
|
|
610
518
|
export {
|
|
611
519
|
assertKinetixProductContract,
|
|
612
520
|
assertKinetixRuntimeIdentityMatchesProducts,
|
|
@@ -631,39 +631,3 @@ function hashLabel(label) {
|
|
|
631
631
|
}
|
|
632
632
|
return hash;
|
|
633
633
|
}
|
|
634
|
-
|
|
635
|
-
export function createFixedTurnsMath(scale) {
|
|
636
|
-
const ONE = scale;
|
|
637
|
-
const mul = (a, b) => Math.trunc((a * b) / ONE);
|
|
638
|
-
const div = (a, b) => (b === 0 ? 0 : Math.trunc((a * ONE) / b));
|
|
639
|
-
const clamp = (v, min, max) => (v < min ? min : v > max ? max : v);
|
|
640
|
-
const fabs = (v) => (v < 0 ? -v : v);
|
|
641
|
-
const fsign = (v) => (v > 0 ? 1 : v < 0 ? -1 : 0);
|
|
642
|
-
const lerp = (a, b, t) => a + mul(b - a, t);
|
|
643
|
-
|
|
644
|
-
// 5th-order odd minimax fit of sin over the first quarter turn, evaluated in
|
|
645
|
-
// fixed point. Angles are in TURNS (one revolution == ONE).
|
|
646
|
-
function sinQuarter(x) {
|
|
647
|
-
const u = div(x, ONE / 4);
|
|
648
|
-
const u2 = mul(u, u);
|
|
649
|
-
const u3 = mul(u2, u);
|
|
650
|
-
const u5 = mul(u3, u2);
|
|
651
|
-
const a1 = Math.round((1571 * ONE) / 1000);
|
|
652
|
-
const a3 = Math.round((-646 * ONE) / 1000);
|
|
653
|
-
const a5 = Math.round((80 * ONE) / 1000);
|
|
654
|
-
return mul(a1, u) + mul(a3, u3) + mul(a5, u5);
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
function fsin(turns) {
|
|
658
|
-
const t = ((turns % ONE) + ONE) % ONE;
|
|
659
|
-
if (t < ONE / 4) return clamp(sinQuarter(t), -ONE, ONE);
|
|
660
|
-
if (t < ONE / 2) return clamp(sinQuarter(ONE / 2 - t), -ONE, ONE);
|
|
661
|
-
if (t < (3 * ONE) / 4) return clamp(-sinQuarter(t - ONE / 2), -ONE, ONE);
|
|
662
|
-
return clamp(-sinQuarter(ONE - t), -ONE, ONE);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
const fcos = (turns) => fsin(turns + ONE / 4);
|
|
666
|
-
const F = (value) => Math.round(value * ONE);
|
|
667
|
-
|
|
668
|
-
return { ONE, F, mul, div, clamp, fabs, fsign, lerp, fsin, fcos };
|
|
669
|
-
}
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
scanKinetixPayloadTree,
|
|
4
4
|
stableKinetixProductBytes,
|
|
5
5
|
} from './kinetix-baker.mjs';
|
|
6
|
-
import { createLegacyFrozenKinetixProfile } from './kinetix-legacy-profile-contracts.mjs';
|
|
7
6
|
import { compileKinetixProject, compileTrustedKinetixSource } from './kinetix-project-compiler.mjs';
|
|
8
7
|
|
|
9
8
|
function compileProducts(input, compile) {
|
|
@@ -11,8 +10,11 @@ function compileProducts(input, compile) {
|
|
|
11
10
|
if (compiled.diagnostics.length > 0 || compiled.envelope === null) {
|
|
12
11
|
throw new Error(`KINETIX_PROJECT_INELIGIBLE ${JSON.stringify(compiled.diagnostics)}`);
|
|
13
12
|
}
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
// Unreachable while the trusted-source compiler is frozen: it always returns a
|
|
14
|
+
// diagnostic, so the throw above fires first. Kept so the shape stays honest
|
|
15
|
+
// for whoever reopens the signed path behind server-side product verification,
|
|
16
|
+
// which will supply real installed profiles here.
|
|
17
|
+
const installedProfiles = new Map();
|
|
16
18
|
const baked = bakeKinetixEnvelope(compiled.envelope, installedProfiles);
|
|
17
19
|
if (!baked.valid) throw new Error(`KINETIX_BAKE_FAILED ${JSON.stringify(baked.diagnostics)}`);
|
|
18
20
|
return { compiled, products: baked.products, runtimeIdentity: baked.runtimeIdentity };
|
|
@@ -170,9 +170,10 @@ function normalizeCustomAuxiliaryAssets(auxiliaryAssets) {
|
|
|
170
170
|
// Derives a source-bound KinetixRuntimeIdentity for a fully-custom installed
|
|
171
171
|
// runtime (createInstalledRuntimeAdapter), folding its runtimeModules manifest
|
|
172
172
|
// — and, when provided, auxiliaryAssets / sessionConfigSchema / deterministicAbi
|
|
173
|
-
// — into engineIdentityHash the same canonical way the
|
|
174
|
-
// This is the supported
|
|
175
|
-
//
|
|
173
|
+
// — into engineIdentityHash the same canonical way the envelope baker does.
|
|
174
|
+
// This is the supported path for game-owned runtimes. It replaced the removed
|
|
175
|
+
// profile-locked declarative lane, which could only bake the two engine-owned
|
|
176
|
+
// numeric profiles that no longer exist.
|
|
176
177
|
export function deriveCustomRuntimeIdentity(options) {
|
|
177
178
|
if (!isPlainObject(options)
|
|
178
179
|
|| Object.keys(options).some((key) => !customRuntimeIdentityOptionKeys.has(key))
|
|
@@ -28,10 +28,9 @@ function treeFingerprint(projectRoot) {
|
|
|
28
28
|
export function compileKinetixProject({
|
|
29
29
|
projectRoot,
|
|
30
30
|
expectedCommit,
|
|
31
|
-
targetProfile,
|
|
32
31
|
classifyInstalledBranches = true,
|
|
33
32
|
}) {
|
|
34
|
-
if (typeof projectRoot !== 'string' || typeof expectedCommit !== 'string'
|
|
33
|
+
if (typeof projectRoot !== 'string' || typeof expectedCommit !== 'string') {
|
|
35
34
|
throw new Error('KINETIX_PROJECT_OPTIONS_INVALID');
|
|
36
35
|
}
|
|
37
36
|
const beforeFingerprint = treeFingerprint(projectRoot);
|
|
@@ -64,7 +63,6 @@ export function compileKinetixProject({
|
|
|
64
63
|
const result = compileTrustedKinetixSource({
|
|
65
64
|
sourceRoot: snapshotRoot,
|
|
66
65
|
expectedCommit,
|
|
67
|
-
targetProfile,
|
|
68
66
|
classifyInstalledBranches,
|
|
69
67
|
});
|
|
70
68
|
if (treeFingerprint(projectRoot) !== beforeFingerprint) {
|
|
@@ -79,11 +77,8 @@ export function compileKinetixProject({
|
|
|
79
77
|
export function compileTrustedKinetixSource({
|
|
80
78
|
sourceRoot,
|
|
81
79
|
expectedCommit,
|
|
82
|
-
targetProfile,
|
|
83
80
|
}) {
|
|
84
|
-
if (typeof sourceRoot !== 'string'
|
|
85
|
-
|| typeof expectedCommit !== 'string'
|
|
86
|
-
|| typeof targetProfile !== 'string') {
|
|
81
|
+
if (typeof sourceRoot !== 'string' || typeof expectedCommit !== 'string') {
|
|
87
82
|
throw new Error('KINETIX_TRUSTED_SOURCE_OPTIONS_INVALID');
|
|
88
83
|
}
|
|
89
84
|
const sourceDigest = kinetixCanonicalSourceDigest(sourceRoot);
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export { defineKinetixProject } from '@series-inc/rundot-syncplay/core/authoring';
|
|
2
|
-
export type { KinetixProjectDeclaration } from '@series-inc/rundot-syncplay/core/authoring';
|
|
3
1
|
export { checkDeterminism, checkSourceText, collectDeterminismSuppressions, type DeterminismCheckMode, type DeterminismCheckOptions, type DeterminismDiagnostic, type DeterminismParserKind, type DeterminismSeverity, type DeterminismSuppression, } from './static-checker.js';
|
|
4
2
|
export { runSyncplaySynctest, type SyncplaySynctestConfig, type SyncplaySynctestDivergence, type SyncplaySynctestInputFuzz, type SyncplaySynctestPhase, type SyncplaySynctestResult, } from '../synctest.js';
|
|
5
3
|
export { rundotSyncplayPlugin, createSyncplayViteConfig, withSyncplayViteConfig, formatDiagnostics, type SyncplayPluginOptions, type MinimalVitePlugin, type MinimalVitePluginApply, type MinimalVitePluginEnforce, type MinimalVitePluginEnvironment, type MinimalViteResolvedConfig, type MinimalViteConfig, } from './vite-plugin.js';
|
package/dist/tools/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { defineKinetixProject } from '@series-inc/rundot-syncplay/core/authoring';
|
|
2
1
|
export { checkDeterminism, checkSourceText, collectDeterminismSuppressions, } from './static-checker.js';
|
|
3
2
|
export { runSyncplaySynctest, } from '../synctest.js';
|
|
4
3
|
// Determinism-lint Vite plugin (relocated from the game SDK's syncplay/tools).
|
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.2",
|
|
4
4
|
"description": "Kinetix orchestration, rollback, replay, late-join, and deterministic networking for RUN.game",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,8 +51,7 @@
|
|
|
51
51
|
"default": "./core/src/runtime.mjs"
|
|
52
52
|
},
|
|
53
53
|
"./core/authoring": {
|
|
54
|
-
"types": "./core/authoring.d.ts"
|
|
55
|
-
"default": "./core/src/kinetix-authoring.mjs"
|
|
54
|
+
"types": "./core/authoring.d.ts"
|
|
56
55
|
},
|
|
57
56
|
"./core/compiler": "./core/src/kinetix-project-compiler.mjs",
|
|
58
57
|
"./core/baker": "./core/src/kinetix-baker.mjs",
|
|
@@ -89,7 +88,7 @@
|
|
|
89
88
|
"scripts": {
|
|
90
89
|
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
|
|
91
90
|
"test": "npm run test:core",
|
|
92
|
-
"test:core": "tsx --test tests/canonical.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/test-manifest.test.ts tests/coarse-region.test.ts tests/input-codec.test.ts tests/platformer-content.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",
|
|
91
|
+
"test:core": "tsx --test tests/canonical.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/test-manifest.test.ts tests/coarse-region.test.ts tests/input-codec.test.ts tests/platformer-content.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",
|
|
93
92
|
"test:soak": "tsx --test tests/runtime-soak.test.ts",
|
|
94
93
|
"test:extended": "tsx --test 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-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-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",
|
|
95
94
|
"test:ci": "npm run build && npm run test:core",
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { normalizeKinetixSessionConfigSchema } from './kinetix-session-config.mjs';
|
|
2
|
-
|
|
3
|
-
const projectKeys = new Set([
|
|
4
|
-
'id',
|
|
5
|
-
'installedProfile',
|
|
6
|
-
'tickRate',
|
|
7
|
-
'inputSchema',
|
|
8
|
-
'sessionConfigSchema',
|
|
9
|
-
'mechanics',
|
|
10
|
-
'components',
|
|
11
|
-
'systems',
|
|
12
|
-
'presentationBindings',
|
|
13
|
-
]);
|
|
14
|
-
|
|
15
|
-
function fail(code) {
|
|
16
|
-
throw new Error(code);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function isPlainObject(value) {
|
|
20
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
21
|
-
&& Object.getPrototypeOf(value) === Object.prototype;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function canonicalData(value) {
|
|
25
|
-
if (Array.isArray(value)) return value.map(canonicalData);
|
|
26
|
-
if (isPlainObject(value)) {
|
|
27
|
-
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, canonicalData(value[key])]));
|
|
28
|
-
}
|
|
29
|
-
if (value === null || typeof value === 'string' || typeof value === 'boolean'
|
|
30
|
-
|| (typeof value === 'number' && Number.isFinite(value) && Number.isSafeInteger(value))) {
|
|
31
|
-
return value;
|
|
32
|
-
}
|
|
33
|
-
fail('KINETIX_AUTHORING_VALUE_INVALID');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function deepFreeze(value) {
|
|
37
|
-
if (value && typeof value === 'object') {
|
|
38
|
-
Object.freeze(value);
|
|
39
|
-
Object.values(value).forEach(deepFreeze);
|
|
40
|
-
}
|
|
41
|
-
return value;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function defineKinetixProject(input) {
|
|
45
|
-
if (!isPlainObject(input)) fail('KINETIX_AUTHORING_INVALID');
|
|
46
|
-
if (Object.keys(input).some((key) => !projectKeys.has(key))) fail('KINETIX_AUTHORING_FIELD_UNKNOWN');
|
|
47
|
-
if (typeof input.id !== 'string' || input.id.length === 0
|
|
48
|
-
|| !['deterministic-f64', 'fixed-1000-3d'].includes(input.installedProfile)
|
|
49
|
-
|| ![10, 20, 30, 60].includes(input.tickRate)
|
|
50
|
-
|| !isPlainObject(input.inputSchema)
|
|
51
|
-
|| !Array.isArray(input.mechanics)
|
|
52
|
-
|| !Array.isArray(input.components)
|
|
53
|
-
|| !Array.isArray(input.systems)
|
|
54
|
-
|| !Array.isArray(input.presentationBindings)) {
|
|
55
|
-
fail('KINETIX_AUTHORING_INVALID');
|
|
56
|
-
}
|
|
57
|
-
const project = canonicalData({
|
|
58
|
-
id: input.id,
|
|
59
|
-
installedProfile: input.installedProfile,
|
|
60
|
-
tickRate: input.tickRate,
|
|
61
|
-
inputSchema: input.inputSchema,
|
|
62
|
-
sessionConfigSchema: normalizeKinetixSessionConfigSchema(input.sessionConfigSchema),
|
|
63
|
-
mechanics: input.mechanics,
|
|
64
|
-
components: input.components,
|
|
65
|
-
systems: input.systems,
|
|
66
|
-
presentationBindings: input.presentationBindings,
|
|
67
|
-
});
|
|
68
|
-
return deepFreeze(project);
|
|
69
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
const profileContracts = Object.freeze({
|
|
2
|
-
'fixed-1000-3d': Object.freeze({
|
|
3
|
-
tickRate: 30,
|
|
4
|
-
targetProfile: 'fixed-1000-3d',
|
|
5
|
-
numericProfile: 'fixed-1000',
|
|
6
|
-
deterministicAbi: 'fixed-1000-3d.v1',
|
|
7
|
-
deterministicVersion: 'rundot.kinetix.fixed-1000-runtime.v1',
|
|
8
|
-
inputSchema: Object.freeze({
|
|
9
|
-
version: 'rundot.kinetix-input-schema.v1',
|
|
10
|
-
slots: 'bounded-array',
|
|
11
|
-
fields: Object.freeze([
|
|
12
|
-
Object.freeze({ name: 'steerX', type: 'fixed-1000' }),
|
|
13
|
-
Object.freeze({ name: 'throttle', type: 'fixed-1000' }),
|
|
14
|
-
Object.freeze({ name: 'handbrake', type: 'boolean' }),
|
|
15
|
-
]),
|
|
16
|
-
}),
|
|
17
|
-
stateSchema: Object.freeze({
|
|
18
|
-
version: 'rundot.kinetix-state-schema.v1',
|
|
19
|
-
checkpoint: 'opaque-canonical-bytes',
|
|
20
|
-
numericProfile: 'fixed-1000',
|
|
21
|
-
componentRoot: 'KinetixRuntimeState',
|
|
22
|
-
}),
|
|
23
|
-
}),
|
|
24
|
-
'deterministic-f64': Object.freeze({
|
|
25
|
-
tickRate: 30,
|
|
26
|
-
targetProfile: 'deterministic-f64',
|
|
27
|
-
numericProfile: 'deterministic-f64',
|
|
28
|
-
deterministicAbi: 'deterministic-f64-2d.v1',
|
|
29
|
-
deterministicVersion: 'rundot.kinetix.deterministic-f64-runtime.v1',
|
|
30
|
-
inputSchema: Object.freeze({
|
|
31
|
-
version: 'rundot.kinetix-input-schema.v1',
|
|
32
|
-
slots: 'bounded-array',
|
|
33
|
-
fields: Object.freeze([
|
|
34
|
-
Object.freeze({ name: 'movementVector', type: 'f64x2' }),
|
|
35
|
-
Object.freeze({ name: 'aimVector', type: 'f64x2' }),
|
|
36
|
-
Object.freeze({ name: 'fireDown', type: 'boolean' }),
|
|
37
|
-
Object.freeze({ name: 'bombPressed', type: 'boolean' }),
|
|
38
|
-
]),
|
|
39
|
-
}),
|
|
40
|
-
stateSchema: Object.freeze({
|
|
41
|
-
version: 'rundot.kinetix-state-schema.v1',
|
|
42
|
-
checkpoint: 'opaque-canonical-bytes',
|
|
43
|
-
numericProfile: 'deterministic-f64',
|
|
44
|
-
componentRoot: 'KinetixRuntimeState',
|
|
45
|
-
}),
|
|
46
|
-
}),
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
export function kinetixRuntimeContractForProfile(numericProfile) {
|
|
50
|
-
const contract = profileContracts[numericProfile];
|
|
51
|
-
if (contract === undefined) throw new Error('KINETIX_NUMERIC_PROFILE_UNSUPPORTED');
|
|
52
|
-
return contract;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function createLegacyFrozenKinetixProfile(numericProfile) {
|
|
56
|
-
const runtimeContract = kinetixRuntimeContractForProfile(numericProfile);
|
|
57
|
-
return {
|
|
58
|
-
abi: runtimeContract.deterministicAbi,
|
|
59
|
-
runtimeContract,
|
|
60
|
-
componentSchemas: new Map([
|
|
61
|
-
['KinetixRuntimeState', { currentSchemaVersion: 1, domains: ['simulation'], validate: () => [] }],
|
|
62
|
-
['KinetixRenderRecord', { currentSchemaVersion: 1, domains: ['presentation'], validate: () => [] }],
|
|
63
|
-
['KinetixRenderBinding', { currentSchemaVersion: 1, domains: ['binding'], validate: () => [] }],
|
|
64
|
-
]),
|
|
65
|
-
systems: [],
|
|
66
|
-
bakeEntity: () => ({ simulation: [], presentation: [], binding: [] }),
|
|
67
|
-
bakePrefab: () => ({ simulation: [], presentation: [], binding: [] }),
|
|
68
|
-
};
|
|
69
|
-
}
|