@genex-ai/multiplayer 0.17.1 → 0.17.6
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/PROTOCOL.md +688 -7
- package/README.md +263 -0
- package/dist/authority/fast-udp.d.ts +13 -0
- package/dist/authority/fast-udp.js +52 -0
- package/dist/authority/fast-webtransport.d.ts +45 -0
- package/dist/authority/fast-webtransport.js +124 -0
- package/dist/authority/index.d.ts +2333 -0
- package/dist/authority/index.js +758 -0
- package/dist/authority/module.d.ts +1 -0
- package/dist/authority/module.js +12 -0
- package/dist/bridge-authority.d.ts +100 -0
- package/dist/bridge-authority.global.js +14 -0
- package/dist/bridge-authority.js +286 -0
- package/dist/bridge-core-Dhc73GC8.d.ts +27 -0
- package/dist/bridge.d.ts +26 -20
- package/dist/bridge.global.js +7 -7
- package/dist/bridge.js +17 -48
- package/dist/chunk-4R7C2TLH.js +68 -0
- package/dist/chunk-5JGRFT2N.js +3646 -0
- package/dist/{chunk-3ZUPU3GT.js → chunk-7QVOIHJF.js} +13 -122
- package/dist/chunk-K3SF5VDS.js +27 -0
- package/dist/chunk-UX3F6YTV.js +143 -0
- package/dist/chunk-Y6XHXDIK.js +6617 -0
- package/dist/{chunk-C52XFIB6.js → chunk-ZSWF4VCN.js} +141 -4
- package/dist/fast-client-CZ41v-oB.d.ts +465 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +3 -2
- package/dist/join-CGoInvUM.d.ts +6 -0
- package/dist/module-DqtFb5wA.d.ts +1436 -0
- package/dist/physics-predict-49udN3a1.d.ts +1208 -0
- package/dist/protocol.d.ts +137 -2
- package/dist/protocol.js +33 -1
- package/package.json +33 -3
|
@@ -0,0 +1,1436 @@
|
|
|
1
|
+
/** Preset names. Append-only: a name, once published, keeps its step function forever. */
|
|
2
|
+
declare const Preset: {
|
|
3
|
+
/** Planar movement on x/y with an aim angle: the server-authority spike's arena. */
|
|
4
|
+
readonly TOPDOWN: "topdown";
|
|
5
|
+
/** Source-engine character movement through a static box-and-ramp world, z up (§17.7). */
|
|
6
|
+
readonly FIRSTPERSON: "firstperson";
|
|
7
|
+
/** An arcade car (Rocket League's ground and air handling, kept level) through the same world (§17.7). */
|
|
8
|
+
readonly VEHICLE: "vehicle";
|
|
9
|
+
};
|
|
10
|
+
type PresetName = (typeof Preset)[keyof typeof Preset];
|
|
11
|
+
/** The longest step a preset simulates; a longer dt (a stalled tab, a hostile value) is cut to it. */
|
|
12
|
+
declare const PRESET_MAX_DT_S = 0.25;
|
|
13
|
+
/** `topdown` parameters. */
|
|
14
|
+
interface TopdownParams {
|
|
15
|
+
/** Movement speed, world units per second. */
|
|
16
|
+
speed: number;
|
|
17
|
+
/** Half the arena's side: x and y stay within [-arena, arena]. */
|
|
18
|
+
arena: number;
|
|
19
|
+
}
|
|
20
|
+
declare const TOPDOWN_DEFAULTS: Readonly<TopdownParams>;
|
|
21
|
+
/** One parameter's accepted range: a finite number in [min, max], above 0 when `positive`, and an
|
|
22
|
+
* integer when `integer` (absent = any number). */
|
|
23
|
+
interface PresetParamRange {
|
|
24
|
+
min: number;
|
|
25
|
+
max: number;
|
|
26
|
+
positive: boolean;
|
|
27
|
+
integer?: true;
|
|
28
|
+
}
|
|
29
|
+
/** The fields a `topdown` step reads and writes. */
|
|
30
|
+
interface TopdownBody {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
/** Aim, radians. */
|
|
34
|
+
yaw: number;
|
|
35
|
+
}
|
|
36
|
+
/** The input fields a `topdown` step reads. */
|
|
37
|
+
interface TopdownInput {
|
|
38
|
+
moveX?: unknown;
|
|
39
|
+
moveY?: unknown;
|
|
40
|
+
yaw?: unknown;
|
|
41
|
+
}
|
|
42
|
+
/** Whether `name` is a known preset. */
|
|
43
|
+
declare function isPresetName(name: unknown): name is PresetName;
|
|
44
|
+
/** Whether `params` is acceptable for preset `name`: absent, or an object whose every key is one of
|
|
45
|
+
* the preset's parameters with a finite number in its range. */
|
|
46
|
+
declare function presetParamsValid(name: PresetName, params: unknown): boolean;
|
|
47
|
+
/** Preset `name`'s parameters from a module's `preset.params`: its defaults for anything unset or
|
|
48
|
+
* invalid, key by key in the order of its range table. */
|
|
49
|
+
declare function resolvePresetParams(name: PresetName, params: unknown): Record<string, number>;
|
|
50
|
+
/** `topdown` parameters from a module's `preset.params`: defaults for anything unset or invalid. */
|
|
51
|
+
declare function resolveTopdownParams(params: unknown): TopdownParams;
|
|
52
|
+
/**
|
|
53
|
+
* One fixed `topdown` step. Pure: returns the next body.
|
|
54
|
+
* - moveX, moveY: non-finite → 0, clamped to [-1, 1], float32; a vector longer than 1 is scaled to 1;
|
|
55
|
+
* - dt: non-finite or ≤ 0 → 0, capped at {@link PRESET_MAX_DT_S};
|
|
56
|
+
* - x' = fround(clamp(x + moveX * scale * speed * dt, -arena, arena)), y' likewise;
|
|
57
|
+
* - yaw' = the input yaw wrapped to [-π, π) as float32, or the previous yaw when it is not usable.
|
|
58
|
+
*/
|
|
59
|
+
declare function stepTopdown(body: TopdownBody, input: TopdownInput, dt: number, params: TopdownParams): TopdownBody;
|
|
60
|
+
/** `firstperson` parameters (§17.7's table, in its order). Each is used as float32. */
|
|
61
|
+
interface FirstpersonParams {
|
|
62
|
+
/** Run speed, units/s, before the body's `cap`. */
|
|
63
|
+
maxSpeed: number;
|
|
64
|
+
/** Speed multiplier while walk is held. */
|
|
65
|
+
walkSpeed: number;
|
|
66
|
+
/** Speed multiplier while ducking (wins over walk). */
|
|
67
|
+
crouchSpeed: number;
|
|
68
|
+
accelerate: number;
|
|
69
|
+
airAccelerate: number;
|
|
70
|
+
/** Air wish speed is clamped to this before the projection (what makes air-strafing work). */
|
|
71
|
+
airMaxWishSpeed: number;
|
|
72
|
+
friction: number;
|
|
73
|
+
/** Below this speed friction acts as if at this speed. */
|
|
74
|
+
stopSpeed: number;
|
|
75
|
+
/** Units/s² along −z. */
|
|
76
|
+
gravity: number;
|
|
77
|
+
/** The `vz` a jump sets. */
|
|
78
|
+
jumpImpulse: number;
|
|
79
|
+
/** Each velocity axis is clamped to ±this. */
|
|
80
|
+
maxVelocity: number;
|
|
81
|
+
/** Half the side of the hull's square footprint. */
|
|
82
|
+
radius: number;
|
|
83
|
+
standHeight: number;
|
|
84
|
+
crouchHeight: number;
|
|
85
|
+
/** Seconds to duck fully, and to stand back up. */
|
|
86
|
+
duckTime: number;
|
|
87
|
+
/** Highest ledge walked up without a jump. */
|
|
88
|
+
stepHeight: number;
|
|
89
|
+
/** Least z of a ramp top's unit normal that can be stood on. */
|
|
90
|
+
standable: number;
|
|
91
|
+
/** How far below the feet ground is found. */
|
|
92
|
+
groundTrace: number;
|
|
93
|
+
/** How far above the feet ground is found (a hull sunk that far is lifted out). */
|
|
94
|
+
groundSnap: number;
|
|
95
|
+
/** Clearance kept from a surface (Source's DIST_EPSILON). */
|
|
96
|
+
skin: number;
|
|
97
|
+
/** Largest |pitch|, radians. */
|
|
98
|
+
pitchLimit: number;
|
|
99
|
+
/** Button masks: any shared bit counts as held; 0 disables. */
|
|
100
|
+
jumpBit: number;
|
|
101
|
+
duckBit: number;
|
|
102
|
+
walkBit: number;
|
|
103
|
+
/** Sub-steps per step, an integer in [1, 8]. */
|
|
104
|
+
substeps: number;
|
|
105
|
+
/** The collision world's hash ({@link firstpersonWorld}), written by the sandbox. */
|
|
106
|
+
world: number;
|
|
107
|
+
}
|
|
108
|
+
/** 32-bit FNV-1a of the empty world: two zero counts (§17.7). */
|
|
109
|
+
declare const EMPTY_WORLD_HASH = 2615243109;
|
|
110
|
+
declare const FIRSTPERSON_DEFAULTS: Readonly<FirstpersonParams>;
|
|
111
|
+
/** `firstperson` parameters from a module's `preset.params`: defaults for anything unset or invalid. */
|
|
112
|
+
declare function resolveFirstpersonParams(params: unknown): FirstpersonParams;
|
|
113
|
+
/** `mstate` bits a `firstperson` step keeps. */
|
|
114
|
+
declare const MoveState: {
|
|
115
|
+
readonly ON_GROUND: 1;
|
|
116
|
+
readonly DUCKING: 2;
|
|
117
|
+
readonly JUMP_HELD: 4;
|
|
118
|
+
};
|
|
119
|
+
/** The fields a `firstperson` step reads (`cap` too, which it never writes). */
|
|
120
|
+
interface FirstpersonBody {
|
|
121
|
+
x: number;
|
|
122
|
+
y: number;
|
|
123
|
+
z: number;
|
|
124
|
+
vx: number;
|
|
125
|
+
vy: number;
|
|
126
|
+
vz: number;
|
|
127
|
+
/** Aim, radians: stored only. */
|
|
128
|
+
yaw: number;
|
|
129
|
+
pitch: number;
|
|
130
|
+
/** 0 standing, 1 fully ducked. */
|
|
131
|
+
duck: number;
|
|
132
|
+
/** {@link MoveState} bits. */
|
|
133
|
+
mstate: number;
|
|
134
|
+
/** Speed multiplier the module writes (a weapon's or a wound's), [0, 4]. */
|
|
135
|
+
cap?: number;
|
|
136
|
+
}
|
|
137
|
+
/** What a `firstperson` step writes: every body field but `cap`. */
|
|
138
|
+
type FirstpersonStep = Omit<FirstpersonBody, 'cap'>;
|
|
139
|
+
/** The input fields a `firstperson` step reads. `moveX`/`moveY` are WORLD axes. */
|
|
140
|
+
interface FirstpersonInput {
|
|
141
|
+
moveX?: unknown;
|
|
142
|
+
moveY?: unknown;
|
|
143
|
+
yaw?: unknown;
|
|
144
|
+
pitch?: unknown;
|
|
145
|
+
buttons?: unknown;
|
|
146
|
+
}
|
|
147
|
+
/** A box: `[minX, minY, minZ, maxX, maxY, maxZ]`. */
|
|
148
|
+
type WorldBox = readonly [number, number, number, number, number, number];
|
|
149
|
+
/** A ramp: a box whose top rises along x (`axis` 0) or y (`axis` 1), from `lo` at that axis's min
|
|
150
|
+
* end to `hi` at its max end: `[minX, minY, minZ, maxX, maxY, maxZ, axis, lo, hi]`. */
|
|
151
|
+
type WorldRamp = readonly [number, number, number, number, number, number, number, number, number];
|
|
152
|
+
/** A world as a module and a game give it (§17.7): boxes, then ramps. */
|
|
153
|
+
interface FirstpersonWorldSpec {
|
|
154
|
+
boxes?: readonly (readonly number[])[];
|
|
155
|
+
ramps?: readonly (readonly number[])[];
|
|
156
|
+
}
|
|
157
|
+
/** A validated world ({@link firstpersonWorld}): frozen, every value a float32 (−0 read as 0). */
|
|
158
|
+
interface FirstpersonWorld {
|
|
159
|
+
readonly boxes: readonly WorldBox[];
|
|
160
|
+
readonly ramps: readonly WorldRamp[];
|
|
161
|
+
/** 32-bit FNV-1a over its counts and values (§17.7): what `params.world` must equal. */
|
|
162
|
+
readonly hash: number;
|
|
163
|
+
/** Whether the step visits only the solids a bounding-volume tree over their boxes says could
|
|
164
|
+
* matter (the results are bit-identical to visiting every one; only the cost changes). */
|
|
165
|
+
readonly broadphase: boolean;
|
|
166
|
+
}
|
|
167
|
+
declare const WorldLimits: {
|
|
168
|
+
readonly MAX_BOXES: 4096;
|
|
169
|
+
readonly MAX_RAMPS: 512;
|
|
170
|
+
/** Every world value's magnitude. */
|
|
171
|
+
readonly MAX_ABS: 1000000000;
|
|
172
|
+
};
|
|
173
|
+
/** Why a world was refused: the path of the first bad value. */
|
|
174
|
+
declare class WorldError extends RangeError {
|
|
175
|
+
readonly path: string;
|
|
176
|
+
constructor(path: string, message: string);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Validates a collision world (§17.7) and returns it frozen, with its hash. Refused (a
|
|
180
|
+
* {@link WorldError}) unless it has at most 4096 boxes and 512 ramps, each exactly six (box) or
|
|
181
|
+
* nine (ramp) numbers, every one a float32 of magnitude at most 1e9, min < max on every axis, and
|
|
182
|
+
* each ramp `axis` 0 or 1 with `minZ ≤ lo`, `minZ ≤ hi` and `maxZ = max(lo, hi)`. `undefined` is
|
|
183
|
+
* the empty world; a missing `boxes` or `ramps` is none, and other keys are ignored. `broadphase`
|
|
184
|
+
* (default true) builds the bounding-volume tree the step filters with; it never changes a result. A
|
|
185
|
+
* world this function returned is returned as is.
|
|
186
|
+
*/
|
|
187
|
+
declare function firstpersonWorld(raw: unknown, options?: {
|
|
188
|
+
broadphase?: boolean;
|
|
189
|
+
}): FirstpersonWorld;
|
|
190
|
+
/** The empty world: no solids (a body in it falls until z reaches −1e9). */
|
|
191
|
+
declare const EMPTY_WORLD: FirstpersonWorld;
|
|
192
|
+
/** A solid posed for one tick beside the static world (a lift at its pose, a head slab): an
|
|
193
|
+
* axis-aligned box `[min, max]`; a box of half extents `half` about centre `c` turned about z by the
|
|
194
|
+
* angle whose sine and cosine are given (`obox`); or an upright disc of `radius` about `c`, `half` its
|
|
195
|
+
* half height. Visited after the static solids, in order, never by the broad phase's tree. */
|
|
196
|
+
type PosedSolid = {
|
|
197
|
+
readonly kind: 'box';
|
|
198
|
+
readonly min: readonly [number, number, number];
|
|
199
|
+
readonly max: readonly [number, number, number];
|
|
200
|
+
} | {
|
|
201
|
+
readonly kind: 'obox';
|
|
202
|
+
readonly c: readonly [number, number, number];
|
|
203
|
+
readonly half: readonly [number, number, number];
|
|
204
|
+
readonly sin: number;
|
|
205
|
+
readonly cos: number;
|
|
206
|
+
} | {
|
|
207
|
+
readonly kind: 'disc';
|
|
208
|
+
readonly c: readonly [number, number, number];
|
|
209
|
+
readonly radius: number;
|
|
210
|
+
readonly half: number;
|
|
211
|
+
};
|
|
212
|
+
/** Posed solids a tick may hold at most (32 lifts and a head slab for each of 64 seats). */
|
|
213
|
+
declare const POSED_MAX = 96;
|
|
214
|
+
/** The step's view of a tick's posed solids ({@link posedWorld}): one flat table per field. `min*`
|
|
215
|
+
* and `max*` are each solid's bounds on x, y and z (an obox's or a disc's box around it). */
|
|
216
|
+
interface PosedWorld {
|
|
217
|
+
readonly count: number;
|
|
218
|
+
readonly kind: Uint8Array;
|
|
219
|
+
readonly minX: Float64Array;
|
|
220
|
+
readonly minY: Float64Array;
|
|
221
|
+
readonly minZ: Float64Array;
|
|
222
|
+
readonly maxX: Float64Array;
|
|
223
|
+
readonly maxY: Float64Array;
|
|
224
|
+
readonly maxZ: Float64Array;
|
|
225
|
+
readonly cx: Float64Array;
|
|
226
|
+
readonly cy: Float64Array;
|
|
227
|
+
readonly hx: Float64Array;
|
|
228
|
+
readonly hy: Float64Array;
|
|
229
|
+
readonly sin: Float64Array;
|
|
230
|
+
readonly cos: Float64Array;
|
|
231
|
+
readonly radius: Float64Array;
|
|
232
|
+
}
|
|
233
|
+
/** A tick's posed solids compiled for the step, in the order given. */
|
|
234
|
+
declare function posedWorld(solids: readonly PosedSolid[]): PosedWorld;
|
|
235
|
+
/** No posed solids. */
|
|
236
|
+
declare const NO_POSED: PosedWorld;
|
|
237
|
+
/** A seat's body as the lift slots read it. */
|
|
238
|
+
interface HullBody {
|
|
239
|
+
readonly x: number;
|
|
240
|
+
readonly y: number;
|
|
241
|
+
readonly z: number;
|
|
242
|
+
readonly vz?: number;
|
|
243
|
+
readonly duck?: number;
|
|
244
|
+
}
|
|
245
|
+
/** What the lift slots ask of a preset's hull, against the world and a tick's posed solids. */
|
|
246
|
+
interface HullOps {
|
|
247
|
+
/** The posed solid (its index) the hull stands on: the highest standable top under it in
|
|
248
|
+
* [z − groundTrace, z + groundSnap] (`checkGround`'s test) when that top is a posed solid's (the
|
|
249
|
+
* first such in order), else −1; −1 too while rising (`vz > 0.1`). */
|
|
250
|
+
support(body: HullBody, solids: readonly PosedSolid[]): number;
|
|
251
|
+
/** Whether the hull is more than skin inside posed solid `k` (§17.7 *Starting inside*'s `inside`). */
|
|
252
|
+
inside(body: HullBody, solids: readonly PosedSolid[], k: number): boolean;
|
|
253
|
+
/** §17.7 *Starting inside* as `slideMove` applies it, with no move: up to four times, out through the
|
|
254
|
+
* face a zero-length sweep reports, by its depth then skin. `inside`: still inside some solid. */
|
|
255
|
+
pushOut(body: HullBody, solids: readonly PosedSolid[]): {
|
|
256
|
+
x: number;
|
|
257
|
+
y: number;
|
|
258
|
+
z: number;
|
|
259
|
+
inside: boolean;
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/** The preset's hull among `world` and a tick's posed solids. */
|
|
263
|
+
declare function hullOps(preset: 'firstperson' | 'vehicle', params: Readonly<Record<string, number>>, world?: FirstpersonWorld): HullOps;
|
|
264
|
+
/**
|
|
265
|
+
* One fixed `firstperson` step (§17.7) through `world` (default: the empty world). Pure: returns the
|
|
266
|
+
* next `x y z vx vy vz yaw pitch duck mstate`; the body's `cap` is read, never written. `world` must
|
|
267
|
+
* be the world whose hash is `params.world` for the result to be the server's; the step itself does
|
|
268
|
+
* not compare them.
|
|
269
|
+
*/
|
|
270
|
+
declare function stepFirstperson(body: Partial<FirstpersonBody>, input: FirstpersonInput, dt: number, params: FirstpersonParams, world?: FirstpersonWorld, posed?: PosedWorld): FirstpersonStep;
|
|
271
|
+
/** `vehicle` parameters (§17.7's table, in its order). Each is used as float32 but the masks,
|
|
272
|
+
* `substeps` and `world`. */
|
|
273
|
+
interface VehicleParams {
|
|
274
|
+
/** Top speed on the throttle, units/s, before the body's `cap`. */
|
|
275
|
+
maxSpeed: number;
|
|
276
|
+
/** Throttle acceleration, units/s². */
|
|
277
|
+
accel: number;
|
|
278
|
+
/** Deceleration while the throttle opposes the motion. */
|
|
279
|
+
brake: number;
|
|
280
|
+
/** Deceleration with no throttle, and down to the throttle's speed when above it without boost. */
|
|
281
|
+
coast: number;
|
|
282
|
+
/** Boost's acceleration along the heading, on top of the throttle's. */
|
|
283
|
+
boostAccel: number;
|
|
284
|
+
/** Top speed under boost, before `cap`. */
|
|
285
|
+
boostMaxSpeed: number;
|
|
286
|
+
/** Fuel burned per second of boost (a full tank is 1). */
|
|
287
|
+
boostBurn: number;
|
|
288
|
+
/** Deceleration of the sideways speed on the ground, units/s². */
|
|
289
|
+
grip: number;
|
|
290
|
+
/** The same, while drift is held. */
|
|
291
|
+
driftGrip: number;
|
|
292
|
+
/** Yaw rate at full steer on the ground, rad/s, at or above `turnSpeed`. */
|
|
293
|
+
turnRate: number;
|
|
294
|
+
/** The forward speed at which the full turn rate is reached. */
|
|
295
|
+
turnSpeed: number;
|
|
296
|
+
/** How fast the yaw rate follows the steering on the ground, rad/s². */
|
|
297
|
+
spinAccel: number;
|
|
298
|
+
/** Yaw rate at full steer in the air, rad/s. */
|
|
299
|
+
airTurnRate: number;
|
|
300
|
+
/** How fast the yaw rate follows the steering in the air, rad/s². */
|
|
301
|
+
airSpinAccel: number;
|
|
302
|
+
/** Units/s² along −z. */
|
|
303
|
+
gravity: number;
|
|
304
|
+
/** The `vz` a jump sets. */
|
|
305
|
+
jumpImpulse: number;
|
|
306
|
+
/** Each velocity axis is clamped to ±this. */
|
|
307
|
+
maxVelocity: number;
|
|
308
|
+
/** Half the side of the hull's square footprint. */
|
|
309
|
+
radius: number;
|
|
310
|
+
/** Hull height. */
|
|
311
|
+
height: number;
|
|
312
|
+
/** Highest ledge driven up without a jump, and the deepest drop a sub-step follows down. */
|
|
313
|
+
stepHeight: number;
|
|
314
|
+
/** Least z of a ramp top's unit normal that can be driven on. */
|
|
315
|
+
standable: number;
|
|
316
|
+
/** How far below the hull's base ground is found. */
|
|
317
|
+
groundTrace: number;
|
|
318
|
+
/** How far above the hull's base ground is found. */
|
|
319
|
+
groundSnap: number;
|
|
320
|
+
/** Clearance kept from a surface. */
|
|
321
|
+
skin: number;
|
|
322
|
+
/** Button masks: any shared bit counts as held; 0 disables. */
|
|
323
|
+
jumpBit: number;
|
|
324
|
+
boostBit: number;
|
|
325
|
+
driftBit: number;
|
|
326
|
+
/** Sub-steps per step, an integer in [1, 8]. */
|
|
327
|
+
substeps: number;
|
|
328
|
+
/** The collision world's hash ({@link firstpersonWorld}), written by the sandbox. */
|
|
329
|
+
world: number;
|
|
330
|
+
}
|
|
331
|
+
declare const VEHICLE_DEFAULTS: Readonly<VehicleParams>;
|
|
332
|
+
/** `vehicle` parameters from a module's `preset.params`: defaults for anything unset or invalid. */
|
|
333
|
+
declare function resolveVehicleParams(params: unknown): VehicleParams;
|
|
334
|
+
/** The fields a `vehicle` step reads (`cap` too, which it never writes; it never touches `pitch`
|
|
335
|
+
* or `duck`). */
|
|
336
|
+
interface VehicleBody {
|
|
337
|
+
x: number;
|
|
338
|
+
y: number;
|
|
339
|
+
z: number;
|
|
340
|
+
vx: number;
|
|
341
|
+
vy: number;
|
|
342
|
+
vz: number;
|
|
343
|
+
/** Heading, radians, +yaw counterclockwise seen from above (z up): body state, not input. */
|
|
344
|
+
yaw: number;
|
|
345
|
+
/** Yaw rate, rad/s, positive toward +yaw, [−1000, 1000]. */
|
|
346
|
+
spin: number;
|
|
347
|
+
/** The boost tank, [0, 1]. The module writes it to refuel; the step only burns it. */
|
|
348
|
+
fuel: number;
|
|
349
|
+
/** {@link MoveState} bits: onGround and jumpHeld. */
|
|
350
|
+
mstate: number;
|
|
351
|
+
/** Multiplier of both top speeds the module writes, [0, 4]. */
|
|
352
|
+
cap?: number;
|
|
353
|
+
}
|
|
354
|
+
/** What a `vehicle` step writes: every body field but `cap`. */
|
|
355
|
+
type VehicleStep = Omit<VehicleBody, 'cap'>;
|
|
356
|
+
/** The input fields a `vehicle` step reads: `moveY` the throttle, `moveX` the steer (+1 turns
|
|
357
|
+
* right, toward −yaw), `buttons` jump, boost and drift. */
|
|
358
|
+
interface VehicleInput {
|
|
359
|
+
moveX?: unknown;
|
|
360
|
+
moveY?: unknown;
|
|
361
|
+
buttons?: unknown;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* One fixed `vehicle` step (§17.7) through `world` (default: the empty world). Pure: returns the
|
|
365
|
+
* next `x y z vx vy vz yaw spin fuel mstate`; the body's `cap` is read, never written, and `pitch`
|
|
366
|
+
* and `duck` are never touched. `world` must be the world whose hash is `params.world` for the
|
|
367
|
+
* result to be the server's; the step itself does not compare them.
|
|
368
|
+
*/
|
|
369
|
+
declare function stepVehicle(body: Partial<VehicleBody>, input: VehicleInput, dt: number, params: VehicleParams, world?: FirstpersonWorld, posed?: PosedWorld): VehicleStep;
|
|
370
|
+
/** A body field a preset step may read and write: the entity's float32 pose and velocity,
|
|
371
|
+
* firstperson's aim pitch, duck amount, movement bits and speed cap, and vehicle's yaw rate and
|
|
372
|
+
* boost fuel. */
|
|
373
|
+
type PresetField = 'x' | 'y' | 'z' | 'vx' | 'vy' | 'vz' | 'yaw' | 'pitch' | 'duck' | 'mstate' | 'cap' | 'spin' | 'fuel';
|
|
374
|
+
/** The body a preset step reads: any of the fields, a missing one read as the step says (0, or 1
|
|
375
|
+
* for `cap`). */
|
|
376
|
+
type PresetBody = Readonly<Partial<Record<PresetField, number>>>;
|
|
377
|
+
/** The input a preset step reads (sanitized by the step itself). */
|
|
378
|
+
type PresetInput = Readonly<Partial<Record<'moveX' | 'moveY' | 'moveZ' | 'yaw' | 'pitch' | 'buttons', unknown>>>;
|
|
379
|
+
/** One preset, type-erased: what a validator, a reconciler and a test server need to run it by
|
|
380
|
+
* name. `fields` are the body fields its step writes (and so the fields a client mirrors);
|
|
381
|
+
* `reads` the further fields it only reads (a client mirrors those too, so a replay sees the
|
|
382
|
+
* server's value); `world` whether it steps through a collision world. */
|
|
383
|
+
interface PresetDefinition {
|
|
384
|
+
readonly defaults: Readonly<Record<string, number>>;
|
|
385
|
+
readonly ranges: Readonly<Record<string, PresetParamRange>>;
|
|
386
|
+
readonly fields: readonly PresetField[];
|
|
387
|
+
readonly reads: readonly PresetField[];
|
|
388
|
+
readonly world: boolean;
|
|
389
|
+
step(body: PresetBody, input: PresetInput, dt: number, params: Readonly<Record<string, number>>, world?: FirstpersonWorld, posed?: PosedWorld): Record<string, number>;
|
|
390
|
+
}
|
|
391
|
+
/** Every preset by name. A new preset is one entry here (plus its §17.7 text and vectors). */
|
|
392
|
+
declare const PRESETS: {
|
|
393
|
+
readonly [N in PresetName]: PresetDefinition;
|
|
394
|
+
};
|
|
395
|
+
/** Preset `name`'s table entry; undefined for a name not in the table (never one reached through
|
|
396
|
+
* the prototype). */
|
|
397
|
+
declare function definitionOf(name: unknown): PresetDefinition | undefined;
|
|
398
|
+
/** One fixed step of preset `name` with already resolved `params` ({@link resolvePresetParams}),
|
|
399
|
+
* through `world` for a preset that has one (default: the empty world). Pure: returns the next
|
|
400
|
+
* values of the preset's `fields`. */
|
|
401
|
+
declare function stepPreset(name: PresetName, body: PresetBody, input: PresetInput, dt: number, params: Readonly<Record<string, number>>, world?: FirstpersonWorld, posed?: PosedWorld): Record<string, number>;
|
|
402
|
+
|
|
403
|
+
/** How a client draws other seats by default (PROTOCOL.md §17.11 *Drawing other seats*): `predicted` on
|
|
404
|
+
* your own clock (the Rocket League model), `separate` on the render timeline and moved only out of
|
|
405
|
+
* your entity, `timeline` on the render timeline alone. A lag-compensated room (§17.5) judges a shot
|
|
406
|
+
* where the shooter's render timeline drew the target, so its seats are drawn there. */
|
|
407
|
+
declare const ContactDrawing: {
|
|
408
|
+
readonly Predicted: "predicted";
|
|
409
|
+
readonly Separate: "separate";
|
|
410
|
+
readonly Timeline: "timeline";
|
|
411
|
+
};
|
|
412
|
+
type ContactDrawing = (typeof ContactDrawing)[keyof typeof ContactDrawing];
|
|
413
|
+
/** How a side contact's push is split (PROTOCOL.md §17.11 *Contact policy* step 4): `shared` half each
|
|
414
|
+
* (equal masses: a car shoves a car), `mover` by each seat's own closing speed (a player walking into
|
|
415
|
+
* another is stopped by it and never shoves it: a shooter's solid players). */
|
|
416
|
+
declare const ContactPush: {
|
|
417
|
+
readonly Shared: "shared";
|
|
418
|
+
readonly Mover: "mover";
|
|
419
|
+
};
|
|
420
|
+
type ContactPush = (typeof ContactPush)[keyof typeof ContactPush];
|
|
421
|
+
/** The genre presets: each a full table of settings (DESIGN's preset table). */
|
|
422
|
+
declare const ContactPreset: {
|
|
423
|
+
readonly Shooter: "shooter";
|
|
424
|
+
readonly Platformer: "platformer";
|
|
425
|
+
readonly VehicleArena: "vehicle-arena";
|
|
426
|
+
readonly PhysicsSandbox: "physics-sandbox";
|
|
427
|
+
readonly Party: "party";
|
|
428
|
+
};
|
|
429
|
+
type ContactPreset = (typeof ContactPreset)[keyof typeof ContactPreset];
|
|
430
|
+
/** What happens when one seat comes to rest on another's top. */
|
|
431
|
+
declare const TopPolicy: {
|
|
432
|
+
/** Rest on the head: a landing stands within `center` of the lower's half width and slides off by
|
|
433
|
+
* `slide` elsewhere; a resting seat holds while its centre is over the footprint; `ride`: carried. */
|
|
434
|
+
readonly Stand: "stand";
|
|
435
|
+
/** Frictionless: the upper seat is slid off within a bounded time. */
|
|
436
|
+
readonly Slide: "slide";
|
|
437
|
+
/** A stomp: the upper seat bounces off, and the module hears it. */
|
|
438
|
+
readonly Bounce: "bounce";
|
|
439
|
+
/** No collision at all. */
|
|
440
|
+
readonly Ghost: "ghost";
|
|
441
|
+
};
|
|
442
|
+
type TopPolicy = (typeof TopPolicy)[keyof typeof TopPolicy];
|
|
443
|
+
/** A seat's contact shape. */
|
|
444
|
+
declare const ContactShapeKind: {
|
|
445
|
+
/** A box turned with the seat's heading and laid on its support up (a vehicle). */
|
|
446
|
+
readonly Box: "box";
|
|
447
|
+
/** An upright axis-aligned square footprint (a `firstperson` character). */
|
|
448
|
+
readonly Hull: "hull";
|
|
449
|
+
};
|
|
450
|
+
type ContactShapeKind = (typeof ContactShapeKind)[keyof typeof ContactShapeKind];
|
|
451
|
+
/** A seat's contact shape, room units: a box's length along the heading, width across it and top
|
|
452
|
+
* above the base; a hull's half side and height. */
|
|
453
|
+
type ContactShape = {
|
|
454
|
+
readonly kind: typeof ContactShapeKind.Box;
|
|
455
|
+
readonly length: number;
|
|
456
|
+
readonly width: number;
|
|
457
|
+
readonly top: number;
|
|
458
|
+
} | {
|
|
459
|
+
readonly kind: typeof ContactShapeKind.Hull;
|
|
460
|
+
readonly radius: number;
|
|
461
|
+
readonly height: number;
|
|
462
|
+
};
|
|
463
|
+
/** A slide: the parting brought up to `speed` (units/s) by at most `step` a tick, `share` of it on the
|
|
464
|
+
* upper seat (the rest on the lower). `step` null: in one tick. */
|
|
465
|
+
interface SlideRule {
|
|
466
|
+
readonly speed: number;
|
|
467
|
+
readonly step: number | null;
|
|
468
|
+
readonly share: number;
|
|
469
|
+
}
|
|
470
|
+
/** A resolved stand (§17.11 step 6 `stand`): carried by the lower seat when `ride`; a landing stands
|
|
471
|
+
* only within `center` × the lower's half width (null: anywhere it touches), else slides by `slide`. */
|
|
472
|
+
interface StandRule {
|
|
473
|
+
readonly policy: typeof TopPolicy.Stand;
|
|
474
|
+
readonly ride: boolean;
|
|
475
|
+
readonly center: number | null;
|
|
476
|
+
readonly slide: SlideRule;
|
|
477
|
+
}
|
|
478
|
+
/** A resolved top rule. A bounce under `stompMin` closing speed slides by its `slide`. */
|
|
479
|
+
type TopRule = StandRule | ({
|
|
480
|
+
readonly policy: typeof TopPolicy.Slide;
|
|
481
|
+
} & SlideRule) | {
|
|
482
|
+
readonly policy: typeof TopPolicy.Bounce;
|
|
483
|
+
readonly stompMin: number;
|
|
484
|
+
readonly bounceSpeed: number;
|
|
485
|
+
readonly jumpBoost: number;
|
|
486
|
+
readonly slide: SlideRule;
|
|
487
|
+
} | {
|
|
488
|
+
readonly policy: typeof TopPolicy.Ghost;
|
|
489
|
+
};
|
|
490
|
+
/** A per-pair override: two seats, one carrying tag `a` and the other `b`, use `top`. */
|
|
491
|
+
interface PairRule {
|
|
492
|
+
readonly a: string;
|
|
493
|
+
readonly b: string;
|
|
494
|
+
readonly top: TopRule;
|
|
495
|
+
}
|
|
496
|
+
/** The resolved policy: every setting filled (from the module, else its preset's table). */
|
|
497
|
+
interface ContactPolicy {
|
|
498
|
+
readonly preset: ContactPreset;
|
|
499
|
+
readonly shape: ContactShape;
|
|
500
|
+
/** The hull the static world is grown by (the movement preset's radius and height) and its skin. */
|
|
501
|
+
readonly hull: {
|
|
502
|
+
readonly radius: number;
|
|
503
|
+
readonly height: number;
|
|
504
|
+
readonly skin: number;
|
|
505
|
+
};
|
|
506
|
+
/** How far under a base the support surface is found (the preset's `groundTrace`). */
|
|
507
|
+
readonly groundTrace: number;
|
|
508
|
+
/** A contact whose normal is at least this close to the lower seat's support up is a top contact;
|
|
509
|
+
* a lower seat whose support up's z is under it cannot be stood on. */
|
|
510
|
+
readonly supportCos: number;
|
|
511
|
+
/** The least closing speed of an axis in the entry-time test (units/s). */
|
|
512
|
+
readonly entrySpeed: number;
|
|
513
|
+
/** How recently a pair must have come down across the plane for the across axis to be its contact
|
|
514
|
+
* axis (seconds): an older overlap was there before the last pass. */
|
|
515
|
+
readonly entryWindow: number;
|
|
516
|
+
/** The share of the closing speed a side contact keeps. */
|
|
517
|
+
readonly restitution: number;
|
|
518
|
+
/** Pile passes a tick at most, and the depth a pair may stay inside another after the first. */
|
|
519
|
+
readonly passes: number;
|
|
520
|
+
readonly slop: number;
|
|
521
|
+
/** How far over a lower seat's top the upper seat is set (units). */
|
|
522
|
+
readonly roofSkin: number;
|
|
523
|
+
/** Each velocity axis a contact writes is clamped to ±this; null: not clamped. */
|
|
524
|
+
readonly maxVelocity: number | null;
|
|
525
|
+
/** A side contact stops both seats' `spin` (a vehicle's yaw rate). */
|
|
526
|
+
readonly stopSpin: boolean;
|
|
527
|
+
readonly top: TopRule;
|
|
528
|
+
/** The slide a top contact becomes when the lower seat's support cannot be stood on. */
|
|
529
|
+
readonly offWall: SlideRule;
|
|
530
|
+
readonly pairs: readonly PairRule[];
|
|
531
|
+
/** How a client draws other seats when the game gives no `seatDrawing`. */
|
|
532
|
+
readonly drawing: ContactDrawing;
|
|
533
|
+
/** How a side contact's push is split between the two seats (§17.11 step 4). */
|
|
534
|
+
readonly push: ContactPush;
|
|
535
|
+
/** How a dynamic module body resting on a seat's top is treated (§17.11 *Contact policy*, props). */
|
|
536
|
+
readonly headFriction: HeadFriction;
|
|
537
|
+
/** The nudge that slides a prop off a head under `headFriction: slide`: up to `speed` by `step` a tick. */
|
|
538
|
+
readonly propSlide: PropSlide;
|
|
539
|
+
}
|
|
540
|
+
/** How a prop on a seat's top is treated: `slide` off (its hull frictionless, nudged by `propSlide`),
|
|
541
|
+
* or `carry` (friction holds it while level; it slides past its friction angle). */
|
|
542
|
+
declare const HeadFriction: {
|
|
543
|
+
readonly Slide: "slide";
|
|
544
|
+
readonly Carry: "carry";
|
|
545
|
+
};
|
|
546
|
+
type HeadFriction = (typeof HeadFriction)[keyof typeof HeadFriction];
|
|
547
|
+
/** A prop's slide off a head: its parting brought up to `speed` (u/s) by at most `step` a tick. */
|
|
548
|
+
interface PropSlide {
|
|
549
|
+
readonly speed: number;
|
|
550
|
+
readonly step: number;
|
|
551
|
+
}
|
|
552
|
+
/** A refused `config.contacts`: the path of the first bad value and why. */
|
|
553
|
+
declare class ContactConfigError extends RangeError {
|
|
554
|
+
readonly path: string;
|
|
555
|
+
constructor(path: string, why: string);
|
|
556
|
+
}
|
|
557
|
+
/** Per-pair overrides a module may declare at most, and a tag's length at most. */
|
|
558
|
+
declare const CONTACT_PAIRS_MAX = 32;
|
|
559
|
+
declare const CONTACT_TAG_MAX = 64;
|
|
560
|
+
/** Pile passes a module may ask for at most. */
|
|
561
|
+
declare const CONTACT_PASSES_MAX = 64;
|
|
562
|
+
/** Every length and speed a module may give at most (units, units/s). */
|
|
563
|
+
declare const CONTACT_VALUE_MAX = 100000;
|
|
564
|
+
/** The share of a head's half width a shooter's landing stands within (owner: "stand on it with some
|
|
565
|
+
* slide off"; 0.25 from the Lift Yard's aimed landings, owner 2026-11-02: a landing with over 3/8 of
|
|
566
|
+
* the foot off the head slides). */
|
|
567
|
+
declare const SHOOTER_CENTER = 0.25;
|
|
568
|
+
/** One preset's table: the top rule by seat shape, the side restitution and the passes. */
|
|
569
|
+
interface PresetTable {
|
|
570
|
+
readonly character: TopRule;
|
|
571
|
+
readonly vehicle: TopRule;
|
|
572
|
+
readonly restitution: number;
|
|
573
|
+
readonly passes: number;
|
|
574
|
+
readonly vehicleOffWall: SlideRule;
|
|
575
|
+
readonly stopSpin: boolean;
|
|
576
|
+
readonly drawing: ContactDrawing;
|
|
577
|
+
readonly push: ContactPush;
|
|
578
|
+
readonly headFriction: HeadFriction;
|
|
579
|
+
}
|
|
580
|
+
/** The genre presets' tables (PROTOCOL.md §17.11 *Contact policy*, the preset table). */
|
|
581
|
+
declare const CONTACT_PRESETS: {
|
|
582
|
+
readonly [P in ContactPreset]: PresetTable;
|
|
583
|
+
};
|
|
584
|
+
/** The settings every preset shares (the preset table's SUPPORT_COS and PILE_SLOP rows). */
|
|
585
|
+
declare const CONTACT_DEFAULTS: Readonly<{
|
|
586
|
+
supportCos: 0.7;
|
|
587
|
+
entrySpeed: 1;
|
|
588
|
+
entryWindow: number;
|
|
589
|
+
slop: 0.1;
|
|
590
|
+
roofSkin: 0.01;
|
|
591
|
+
}>;
|
|
592
|
+
/** A slide or top rule as a module declares it: every key but `policy` optional (the preset fills it). */
|
|
593
|
+
type Declared<T> = {
|
|
594
|
+
readonly [K in keyof T]?: T[K];
|
|
595
|
+
};
|
|
596
|
+
/** `config.contacts` as a module declares it (PROTOCOL.md §17.11 *Contact policy*): the preset, and any
|
|
597
|
+
* cell of its table overridden. Resolved by {@link resolveContactConfig}. */
|
|
598
|
+
interface ContactDeclaration {
|
|
599
|
+
readonly preset: ContactPreset;
|
|
600
|
+
readonly shape?: ContactShape;
|
|
601
|
+
readonly top?: {
|
|
602
|
+
readonly policy: typeof TopPolicy.Stand;
|
|
603
|
+
readonly ride?: boolean;
|
|
604
|
+
readonly center?: number | null;
|
|
605
|
+
readonly slide?: Declared<SlideRule>;
|
|
606
|
+
} | {
|
|
607
|
+
readonly policy: typeof TopPolicy.Ghost;
|
|
608
|
+
} | ({
|
|
609
|
+
readonly policy: typeof TopPolicy.Slide;
|
|
610
|
+
} & Declared<SlideRule>) | {
|
|
611
|
+
readonly policy: typeof TopPolicy.Bounce;
|
|
612
|
+
readonly stompMin?: number;
|
|
613
|
+
readonly bounceSpeed?: number;
|
|
614
|
+
readonly jumpBoost?: number;
|
|
615
|
+
};
|
|
616
|
+
readonly offWall?: Declared<SlideRule>;
|
|
617
|
+
readonly restitution?: number;
|
|
618
|
+
readonly passes?: number;
|
|
619
|
+
readonly slop?: number;
|
|
620
|
+
readonly supportCos?: number;
|
|
621
|
+
readonly entrySpeed?: number;
|
|
622
|
+
readonly entryWindow?: number;
|
|
623
|
+
readonly roofSkin?: number;
|
|
624
|
+
readonly maxVelocity?: number | null;
|
|
625
|
+
readonly stopSpin?: boolean;
|
|
626
|
+
readonly pairs?: readonly {
|
|
627
|
+
readonly a: string;
|
|
628
|
+
readonly b: string;
|
|
629
|
+
readonly top: NonNullable<ContactDeclaration['top']>;
|
|
630
|
+
}[];
|
|
631
|
+
readonly drawing?: ContactDrawing;
|
|
632
|
+
readonly push?: ContactPush;
|
|
633
|
+
readonly headFriction?: HeadFriction;
|
|
634
|
+
readonly propSlide?: Declared<PropSlide>;
|
|
635
|
+
}
|
|
636
|
+
/** The movement preset the seats step with, as the contact policy reads it: its hull and ground. */
|
|
637
|
+
interface ContactPresetParams {
|
|
638
|
+
readonly name: string;
|
|
639
|
+
readonly params: Readonly<Record<string, number>>;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* `config.contacts` resolved against the room's movement preset (§17.11 *Contact policy*): every setting
|
|
643
|
+
* the module gave, else the genre preset's. Throws {@link ContactConfigError} (the path from `path`,
|
|
644
|
+
* default `$.config.contacts`) for a value that is not an object, an unknown key, a preset or policy
|
|
645
|
+
* that does not exist, a number out of range, a shape without its dimensions, more than
|
|
646
|
+
* CONTACT_PAIRS_MAX pair rules, or a room whose movement preset steps through no world.
|
|
647
|
+
*/
|
|
648
|
+
declare function resolveContactConfig(given: unknown, preset: ContactPresetParams | null, path?: string): ContactPolicy;
|
|
649
|
+
|
|
650
|
+
/** Whether a predicting room repeats a starved seat's held input (§17.11 *Input pacing*): `on`, the
|
|
651
|
+
* seat is stepped under its last input and its client reconciles at `ack + inRep` and throttles; `off`,
|
|
652
|
+
* the seat holds on a starved tick, its client reads the plain ack and never throttles. */
|
|
653
|
+
declare const InputRepeat: {
|
|
654
|
+
readonly On: "on";
|
|
655
|
+
readonly Off: "off";
|
|
656
|
+
};
|
|
657
|
+
type InputRepeat = (typeof InputRepeat)[keyof typeof InputRepeat];
|
|
658
|
+
/** The resolved input settings a room runs with. */
|
|
659
|
+
interface InputPolicy {
|
|
660
|
+
readonly repeat: InputRepeat;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Each genre preset's input settings. Measured on the bench (interleaved runs, one-sided Mann–Whitney,
|
|
664
|
+
* evidence folder input-repeat/ and R2): `vehicle-arena` is off, because on car-ball 3v3 at a clean
|
|
665
|
+
* 200 ms round trip the repeats and the throttle cost other players' cars (remote corrP95 and errAtLead
|
|
666
|
+
* worse, p ≤ .013) while a client that never asks for them stays within the base; `shooter` is on,
|
|
667
|
+
* because Mechanism Hall and Blast Yard under loss draw your own character with fewer pops and hitches
|
|
668
|
+
* and stack and rocket better with it. `platformer`, `party` and `physics-sandbox` seat characters as a
|
|
669
|
+
* shooter does and have no measurement of their own, so they are on as measured for characters.
|
|
670
|
+
*/
|
|
671
|
+
declare const INPUT_PRESETS: {
|
|
672
|
+
readonly shooter: {
|
|
673
|
+
readonly repeat: "on";
|
|
674
|
+
};
|
|
675
|
+
readonly platformer: {
|
|
676
|
+
readonly repeat: "on";
|
|
677
|
+
};
|
|
678
|
+
readonly "vehicle-arena": {
|
|
679
|
+
readonly repeat: "off";
|
|
680
|
+
};
|
|
681
|
+
readonly "physics-sandbox": {
|
|
682
|
+
readonly repeat: "on";
|
|
683
|
+
};
|
|
684
|
+
readonly party: {
|
|
685
|
+
readonly repeat: "on";
|
|
686
|
+
};
|
|
687
|
+
};
|
|
688
|
+
/** A room that names no genre and no setting repeats, as every predicting room did before the setting. */
|
|
689
|
+
declare const INPUTS_DEFAULT: InputPolicy;
|
|
690
|
+
/** A refused `config.inputs`: the path of the first bad value. */
|
|
691
|
+
declare class InputConfigError extends Error {
|
|
692
|
+
readonly path: string;
|
|
693
|
+
constructor(path: string);
|
|
694
|
+
}
|
|
695
|
+
/** `config.inputs` as a module declares it: a genre preset (default the contact policy's), and a
|
|
696
|
+
* setting that overrides the preset's. */
|
|
697
|
+
interface InputDeclaration {
|
|
698
|
+
preset?: ContactPreset;
|
|
699
|
+
repeat?: InputRepeat;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* `config.inputs` resolved (§17.11 *Input pacing*): `repeat` as given, else the table of the genre preset
|
|
703
|
+
* (`inputs.preset`, else `contactsPreset`, the room's declared contact policy's), else on. Null when the
|
|
704
|
+
* module declares neither inputs nor a contact policy (a room without the setting: on). Throws an
|
|
705
|
+
* {@link InputConfigError} naming the path (default `$.config.inputs`) of a value that is not an
|
|
706
|
+
* object, an unknown key, a preset that is not a genre preset or a setting that is not `on` or `off`.
|
|
707
|
+
*/
|
|
708
|
+
declare function resolveInputConfig(given: unknown, contactsPreset: ContactPreset | null, path?: string): InputPolicy | null;
|
|
709
|
+
/**
|
|
710
|
+
* The room's input repeat setting as a client reads it from the state's `preset` text (JSON, its
|
|
711
|
+
* `inputs` key): `off` only when the text says so outright, `on` for anything else. Absent is a room
|
|
712
|
+
* without the setting or an older host, which repeats; for a hostile value the room's own fields are the
|
|
713
|
+
* truth, and a room that is off writes none of them.
|
|
714
|
+
*/
|
|
715
|
+
declare function presetInputRepeat(presetText: string): InputRepeat;
|
|
716
|
+
|
|
717
|
+
/** A lift's shape, room units, about its centre. */
|
|
718
|
+
declare const LiftShapeKind: {
|
|
719
|
+
/** A box `half` (x, y, z): axis-aligned, or turned about z when it spins (a sweeper bar). */
|
|
720
|
+
readonly Box: "box";
|
|
721
|
+
/** An upright disc of `radius`, `half` its half height (a turntable, a spinner). */
|
|
722
|
+
readonly Disc: "disc";
|
|
723
|
+
};
|
|
724
|
+
type LiftShapeKind = (typeof LiftShapeKind)[keyof typeof LiftShapeKind];
|
|
725
|
+
/** How a path runs its points. */
|
|
726
|
+
declare const LiftPathMode: {
|
|
727
|
+
/** Out along the legs, then back along them. */
|
|
728
|
+
readonly PingPong: "pingpong";
|
|
729
|
+
/** Round the points and from the last back to the first. */
|
|
730
|
+
readonly Loop: "loop";
|
|
731
|
+
};
|
|
732
|
+
type LiftPathMode = (typeof LiftPathMode)[keyof typeof LiftPathMode];
|
|
733
|
+
/** How a leg moves between its points. */
|
|
734
|
+
declare const LiftEase: {
|
|
735
|
+
/** At a steady speed. */
|
|
736
|
+
readonly Linear: "linear";
|
|
737
|
+
/** Starting and stopping at rest (`s·s·(3 − 2s)`; its peak speed 1.5 times the mean). */
|
|
738
|
+
readonly Smooth: "smooth";
|
|
739
|
+
};
|
|
740
|
+
type LiftEase = (typeof LiftEase)[keyof typeof LiftEase];
|
|
741
|
+
/** What a rider keeps of its lift's motion when it leaves it. */
|
|
742
|
+
declare const LiftImpart: {
|
|
743
|
+
/** The lift's velocity at the rider's point (Unreal's and Godot's default). */
|
|
744
|
+
readonly All: "all";
|
|
745
|
+
/** Nothing: the rider leaves with its own velocity. */
|
|
746
|
+
readonly None: "none";
|
|
747
|
+
};
|
|
748
|
+
type LiftImpart = (typeof LiftImpart)[keyof typeof LiftImpart];
|
|
749
|
+
/** A point or size in room units. */
|
|
750
|
+
type LiftVec = readonly [number, number, number];
|
|
751
|
+
/** A lift's shape. */
|
|
752
|
+
type LiftShape = {
|
|
753
|
+
readonly kind: typeof LiftShapeKind.Box;
|
|
754
|
+
readonly half: LiftVec;
|
|
755
|
+
} | {
|
|
756
|
+
readonly kind: typeof LiftShapeKind.Disc;
|
|
757
|
+
readonly radius: number;
|
|
758
|
+
readonly half: number;
|
|
759
|
+
};
|
|
760
|
+
/** A resolved path: `points` (float32), a leg's ticks and a wait's ticks per point. */
|
|
761
|
+
interface LiftPath {
|
|
762
|
+
readonly points: readonly LiftVec[];
|
|
763
|
+
readonly legs: readonly number[];
|
|
764
|
+
readonly waits: readonly number[];
|
|
765
|
+
readonly mode: LiftPathMode;
|
|
766
|
+
readonly ease: LiftEase;
|
|
767
|
+
readonly phase: number;
|
|
768
|
+
/** The ticks of one whole cycle. */
|
|
769
|
+
readonly period: number;
|
|
770
|
+
}
|
|
771
|
+
/** A resolved spin: a turn every `period` ticks, `dir` 1 (counter-clockwise from above) or −1. */
|
|
772
|
+
interface LiftSpin {
|
|
773
|
+
readonly period: number;
|
|
774
|
+
readonly dir: 1 | -1;
|
|
775
|
+
readonly phase: number;
|
|
776
|
+
}
|
|
777
|
+
/** One resolved platform. */
|
|
778
|
+
interface Lift {
|
|
779
|
+
readonly id: string;
|
|
780
|
+
readonly shape: LiftShape;
|
|
781
|
+
/** The centre when it has no path (else the path's first point). */
|
|
782
|
+
readonly at: LiftVec;
|
|
783
|
+
readonly path: LiftPath | null;
|
|
784
|
+
readonly spin: LiftSpin | null;
|
|
785
|
+
/** A conveyor's surface velocity in its own frame (u/s), or null. */
|
|
786
|
+
readonly surface: readonly [number, number] | null;
|
|
787
|
+
readonly impart: LiftImpart;
|
|
788
|
+
/** Still until the module calls it; a call runs one cycle of its path. */
|
|
789
|
+
readonly called: boolean;
|
|
790
|
+
}
|
|
791
|
+
/** The resolved `config.lifts`. */
|
|
792
|
+
interface LiftsPolicy {
|
|
793
|
+
readonly platforms: readonly Lift[];
|
|
794
|
+
/** Whether seats ride level dynamic module bodies too. */
|
|
795
|
+
readonly bodies: boolean;
|
|
796
|
+
}
|
|
797
|
+
/** No lifts: what the slots run over in a room whose only carry is by seats (a stand rule that rides). */
|
|
798
|
+
declare const NO_LIFTS: LiftsPolicy;
|
|
799
|
+
/** A platform refused for moving too fast: its id, its fastest point's move a tick, the most allowed
|
|
800
|
+
* (the hull radius) and the tick rate, all units a tick. */
|
|
801
|
+
interface LiftTooFast {
|
|
802
|
+
readonly platform: string;
|
|
803
|
+
readonly move: number;
|
|
804
|
+
readonly allowed: number;
|
|
805
|
+
readonly tickHz: number;
|
|
806
|
+
}
|
|
807
|
+
/** A refused `config.lifts`: the path of the first bad value and why (`fast`: a platform too fast). */
|
|
808
|
+
declare class LiftsConfigError extends RangeError {
|
|
809
|
+
readonly path: string;
|
|
810
|
+
readonly fast: LiftTooFast | undefined;
|
|
811
|
+
constructor(path: string, why: string, fast?: LiftTooFast);
|
|
812
|
+
}
|
|
813
|
+
/** Platforms a module may declare at most, points a path may hold, and a lift id's length. */
|
|
814
|
+
declare const LIFTS_MAX = 32;
|
|
815
|
+
declare const LIFT_POINTS_MAX = 16;
|
|
816
|
+
declare const LIFT_ID_MAX = 32;
|
|
817
|
+
/** Every position, size and speed's magnitude at most (units, units/s). */
|
|
818
|
+
declare const LIFT_VALUE_MAX = 1000000;
|
|
819
|
+
/** Every tick count (a leg, a wait, a phase, a spin's period) at most: about 4.6 days at 120 Hz. */
|
|
820
|
+
declare const LIFT_TICKS_MAX: number;
|
|
821
|
+
/** A spin's fewest ticks a turn. */
|
|
822
|
+
declare const LIFT_SPIN_MIN_PERIOD = 8;
|
|
823
|
+
/** Whether `id` is a lift id: 1 to LIFT_ID_MAX of a-z, 0-9 and -. */
|
|
824
|
+
declare const isLiftId: (id: unknown) => id is string;
|
|
825
|
+
/**
|
|
826
|
+
* `config.lifts` resolved against the room's movement preset and tick rate (§17.11 *Lifts*). Throws
|
|
827
|
+
* {@link LiftsConfigError} (the path from `path`, default `$.config.lifts`) for a value that is not an
|
|
828
|
+
* object, an unknown key, an empty list or more than LIFTS_MAX platforms, an id that is not 1-32 of
|
|
829
|
+
* `[a-z0-9-]` or repeats, a shape without its sizes, a path whose points, legs and waits do not match, a
|
|
830
|
+
* tick count that is not a whole number, a phase on a called lift, a value out of range, a platform any
|
|
831
|
+
* point of which moves more than the hull radius a tick (named, with its speed and the most allowed),
|
|
832
|
+
* or a room whose movement preset steps through no world.
|
|
833
|
+
*/
|
|
834
|
+
declare function resolveLifts(given: unknown, preset: ContactPresetParams | null, tickHz: number, path?: string): LiftsPolicy;
|
|
835
|
+
|
|
836
|
+
declare const MODULE_CONTRACT = 1;
|
|
837
|
+
/** A JSON value: what crosses the sandbox boundary in events, logs and entity `data`. */
|
|
838
|
+
type Json = null | boolean | number | string | Json[] | {
|
|
839
|
+
[key: string]: Json;
|
|
840
|
+
};
|
|
841
|
+
/** A point or a direction in world units. */
|
|
842
|
+
interface Vec3 {
|
|
843
|
+
x: number;
|
|
844
|
+
y: number;
|
|
845
|
+
z: number;
|
|
846
|
+
}
|
|
847
|
+
/** Fixed simulation rates a module may choose. */
|
|
848
|
+
/** A room's tick rate. 120 is a physics room's only (PROTOCOL.md §17.11, car-ball rooms). */
|
|
849
|
+
type TickHz = 20 | 30 | 60 | 120;
|
|
850
|
+
/** Why a player left (`onLeave`). */
|
|
851
|
+
declare const LeaveReason: {
|
|
852
|
+
/** The client left on purpose. */
|
|
853
|
+
readonly LEFT: "left";
|
|
854
|
+
/** The connection dropped and the reconnection grace ran out. */
|
|
855
|
+
readonly DROPPED: "dropped";
|
|
856
|
+
/** A newer session of the same player took the seat. */
|
|
857
|
+
readonly SUPERSEDED: "superseded";
|
|
858
|
+
/** The player's session was revoked. */
|
|
859
|
+
readonly REVOKED: "revoked";
|
|
860
|
+
/** The room is closing (drain, halt or fault). */
|
|
861
|
+
readonly CLOSING: "closing";
|
|
862
|
+
};
|
|
863
|
+
type LeaveReason = (typeof LeaveReason)[keyof typeof LeaveReason];
|
|
864
|
+
/** A seated player, as the server verified them. */
|
|
865
|
+
interface PlayerInfo {
|
|
866
|
+
/** The seat's session id: the key of `AuthorityRoomState.players` and an entity's `owner`. */
|
|
867
|
+
id: string;
|
|
868
|
+
/** The stable per-game player id (PROTOCOL.md §3.3). Key saves and scores by it. */
|
|
869
|
+
uid: string;
|
|
870
|
+
name: string;
|
|
871
|
+
/** '' = no team. */
|
|
872
|
+
team: string;
|
|
873
|
+
}
|
|
874
|
+
/** One frame of a player's input (`AuthorityInput`), already sanitized by the server: move axes in
|
|
875
|
+
* [-1, 1], yaw and pitch wrapped to [-π, π), `buttons` an integer bitmask below 2^24. */
|
|
876
|
+
interface InputData {
|
|
877
|
+
moveX: number;
|
|
878
|
+
moveY: number;
|
|
879
|
+
moveZ: number;
|
|
880
|
+
yaw: number;
|
|
881
|
+
pitch: number;
|
|
882
|
+
buttons: number;
|
|
883
|
+
}
|
|
884
|
+
/** An entity as the module reads it. Positions and velocities are float32 on the wire. */
|
|
885
|
+
interface EntityData {
|
|
886
|
+
readonly id: string;
|
|
887
|
+
kind: string;
|
|
888
|
+
/** Owning seat's session id, or ''. */
|
|
889
|
+
owner: string;
|
|
890
|
+
x: number;
|
|
891
|
+
y: number;
|
|
892
|
+
z: number;
|
|
893
|
+
vx: number;
|
|
894
|
+
vy: number;
|
|
895
|
+
vz: number;
|
|
896
|
+
/** Heading in radians. */
|
|
897
|
+
yaw: number;
|
|
898
|
+
hp: number;
|
|
899
|
+
/** Game-defined bitmask below 2^24. */
|
|
900
|
+
flags: number;
|
|
901
|
+
/** Bump it to make clients snap instead of interpolate (a teleport or a respawn). */
|
|
902
|
+
epoch: number;
|
|
903
|
+
/** Free-form JSON, at most 256 characters once encoded. */
|
|
904
|
+
data: Json;
|
|
905
|
+
/** A projectile's launch tick (§17.9); 0 = not a projectile. Written by `ctx.projectiles.launch`. */
|
|
906
|
+
t0: number;
|
|
907
|
+
/** A projectile's downward acceleration along −z, units/s², in [0, 1000]. */
|
|
908
|
+
g: number;
|
|
909
|
+
/** The seat that fired a projectile, '' = none (a server projectile). */
|
|
910
|
+
src: string;
|
|
911
|
+
/** A `firstperson` body's aim pitch, radians (§17.7): the preset writes it from the input. */
|
|
912
|
+
pitch: number;
|
|
913
|
+
/** A `firstperson` body's duck amount, 0 standing to 1 fully ducked: the preset writes it. */
|
|
914
|
+
duck: number;
|
|
915
|
+
/** A `firstperson` or `vehicle` body's movement bits (1 onGround, 2 ducking, 4 jumpHeld; a
|
|
916
|
+
* vehicle keeps 1 and 4): the preset writes them; write 0 on a respawn to start a life in the air
|
|
917
|
+
* with jump released. */
|
|
918
|
+
mstate: number;
|
|
919
|
+
/** A `firstperson` body's speed multiplier, or a `vehicle`'s multiplier of both top speeds,
|
|
920
|
+
* [0, 4], default 1: yours to write (a weapon's or a wound's speed); the preset only reads it. */
|
|
921
|
+
cap: number;
|
|
922
|
+
/** A `vehicle` body's yaw rate, rad/s, positive toward +yaw (§17.7): the preset writes it; write 0
|
|
923
|
+
* on a respawn. */
|
|
924
|
+
spin: number;
|
|
925
|
+
/** A `vehicle` body's boost fuel, [0, 1], default 0: write it to refuel (a boost pad, a respawn);
|
|
926
|
+
* the preset only burns it. */
|
|
927
|
+
fuel: number;
|
|
928
|
+
}
|
|
929
|
+
/** The fields only `projectiles.launch` writes. */
|
|
930
|
+
type ProjectileField = 't0' | 'g' | 'src';
|
|
931
|
+
/** What `entities.spawn` takes: a kind, and any field to start from other than the default (a
|
|
932
|
+
* projectile's fields come from `projectiles.launch`). */
|
|
933
|
+
type EntityInit = {
|
|
934
|
+
kind: string;
|
|
935
|
+
id?: string;
|
|
936
|
+
} & Partial<Omit<EntityData, 'id' | 'kind' | ProjectileField>>;
|
|
937
|
+
/** The room's entities. Every write is recorded and only changed entities go out each tick. */
|
|
938
|
+
interface EntityApi {
|
|
939
|
+
/** Adds an entity and returns its id (`id` when given and free, else a fresh one). */
|
|
940
|
+
spawn(entity: EntityInit): string;
|
|
941
|
+
get(id: string): EntityData | undefined;
|
|
942
|
+
update(id: string, patch: Partial<Omit<EntityData, 'id' | ProjectileField>>): void;
|
|
943
|
+
remove(id: string): void;
|
|
944
|
+
/** The first entity this seat owns. */
|
|
945
|
+
ownedBy(playerId: string): EntityData | undefined;
|
|
946
|
+
all(): readonly EntityData[];
|
|
947
|
+
}
|
|
948
|
+
/** Lag compensation. Both calls read the snapshot of positions the current input's sender saw when
|
|
949
|
+
* they pressed it (only while handling an input whose buttons match `config.lagComp.buttons`;
|
|
950
|
+
* otherwise the current positions). */
|
|
951
|
+
interface RewindApi {
|
|
952
|
+
hitscan(origin: Vec3, dir: Vec3, options?: {
|
|
953
|
+
range?: number;
|
|
954
|
+
radius?: number;
|
|
955
|
+
kinds?: string[];
|
|
956
|
+
}): {
|
|
957
|
+
id: string;
|
|
958
|
+
dist: number;
|
|
959
|
+
} | null;
|
|
960
|
+
overlapAt(center: Vec3, radius: number): string[];
|
|
961
|
+
/** Where the shooter of the lag-compensated input being handled saw entity `id` (the position
|
|
962
|
+
* `hitscan` and `overlapAt` judge it at), its live position otherwise; null for an unknown id, a
|
|
963
|
+
* projectile or a physics body entity. For a module's own tests against things that are not
|
|
964
|
+
* spheres (a door, a shutter): §17.5. */
|
|
965
|
+
at(id: string): Vec3 | null;
|
|
966
|
+
}
|
|
967
|
+
/** What `projectiles.launch` takes: where and how fast the projectile starts. `g` (default 0) pulls
|
|
968
|
+
* it down along −z; `id` is required outside a seat's input and refused inside one, where the id is
|
|
969
|
+
* `<seat>.<seq>.<n>` (the input's client sequence, and the launches during it from 0). */
|
|
970
|
+
interface ProjectileInit {
|
|
971
|
+
kind: string;
|
|
972
|
+
x: number;
|
|
973
|
+
y: number;
|
|
974
|
+
z: number;
|
|
975
|
+
vx: number;
|
|
976
|
+
vy: number;
|
|
977
|
+
vz: number;
|
|
978
|
+
g?: number;
|
|
979
|
+
hp?: number;
|
|
980
|
+
flags?: number;
|
|
981
|
+
data?: Json;
|
|
982
|
+
id?: string;
|
|
983
|
+
}
|
|
984
|
+
/** Where a projectile is at one tick (its flight, §17.9). */
|
|
985
|
+
interface ProjectileFlight {
|
|
986
|
+
x: number;
|
|
987
|
+
y: number;
|
|
988
|
+
z: number;
|
|
989
|
+
vx: number;
|
|
990
|
+
vy: number;
|
|
991
|
+
vz: number;
|
|
992
|
+
}
|
|
993
|
+
/** One entity a projectile's path crossed this tick, and how far along the path from its point at
|
|
994
|
+
* the previous tick (world units). */
|
|
995
|
+
interface ProjectileHit {
|
|
996
|
+
id: string;
|
|
997
|
+
dist: number;
|
|
998
|
+
}
|
|
999
|
+
/** Projectiles (§17.9): entities on a closed-form ballistic flight that every client draws from its
|
|
1000
|
+
* launch. They take no part in lag compensation, and the server never ages one out. */
|
|
1001
|
+
interface ProjectileApi {
|
|
1002
|
+
/** Launches (or, for an id that exists, re-launches) a projectile at the tick being stepped, with
|
|
1003
|
+
* `owner` '' and `src` the seat whose input is being handled ('' elsewhere). Returns its id.
|
|
1004
|
+
* Throws a RangeError, writing nothing, when the launches not yet sent would pass
|
|
1005
|
+
* `AuthorityLimits.MAX_LAUNCH_UPDATES` entities or `MAX_LAUNCH_BYTES` of output (about a hundred
|
|
1006
|
+
* a tick): catch it to cap a burst, or it is a strike. */
|
|
1007
|
+
launch(projectile: ProjectileInit): string;
|
|
1008
|
+
/** The projectile's flight at `tick` (default: the tick being stepped); undefined for an id that is
|
|
1009
|
+
* not a projectile. */
|
|
1010
|
+
at(id: string, tick?: number): ProjectileFlight | undefined;
|
|
1011
|
+
/** The entities of `kinds` whose sphere of `radius` the projectile's path crosses between the
|
|
1012
|
+
* previous tick and this one, nearest first. Only those kinds are visited, so a call costs the
|
|
1013
|
+
* number of their entities. */
|
|
1014
|
+
sweep(id: string, options: {
|
|
1015
|
+
kinds: string[];
|
|
1016
|
+
radius: number;
|
|
1017
|
+
}): ProjectileHit[];
|
|
1018
|
+
}
|
|
1019
|
+
/** Preset movement a module can hand to the server (`config.preset`). */
|
|
1020
|
+
interface PresetSpec {
|
|
1021
|
+
name: PresetName;
|
|
1022
|
+
params?: Record<string, number>;
|
|
1023
|
+
}
|
|
1024
|
+
/** A rotation as a unit quaternion. */
|
|
1025
|
+
interface Quat {
|
|
1026
|
+
x: number;
|
|
1027
|
+
y: number;
|
|
1028
|
+
z: number;
|
|
1029
|
+
w: number;
|
|
1030
|
+
}
|
|
1031
|
+
/** A position and a rotation. */
|
|
1032
|
+
interface PhysicsPose {
|
|
1033
|
+
p: Vec3;
|
|
1034
|
+
q: Quat;
|
|
1035
|
+
}
|
|
1036
|
+
/** The contract's limits of one world (C§11). `worldExtent` and `maxSpeed` are in the room's units
|
|
1037
|
+
* (they scale with `lengthUnit`); the rest are counts, bytes or rad/s. */
|
|
1038
|
+
interface PhysicsLimits {
|
|
1039
|
+
bodies: number;
|
|
1040
|
+
collidersPerBody: number;
|
|
1041
|
+
hullVertices: number;
|
|
1042
|
+
triangles: number;
|
|
1043
|
+
joints: number;
|
|
1044
|
+
ccdBodies: number;
|
|
1045
|
+
queriesPerStep: number;
|
|
1046
|
+
eventsPerStep: number;
|
|
1047
|
+
heightfieldSamples: number;
|
|
1048
|
+
staticPieces: number;
|
|
1049
|
+
memoryBytes: number;
|
|
1050
|
+
worldExtent: number;
|
|
1051
|
+
maxSpeed: number;
|
|
1052
|
+
maxAngularSpeed: number;
|
|
1053
|
+
}
|
|
1054
|
+
/** What a module may ask for in `config.physics` (`true` is the same as `{}`). Any other key, or a
|
|
1055
|
+
* value outside its range, is a bad config. */
|
|
1056
|
+
interface PhysicsRequest {
|
|
1057
|
+
/** Room units per metre, (0, 1e6]. Default 39.37 with `firstperson`, 100 with `vehicle`, else 1. */
|
|
1058
|
+
lengthUnit?: number;
|
|
1059
|
+
/** Units/s² along −z, [0, 1e7]. Default the preset's `gravity` with `firstperson` or `vehicle`,
|
|
1060
|
+
* else 9.81 · lengthUnit. */
|
|
1061
|
+
gravity?: number;
|
|
1062
|
+
/** The solver hint, an integer in [1, 16]. Default 4. */
|
|
1063
|
+
iterations?: number;
|
|
1064
|
+
/** Lower any limit; one above the room's ceiling (the contract's default in release 1) is clamped. */
|
|
1065
|
+
limits?: Partial<PhysicsLimits>;
|
|
1066
|
+
/** At most 8 distinct physics kinds whose changed bodies the module receives each tick. */
|
|
1067
|
+
subscribe?: string[];
|
|
1068
|
+
/** The preset bodies: `radius` and `height` (each (0, 1e6]) size a `topdown` room's, and are
|
|
1069
|
+
* refused with any other preset; `events` (default false) opts their colliders into contact
|
|
1070
|
+
* events. */
|
|
1071
|
+
player?: {
|
|
1072
|
+
radius?: number;
|
|
1073
|
+
height?: number;
|
|
1074
|
+
events?: boolean;
|
|
1075
|
+
};
|
|
1076
|
+
/** Whether clients predict the room's bodies (§17.11). Default true; the operator and each client
|
|
1077
|
+
* can still turn it off. */
|
|
1078
|
+
predict?: boolean;
|
|
1079
|
+
/** Keyframes (tick-aligned patches) per second in a predicting room, one of 10, 15, 20, 30 and 60.
|
|
1080
|
+
* Default 20; the room uses the largest of those at or below it that divides its tick rate. */
|
|
1081
|
+
patchHz?: number;
|
|
1082
|
+
/** How a client moves other players' preset bodies between keyframes: by their velocity
|
|
1083
|
+
* (`extrapolate`, the default), by their last input held (`hold`, the Rocket League model: the room
|
|
1084
|
+
* preset's step under that input every tick), or by their last input, repeated and decayed
|
|
1085
|
+
* (`inputDecay`). */
|
|
1086
|
+
remotePredict?: RemotePredict;
|
|
1087
|
+
/** `inputDecay`: ms after which a repeated input's axes have decayed to 0, in [0, 1000]. Default 50. */
|
|
1088
|
+
remoteDecayMs?: number;
|
|
1089
|
+
/** How far a client predicts past a keyframe's ack, ms, in [50, 1000]. Default 250. */
|
|
1090
|
+
leadMs?: number;
|
|
1091
|
+
}
|
|
1092
|
+
/** `config.physics.remotePredict` (§17.11). */
|
|
1093
|
+
declare const RemotePredict: {
|
|
1094
|
+
readonly EXTRAPOLATE: "extrapolate";
|
|
1095
|
+
readonly HOLD: "hold";
|
|
1096
|
+
readonly INPUT_DECAY: "inputDecay";
|
|
1097
|
+
};
|
|
1098
|
+
type RemotePredict = (typeof RemotePredict)[keyof typeof RemotePredict];
|
|
1099
|
+
/** `config.physics` after defaults, as `init` sees it: what the room's world runs with. */
|
|
1100
|
+
interface ResolvedPhysics {
|
|
1101
|
+
lengthUnit: number;
|
|
1102
|
+
gravity: number;
|
|
1103
|
+
iterations: number;
|
|
1104
|
+
/** Every limit, clamped to the room's ceiling. */
|
|
1105
|
+
limits: PhysicsLimits;
|
|
1106
|
+
subscribe: string[];
|
|
1107
|
+
player: {
|
|
1108
|
+
radius?: number;
|
|
1109
|
+
height?: number;
|
|
1110
|
+
events: boolean;
|
|
1111
|
+
};
|
|
1112
|
+
/** Whether the module asked for prediction (§17.11); the host may still run the room without it. */
|
|
1113
|
+
predict: boolean;
|
|
1114
|
+
/** The requested keyframe rate (§17.11). */
|
|
1115
|
+
patchHz: number;
|
|
1116
|
+
remotePredict: RemotePredict;
|
|
1117
|
+
remoteDecayMs: number;
|
|
1118
|
+
leadMs: number;
|
|
1119
|
+
}
|
|
1120
|
+
/** Collision groups: 16-bit masks (C§6.2). */
|
|
1121
|
+
interface PhysicsGroups {
|
|
1122
|
+
member: number;
|
|
1123
|
+
collides: number;
|
|
1124
|
+
}
|
|
1125
|
+
/** A collider shape (C§6.2). */
|
|
1126
|
+
type PhysicsShape = {
|
|
1127
|
+
kind: 'box';
|
|
1128
|
+
half: Vec3;
|
|
1129
|
+
} | {
|
|
1130
|
+
kind: 'sphere';
|
|
1131
|
+
radius: number;
|
|
1132
|
+
} | {
|
|
1133
|
+
kind: 'capsule';
|
|
1134
|
+
halfHeight: number;
|
|
1135
|
+
radius: number;
|
|
1136
|
+
axis: 'x' | 'y' | 'z';
|
|
1137
|
+
} | {
|
|
1138
|
+
kind: 'hull';
|
|
1139
|
+
points: Vec3[];
|
|
1140
|
+
} | {
|
|
1141
|
+
kind: 'trimesh';
|
|
1142
|
+
vertices: Vec3[];
|
|
1143
|
+
indices: [number, number, number][];
|
|
1144
|
+
} | {
|
|
1145
|
+
kind: 'heightfield';
|
|
1146
|
+
rows: number;
|
|
1147
|
+
cols: number;
|
|
1148
|
+
heights: number[];
|
|
1149
|
+
size: Vec3;
|
|
1150
|
+
};
|
|
1151
|
+
/** One collider of a body (C§6.2). */
|
|
1152
|
+
interface PhysicsColliderDesc {
|
|
1153
|
+
key: string;
|
|
1154
|
+
shape: PhysicsShape;
|
|
1155
|
+
offset?: PhysicsPose;
|
|
1156
|
+
density?: number;
|
|
1157
|
+
mass?: number;
|
|
1158
|
+
friction?: number;
|
|
1159
|
+
restitution?: number;
|
|
1160
|
+
groups?: PhysicsGroups;
|
|
1161
|
+
sensor?: boolean;
|
|
1162
|
+
events?: boolean;
|
|
1163
|
+
}
|
|
1164
|
+
/** A body type (C§6.1). */
|
|
1165
|
+
type PhysicsBodyType = 'dynamic' | 'fixed' | 'kinematic';
|
|
1166
|
+
/** A body to spawn (C§6.1). `key` is the id of an entity that exists once this tick's entity
|
|
1167
|
+
* changes apply, and must be a contract key (1-64 of `[A-Za-z0-9_.:-]`, not `static:`…). `kind` is
|
|
1168
|
+
* the body's own physics kind (1-32 of the same charset, '' or absent = none), not the entity's. */
|
|
1169
|
+
interface PhysicsBodyDesc {
|
|
1170
|
+
key: string;
|
|
1171
|
+
type: PhysicsBodyType;
|
|
1172
|
+
pose: PhysicsPose;
|
|
1173
|
+
linvel?: Vec3;
|
|
1174
|
+
angvel?: Vec3;
|
|
1175
|
+
colliders: PhysicsColliderDesc[];
|
|
1176
|
+
linearDamping?: number;
|
|
1177
|
+
angularDamping?: number;
|
|
1178
|
+
gravityScale?: number;
|
|
1179
|
+
lockRotation?: [boolean, boolean, boolean];
|
|
1180
|
+
ccd?: boolean;
|
|
1181
|
+
canSleep?: boolean;
|
|
1182
|
+
kind?: string;
|
|
1183
|
+
}
|
|
1184
|
+
/** A joint motor's targets (C§6.4). */
|
|
1185
|
+
interface PhysicsMotor {
|
|
1186
|
+
targetVel?: number;
|
|
1187
|
+
targetPos?: number;
|
|
1188
|
+
stiffness?: number;
|
|
1189
|
+
damping?: number;
|
|
1190
|
+
maxForce?: number;
|
|
1191
|
+
}
|
|
1192
|
+
/** A joint (C§6.4). `b: null` attaches to the world. */
|
|
1193
|
+
interface PhysicsJointDesc {
|
|
1194
|
+
key: string;
|
|
1195
|
+
a: string;
|
|
1196
|
+
b: string | null;
|
|
1197
|
+
type: 'fixed' | 'hinge' | 'ball' | 'slider' | 'rope';
|
|
1198
|
+
anchorA: Vec3;
|
|
1199
|
+
anchorB: Vec3;
|
|
1200
|
+
axis?: Vec3;
|
|
1201
|
+
limits?: [number, number];
|
|
1202
|
+
motor?: PhysicsMotor;
|
|
1203
|
+
collideConnected?: boolean;
|
|
1204
|
+
}
|
|
1205
|
+
/** `setPose` options (C§7). */
|
|
1206
|
+
interface PhysicsSetPoseOptions {
|
|
1207
|
+
resetVelocity?: boolean;
|
|
1208
|
+
wake?: boolean;
|
|
1209
|
+
}
|
|
1210
|
+
/** A query filter (C§13). */
|
|
1211
|
+
interface PhysicsQueryFilter {
|
|
1212
|
+
groups?: PhysicsGroups;
|
|
1213
|
+
excludeBodies?: string[];
|
|
1214
|
+
includeSensors?: boolean;
|
|
1215
|
+
}
|
|
1216
|
+
/** The contract's error codes (C§14), as `refused` and a refused query's answer carry them. */
|
|
1217
|
+
type PhysicsErrorCode = 'invalid-key' | 'duplicate-key' | 'unknown-key' | 'non-finite' | 'out-of-range' | 'bad-shape' | 'dynamic-mesh' | 'wrong-body-type' | 'bad-command' | 'limit-bodies' | 'limit-colliders' | 'limit-hull-vertices' | 'limit-triangles' | 'limit-joints' | 'limit-ccd' | 'limit-queries' | 'limit-static' | 'limit-memory' | 'bad-snapshot' | 'build-mismatch' | 'contract-mismatch' | 'internal';
|
|
1218
|
+
/** A command's number, or `'invalid-key'` when the prelude refused it before it was numbered. The
|
|
1219
|
+
* module's commands are numbered 0, 1, 2 … across the room's life. */
|
|
1220
|
+
type PhysicsCommandResult = number | 'invalid-key';
|
|
1221
|
+
/** A ray or shape-cast hit (C§13). */
|
|
1222
|
+
interface PhysicsHit {
|
|
1223
|
+
body: string;
|
|
1224
|
+
collider: string;
|
|
1225
|
+
dist: number;
|
|
1226
|
+
point: Vec3;
|
|
1227
|
+
normal: Vec3;
|
|
1228
|
+
}
|
|
1229
|
+
/** An overlap's bodies: at most 64 keys in key order, `more` when the core found more. */
|
|
1230
|
+
interface PhysicsOverlap {
|
|
1231
|
+
keys: string[];
|
|
1232
|
+
more: boolean;
|
|
1233
|
+
}
|
|
1234
|
+
/** One query's answer on the next tick: a hit or null (ray, shapeCast), an overlap, or the C§14
|
|
1235
|
+
* code of a refused query (`limit-queries` past the quota). */
|
|
1236
|
+
type PhysicsAnswer = PhysicsHit | null | PhysicsOverlap | PhysicsErrorCode;
|
|
1237
|
+
/** A contact or trigger event of the last step (C§8.1). A missing physics kind is absent. */
|
|
1238
|
+
type PhysicsEvent = {
|
|
1239
|
+
kind: 'contactBegin';
|
|
1240
|
+
a: string;
|
|
1241
|
+
b: string;
|
|
1242
|
+
ca: string;
|
|
1243
|
+
cb: string;
|
|
1244
|
+
ka?: string;
|
|
1245
|
+
kb?: string;
|
|
1246
|
+
impulse: number;
|
|
1247
|
+
point: Vec3;
|
|
1248
|
+
normal: Vec3;
|
|
1249
|
+
} | {
|
|
1250
|
+
kind: 'contactEnd';
|
|
1251
|
+
a: string;
|
|
1252
|
+
b: string;
|
|
1253
|
+
ca: string;
|
|
1254
|
+
cb: string;
|
|
1255
|
+
ka?: string;
|
|
1256
|
+
kb?: string;
|
|
1257
|
+
} | {
|
|
1258
|
+
kind: 'triggerEnter';
|
|
1259
|
+
sensor: string;
|
|
1260
|
+
sc: string;
|
|
1261
|
+
other: string;
|
|
1262
|
+
oc: string;
|
|
1263
|
+
ks?: string;
|
|
1264
|
+
ko?: string;
|
|
1265
|
+
} | {
|
|
1266
|
+
kind: 'triggerExit';
|
|
1267
|
+
sensor: string;
|
|
1268
|
+
sc: string;
|
|
1269
|
+
other: string;
|
|
1270
|
+
oc: string;
|
|
1271
|
+
ks?: string;
|
|
1272
|
+
ko?: string;
|
|
1273
|
+
};
|
|
1274
|
+
/** A body's replicated state (C§9.2's NetBodyState): position, rotation, linear velocity (room
|
|
1275
|
+
* units/s), angular velocity (rad/s, world axes). */
|
|
1276
|
+
interface PhysicsBodyState {
|
|
1277
|
+
p: Vec3;
|
|
1278
|
+
q: Quat;
|
|
1279
|
+
v: Vec3;
|
|
1280
|
+
w: Vec3;
|
|
1281
|
+
sleeping: boolean;
|
|
1282
|
+
}
|
|
1283
|
+
/** A command the core refused (or the host or prelude did): its number and its C§14 code. */
|
|
1284
|
+
interface PhysicsRefusal {
|
|
1285
|
+
n: number;
|
|
1286
|
+
code: PhysicsErrorCode;
|
|
1287
|
+
}
|
|
1288
|
+
/** The physics commands (C§7), allowed in every hook and in `init` (they apply before tick 0). Each
|
|
1289
|
+
* takes effect in this tick's step. Past `AuthorityLimits.MAX_PHYS_COMMANDS` (or `MAX_PHYS_BYTES`)
|
|
1290
|
+
* waiting to go out, a command throws a RangeError and adds nothing. */
|
|
1291
|
+
interface PhysicsCommands {
|
|
1292
|
+
spawn(desc: PhysicsBodyDesc): PhysicsCommandResult;
|
|
1293
|
+
remove(key: string): PhysicsCommandResult;
|
|
1294
|
+
addJoint(desc: PhysicsJointDesc): PhysicsCommandResult;
|
|
1295
|
+
removeJoint(key: string): PhysicsCommandResult;
|
|
1296
|
+
impulse(key: string, j: Vec3, at?: Vec3): PhysicsCommandResult;
|
|
1297
|
+
setVelocity(key: string, linvel?: Vec3, angvel?: Vec3): PhysicsCommandResult;
|
|
1298
|
+
setPose(key: string, pose: PhysicsPose, options?: PhysicsSetPoseOptions): PhysicsCommandResult;
|
|
1299
|
+
setNextKinematicPose(key: string, pose: PhysicsPose): PhysicsCommandResult;
|
|
1300
|
+
areaPush(center: Vec3, radius: number, strength: number, falloff: number, groups?: PhysicsGroups): PhysicsCommandResult;
|
|
1301
|
+
setMotor(jointKey: string, motor: PhysicsMotor): PhysicsCommandResult;
|
|
1302
|
+
wake(key: string): PhysicsCommandResult;
|
|
1303
|
+
sleep(key: string): PhysicsCommandResult;
|
|
1304
|
+
}
|
|
1305
|
+
/** `ctx.physics` (§17.10) in a physics room: commands, queries answered on the next tick, and what
|
|
1306
|
+
* the last step did, as this tick's `TickIn` carried it. */
|
|
1307
|
+
interface PhysicsApi extends PhysicsCommands {
|
|
1308
|
+
/** Each query returns its index in this tick's queries; read the answer at `answers[index]` on
|
|
1309
|
+
* the next tick, never this one. Past the world's `queriesPerStep`, it throws a RangeError. */
|
|
1310
|
+
ray(origin: Vec3, dir: Vec3, maxDist: number, filter?: PhysicsQueryFilter): number;
|
|
1311
|
+
shapeCast(shape: PhysicsShape, pose: PhysicsPose, dir: Vec3, maxDist: number, filter?: PhysicsQueryFilter): number;
|
|
1312
|
+
overlap(shape: PhysicsShape, pose: PhysicsPose, filter?: PhysicsQueryFilter): number;
|
|
1313
|
+
/** The answers to the previous tick's queries, in order; empty after an output the host did not
|
|
1314
|
+
* apply. */
|
|
1315
|
+
readonly answers: readonly PhysicsAnswer[];
|
|
1316
|
+
/** The commands the last step's batch refused. A refusal is never a strike. */
|
|
1317
|
+
readonly refused: readonly PhysicsRefusal[];
|
|
1318
|
+
readonly events: readonly PhysicsEvent[];
|
|
1319
|
+
/** True when the last step dropped events past `eventsPerStep`. */
|
|
1320
|
+
readonly overflowed: boolean;
|
|
1321
|
+
/** The entities whose bodies left the world; the host has removed those entities. */
|
|
1322
|
+
readonly escaped: readonly string[];
|
|
1323
|
+
/** The latest state of a body of a subscribed kind, kept across ticks until it is removed or
|
|
1324
|
+
* escapes; undefined for any other body, and before the first state arrives. */
|
|
1325
|
+
body(key: string): PhysicsBodyState | undefined;
|
|
1326
|
+
}
|
|
1327
|
+
/** What a module may ask for in `config`. The server clamps every value to the game's admission. */
|
|
1328
|
+
interface ModuleConfig {
|
|
1329
|
+
tickHz?: TickHz;
|
|
1330
|
+
maxPlayers?: number;
|
|
1331
|
+
preset?: PresetSpec;
|
|
1332
|
+
/** Inputs whose `buttons` intersect this mask carry the sender's `seen` snapshot (§17.5). */
|
|
1333
|
+
lagComp?: {
|
|
1334
|
+
buttons: number;
|
|
1335
|
+
};
|
|
1336
|
+
/** Server physics (§17.10): `true`, or a request. Absent, `false` or `null` = none. The room must
|
|
1337
|
+
* be admitted with physics (a published module's config says so). */
|
|
1338
|
+
physics?: boolean | PhysicsRequest | null;
|
|
1339
|
+
/** The seats' contact policy as data (§17.11 *Contact policy*): every SDK runs the declared pass in
|
|
1340
|
+
* its prediction and replay. Needs a `firstperson` or `vehicle` preset; a bad one is a bad config. */
|
|
1341
|
+
contacts?: ContactDeclaration | null;
|
|
1342
|
+
/** The seats' input settings as data (§17.11 *Input pacing*): whether a starved seat's held input is
|
|
1343
|
+
* repeated (`repeat`), else its genre `preset`'s setting, else the contact policy's preset's, else
|
|
1344
|
+
* on. A bad one is a bad config (input-config.ts `resolveInputConfig` names its path). */
|
|
1345
|
+
inputs?: InputDeclaration | null;
|
|
1346
|
+
/** The room's lifts as data (§17.11 *Lifts*): platforms posed by an exact function of the tick,
|
|
1347
|
+
* which seats collide with and ride. Needs a `firstperson` or `vehicle` preset; a bad one is a bad
|
|
1348
|
+
* config (lifts-config.ts `resolveLifts` names its path). */
|
|
1349
|
+
lifts?: unknown;
|
|
1350
|
+
}
|
|
1351
|
+
/** `config` after defaults: what the room actually runs with. */
|
|
1352
|
+
interface ResolvedModuleConfig {
|
|
1353
|
+
tickHz: TickHz;
|
|
1354
|
+
maxPlayers: number;
|
|
1355
|
+
preset: PresetSpec | null;
|
|
1356
|
+
lagComp: {
|
|
1357
|
+
buttons: number;
|
|
1358
|
+
} | null;
|
|
1359
|
+
/** The physics the room runs (§17.10), null without. */
|
|
1360
|
+
physics: ResolvedPhysics | null;
|
|
1361
|
+
/** The declared contact policy, resolved by the host (§17.11 *Contact policy*); null or absent
|
|
1362
|
+
* without one. The host publishes it in the state's `preset` text; a module's own `ctx.config`
|
|
1363
|
+
* does not carry it. */
|
|
1364
|
+
contacts?: ContactPolicy | null;
|
|
1365
|
+
/** The input settings, resolved by the host (§17.11 *Input pacing*); absent when the module declares
|
|
1366
|
+
* neither inputs nor a contact policy (the room repeats, as before the setting). The host publishes
|
|
1367
|
+
* them in the state's `preset` text. */
|
|
1368
|
+
inputs?: InputPolicy | null;
|
|
1369
|
+
/** The declared lifts, resolved by the host (§17.11 *Lifts*); absent without. The host publishes
|
|
1370
|
+
* them in the state's `preset` text. */
|
|
1371
|
+
lifts?: LiftsPolicy | null;
|
|
1372
|
+
}
|
|
1373
|
+
/** What `init` gets. */
|
|
1374
|
+
interface InitContext {
|
|
1375
|
+
readonly config: ResolvedModuleConfig;
|
|
1376
|
+
/** The room's seed; `random()` is a PRNG seeded from it, so a replay is exact. */
|
|
1377
|
+
readonly seed: number;
|
|
1378
|
+
random(): number;
|
|
1379
|
+
readonly entities: EntityApi;
|
|
1380
|
+
/** Projectiles. `launch` throws here: a launch's `t0` is the tick being stepped, and `init` runs
|
|
1381
|
+
* before the first tick. Launch from `step` or a hook instead. */
|
|
1382
|
+
readonly projectiles: ProjectileApi;
|
|
1383
|
+
/** Physics commands (§17.10), applied before tick 0. Queries throw in `init`. */
|
|
1384
|
+
readonly physics: PhysicsCommands;
|
|
1385
|
+
/** Rate-limited server log line. */
|
|
1386
|
+
log(...args: Json[]): void;
|
|
1387
|
+
}
|
|
1388
|
+
/** What every hook after `init` gets. */
|
|
1389
|
+
interface RoomContext<S> {
|
|
1390
|
+
/** The module's own state, as `init` returned it. Never sent to clients. */
|
|
1391
|
+
state: S;
|
|
1392
|
+
/** Fixed-step tick number, from 0. */
|
|
1393
|
+
readonly tick: number;
|
|
1394
|
+
/** Seconds per tick (1 / tickHz). */
|
|
1395
|
+
readonly dt: number;
|
|
1396
|
+
random(): number;
|
|
1397
|
+
readonly entities: EntityApi;
|
|
1398
|
+
readonly rewind: RewindApi;
|
|
1399
|
+
readonly projectiles: ProjectileApi;
|
|
1400
|
+
/** Server physics (§17.10) in a room whose config asks for it. */
|
|
1401
|
+
readonly physics: PhysicsApi;
|
|
1402
|
+
/** The room's declared lifts (§17.11 *Lifts*). */
|
|
1403
|
+
readonly lifts: LiftsApi;
|
|
1404
|
+
/** Sends a game event to every client, or only to the seat `to`. */
|
|
1405
|
+
emit(type: string, data?: Json, to?: string): void;
|
|
1406
|
+
log(...args: Json[]): void;
|
|
1407
|
+
}
|
|
1408
|
+
/** What a module reads of its declared lifts this tick (§17.11 *Lifts*). */
|
|
1409
|
+
interface LiftsApi {
|
|
1410
|
+
/** The seats a lift pushed against the world this tick, each with the lift (damage is the module's). */
|
|
1411
|
+
crushed(): {
|
|
1412
|
+
seat: string;
|
|
1413
|
+
lift: string;
|
|
1414
|
+
}[];
|
|
1415
|
+
}
|
|
1416
|
+
/** A game's room module. No Date, timers, fetch, require or shared memory exist inside it. */
|
|
1417
|
+
interface GenexRoomModule<S = unknown> {
|
|
1418
|
+
contract: typeof MODULE_CONTRACT;
|
|
1419
|
+
config?: ModuleConfig;
|
|
1420
|
+
/** The `firstperson` and `vehicle` presets' collision world (§17.7): boxes then ramps, fixed for the room's
|
|
1421
|
+
* life. The sandbox validates it at init and writes its hash into the resolved preset's
|
|
1422
|
+
* `params.world`; the game client hands its SDK the same world (`session.setWorld`). Absent =
|
|
1423
|
+
* the empty world (no floor: add one box for open ground). */
|
|
1424
|
+
world?: FirstpersonWorldSpec;
|
|
1425
|
+
init(ctx: InitContext): S;
|
|
1426
|
+
onJoin(ctx: RoomContext<S>, player: PlayerInfo): void;
|
|
1427
|
+
onLeave(ctx: RoomContext<S>, player: PlayerInfo, reason: LeaveReason): void;
|
|
1428
|
+
/** One call per input, in each player's sequence order, before `step`. */
|
|
1429
|
+
onInput(ctx: RoomContext<S>, playerId: string, input: InputData): void;
|
|
1430
|
+
/** Once per tick, after the tick's inputs. */
|
|
1431
|
+
step(ctx: RoomContext<S>): void;
|
|
1432
|
+
}
|
|
1433
|
+
/** Identity helper that types a room module: `export default defineRoom({ ... })`. */
|
|
1434
|
+
declare const defineRoom: <S>(module: GenexRoomModule<S>) => GenexRoomModule<S>;
|
|
1435
|
+
|
|
1436
|
+
export { type InputPolicy as $, EMPTY_WORLD as A, EMPTY_WORLD_HASH as B, type ContactPolicy as C, type EntityApi as D, type EntityData as E, type FirstpersonWorld as F, type EntityInit as G, type HullBody as H, type InputData as I, type Json as J, FIRSTPERSON_DEFAULTS as K, LeaveReason as L, type FirstpersonBody as M, type FirstpersonInput as N, type FirstpersonParams as O, type PlayerInfo as P, type FirstpersonStep as Q, type ResolvedModuleConfig as R, type StandRule as S, type TickHz as T, type GenexRoomModule as U, HeadFriction as V, INPUTS_DEFAULT as W, INPUT_PRESETS as X, type InitContext as Y, InputConfigError as Z, type InputDeclaration as _, type PhysicsErrorCode as a, SHOOTER_CENTER as a$, LIFTS_MAX as a0, LIFT_ID_MAX as a1, LIFT_POINTS_MAX as a2, LIFT_SPIN_MIN_PERIOD as a3, LIFT_TICKS_MAX as a4, LIFT_VALUE_MAX as a5, LiftEase as a6, LiftImpart as a7, type LiftPath as a8, LiftPathMode as a9, type PhysicsJointDesc as aA, type PhysicsLimits as aB, type PhysicsMotor as aC, type PhysicsOverlap as aD, type PhysicsPose as aE, type PhysicsQueryFilter as aF, type PhysicsRefusal as aG, type PhysicsRequest as aH, type PhysicsSetPoseOptions as aI, type PhysicsShape as aJ, Preset as aK, type PresetBody as aL, type PresetDefinition as aM, type PresetField as aN, type PresetInput as aO, type PresetName as aP, type PresetParamRange as aQ, type PresetSpec as aR, type ProjectileApi as aS, type ProjectileField as aT, type ProjectileFlight as aU, type ProjectileHit as aV, type ProjectileInit as aW, type Quat as aX, type ResolvedPhysics as aY, type RewindApi as aZ, type RoomContext as a_, type LiftShape as aa, LiftShapeKind as ab, type LiftSpin as ac, type LiftTooFast as ad, type LiftsApi as ae, LiftsConfigError as af, MODULE_CONTRACT as ag, type ModuleConfig as ah, MoveState as ai, NO_LIFTS as aj, NO_POSED as ak, POSED_MAX as al, PRESETS as am, PRESET_MAX_DT_S as an, type PairRule as ao, type PhysicsAnswer as ap, type PhysicsApi as aq, type PhysicsBodyDesc as ar, type PhysicsBodyState as as, type PhysicsBodyType as at, type PhysicsColliderDesc as au, type PhysicsCommandResult as av, type PhysicsCommands as aw, type PhysicsEvent as ax, type PhysicsGroups as ay, type PhysicsHit as az, InputRepeat as b, type SlideRule as b0, TOPDOWN_DEFAULTS as b1, TopPolicy as b2, type TopdownBody as b3, type TopdownInput as b4, type TopdownParams as b5, VEHICLE_DEFAULTS as b6, type Vec3 as b7, type VehicleBody as b8, type VehicleInput as b9, type VehicleParams as ba, type VehicleStep as bb, type WorldBox as bc, WorldError as bd, WorldLimits as be, type WorldRamp as bf, defineRoom as bg, definitionOf as bh, firstpersonWorld as bi, hullOps as bj, isLiftId as bk, isPresetName as bl, posedWorld as bm, presetInputRepeat as bn, presetParamsValid as bo, resolveContactConfig as bp, resolveFirstpersonParams as bq, resolveInputConfig as br, resolveLifts as bs, resolvePresetParams as bt, resolveTopdownParams as bu, resolveVehicleParams as bv, stepFirstperson as bw, stepPreset as bx, stepTopdown as by, stepVehicle as bz, type LiftVec as c, type PosedSolid as d, type Lift as e, type LiftsPolicy as f, type HullOps as g, type PosedWorld as h, RemotePredict as i, type TopRule as j, type PropSlide as k, ContactDrawing as l, type FirstpersonWorldSpec as m, CONTACT_DEFAULTS as n, CONTACT_PAIRS_MAX as o, CONTACT_PASSES_MAX as p, CONTACT_PRESETS as q, CONTACT_TAG_MAX as r, CONTACT_VALUE_MAX as s, ContactConfigError as t, type ContactDeclaration as u, ContactPreset as v, type ContactPresetParams as w, ContactPush as x, type ContactShape as y, ContactShapeKind as z };
|