@series-inc/rundot-syncplay 5.26.0-beta.5 → 5.26.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 +125 -44
- package/core/index.d.ts +0 -54
- package/core/native/fixed_math.cpp +11 -4
- package/core/runtime.d.ts +0 -304
- package/core/scripts/emit-product-digests.mjs +0 -14
- package/core/src/index.mjs +0 -40
- package/core/src/kinetix-deterministic-stdlib.mjs +30 -13
- package/core/src/runtime.mjs +0 -40
- package/dist/browser.d.ts +1 -3
- package/dist/browser.js +0 -1
- package/dist/effects-adapter.d.ts +1 -0
- package/dist/effects-adapter.js +9 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.js +0 -1
- package/dist/math.js +30 -5
- package/dist/modules/actors-ai.d.ts +111 -0
- package/dist/modules/actors-ai.js +808 -0
- package/dist/modules/character-3d.d.ts +129 -0
- package/dist/modules/character-3d.js +844 -0
- package/dist/modules/combat.d.ts +183 -0
- package/dist/modules/combat.js +861 -0
- package/dist/modules/component-model.d.ts +95 -0
- package/dist/modules/component-model.js +466 -0
- package/dist/modules/entity-registry.d.ts +40 -0
- package/dist/modules/entity-registry.js +177 -0
- package/dist/modules/firearm.d.ts +72 -0
- package/dist/modules/firearm.js +367 -0
- package/dist/modules/gameplay-effects.d.ts +157 -0
- package/dist/modules/gameplay-effects.js +795 -0
- package/dist/modules/identity.d.ts +143 -0
- package/dist/modules/identity.js +147 -0
- package/dist/modules/inventory-equipment.d.ts +171 -0
- package/dist/modules/inventory-equipment.js +984 -0
- package/dist/modules/module-contract.d.ts +20 -0
- package/dist/modules/module-contract.js +129 -0
- package/dist/modules/module-event.d.ts +15 -0
- package/dist/modules/module-event.js +84 -0
- package/dist/modules/platformer-2d.d.ts +107 -0
- package/dist/modules/platformer-2d.js +566 -0
- package/dist/modules/rts.d.ts +192 -0
- package/dist/modules/rts.js +1446 -0
- package/dist/modules/spatial-interaction.d.ts +44 -0
- package/dist/modules/spatial-interaction.js +248 -0
- package/dist/modules/stats-abilities.d.ts +235 -0
- package/dist/modules/stats-abilities.js +1488 -0
- package/dist/modules/unit-control.d.ts +101 -0
- package/dist/modules/unit-control.js +387 -0
- package/dist/movement3d.js +93 -6
- package/dist/networked-client.d.ts +9 -0
- package/dist/networked-client.js +132 -12
- package/dist/run-room-transport.d.ts +2 -0
- package/dist/run-room-transport.js +1 -0
- package/dist/runner.d.ts +9 -1
- package/dist/runner.js +7 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/module-certification.d.ts +41 -0
- package/dist/tools/module-certification.js +456 -0
- package/dist/tools/static-checker.d.ts +2 -1
- package/dist/tools/static-checker.js +540 -48
- package/package.json +101 -6
- package/core/scripts/generate-platformer2d-manifest.mjs +0 -82
- package/core/src/kinetix-platformer2d-errors.mjs +0 -25
- package/core/src/kinetix-platformer2d-level.mjs +0 -323
- package/core/src/kinetix-platformer2d-mechanics.mjs +0 -616
- package/core/src/kinetix-platformer2d-registry.mjs +0 -154
- package/core/src/kinetix-platformer2d-runtime.mjs +0 -1266
- package/core/src/kinetix-platformer2d-source-manifest.generated.mjs +0 -16
- package/dist/platformer-content.d.ts +0 -49
- package/dist/platformer-content.js +0 -100
package/README.md
CHANGED
|
@@ -7,6 +7,123 @@ streams, stepping, opaque checkpoints, serialization, hydration, and checksums.
|
|
|
7
7
|
Syncplay owns inputs, prediction, confirmation, rollback policy, replay logs,
|
|
8
8
|
late join, secrets, and transport.
|
|
9
9
|
|
|
10
|
+
## Game module entrypoints
|
|
11
|
+
|
|
12
|
+
Generic game-layer modules use explicit `./modules/*` package subpaths. The
|
|
13
|
+
module tree is code-only: binary art, audio, meshes, and other game content
|
|
14
|
+
belong in complete examples, not this published package. Each entrypoint
|
|
15
|
+
exports and documents its own `stable` or `provisional` API status.
|
|
16
|
+
|
|
17
|
+
Every new module subpath must be present in the export-surface golden and the
|
|
18
|
+
module bundle-budget golden. The guards derive their entry set from
|
|
19
|
+
`package.json#exports`, so an unclassified export or a module without a budget
|
|
20
|
+
fails CI.
|
|
21
|
+
|
|
22
|
+
The first real entrypoint is
|
|
23
|
+
`@series-inc/rundot-syncplay/modules/component-model`. It defines pure
|
|
24
|
+
`step(slice, input, context)` modules, canonical state-slice validation,
|
|
25
|
+
rollback-safe one-shot presentation, a KayKit-backed character clip vocabulary,
|
|
26
|
+
and one third-person KCC3D reference component. V1 module context has no random
|
|
27
|
+
generator: random and secret outcomes arrive as explicit input or authority
|
|
28
|
+
commands so adding or reordering components cannot change a shared RNG stream.
|
|
29
|
+
V1 is function-only; Kinetix descriptor adapters remain outside this arc. The
|
|
30
|
+
component-model API is stable.
|
|
31
|
+
|
|
32
|
+
`@series-inc/rundot-syncplay/modules/character-3d` composes the existing
|
|
33
|
+
deterministic KCC3D and top-down mover into kinematic and top-down controllers,
|
|
34
|
+
with crouch, slide, mantle, dash, wall-run, canonical animation parameters, and
|
|
35
|
+
first-person, orbit, or top-down camera projections. Collision and traversal
|
|
36
|
+
facts are explicit context input; render libraries, assets, and module-owned
|
|
37
|
+
randomness remain outside the module. Its API is stable.
|
|
38
|
+
|
|
39
|
+
`@series-inc/rundot-syncplay/modules/combat` composes deterministic combatant
|
|
40
|
+
health/resistance/i-frame/hitstop resolution, active-frame melee arcs,
|
|
41
|
+
travelling projectiles through the existing 3D shape-cast path, and hitscan
|
|
42
|
+
authority through the existing lag-compensation validator. Hits, hurtboxes,
|
|
43
|
+
body ownership, targets, occluders, and server timing are explicit input or
|
|
44
|
+
context; the module owns no random stream, renderer, or physics engine. Its API
|
|
45
|
+
is stable.
|
|
46
|
+
|
|
47
|
+
`@series-inc/rundot-syncplay/modules/firearm` owns canonical semi-automatic or
|
|
48
|
+
automatic cadence, magazine, reload, recoil, spread-sequence, and trigger-edge
|
|
49
|
+
state. Its inventory helper consumes only the authored ammo item atomically.
|
|
50
|
+
Aim, hits, damage, switching, attachments, and presentation remain caller-owned.
|
|
51
|
+
Its API is provisional because the FPS is its only production example consumer.
|
|
52
|
+
|
|
53
|
+
`@series-inc/rundot-syncplay/modules/gameplay-effects` emits ordered typed
|
|
54
|
+
damage, healing, resource, status, and interrupt commands from immediate or
|
|
55
|
+
periodic definitions. The composition helper applies combat and stats changes
|
|
56
|
+
atomically in target/command order. Targeting, balance, crits, randomness, and
|
|
57
|
+
presentation remain caller-owned. Its API is stable.
|
|
58
|
+
|
|
59
|
+
`@series-inc/rundot-syncplay/modules/actors-ai` adds deterministic authored
|
|
60
|
+
encounters, wave spawning, threat-based aggro, squad target sharing, and crowd
|
|
61
|
+
movement over the existing bot and cooked-navmesh primitives. It accepts
|
|
62
|
+
defeats and threat changes as explicit input, emits rollback-safe semantic
|
|
63
|
+
events, and keeps render, combat, loot, and random encounter generation outside
|
|
64
|
+
the module. Its API is provisional until the promotion gate.
|
|
65
|
+
|
|
66
|
+
`@series-inc/rundot-syncplay/modules/stats-abilities` adds deterministic stat
|
|
67
|
+
blocks, source-replaced modifiers, cast/channel/cooldown ability state, stacked
|
|
68
|
+
statuses, experience levels, and ticket-resolved loot. Random authority remains
|
|
69
|
+
outside the module: loot consumes an explicit integer ticket carried in synced
|
|
70
|
+
input. Equipment integrates through source IDs such as
|
|
71
|
+
`equipment:<ownerId>:<slotId>` and atomic modifier replacement. Its API is
|
|
72
|
+
provisional until the promotion gate.
|
|
73
|
+
|
|
74
|
+
`@series-inc/rundot-syncplay/modules/inventory-equipment` owns deterministic
|
|
75
|
+
inventory slots, stacks, loot containers, pickups, drops, and equipment
|
|
76
|
+
references. Equipped items remain in canonical inventory slots, and successful
|
|
77
|
+
equipment transitions become complete source-replacement input for the real
|
|
78
|
+
stats/abilities step. It owns no loot randomness, persistence, trading,
|
|
79
|
+
rendering, or binary content. Its API is stable.
|
|
80
|
+
|
|
81
|
+
`@series-inc/rundot-syncplay/modules/spatial-interaction` owns deterministic
|
|
82
|
+
range/cone selection, focus, hold progress, cancellation, and one-shot
|
|
83
|
+
completion. The caller owns the completed action and presentation. Its API is
|
|
84
|
+
stable.
|
|
85
|
+
|
|
86
|
+
`@series-inc/rundot-syncplay/modules/platformer-2d` composes the deterministic
|
|
87
|
+
2D KCC with manual and autonomous movement, pause/resume/reverse control,
|
|
88
|
+
edge/wall reversal latches, rising-edge jump input, one-way drop priority, and
|
|
89
|
+
canonical render bindings. Level geometry and environmental facts remain
|
|
90
|
+
explicit game-owned context. Its API is provisional until the promotion gate.
|
|
91
|
+
|
|
92
|
+
`@series-inc/rundot-syncplay/modules/rts` declares the provisional deterministic
|
|
93
|
+
RTS state, command, configuration, event, and projection boundary. The module
|
|
94
|
+
delegates unit movement to the existing public deterministic navmesh step;
|
|
95
|
+
rendering, content, health resolution, room lifecycle, and win rules remain
|
|
96
|
+
game-owned. Its exact-shape implementation includes bounded selection/orders,
|
|
97
|
+
formations, live-hostile target validation, explicit attack-move acquisition
|
|
98
|
+
provenance, fog, economy, placement, production, and stable attack requests;
|
|
99
|
+
certification remains a required provisional-module gate. Hydrated and issued
|
|
100
|
+
authored `attack-target` orders must name a live opposing unit or building;
|
|
101
|
+
self, friendly, queued, retired, and missing targets fail closed.
|
|
102
|
+
|
|
103
|
+
The normal runner emits side effects only from authority-confirmed frames.
|
|
104
|
+
Predicted/canceled lifecycle callbacks are available to lower-level speculative
|
|
105
|
+
presentations that feed their `createDeterministicEventTimeline` records into
|
|
106
|
+
the module presentation adapter; prediction state never enters deterministic
|
|
107
|
+
module context.
|
|
108
|
+
|
|
109
|
+
`./tools` is a supported public Node/build-tooling surface and is governed by
|
|
110
|
+
the export golden. The import-safe `./core/build-native` and
|
|
111
|
+
`./core/build-wasm-physics` callables are governed as well. Only
|
|
112
|
+
`./core/preflight` is excluded from public-symbol comparison because it exports
|
|
113
|
+
nothing and is a command that executes on import.
|
|
114
|
+
|
|
115
|
+
`certifyModule(component, fixture)` on `./tools` is the provisional conformance
|
|
116
|
+
gate for `./modules/*`. It delegates straight, rollback, replay, and hydration
|
|
117
|
+
equivalence to `runSyncplaySynctest`, then adds module-only source rules,
|
|
118
|
+
permutation equivalence, p99 step time, and retained heap per step. Run it under
|
|
119
|
+
Node with `--expose-gc`; missing GC support fails closed instead of reporting a
|
|
120
|
+
zero allocation.
|
|
121
|
+
|
|
122
|
+
The published JavaScript graph has a committed exhaustive side-effect audit.
|
|
123
|
+
`core/scripts/preflight.mjs` and
|
|
124
|
+
`core/scripts/emit-product-digests.mjs` are the only import-time command
|
|
125
|
+
effects and are preserved through the package's precise `sideEffects` allowlist.
|
|
126
|
+
|
|
10
127
|
> **Upgrading an existing game?** See
|
|
11
128
|
> [MIGRATING-SESSION-PRIMITIVES.md](./MIGRATING-SESSION-PRIMITIVES.md). Two
|
|
12
129
|
> changes are not additive: the runner now rejects an unsafe input codec at
|
|
@@ -44,14 +161,8 @@ There is one session factory:
|
|
|
44
161
|
|
|
45
162
|
```ts
|
|
46
163
|
import { createSyncplaySession } from '@series-inc/rundot-syncplay/browser';
|
|
47
|
-
import { createInstalledPlatformer2dRuntime } from '@series-inc/rundot-syncplay/core/runtime';
|
|
48
164
|
|
|
49
|
-
const runtime =
|
|
50
|
-
pack,
|
|
51
|
-
levelBytes,
|
|
52
|
-
identity,
|
|
53
|
-
sessionConfigBytes,
|
|
54
|
-
});
|
|
165
|
+
const runtime = runtimeFactory(runtimeIdentity, sessionConfigBytes);
|
|
55
166
|
|
|
56
167
|
const session = createSyncplaySession(runtime, {
|
|
57
168
|
playerCount: 4,
|
|
@@ -64,8 +175,8 @@ const session = createSyncplaySession(runtime, {
|
|
|
64
175
|
|
|
65
176
|
The runtime must implement `@series-inc/rundot-syncplay/core/runtime`. Syncplay never
|
|
66
177
|
accepts an initial state, reducer, or creator-authored step callback. There is no
|
|
67
|
-
compatibility mode for the removed reducer API. A
|
|
68
|
-
|
|
178
|
+
compatibility mode for the removed reducer API. A game supplies the ABI through
|
|
179
|
+
`createInstalledRuntimeAdapter` and binds its source with
|
|
69
180
|
`deriveCustomRuntimeIdentity` plus `kinetixRuntimeModuleManifest`.
|
|
70
181
|
|
|
71
182
|
## Runtime and snapshot contract
|
|
@@ -78,7 +189,8 @@ destination validates all of those values before mutating its live session.
|
|
|
78
189
|
|
|
79
190
|
The networked client receives the authority's exact runtime identity and
|
|
80
191
|
session-config bytes, asks the game for a matching runtime through its runtime
|
|
81
|
-
factory, and rejects a
|
|
192
|
+
factory, and rejects a local runtime or content identity mismatch before frame 0.
|
|
193
|
+
Replays use
|
|
82
194
|
the same identity/config/factory path.
|
|
83
195
|
|
|
84
196
|
### Capability requirements
|
|
@@ -235,29 +347,9 @@ async preparation, then replays every buffered message once.
|
|
|
235
347
|
|
|
236
348
|
The descriptor is exactly `{ slot, playerCount, seed, tickRateHz,
|
|
237
349
|
hardToleranceTicks, role, runtimeIdentity, sessionConfigBytes,
|
|
238
|
-
sessionConfigDigest }`.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
```ts
|
|
243
|
-
import { decodeKinetixSessionConfig } from '@series-inc/rundot-syncplay/core/runtime';
|
|
244
|
-
import { kinetixPlatformer2dSessionConfigSchema } from '@series-inc/rundot-syncplay/core/runtime';
|
|
245
|
-
|
|
246
|
-
const prepareNetworkRuntime = async (descriptor, signal) => {
|
|
247
|
-
const session = decodeKinetixSessionConfig(
|
|
248
|
-
kinetixPlatformer2dSessionConfigSchema,
|
|
249
|
-
descriptor.sessionConfigBytes,
|
|
250
|
-
);
|
|
251
|
-
// session.levelEntryId / session.levelDigest / session.levelByteLength
|
|
252
|
-
const levelBytes = await preparePlatformerLevel(api.ugc, {
|
|
253
|
-
levelEntryId: session.levelEntryId,
|
|
254
|
-
levelDigest: session.levelDigest,
|
|
255
|
-
levelByteLength: session.levelByteLength,
|
|
256
|
-
encodeLevel: (level) => encodeKinetixPlatformer2dLevel(level, { maxBytes: 102_400 }),
|
|
257
|
-
});
|
|
258
|
-
registerPreparedLevel(descriptor.sessionConfigDigest, levelBytes);
|
|
259
|
-
};
|
|
260
|
-
```
|
|
350
|
+
sessionConfigDigest }`. Game-specific asset declarations belong inside canonical
|
|
351
|
+
`sessionConfigBytes`; resolve and verify those assets before constructing the
|
|
352
|
+
network runtime.
|
|
261
353
|
|
|
262
354
|
## Safe input codecs
|
|
263
355
|
|
|
@@ -274,17 +366,6 @@ For an existing hand-written codec, `validateSyncplayInputCodec({ encodeInput,
|
|
|
274
366
|
decodeInput, samples })` probes round-trip stability, `null → neutral`, and a
|
|
275
367
|
battery of hostile payloads, failing with `SYNCPLAY_INPUT_CODEC_UNSAFE`.
|
|
276
368
|
|
|
277
|
-
## Version-safe content references
|
|
278
|
-
|
|
279
|
-
`publishPlatformerLevel(api, level, options)` always calls `ugc.create` — never
|
|
280
|
-
`ugc.update` on a referenced entry, which would make every live room pinned to it
|
|
281
|
-
permanently unjoinable. `preparePlatformerLevel(api, declaration)` resolves a pin
|
|
282
|
-
and raises the typed `SYNCPLAY_LEVEL_CONTENT_CHANGED` (naming the entry id and the
|
|
283
|
-
edit-vs-pin cause) rather than an opaque `KINETIX_AUX_ASSET_MISMATCH` deep inside
|
|
284
|
-
runtime construction. `declaration.encodeLevel` is an **injected callback**, not a
|
|
285
|
-
static core import, so Syncplay's compiled output never hard-binds a specific
|
|
286
|
-
core build.
|
|
287
|
-
|
|
288
369
|
**Capacity and late-join budgets.** Snapshot transfer defaults are **256 KiB**
|
|
289
370
|
chunks and a **4 MiB** total ceiling; the immutable level payload is fetched and
|
|
290
371
|
verified separately and never rides a snapshot. `maxPredictionTicks` is a
|
package/core/index.d.ts
CHANGED
|
@@ -23,61 +23,7 @@ export type {
|
|
|
23
23
|
KinetixRuntimeIdentity,
|
|
24
24
|
KinetixSessionConfigEnvelope,
|
|
25
25
|
} from './runtime';
|
|
26
|
-
export {
|
|
27
|
-
KINETIX_PLATFORMER2D_LEVEL_FORMAT,
|
|
28
|
-
KINETIX_PLATFORMER2D_PACK_FORMAT,
|
|
29
|
-
KINETIX_PLATFORMER2D_PACK_FORMAT_V1,
|
|
30
|
-
KINETIX_PLATFORMER2D_LEVEL_FORMAT_V1,
|
|
31
|
-
KINETIX_PLATFORMER2D_SPENT_TILE_ID,
|
|
32
|
-
KinetixPlatformerLevelError,
|
|
33
|
-
KinetixPlatformerRegistryError,
|
|
34
|
-
MAX_KINETIX_PLATFORMER2D_LEVEL_BYTES,
|
|
35
|
-
applyKinetixPlatformer2dTileOverrides,
|
|
36
|
-
createInstalledPlatformer2dRuntime,
|
|
37
|
-
createKinetixPlatformer2dRegistry,
|
|
38
|
-
createKinetixPlatformer2dSession,
|
|
39
|
-
decodeKinetixPlatformer2dLevel,
|
|
40
|
-
defineKinetixPlatformer2dPack,
|
|
41
|
-
deriveKinetixPlatformer2dRuntimeIdentity,
|
|
42
|
-
describeKinetixPlatformer2dLevel,
|
|
43
|
-
encodeKinetixPlatformer2dLevel,
|
|
44
|
-
kinetixPlatformer2dBuiltInEntityTypes,
|
|
45
|
-
kinetixPlatformer2dDeterministicAbi,
|
|
46
|
-
kinetixPlatformer2dLevelBonusBlocks,
|
|
47
|
-
kinetixPlatformer2dLevelBreakableTileIds,
|
|
48
|
-
kinetixPlatformer2dLevelOpenBounds,
|
|
49
|
-
kinetixPlatformer2dLevelSlopeTiles,
|
|
50
|
-
kinetixPlatformer2dSessionConfigSchema,
|
|
51
|
-
kinetixPlatformer2dSourceManifest,
|
|
52
|
-
migrateKinetixPlatformer2dLevel,
|
|
53
|
-
migrateKinetixPlatformer2dPack,
|
|
54
|
-
nextRngUint32,
|
|
55
|
-
rngInt,
|
|
56
|
-
seedRng,
|
|
57
|
-
} from './runtime';
|
|
58
26
|
export type {
|
|
59
|
-
KinetixPlatformer2dAbilities,
|
|
60
|
-
KinetixPlatformer2dBonusBlock,
|
|
61
|
-
KinetixPlatformer2dBonusContents,
|
|
62
|
-
KinetixPlatformer2dEntityDescriptor,
|
|
63
|
-
KinetixPlatformer2dPackV1,
|
|
64
|
-
KinetixPlatformer2dEntityValidationContext,
|
|
65
|
-
KinetixPlatformer2dIdentityOptions,
|
|
66
|
-
KinetixPlatformer2dEvent,
|
|
67
|
-
KinetixPlatformer2dGoalMode,
|
|
68
|
-
KinetixPlatformer2dInput,
|
|
69
|
-
KinetixPlatformer2dLevel,
|
|
70
|
-
KinetixPlatformer2dLevelEntity,
|
|
71
|
-
KinetixPlatformer2dPack,
|
|
72
|
-
KinetixPlatformer2dPhase,
|
|
73
|
-
KinetixPlatformer2dPlayerProjection,
|
|
74
|
-
KinetixPlatformer2dPowerUpKind,
|
|
75
|
-
KinetixPlatformer2dProjection,
|
|
76
|
-
KinetixPlatformer2dRegistry,
|
|
77
|
-
KinetixPlatformer2dSessionSetup,
|
|
78
|
-
KinetixPlatformer2dSlopeTile,
|
|
79
|
-
KinetixPlatformer2dTickRate,
|
|
80
|
-
KinetixPlatformerLevelErrorCode,
|
|
81
27
|
KinetixRuntimeModuleManifestEntry,
|
|
82
28
|
} from './runtime';
|
|
83
29
|
export type { KinetixSessionConfigSchema } from './authoring';
|
|
@@ -24,6 +24,13 @@ double jsRound(double x) {
|
|
|
24
24
|
// Number.isInteger: finite and integral. Non-integers make the reference throw.
|
|
25
25
|
bool isFixed(double v) { return std::isfinite(v) && std::floor(v) == v; }
|
|
26
26
|
|
|
27
|
+
// The reference restricts the domain so the over-2^53 divergence window is
|
|
28
|
+
// empty rather than merely unlikely (src/math.ts). An interpolant outside
|
|
29
|
+
// [0, fixedScale] is the one intermediate that restriction does not cover.
|
|
30
|
+
bool isInterpolant(const Config& c, double v) {
|
|
31
|
+
return isFixed(v) && v >= 0.0 && v <= c.fixedScale;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
bool isSafeInteger(double v) { return isFixed(v) && std::fabs(v) <= 9007199254740991.0; }
|
|
28
35
|
|
|
29
36
|
// NOT std::fmin/std::fmax: those IGNORE a NaN operand and return the other one,
|
|
@@ -249,7 +256,7 @@ double maxFixed(const Config& c, double a, double b, bool& ok) {
|
|
|
249
256
|
}
|
|
250
257
|
|
|
251
258
|
double lerp(const Config& c, double a, double b, double t, bool& ok) {
|
|
252
|
-
if (!isFixed(a) || !isFixed(b) || !
|
|
259
|
+
if (!isFixed(a) || !isFixed(b) || !isInterpolant(c, t)) {
|
|
253
260
|
ok = false;
|
|
254
261
|
return 0.0;
|
|
255
262
|
}
|
|
@@ -594,7 +601,7 @@ Vec2 vec2FromAngleTurns(const Config& c, double turns, double length, bool& ok)
|
|
|
594
601
|
double vec2AngleTurns(const Config& c, Vec2 v, bool& ok) { return atan2Turns(c, v.y, v.x, ok); }
|
|
595
602
|
|
|
596
603
|
Vec2 vec2Lerp(const Config& c, Vec2 a, Vec2 b, double t, bool& ok) {
|
|
597
|
-
if (!
|
|
604
|
+
if (!isInterpolant(c, t)) {
|
|
598
605
|
ok = false;
|
|
599
606
|
return Vec2{0, 0};
|
|
600
607
|
}
|
|
@@ -686,7 +693,7 @@ Vec3 vec3Normalize(const Config& c, Vec3 v, bool& ok) {
|
|
|
686
693
|
}
|
|
687
694
|
|
|
688
695
|
Vec3 vec3Lerp(const Config& c, Vec3 a, Vec3 b, double t, bool& ok) {
|
|
689
|
-
if (!
|
|
696
|
+
if (!isInterpolant(c, t)) {
|
|
690
697
|
ok = false;
|
|
691
698
|
return Vec3{0, 0, 0};
|
|
692
699
|
}
|
|
@@ -718,7 +725,7 @@ Quat quatFromYawTurns(const Config& c, double turns, bool& ok) {
|
|
|
718
725
|
}
|
|
719
726
|
|
|
720
727
|
Quat quatLerp(const Config& c, Quat a, Quat b, double t, bool& ok) {
|
|
721
|
-
if (!
|
|
728
|
+
if (!isInterpolant(c, t)) {
|
|
722
729
|
ok = false;
|
|
723
730
|
return Quat{0, 0, 0, 0};
|
|
724
731
|
}
|
package/core/runtime.d.ts
CHANGED
|
@@ -161,311 +161,7 @@ export function createKinetixSessionConfig(
|
|
|
161
161
|
value: Readonly<Record<string, unknown>>,
|
|
162
162
|
): KinetixSessionConfigEnvelope;
|
|
163
163
|
|
|
164
|
-
export type KinetixPlatformer2dTickRate = 10 | 20 | 30 | 60;
|
|
165
|
-
export type KinetixPlatformer2dGoalMode = 'any-player' | 'all-players';
|
|
166
|
-
|
|
167
|
-
export interface KinetixPlatformer2dAbilities {
|
|
168
|
-
readonly crouch: boolean;
|
|
169
|
-
readonly groundPound: boolean;
|
|
170
|
-
readonly wallJump: boolean;
|
|
171
|
-
readonly climb: boolean;
|
|
172
|
-
readonly swim: boolean;
|
|
173
|
-
readonly carry: boolean;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export interface KinetixPlatformer2dPack {
|
|
177
|
-
readonly format: 'rundot.kinetix-platformer2d-pack.v2';
|
|
178
|
-
readonly tickRate: KinetixPlatformer2dTickRate;
|
|
179
|
-
readonly player: { readonly halfWidth: number; readonly halfHeight: number; readonly maxHealth: number };
|
|
180
|
-
readonly movement: {
|
|
181
|
-
readonly gravity: number; readonly maxFallSpeed: number;
|
|
182
|
-
readonly runAcceleration: number; readonly runDeceleration: number; readonly maxRunSpeed: number;
|
|
183
|
-
readonly jumpSpeed: number; readonly jumpCutGravityMultiplier: number;
|
|
184
|
-
readonly coyoteFrames: number; readonly jumpBufferFrames: number; readonly dropThroughFrames: number;
|
|
185
|
-
readonly crouchHalfHeight: number; readonly climbSpeed: number;
|
|
186
|
-
readonly swimGravity: number; readonly swimStrokeSpeed: number; readonly maxSwimFallSpeed: number;
|
|
187
|
-
readonly wallSlideSpeed: number; readonly wallJumpSpeedX: number; readonly wallJumpSpeedY: number;
|
|
188
|
-
readonly groundPoundSpeed: number; readonly throwSpeedX: number; readonly throwSpeedY: number;
|
|
189
|
-
};
|
|
190
|
-
readonly damage: {
|
|
191
|
-
readonly invulnerabilityFrames: number; readonly respawnFrames: number;
|
|
192
|
-
readonly stompBounceSpeed: number; readonly respawnGraceFrames: number;
|
|
193
|
-
};
|
|
194
|
-
readonly match: { readonly countdownFrames: number; readonly resultsFrames: number; readonly goalMode: KinetixPlatformer2dGoalMode };
|
|
195
|
-
readonly abilities: KinetixPlatformer2dAbilities;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/** A v1 pack, accepted by `defineKinetixPlatformer2dPack` and migrated with every v2 ability off. */
|
|
199
|
-
export interface KinetixPlatformer2dPackV1 {
|
|
200
|
-
readonly format: 'rundot.kinetix-platformer2d-pack.v1';
|
|
201
|
-
readonly tickRate: KinetixPlatformer2dTickRate;
|
|
202
|
-
readonly player: KinetixPlatformer2dPack['player'];
|
|
203
|
-
readonly movement: Omit<KinetixPlatformer2dPack['movement'],
|
|
204
|
-
'crouchHalfHeight' | 'climbSpeed' | 'swimGravity' | 'swimStrokeSpeed' | 'maxSwimFallSpeed'
|
|
205
|
-
| 'wallSlideSpeed' | 'wallJumpSpeedX' | 'wallJumpSpeedY' | 'groundPoundSpeed' | 'throwSpeedX' | 'throwSpeedY'>;
|
|
206
|
-
readonly damage: Omit<KinetixPlatformer2dPack['damage'], 'respawnGraceFrames'>;
|
|
207
|
-
readonly match: KinetixPlatformer2dPack['match'];
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export const KINETIX_PLATFORMER2D_PACK_FORMAT: 'rundot.kinetix-platformer2d-pack.v2';
|
|
211
|
-
export const KINETIX_PLATFORMER2D_PACK_FORMAT_V1: 'rundot.kinetix-platformer2d-pack.v1';
|
|
212
|
-
|
|
213
|
-
export function migrateKinetixPlatformer2dPack(
|
|
214
|
-
value: KinetixPlatformer2dPack | KinetixPlatformer2dPackV1,
|
|
215
|
-
): KinetixPlatformer2dPack;
|
|
216
|
-
|
|
217
|
-
export interface KinetixPlatformer2dInput {
|
|
218
|
-
readonly moveX: -1 | 0 | 1;
|
|
219
|
-
readonly moveY: -1 | 0 | 1;
|
|
220
|
-
readonly jumpPressed: boolean;
|
|
221
|
-
readonly jumpHeld: boolean;
|
|
222
|
-
readonly dropPressed: boolean;
|
|
223
|
-
readonly actionPressed: boolean;
|
|
224
|
-
readonly crouchHeld: boolean;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/** Folds a running session's `tileOverrides` back into a publishable level object. */
|
|
228
|
-
export function applyKinetixPlatformer2dTileOverrides(
|
|
229
|
-
level: KinetixPlatformer2dLevel,
|
|
230
|
-
state: { readonly tileOverrides: readonly (readonly [number, number])[] },
|
|
231
|
-
): KinetixPlatformer2dLevel;
|
|
232
|
-
|
|
233
|
-
interface KinetixPlatformer2dBox {
|
|
234
|
-
readonly x: number; readonly y: number;
|
|
235
|
-
readonly halfWidth: number; readonly halfHeight: number;
|
|
236
|
-
}
|
|
237
|
-
interface KinetixPlatformer2dPath {
|
|
238
|
-
readonly halfWidth: number; readonly halfHeight: number;
|
|
239
|
-
readonly framesPerLeg: number;
|
|
240
|
-
readonly waypoints: readonly { readonly x: number; readonly y: number }[];
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export type KinetixPlatformer2dLevelEntity =
|
|
244
|
-
| { readonly id: string; readonly type: 'spawn'; readonly slot: number; readonly x: number; readonly y: number }
|
|
245
|
-
| ({ readonly id: string; readonly type: 'oneWayPlatform' } & KinetixPlatformer2dBox)
|
|
246
|
-
| ({ readonly id: string; readonly type: 'movingPlatform' } & KinetixPlatformer2dPath)
|
|
247
|
-
| ({ readonly id: string; readonly type: 'fallingPlatform'; readonly triggerDelayFrames: number; readonly resetFrames: number } & KinetixPlatformer2dBox)
|
|
248
|
-
| ({ readonly id: string; readonly type: 'hazard'; readonly damage: number } & KinetixPlatformer2dBox)
|
|
249
|
-
| ({ readonly id: string; readonly type: 'stompable'; readonly respawnFrames: number } & KinetixPlatformer2dBox)
|
|
250
|
-
| ({ readonly id: string; readonly type: 'goalTrigger' } & KinetixPlatformer2dBox)
|
|
251
|
-
| ({ readonly id: string; readonly type: 'patrolEnemy'; readonly speed: number; readonly turnAtLedge: boolean; readonly damage: number; readonly respawnFrames: number } & KinetixPlatformer2dBox)
|
|
252
|
-
| ({ readonly id: string; readonly type: 'waypointEnemy'; readonly damage: number; readonly respawnFrames: number } & KinetixPlatformer2dPath)
|
|
253
|
-
| ({ readonly id: string; readonly type: 'movingHazard'; readonly damage: number } & KinetixPlatformer2dPath)
|
|
254
|
-
| ({ readonly id: string; readonly type: 'spring'; readonly launchSpeed: number } & KinetixPlatformer2dBox)
|
|
255
|
-
| ({ readonly id: string; readonly type: 'conveyor'; readonly beltSpeed: number } & KinetixPlatformer2dBox)
|
|
256
|
-
| ({ readonly id: string; readonly type: 'checkpoint' } & KinetixPlatformer2dBox)
|
|
257
|
-
| ({ readonly id: string; readonly type: 'coin' } & KinetixPlatformer2dBox)
|
|
258
|
-
| ({ readonly id: string; readonly type: 'powerUp'; readonly kind: KinetixPlatformer2dPowerUpKind } & KinetixPlatformer2dBox)
|
|
259
|
-
| ({ readonly id: string; readonly type: 'carryItem'; readonly damage: number } & KinetixPlatformer2dBox)
|
|
260
|
-
| ({ readonly id: string; readonly type: 'warpPipe'; readonly targetId: string } & KinetixPlatformer2dBox)
|
|
261
|
-
| ({ readonly id: string; readonly type: 'climbRegion' } & KinetixPlatformer2dBox)
|
|
262
|
-
| ({ readonly id: string; readonly type: 'waterRegion' } & KinetixPlatformer2dBox)
|
|
263
|
-
| { readonly id: string; readonly type: string; readonly [key: string]: unknown };
|
|
264
|
-
|
|
265
|
-
export type KinetixPlatformer2dPowerUpKind = 'heal' | 'maxHealthUp' | 'doubleJump';
|
|
266
|
-
export type KinetixPlatformer2dBonusContents = 'coin' | 'random' | KinetixPlatformer2dPowerUpKind;
|
|
267
|
-
|
|
268
|
-
export interface KinetixPlatformer2dSlopeTile {
|
|
269
|
-
readonly tileId: number;
|
|
270
|
-
readonly dir: 'up-right' | 'up-left';
|
|
271
|
-
}
|
|
272
|
-
export interface KinetixPlatformer2dBonusBlock {
|
|
273
|
-
readonly x: number; readonly y: number;
|
|
274
|
-
readonly contents: KinetixPlatformer2dBonusContents;
|
|
275
|
-
readonly count: number;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export interface KinetixPlatformer2dLevel extends Readonly<Record<string, unknown>> {
|
|
279
|
-
readonly format: 'rundot.kinetix-platformer2d-level.v2';
|
|
280
|
-
readonly playerCapacity: number;
|
|
281
|
-
readonly tileGrid: {
|
|
282
|
-
readonly width: number; readonly height: number; readonly tileSize: number;
|
|
283
|
-
readonly tilesBase64: string; readonly solidTileIds: readonly number[];
|
|
284
|
-
};
|
|
285
|
-
readonly entities: readonly KinetixPlatformer2dLevelEntity[];
|
|
286
|
-
/** Absent means `false`: implicit solid walls at the level's left and right edges. */
|
|
287
|
-
readonly openBounds?: boolean;
|
|
288
|
-
readonly slopeTiles?: readonly KinetixPlatformer2dSlopeTile[];
|
|
289
|
-
readonly breakableTileIds?: readonly number[];
|
|
290
|
-
readonly bonusBlocks?: readonly KinetixPlatformer2dBonusBlock[];
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export type KinetixPlatformerLevelErrorCode =
|
|
294
|
-
| 'KINETIX_PLATFORMER_LEVEL_INVALID'
|
|
295
|
-
| 'KINETIX_PLATFORMER_LEVEL_NONCANONICAL'
|
|
296
|
-
| 'KINETIX_PLATFORMER_LEVEL_SIZE_LIMIT';
|
|
297
|
-
|
|
298
|
-
export class KinetixPlatformerLevelError extends Error {
|
|
299
|
-
readonly code: KinetixPlatformerLevelErrorCode;
|
|
300
|
-
/** JSON-pointer-style path to the offending field, `''` for whole-level failures. */
|
|
301
|
-
readonly path: string;
|
|
302
|
-
readonly expected: string;
|
|
303
|
-
readonly actual: unknown;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
export class KinetixPlatformerRegistryError extends Error {
|
|
307
|
-
readonly code:
|
|
308
|
-
| 'KINETIX_PLATFORMER_REGISTRY_INVALID'
|
|
309
|
-
| 'KINETIX_PLATFORMER_REGISTRY_UNBOUND'
|
|
310
|
-
| 'KINETIX_PLATFORMER_SOURCE_MANIFEST_INVALID';
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
export interface KinetixPlatformer2dEntityValidationContext {
|
|
314
|
-
/** Throws a structured error; `path` is relative to this entity (e.g. `'/pushX'`). */
|
|
315
|
-
readonly fail: (path: string, expected: string, actual: unknown) => never;
|
|
316
|
-
readonly level: KinetixPlatformer2dLevel;
|
|
317
|
-
readonly grid: KinetixPlatformer2dLevel['tileGrid'];
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export interface KinetixPlatformer2dEntityDescriptor {
|
|
321
|
-
readonly type: string;
|
|
322
|
-
readonly keys: readonly string[];
|
|
323
|
-
readonly validate: (entity: never, ctx: KinetixPlatformer2dEntityValidationContext) => void;
|
|
324
|
-
readonly behavior: {
|
|
325
|
-
readonly init?: (entity: never, ctx: never) => unknown;
|
|
326
|
-
readonly step?: (entityState: never, ctx: never) => void;
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
export interface KinetixPlatformer2dRegistry {
|
|
331
|
-
readonly hasExtensions: boolean;
|
|
332
|
-
readonly extensionTypes: readonly string[];
|
|
333
|
-
readonly byType: ReadonlyMap<string, KinetixPlatformer2dEntityDescriptor>;
|
|
334
|
-
readonly descriptors: readonly KinetixPlatformer2dEntityDescriptor[];
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
export function createKinetixPlatformer2dRegistry(
|
|
338
|
-
extensions?: readonly KinetixPlatformer2dEntityDescriptor[],
|
|
339
|
-
): KinetixPlatformer2dRegistry;
|
|
340
|
-
export const kinetixPlatformer2dBuiltInEntityTypes: readonly string[];
|
|
341
|
-
|
|
342
|
-
export const KINETIX_PLATFORMER2D_LEVEL_FORMAT: 'rundot.kinetix-platformer2d-level.v2';
|
|
343
|
-
export const KINETIX_PLATFORMER2D_LEVEL_FORMAT_V1: 'rundot.kinetix-platformer2d-level.v1';
|
|
344
|
-
export const KINETIX_PLATFORMER2D_SPENT_TILE_ID: 255;
|
|
345
|
-
|
|
346
|
-
export function migrateKinetixPlatformer2dLevel(level: unknown): KinetixPlatformer2dLevel;
|
|
347
|
-
export function kinetixPlatformer2dLevelOpenBounds(level: KinetixPlatformer2dLevel): boolean;
|
|
348
|
-
export function kinetixPlatformer2dLevelSlopeTiles(
|
|
349
|
-
level: KinetixPlatformer2dLevel,
|
|
350
|
-
): readonly KinetixPlatformer2dSlopeTile[];
|
|
351
|
-
export function kinetixPlatformer2dLevelBreakableTileIds(
|
|
352
|
-
level: KinetixPlatformer2dLevel,
|
|
353
|
-
): readonly number[];
|
|
354
|
-
export function kinetixPlatformer2dLevelBonusBlocks(
|
|
355
|
-
level: KinetixPlatformer2dLevel,
|
|
356
|
-
): readonly KinetixPlatformer2dBonusBlock[];
|
|
357
|
-
|
|
358
|
-
export interface KinetixPlatformer2dSessionSetup {
|
|
359
|
-
readonly runtimeIdentity: KinetixRuntimeIdentity;
|
|
360
|
-
readonly sessionConfigBytes: Uint8Array;
|
|
361
|
-
readonly sessionConfigDigest: string;
|
|
362
|
-
readonly level: KinetixAuxiliaryAssetDeclaration;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export const kinetixPlatformer2dDeterministicAbi: 'rundot.kinetix-platformer2d-js.v2';
|
|
366
|
-
export const MAX_KINETIX_PLATFORMER2D_LEVEL_BYTES: 102_400;
|
|
367
|
-
export const kinetixPlatformer2dSessionConfigSchema: import('./authoring').KinetixSessionConfigSchema;
|
|
368
|
-
|
|
369
|
-
export function defineKinetixPlatformer2dPack(
|
|
370
|
-
value: KinetixPlatformer2dPack | KinetixPlatformer2dPackV1,
|
|
371
|
-
): Readonly<KinetixPlatformer2dPack>;
|
|
372
164
|
export interface KinetixRuntimeModuleManifestEntry {
|
|
373
165
|
readonly path: string;
|
|
374
|
-
/** Lowercase sha256 hex of the module's bytes. */
|
|
375
166
|
readonly digest: string;
|
|
376
167
|
}
|
|
377
|
-
|
|
378
|
-
export interface KinetixPlatformer2dIdentityOptions {
|
|
379
|
-
readonly registry?: KinetixPlatformer2dRegistry;
|
|
380
|
-
/**
|
|
381
|
-
* Required when `registry` carries extensions — custom entity behavior must be
|
|
382
|
-
* folded into identity or peers running different code would silently diverge.
|
|
383
|
-
* Omitting it throws `KINETIX_PLATFORMER_REGISTRY_UNBOUND`.
|
|
384
|
-
*/
|
|
385
|
-
readonly registryManifest?: readonly KinetixRuntimeModuleManifestEntry[];
|
|
386
|
-
readonly sourceManifest?: readonly KinetixRuntimeModuleManifestEntry[];
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export const kinetixPlatformer2dSourceManifest: readonly KinetixRuntimeModuleManifestEntry[];
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* The reducer's xorshift32 stream, exported so presentation can re-derive sim
|
|
393
|
-
* rolls (predicted particles, cosmetic effects) without forking constants.
|
|
394
|
-
* Seeded and stepped exactly like syncplay's `DeterministicRandom`; the parity
|
|
395
|
-
* vector is golden-pinned in tests. Wrap the seed as `{ rng: seedRng(seed) }` —
|
|
396
|
-
* the steppers mutate `state.rng` in place.
|
|
397
|
-
*/
|
|
398
|
-
export function seedRng(seed?: number): number;
|
|
399
|
-
export function nextRngUint32(state: { rng: number }): number;
|
|
400
|
-
export function rngInt(state: { rng: number }, minInclusive: number, maxInclusive: number): number;
|
|
401
|
-
|
|
402
|
-
export function deriveKinetixPlatformer2dRuntimeIdentity(
|
|
403
|
-
pack: KinetixPlatformer2dPack,
|
|
404
|
-
options?: KinetixPlatformer2dIdentityOptions,
|
|
405
|
-
): KinetixRuntimeIdentity;
|
|
406
|
-
export function encodeKinetixPlatformer2dLevel(
|
|
407
|
-
level: unknown,
|
|
408
|
-
options: { readonly maxBytes: number; readonly registry?: KinetixPlatformer2dRegistry },
|
|
409
|
-
): Uint8Array;
|
|
410
|
-
export function decodeKinetixPlatformer2dLevel(
|
|
411
|
-
bytes: Uint8Array,
|
|
412
|
-
options?: { readonly registry?: KinetixPlatformer2dRegistry },
|
|
413
|
-
): Readonly<KinetixPlatformer2dLevel>;
|
|
414
|
-
export function describeKinetixPlatformer2dLevel(
|
|
415
|
-
bytes: Uint8Array,
|
|
416
|
-
options?: { readonly registry?: KinetixPlatformer2dRegistry },
|
|
417
|
-
): Readonly<KinetixAuxiliaryAssetDeclaration>;
|
|
418
|
-
export function createKinetixPlatformer2dSession(options: {
|
|
419
|
-
readonly pack: KinetixPlatformer2dPack;
|
|
420
|
-
readonly levelEntryId: string;
|
|
421
|
-
readonly levelBytes: Uint8Array;
|
|
422
|
-
readonly playerCount: number;
|
|
423
|
-
readonly seed: number;
|
|
424
|
-
readonly registry?: KinetixPlatformer2dRegistry;
|
|
425
|
-
readonly registryManifest?: readonly KinetixRuntimeModuleManifestEntry[];
|
|
426
|
-
}): Readonly<KinetixPlatformer2dSessionSetup>;
|
|
427
|
-
|
|
428
|
-
export type KinetixPlatformer2dPhase = 'countdown' | 'playing' | 'results';
|
|
429
|
-
export interface KinetixPlatformer2dEvent {
|
|
430
|
-
readonly eventId: string; readonly frame: number; readonly sequence: number;
|
|
431
|
-
readonly payload: Readonly<Record<string, string | number | boolean>>;
|
|
432
|
-
readonly predictionState: 'synced'; readonly dedupeKey: string;
|
|
433
|
-
readonly notHashedKeys: readonly string[]; readonly deliveryScope: 'all';
|
|
434
|
-
}
|
|
435
|
-
export interface KinetixPlatformer2dPlayerProjection {
|
|
436
|
-
readonly slot: number; readonly x: number; readonly y: number;
|
|
437
|
-
readonly vx: number; readonly vy: number; readonly health: number;
|
|
438
|
-
readonly grounded: boolean; readonly groundId: string | null;
|
|
439
|
-
readonly dead: boolean; readonly finished: boolean;
|
|
440
|
-
readonly maxHealth: number; readonly score: number; readonly coins: number;
|
|
441
|
-
readonly facing: -1 | 1; readonly crouching: boolean; readonly climbing: boolean;
|
|
442
|
-
readonly swimming: boolean; readonly groundPounding: boolean;
|
|
443
|
-
readonly carryingId: string | null; readonly checkpointId: string | null;
|
|
444
|
-
readonly doubleJumpCharges: number;
|
|
445
|
-
}
|
|
446
|
-
export interface KinetixPlatformer2dProjection {
|
|
447
|
-
readonly frame: number; readonly phase: KinetixPlatformer2dPhase; readonly phaseFrame: number;
|
|
448
|
-
readonly players: readonly KinetixPlatformer2dPlayerProjection[];
|
|
449
|
-
readonly platforms: readonly Readonly<Record<string, unknown>>[];
|
|
450
|
-
readonly stompables: readonly Readonly<Record<string, unknown>>[];
|
|
451
|
-
readonly enemies: readonly Readonly<Record<string, unknown>>[];
|
|
452
|
-
readonly hazards: readonly Readonly<Record<string, unknown>>[];
|
|
453
|
-
readonly pickups: readonly Readonly<Record<string, unknown>>[];
|
|
454
|
-
readonly spawnedPickups: readonly Readonly<Record<string, unknown>>[];
|
|
455
|
-
readonly carryItems: readonly Readonly<Record<string, unknown>>[];
|
|
456
|
-
/** Per-extension-entity behavior state (`behavior.init` return values), sorted by entity id. */
|
|
457
|
-
readonly extensions: readonly { readonly id: string; readonly state: unknown }[];
|
|
458
|
-
/** Sparse `[tileIndex, tileId]` pairs; apply with `applyKinetixPlatformer2dTileOverrides`. */
|
|
459
|
-
readonly tileOverrides: readonly (readonly [number, number])[];
|
|
460
|
-
readonly events: readonly KinetixPlatformer2dEvent[];
|
|
461
|
-
}
|
|
462
|
-
export function createInstalledPlatformer2dRuntime(options: {
|
|
463
|
-
readonly pack: KinetixPlatformer2dPack;
|
|
464
|
-
readonly levelBytes: Uint8Array;
|
|
465
|
-
readonly identity: KinetixRuntimeIdentity;
|
|
466
|
-
readonly sessionConfigBytes: Uint8Array;
|
|
467
|
-
readonly registry?: KinetixPlatformer2dRegistry;
|
|
468
|
-
readonly registryManifest?: readonly KinetixRuntimeModuleManifestEntry[];
|
|
469
|
-
/** How often the full-state finiteness walk runs, in frames. Defaults to 60. */
|
|
470
|
-
readonly validateStateEveryFrames?: number;
|
|
471
|
-
}): KinetixRuntime<Readonly<Record<string, unknown>>, KinetixPlatformer2dInput, unknown, KinetixPlatformer2dProjection>;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
3
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
5
|
-
import { deriveKinetixPlatformer2dRuntimeIdentity } from '../src/kinetix-platformer2d-runtime.mjs';
|
|
6
5
|
|
|
7
6
|
function option(name) {
|
|
8
7
|
const index = process.argv.indexOf(`--${name}`);
|
|
@@ -32,18 +31,6 @@ function checkGeneratedProduct(root, game) {
|
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
const platformerPack = {
|
|
36
|
-
format: 'rundot.kinetix-platformer2d-pack.v1',
|
|
37
|
-
tickRate: 60,
|
|
38
|
-
player: { halfWidth: 6, halfHeight: 12, maxHealth: 2 },
|
|
39
|
-
movement: {
|
|
40
|
-
gravity: 900, maxFallSpeed: 420, runAcceleration: 1200, runDeceleration: 1600,
|
|
41
|
-
maxRunSpeed: 150, jumpSpeed: 330, jumpCutGravityMultiplier: 2.5,
|
|
42
|
-
coyoteFrames: 6, jumpBufferFrames: 6, dropThroughFrames: 8,
|
|
43
|
-
},
|
|
44
|
-
damage: { invulnerabilityFrames: 45, respawnFrames: 60, stompBounceSpeed: 220 },
|
|
45
|
-
match: { countdownFrames: 60, resultsFrames: 180, goalMode: 'all-players' },
|
|
46
|
-
};
|
|
47
34
|
const racingRoot = resolve(option('racing-root'));
|
|
48
35
|
const latticeRoot = resolve(option('lattice-root'));
|
|
49
36
|
checkGeneratedProduct(racingRoot, 'racing');
|
|
@@ -51,7 +38,6 @@ checkGeneratedProduct(latticeRoot, 'lattice');
|
|
|
51
38
|
const digests = {
|
|
52
39
|
racing: runtimeIdentity(racingRoot, 'src/kinetix/racing.product.generated.json'),
|
|
53
40
|
lattice: runtimeIdentity(latticeRoot, 'src/kinetix/lattice.product.generated.json'),
|
|
54
|
-
platformerEngineIdentity: deriveKinetixPlatformer2dRuntimeIdentity(platformerPack).engineIdentityHash,
|
|
55
41
|
};
|
|
56
42
|
|
|
57
43
|
writeFileSync(option('out'), `${JSON.stringify(digests, null, 2)}\n`);
|