@series-inc/rundot-syncplay 5.23.0-beta.7
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 +610 -0
- package/dist/animation.d.ts +119 -0
- package/dist/animation.js +488 -0
- package/dist/asset-cooking.d.ts +82 -0
- package/dist/asset-cooking.js +364 -0
- package/dist/authority-room.d.ts +217 -0
- package/dist/authority-room.js +701 -0
- package/dist/bot-documents.d.ts +115 -0
- package/dist/bot-documents.js +549 -0
- package/dist/bot-primitives.d.ts +152 -0
- package/dist/bot-primitives.js +472 -0
- package/dist/browser.d.ts +57 -0
- package/dist/browser.js +53 -0
- package/dist/canonical.d.ts +12 -0
- package/dist/canonical.js +288 -0
- package/dist/certification.d.ts +455 -0
- package/dist/certification.js +1855 -0
- package/dist/chaos-certification.d.ts +56 -0
- package/dist/chaos-certification.js +1397 -0
- package/dist/cjs/animation.js +495 -0
- package/dist/cjs/asset-cooking.js +372 -0
- package/dist/cjs/authority-room.js +704 -0
- package/dist/cjs/bot-documents.js +556 -0
- package/dist/cjs/bot-primitives.js +482 -0
- package/dist/cjs/browser.js +96 -0
- package/dist/cjs/canonical.js +299 -0
- package/dist/cjs/certification.js +1860 -0
- package/dist/cjs/chaos-certification.js +1402 -0
- package/dist/cjs/collections.js +36 -0
- package/dist/cjs/collision.js +96 -0
- package/dist/cjs/commands.js +987 -0
- package/dist/cjs/config-bundle.js +491 -0
- package/dist/cjs/creator.js +30 -0
- package/dist/cjs/deterministic-primitives.js +134 -0
- package/dist/cjs/deterministic-session.js +1410 -0
- package/dist/cjs/ecs-lite.js +155 -0
- package/dist/cjs/effects-adapter.js +97 -0
- package/dist/cjs/engine-complete.js +2297 -0
- package/dist/cjs/engine-core-proof.js +70 -0
- package/dist/cjs/engine-parity-matrix.js +874 -0
- package/dist/cjs/engine-parity.js +3183 -0
- package/dist/cjs/engine-runtime.js +3141 -0
- package/dist/cjs/errors.js +22 -0
- package/dist/cjs/events.js +247 -0
- package/dist/cjs/examples/counter-duel.js +157 -0
- package/dist/cjs/examples/fixed-arena.js +368 -0
- package/dist/cjs/index.js +229 -0
- package/dist/cjs/input-authority.js +150 -0
- package/dist/cjs/input-button.js +256 -0
- package/dist/cjs/input-timeline.js +418 -0
- package/dist/cjs/instant-replay.js +66 -0
- package/dist/cjs/lag-compensation3d.js +837 -0
- package/dist/cjs/local-authority-server-runner.js +1121 -0
- package/dist/cjs/local-authority-wire.js +1497 -0
- package/dist/cjs/local-authority.js +817 -0
- package/dist/cjs/local-ws-adapter.js +150 -0
- package/dist/cjs/match-log.js +61 -0
- package/dist/cjs/math.js +157 -0
- package/dist/cjs/mock-network-adapter.js +26 -0
- package/dist/cjs/movement.js +703 -0
- package/dist/cjs/movement3d.js +1246 -0
- package/dist/cjs/multiclient-authority.js +308 -0
- package/dist/cjs/multiplayer-service.js +434 -0
- package/dist/cjs/navigation.js +347 -0
- package/dist/cjs/navmesh.js +1437 -0
- package/dist/cjs/networked-client.js +691 -0
- package/dist/cjs/networked-input-authority.js +443 -0
- package/dist/cjs/node.js +47 -0
- package/dist/cjs/number-lab.js +78 -0
- package/dist/cjs/offline-session.js +370 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/pause-resume.js +43 -0
- package/dist/cjs/physics-cert.js +426 -0
- package/dist/cjs/physics2d.js +751 -0
- package/dist/cjs/physics3d.js +1996 -0
- package/dist/cjs/prediction-culling.js +203 -0
- package/dist/cjs/protocol-lab.js +1299 -0
- package/dist/cjs/r3f-render-adapter.js +209 -0
- package/dist/cjs/random.js +59 -0
- package/dist/cjs/replay-bundle.js +1598 -0
- package/dist/cjs/replay-inspect.js +236 -0
- package/dist/cjs/replay-scoring.js +68 -0
- package/dist/cjs/replay.js +356 -0
- package/dist/cjs/reports.js +181 -0
- package/dist/cjs/rollback-history.js +251 -0
- package/dist/cjs/runtime-guards.js +111 -0
- package/dist/cjs/sample-lab.js +145 -0
- package/dist/cjs/sample-scenes.js +752 -0
- package/dist/cjs/schema-artifacts.js +359 -0
- package/dist/cjs/schema-codegen.js +544 -0
- package/dist/cjs/schema-dsl-binary.js +655 -0
- package/dist/cjs/schema.js +319 -0
- package/dist/cjs/sdk-offline.js +99 -0
- package/dist/cjs/session-promotion.js +74 -0
- package/dist/cjs/session-snapshot.js +265 -0
- package/dist/cjs/session-wire.js +216 -0
- package/dist/cjs/signals.js +103 -0
- package/dist/cjs/sparse-set.js +169 -0
- package/dist/cjs/synctest.js +136 -0
- package/dist/cjs/system-lifecycle.js +286 -0
- package/dist/cjs/timers.js +61 -0
- package/dist/cjs/tools/static-checker.js +1468 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/venus-mp-budget.js +125 -0
- package/dist/cjs/ws-frame.js +255 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +11379 -0
- package/dist/collections.d.ts +9 -0
- package/dist/collections.js +30 -0
- package/dist/collision.d.ts +25 -0
- package/dist/collision.js +85 -0
- package/dist/commands.d.ts +137 -0
- package/dist/commands.js +977 -0
- package/dist/config-bundle.d.ts +89 -0
- package/dist/config-bundle.js +483 -0
- package/dist/creator.d.ts +20 -0
- package/dist/creator.js +11 -0
- package/dist/deterministic-primitives.d.ts +59 -0
- package/dist/deterministic-primitives.js +126 -0
- package/dist/deterministic-session.d.ts +197 -0
- package/dist/deterministic-session.js +1401 -0
- package/dist/ecs-lite.d.ts +26 -0
- package/dist/ecs-lite.js +152 -0
- package/dist/effects-adapter.d.ts +21 -0
- package/dist/effects-adapter.js +94 -0
- package/dist/engine-complete.d.ts +45 -0
- package/dist/engine-complete.js +2292 -0
- package/dist/engine-core-proof.d.ts +26 -0
- package/dist/engine-core-proof.js +66 -0
- package/dist/engine-parity-matrix.d.ts +300 -0
- package/dist/engine-parity-matrix.js +869 -0
- package/dist/engine-parity.d.ts +74 -0
- package/dist/engine-parity.js +3178 -0
- package/dist/engine-runtime.d.ts +351 -0
- package/dist/engine-runtime.js +3123 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +17 -0
- package/dist/events.d.ts +45 -0
- package/dist/events.js +242 -0
- package/dist/examples/counter-duel.d.ts +43 -0
- package/dist/examples/counter-duel.js +150 -0
- package/dist/examples/fixed-arena.d.ts +110 -0
- package/dist/examples/fixed-arena.js +356 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +66 -0
- package/dist/input-authority.d.ts +82 -0
- package/dist/input-authority.js +147 -0
- package/dist/input-button.d.ts +36 -0
- package/dist/input-button.js +249 -0
- package/dist/input-timeline.d.ts +65 -0
- package/dist/input-timeline.js +412 -0
- package/dist/instant-replay.d.ts +59 -0
- package/dist/instant-replay.js +62 -0
- package/dist/lag-compensation3d.d.ts +146 -0
- package/dist/lag-compensation3d.js +827 -0
- package/dist/local-authority-server-runner.d.ts +79 -0
- package/dist/local-authority-server-runner.js +1118 -0
- package/dist/local-authority-wire.d.ts +142 -0
- package/dist/local-authority-wire.js +1477 -0
- package/dist/local-authority.d.ts +160 -0
- package/dist/local-authority.js +810 -0
- package/dist/local-ws-adapter.d.ts +14 -0
- package/dist/local-ws-adapter.js +146 -0
- package/dist/match-log.d.ts +74 -0
- package/dist/match-log.js +57 -0
- package/dist/math.d.ts +41 -0
- package/dist/math.js +154 -0
- package/dist/mock-network-adapter.d.ts +13 -0
- package/dist/mock-network-adapter.js +23 -0
- package/dist/movement.d.ts +155 -0
- package/dist/movement.js +694 -0
- package/dist/movement3d.d.ts +195 -0
- package/dist/movement3d.js +1238 -0
- package/dist/multiclient-authority.d.ts +30 -0
- package/dist/multiclient-authority.js +305 -0
- package/dist/multiplayer-service.d.ts +153 -0
- package/dist/multiplayer-service.js +429 -0
- package/dist/navigation.d.ts +91 -0
- package/dist/navigation.js +338 -0
- package/dist/navmesh.d.ts +236 -0
- package/dist/navmesh.js +1423 -0
- package/dist/networked-client.d.ts +124 -0
- package/dist/networked-client.js +688 -0
- package/dist/networked-input-authority.d.ts +51 -0
- package/dist/networked-input-authority.js +439 -0
- package/dist/node.d.ts +22 -0
- package/dist/node.js +19 -0
- package/dist/number-lab.d.ts +15 -0
- package/dist/number-lab.js +75 -0
- package/dist/offline-session.d.ts +2 -0
- package/dist/offline-session.js +367 -0
- package/dist/pause-resume.d.ts +48 -0
- package/dist/pause-resume.js +40 -0
- package/dist/physics-cert.d.ts +40 -0
- package/dist/physics-cert.js +389 -0
- package/dist/physics2d.d.ts +142 -0
- package/dist/physics2d.js +741 -0
- package/dist/physics3d.d.ts +285 -0
- package/dist/physics3d.js +1979 -0
- package/dist/prediction-culling.d.ts +34 -0
- package/dist/prediction-culling.js +200 -0
- package/dist/protocol-lab.d.ts +115 -0
- package/dist/protocol-lab.js +1295 -0
- package/dist/r3f-render-adapter.d.ts +61 -0
- package/dist/r3f-render-adapter.js +203 -0
- package/dist/random.d.ts +13 -0
- package/dist/random.js +55 -0
- package/dist/replay-bundle.d.ts +150 -0
- package/dist/replay-bundle.js +1586 -0
- package/dist/replay-inspect.d.ts +60 -0
- package/dist/replay-inspect.js +228 -0
- package/dist/replay-scoring.d.ts +28 -0
- package/dist/replay-scoring.js +65 -0
- package/dist/replay.d.ts +17 -0
- package/dist/replay.js +351 -0
- package/dist/reports.d.ts +176 -0
- package/dist/reports.js +174 -0
- package/dist/rollback-history.d.ts +46 -0
- package/dist/rollback-history.js +247 -0
- package/dist/runtime-guards.d.ts +1 -0
- package/dist/runtime-guards.js +108 -0
- package/dist/sample-lab.d.ts +32 -0
- package/dist/sample-lab.js +134 -0
- package/dist/sample-scenes.d.ts +86 -0
- package/dist/sample-scenes.js +745 -0
- package/dist/schema-artifacts.d.ts +49 -0
- package/dist/schema-artifacts.js +356 -0
- package/dist/schema-codegen.d.ts +21 -0
- package/dist/schema-codegen.js +538 -0
- package/dist/schema-dsl-binary.d.ts +75 -0
- package/dist/schema-dsl-binary.js +648 -0
- package/dist/schema.d.ts +87 -0
- package/dist/schema.js +315 -0
- package/dist/sdk-offline.d.ts +34 -0
- package/dist/sdk-offline.js +96 -0
- package/dist/session-promotion.d.ts +26 -0
- package/dist/session-promotion.js +70 -0
- package/dist/session-snapshot.d.ts +95 -0
- package/dist/session-snapshot.js +255 -0
- package/dist/session-wire.d.ts +89 -0
- package/dist/session-wire.js +211 -0
- package/dist/signals.d.ts +31 -0
- package/dist/signals.js +99 -0
- package/dist/sparse-set.d.ts +34 -0
- package/dist/sparse-set.js +165 -0
- package/dist/synctest.d.ts +31 -0
- package/dist/synctest.js +133 -0
- package/dist/system-lifecycle.d.ts +72 -0
- package/dist/system-lifecycle.js +281 -0
- package/dist/timers.d.ts +17 -0
- package/dist/timers.js +53 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/static-checker.d.ts +31 -0
- package/dist/tools/static-checker.js +1462 -0
- package/dist/tools/vite-plugin.d.ts +69 -0
- package/dist/tools/vite-plugin.js +203 -0
- package/dist/types.d.ts +148 -0
- package/dist/types.js +1 -0
- package/dist/venus-mp-budget.d.ts +29 -0
- package/dist/venus-mp-budget.js +122 -0
- package/dist/ws-frame.d.ts +49 -0
- package/dist/ws-frame.js +247 -0
- package/package.json +169 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSchemaArtifacts = createSchemaArtifacts;
|
|
4
|
+
const canonical_js_1 = require("./canonical.js");
|
|
5
|
+
const errors_js_1 = require("./errors.js");
|
|
6
|
+
const schema_js_1 = require("./schema.js");
|
|
7
|
+
function createSchemaArtifacts(descriptor) {
|
|
8
|
+
const validated = (0, schema_js_1.defineSyncplayGame)(descriptor);
|
|
9
|
+
const canonicalizeInput = (input) => {
|
|
10
|
+
return canonicalizeDeclaredFields('input', input, validated.input.fields, validated.maxPlayers);
|
|
11
|
+
};
|
|
12
|
+
const canonicalizeState = (state) => {
|
|
13
|
+
return canonicalizeDeclaredFields('state', state, validated.state.fields, validated.maxPlayers, (field) => 'checksummed' in field && field.checksummed);
|
|
14
|
+
};
|
|
15
|
+
const canonicalizeFullState = (state) => {
|
|
16
|
+
return canonicalizeDeclaredFields('state', state, validated.state.fields, validated.maxPlayers);
|
|
17
|
+
};
|
|
18
|
+
const commandChannels = new Map((validated.commandChannels ?? []).map((channel) => [channel.id, channel]));
|
|
19
|
+
const canonicalizeCommand = (channelId, command) => {
|
|
20
|
+
const channel = commandChannels.get(channelId);
|
|
21
|
+
if (channel === undefined) {
|
|
22
|
+
throw new errors_js_1.SyncplayError(`Command channel ${channelId} is not declared in schema`, {
|
|
23
|
+
code: 'schema-artifacts.command-channel',
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return canonicalizeDeclaredFields(`command.${channelId}`, command, channel.fields, validated.maxPlayers);
|
|
27
|
+
};
|
|
28
|
+
const canonicalizeReplayMetadata = (metadata) => {
|
|
29
|
+
return canonicalizeLabeledRecord('replayMetadata', metadata);
|
|
30
|
+
};
|
|
31
|
+
const canonicalizeAssetManifest = (manifest) => {
|
|
32
|
+
return canonicalizeLabeledRecord('assetManifest', manifest);
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
descriptor: validated,
|
|
36
|
+
canonicalizeInput,
|
|
37
|
+
canonicalizeCommand,
|
|
38
|
+
canonicalizeState,
|
|
39
|
+
canonicalizeFullState,
|
|
40
|
+
canonicalizeReplayMetadata,
|
|
41
|
+
canonicalizeAssetManifest,
|
|
42
|
+
serializeInput(input) {
|
|
43
|
+
return serializeCanonicalValue(canonicalizeInput(input));
|
|
44
|
+
},
|
|
45
|
+
deserializeInput(bytes) {
|
|
46
|
+
return canonicalizeInput(deserializeCanonicalValue('input', bytes));
|
|
47
|
+
},
|
|
48
|
+
serializeCommand(channelId, command) {
|
|
49
|
+
return serializeCanonicalValue(canonicalizeCommand(channelId, command));
|
|
50
|
+
},
|
|
51
|
+
deserializeCommand(channelId, bytes) {
|
|
52
|
+
return canonicalizeCommand(channelId, deserializeCanonicalValue(`command.${channelId}`, bytes));
|
|
53
|
+
},
|
|
54
|
+
serializeState(state) {
|
|
55
|
+
return serializeCanonicalValue(canonicalizeState(state));
|
|
56
|
+
},
|
|
57
|
+
deserializeState(bytes) {
|
|
58
|
+
return canonicalizeState(deserializeCanonicalValue('state', bytes));
|
|
59
|
+
},
|
|
60
|
+
serializeFullState(state) {
|
|
61
|
+
return serializeCanonicalValue(canonicalizeFullState(state));
|
|
62
|
+
},
|
|
63
|
+
deserializeFullState(bytes) {
|
|
64
|
+
return canonicalizeFullState(deserializeCanonicalValue('state', bytes));
|
|
65
|
+
},
|
|
66
|
+
serializeReplayMetadata(metadata) {
|
|
67
|
+
return serializeCanonicalValue(canonicalizeReplayMetadata(metadata));
|
|
68
|
+
},
|
|
69
|
+
deserializeReplayMetadata(bytes) {
|
|
70
|
+
return canonicalizeReplayMetadata(deserializeCanonicalValue('replayMetadata', bytes));
|
|
71
|
+
},
|
|
72
|
+
serializeAssetManifest(manifest) {
|
|
73
|
+
return serializeCanonicalValue(canonicalizeAssetManifest(manifest));
|
|
74
|
+
},
|
|
75
|
+
deserializeAssetManifest(bytes) {
|
|
76
|
+
return canonicalizeAssetManifest(deserializeCanonicalValue('assetManifest', bytes));
|
|
77
|
+
},
|
|
78
|
+
checksumState(state) {
|
|
79
|
+
return (0, canonical_js_1.defaultChecksum)(canonicalizeState(state));
|
|
80
|
+
},
|
|
81
|
+
dumpFrame(config) {
|
|
82
|
+
if (!Number.isInteger(config.frame) || config.frame < 0) {
|
|
83
|
+
throw new errors_js_1.SyncplayError('Frame dump requires a non-negative integer frame', {
|
|
84
|
+
code: 'schema-artifacts.frame',
|
|
85
|
+
frame: config.frame,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const state = canonicalizeState(config.state);
|
|
89
|
+
const inputFrame = config.inputFrame === undefined
|
|
90
|
+
? undefined
|
|
91
|
+
: canonicalizeRecordedInputFrame(config.inputFrame, canonicalizeInput, validated.maxPlayers);
|
|
92
|
+
return {
|
|
93
|
+
frame: config.frame,
|
|
94
|
+
artifactId: validated.artifactId,
|
|
95
|
+
inputSchemaId: validated.input.id,
|
|
96
|
+
stateSchemaId: validated.state.id,
|
|
97
|
+
checksum: (0, canonical_js_1.defaultChecksum)(state),
|
|
98
|
+
state,
|
|
99
|
+
...(inputFrame === undefined ? {} : { inputFrame }),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const serializeCanonicalValue = (value) => (0, canonical_js_1.encodeUtf8)(JSON.stringify(value));
|
|
105
|
+
function deserializeCanonicalValue(label, bytes) {
|
|
106
|
+
try {
|
|
107
|
+
const source = (0, canonical_js_1.decodeUtf8)(bytes);
|
|
108
|
+
return JSON.parse(source);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
throw new errors_js_1.SyncplayError(`${label} bytes are not valid schema JSON`, {
|
|
112
|
+
code: 'schema-artifacts.deserialize',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function canonicalizeDeclaredFields(label, value, fields, maxPlayers, includeField = () => true) {
|
|
117
|
+
const record = requireRecord(label, value);
|
|
118
|
+
const output = {};
|
|
119
|
+
const declaredFields = new Map(fields.map((field) => [field.name, field]));
|
|
120
|
+
for (const field of fields) {
|
|
121
|
+
if (!Object.prototype.hasOwnProperty.call(record, field.name)) {
|
|
122
|
+
if ('defaultValue' in field) {
|
|
123
|
+
if (includeField(field)) {
|
|
124
|
+
output[field.name] = (0, canonical_js_1.canonicalize)(validateFieldValue(`${label}.${field.name}`, field, field.defaultValue, maxPlayers));
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (includeField(field)) {
|
|
129
|
+
throw new errors_js_1.SyncplayError(`${label}.${field.name} is missing`, {
|
|
130
|
+
code: 'schema-artifacts.missing-field',
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const canonicalValue = (0, canonical_js_1.canonicalize)(validateFieldValue(`${label}.${field.name}`, field, record[field.name], maxPlayers));
|
|
136
|
+
if (includeField(field)) {
|
|
137
|
+
output[field.name] = canonicalValue;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (const key of Object.keys(record).sort(compareStrings)) {
|
|
141
|
+
if (!declaredFields.has(key)) {
|
|
142
|
+
throw new errors_js_1.SyncplayError(`${label}.${key} is not declared in schema`, {
|
|
143
|
+
code: 'schema-artifacts.undeclared-field',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return output;
|
|
148
|
+
}
|
|
149
|
+
function compareStrings(left, right) {
|
|
150
|
+
if (left < right) {
|
|
151
|
+
return -1;
|
|
152
|
+
}
|
|
153
|
+
/* istanbul ignore else -- record-key sorts in this module cannot compare duplicate keys. */
|
|
154
|
+
if (left > right) {
|
|
155
|
+
return 1;
|
|
156
|
+
}
|
|
157
|
+
/* istanbul ignore next -- record-key sorts in this module cannot compare duplicate keys. */
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
function canonicalizeLabeledRecord(label, value) {
|
|
161
|
+
return (0, canonical_js_1.canonicalize)(requireRecord(label, value));
|
|
162
|
+
}
|
|
163
|
+
function validateFieldValue(label, field, value, maxPlayers) {
|
|
164
|
+
if (Array.isArray(value)) {
|
|
165
|
+
if (field.collectionOrdering === undefined) {
|
|
166
|
+
throw new errors_js_1.SyncplayError(`${label} is an array but does not declare collectionOrdering`, {
|
|
167
|
+
code: 'schema-artifacts.collection-ordering',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
const items = value.map((item, index) => validateScalarValue(`${label}[${index}]`, field, item, maxPlayers));
|
|
171
|
+
// ENFORCE the declared ordering, don't just validate the enum: two peers
|
|
172
|
+
// holding the same logical collection in different array order must
|
|
173
|
+
// canonicalize identically, or the engine reports a desync that isn't one.
|
|
174
|
+
// 'indexed' means array order IS the meaning — preserved as-is.
|
|
175
|
+
if (field.collectionOrdering === 'sorted-key') {
|
|
176
|
+
return [...items].sort((left, right) => compareCanonicalScalars(label, left, right));
|
|
177
|
+
}
|
|
178
|
+
return items;
|
|
179
|
+
}
|
|
180
|
+
return validateScalarValue(label, field, value, maxPlayers);
|
|
181
|
+
}
|
|
182
|
+
function compareCanonicalScalars(label, left, right) {
|
|
183
|
+
if (typeof left === 'number' && typeof right === 'number') {
|
|
184
|
+
return left - right;
|
|
185
|
+
}
|
|
186
|
+
if (typeof left === 'string' && typeof right === 'string') {
|
|
187
|
+
return compareStrings(left, right);
|
|
188
|
+
}
|
|
189
|
+
if (typeof left === 'boolean' && typeof right === 'boolean') {
|
|
190
|
+
return Number(left) - Number(right);
|
|
191
|
+
}
|
|
192
|
+
throw new errors_js_1.SyncplayError(`${label} sorted-key collection requires uniformly comparable scalar items`, {
|
|
193
|
+
code: 'schema-artifacts.collection-ordering',
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
function validateScalarValue(label, field, value, maxPlayers) {
|
|
197
|
+
switch (field.kind) {
|
|
198
|
+
case 'boolean':
|
|
199
|
+
case 'button':
|
|
200
|
+
if (typeof value !== 'boolean') {
|
|
201
|
+
throwTypeError(label, 'boolean');
|
|
202
|
+
}
|
|
203
|
+
return value;
|
|
204
|
+
case 'string':
|
|
205
|
+
case 'asset-ref':
|
|
206
|
+
if (typeof value !== 'string') {
|
|
207
|
+
throwTypeError(label, 'string');
|
|
208
|
+
}
|
|
209
|
+
return value;
|
|
210
|
+
case 'integer':
|
|
211
|
+
case 'fixed-q16':
|
|
212
|
+
case 'bitfield':
|
|
213
|
+
case 'player-slot':
|
|
214
|
+
if (typeof value !== 'number' || !Number.isInteger(value)) {
|
|
215
|
+
throwTypeError(label, 'integer');
|
|
216
|
+
}
|
|
217
|
+
assertRange(label, field, value);
|
|
218
|
+
if (field.kind === 'player-slot' && (value < 0 || value >= maxPlayers)) {
|
|
219
|
+
throw new errors_js_1.SyncplayError(`${label} must be a player slot between 0 and ${maxPlayers - 1}`, {
|
|
220
|
+
code: 'schema-artifacts.player-slot',
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return value;
|
|
224
|
+
/* istanbul ignore next -- defineSyncplayGame rejects unsupported field kinds before artifact creation. */
|
|
225
|
+
default:
|
|
226
|
+
throw new errors_js_1.SyncplayError(`${label} uses unsupported field kind`, {
|
|
227
|
+
code: 'schema-artifacts.unsupported-kind',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function canonicalizeRecordedInputFrame(inputFrame, canonicalizeInput, maxPlayers) {
|
|
232
|
+
const record = requireRecord('inputFrame', inputFrame);
|
|
233
|
+
const allowedKeys = new Set(['frame', 'inputs', 'inputFlags']);
|
|
234
|
+
for (const key of Object.keys(record).sort(compareStrings)) {
|
|
235
|
+
if (!allowedKeys.has(key)) {
|
|
236
|
+
throw new errors_js_1.SyncplayError(`inputFrame.${key} is not declared`, {
|
|
237
|
+
code: 'schema-artifacts.input-frame',
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
const frame = requireOwnProperty(record, 'inputFrame', 'frame');
|
|
242
|
+
if (typeof frame !== 'number' || !Number.isInteger(frame) || frame < 0) {
|
|
243
|
+
throw new errors_js_1.SyncplayError('inputFrame.frame must be a non-negative integer', {
|
|
244
|
+
code: 'schema-artifacts.input-frame',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const inputs = requireOwnProperty(record, 'inputFrame', 'inputs');
|
|
248
|
+
if (!Array.isArray(inputs)) {
|
|
249
|
+
throw new errors_js_1.SyncplayError('inputFrame.inputs must be an array', {
|
|
250
|
+
code: 'schema-artifacts.input-frame',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (inputs.length > maxPlayers) {
|
|
254
|
+
throw new errors_js_1.SyncplayError(`inputFrame.inputs cannot exceed maxPlayers ${maxPlayers}`, {
|
|
255
|
+
code: 'schema-artifacts.input-frame',
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const output = {
|
|
259
|
+
frame,
|
|
260
|
+
inputs: inputs.map((input) => canonicalizeInput(input)),
|
|
261
|
+
};
|
|
262
|
+
const hasInputFlags = Object.prototype.hasOwnProperty.call(record, 'inputFlags');
|
|
263
|
+
if (!hasInputFlags && record.inputFlags !== undefined) {
|
|
264
|
+
throw new errors_js_1.SyncplayError('inputFrame.inputFlags must be an own property when present', {
|
|
265
|
+
code: 'schema-artifacts.input-frame',
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (!hasInputFlags) {
|
|
269
|
+
return (0, canonical_js_1.cloneCanonical)(output);
|
|
270
|
+
}
|
|
271
|
+
const inputFlags = record.inputFlags;
|
|
272
|
+
if (!Array.isArray(inputFlags)) {
|
|
273
|
+
throw new errors_js_1.SyncplayError('inputFrame.inputFlags must be an array when present', {
|
|
274
|
+
code: 'schema-artifacts.input-frame',
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return (0, canonical_js_1.cloneCanonical)({
|
|
278
|
+
...output,
|
|
279
|
+
inputFlags: canonicalizeRecordedInputFlags(inputFlags, inputs.length, maxPlayers),
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
function canonicalizeRecordedInputFlags(inputFlags, expectedLength, maxPlayers) {
|
|
283
|
+
if (inputFlags.length !== expectedLength) {
|
|
284
|
+
throw new errors_js_1.SyncplayError(`inputFrame.inputFlags length ${inputFlags.length} must match inputs length ${expectedLength}`, {
|
|
285
|
+
code: 'schema-artifacts.input-frame',
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return inputFlags.map((flag, index) => {
|
|
289
|
+
const record = requireRecord(`inputFrame.inputFlags[${index}]`, flag);
|
|
290
|
+
const allowedKeys = new Set(['slot', 'playerPresent', 'repeatedPrevious', 'replacedByServer', 'commandInput']);
|
|
291
|
+
for (const key of Object.keys(record).sort(compareStrings)) {
|
|
292
|
+
if (!allowedKeys.has(key)) {
|
|
293
|
+
throw new errors_js_1.SyncplayError(`inputFrame.inputFlags[${index}].${key} is not declared`, {
|
|
294
|
+
code: 'schema-artifacts.input-frame',
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const slot = requireOwnProperty(record, `inputFrame.inputFlags[${index}]`, 'slot');
|
|
299
|
+
if (typeof slot !== 'number' || !Number.isInteger(slot) || slot < 0 || slot >= maxPlayers) {
|
|
300
|
+
throw new errors_js_1.SyncplayError(`inputFrame.inputFlags[${index}].slot must be a valid player slot`, {
|
|
301
|
+
code: 'schema-artifacts.input-frame',
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (slot !== index) {
|
|
305
|
+
throw new errors_js_1.SyncplayError(`inputFrame.inputFlags[${index}].slot must match its index`, {
|
|
306
|
+
code: 'schema-artifacts.input-frame',
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
slot,
|
|
311
|
+
playerPresent: requireBooleanFlag(record, index, 'playerPresent'),
|
|
312
|
+
repeatedPrevious: requireBooleanFlag(record, index, 'repeatedPrevious'),
|
|
313
|
+
replacedByServer: requireBooleanFlag(record, index, 'replacedByServer'),
|
|
314
|
+
commandInput: requireBooleanFlag(record, index, 'commandInput'),
|
|
315
|
+
};
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function requireBooleanFlag(record, index, key) {
|
|
319
|
+
const value = requireOwnProperty(record, `inputFrame.inputFlags[${index}]`, key);
|
|
320
|
+
if (typeof value !== 'boolean') {
|
|
321
|
+
throw new errors_js_1.SyncplayError(`inputFrame.inputFlags[${index}].${key} must be boolean`, {
|
|
322
|
+
code: 'schema-artifacts.input-frame',
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
return value;
|
|
326
|
+
}
|
|
327
|
+
function requireOwnProperty(record, label, key) {
|
|
328
|
+
if (!Object.prototype.hasOwnProperty.call(record, key)) {
|
|
329
|
+
throw new errors_js_1.SyncplayError(`${label}.${key} must be an own property`, {
|
|
330
|
+
code: 'schema-artifacts.input-frame',
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
return record[key];
|
|
334
|
+
}
|
|
335
|
+
function assertRange(label, field, value) {
|
|
336
|
+
if (field.min !== undefined && value < field.min) {
|
|
337
|
+
throw new errors_js_1.SyncplayError(`${label} is below minimum ${field.min}`, {
|
|
338
|
+
code: 'schema-artifacts.range',
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
if (field.max !== undefined && value > field.max) {
|
|
342
|
+
throw new errors_js_1.SyncplayError(`${label} is above maximum ${field.max}`, {
|
|
343
|
+
code: 'schema-artifacts.range',
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function requireRecord(label, value) {
|
|
348
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
349
|
+
throw new errors_js_1.SyncplayError(`${label} must be an object`, {
|
|
350
|
+
code: 'schema-artifacts.record',
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return value;
|
|
354
|
+
}
|
|
355
|
+
function throwTypeError(label, expected) {
|
|
356
|
+
throw new errors_js_1.SyncplayError(`${label} must be ${expected}`, {
|
|
357
|
+
code: 'schema-artifacts.type',
|
|
358
|
+
});
|
|
359
|
+
}
|