@genex-ai/multiplayer 0.17.1 → 0.17.8
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 +761 -9
- package/README.md +285 -1
- package/WORLD-GLTF.md +109 -0
- package/WORLD-SCENE.md +135 -0
- package/dist/authority/fast-udp.d.ts +15 -0
- package/dist/authority/fast-udp.js +14 -0
- package/dist/authority/fast-webtransport.d.ts +67 -0
- package/dist/authority/fast-webtransport.js +21 -0
- package/dist/authority/index.d.ts +2671 -0
- package/dist/authority/index.js +958 -0
- package/dist/authority/module.d.ts +2 -0
- package/dist/authority/module.js +12 -0
- package/dist/bridge-authority.d.ts +101 -0
- package/dist/bridge-authority.global.js +15 -0
- package/dist/bridge-authority.js +291 -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-2NGXXQDS.js +1916 -0
- package/dist/chunk-4R7C2TLH.js +68 -0
- package/dist/{chunk-C52XFIB6.js → chunk-CSQEEANB.js} +217 -4
- package/dist/chunk-FPWP22Y7.js +143 -0
- package/dist/chunk-NFMGIKFZ.js +2627 -0
- package/dist/chunk-OD6NRSX2.js +29 -0
- package/dist/{chunk-3ZUPU3GT.js → chunk-QY3QQNDJ.js} +13 -122
- package/dist/chunk-QZOCUQJT.js +6900 -0
- package/dist/chunk-TSYEWPVO.js +44 -0
- package/dist/chunk-TVTVIMIH.js +675 -0
- package/dist/chunk-YWQ3ALYY.js +69 -0
- package/dist/fast-client--A88q0kk.d.ts +546 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +3 -2
- package/dist/join-CGoInvUM.d.ts +6 -0
- package/dist/level-build-CbRqrYJq.d.ts +140 -0
- package/dist/module-CSpXrzFc.d.ts +1187 -0
- package/dist/physics-predict-C7ZB1gDG.d.ts +1223 -0
- package/dist/protocol.d.ts +222 -2
- package/dist/protocol.js +47 -1
- package/dist/shot-world-D22tqOUT.d.ts +485 -0
- package/dist/world-fit/index.d.ts +549 -0
- package/dist/world-fit/index.js +4763 -0
- package/dist/world-fit/three.d.ts +77 -0
- package/dist/world-fit/three.js +140 -0
- package/package.json +46 -4
|
@@ -0,0 +1,1187 @@
|
|
|
1
|
+
import { P as PresetName, d as FirstpersonWorldSpec, I as WorldKey, C as ClaimVerdict } from './shot-world-D22tqOUT.js';
|
|
2
|
+
|
|
3
|
+
/** What a room runs with, unless its module says otherwise, and the bounds of what it may say (seconds). */
|
|
4
|
+
declare const AuthorityLifetime: {
|
|
5
|
+
readonly IDLE_SEAT_S: number;
|
|
6
|
+
readonly IDLE_SEAT_MIN_S: 30;
|
|
7
|
+
readonly IDLE_SEAT_MAX_S: number;
|
|
8
|
+
readonly ROOM_AGE_S: number;
|
|
9
|
+
readonly ROOM_AGE_MIN_S: 60;
|
|
10
|
+
readonly ROOM_AGE_MAX_S: number;
|
|
11
|
+
};
|
|
12
|
+
/** The resolved limits. */
|
|
13
|
+
interface LifetimePolicy {
|
|
14
|
+
/** A seat whose input has not changed for this long leaves. */
|
|
15
|
+
readonly idleSeatS: number;
|
|
16
|
+
/** A room this old drains and ends. */
|
|
17
|
+
readonly maxRoomAgeS: number;
|
|
18
|
+
}
|
|
19
|
+
declare const LIFETIME_DEFAULT: LifetimePolicy;
|
|
20
|
+
/** A refused `config.lifetime`: the path of the first bad value. */
|
|
21
|
+
declare class LifetimeConfigError extends Error {
|
|
22
|
+
readonly path: string;
|
|
23
|
+
constructor(path: string);
|
|
24
|
+
}
|
|
25
|
+
/** `config.lifetime` as a module declares it: either limit, whole seconds. */
|
|
26
|
+
interface LifetimeDeclaration {
|
|
27
|
+
idleSeatS?: number;
|
|
28
|
+
maxRoomAgeS?: number;
|
|
29
|
+
}
|
|
30
|
+
/** `config.lifetime` resolved: what it gives, the defaults for the rest. Null when it is not declared (the
|
|
31
|
+
* host runs {@link LIFETIME_DEFAULT}). Throws a {@link LifetimeConfigError} naming the path of a value
|
|
32
|
+
* that is not an object, an unknown key, or a limit that is not a whole number inside its bounds. */
|
|
33
|
+
declare function resolveLifetime(given: unknown, path?: string): LifetimePolicy | null;
|
|
34
|
+
|
|
35
|
+
/** How a client draws other seats by default (PROTOCOL.md §17.11 *Drawing other seats*): `predicted` on
|
|
36
|
+
* your own clock (the Rocket League model), `separate` on the render timeline and moved only out of
|
|
37
|
+
* your entity, `timeline` on the render timeline alone. A lag-compensated room (§17.5) judges a shot
|
|
38
|
+
* where the shooter's render timeline drew the target, so its seats are drawn there. */
|
|
39
|
+
declare const ContactDrawing: {
|
|
40
|
+
readonly Predicted: "predicted";
|
|
41
|
+
readonly Separate: "separate";
|
|
42
|
+
readonly Timeline: "timeline";
|
|
43
|
+
};
|
|
44
|
+
type ContactDrawing = (typeof ContactDrawing)[keyof typeof ContactDrawing];
|
|
45
|
+
/** How a side contact's push is split (PROTOCOL.md §17.11 *Contact policy* step 4): `shared` half each
|
|
46
|
+
* (equal masses: a car shoves a car), `mover` by each seat's own closing speed (a player walking into
|
|
47
|
+
* another is stopped by it and never shoves it: a shooter's solid players). */
|
|
48
|
+
declare const ContactPush: {
|
|
49
|
+
readonly Shared: "shared";
|
|
50
|
+
readonly Mover: "mover";
|
|
51
|
+
};
|
|
52
|
+
type ContactPush = (typeof ContactPush)[keyof typeof ContactPush];
|
|
53
|
+
/** The genre presets: each a full table of settings (DESIGN's preset table). */
|
|
54
|
+
declare const ContactPreset: {
|
|
55
|
+
readonly Shooter: "shooter";
|
|
56
|
+
readonly Platformer: "platformer";
|
|
57
|
+
readonly VehicleArena: "vehicle-arena";
|
|
58
|
+
readonly PhysicsSandbox: "physics-sandbox";
|
|
59
|
+
readonly Party: "party";
|
|
60
|
+
};
|
|
61
|
+
type ContactPreset = (typeof ContactPreset)[keyof typeof ContactPreset];
|
|
62
|
+
/** What happens when one seat comes to rest on another's top. */
|
|
63
|
+
declare const TopPolicy: {
|
|
64
|
+
/** Rest on the head: a landing stands within `center` of the lower's half width and slides off by
|
|
65
|
+
* `slide` elsewhere; a resting seat holds while its centre is over the footprint; `ride`: carried. */
|
|
66
|
+
readonly Stand: "stand";
|
|
67
|
+
/** Frictionless: the upper seat is slid off within a bounded time. */
|
|
68
|
+
readonly Slide: "slide";
|
|
69
|
+
/** A stomp: the upper seat bounces off, and the module hears it. */
|
|
70
|
+
readonly Bounce: "bounce";
|
|
71
|
+
/** No collision at all. */
|
|
72
|
+
readonly Ghost: "ghost";
|
|
73
|
+
};
|
|
74
|
+
type TopPolicy = (typeof TopPolicy)[keyof typeof TopPolicy];
|
|
75
|
+
/** A seat's contact shape. */
|
|
76
|
+
declare const ContactShapeKind: {
|
|
77
|
+
/** A box turned with the seat's heading and laid on its support up (a vehicle). */
|
|
78
|
+
readonly Box: "box";
|
|
79
|
+
/** An upright axis-aligned square footprint (a `firstperson` character). */
|
|
80
|
+
readonly Hull: "hull";
|
|
81
|
+
};
|
|
82
|
+
type ContactShapeKind = (typeof ContactShapeKind)[keyof typeof ContactShapeKind];
|
|
83
|
+
/** A seat's contact shape, room units: a box's length along the heading, width across it and top
|
|
84
|
+
* above the base; a hull's half side and height. */
|
|
85
|
+
type ContactShape = {
|
|
86
|
+
readonly kind: typeof ContactShapeKind.Box;
|
|
87
|
+
readonly length: number;
|
|
88
|
+
readonly width: number;
|
|
89
|
+
readonly top: number;
|
|
90
|
+
} | {
|
|
91
|
+
readonly kind: typeof ContactShapeKind.Hull;
|
|
92
|
+
readonly radius: number;
|
|
93
|
+
readonly height: number;
|
|
94
|
+
};
|
|
95
|
+
/** A slide: the parting brought up to `speed` (units/s) by at most `step` a tick, `share` of it on the
|
|
96
|
+
* upper seat (the rest on the lower). `step` null: in one tick. */
|
|
97
|
+
interface SlideRule {
|
|
98
|
+
readonly speed: number;
|
|
99
|
+
readonly step: number | null;
|
|
100
|
+
readonly share: number;
|
|
101
|
+
}
|
|
102
|
+
/** A resolved stand (§17.11 step 6 `stand`): carried by the lower seat when `ride`; a landing stands
|
|
103
|
+
* only within `center` × the lower's half width (null: anywhere it touches), else slides by `slide`. */
|
|
104
|
+
interface StandRule {
|
|
105
|
+
readonly policy: typeof TopPolicy.Stand;
|
|
106
|
+
readonly ride: boolean;
|
|
107
|
+
readonly center: number | null;
|
|
108
|
+
readonly slide: SlideRule;
|
|
109
|
+
}
|
|
110
|
+
/** A resolved top rule. A bounce under `stompMin` closing speed slides by its `slide`. */
|
|
111
|
+
type TopRule = StandRule | ({
|
|
112
|
+
readonly policy: typeof TopPolicy.Slide;
|
|
113
|
+
} & SlideRule) | {
|
|
114
|
+
readonly policy: typeof TopPolicy.Bounce;
|
|
115
|
+
readonly stompMin: number;
|
|
116
|
+
readonly bounceSpeed: number;
|
|
117
|
+
readonly jumpBoost: number;
|
|
118
|
+
readonly slide: SlideRule;
|
|
119
|
+
} | {
|
|
120
|
+
readonly policy: typeof TopPolicy.Ghost;
|
|
121
|
+
};
|
|
122
|
+
/** A per-pair override: two seats, one carrying tag `a` and the other `b`, use `top`. */
|
|
123
|
+
interface PairRule {
|
|
124
|
+
readonly a: string;
|
|
125
|
+
readonly b: string;
|
|
126
|
+
readonly top: TopRule;
|
|
127
|
+
}
|
|
128
|
+
/** The resolved policy: every setting filled (from the module, else its preset's table). */
|
|
129
|
+
interface ContactPolicy {
|
|
130
|
+
readonly preset: ContactPreset;
|
|
131
|
+
readonly shape: ContactShape;
|
|
132
|
+
/** The hull the static world is grown by (the movement preset's radius and height) and its skin. */
|
|
133
|
+
readonly hull: {
|
|
134
|
+
readonly radius: number;
|
|
135
|
+
readonly height: number;
|
|
136
|
+
readonly skin: number;
|
|
137
|
+
};
|
|
138
|
+
/** How far under a base the support surface is found (the preset's `groundTrace`). */
|
|
139
|
+
readonly groundTrace: number;
|
|
140
|
+
/** A contact whose normal is at least this close to the lower seat's support up is a top contact;
|
|
141
|
+
* a lower seat whose support up's z is under it cannot be stood on. */
|
|
142
|
+
readonly supportCos: number;
|
|
143
|
+
/** The least closing speed of an axis in the entry-time test (units/s). */
|
|
144
|
+
readonly entrySpeed: number;
|
|
145
|
+
/** How recently a pair must have come down across the plane for the across axis to be its contact
|
|
146
|
+
* axis (seconds): an older overlap was there before the last pass. */
|
|
147
|
+
readonly entryWindow: number;
|
|
148
|
+
/** The share of the closing speed a side contact keeps. */
|
|
149
|
+
readonly restitution: number;
|
|
150
|
+
/** Pile passes a tick at most, and the depth a pair may stay inside another after the first. */
|
|
151
|
+
readonly passes: number;
|
|
152
|
+
readonly slop: number;
|
|
153
|
+
/** How far over a lower seat's top the upper seat is set (units). */
|
|
154
|
+
readonly roofSkin: number;
|
|
155
|
+
/** Each velocity axis a contact writes is clamped to ±this; null: not clamped. */
|
|
156
|
+
readonly maxVelocity: number | null;
|
|
157
|
+
/** A side contact stops both seats' `spin` (a vehicle's yaw rate). */
|
|
158
|
+
readonly stopSpin: boolean;
|
|
159
|
+
readonly top: TopRule;
|
|
160
|
+
/** The slide a top contact becomes when the lower seat's support cannot be stood on. */
|
|
161
|
+
readonly offWall: SlideRule;
|
|
162
|
+
readonly pairs: readonly PairRule[];
|
|
163
|
+
/** How a client draws other seats when the game gives no `seatDrawing`. */
|
|
164
|
+
readonly drawing: ContactDrawing;
|
|
165
|
+
/** How a side contact's push is split between the two seats (§17.11 step 4). */
|
|
166
|
+
readonly push: ContactPush;
|
|
167
|
+
/** How a dynamic module body resting on a seat's top is treated (§17.11 *Contact policy*, props). */
|
|
168
|
+
readonly headFriction: HeadFriction;
|
|
169
|
+
/** The nudge that slides a prop off a head under `headFriction: slide`: up to `speed` by `step` a tick. */
|
|
170
|
+
readonly propSlide: PropSlide;
|
|
171
|
+
}
|
|
172
|
+
/** How a prop on a seat's top is treated: `slide` off (its hull frictionless, nudged by `propSlide`),
|
|
173
|
+
* or `carry` (friction holds it while level; it slides past its friction angle). */
|
|
174
|
+
declare const HeadFriction: {
|
|
175
|
+
readonly Slide: "slide";
|
|
176
|
+
readonly Carry: "carry";
|
|
177
|
+
};
|
|
178
|
+
type HeadFriction = (typeof HeadFriction)[keyof typeof HeadFriction];
|
|
179
|
+
/** A prop's slide off a head: its parting brought up to `speed` (u/s) by at most `step` a tick. */
|
|
180
|
+
interface PropSlide {
|
|
181
|
+
readonly speed: number;
|
|
182
|
+
readonly step: number;
|
|
183
|
+
}
|
|
184
|
+
/** A refused `config.contacts`: the path of the first bad value and why. */
|
|
185
|
+
declare class ContactConfigError extends RangeError {
|
|
186
|
+
readonly path: string;
|
|
187
|
+
constructor(path: string, why: string);
|
|
188
|
+
}
|
|
189
|
+
/** Per-pair overrides a module may declare at most, and a tag's length at most. */
|
|
190
|
+
declare const CONTACT_PAIRS_MAX = 32;
|
|
191
|
+
declare const CONTACT_TAG_MAX = 64;
|
|
192
|
+
/** Pile passes a module may ask for at most. */
|
|
193
|
+
declare const CONTACT_PASSES_MAX = 64;
|
|
194
|
+
/** Every length and speed a module may give at most (units, units/s). */
|
|
195
|
+
declare const CONTACT_VALUE_MAX = 100000;
|
|
196
|
+
/** The share of a head's half width a shooter's landing stands within (owner: "stand on it with some
|
|
197
|
+
* slide off"; 0.25 from the Lift Yard's aimed landings, owner 2026-11-02: a landing with over 3/8 of
|
|
198
|
+
* the foot off the head slides). */
|
|
199
|
+
declare const SHOOTER_CENTER = 0.25;
|
|
200
|
+
/** One preset's table: the top rule by seat shape, the side restitution and the passes. */
|
|
201
|
+
interface PresetTable {
|
|
202
|
+
readonly character: TopRule;
|
|
203
|
+
readonly vehicle: TopRule;
|
|
204
|
+
readonly restitution: number;
|
|
205
|
+
readonly passes: number;
|
|
206
|
+
readonly vehicleOffWall: SlideRule;
|
|
207
|
+
readonly stopSpin: boolean;
|
|
208
|
+
readonly drawing: ContactDrawing;
|
|
209
|
+
readonly push: ContactPush;
|
|
210
|
+
readonly headFriction: HeadFriction;
|
|
211
|
+
}
|
|
212
|
+
/** The genre presets' tables (PROTOCOL.md §17.11 *Contact policy*, the preset table). */
|
|
213
|
+
declare const CONTACT_PRESETS: {
|
|
214
|
+
readonly [P in ContactPreset]: PresetTable;
|
|
215
|
+
};
|
|
216
|
+
/** The settings every preset shares (the preset table's SUPPORT_COS and PILE_SLOP rows). */
|
|
217
|
+
declare const CONTACT_DEFAULTS: Readonly<{
|
|
218
|
+
supportCos: 0.7;
|
|
219
|
+
entrySpeed: 1;
|
|
220
|
+
entryWindow: number;
|
|
221
|
+
slop: 0.1;
|
|
222
|
+
roofSkin: 0.01;
|
|
223
|
+
}>;
|
|
224
|
+
/** A slide or top rule as a module declares it: every key but `policy` optional (the preset fills it). */
|
|
225
|
+
type Declared<T> = {
|
|
226
|
+
readonly [K in keyof T]?: T[K];
|
|
227
|
+
};
|
|
228
|
+
/** `config.contacts` as a module declares it (PROTOCOL.md §17.11 *Contact policy*): the preset, and any
|
|
229
|
+
* cell of its table overridden. Resolved by {@link resolveContactConfig}. */
|
|
230
|
+
interface ContactDeclaration {
|
|
231
|
+
readonly preset: ContactPreset;
|
|
232
|
+
readonly shape?: ContactShape;
|
|
233
|
+
readonly top?: {
|
|
234
|
+
readonly policy: typeof TopPolicy.Stand;
|
|
235
|
+
readonly ride?: boolean;
|
|
236
|
+
readonly center?: number | null;
|
|
237
|
+
readonly slide?: Declared<SlideRule>;
|
|
238
|
+
} | {
|
|
239
|
+
readonly policy: typeof TopPolicy.Ghost;
|
|
240
|
+
} | ({
|
|
241
|
+
readonly policy: typeof TopPolicy.Slide;
|
|
242
|
+
} & Declared<SlideRule>) | {
|
|
243
|
+
readonly policy: typeof TopPolicy.Bounce;
|
|
244
|
+
readonly stompMin?: number;
|
|
245
|
+
readonly bounceSpeed?: number;
|
|
246
|
+
readonly jumpBoost?: number;
|
|
247
|
+
};
|
|
248
|
+
readonly offWall?: Declared<SlideRule>;
|
|
249
|
+
readonly restitution?: number;
|
|
250
|
+
readonly passes?: number;
|
|
251
|
+
readonly slop?: number;
|
|
252
|
+
readonly supportCos?: number;
|
|
253
|
+
readonly entrySpeed?: number;
|
|
254
|
+
readonly entryWindow?: number;
|
|
255
|
+
readonly roofSkin?: number;
|
|
256
|
+
readonly maxVelocity?: number | null;
|
|
257
|
+
readonly stopSpin?: boolean;
|
|
258
|
+
readonly pairs?: readonly {
|
|
259
|
+
readonly a: string;
|
|
260
|
+
readonly b: string;
|
|
261
|
+
readonly top: NonNullable<ContactDeclaration['top']>;
|
|
262
|
+
}[];
|
|
263
|
+
readonly drawing?: ContactDrawing;
|
|
264
|
+
readonly push?: ContactPush;
|
|
265
|
+
readonly headFriction?: HeadFriction;
|
|
266
|
+
readonly propSlide?: Declared<PropSlide>;
|
|
267
|
+
}
|
|
268
|
+
/** The movement preset the seats step with, as the contact policy reads it: its hull and ground. */
|
|
269
|
+
interface ContactPresetParams {
|
|
270
|
+
readonly name: string;
|
|
271
|
+
readonly params: Readonly<Record<string, number>>;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* `config.contacts` resolved against the room's movement preset (§17.11 *Contact policy*): every setting
|
|
275
|
+
* the module gave, else the genre preset's. Throws {@link ContactConfigError} (the path from `path`,
|
|
276
|
+
* default `$.config.contacts`) for a value that is not an object, an unknown key, a preset or policy
|
|
277
|
+
* that does not exist, a number out of range, a shape without its dimensions, more than
|
|
278
|
+
* CONTACT_PAIRS_MAX pair rules, or a room whose movement preset steps through no world.
|
|
279
|
+
*/
|
|
280
|
+
declare function resolveContactConfig(given: unknown, preset: ContactPresetParams | null, path?: string): ContactPolicy;
|
|
281
|
+
|
|
282
|
+
/** Whether a predicting room repeats a starved seat's held input (§17.11 *Input pacing*): `on`, the
|
|
283
|
+
* seat is stepped under its last input and its client reconciles at `ack + inRep` and throttles; `off`,
|
|
284
|
+
* the seat holds on a starved tick, its client reads the plain ack and never throttles. */
|
|
285
|
+
declare const InputRepeat: {
|
|
286
|
+
readonly On: "on";
|
|
287
|
+
readonly Off: "off";
|
|
288
|
+
};
|
|
289
|
+
type InputRepeat = (typeof InputRepeat)[keyof typeof InputRepeat];
|
|
290
|
+
/** The resolved input settings a room runs with. `settleMs` is there only when set, so a room without it
|
|
291
|
+
* publishes the text it always did. */
|
|
292
|
+
interface InputPolicy {
|
|
293
|
+
readonly repeat: InputRepeat;
|
|
294
|
+
/** §17.11 *A quiet seat*: after this long with no input the room steps a seat's body under a stick at
|
|
295
|
+
* rest until it lies still. Absent: the body holds where its last input left it. */
|
|
296
|
+
readonly settleMs?: number;
|
|
297
|
+
}
|
|
298
|
+
/** The range of `config.inputs.settleMs`, ms: above any hitch a game should sit through, below a long AFK. */
|
|
299
|
+
declare const INPUT_SETTLE_MS: {
|
|
300
|
+
readonly MIN: 100;
|
|
301
|
+
readonly MAX: 60000;
|
|
302
|
+
};
|
|
303
|
+
/**
|
|
304
|
+
* Each genre preset's input settings. Measured on the bench (interleaved runs, one-sided Mann–Whitney,
|
|
305
|
+
* evidence folder input-repeat/ and R2): `vehicle-arena` is off, because on car-ball 3v3 at a clean
|
|
306
|
+
* 200 ms round trip the repeats and the throttle cost other players' cars (remote corrP95 and errAtLead
|
|
307
|
+
* worse, p ≤ .013) while a client that never asks for them stays within the base; `shooter` is on,
|
|
308
|
+
* because Mechanism Hall and Blast Yard under loss draw your own character with fewer pops and hitches
|
|
309
|
+
* and stack and rocket better with it. `platformer`, `party` and `physics-sandbox` seat characters as a
|
|
310
|
+
* shooter does and have no measurement of their own, so they are on as measured for characters.
|
|
311
|
+
*/
|
|
312
|
+
declare const INPUT_PRESETS: {
|
|
313
|
+
readonly shooter: {
|
|
314
|
+
readonly repeat: "on";
|
|
315
|
+
};
|
|
316
|
+
readonly platformer: {
|
|
317
|
+
readonly repeat: "on";
|
|
318
|
+
};
|
|
319
|
+
readonly "vehicle-arena": {
|
|
320
|
+
readonly repeat: "off";
|
|
321
|
+
};
|
|
322
|
+
readonly "physics-sandbox": {
|
|
323
|
+
readonly repeat: "on";
|
|
324
|
+
};
|
|
325
|
+
readonly party: {
|
|
326
|
+
readonly repeat: "on";
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
/** A room that names no genre and no setting repeats, as every predicting room did before the setting. */
|
|
330
|
+
declare const INPUTS_DEFAULT: InputPolicy;
|
|
331
|
+
/** A refused `config.inputs`: the path of the first bad value. */
|
|
332
|
+
declare class InputConfigError extends Error {
|
|
333
|
+
readonly path: string;
|
|
334
|
+
constructor(path: string);
|
|
335
|
+
}
|
|
336
|
+
/** `config.inputs` as a module declares it: a genre preset (default the contact policy's), and a
|
|
337
|
+
* setting that overrides the preset's. */
|
|
338
|
+
interface InputDeclaration {
|
|
339
|
+
preset?: ContactPreset;
|
|
340
|
+
repeat?: InputRepeat;
|
|
341
|
+
settleMs?: number;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* `config.inputs` resolved (§17.11 *Input pacing*): `repeat` as given, else the table of the genre preset
|
|
345
|
+
* (`inputs.preset`, else `contactsPreset`, the room's declared contact policy's), else on. Null when the
|
|
346
|
+
* module declares neither inputs nor a contact policy (a room without the setting: on). Throws an
|
|
347
|
+
* {@link InputConfigError} naming the path (default `$.config.inputs`) of a value that is not an
|
|
348
|
+
* object, an unknown key, a preset that is not a genre preset or a setting that is not `on` or `off`.
|
|
349
|
+
*/
|
|
350
|
+
declare function resolveInputConfig(given: unknown, contactsPreset: ContactPreset | null, path?: string): InputPolicy | null;
|
|
351
|
+
/**
|
|
352
|
+
* The room's input repeat setting as a client reads it from the state's `preset` text (JSON, its
|
|
353
|
+
* `inputs` key): `off` only when the text says so outright, `on` for anything else. Absent is a room
|
|
354
|
+
* without the setting or an older host, which repeats; for a hostile value the room's own fields are the
|
|
355
|
+
* truth, and a room that is off writes none of them.
|
|
356
|
+
*/
|
|
357
|
+
declare function presetInputRepeat(presetText: string): InputRepeat;
|
|
358
|
+
|
|
359
|
+
/** A lift's shape, room units, about its centre. */
|
|
360
|
+
declare const LiftShapeKind: {
|
|
361
|
+
/** A box `half` (x, y, z): axis-aligned, or turned about z when it spins (a sweeper bar). */
|
|
362
|
+
readonly Box: "box";
|
|
363
|
+
/** An upright disc of `radius`, `half` its half height (a turntable, a spinner). */
|
|
364
|
+
readonly Disc: "disc";
|
|
365
|
+
};
|
|
366
|
+
type LiftShapeKind = (typeof LiftShapeKind)[keyof typeof LiftShapeKind];
|
|
367
|
+
/** How a path runs its points. */
|
|
368
|
+
declare const LiftPathMode: {
|
|
369
|
+
/** Out along the legs, then back along them. */
|
|
370
|
+
readonly PingPong: "pingpong";
|
|
371
|
+
/** Round the points and from the last back to the first. */
|
|
372
|
+
readonly Loop: "loop";
|
|
373
|
+
};
|
|
374
|
+
type LiftPathMode = (typeof LiftPathMode)[keyof typeof LiftPathMode];
|
|
375
|
+
/** How a leg moves between its points. */
|
|
376
|
+
declare const LiftEase: {
|
|
377
|
+
/** At a steady speed. */
|
|
378
|
+
readonly Linear: "linear";
|
|
379
|
+
/** Starting and stopping at rest (`s·s·(3 − 2s)`; its peak speed 1.5 times the mean). */
|
|
380
|
+
readonly Smooth: "smooth";
|
|
381
|
+
};
|
|
382
|
+
type LiftEase = (typeof LiftEase)[keyof typeof LiftEase];
|
|
383
|
+
/** What a rider keeps of its lift's motion when it leaves it. */
|
|
384
|
+
declare const LiftImpart: {
|
|
385
|
+
/** The lift's velocity at the rider's point (Unreal's and Godot's default). */
|
|
386
|
+
readonly All: "all";
|
|
387
|
+
/** Nothing: the rider leaves with its own velocity. */
|
|
388
|
+
readonly None: "none";
|
|
389
|
+
};
|
|
390
|
+
type LiftImpart = (typeof LiftImpart)[keyof typeof LiftImpart];
|
|
391
|
+
/** A point or size in room units. */
|
|
392
|
+
type LiftVec = readonly [number, number, number];
|
|
393
|
+
/** A lift's shape. */
|
|
394
|
+
type LiftShape = {
|
|
395
|
+
readonly kind: typeof LiftShapeKind.Box;
|
|
396
|
+
readonly half: LiftVec;
|
|
397
|
+
} | {
|
|
398
|
+
readonly kind: typeof LiftShapeKind.Disc;
|
|
399
|
+
readonly radius: number;
|
|
400
|
+
readonly half: number;
|
|
401
|
+
};
|
|
402
|
+
/** A resolved path: `points` (float32), a leg's ticks and a wait's ticks per point. */
|
|
403
|
+
interface LiftPath {
|
|
404
|
+
readonly points: readonly LiftVec[];
|
|
405
|
+
readonly legs: readonly number[];
|
|
406
|
+
readonly waits: readonly number[];
|
|
407
|
+
readonly mode: LiftPathMode;
|
|
408
|
+
readonly ease: LiftEase;
|
|
409
|
+
readonly phase: number;
|
|
410
|
+
/** The ticks of one whole cycle. */
|
|
411
|
+
readonly period: number;
|
|
412
|
+
}
|
|
413
|
+
/** A resolved spin: a turn every `period` ticks, `dir` 1 (counter-clockwise from above) or −1. */
|
|
414
|
+
interface LiftSpin {
|
|
415
|
+
readonly period: number;
|
|
416
|
+
readonly dir: 1 | -1;
|
|
417
|
+
readonly phase: number;
|
|
418
|
+
}
|
|
419
|
+
/** One resolved platform. */
|
|
420
|
+
interface Lift {
|
|
421
|
+
readonly id: string;
|
|
422
|
+
readonly shape: LiftShape;
|
|
423
|
+
/** The centre when it has no path (else the path's first point). */
|
|
424
|
+
readonly at: LiftVec;
|
|
425
|
+
readonly path: LiftPath | null;
|
|
426
|
+
readonly spin: LiftSpin | null;
|
|
427
|
+
/** A conveyor's surface velocity in its own frame (u/s), or null. */
|
|
428
|
+
readonly surface: readonly [number, number] | null;
|
|
429
|
+
readonly impart: LiftImpart;
|
|
430
|
+
/** Still until the module calls it; a call runs one cycle of its path. */
|
|
431
|
+
readonly called: boolean;
|
|
432
|
+
}
|
|
433
|
+
/** The resolved `config.lifts`. */
|
|
434
|
+
interface LiftsPolicy {
|
|
435
|
+
readonly platforms: readonly Lift[];
|
|
436
|
+
/** Whether seats ride level dynamic module bodies too. */
|
|
437
|
+
readonly bodies: boolean;
|
|
438
|
+
}
|
|
439
|
+
/** No lifts: what the slots run over in a room whose only carry is by seats (a stand rule that rides). */
|
|
440
|
+
declare const NO_LIFTS: LiftsPolicy;
|
|
441
|
+
/** A platform refused for moving too fast: its id, its fastest point's move a tick, the most allowed
|
|
442
|
+
* (the hull radius) and the tick rate, all units a tick. */
|
|
443
|
+
interface LiftTooFast {
|
|
444
|
+
readonly platform: string;
|
|
445
|
+
readonly move: number;
|
|
446
|
+
readonly allowed: number;
|
|
447
|
+
readonly tickHz: number;
|
|
448
|
+
}
|
|
449
|
+
/** A refused `config.lifts`: the path of the first bad value and why (`fast`: a platform too fast). */
|
|
450
|
+
declare class LiftsConfigError extends RangeError {
|
|
451
|
+
readonly path: string;
|
|
452
|
+
readonly fast: LiftTooFast | undefined;
|
|
453
|
+
constructor(path: string, why: string, fast?: LiftTooFast);
|
|
454
|
+
}
|
|
455
|
+
/** Platforms a module may declare at most, points a path may hold, and a lift id's length. */
|
|
456
|
+
declare const LIFTS_MAX = 32;
|
|
457
|
+
declare const LIFT_POINTS_MAX = 16;
|
|
458
|
+
declare const LIFT_ID_MAX = 32;
|
|
459
|
+
/** Every position, size and speed's magnitude at most (units, units/s). */
|
|
460
|
+
declare const LIFT_VALUE_MAX = 1000000;
|
|
461
|
+
/** Every tick count (a leg, a wait, a phase, a spin's period) at most: about 4.6 days at 120 Hz. */
|
|
462
|
+
declare const LIFT_TICKS_MAX: number;
|
|
463
|
+
/** A spin's fewest ticks a turn. */
|
|
464
|
+
declare const LIFT_SPIN_MIN_PERIOD = 8;
|
|
465
|
+
/** Whether `id` is a lift id: 1 to LIFT_ID_MAX of a-z, 0-9 and -. */
|
|
466
|
+
declare const isLiftId: (id: unknown) => id is string;
|
|
467
|
+
/**
|
|
468
|
+
* `config.lifts` resolved against the room's movement preset and tick rate (§17.11 *Lifts*). Throws
|
|
469
|
+
* {@link LiftsConfigError} (the path from `path`, default `$.config.lifts`) for a value that is not an
|
|
470
|
+
* object, an unknown key, an empty list or more than LIFTS_MAX platforms, an id that is not 1-32 of
|
|
471
|
+
* `[a-z0-9-]` or repeats, a shape without its sizes, a path whose points, legs and waits do not match, a
|
|
472
|
+
* tick count that is not a whole number, a phase on a called lift, a value out of range, a platform any
|
|
473
|
+
* point of which moves more than the hull radius a tick (named, with its speed and the most allowed),
|
|
474
|
+
* or a room whose movement preset steps through no world.
|
|
475
|
+
*/
|
|
476
|
+
declare function resolveLifts(given: unknown, preset: ContactPresetParams | null, tickHz: number, path?: string): LiftsPolicy;
|
|
477
|
+
|
|
478
|
+
declare const MODULE_CONTRACT = 1;
|
|
479
|
+
/** A JSON value: what crosses the sandbox boundary in events, logs and entity `data`. */
|
|
480
|
+
type Json = null | boolean | number | string | Json[] | {
|
|
481
|
+
[key: string]: Json;
|
|
482
|
+
};
|
|
483
|
+
/** A point or a direction in world units. */
|
|
484
|
+
interface Vec3 {
|
|
485
|
+
x: number;
|
|
486
|
+
y: number;
|
|
487
|
+
z: number;
|
|
488
|
+
}
|
|
489
|
+
/** Fixed simulation rates a module may choose. */
|
|
490
|
+
/** A room's tick rate. 120 is a physics room's only (PROTOCOL.md §17.11, car-ball rooms). */
|
|
491
|
+
type TickHz = 20 | 30 | 60 | 120;
|
|
492
|
+
/** Why a player left (`onLeave`). */
|
|
493
|
+
declare const LeaveReason: {
|
|
494
|
+
/** The client left on purpose. */
|
|
495
|
+
readonly LEFT: "left";
|
|
496
|
+
/** The connection dropped and the reconnection grace ran out. */
|
|
497
|
+
readonly DROPPED: "dropped";
|
|
498
|
+
/** A newer session of the same player took the seat. */
|
|
499
|
+
readonly SUPERSEDED: "superseded";
|
|
500
|
+
/** The player's session was revoked. */
|
|
501
|
+
readonly REVOKED: "revoked";
|
|
502
|
+
/** The room is closing (drain, halt or fault). */
|
|
503
|
+
readonly CLOSING: "closing";
|
|
504
|
+
/** The seat's input had not changed for the room's `config.lifetime.idleSeatS`. */
|
|
505
|
+
readonly IDLE: "idle";
|
|
506
|
+
};
|
|
507
|
+
type LeaveReason = (typeof LeaveReason)[keyof typeof LeaveReason];
|
|
508
|
+
/** A seated player, as the server verified them. */
|
|
509
|
+
interface PlayerInfo {
|
|
510
|
+
/** The seat's session id: the key of `AuthorityRoomState.players` and an entity's `owner`. */
|
|
511
|
+
id: string;
|
|
512
|
+
/** The stable per-game player id (PROTOCOL.md §3.3). Key saves and scores by it. */
|
|
513
|
+
uid: string;
|
|
514
|
+
name: string;
|
|
515
|
+
/** '' = no team. */
|
|
516
|
+
team: string;
|
|
517
|
+
}
|
|
518
|
+
/** One frame of a player's input (`AuthorityInput`), already sanitized by the server: move axes in
|
|
519
|
+
* [-1, 1], yaw and pitch wrapped to [-π, π), `buttons` an integer bitmask below 2^24. */
|
|
520
|
+
interface InputData {
|
|
521
|
+
moveX: number;
|
|
522
|
+
moveY: number;
|
|
523
|
+
moveZ: number;
|
|
524
|
+
yaw: number;
|
|
525
|
+
pitch: number;
|
|
526
|
+
buttons: number;
|
|
527
|
+
}
|
|
528
|
+
/** An entity as the module reads it. Positions and velocities are float32 on the wire. */
|
|
529
|
+
interface EntityData {
|
|
530
|
+
readonly id: string;
|
|
531
|
+
kind: string;
|
|
532
|
+
/** Owning seat's session id, or ''. */
|
|
533
|
+
owner: string;
|
|
534
|
+
x: number;
|
|
535
|
+
y: number;
|
|
536
|
+
z: number;
|
|
537
|
+
vx: number;
|
|
538
|
+
vy: number;
|
|
539
|
+
vz: number;
|
|
540
|
+
/** Heading in radians. */
|
|
541
|
+
yaw: number;
|
|
542
|
+
hp: number;
|
|
543
|
+
/** Game-defined bitmask below 2^24. */
|
|
544
|
+
flags: number;
|
|
545
|
+
/** Bump it to make clients snap instead of interpolate (a teleport or a respawn). */
|
|
546
|
+
epoch: number;
|
|
547
|
+
/** Free-form JSON, at most 256 characters once encoded. */
|
|
548
|
+
data: Json;
|
|
549
|
+
/** A projectile's launch tick (§17.9); 0 = not a projectile. Written by `ctx.projectiles.launch`. */
|
|
550
|
+
t0: number;
|
|
551
|
+
/** A projectile's downward acceleration along −z, units/s², in [0, 1000]. */
|
|
552
|
+
g: number;
|
|
553
|
+
/** The seat that fired a projectile, '' = none (a server projectile). */
|
|
554
|
+
src: string;
|
|
555
|
+
/** A `firstperson` body's aim pitch, radians (§17.7): the preset writes it from the input. */
|
|
556
|
+
pitch: number;
|
|
557
|
+
/** A `firstperson` body's duck amount, 0 standing to 1 fully ducked: the preset writes it. */
|
|
558
|
+
duck: number;
|
|
559
|
+
/** A `firstperson` or `vehicle` body's movement bits (1 onGround, 2 ducking, 4 jumpHeld; a
|
|
560
|
+
* vehicle keeps 1 and 4): the preset writes them; write 0 on a respawn to start a life in the air
|
|
561
|
+
* with jump released. */
|
|
562
|
+
mstate: number;
|
|
563
|
+
/** A `firstperson` body's speed multiplier, or a `vehicle`'s multiplier of both top speeds,
|
|
564
|
+
* [0, 4], default 1: yours to write (a weapon's or a wound's speed); the preset only reads it. */
|
|
565
|
+
cap: number;
|
|
566
|
+
/** A `vehicle` body's yaw rate, rad/s, positive toward +yaw (§17.7): the preset writes it; write 0
|
|
567
|
+
* on a respawn. */
|
|
568
|
+
spin: number;
|
|
569
|
+
/** A `vehicle` body's boost fuel, [0, 1], default 0: write it to refuel (a boost pad, a respawn);
|
|
570
|
+
* the preset only burns it. */
|
|
571
|
+
fuel: number;
|
|
572
|
+
}
|
|
573
|
+
/** The fields only `projectiles.launch` writes. */
|
|
574
|
+
type ProjectileField = 't0' | 'g' | 'src';
|
|
575
|
+
/** What `entities.spawn` takes: a kind, and any field to start from other than the default (a
|
|
576
|
+
* projectile's fields come from `projectiles.launch`). */
|
|
577
|
+
type EntityInit = {
|
|
578
|
+
kind: string;
|
|
579
|
+
id?: string;
|
|
580
|
+
} & Partial<Omit<EntityData, 'id' | 'kind' | ProjectileField>>;
|
|
581
|
+
/** The room's entities. Every write is recorded and only changed entities go out each tick. */
|
|
582
|
+
interface EntityApi {
|
|
583
|
+
/** Adds an entity and returns its id (`id` when given and free, else a fresh one). */
|
|
584
|
+
spawn(entity: EntityInit): string;
|
|
585
|
+
get(id: string): EntityData | undefined;
|
|
586
|
+
update(id: string, patch: Partial<Omit<EntityData, 'id' | ProjectileField>>): void;
|
|
587
|
+
remove(id: string): void;
|
|
588
|
+
/** The first entity this seat owns. */
|
|
589
|
+
ownedBy(playerId: string): EntityData | undefined;
|
|
590
|
+
all(): readonly EntityData[];
|
|
591
|
+
}
|
|
592
|
+
/** An entity as the shooter saw it (`RewindApi.pose`): where it stood, which way it faced (radians)
|
|
593
|
+
* and how far it ducked (0 standing to 1 fully ducked; 0 for an entity that never ducks). */
|
|
594
|
+
interface RewoundPose extends Vec3 {
|
|
595
|
+
yaw: number;
|
|
596
|
+
duck: number;
|
|
597
|
+
}
|
|
598
|
+
/** Lag compensation. Both calls read the snapshot of positions the current input's sender saw when
|
|
599
|
+
* they pressed it (only while handling an input whose buttons match `config.lagComp.buttons`;
|
|
600
|
+
* otherwise the current positions). */
|
|
601
|
+
interface RewindApi {
|
|
602
|
+
hitscan(origin: Vec3, dir: Vec3, options?: {
|
|
603
|
+
range?: number;
|
|
604
|
+
radius?: number;
|
|
605
|
+
kinds?: string[];
|
|
606
|
+
}): {
|
|
607
|
+
id: string;
|
|
608
|
+
dist: number;
|
|
609
|
+
} | null;
|
|
610
|
+
overlapAt(center: Vec3, radius: number): string[];
|
|
611
|
+
/** Where the shooter of the lag-compensated input being handled saw entity `id` (the position
|
|
612
|
+
* `hitscan` and `overlapAt` judge it at), its live position otherwise; null for an unknown id, a
|
|
613
|
+
* projectile or a physics body entity. For a module's own tests against things that are not
|
|
614
|
+
* spheres (a door, a shutter): §17.5. */
|
|
615
|
+
at(id: string): Vec3 | null;
|
|
616
|
+
/** `at` with the entity's `yaw` and `duck` at that same instant, live where `at` is live and null
|
|
617
|
+
* where `at` is null. For hit zones that depend on stance: a head lower on a ducking target. §17.5. */
|
|
618
|
+
pose(id: string): RewoundPose | null;
|
|
619
|
+
}
|
|
620
|
+
/** Where a ray entered the room's world (`WorldApi.ray`). */
|
|
621
|
+
interface WorldRayHit {
|
|
622
|
+
/** World units along the ray from its origin; 0 when it started inside or on the way in. */
|
|
623
|
+
dist: number;
|
|
624
|
+
point: Vec3;
|
|
625
|
+
/** The face it entered by, a unit vector against the ray; all zeros when it started `inside`. */
|
|
626
|
+
normal: Vec3;
|
|
627
|
+
/** The ray started inside the solid. */
|
|
628
|
+
inside: boolean;
|
|
629
|
+
/** Which solid, counting the module's `boxes` and then its `ramps`. */
|
|
630
|
+
solid: number;
|
|
631
|
+
}
|
|
632
|
+
/** A world only rays read: `FirstpersonWorldSpec` plus how far off its surfaces may be, as its fit stated. */
|
|
633
|
+
interface ShotWorldSpec extends FirstpersonWorldSpec {
|
|
634
|
+
/** Units a claimed stop may differ from this world: `ctx.world.claim`'s default. Default 0. */
|
|
635
|
+
tolerance?: number;
|
|
636
|
+
}
|
|
637
|
+
/** The room's collision world, for the module's own questions (§17.7 *Rays*). Static: it needs no
|
|
638
|
+
* rewinding, and it holds what bodies collide with, not what is drawn. */
|
|
639
|
+
interface WorldApi {
|
|
640
|
+
/** The first solid along the ray within `range` (default: no limit), or null: also null for a ray
|
|
641
|
+
* that is none (zero length, or a value that is not a finite number). Touching a face is no hit.
|
|
642
|
+
* Takes a hitscan's options as they are. Server only: no client SDK has it. */
|
|
643
|
+
ray(origin: Vec3, dir: Vec3, options?: WorldRayOptions): WorldRayHit | null;
|
|
644
|
+
/** A shooter's claimed stop, from its own render mesh, held to the shot world (§17.7 *A claimed stop*): the distance
|
|
645
|
+
* the shot flies, and why. Null for a ray that is none. Any `claimed` that is not a number means the shot flew on. A room with
|
|
646
|
+
* no shot world throws naming `no_shot_world`. */
|
|
647
|
+
claim(origin: Vec3, dir: Vec3, claimed: unknown, options?: WorldClaimOptions): WorldClaim | null;
|
|
648
|
+
}
|
|
649
|
+
/** What `WorldApi.ray` takes. */
|
|
650
|
+
interface WorldRayOptions {
|
|
651
|
+
range?: number;
|
|
652
|
+
/** `'shot'`: the module's `shotWorld`. A room that has none throws naming `no_shot_world`, and is never answered from its `world`. */
|
|
653
|
+
world?: WorldKey;
|
|
654
|
+
}
|
|
655
|
+
/** What `WorldApi.claim` takes: a weapon's rules. */
|
|
656
|
+
interface WorldClaimOptions {
|
|
657
|
+
range?: number;
|
|
658
|
+
/** How far off a surface a claim may be, either way: the cheat margin. Along a ray that grazes the surface it reads further. Default:
|
|
659
|
+
* the shot world's own `tolerance`. */
|
|
660
|
+
tolerance?: number;
|
|
661
|
+
/** One wall this weapon goes through, in units along the ray. Default none. */
|
|
662
|
+
pierce?: number;
|
|
663
|
+
/** The weapon's last word: a distance replaces `dist`. Called with the verdict as it stands, each claim. */
|
|
664
|
+
rule?: (claim: WorldClaim) => number | undefined;
|
|
665
|
+
}
|
|
666
|
+
/** What `WorldApi.claim` decided. */
|
|
667
|
+
interface WorldClaim {
|
|
668
|
+
/** How far the shot flies: judge targets inside this. The range, or Infinity without one, when nothing stops it. */
|
|
669
|
+
dist: number;
|
|
670
|
+
verdict: ClaimVerdict;
|
|
671
|
+
/** What was claimed, as read: Infinity for the shot flying on. */
|
|
672
|
+
claimed: number;
|
|
673
|
+
/** Where the shot world stops the ray, or null if nothing does in range. */
|
|
674
|
+
server: number | null;
|
|
675
|
+
tolerance: number;
|
|
676
|
+
/** How far either way of the world's stop this claim could stand, along this ray: the tolerance, grown by how much the ray grazes. */
|
|
677
|
+
slack: number;
|
|
678
|
+
/** The wall a piercing claim went through, or null. */
|
|
679
|
+
through: number | null;
|
|
680
|
+
/** Which solid and which face of the shot world stopped the ray, or null. */
|
|
681
|
+
solid: number | null;
|
|
682
|
+
normal: Vec3 | null;
|
|
683
|
+
/** Where the shot stops, or null when it flies on for ever. */
|
|
684
|
+
point: Vec3 | null;
|
|
685
|
+
}
|
|
686
|
+
/** What `projectiles.launch` takes: where and how fast the projectile starts. `g` (default 0) pulls
|
|
687
|
+
* it down along −z; `id` is required outside a seat's input and refused inside one, where the id is
|
|
688
|
+
* `<seat>.<seq>.<n>` (the input's client sequence, and the launches during it from 0). */
|
|
689
|
+
interface ProjectileInit {
|
|
690
|
+
kind: string;
|
|
691
|
+
x: number;
|
|
692
|
+
y: number;
|
|
693
|
+
z: number;
|
|
694
|
+
vx: number;
|
|
695
|
+
vy: number;
|
|
696
|
+
vz: number;
|
|
697
|
+
g?: number;
|
|
698
|
+
hp?: number;
|
|
699
|
+
flags?: number;
|
|
700
|
+
data?: Json;
|
|
701
|
+
id?: string;
|
|
702
|
+
}
|
|
703
|
+
/** Where a projectile is at one tick (its flight, §17.9). */
|
|
704
|
+
interface ProjectileFlight {
|
|
705
|
+
x: number;
|
|
706
|
+
y: number;
|
|
707
|
+
z: number;
|
|
708
|
+
vx: number;
|
|
709
|
+
vy: number;
|
|
710
|
+
vz: number;
|
|
711
|
+
}
|
|
712
|
+
/** One entity a projectile's path crossed this tick, and how far along the path from its point at
|
|
713
|
+
* the previous tick (world units). */
|
|
714
|
+
interface ProjectileHit {
|
|
715
|
+
id: string;
|
|
716
|
+
dist: number;
|
|
717
|
+
}
|
|
718
|
+
/** Projectiles (§17.9): entities on a closed-form ballistic flight that every client draws from its
|
|
719
|
+
* launch. They take no part in lag compensation, and the server never ages one out. */
|
|
720
|
+
interface ProjectileApi {
|
|
721
|
+
/** Launches (or, for an id that exists, re-launches) a projectile at the tick being stepped, with
|
|
722
|
+
* `owner` '' and `src` the seat whose input is being handled ('' elsewhere). Returns its id.
|
|
723
|
+
* Throws a RangeError, writing nothing, when the launches not yet sent would pass
|
|
724
|
+
* `AuthorityLimits.MAX_LAUNCH_UPDATES` entities or `MAX_LAUNCH_BYTES` of output (about a hundred
|
|
725
|
+
* a tick): catch it to cap a burst, or it is a strike. */
|
|
726
|
+
launch(projectile: ProjectileInit): string;
|
|
727
|
+
/** The projectile's flight at `tick` (default: the tick being stepped); undefined for an id that is
|
|
728
|
+
* not a projectile. */
|
|
729
|
+
at(id: string, tick?: number): ProjectileFlight | undefined;
|
|
730
|
+
/** The entities of `kinds` whose sphere of `radius` the projectile's path crosses between the
|
|
731
|
+
* previous tick and this one, nearest first. Only those kinds are visited, so a call costs the
|
|
732
|
+
* number of their entities. */
|
|
733
|
+
sweep(id: string, options: {
|
|
734
|
+
kinds: string[];
|
|
735
|
+
radius: number;
|
|
736
|
+
}): ProjectileHit[];
|
|
737
|
+
}
|
|
738
|
+
/** Preset movement a module can hand to the server (`config.preset`). */
|
|
739
|
+
interface PresetSpec {
|
|
740
|
+
name: PresetName;
|
|
741
|
+
params?: Record<string, number>;
|
|
742
|
+
}
|
|
743
|
+
/** A rotation as a unit quaternion. */
|
|
744
|
+
interface Quat {
|
|
745
|
+
x: number;
|
|
746
|
+
y: number;
|
|
747
|
+
z: number;
|
|
748
|
+
w: number;
|
|
749
|
+
}
|
|
750
|
+
/** A position and a rotation. */
|
|
751
|
+
interface PhysicsPose {
|
|
752
|
+
p: Vec3;
|
|
753
|
+
q: Quat;
|
|
754
|
+
}
|
|
755
|
+
/** The contract's limits of one world (C§11). `worldExtent` and `maxSpeed` are in the room's units
|
|
756
|
+
* (they scale with `lengthUnit`); the rest are counts, bytes or rad/s. */
|
|
757
|
+
interface PhysicsLimits {
|
|
758
|
+
bodies: number;
|
|
759
|
+
collidersPerBody: number;
|
|
760
|
+
hullVertices: number;
|
|
761
|
+
triangles: number;
|
|
762
|
+
joints: number;
|
|
763
|
+
ccdBodies: number;
|
|
764
|
+
queriesPerStep: number;
|
|
765
|
+
eventsPerStep: number;
|
|
766
|
+
heightfieldSamples: number;
|
|
767
|
+
staticPieces: number;
|
|
768
|
+
memoryBytes: number;
|
|
769
|
+
worldExtent: number;
|
|
770
|
+
maxSpeed: number;
|
|
771
|
+
maxAngularSpeed: number;
|
|
772
|
+
}
|
|
773
|
+
/** What a module may ask for in `config.physics` (`true` is the same as `{}`). Any other key, or a
|
|
774
|
+
* value outside its range, is a bad config. */
|
|
775
|
+
interface PhysicsRequest {
|
|
776
|
+
/** Room units per metre, (0, 1e6]. Default 39.37 with `firstperson`, 100 with `vehicle`, else 1. */
|
|
777
|
+
lengthUnit?: number;
|
|
778
|
+
/** Units/s² along −z, [0, 1e7]. Default the preset's `gravity` with `firstperson` or `vehicle`,
|
|
779
|
+
* else 9.81 · lengthUnit. */
|
|
780
|
+
gravity?: number;
|
|
781
|
+
/** The solver hint, an integer in [1, 16]. Default 4. */
|
|
782
|
+
iterations?: number;
|
|
783
|
+
/** Lower any limit; one above the room's ceiling (the contract's default in release 1) is clamped. */
|
|
784
|
+
limits?: Partial<PhysicsLimits>;
|
|
785
|
+
/** At most 8 distinct physics kinds whose changed bodies the module receives each tick. */
|
|
786
|
+
subscribe?: string[];
|
|
787
|
+
/** The preset bodies: `radius` and `height` (each (0, 1e6]) size a `topdown` room's, and are
|
|
788
|
+
* refused with any other preset; `events` (default false) opts their colliders into contact
|
|
789
|
+
* events. */
|
|
790
|
+
player?: {
|
|
791
|
+
radius?: number;
|
|
792
|
+
height?: number;
|
|
793
|
+
events?: boolean;
|
|
794
|
+
};
|
|
795
|
+
/** Whether clients predict the room's bodies (§17.11). Default true; the operator and each client
|
|
796
|
+
* can still turn it off. */
|
|
797
|
+
predict?: boolean;
|
|
798
|
+
/** Keyframes (tick-aligned patches) per second in a predicting room, one of 10, 15, 20, 30 and 60.
|
|
799
|
+
* Default 20; the room uses the largest of those at or below it that divides its tick rate. */
|
|
800
|
+
patchHz?: number;
|
|
801
|
+
/** How a client moves other players' preset bodies between keyframes: by their velocity
|
|
802
|
+
* (`extrapolate`, the default), by their last input held (`hold`, the Rocket League model: the room
|
|
803
|
+
* preset's step under that input every tick), or by their last input, repeated and decayed
|
|
804
|
+
* (`inputDecay`). */
|
|
805
|
+
remotePredict?: RemotePredict;
|
|
806
|
+
/** `inputDecay`: ms after which a repeated input's axes have decayed to 0, in [0, 1000]. Default 50. */
|
|
807
|
+
remoteDecayMs?: number;
|
|
808
|
+
/** How far a client predicts past a keyframe's ack, ms, in [50, 1000]. Default 250. */
|
|
809
|
+
leadMs?: number;
|
|
810
|
+
}
|
|
811
|
+
/** `config.physics.remotePredict` (§17.11). */
|
|
812
|
+
declare const RemotePredict: {
|
|
813
|
+
readonly EXTRAPOLATE: "extrapolate";
|
|
814
|
+
readonly HOLD: "hold";
|
|
815
|
+
readonly INPUT_DECAY: "inputDecay";
|
|
816
|
+
};
|
|
817
|
+
type RemotePredict = (typeof RemotePredict)[keyof typeof RemotePredict];
|
|
818
|
+
/** `config.physics` after defaults, as `init` sees it: what the room's world runs with. */
|
|
819
|
+
interface ResolvedPhysics {
|
|
820
|
+
lengthUnit: number;
|
|
821
|
+
gravity: number;
|
|
822
|
+
iterations: number;
|
|
823
|
+
/** Every limit, clamped to the room's ceiling. */
|
|
824
|
+
limits: PhysicsLimits;
|
|
825
|
+
subscribe: string[];
|
|
826
|
+
player: {
|
|
827
|
+
radius?: number;
|
|
828
|
+
height?: number;
|
|
829
|
+
events: boolean;
|
|
830
|
+
};
|
|
831
|
+
/** Whether the module asked for prediction (§17.11); the host may still run the room without it. */
|
|
832
|
+
predict: boolean;
|
|
833
|
+
/** The requested keyframe rate (§17.11). */
|
|
834
|
+
patchHz: number;
|
|
835
|
+
remotePredict: RemotePredict;
|
|
836
|
+
remoteDecayMs: number;
|
|
837
|
+
leadMs: number;
|
|
838
|
+
}
|
|
839
|
+
/** Collision groups: 16-bit masks (C§6.2). */
|
|
840
|
+
interface PhysicsGroups {
|
|
841
|
+
member: number;
|
|
842
|
+
collides: number;
|
|
843
|
+
}
|
|
844
|
+
/** A collider shape (C§6.2). */
|
|
845
|
+
type PhysicsShape = {
|
|
846
|
+
kind: 'box';
|
|
847
|
+
half: Vec3;
|
|
848
|
+
} | {
|
|
849
|
+
kind: 'sphere';
|
|
850
|
+
radius: number;
|
|
851
|
+
} | {
|
|
852
|
+
kind: 'capsule';
|
|
853
|
+
halfHeight: number;
|
|
854
|
+
radius: number;
|
|
855
|
+
axis: 'x' | 'y' | 'z';
|
|
856
|
+
} | {
|
|
857
|
+
kind: 'hull';
|
|
858
|
+
points: Vec3[];
|
|
859
|
+
} | {
|
|
860
|
+
kind: 'trimesh';
|
|
861
|
+
vertices: Vec3[];
|
|
862
|
+
indices: [number, number, number][];
|
|
863
|
+
} | {
|
|
864
|
+
kind: 'heightfield';
|
|
865
|
+
rows: number;
|
|
866
|
+
cols: number;
|
|
867
|
+
heights: number[];
|
|
868
|
+
size: Vec3;
|
|
869
|
+
};
|
|
870
|
+
/** One collider of a body (C§6.2). */
|
|
871
|
+
interface PhysicsColliderDesc {
|
|
872
|
+
key: string;
|
|
873
|
+
shape: PhysicsShape;
|
|
874
|
+
offset?: PhysicsPose;
|
|
875
|
+
density?: number;
|
|
876
|
+
mass?: number;
|
|
877
|
+
friction?: number;
|
|
878
|
+
restitution?: number;
|
|
879
|
+
groups?: PhysicsGroups;
|
|
880
|
+
sensor?: boolean;
|
|
881
|
+
events?: boolean;
|
|
882
|
+
}
|
|
883
|
+
/** A body type (C§6.1). */
|
|
884
|
+
type PhysicsBodyType = 'dynamic' | 'fixed' | 'kinematic';
|
|
885
|
+
/** A body to spawn (C§6.1). `key` is the id of an entity that exists once this tick's entity
|
|
886
|
+
* changes apply, and must be a contract key (1-64 of `[A-Za-z0-9_.:-]`, not `static:`…). `kind` is
|
|
887
|
+
* the body's own physics kind (1-32 of the same charset, '' or absent = none), not the entity's. */
|
|
888
|
+
interface PhysicsBodyDesc {
|
|
889
|
+
key: string;
|
|
890
|
+
type: PhysicsBodyType;
|
|
891
|
+
pose: PhysicsPose;
|
|
892
|
+
linvel?: Vec3;
|
|
893
|
+
angvel?: Vec3;
|
|
894
|
+
colliders: PhysicsColliderDesc[];
|
|
895
|
+
linearDamping?: number;
|
|
896
|
+
angularDamping?: number;
|
|
897
|
+
gravityScale?: number;
|
|
898
|
+
lockRotation?: [boolean, boolean, boolean];
|
|
899
|
+
ccd?: boolean;
|
|
900
|
+
canSleep?: boolean;
|
|
901
|
+
kind?: string;
|
|
902
|
+
}
|
|
903
|
+
/** A joint motor's targets (C§6.4). */
|
|
904
|
+
interface PhysicsMotor {
|
|
905
|
+
targetVel?: number;
|
|
906
|
+
targetPos?: number;
|
|
907
|
+
stiffness?: number;
|
|
908
|
+
damping?: number;
|
|
909
|
+
maxForce?: number;
|
|
910
|
+
}
|
|
911
|
+
/** A joint (C§6.4). `b: null` attaches to the world. */
|
|
912
|
+
interface PhysicsJointDesc {
|
|
913
|
+
key: string;
|
|
914
|
+
a: string;
|
|
915
|
+
b: string | null;
|
|
916
|
+
type: 'fixed' | 'hinge' | 'ball' | 'slider' | 'rope';
|
|
917
|
+
anchorA: Vec3;
|
|
918
|
+
anchorB: Vec3;
|
|
919
|
+
axis?: Vec3;
|
|
920
|
+
limits?: [number, number];
|
|
921
|
+
motor?: PhysicsMotor;
|
|
922
|
+
collideConnected?: boolean;
|
|
923
|
+
}
|
|
924
|
+
/** `setPose` options (C§7). */
|
|
925
|
+
interface PhysicsSetPoseOptions {
|
|
926
|
+
resetVelocity?: boolean;
|
|
927
|
+
wake?: boolean;
|
|
928
|
+
}
|
|
929
|
+
/** A query filter (C§13). */
|
|
930
|
+
interface PhysicsQueryFilter {
|
|
931
|
+
groups?: PhysicsGroups;
|
|
932
|
+
excludeBodies?: string[];
|
|
933
|
+
includeSensors?: boolean;
|
|
934
|
+
}
|
|
935
|
+
/** The contract's error codes (C§14), as `refused` and a refused query's answer carry them. */
|
|
936
|
+
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';
|
|
937
|
+
/** A command's number, or `'invalid-key'` when the prelude refused it before it was numbered. The
|
|
938
|
+
* module's commands are numbered 0, 1, 2 … across the room's life. */
|
|
939
|
+
type PhysicsCommandResult = number | 'invalid-key';
|
|
940
|
+
/** A ray or shape-cast hit (C§13). */
|
|
941
|
+
interface PhysicsHit {
|
|
942
|
+
body: string;
|
|
943
|
+
collider: string;
|
|
944
|
+
dist: number;
|
|
945
|
+
point: Vec3;
|
|
946
|
+
normal: Vec3;
|
|
947
|
+
}
|
|
948
|
+
/** An overlap's bodies: at most 64 keys in key order, `more` when the core found more. */
|
|
949
|
+
interface PhysicsOverlap {
|
|
950
|
+
keys: string[];
|
|
951
|
+
more: boolean;
|
|
952
|
+
}
|
|
953
|
+
/** One query's answer on the next tick: a hit or null (ray, shapeCast), an overlap, or the C§14
|
|
954
|
+
* code of a refused query (`limit-queries` past the quota). */
|
|
955
|
+
type PhysicsAnswer = PhysicsHit | null | PhysicsOverlap | PhysicsErrorCode;
|
|
956
|
+
/** A contact or trigger event of the last step (C§8.1). A missing physics kind is absent. */
|
|
957
|
+
type PhysicsEvent = {
|
|
958
|
+
kind: 'contactBegin';
|
|
959
|
+
a: string;
|
|
960
|
+
b: string;
|
|
961
|
+
ca: string;
|
|
962
|
+
cb: string;
|
|
963
|
+
ka?: string;
|
|
964
|
+
kb?: string;
|
|
965
|
+
impulse: number;
|
|
966
|
+
point: Vec3;
|
|
967
|
+
normal: Vec3;
|
|
968
|
+
} | {
|
|
969
|
+
kind: 'contactEnd';
|
|
970
|
+
a: string;
|
|
971
|
+
b: string;
|
|
972
|
+
ca: string;
|
|
973
|
+
cb: string;
|
|
974
|
+
ka?: string;
|
|
975
|
+
kb?: string;
|
|
976
|
+
} | {
|
|
977
|
+
kind: 'triggerEnter';
|
|
978
|
+
sensor: string;
|
|
979
|
+
sc: string;
|
|
980
|
+
other: string;
|
|
981
|
+
oc: string;
|
|
982
|
+
ks?: string;
|
|
983
|
+
ko?: string;
|
|
984
|
+
} | {
|
|
985
|
+
kind: 'triggerExit';
|
|
986
|
+
sensor: string;
|
|
987
|
+
sc: string;
|
|
988
|
+
other: string;
|
|
989
|
+
oc: string;
|
|
990
|
+
ks?: string;
|
|
991
|
+
ko?: string;
|
|
992
|
+
};
|
|
993
|
+
/** A body's replicated state (C§9.2's NetBodyState): position, rotation, linear velocity (room
|
|
994
|
+
* units/s), angular velocity (rad/s, world axes). */
|
|
995
|
+
interface PhysicsBodyState {
|
|
996
|
+
p: Vec3;
|
|
997
|
+
q: Quat;
|
|
998
|
+
v: Vec3;
|
|
999
|
+
w: Vec3;
|
|
1000
|
+
sleeping: boolean;
|
|
1001
|
+
}
|
|
1002
|
+
/** A command the core refused (or the host or prelude did): its number and its C§14 code. */
|
|
1003
|
+
interface PhysicsRefusal {
|
|
1004
|
+
n: number;
|
|
1005
|
+
code: PhysicsErrorCode;
|
|
1006
|
+
}
|
|
1007
|
+
/** The physics commands (C§7), allowed in every hook and in `init` (they apply before tick 0). Each
|
|
1008
|
+
* takes effect in this tick's step. Past `AuthorityLimits.MAX_PHYS_COMMANDS` (or `MAX_PHYS_BYTES`)
|
|
1009
|
+
* waiting to go out, a command throws a RangeError and adds nothing. */
|
|
1010
|
+
interface PhysicsCommands {
|
|
1011
|
+
spawn(desc: PhysicsBodyDesc): PhysicsCommandResult;
|
|
1012
|
+
remove(key: string): PhysicsCommandResult;
|
|
1013
|
+
addJoint(desc: PhysicsJointDesc): PhysicsCommandResult;
|
|
1014
|
+
removeJoint(key: string): PhysicsCommandResult;
|
|
1015
|
+
impulse(key: string, j: Vec3, at?: Vec3): PhysicsCommandResult;
|
|
1016
|
+
setVelocity(key: string, linvel?: Vec3, angvel?: Vec3): PhysicsCommandResult;
|
|
1017
|
+
setPose(key: string, pose: PhysicsPose, options?: PhysicsSetPoseOptions): PhysicsCommandResult;
|
|
1018
|
+
setNextKinematicPose(key: string, pose: PhysicsPose): PhysicsCommandResult;
|
|
1019
|
+
areaPush(center: Vec3, radius: number, strength: number, falloff: number, groups?: PhysicsGroups): PhysicsCommandResult;
|
|
1020
|
+
setMotor(jointKey: string, motor: PhysicsMotor): PhysicsCommandResult;
|
|
1021
|
+
wake(key: string): PhysicsCommandResult;
|
|
1022
|
+
sleep(key: string): PhysicsCommandResult;
|
|
1023
|
+
}
|
|
1024
|
+
/** `ctx.physics` (§17.10) in a physics room: commands, queries answered on the next tick, and what
|
|
1025
|
+
* the last step did, as this tick's `TickIn` carried it. */
|
|
1026
|
+
interface PhysicsApi extends PhysicsCommands {
|
|
1027
|
+
/** Each query returns its index in this tick's queries; read the answer at `answers[index]` on
|
|
1028
|
+
* the next tick, never this one. Past the world's `queriesPerStep`, it throws a RangeError. */
|
|
1029
|
+
ray(origin: Vec3, dir: Vec3, maxDist: number, filter?: PhysicsQueryFilter): number;
|
|
1030
|
+
shapeCast(shape: PhysicsShape, pose: PhysicsPose, dir: Vec3, maxDist: number, filter?: PhysicsQueryFilter): number;
|
|
1031
|
+
overlap(shape: PhysicsShape, pose: PhysicsPose, filter?: PhysicsQueryFilter): number;
|
|
1032
|
+
/** The answers to the previous tick's queries, in order; empty after an output the host did not
|
|
1033
|
+
* apply. */
|
|
1034
|
+
readonly answers: readonly PhysicsAnswer[];
|
|
1035
|
+
/** The commands the last step's batch refused. A refusal is never a strike. */
|
|
1036
|
+
readonly refused: readonly PhysicsRefusal[];
|
|
1037
|
+
readonly events: readonly PhysicsEvent[];
|
|
1038
|
+
/** True when the last step dropped events past `eventsPerStep`. */
|
|
1039
|
+
readonly overflowed: boolean;
|
|
1040
|
+
/** The entities whose bodies left the world; the host has removed those entities. */
|
|
1041
|
+
readonly escaped: readonly string[];
|
|
1042
|
+
/** The latest state of a body of a subscribed kind, kept across ticks until it is removed or
|
|
1043
|
+
* escapes; undefined for any other body, and before the first state arrives. */
|
|
1044
|
+
body(key: string): PhysicsBodyState | undefined;
|
|
1045
|
+
}
|
|
1046
|
+
/** What a module may ask for in `config`. The server clamps every value to the game's admission. */
|
|
1047
|
+
interface ModuleConfig {
|
|
1048
|
+
tickHz?: TickHz;
|
|
1049
|
+
maxPlayers?: number;
|
|
1050
|
+
preset?: PresetSpec;
|
|
1051
|
+
/** Inputs whose `buttons` intersect this mask carry the sender's `seen` snapshot (§17.5). */
|
|
1052
|
+
lagComp?: {
|
|
1053
|
+
buttons: number;
|
|
1054
|
+
};
|
|
1055
|
+
/** Server physics (§17.10): `true`, or a request. Absent, `false` or `null` = none. The room must
|
|
1056
|
+
* be admitted with physics (a published module's config says so). */
|
|
1057
|
+
physics?: boolean | PhysicsRequest | null;
|
|
1058
|
+
/** The seats' contact policy as data (§17.11 *Contact policy*): every SDK runs the declared pass in
|
|
1059
|
+
* its prediction and replay. Needs a `firstperson` or `vehicle` preset; a bad one is a bad config. */
|
|
1060
|
+
contacts?: ContactDeclaration | null;
|
|
1061
|
+
/** The seats' input settings as data (§17.11 *Input pacing*): whether a starved seat's held input is
|
|
1062
|
+
* repeated (`repeat`), else its genre `preset`'s setting, else the contact policy's preset's, else
|
|
1063
|
+
* on. A bad one is a bad config (input-config.ts `resolveInputConfig` names its path). */
|
|
1064
|
+
inputs?: InputDeclaration | null;
|
|
1065
|
+
/** How long a seat may sit idle and the room may live (§17.6 *Room lifetime*), whole seconds inside
|
|
1066
|
+
* their bounds; a bad one is a bad config (lifetime-config.ts `resolveLifetime` names its path). */
|
|
1067
|
+
lifetime?: LifetimeDeclaration | null;
|
|
1068
|
+
/** The room's lifts as data (§17.11 *Lifts*): platforms posed by an exact function of the tick,
|
|
1069
|
+
* which seats collide with and ride. Needs a `firstperson` or `vehicle` preset; a bad one is a bad
|
|
1070
|
+
* config (lifts-config.ts `resolveLifts` names its path). */
|
|
1071
|
+
lifts?: unknown;
|
|
1072
|
+
}
|
|
1073
|
+
/** `config` after defaults: what the room actually runs with. */
|
|
1074
|
+
interface ResolvedModuleConfig {
|
|
1075
|
+
tickHz: TickHz;
|
|
1076
|
+
maxPlayers: number;
|
|
1077
|
+
preset: PresetSpec | null;
|
|
1078
|
+
lagComp: {
|
|
1079
|
+
buttons: number;
|
|
1080
|
+
} | null;
|
|
1081
|
+
/** The physics the room runs (§17.10), null without. */
|
|
1082
|
+
physics: ResolvedPhysics | null;
|
|
1083
|
+
/** The declared contact policy, resolved by the host (§17.11 *Contact policy*); null or absent
|
|
1084
|
+
* without one. The host publishes it in the state's `preset` text; a module's own `ctx.config`
|
|
1085
|
+
* does not carry it. */
|
|
1086
|
+
contacts?: ContactPolicy | null;
|
|
1087
|
+
/** The input settings, resolved by the host (§17.11 *Input pacing*); absent when the module declares
|
|
1088
|
+
* neither inputs nor a contact policy (the room repeats, as before the setting). The host publishes
|
|
1089
|
+
* them in the state's `preset` text. */
|
|
1090
|
+
inputs?: InputPolicy | null;
|
|
1091
|
+
/** The declared lifts, resolved by the host (§17.11 *Lifts*); absent without. The host publishes
|
|
1092
|
+
* them in the state's `preset` text. */
|
|
1093
|
+
lifts?: LiftsPolicy | null;
|
|
1094
|
+
/** The declared limits on a seat's idling and the room's age, resolved by the host (§17.3 *Room
|
|
1095
|
+
* lifetime*); absent: the defaults. */
|
|
1096
|
+
lifetime?: LifetimePolicy | null;
|
|
1097
|
+
}
|
|
1098
|
+
/** What `init` gets. */
|
|
1099
|
+
interface InitContext {
|
|
1100
|
+
readonly config: ResolvedModuleConfig;
|
|
1101
|
+
/** The room's seed; `random()` is a PRNG seeded from it, so a replay is exact. */
|
|
1102
|
+
readonly seed: number;
|
|
1103
|
+
/** The key its first client asked for (§17.2 *Choosing a room*), to choose a mode by; '' for none.
|
|
1104
|
+
* A client chose it: treat a key you do not know as the default, never as an error. */
|
|
1105
|
+
readonly matchKey: string;
|
|
1106
|
+
/** What the room this one follows returned from `onDrain` (§17.13 *Drain and rejoin*), or null: a
|
|
1107
|
+
* room with no predecessor, a predecessor with nothing to hand on, or one in another region. Treat
|
|
1108
|
+
* it as written by an older version of your module: the module may have changed in the deploy. */
|
|
1109
|
+
readonly handoff: Json | null;
|
|
1110
|
+
random(): number;
|
|
1111
|
+
readonly entities: EntityApi;
|
|
1112
|
+
/** The room's collision world, from where it stands before tick 0 (§17.7 *Rays*). */
|
|
1113
|
+
readonly world: WorldApi;
|
|
1114
|
+
/** Projectiles. `launch` throws here: a launch's `t0` is the tick being stepped, and `init` runs
|
|
1115
|
+
* before the first tick. Launch from `step` or a hook instead. */
|
|
1116
|
+
readonly projectiles: ProjectileApi;
|
|
1117
|
+
/** Physics commands (§17.10), applied before tick 0. Queries throw in `init`. */
|
|
1118
|
+
readonly physics: PhysicsCommands;
|
|
1119
|
+
/** Rate-limited server log line. */
|
|
1120
|
+
log(...args: Json[]): void;
|
|
1121
|
+
}
|
|
1122
|
+
/** What every hook after `init` gets. */
|
|
1123
|
+
interface RoomContext<S> {
|
|
1124
|
+
/** The module's own state, as `init` returned it. Never sent to clients. */
|
|
1125
|
+
state: S;
|
|
1126
|
+
/** Fixed-step tick number, from 0. */
|
|
1127
|
+
readonly tick: number;
|
|
1128
|
+
/** Seconds per tick (1 / tickHz). */
|
|
1129
|
+
readonly dt: number;
|
|
1130
|
+
random(): number;
|
|
1131
|
+
readonly entities: EntityApi;
|
|
1132
|
+
readonly rewind: RewindApi;
|
|
1133
|
+
/** The room's collision world (§17.7 *Rays*). */
|
|
1134
|
+
readonly world: WorldApi;
|
|
1135
|
+
readonly projectiles: ProjectileApi;
|
|
1136
|
+
/** Server physics (§17.10) in a room whose config asks for it. */
|
|
1137
|
+
readonly physics: PhysicsApi;
|
|
1138
|
+
/** The room's declared lifts (§17.11 *Lifts*). */
|
|
1139
|
+
readonly lifts: LiftsApi;
|
|
1140
|
+
/** Sends a game event to every client, or only to the seat `to`. */
|
|
1141
|
+
emit(type: string, data?: Json, to?: string): void;
|
|
1142
|
+
log(...args: Json[]): void;
|
|
1143
|
+
}
|
|
1144
|
+
/** What a module reads of its declared lifts this tick (§17.11 *Lifts*). */
|
|
1145
|
+
interface LiftsApi {
|
|
1146
|
+
/** The seats a lift pushed against the world this tick, each with the lift (damage is the module's). */
|
|
1147
|
+
crushed(): {
|
|
1148
|
+
seat: string;
|
|
1149
|
+
lift: string;
|
|
1150
|
+
}[];
|
|
1151
|
+
}
|
|
1152
|
+
/** A game's room module. No Date, timers, fetch, require or shared memory exist inside it. */
|
|
1153
|
+
interface GenexRoomModule<S = unknown> {
|
|
1154
|
+
contract: typeof MODULE_CONTRACT;
|
|
1155
|
+
config?: ModuleConfig;
|
|
1156
|
+
/** The `firstperson` and `vehicle` presets' collision world (§17.7): boxes then ramps, fixed for the room's
|
|
1157
|
+
* life. The sandbox validates it at init and writes its hash into the resolved preset's
|
|
1158
|
+
* `params.world`; the game client hands its SDK the same world (`session.setWorld`). Absent =
|
|
1159
|
+
* the empty world (no floor: add one box for open ground). */
|
|
1160
|
+
world?: FirstpersonWorldSpec;
|
|
1161
|
+
/** Several worlds, by match key (§17.2 *Choosing a room*): a room whose key the map names runs that
|
|
1162
|
+
* world, any other room `world`. At most 16, each name a match key, and one room compiles only its
|
|
1163
|
+
* own; `authority validate` loads them all. A module with this map leaves `params.world` unset. */
|
|
1164
|
+
worlds?: Readonly<Record<string, FirstpersonWorldSpec>>;
|
|
1165
|
+
/** What bullets meet where it differs from what bodies do (§17.7 *The shot world*): only `ctx.world`'s rays read it. Its own
|
|
1166
|
+
* tree, no hash, no client knows it, and it counts against the module's bytes and a load's cost. */
|
|
1167
|
+
shotWorld?: ShotWorldSpec;
|
|
1168
|
+
/** Several, by match key, as `worlds`: any other room `shotWorld`. */
|
|
1169
|
+
shotWorlds?: Readonly<Record<string, ShotWorldSpec>>;
|
|
1170
|
+
init(ctx: InitContext): S;
|
|
1171
|
+
onJoin(ctx: RoomContext<S>, player: PlayerInfo): void;
|
|
1172
|
+
onLeave(ctx: RoomContext<S>, player: PlayerInfo, reason: LeaveReason): void;
|
|
1173
|
+
/** One call per input, in each player's sequence order, before `step`. */
|
|
1174
|
+
onInput(ctx: RoomContext<S>, playerId: string, input: InputData): void;
|
|
1175
|
+
/** Once per tick, after the tick's inputs. */
|
|
1176
|
+
step(ctx: RoomContext<S>): void;
|
|
1177
|
+
/** Optional. The server is about to close this room for a deploy (§17.13 *Drain and rejoin*): what to
|
|
1178
|
+
* hand the room that follows it, at most 256 KiB of JSON, which it reads as `ctx.handoff` in `init`.
|
|
1179
|
+
* Called once, right before the close, under `init`'s time limit. Its seats come back by themselves,
|
|
1180
|
+
* with new session ids: key what you keep by uid. Without this hook, or when it throws or returns
|
|
1181
|
+
* nothing, the successor starts clean. */
|
|
1182
|
+
onDrain?(ctx: RoomContext<S>): Json | undefined;
|
|
1183
|
+
}
|
|
1184
|
+
/** Identity helper that types a room module: `export default defineRoom({ ... })`. */
|
|
1185
|
+
declare const defineRoom: <S>(module: GenexRoomModule<S>) => GenexRoomModule<S>;
|
|
1186
|
+
|
|
1187
|
+
export { LiftEase as $, AuthorityLifetime as A, INPUT_SETTLE_MS as B, type ContactPolicy as C, type InitContext as D, type EntityData as E, InputConfigError as F, type GenexRoomModule as G, HeadFriction as H, type InputData as I, type Json as J, type InputDeclaration as K, LeaveReason as L, type InputPolicy as M, LIFETIME_DEFAULT as N, LIFTS_MAX as O, type PlayerInfo as P, LIFT_ID_MAX as Q, type ResolvedModuleConfig as R, type StandRule as S, type TickHz as T, LIFT_POINTS_MAX as U, LIFT_SPIN_MIN_PERIOD as V, LIFT_TICKS_MAX as W, LIFT_VALUE_MAX as X, LifetimeConfigError as Y, type LifetimeDeclaration as Z, type LifetimePolicy as _, type PhysicsErrorCode as a, LiftImpart as a0, type LiftPath as a1, LiftPathMode as a2, type LiftShape as a3, LiftShapeKind as a4, type LiftSpin as a5, type LiftTooFast as a6, type LiftsApi as a7, LiftsConfigError as a8, MODULE_CONTRACT as a9, type ProjectileField as aA, type ProjectileFlight as aB, type ProjectileHit as aC, type ProjectileInit as aD, type Quat as aE, type ResolvedPhysics as aF, type RewindApi as aG, type RewoundPose as aH, type RoomContext as aI, SHOOTER_CENTER as aJ, type ShotWorldSpec as aK, type SlideRule as aL, TopPolicy as aM, type Vec3 as aN, type WorldApi as aO, type WorldClaim as aP, type WorldClaimOptions as aQ, type WorldRayHit as aR, type WorldRayOptions as aS, defineRoom as aT, isLiftId as aU, presetInputRepeat as aV, resolveContactConfig as aW, resolveInputConfig as aX, resolveLifetime as aY, resolveLifts as aZ, type ModuleConfig as aa, NO_LIFTS as ab, type PairRule as ac, type PhysicsAnswer as ad, type PhysicsApi as ae, type PhysicsBodyDesc as af, type PhysicsBodyState as ag, type PhysicsBodyType as ah, type PhysicsColliderDesc as ai, type PhysicsCommandResult as aj, type PhysicsCommands as ak, type PhysicsEvent as al, type PhysicsGroups as am, type PhysicsHit as an, type PhysicsJointDesc as ao, type PhysicsLimits as ap, type PhysicsMotor as aq, type PhysicsOverlap as ar, type PhysicsPose as as, type PhysicsQueryFilter as at, type PhysicsRefusal as au, type PhysicsRequest as av, type PhysicsSetPoseOptions as aw, type PhysicsShape as ax, type PresetSpec as ay, type ProjectileApi as az, InputRepeat as b, type LiftVec as c, type Lift as d, type LiftsPolicy as e, RemotePredict as f, type TopRule as g, type PropSlide as h, ContactDrawing as i, CONTACT_DEFAULTS as j, CONTACT_PAIRS_MAX as k, CONTACT_PASSES_MAX as l, CONTACT_PRESETS as m, CONTACT_TAG_MAX as n, CONTACT_VALUE_MAX as o, ContactConfigError as p, type ContactDeclaration as q, ContactPreset as r, type ContactPresetParams as s, ContactPush as t, type ContactShape as u, ContactShapeKind as v, type EntityApi as w, type EntityInit as x, INPUTS_DEFAULT as y, INPUT_PRESETS as z };
|