@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.
@@ -0,0 +1,3646 @@
1
+ import {
2
+ RemotePredict
3
+ } from "./chunk-K3SF5VDS.js";
4
+ import {
5
+ Limits,
6
+ isReservedEventType
7
+ } from "./chunk-ZSWF4VCN.js";
8
+
9
+ // src/authority/numeric.ts
10
+ var MAX_ABS = 1e9;
11
+ var MASK_LIMIT = 2 ** 24;
12
+ var TWO_PI = 2 * Math.PI;
13
+ var F32_PI = Math.fround(Math.PI);
14
+ var unsignedZero = (v) => v === 0 ? 0 : v;
15
+ function axis(v) {
16
+ if (typeof v !== "number" || !Number.isFinite(v)) return 0;
17
+ const clamped = v < -1 ? -1 : v > 1 ? 1 : v;
18
+ return unsignedZero(Math.fround(clamped));
19
+ }
20
+ function wrappedAngle(v) {
21
+ if (typeof v !== "number" || !Number.isFinite(v) || Math.abs(v) > MAX_ABS) return null;
22
+ const wrapped = Math.fround(v - TWO_PI * Math.floor((v + Math.PI) / TWO_PI));
23
+ return wrapped === F32_PI ? -F32_PI : unsignedZero(wrapped);
24
+ }
25
+ function mask(v) {
26
+ return typeof v === "number" && Number.isInteger(v) && v >= 0 && v < MASK_LIMIT ? v : 0;
27
+ }
28
+ var isMask = (v) => typeof v === "number" && mask(v) === v;
29
+ function stepSeconds(v, max) {
30
+ if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) return 0;
31
+ return v > max ? max : v;
32
+ }
33
+ var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
34
+ var INVPIO2 = 5734161139222659 / 2 ** 53;
35
+ var PIO2_1 = 1686629713 / 2 ** 30;
36
+ var PIO2_2 = 2242054355 / 2 ** 65;
37
+ var PIO2_2T = 5376105825661043 / 2 ** 121;
38
+ var S1 = -6004799503160649 / 2 ** 55;
39
+ var S2 = 2401919801261139 / 2 ** 58;
40
+ var S3 = -7320136532976085 / 2 ** 65;
41
+ var S4 = 6506786730409597 / 2 ** 71;
42
+ var S5 = -7571127717829867 / 2 ** 78;
43
+ var S6 = 1537454791456095 / 2 ** 83;
44
+ var C1 = 1501199875790163 / 2 ** 55;
45
+ var C2 = -6405119470031223 / 2 ** 62;
46
+ var C3 = 457508533350745 / 2 ** 64;
47
+ var C4 = -5205429506036397 / 2 ** 74;
48
+ var C5 = 1261860039765105 / 2 ** 79;
49
+ var C6 = -1757820317994549 / 2 ** 87;
50
+ var SINCOS_CONSTANTS = Object.freeze({
51
+ invpio2: INVPIO2,
52
+ pio2_1: PIO2_1,
53
+ pio2_2: PIO2_2,
54
+ pio2_2t: PIO2_2T,
55
+ S1,
56
+ S2,
57
+ S3,
58
+ S4,
59
+ S5,
60
+ S6,
61
+ C1,
62
+ C2,
63
+ C3,
64
+ C4,
65
+ C5,
66
+ C6
67
+ });
68
+ function psincos(a) {
69
+ if (!Number.isFinite(a) || Math.abs(a) > MAX_ABS) return [0, 1];
70
+ const n = Math.floor(a * INVPIO2 + 0.5);
71
+ const q = n - 4 * Math.floor(n / 4);
72
+ const r = a - n * PIO2_1 - n * PIO2_2 - n * PIO2_2T;
73
+ const z = r * r;
74
+ const ks = r + z * r * (S1 + z * (S2 + z * (S3 + z * (S4 + z * (S5 + z * S6)))));
75
+ const hz = 0.5 * z;
76
+ const w = 1 - hz;
77
+ const kc = w + (1 - w - hz + z * (z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * C6)))))));
78
+ if (q === 0) return [unsignedZero(ks), unsignedZero(kc)];
79
+ if (q === 1) return [unsignedZero(kc), unsignedZero(-ks)];
80
+ if (q === 2) return [unsignedZero(-ks), unsignedZero(-kc)];
81
+ return [unsignedZero(-kc), unsignedZero(ks)];
82
+ }
83
+ var psin = (a) => psincos(a)[0];
84
+ var pcos = (a) => psincos(a)[1];
85
+
86
+ // src/authority/presets.ts
87
+ var Preset = {
88
+ /** Planar movement on x/y with an aim angle: the server-authority spike's arena. */
89
+ TOPDOWN: "topdown",
90
+ /** Source-engine character movement through a static box-and-ramp world, z up (§17.7). */
91
+ FIRSTPERSON: "firstperson",
92
+ /** An arcade car (Rocket League's ground and air handling, kept level) through the same world (§17.7). */
93
+ VEHICLE: "vehicle"
94
+ };
95
+ var PRESET_MAX_DT_S = 0.25;
96
+ var TOPDOWN_DEFAULTS = Object.freeze({ speed: 6, arena: 20 });
97
+ var TOPDOWN_RANGE = {
98
+ speed: { min: 0, max: 1e3, positive: false },
99
+ arena: { min: 0, max: 1e6, positive: true }
100
+ };
101
+ function isPresetName(name) {
102
+ return Object.values(Preset).some((known) => known === name);
103
+ }
104
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && Object.getPrototypeOf(v) === Object.prototype;
105
+ function presetParamsValid(name, params) {
106
+ if (params === void 0) return true;
107
+ const ranges = definitionOf(name)?.ranges;
108
+ if (ranges === void 0 || !isPlainObject(params)) return false;
109
+ return Object.keys(params).every((key) => {
110
+ if (!Object.hasOwn(ranges, key)) return false;
111
+ const range = ranges[key];
112
+ const v = params[key];
113
+ if (typeof v !== "number" || !Number.isFinite(v) || v < range.min || v > range.max) return false;
114
+ if (range.integer === true && !Number.isInteger(v)) return false;
115
+ return !range.positive || v > 0 && Math.fround(v) > 0;
116
+ });
117
+ }
118
+ function resolvePresetParams(name, params) {
119
+ const definition = definitionOf(name);
120
+ if (definition === void 0) return {};
121
+ const out = { ...definition.defaults };
122
+ if (!isPlainObject(params)) return out;
123
+ for (const key of Object.keys(definition.ranges)) {
124
+ if (presetParamsValid(name, { [key]: params[key] })) out[key] = params[key];
125
+ }
126
+ return out;
127
+ }
128
+ function resolveTopdownParams(params) {
129
+ return resolvePresetParams(Preset.TOPDOWN, params);
130
+ }
131
+ var finiteOrZero = (v) => Number.isFinite(v) ? v : 0;
132
+ function stepTopdown(body, input, dt, params) {
133
+ const moveX = axis(input.moveX);
134
+ const moveY = axis(input.moveY);
135
+ const step = stepSeconds(dt, PRESET_MAX_DT_S);
136
+ const len = Math.sqrt(moveX * moveX + moveY * moveY);
137
+ const scale = len > 1 ? 1 / len : 1;
138
+ const x = finiteOrZero(body.x) + moveX * scale * params.speed * step;
139
+ const y = finiteOrZero(body.y) + moveY * scale * params.speed * step;
140
+ return {
141
+ x: unsignedZero(Math.fround(clamp(x, -params.arena, params.arena))),
142
+ y: unsignedZero(Math.fround(clamp(y, -params.arena, params.arena))),
143
+ yaw: wrappedAngle(input.yaw) ?? unsignedZero(Math.fround(finiteOrZero(body.yaw)))
144
+ };
145
+ }
146
+ var EMPTY_WORLD_HASH = 2615243109;
147
+ var FIRSTPERSON_DEFAULTS = Object.freeze({
148
+ maxSpeed: 250,
149
+ walkSpeed: 0.52,
150
+ crouchSpeed: 0.34,
151
+ accelerate: 5.5,
152
+ airAccelerate: 12,
153
+ airMaxWishSpeed: 30,
154
+ friction: 5.2,
155
+ stopSpeed: 80,
156
+ gravity: 800,
157
+ jumpImpulse: 301.993,
158
+ maxVelocity: 3500,
159
+ radius: 16,
160
+ standHeight: 72,
161
+ crouchHeight: 54,
162
+ duckTime: 0.4,
163
+ stepHeight: 18,
164
+ standable: 0.7,
165
+ groundTrace: 2.5,
166
+ groundSnap: 1,
167
+ skin: 0.03,
168
+ pitchLimit: 1.5533430576324463,
169
+ jumpBit: 1,
170
+ duckBit: 2,
171
+ walkBit: 4,
172
+ substeps: 2,
173
+ world: EMPTY_WORLD_HASH
174
+ });
175
+ var inRange = (min, max) => ({ min, max, positive: false });
176
+ var aboveZero = (max) => ({ min: 0, max, positive: true });
177
+ var integerIn = (min, max) => ({ min, max, positive: false, integer: true });
178
+ var FIRSTPERSON_RANGE = {
179
+ maxSpeed: inRange(0, 1e4),
180
+ walkSpeed: inRange(0, 1),
181
+ crouchSpeed: inRange(0, 1),
182
+ accelerate: inRange(0, 1e3),
183
+ airAccelerate: inRange(0, 1e3),
184
+ airMaxWishSpeed: inRange(0, 1e4),
185
+ friction: inRange(0, 1e3),
186
+ stopSpeed: inRange(0, 1e4),
187
+ gravity: inRange(0, 1e5),
188
+ jumpImpulse: inRange(0, 1e5),
189
+ maxVelocity: inRange(0, 1e5),
190
+ radius: aboveZero(1e4),
191
+ standHeight: aboveZero(1e4),
192
+ crouchHeight: aboveZero(1e4),
193
+ duckTime: aboveZero(100),
194
+ stepHeight: inRange(0, 1e4),
195
+ standable: inRange(0, 1),
196
+ groundTrace: inRange(0, 1e4),
197
+ groundSnap: inRange(0, 1e4),
198
+ skin: inRange(0, 100),
199
+ pitchLimit: inRange(0, 1.5707963705062866),
200
+ jumpBit: integerIn(0, MASK_LIMIT - 1),
201
+ duckBit: integerIn(0, MASK_LIMIT - 1),
202
+ walkBit: integerIn(0, MASK_LIMIT - 1),
203
+ substeps: integerIn(1, 8),
204
+ world: integerIn(0, 2 ** 32 - 1)
205
+ };
206
+ function resolveFirstpersonParams(params) {
207
+ return resolvePresetParams(Preset.FIRSTPERSON, params);
208
+ }
209
+ var MoveState = {
210
+ ON_GROUND: 1,
211
+ DUCKING: 2,
212
+ JUMP_HELD: 4
213
+ };
214
+ var MSTATE_BITS = MoveState.ON_GROUND | MoveState.DUCKING | MoveState.JUMP_HELD;
215
+ var WorldLimits = {
216
+ MAX_BOXES: 4096,
217
+ MAX_RAMPS: 512,
218
+ /** Every world value's magnitude. */
219
+ MAX_ABS
220
+ };
221
+ var WorldError = class extends RangeError {
222
+ path;
223
+ constructor(path, message) {
224
+ super(`firstperson world: ${path} ${message}`);
225
+ this.name = "WorldError";
226
+ this.path = path;
227
+ }
228
+ };
229
+ var compiled = /* @__PURE__ */ new WeakMap();
230
+ var worldValue = (v, path) => {
231
+ if (typeof v !== "number" || !Number.isFinite(v)) throw new WorldError(path, "is not a finite number");
232
+ if (Math.fround(v) !== v) throw new WorldError(path, "is not a float32");
233
+ if (Math.abs(v) > MAX_ABS) throw new WorldError(path, `is past \xB1${MAX_ABS}`);
234
+ return unsignedZero(v);
235
+ };
236
+ function solidValues(raw, length, path) {
237
+ if (!Array.isArray(raw) || raw.length !== length) throw new WorldError(path, `is not an array of ${length} numbers`);
238
+ const out = [];
239
+ for (let i = 0; i < length; i++) out.push(worldValue(raw[i], `${path}[${i}]`));
240
+ if (!(out[0] < out[3] && out[1] < out[4] && out[2] < out[5])) throw new WorldError(path, "needs min < max on every axis");
241
+ return out;
242
+ }
243
+ function solidList(raw, key, max) {
244
+ if (raw === void 0) return [];
245
+ if (!Array.isArray(raw)) throw new WorldError(key, "is not an array");
246
+ if (raw.length > max) throw new WorldError(key, `holds more than ${max}`);
247
+ return raw;
248
+ }
249
+ function worldHash(boxes, ramps) {
250
+ const bytes = new DataView(new ArrayBuffer(8 + 4 * (6 * boxes.length + 9 * ramps.length)));
251
+ bytes.setUint32(0, boxes.length, true);
252
+ bytes.setUint32(4, ramps.length, true);
253
+ let at = 8;
254
+ for (const solid of [...boxes, ...ramps]) {
255
+ for (const v of solid) {
256
+ bytes.setFloat32(at, v, true);
257
+ at += 4;
258
+ }
259
+ }
260
+ let h = 2166136261;
261
+ for (let i = 0; i < bytes.byteLength; i++) h = Math.imul(h ^ bytes.getUint8(i), 16777619) >>> 0;
262
+ return h;
263
+ }
264
+ function firstpersonWorld(raw, options = {}) {
265
+ const made = compiled.has(raw) ? raw : void 0;
266
+ if (made !== void 0 && (options.broadphase === void 0 || options.broadphase === made.broadphase)) return made;
267
+ if (raw !== void 0 && !isPlainObject(raw)) throw new WorldError("$", "is not an object");
268
+ const spec = raw ?? {};
269
+ const boxes = solidList(spec.boxes, "boxes", WorldLimits.MAX_BOXES).map(
270
+ (box, i) => Object.freeze(solidValues(box, 6, `boxes[${i}]`))
271
+ );
272
+ const ramps = solidList(spec.ramps, "ramps", WorldLimits.MAX_RAMPS).map((ramp, i) => {
273
+ const path = `ramps[${i}]`;
274
+ const v = solidValues(ramp, 9, path);
275
+ if (v[6] !== 0 && v[6] !== 1) throw new WorldError(`${path}[6]`, "is not an axis (0 or 1)");
276
+ const [minZ, maxZ, lo, hi] = [v[2], v[5], v[7], v[8]];
277
+ if (!(minZ <= lo && minZ <= hi)) throw new WorldError(path, "needs minZ \u2264 lo and minZ \u2264 hi");
278
+ if (maxZ !== Math.max(lo, hi)) throw new WorldError(path, "needs maxZ = max(lo, hi)");
279
+ return Object.freeze(v);
280
+ });
281
+ const world = Object.freeze({
282
+ boxes: Object.freeze(boxes),
283
+ ramps: Object.freeze(ramps),
284
+ hash: worldHash(boxes, ramps),
285
+ broadphase: options.broadphase !== false
286
+ });
287
+ compiled.set(world, compile(world));
288
+ return world;
289
+ }
290
+ var EMPTY_WORLD = firstpersonWorld(void 0);
291
+ var PosedKind = { Box: 0, OBox: 1, Disc: 2 };
292
+ var POSED_MAX = 96;
293
+ var POSED_SCRATCH = new Int32Array(POSED_MAX);
294
+ function posedWorld(solids) {
295
+ const n = solids.length;
296
+ if (n > POSED_MAX) throw new RangeError(`posed solids: at most ${POSED_MAX} a tick`);
297
+ const table = () => new Float64Array(n);
298
+ const p = { count: n, kind: new Uint8Array(n), minX: table(), minY: table(), minZ: table(), maxX: table(), maxY: table(), maxZ: table(), cx: table(), cy: table(), hx: table(), hy: table(), sin: table(), cos: table(), radius: table() };
299
+ solids.forEach((s, i) => {
300
+ if (s.kind === "box") {
301
+ p.kind[i] = PosedKind.Box;
302
+ [p.minX[i], p.minY[i], p.minZ[i]] = s.min;
303
+ [p.maxX[i], p.maxY[i], p.maxZ[i]] = s.max;
304
+ return;
305
+ }
306
+ p.cx[i] = s.c[0];
307
+ p.cy[i] = s.c[1];
308
+ p.minZ[i] = s.c[2] - (s.kind === "disc" ? s.half : s.half[2]);
309
+ p.maxZ[i] = s.c[2] + (s.kind === "disc" ? s.half : s.half[2]);
310
+ if (s.kind === "disc") {
311
+ p.kind[i] = PosedKind.Disc;
312
+ p.radius[i] = s.radius;
313
+ p.minX[i] = s.c[0] - s.radius;
314
+ p.maxX[i] = s.c[0] + s.radius;
315
+ p.minY[i] = s.c[1] - s.radius;
316
+ p.maxY[i] = s.c[1] + s.radius;
317
+ return;
318
+ }
319
+ p.kind[i] = PosedKind.OBox;
320
+ p.hx[i] = s.half[0];
321
+ p.hy[i] = s.half[1];
322
+ p.sin[i] = s.sin;
323
+ p.cos[i] = s.cos;
324
+ const ac = Math.abs(s.cos);
325
+ const as = Math.abs(s.sin);
326
+ const ex = s.half[0] * ac + s.half[1] * as;
327
+ const ey = s.half[0] * as + s.half[1] * ac;
328
+ p.minX[i] = s.c[0] - ex;
329
+ p.maxX[i] = s.c[0] + ex;
330
+ p.minY[i] = s.c[1] - ey;
331
+ p.maxY[i] = s.c[1] + ey;
332
+ });
333
+ return p;
334
+ }
335
+ var NO_POSED = posedWorld([]);
336
+ function compiledOf(world) {
337
+ const known = compiled.get(world);
338
+ if (known !== void 0) return known;
339
+ const checked = firstpersonWorld(world, { broadphase: world.broadphase !== false });
340
+ const tables = compiled.get(checked);
341
+ compiled.set(world, tables);
342
+ return tables;
343
+ }
344
+ var TREE_FANOUT = 8;
345
+ function compile(world) {
346
+ const solids = [...world.boxes, ...world.ramps];
347
+ const count = solids.length;
348
+ const table = () => new Float64Array(count);
349
+ const t = {
350
+ boxCount: world.boxes.length,
351
+ count,
352
+ minX: table(),
353
+ minY: table(),
354
+ minZ: table(),
355
+ maxX: table(),
356
+ maxY: table(),
357
+ maxZ: table(),
358
+ axis: new Uint8Array(count),
359
+ lo: table(),
360
+ hi: table(),
361
+ m0: table(),
362
+ span: table(),
363
+ slope: table(),
364
+ topZ: table()
365
+ };
366
+ solids.forEach((s, i) => {
367
+ t.minX[i] = s[0];
368
+ t.minY[i] = s[1];
369
+ t.minZ[i] = s[2];
370
+ t.maxX[i] = s[3];
371
+ t.maxY[i] = s[4];
372
+ t.maxZ[i] = s[5];
373
+ if (i < t.boxCount) return;
374
+ const axis2 = s[6];
375
+ const m0 = axis2 === 0 ? s[0] : s[1];
376
+ const m1 = axis2 === 0 ? s[3] : s[4];
377
+ const span = m1 - m0;
378
+ const slope = (s[8] - s[7]) / span;
379
+ t.axis[i] = axis2;
380
+ t.lo[i] = s[7];
381
+ t.hi[i] = s[8];
382
+ t.m0[i] = m0;
383
+ t.span[i] = span;
384
+ t.slope[i] = slope;
385
+ t.topZ[i] = 1 / Math.sqrt(1 + slope * slope);
386
+ });
387
+ return { ...t, tree: world.broadphase && count > 0 ? buildTree(t) : null };
388
+ }
389
+ function spread3(v) {
390
+ let x = v & 1023;
391
+ x = (x | x << 16) & 50331903;
392
+ x = (x | x << 8) & 50393103;
393
+ x = (x | x << 4) & 51130563;
394
+ x = (x | x << 2) & 153391689;
395
+ return x;
396
+ }
397
+ function buildTree(t) {
398
+ const n = t.count;
399
+ let x0 = Infinity, y0 = Infinity, z0 = Infinity, x1 = -Infinity, y1 = -Infinity, z1 = -Infinity;
400
+ for (let i = 0; i < n; i++) {
401
+ x0 = Math.min(x0, t.minX[i]);
402
+ y0 = Math.min(y0, t.minY[i]);
403
+ z0 = Math.min(z0, t.minZ[i]);
404
+ x1 = Math.max(x1, t.maxX[i]);
405
+ y1 = Math.max(y1, t.maxY[i]);
406
+ z1 = Math.max(z1, t.maxZ[i]);
407
+ }
408
+ const cell = (v, lo, hi) => hi > lo ? Math.min(1023, Math.floor((v - lo) / (hi - lo) * 1024)) : 0;
409
+ const keys = new Float64Array(n);
410
+ for (let i = 0; i < n; i++) {
411
+ const cx = cell((t.minX[i] + t.maxX[i]) / 2, x0, x1);
412
+ const cy = cell((t.minY[i] + t.maxY[i]) / 2, y0, y1);
413
+ const cz = cell((t.minZ[i] + t.maxZ[i]) / 2, z0, z1);
414
+ keys[i] = (spread3(cx) | spread3(cy) << 1 | spread3(cz) << 2) * 8192 + i;
415
+ }
416
+ keys.sort();
417
+ const sizes = [n];
418
+ while (sizes[sizes.length - 1] > 1) sizes.push(Math.ceil(sizes[sizes.length - 1] / TREE_FANOUT));
419
+ const total = sizes.reduce((a, b) => a + b, 0);
420
+ const boxes = new Float64Array(total * 6);
421
+ const ref = new Int32Array(total);
422
+ const levelEnd = new Int32Array(sizes.length);
423
+ for (let k = 0; k < n; k++) {
424
+ const i = keys[k] % 8192;
425
+ const o = k * 6;
426
+ boxes[o] = t.minX[i];
427
+ boxes[o + 1] = t.minY[i];
428
+ boxes[o + 2] = t.minZ[i];
429
+ boxes[o + 3] = t.maxX[i];
430
+ boxes[o + 4] = t.maxY[i];
431
+ boxes[o + 5] = t.maxZ[i];
432
+ ref[k] = i;
433
+ }
434
+ levelEnd[0] = n;
435
+ let from = 0;
436
+ let at = n;
437
+ for (let level = 1; level < sizes.length; level++) {
438
+ const end = levelEnd[level - 1];
439
+ for (let child = from; child < end; child += TREE_FANOUT) {
440
+ const last = Math.min(child + TREE_FANOUT, end);
441
+ let a = Infinity, b = Infinity, c = Infinity, d = -Infinity, e = -Infinity, g = -Infinity;
442
+ for (let k = child; k < last; k++) {
443
+ const o2 = k * 6;
444
+ a = Math.min(a, boxes[o2]);
445
+ b = Math.min(b, boxes[o2 + 1]);
446
+ c = Math.min(c, boxes[o2 + 2]);
447
+ d = Math.max(d, boxes[o2 + 3]);
448
+ e = Math.max(e, boxes[o2 + 4]);
449
+ g = Math.max(g, boxes[o2 + 5]);
450
+ }
451
+ const o = at * 6;
452
+ boxes[o] = a;
453
+ boxes[o + 1] = b;
454
+ boxes[o + 2] = c;
455
+ boxes[o + 3] = d;
456
+ boxes[o + 4] = e;
457
+ boxes[o + 5] = g;
458
+ ref[at] = child;
459
+ at++;
460
+ }
461
+ from = end;
462
+ levelEnd[level] = at;
463
+ }
464
+ return { boxes, ref, levelEnd, root: total - 1 };
465
+ }
466
+ var queryMargin = (a, b, c, d, e, g) => 1e-3 + 1e-7 * (Math.abs(a) + Math.abs(b) + Math.abs(c) + Math.abs(d) + Math.abs(e) + Math.abs(g));
467
+ function query(m, qx0, qy0, qz0, qx1, qy1, qz1) {
468
+ queryPosed(m, qx0, qy0, qz0, qx1, qy1, qz1);
469
+ const tree = m.w.tree;
470
+ if (tree === null) {
471
+ m.nCand = -1;
472
+ return;
473
+ }
474
+ const margin = queryMargin(qx0, qy0, qz0, qx1, qy1, qz1);
475
+ const pad = m.r + margin;
476
+ const x0 = qx0 - pad, x1 = qx1 + pad, y0 = qy0 - pad, y1 = qy1 + pad, z0 = qz0 - margin, z1 = qz1 + margin;
477
+ const { boxes, ref, levelEnd } = tree;
478
+ const count = levelEnd[0];
479
+ const cand = m.cand;
480
+ const stack = m.stack;
481
+ let n = 0;
482
+ let top = 0;
483
+ stack[top++] = tree.root;
484
+ while (top > 0) {
485
+ const node = stack[--top];
486
+ const o = node * 6;
487
+ if (boxes[o + 3] < x0 || boxes[o] > x1 || boxes[o + 4] < y0 || boxes[o + 1] > y1 || boxes[o + 5] < z0 || boxes[o + 2] > z1) continue;
488
+ if (node < count) {
489
+ cand[n++] = ref[node];
490
+ continue;
491
+ }
492
+ const first = ref[node];
493
+ let end = first + TREE_FANOUT;
494
+ for (let l = 0; l < levelEnd.length; l++) {
495
+ if (first < levelEnd[l]) {
496
+ end = Math.min(end, levelEnd[l]);
497
+ break;
498
+ }
499
+ }
500
+ for (let k = first; k < end; k++) stack[top++] = k;
501
+ }
502
+ m.nCand = n;
503
+ }
504
+ function queryPosed(m, qx0, qy0, qz0, qx1, qy1, qz1) {
505
+ const p = m.p;
506
+ let n = 0;
507
+ if (p.count > 0) {
508
+ const margin = queryMargin(qx0, qy0, qz0, qx1, qy1, qz1);
509
+ const pad = m.r + margin;
510
+ const x0 = qx0 - pad, x1 = qx1 + pad, y0 = qy0 - pad, y1 = qy1 + pad, z0 = qz0 - margin, z1 = qz1 + margin;
511
+ for (let j = 0; j < p.count; j++) {
512
+ if (p.maxX[j] < x0 || p.minX[j] > x1 || p.maxY[j] < y0 || p.minY[j] > y1 || p.maxZ[j] < z0 || p.minZ[j] > z1) continue;
513
+ m.pc[n++] = j;
514
+ }
515
+ }
516
+ m.nPosed = n;
517
+ }
518
+ var visits = (m) => (m.nCand < 0 ? m.w.count : m.nCand) + m.nPosed;
519
+ var solidAt = (m, k) => {
520
+ const base = m.nCand < 0 ? m.w.count : m.nCand;
521
+ if (k >= base) return m.w.count + m.pc[k - base];
522
+ return m.nCand < 0 ? k : m.cand[k];
523
+ };
524
+ var hullAt = (m, d) => m.standHeight + (m.crouchHeight - m.standHeight) * d;
525
+ function support(m, i, px, py) {
526
+ const w = m.w;
527
+ const slope = w.slope[i];
528
+ const c = w.axis[i] === 0 ? px : py;
529
+ const c2 = slope > 0 ? c + m.r : slope < 0 ? c - m.r : c;
530
+ return w.lo[i] + slope * clamp(c2 - w.m0[i], 0, w.span[i]);
531
+ }
532
+ function topOf(m, i, px, py) {
533
+ const w = m.w;
534
+ if (i < w.boxCount) return w.maxZ[i];
535
+ if (i < w.count) return support(m, i, px, py);
536
+ return m.p.maxZ[i - w.count];
537
+ }
538
+ var minZOf = (m, i) => i < m.w.count ? m.w.minZ[i] : m.p.minZ[i - m.w.count];
539
+ function overlaps(m, i, px, py) {
540
+ const w = m.w;
541
+ const r = m.r;
542
+ if (i >= w.count) return posedOverlaps(m, i - w.count, px, py);
543
+ return px + r > w.minX[i] && px - r < w.maxX[i] && py + r > w.minY[i] && py - r < w.maxY[i];
544
+ }
545
+ function fits(m, px, py, pz, height) {
546
+ const w = m.w;
547
+ query(m, px, py, pz, px, py, pz + height);
548
+ const n = visits(m);
549
+ for (let k = 0; k < n; k++) {
550
+ const i = solidAt(m, k);
551
+ if (!overlaps(m, i, px, py) || !(pz + height > minZOf(m, i))) continue;
552
+ const top = topOf(m, i, px, py);
553
+ if (pz < top - 1e-4) return false;
554
+ }
555
+ return true;
556
+ }
557
+ function traceDown(m, px, py, pz, down, up) {
558
+ const w = m.w;
559
+ query(m, px, py, pz - down, px, py, pz + up);
560
+ const n = visits(m);
561
+ let best = Number.NaN;
562
+ let steep = Number.NaN;
563
+ for (let k = 0; k < n; k++) {
564
+ const i = solidAt(m, k);
565
+ if (!overlaps(m, i, px, py)) continue;
566
+ const top = topOf(m, i, px, py);
567
+ if (!(top <= pz + up && top >= pz - down)) continue;
568
+ if (i < w.boxCount || i >= w.count || w.topZ[i] >= m.standable) {
569
+ if (best !== best || top > best) best = top;
570
+ } else if (steep !== steep || top > steep) steep = top;
571
+ }
572
+ return steep > best ? Number.NaN : best;
573
+ }
574
+ function checkGround(m) {
575
+ if (m.vz > 0.1) {
576
+ m.onGround = false;
577
+ return;
578
+ }
579
+ const g = traceDown(m, m.x, m.y, m.z, m.groundTrace, m.groundSnap);
580
+ if (g === g) {
581
+ m.z = g;
582
+ m.onGround = true;
583
+ } else m.onGround = false;
584
+ }
585
+ function slabAxis(m, p, d, lo, hi, a) {
586
+ if (Math.abs(d) < 1e-9) return p > lo && p < hi;
587
+ let t1 = (lo - p) / d;
588
+ let t2 = (hi - p) / d;
589
+ let s = -1;
590
+ if (t1 > t2) {
591
+ const swap = t1;
592
+ t1 = t2;
593
+ t2 = swap;
594
+ s = 1;
595
+ }
596
+ if (t1 >= m.tMin) {
597
+ m.tMin = t1;
598
+ m.ax = a;
599
+ m.sg = s;
600
+ }
601
+ if (t2 < m.tMax) m.tMax = t2;
602
+ return !(m.tMin > m.tMax);
603
+ }
604
+ var PLANE = new Float64Array(4);
605
+ function wedgePlane(m, i, plane, height) {
606
+ const w = m.w;
607
+ const r = m.r;
608
+ let nx = 0;
609
+ let ny = 0;
610
+ let nz = 0;
611
+ let e;
612
+ switch (plane) {
613
+ case 0:
614
+ nx = 1;
615
+ e = w.maxX[i] + r;
616
+ break;
617
+ case 1:
618
+ nx = -1;
619
+ e = -w.minX[i] + r;
620
+ break;
621
+ case 2:
622
+ ny = 1;
623
+ e = w.maxY[i] + r;
624
+ break;
625
+ case 3:
626
+ ny = -1;
627
+ e = -w.minY[i] + r;
628
+ break;
629
+ case 4:
630
+ nz = -1;
631
+ e = -w.minZ[i] + height;
632
+ break;
633
+ case 5: {
634
+ const slope = w.slope[i];
635
+ if (w.axis[i] === 0) nx = -slope;
636
+ else ny = -slope;
637
+ nz = 1;
638
+ e = w.lo[i] - slope * w.m0[i] + r * Math.abs(slope);
639
+ break;
640
+ }
641
+ default:
642
+ nz = 1;
643
+ e = w.maxZ[i];
644
+ }
645
+ PLANE[0] = nx;
646
+ PLANE[1] = ny;
647
+ PLANE[2] = nz;
648
+ PLANE[3] = e;
649
+ }
650
+ function insideSolid(m, px, py, pz, i, height) {
651
+ const w = m.w;
652
+ const r = m.r;
653
+ const skin = m.skin;
654
+ if (i >= w.count) return posedInside(m, px, py, pz, i - w.count, height);
655
+ if (i < w.boxCount) {
656
+ return px - (w.minX[i] - r) > skin && w.maxX[i] + r - px > skin && py - (w.minY[i] - r) > skin && w.maxY[i] + r - py > skin && pz - (w.minZ[i] - height) > skin && w.maxZ[i] - pz > skin;
657
+ }
658
+ for (let plane = 0; plane < 7; plane++) {
659
+ wedgePlane(m, i, plane, height);
660
+ const nx = PLANE[0];
661
+ const ny = PLANE[1];
662
+ const nz = PLANE[2];
663
+ const depth = -(nx * px + ny * py + nz * pz - PLANE[3]) * (1 / Math.sqrt(nx * nx + ny * ny + nz * nz));
664
+ if (!(depth > skin)) return false;
665
+ }
666
+ return true;
667
+ }
668
+ function covered(m, qx, qy, qz, px, py, pz, self, height) {
669
+ const all = m.w.count + m.p.count;
670
+ for (let j = 0; j < all; j++) {
671
+ if (j !== self && insideSolid(m, qx, qy, qz, j, height) && !insideSolid(m, px, py, pz, j, height)) return true;
672
+ }
673
+ return false;
674
+ }
675
+ function lidCovered(m, px, py, pz, ux, uy, uz, depth, i, height) {
676
+ return covered(m, px + ux * depth, py + uy * depth, pz + uz * depth, px, py, pz, i, height);
677
+ }
678
+ function slabInside(m, px, py, pz, b, i, height, self) {
679
+ const r = m.r;
680
+ const skin = m.skin;
681
+ const lx = px - (b.minX[i] - r);
682
+ const hx = b.maxX[i] + r - px;
683
+ const ly = py - (b.minY[i] - r);
684
+ const hy = b.maxY[i] + r - py;
685
+ const lz = pz - (b.minZ[i] - height);
686
+ const hz = b.maxZ[i] - pz;
687
+ if (!(lx > skin && hx > skin && ly > skin && hy > skin && lz > skin && hz > skin)) return false;
688
+ let depth = lx;
689
+ let nx = -1;
690
+ let ny = 0;
691
+ let nz = 0;
692
+ if (hx < depth) [depth, nx, ny, nz] = [hx, 1, 0, 0];
693
+ if (ly < depth) [depth, nx, ny, nz] = [ly, 0, -1, 0];
694
+ if (hy < depth) [depth, nx, ny, nz] = [hy, 0, 1, 0];
695
+ if (hz < depth && !covered(m, px, py, pz + hz, px, py, pz, self, height)) [depth, nx, ny, nz] = [hz, 0, 0, 1];
696
+ m.tMin = 0;
697
+ m.nx = nx;
698
+ m.ny = ny;
699
+ m.nz = nz;
700
+ m.depth = depth;
701
+ return true;
702
+ }
703
+ function slab(m, px, py, pz, dx, dy, dz, i, height, b = m.w, self = i) {
704
+ const r = m.r;
705
+ m.depth = 0;
706
+ if (slabInside(m, px, py, pz, b, i, height, self)) return true;
707
+ m.tMin = 0;
708
+ m.tMax = 1;
709
+ m.ax = -1;
710
+ m.sg = 0;
711
+ if (!slabAxis(m, px, dx, b.minX[i] - r, b.maxX[i] + r, 0)) return false;
712
+ if (!slabAxis(m, py, dy, b.minY[i] - r, b.maxY[i] + r, 1)) return false;
713
+ if (!slabAxis(m, pz, dz, b.minZ[i] - height, b.maxZ[i], 2)) return false;
714
+ if (m.tMin >= 1 || m.ax < 0) return false;
715
+ m.nx = m.ax === 0 ? m.sg : 0;
716
+ m.ny = m.ax === 1 ? m.sg : 0;
717
+ m.nz = m.ax === 2 ? m.sg : 0;
718
+ return true;
719
+ }
720
+ function wedge(m, px, py, pz, dx, dy, dz, i, height) {
721
+ const w = m.w;
722
+ const r = m.r;
723
+ const slope = w.slope[i];
724
+ const axisX = w.axis[i] === 0;
725
+ let enter = 0;
726
+ let leave = 1;
727
+ let taken = false;
728
+ let inside = true;
729
+ let least = Number.POSITIVE_INFINITY;
730
+ let lx = 0;
731
+ let ly = 0;
732
+ let lz = 0;
733
+ m.depth = 0;
734
+ for (let plane = 0; plane < 7; plane++) {
735
+ let nx = 0;
736
+ let ny = 0;
737
+ let nz = 0;
738
+ let e;
739
+ switch (plane) {
740
+ case 0:
741
+ nx = 1;
742
+ e = w.maxX[i] + r;
743
+ break;
744
+ case 1:
745
+ nx = -1;
746
+ e = -w.minX[i] + r;
747
+ break;
748
+ case 2:
749
+ ny = 1;
750
+ e = w.maxY[i] + r;
751
+ break;
752
+ case 3:
753
+ ny = -1;
754
+ e = -w.minY[i] + r;
755
+ break;
756
+ case 4:
757
+ nz = -1;
758
+ e = -w.minZ[i] + height;
759
+ break;
760
+ case 5:
761
+ if (axisX) nx = -slope;
762
+ else ny = -slope;
763
+ nz = 1;
764
+ e = w.lo[i] - slope * w.m0[i] + r * Math.abs(slope);
765
+ break;
766
+ default:
767
+ nz = 1;
768
+ e = w.maxZ[i];
769
+ }
770
+ const dist = nx * px + ny * py + nz * pz - e;
771
+ const motion = nx * dx + ny * dy + nz * dz;
772
+ const k = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
773
+ const depth = -dist * k;
774
+ if (!(depth > m.skin)) inside = false;
775
+ else if (plane !== 4 && depth < least && !(inside && plane >= 5 && lidCovered(m, px, py, pz, nx * k, ny * k, nz * k, depth, i, height))) {
776
+ least = depth;
777
+ lx = nx * k;
778
+ ly = ny * k;
779
+ lz = nz * k;
780
+ }
781
+ if (Math.abs(motion) < 1e-9) {
782
+ if (dist > 0) return false;
783
+ continue;
784
+ }
785
+ const t = motion < 0 && dist <= 0 && dist > -m.skin ? 0 : -dist / motion;
786
+ if (motion < 0) {
787
+ if (t >= enter) {
788
+ enter = t;
789
+ m.nx = nx * k;
790
+ m.ny = ny * k;
791
+ m.nz = nz * k;
792
+ taken = true;
793
+ }
794
+ } else leave = Math.min(leave, t);
795
+ if (enter > leave) return false;
796
+ }
797
+ if (inside) {
798
+ m.tMin = 0;
799
+ m.nx = lx;
800
+ m.ny = ly;
801
+ m.nz = lz;
802
+ m.depth = least;
803
+ return true;
804
+ }
805
+ if (!taken || !(enter < 1) || !(leave >= 0)) return false;
806
+ m.tMin = enter;
807
+ return true;
808
+ }
809
+ function oboxPlane(m, j, plane, height) {
810
+ const p = m.p;
811
+ const c = p.cos[j];
812
+ const s = p.sin[j];
813
+ const cx = p.cx[j];
814
+ const cy = p.cy[j];
815
+ const hx = p.hx[j];
816
+ const hy = p.hy[j];
817
+ const r = m.r;
818
+ const reach = r * (Math.abs(c) + Math.abs(s));
819
+ let nx = 0;
820
+ let ny = 0;
821
+ let nz = 0;
822
+ let e;
823
+ switch (plane) {
824
+ case 0:
825
+ nx = c;
826
+ ny = s;
827
+ e = c * cx + s * cy + hx + reach;
828
+ break;
829
+ case 1:
830
+ nx = -c;
831
+ ny = -s;
832
+ e = -(c * cx + s * cy) + hx + reach;
833
+ break;
834
+ case 2:
835
+ nx = -s;
836
+ ny = c;
837
+ e = -s * cx + c * cy + hy + reach;
838
+ break;
839
+ case 3:
840
+ nx = s;
841
+ ny = -c;
842
+ e = s * cx - c * cy + hy + reach;
843
+ break;
844
+ case 4:
845
+ nx = 1;
846
+ e = cx + r + hx * Math.abs(c) + hy * Math.abs(s);
847
+ break;
848
+ case 5:
849
+ nx = -1;
850
+ e = -cx + r + hx * Math.abs(c) + hy * Math.abs(s);
851
+ break;
852
+ case 6:
853
+ ny = 1;
854
+ e = cy + r + hx * Math.abs(s) + hy * Math.abs(c);
855
+ break;
856
+ case 7:
857
+ ny = -1;
858
+ e = -cy + r + hx * Math.abs(s) + hy * Math.abs(c);
859
+ break;
860
+ case 8:
861
+ nz = -1;
862
+ e = -p.minZ[j] + height;
863
+ break;
864
+ default:
865
+ nz = 1;
866
+ e = p.maxZ[j];
867
+ }
868
+ PLANE[0] = nx;
869
+ PLANE[1] = ny;
870
+ PLANE[2] = nz;
871
+ PLANE[3] = e;
872
+ }
873
+ var OBOX_PLANES = 10;
874
+ var OBOX_BOTTOM = 8;
875
+ var OBOX_TOP = 9;
876
+ function posedOverlaps(m, j, px, py) {
877
+ const p = m.p;
878
+ const r = m.r;
879
+ if (!(px + r > p.minX[j] && px - r < p.maxX[j] && py + r > p.minY[j] && py - r < p.maxY[j])) return false;
880
+ const kind = p.kind[j];
881
+ if (kind === PosedKind.Box) return true;
882
+ const dx = px - p.cx[j];
883
+ const dy = py - p.cy[j];
884
+ if (kind === PosedKind.Disc) {
885
+ const ax = Math.max(Math.abs(dx) - r, 0);
886
+ const ay = Math.max(Math.abs(dy) - r, 0);
887
+ return ax * ax + ay * ay < p.radius[j] * p.radius[j];
888
+ }
889
+ const c = p.cos[j];
890
+ const s = p.sin[j];
891
+ const reach = r * (Math.abs(c) + Math.abs(s));
892
+ return Math.abs(c * dx + s * dy) < p.hx[j] + reach && Math.abs(-s * dx + c * dy) < p.hy[j] + reach;
893
+ }
894
+ function discDepth(m, j, px, py) {
895
+ const p = m.p;
896
+ const r = m.r;
897
+ const dx = px - p.cx[j];
898
+ const dy = py - p.cy[j];
899
+ const adx = Math.abs(dx);
900
+ const ady = Math.abs(dy);
901
+ const R = p.radius[j];
902
+ if (adx <= r && ady <= r) {
903
+ const alongX = adx >= ady;
904
+ PLANE[0] = alongX ? dx < 0 ? -1 : 1 : 0;
905
+ PLANE[1] = alongX ? 0 : dy < 0 ? -1 : 1;
906
+ return R + r - (alongX ? adx : ady);
907
+ }
908
+ const qx = adx > r ? dx < 0 ? -(adx - r) : adx - r : 0;
909
+ const qy = ady > r ? dy < 0 ? -(ady - r) : ady - r : 0;
910
+ const len = Math.sqrt(qx * qx + qy * qy);
911
+ PLANE[0] = qx / len;
912
+ PLANE[1] = qy / len;
913
+ return R - len;
914
+ }
915
+ function posedInside(m, px, py, pz, j, height) {
916
+ const p = m.p;
917
+ const skin = m.skin;
918
+ const kind = p.kind[j];
919
+ if (kind === PosedKind.Box) {
920
+ const r = m.r;
921
+ return px - (p.minX[j] - r) > skin && p.maxX[j] + r - px > skin && py - (p.minY[j] - r) > skin && p.maxY[j] + r - py > skin && pz - (p.minZ[j] - height) > skin && p.maxZ[j] - pz > skin;
922
+ }
923
+ if (!(pz - (p.minZ[j] - height) > skin && p.maxZ[j] - pz > skin)) return false;
924
+ if (kind === PosedKind.Disc) return discDepth(m, j, px, py) > skin;
925
+ for (let plane = 0; plane < OBOX_BOTTOM; plane++) {
926
+ oboxPlane(m, j, plane, height);
927
+ const nx = PLANE[0];
928
+ const ny = PLANE[1];
929
+ if (!(-(nx * px + ny * py - PLANE[3]) * (1 / Math.sqrt(nx * nx + ny * ny)) > skin)) return false;
930
+ }
931
+ return true;
932
+ }
933
+ function oboxSweep(m, px, py, pz, dx, dy, dz, j, i, height) {
934
+ let enter = 0;
935
+ let leave = 1;
936
+ let taken = false;
937
+ let inside = true;
938
+ let least = Number.POSITIVE_INFINITY;
939
+ let lx = 0;
940
+ let ly = 0;
941
+ let lz = 0;
942
+ m.depth = 0;
943
+ for (let plane = 0; plane < OBOX_PLANES; plane++) {
944
+ oboxPlane(m, j, plane, height);
945
+ const nx = PLANE[0];
946
+ const ny = PLANE[1];
947
+ const nz = PLANE[2];
948
+ const dist = nx * px + ny * py + nz * pz - PLANE[3];
949
+ const motion = nx * dx + ny * dy + nz * dz;
950
+ const k = 1 / Math.sqrt(nx * nx + ny * ny + nz * nz);
951
+ const depth = -dist * k;
952
+ if (!(depth > m.skin)) inside = false;
953
+ else if (plane !== OBOX_BOTTOM && depth < least && !(inside && plane === OBOX_TOP && lidCovered(m, px, py, pz, nx * k, ny * k, nz * k, depth, i, height))) {
954
+ least = depth;
955
+ lx = nx * k;
956
+ ly = ny * k;
957
+ lz = nz * k;
958
+ }
959
+ if (Math.abs(motion) < 1e-9) {
960
+ if (dist > 0) return false;
961
+ continue;
962
+ }
963
+ const t = motion < 0 && dist <= 0 && dist > -m.skin ? 0 : -dist / motion;
964
+ if (motion < 0) {
965
+ if (t >= enter) {
966
+ enter = t;
967
+ m.nx = nx * k;
968
+ m.ny = ny * k;
969
+ m.nz = nz * k;
970
+ taken = true;
971
+ }
972
+ } else leave = Math.min(leave, t);
973
+ if (enter > leave) return false;
974
+ }
975
+ if (inside) {
976
+ m.tMin = 0;
977
+ m.nx = lx;
978
+ m.ny = ly;
979
+ m.nz = lz;
980
+ m.depth = least;
981
+ return true;
982
+ }
983
+ if (!taken || !(enter < 1) || !(leave >= 0)) return false;
984
+ m.tMin = enter;
985
+ return true;
986
+ }
987
+ function cylinder(m, px, py, pz, dx, dy, dz, ox, oy, R, z0, z1) {
988
+ m.tMin = 0;
989
+ m.tMax = 1;
990
+ m.ax = -1;
991
+ m.sg = 0;
992
+ const ex = px - ox;
993
+ const ey = py - oy;
994
+ const a = dx * dx + dy * dy;
995
+ const c = ex * ex + ey * ey - R * R;
996
+ if (a < 1e-18) {
997
+ if (!(c < 0)) return false;
998
+ } else {
999
+ const b = ex * dx + ey * dy;
1000
+ const disc = b * b - a * c;
1001
+ if (disc < 0) return false;
1002
+ const sq = Math.sqrt(disc);
1003
+ const t1 = (-b - sq) / a;
1004
+ const t2 = (-b + sq) / a;
1005
+ if (t1 >= m.tMin) {
1006
+ m.tMin = t1;
1007
+ m.ax = 3;
1008
+ }
1009
+ if (t2 < m.tMax) m.tMax = t2;
1010
+ if (m.tMin > m.tMax) return false;
1011
+ }
1012
+ if (!slabAxis(m, pz, dz, z0, z1, 2)) return false;
1013
+ if (m.tMin >= 1 || m.ax < 0) return false;
1014
+ if (m.ax === 3) {
1015
+ const hx = ex + dx * m.tMin;
1016
+ const hy = ey + dy * m.tMin;
1017
+ const len = Math.sqrt(hx * hx + hy * hy);
1018
+ m.nx = hx / len;
1019
+ m.ny = hy / len;
1020
+ m.nz = 0;
1021
+ } else {
1022
+ m.nx = 0;
1023
+ m.ny = 0;
1024
+ m.nz = m.sg;
1025
+ }
1026
+ return true;
1027
+ }
1028
+ var CROSS = { minX: new Float64Array(2), minY: new Float64Array(2), minZ: new Float64Array(2), maxX: new Float64Array(2), maxY: new Float64Array(2), maxZ: new Float64Array(2) };
1029
+ function discSweep(m, px, py, pz, dx, dy, dz, j, i, height) {
1030
+ const p = m.p;
1031
+ const r = m.r;
1032
+ const R = p.radius[j];
1033
+ const cx = p.cx[j];
1034
+ const cy = p.cy[j];
1035
+ const z0 = p.minZ[j] - height;
1036
+ const z1 = p.maxZ[j];
1037
+ m.depth = 0;
1038
+ const side = discDepth(m, j, px, py);
1039
+ const lz = pz - z0;
1040
+ const hz = z1 - pz;
1041
+ if (side > m.skin && lz > m.skin && hz > m.skin) {
1042
+ m.tMin = 0;
1043
+ if (hz < side && !covered(m, px, py, pz + hz, px, py, pz, i, height)) [m.nx, m.ny, m.nz, m.depth] = [0, 0, 1, hz];
1044
+ else [m.nx, m.ny, m.nz, m.depth] = [PLANE[0], PLANE[1], 0, side];
1045
+ return true;
1046
+ }
1047
+ let found = false;
1048
+ let bt = 0;
1049
+ let bx = 0;
1050
+ let by = 0;
1051
+ let bz = 0;
1052
+ CROSS.minX[0] = cx - R;
1053
+ CROSS.maxX[0] = cx + R;
1054
+ CROSS.minY[0] = cy;
1055
+ CROSS.maxY[0] = cy;
1056
+ CROSS.minX[1] = cx;
1057
+ CROSS.maxX[1] = cx;
1058
+ CROSS.minY[1] = cy - R;
1059
+ CROSS.maxY[1] = cy + R;
1060
+ for (let k = 0; k < 2; k++) {
1061
+ CROSS.minZ[k] = p.minZ[j];
1062
+ CROSS.maxZ[k] = z1;
1063
+ if (slab(m, px, py, pz, dx, dy, dz, k, height, CROSS, i) && (!found || m.tMin < bt)) {
1064
+ found = true;
1065
+ bt = m.tMin;
1066
+ bx = m.nx;
1067
+ by = m.ny;
1068
+ bz = m.nz;
1069
+ }
1070
+ }
1071
+ for (let k = 0; k < 4; k++) {
1072
+ const ox = k % 2 === 0 ? cx - r : cx + r;
1073
+ const oy = k < 2 ? cy - r : cy + r;
1074
+ if (cylinder(m, px, py, pz, dx, dy, dz, ox, oy, R, z0, z1) && (!found || m.tMin < bt)) {
1075
+ found = true;
1076
+ bt = m.tMin;
1077
+ bx = m.nx;
1078
+ by = m.ny;
1079
+ bz = m.nz;
1080
+ }
1081
+ }
1082
+ if (!found) return false;
1083
+ m.tMin = bt;
1084
+ m.nx = bx;
1085
+ m.ny = by;
1086
+ m.nz = bz;
1087
+ m.depth = 0;
1088
+ return true;
1089
+ }
1090
+ function posedSweep(m, px, py, pz, dx, dy, dz, i, height) {
1091
+ const j = i - m.w.count;
1092
+ const kind = m.p.kind[j];
1093
+ if (kind === PosedKind.Box) return slab(m, px, py, pz, dx, dy, dz, j, height, m.p, i);
1094
+ if (kind === PosedKind.OBox) return oboxSweep(m, px, py, pz, dx, dy, dz, j, i, height);
1095
+ return discSweep(m, px, py, pz, dx, dy, dz, j, i, height);
1096
+ }
1097
+ function sweep(m, px, py, pz, dx, dy, dz) {
1098
+ const w = m.w;
1099
+ const height = hullAt(m, m.duck);
1100
+ const ex = px + dx;
1101
+ const ey = py + dy;
1102
+ const ez = pz + dz;
1103
+ query(m, px < ex ? px : ex, py < ey ? py : ey, pz < ez ? pz : ez, px < ex ? ex : px, py < ey ? ey : py, (pz < ez ? ez : pz) + height);
1104
+ const n = visits(m);
1105
+ let found = false;
1106
+ let bestT = 0;
1107
+ let bestI = 0;
1108
+ let bnx = 0;
1109
+ let bny = 0;
1110
+ let bnz = 0;
1111
+ let bd = 0;
1112
+ for (let k = 0; k < n; k++) {
1113
+ const i = solidAt(m, k);
1114
+ const hit = i < w.boxCount ? slab(m, px, py, pz, dx, dy, dz, i, height) : i < w.count ? wedge(m, px, py, pz, dx, dy, dz, i, height) : posedSweep(m, px, py, pz, dx, dy, dz, i, height);
1115
+ if (hit && (!found || m.tMin < bestT || m.tMin === bestT && i < bestI)) {
1116
+ found = true;
1117
+ bestT = m.tMin;
1118
+ bestI = i;
1119
+ bnx = m.nx;
1120
+ bny = m.ny;
1121
+ bnz = m.nz;
1122
+ bd = m.depth;
1123
+ }
1124
+ }
1125
+ m.hit = found;
1126
+ if (found) {
1127
+ m.tMin = Math.max(0, bestT - 1e-4);
1128
+ m.nx = bnx;
1129
+ m.ny = bny;
1130
+ m.nz = bnz;
1131
+ m.depth = bd;
1132
+ } else {
1133
+ m.tMin = 1;
1134
+ m.depth = 0;
1135
+ }
1136
+ }
1137
+ function slideMove(m, T, car = false) {
1138
+ let remaining = T;
1139
+ let hasPrev = false;
1140
+ let qx = 0;
1141
+ let qy = 0;
1142
+ let qz = 0;
1143
+ for (let bump = 0; bump < 4 && remaining > 1e-6; bump++) {
1144
+ if (m.vx * m.vx + m.vy * m.vy + m.vz * m.vz < 1e-8) return;
1145
+ sweep(m, m.x, m.y, m.z, m.vx * remaining, m.vy * remaining, m.vz * remaining);
1146
+ const t = m.tMin;
1147
+ if (t > 0) {
1148
+ const s = t * remaining;
1149
+ m.x = m.x + m.vx * s;
1150
+ m.y = m.y + m.vy * s;
1151
+ m.z = m.z + m.vz * s;
1152
+ remaining = remaining - s;
1153
+ }
1154
+ if (!m.hit) return;
1155
+ if (m.depth > 0) {
1156
+ m.x = m.x + m.nx * m.depth;
1157
+ m.y = m.y + m.ny * m.depth;
1158
+ m.z = m.z + m.nz * m.depth;
1159
+ }
1160
+ let nx = m.nx;
1161
+ let ny = m.ny;
1162
+ let nz = m.nz;
1163
+ let lift = false;
1164
+ if (car && nz > 0) {
1165
+ if (nz >= m.standable) lift = true;
1166
+ else {
1167
+ const k = 1 / Math.sqrt(nx * nx + ny * ny);
1168
+ nx = nx * k;
1169
+ ny = ny * k;
1170
+ nz = 0;
1171
+ }
1172
+ }
1173
+ m.x = m.x + nx * m.skin;
1174
+ m.y = m.y + ny * m.skin;
1175
+ m.z = m.z + nz * m.skin;
1176
+ const b = m.vx * nx + m.vy * ny + m.vz * nz;
1177
+ if (b < 0) {
1178
+ if (lift) m.vz = m.vz - b / nz;
1179
+ else {
1180
+ m.vx = m.vx + nx * -b;
1181
+ m.vy = m.vy + ny * -b;
1182
+ m.vz = m.vz + nz * -b;
1183
+ }
1184
+ }
1185
+ if (hasPrev) {
1186
+ let cx = qy * nz - qz * ny;
1187
+ let cy = qz * nx - qx * nz;
1188
+ let cz = qx * ny - qy * nx;
1189
+ const L = cx * cx + cy * cy + cz * cz;
1190
+ if (L > 1e-6) {
1191
+ const k = 1 / Math.sqrt(L);
1192
+ cx = cx * k;
1193
+ cy = cy * k;
1194
+ cz = cz * k;
1195
+ const d = m.vx * cx + m.vy * cy + m.vz * cz;
1196
+ m.vx = cx * d;
1197
+ m.vy = cy * d;
1198
+ m.vz = cz * d;
1199
+ }
1200
+ }
1201
+ hasPrev = true;
1202
+ qx = nx;
1203
+ qy = ny;
1204
+ qz = nz;
1205
+ if (m.vx * m.vx + m.vy * m.vy + m.vz * m.vz < 1e-8) return;
1206
+ }
1207
+ }
1208
+ function groundMove(m, car = false) {
1209
+ const x0 = m.x, y0 = m.y, z0 = m.z;
1210
+ const vx0 = m.vx, vy0 = m.vy, vz0 = m.vz;
1211
+ slideMove(m, m.h, car);
1212
+ const x1 = m.x, y1 = m.y, z1 = m.z;
1213
+ const vx1 = m.vx, vy1 = m.vy, vz1 = m.vz;
1214
+ const ax = x1 - x0;
1215
+ const ay = y1 - y0;
1216
+ const flat = ax * ax + ay * ay;
1217
+ m.x = x0;
1218
+ m.y = y0;
1219
+ m.z = z0;
1220
+ m.vx = vx0;
1221
+ m.vy = vy0;
1222
+ m.vz = vz0;
1223
+ const lift = m.stepHeight + m.skin;
1224
+ sweep(m, x0, y0, z0, 0, 0, lift);
1225
+ const t = m.tMin;
1226
+ const zl = z0 + lift * t;
1227
+ if (t > 0 && fits(m, x0, y0, zl, hullAt(m, m.duck))) {
1228
+ m.z = zl;
1229
+ slideMove(m, m.h, car);
1230
+ const g = traceDown(m, m.x, m.y, m.z, m.stepHeight * 2, 1e-3);
1231
+ if (g === g) m.z = g;
1232
+ const bx = m.x - x0;
1233
+ const by = m.y - y0;
1234
+ const stepped = bx * bx + by * by;
1235
+ if (g === g && stepped > flat + 1e-3) {
1236
+ m.vz = 0;
1237
+ return;
1238
+ }
1239
+ }
1240
+ m.x = x1;
1241
+ m.y = y1;
1242
+ m.z = z1;
1243
+ m.vx = vx1;
1244
+ m.vy = vy1;
1245
+ m.vz = vz1;
1246
+ }
1247
+ function stayOnGround(m) {
1248
+ const g = traceDown(m, m.x, m.y, m.z, m.stepHeight, m.groundSnap);
1249
+ if (g === g) {
1250
+ m.z = g;
1251
+ m.vz = 0;
1252
+ }
1253
+ }
1254
+ function applyFriction(m) {
1255
+ const speed = Math.sqrt(m.vx * m.vx + m.vy * m.vy);
1256
+ if (speed < 0.1) {
1257
+ m.vx = 0;
1258
+ m.vy = 0;
1259
+ return;
1260
+ }
1261
+ const control = speed < m.stopSpeed ? m.stopSpeed : speed;
1262
+ const drop = control * m.friction * m.h;
1263
+ const k = Math.max(0, speed - drop) / speed;
1264
+ m.vx = m.vx * k;
1265
+ m.vy = m.vy * k;
1266
+ }
1267
+ function groundAccelerate(m) {
1268
+ const add = m.wishSpeed - (m.vx * m.dirX + m.vy * m.dirY);
1269
+ if (add <= 0) return;
1270
+ let a = m.accelerate * m.h * Math.max(m.wishSpeed, m.maxSpeed);
1271
+ if (a > add) a = add;
1272
+ m.vx = m.vx + a * m.dirX;
1273
+ m.vy = m.vy + a * m.dirY;
1274
+ }
1275
+ function airAccelerate(m) {
1276
+ const add = Math.min(m.wishSpeed, m.airMaxWishSpeed) - (m.vx * m.dirX + m.vy * m.dirY);
1277
+ if (add <= 0) return;
1278
+ let a = m.airAccelerate * m.wishSpeed * m.h;
1279
+ if (a > add) a = add;
1280
+ m.vx = m.vx + a * m.dirX;
1281
+ m.vy = m.vy + a * m.dirY;
1282
+ }
1283
+ var stored = (v) => unsignedZero(Math.fround(clamp(v, -MAX_ABS, MAX_ABS)));
1284
+ function subStep(m, duckDown, walk) {
1285
+ const rate = m.h / m.duckTime;
1286
+ if (duckDown) {
1287
+ const next = Math.min(1, m.duck + rate);
1288
+ if (!m.onGround) m.z = m.z + (hullAt(m, m.duck) - hullAt(m, next));
1289
+ m.duck = next;
1290
+ m.ducking = true;
1291
+ } else if (m.duck > 0) {
1292
+ const next = Math.max(0, m.duck - rate);
1293
+ const zn = m.onGround ? m.z : m.z - (hullAt(m, next) - hullAt(m, m.duck));
1294
+ if (fits(m, m.x, m.y, zn, hullAt(m, next))) {
1295
+ m.duck = next;
1296
+ m.z = zn;
1297
+ if (m.duck === 0) m.ducking = false;
1298
+ }
1299
+ }
1300
+ const was = m.onGround;
1301
+ checkGround(m);
1302
+ if (m.onGround && !was) m.vz = 0;
1303
+ m.base = m.maxSpeed * m.cap;
1304
+ const target = m.ducking ? m.base * m.crouchSpeed : walk ? m.base * m.walkSpeed : m.base;
1305
+ m.wishSpeed = m.amount * target;
1306
+ if (m.onGround) {
1307
+ if (m.pressed) {
1308
+ m.vz = m.jumpImpulse;
1309
+ m.onGround = false;
1310
+ m.pressed = false;
1311
+ airAccelerate(m);
1312
+ } else {
1313
+ applyFriction(m);
1314
+ groundAccelerate(m);
1315
+ m.vz = 0;
1316
+ }
1317
+ } else airAccelerate(m);
1318
+ const falling = !m.onGround;
1319
+ if (falling) m.vz = m.vz - m.gravity * m.h * 0.5;
1320
+ m.vx = clamp(m.vx, -m.maxVelocity, m.maxVelocity);
1321
+ m.vy = clamp(m.vy, -m.maxVelocity, m.maxVelocity);
1322
+ m.vz = clamp(m.vz, -m.maxVelocity, m.maxVelocity);
1323
+ if (m.onGround) {
1324
+ groundMove(m);
1325
+ stayOnGround(m);
1326
+ } else slideMove(m, m.h);
1327
+ checkGround(m);
1328
+ if (falling && !m.onGround) m.vz = m.vz - m.gravity * m.h * 0.5;
1329
+ else if (m.onGround) m.vz = 0;
1330
+ m.x = stored(m.x);
1331
+ m.y = stored(m.y);
1332
+ m.z = stored(m.z);
1333
+ m.vx = stored(m.vx);
1334
+ m.vy = stored(m.vy);
1335
+ m.vz = stored(m.vz);
1336
+ m.duck = unsignedZero(Math.fround(m.duck));
1337
+ }
1338
+ var bodyValue = (v, fallback, lo, hi) => unsignedZero(Math.fround(clamp(typeof v === "number" && Number.isFinite(v) ? v : fallback, lo, hi)));
1339
+ var bodyAngle = (v) => unsignedZero(Math.fround(typeof v === "number" && Number.isFinite(v) ? v : 0));
1340
+ var scratch = { cand: new Int32Array(0), stack: new Int32Array(TREE_FANOUT * 16) };
1341
+ function scratchFor(w) {
1342
+ if (scratch.cand.length < w.count) scratch = { cand: new Int32Array(w.count), stack: scratch.stack };
1343
+ return scratch;
1344
+ }
1345
+ function hullOps(preset, params, world = EMPTY_WORLD) {
1346
+ const w = compiledOf(world);
1347
+ const f = Math.fround;
1348
+ const height = (k) => f(preset === "vehicle" ? params.height : params[k]);
1349
+ const hullOf = (body, solids) => {
1350
+ const room = scratchFor(w);
1351
+ return {
1352
+ w,
1353
+ p: posedWorld(solids),
1354
+ r: f(params.radius),
1355
+ standHeight: height("standHeight"),
1356
+ crouchHeight: height("crouchHeight"),
1357
+ stepHeight: f(params.stepHeight),
1358
+ standable: f(params.standable),
1359
+ groundTrace: f(params.groundTrace),
1360
+ groundSnap: f(params.groundSnap),
1361
+ skin: f(params.skin),
1362
+ x: body.x,
1363
+ y: body.y,
1364
+ z: body.z,
1365
+ vx: 0,
1366
+ vy: 0,
1367
+ vz: body.vz ?? 0,
1368
+ duck: preset === "vehicle" ? 0 : body.duck ?? 0,
1369
+ onGround: false,
1370
+ h: 0,
1371
+ tMin: 0,
1372
+ tMax: 0,
1373
+ ax: -1,
1374
+ sg: 0,
1375
+ nx: 0,
1376
+ ny: 0,
1377
+ nz: 0,
1378
+ hit: false,
1379
+ depth: 0,
1380
+ cand: room.cand,
1381
+ nCand: -1,
1382
+ pc: POSED_SCRATCH,
1383
+ nPosed: 0,
1384
+ stack: room.stack
1385
+ };
1386
+ };
1387
+ return {
1388
+ support(body, solids) {
1389
+ const m = hullOf(body, solids);
1390
+ if (m.vz > 0.1) return -1;
1391
+ const g = traceDown(m, m.x, m.y, m.z, m.groundTrace, m.groundSnap);
1392
+ if (g !== g) return -1;
1393
+ for (let j = 0; j < m.p.count; j++) if (m.p.maxZ[j] === g && posedOverlaps(m, j, m.x, m.y)) return j;
1394
+ return -1;
1395
+ },
1396
+ inside(body, solids, k) {
1397
+ const m = hullOf(body, solids);
1398
+ return insideSolid(m, m.x, m.y, m.z, w.count + k, hullAt(m, m.duck));
1399
+ },
1400
+ pushOut(body, solids) {
1401
+ const m = hullOf(body, solids);
1402
+ for (let k = 0; k < 4; k++) {
1403
+ sweep(m, m.x, m.y, m.z, 0, 0, 0);
1404
+ if (!m.hit) break;
1405
+ m.x = m.x + m.nx * m.depth;
1406
+ m.y = m.y + m.ny * m.depth;
1407
+ m.z = m.z + m.nz * m.depth;
1408
+ m.x = m.x + m.nx * m.skin;
1409
+ m.y = m.y + m.ny * m.skin;
1410
+ m.z = m.z + m.nz * m.skin;
1411
+ }
1412
+ const height2 = hullAt(m, m.duck);
1413
+ let inside = false;
1414
+ for (let i = 0; i < w.count + m.p.count && !inside; i++) inside = insideSolid(m, m.x, m.y, m.z, i, height2);
1415
+ return { x: m.x, y: m.y, z: m.z, inside };
1416
+ }
1417
+ };
1418
+ }
1419
+ function stepFirstperson(body, input, dt, params, world = EMPTY_WORLD, posed = NO_POSED) {
1420
+ const w = compiledOf(world);
1421
+ const room = scratchFor(w);
1422
+ const f = Math.fround;
1423
+ const mstate = mask(body.mstate) & MSTATE_BITS;
1424
+ const m = {
1425
+ w,
1426
+ p: posed,
1427
+ maxSpeed: f(params.maxSpeed),
1428
+ walkSpeed: f(params.walkSpeed),
1429
+ crouchSpeed: f(params.crouchSpeed),
1430
+ accelerate: f(params.accelerate),
1431
+ airAccelerate: f(params.airAccelerate),
1432
+ airMaxWishSpeed: f(params.airMaxWishSpeed),
1433
+ friction: f(params.friction),
1434
+ stopSpeed: f(params.stopSpeed),
1435
+ gravity: f(params.gravity),
1436
+ jumpImpulse: f(params.jumpImpulse),
1437
+ maxVelocity: f(params.maxVelocity),
1438
+ r: f(params.radius),
1439
+ standHeight: f(params.standHeight),
1440
+ crouchHeight: f(params.crouchHeight),
1441
+ duckTime: f(params.duckTime),
1442
+ stepHeight: f(params.stepHeight),
1443
+ standable: f(params.standable),
1444
+ groundTrace: f(params.groundTrace),
1445
+ groundSnap: f(params.groundSnap),
1446
+ skin: f(params.skin),
1447
+ x: bodyValue(body.x, 0, -MAX_ABS, MAX_ABS),
1448
+ y: bodyValue(body.y, 0, -MAX_ABS, MAX_ABS),
1449
+ z: bodyValue(body.z, 0, -MAX_ABS, MAX_ABS),
1450
+ vx: bodyValue(body.vx, 0, -MAX_ABS, MAX_ABS),
1451
+ vy: bodyValue(body.vy, 0, -MAX_ABS, MAX_ABS),
1452
+ vz: bodyValue(body.vz, 0, -MAX_ABS, MAX_ABS),
1453
+ duck: bodyValue(body.duck, 0, 0, 1),
1454
+ cap: bodyValue(body.cap, 1, 0, 4),
1455
+ onGround: (mstate & MoveState.ON_GROUND) !== 0,
1456
+ ducking: (mstate & MoveState.DUCKING) !== 0,
1457
+ jumpHeld: (mstate & MoveState.JUMP_HELD) !== 0,
1458
+ pressed: false,
1459
+ h: 0,
1460
+ dirX: 0,
1461
+ dirY: 0,
1462
+ amount: 0,
1463
+ base: 0,
1464
+ wishSpeed: 0,
1465
+ tMin: 0,
1466
+ tMax: 0,
1467
+ ax: -1,
1468
+ sg: 0,
1469
+ nx: 0,
1470
+ ny: 0,
1471
+ nz: 0,
1472
+ hit: false,
1473
+ depth: 0,
1474
+ cand: room.cand,
1475
+ nCand: -1,
1476
+ pc: POSED_SCRATCH,
1477
+ nPosed: 0,
1478
+ stack: room.stack
1479
+ };
1480
+ const mx = axis(input.moveX);
1481
+ const my = axis(input.moveY);
1482
+ const len = Math.sqrt(mx * mx + my * my);
1483
+ if (len > 1e-4) {
1484
+ m.dirX = mx / len;
1485
+ m.dirY = my / len;
1486
+ m.amount = Math.min(len, 1);
1487
+ }
1488
+ const buttons = mask(input.buttons);
1489
+ const jump = (buttons & params.jumpBit) !== 0;
1490
+ const duckDown = (buttons & params.duckBit) !== 0;
1491
+ const walk = (buttons & params.walkBit) !== 0;
1492
+ const step = stepSeconds(dt, PRESET_MAX_DT_S);
1493
+ m.h = step / params.substeps;
1494
+ const yaw = wrappedAngle(input.yaw) ?? bodyAngle(body.yaw);
1495
+ const pitchLimit = f(params.pitchLimit);
1496
+ const pitch = unsignedZero(clamp(wrappedAngle(input.pitch) ?? bodyAngle(body.pitch), -pitchLimit, pitchLimit));
1497
+ if (step > 0) {
1498
+ m.pressed = jump && !m.jumpHeld;
1499
+ m.jumpHeld = jump;
1500
+ for (let i = 0; i < params.substeps; i++) subStep(m, duckDown, walk);
1501
+ }
1502
+ return {
1503
+ x: m.x,
1504
+ y: m.y,
1505
+ z: m.z,
1506
+ vx: m.vx,
1507
+ vy: m.vy,
1508
+ vz: m.vz,
1509
+ yaw,
1510
+ pitch,
1511
+ duck: m.duck,
1512
+ mstate: (m.onGround ? MoveState.ON_GROUND : 0) + (m.ducking ? MoveState.DUCKING : 0) + (m.jumpHeld ? MoveState.JUMP_HELD : 0)
1513
+ };
1514
+ }
1515
+ var VEHICLE_DEFAULTS = Object.freeze({
1516
+ maxSpeed: 1410,
1517
+ accel: 1e3,
1518
+ brake: 3500,
1519
+ coast: 525,
1520
+ boostAccel: 1e3,
1521
+ boostMaxSpeed: 2300,
1522
+ boostBurn: 0.3333333432674408,
1523
+ grip: 6e3,
1524
+ driftGrip: 1500,
1525
+ turnRate: 2.2,
1526
+ turnSpeed: 400,
1527
+ spinAccel: 30,
1528
+ airTurnRate: 5.5,
1529
+ airSpinAccel: 9,
1530
+ gravity: 650,
1531
+ jumpImpulse: 450,
1532
+ maxVelocity: 6e3,
1533
+ radius: 50,
1534
+ height: 36,
1535
+ stepHeight: 8,
1536
+ standable: 0.7,
1537
+ groundTrace: 4,
1538
+ groundSnap: 2,
1539
+ skin: 0.03,
1540
+ jumpBit: 1,
1541
+ boostBit: 2,
1542
+ driftBit: 4,
1543
+ substeps: 2,
1544
+ world: EMPTY_WORLD_HASH
1545
+ });
1546
+ var VEHICLE_RANGE = {
1547
+ maxSpeed: inRange(0, 1e5),
1548
+ accel: inRange(0, 1e5),
1549
+ brake: inRange(0, 1e5),
1550
+ coast: inRange(0, 1e5),
1551
+ boostAccel: inRange(0, 1e5),
1552
+ boostMaxSpeed: inRange(0, 1e5),
1553
+ boostBurn: inRange(0, 1e3),
1554
+ grip: inRange(0, 1e5),
1555
+ driftGrip: inRange(0, 1e5),
1556
+ turnRate: inRange(0, 100),
1557
+ turnSpeed: aboveZero(1e5),
1558
+ spinAccel: inRange(0, 1e4),
1559
+ airTurnRate: inRange(0, 100),
1560
+ airSpinAccel: inRange(0, 1e4),
1561
+ gravity: inRange(0, 1e5),
1562
+ jumpImpulse: inRange(0, 1e5),
1563
+ maxVelocity: inRange(0, 1e5),
1564
+ radius: aboveZero(1e4),
1565
+ height: aboveZero(1e4),
1566
+ stepHeight: inRange(0, 1e4),
1567
+ standable: inRange(0, 1),
1568
+ groundTrace: inRange(0, 1e4),
1569
+ groundSnap: inRange(0, 1e4),
1570
+ skin: inRange(0, 100),
1571
+ jumpBit: integerIn(0, MASK_LIMIT - 1),
1572
+ boostBit: integerIn(0, MASK_LIMIT - 1),
1573
+ driftBit: integerIn(0, MASK_LIMIT - 1),
1574
+ substeps: integerIn(1, 8),
1575
+ world: integerIn(0, 2 ** 32 - 1)
1576
+ };
1577
+ function resolveVehicleParams(params) {
1578
+ return resolvePresetParams(Preset.VEHICLE, params);
1579
+ }
1580
+ var VEHICLE_MSTATE_BITS = MoveState.ON_GROUND | MoveState.JUMP_HELD;
1581
+ var toward = (v, target, d) => v < target ? Math.min(target, v + d) : v > target ? Math.max(target, v - d) : v;
1582
+ var wrap = (v) => wrappedAngle(v) ?? 0;
1583
+ function drive(m) {
1584
+ const top = m.maxSpeed * m.cap;
1585
+ const t = m.boosting ? 1 : m.throttle;
1586
+ const target = t * top;
1587
+ let f = m.f;
1588
+ if (t === 0) f = toward(f, 0, m.coast * m.h);
1589
+ else if (t > 0 && f < 0 || t < 0 && f > 0) f = toward(f, 0, m.brake * m.h);
1590
+ else if (Math.abs(f) < Math.abs(target)) f = toward(f, target, m.accel * m.h);
1591
+ else if (!m.boosting) f = toward(f, target, m.coast * m.h);
1592
+ if (m.boosting) {
1593
+ const boostTop = m.boostMaxSpeed * m.cap;
1594
+ if (f < boostTop) f = Math.min(boostTop, f + m.boostAccel * m.h);
1595
+ }
1596
+ const l = toward(m.l, 0, (m.drift ? m.driftGrip : m.grip) * m.h);
1597
+ let want = m.steer * m.turnRate * Math.min(1, Math.abs(f) / m.turnSpeed);
1598
+ if (f < 0) want = -want;
1599
+ m.spin = toward(m.spin, want, m.spinAccel * m.h);
1600
+ m.vx = f * m.c - l * m.s;
1601
+ m.vy = f * m.s + l * m.c;
1602
+ m.vz = 0;
1603
+ m.f = f;
1604
+ m.l = l;
1605
+ }
1606
+ function air(m) {
1607
+ m.spin = toward(m.spin, m.steer * m.airTurnRate, m.airSpinAccel * m.h);
1608
+ if (m.boosting) {
1609
+ const boostTop = m.boostMaxSpeed * m.cap;
1610
+ if (m.f < boostTop) {
1611
+ const a = Math.min(m.boostAccel * m.h, boostTop - m.f);
1612
+ m.vx = m.vx + a * m.c;
1613
+ m.vy = m.vy + a * m.s;
1614
+ }
1615
+ }
1616
+ }
1617
+ function carSubStep(m) {
1618
+ const was = m.onGround;
1619
+ checkGround(m);
1620
+ if (m.onGround && !was) m.vz = 0;
1621
+ m.c = pcos(m.yaw);
1622
+ m.s = psin(m.yaw);
1623
+ m.f = m.vx * m.c + m.vy * m.s;
1624
+ m.l = m.vy * m.c - m.vx * m.s;
1625
+ m.boosting = m.boost && m.fuel > 0;
1626
+ if (m.boosting) m.fuel = Math.max(0, m.fuel - m.boostBurn * m.h);
1627
+ if (m.onGround && m.pressed) {
1628
+ m.vz = m.jumpImpulse;
1629
+ m.onGround = false;
1630
+ m.pressed = false;
1631
+ }
1632
+ if (m.onGround) drive(m);
1633
+ else air(m);
1634
+ const falling = !m.onGround;
1635
+ if (falling) m.vz -= m.gravity * m.h * 0.5;
1636
+ m.vx = clamp(m.vx, -m.maxVelocity, m.maxVelocity);
1637
+ m.vy = clamp(m.vy, -m.maxVelocity, m.maxVelocity);
1638
+ m.vz = clamp(m.vz, -m.maxVelocity, m.maxVelocity);
1639
+ m.yaw = wrap(m.yaw + m.spin * m.h);
1640
+ if (m.onGround) {
1641
+ groundMove(m, true);
1642
+ stayOnGround(m);
1643
+ } else slideMove(m, m.h);
1644
+ checkGround(m);
1645
+ if (falling && !m.onGround) m.vz -= m.gravity * m.h * 0.5;
1646
+ else if (m.onGround) m.vz = 0;
1647
+ m.x = stored(m.x);
1648
+ m.y = stored(m.y);
1649
+ m.z = stored(m.z);
1650
+ m.vx = stored(m.vx);
1651
+ m.vy = stored(m.vy);
1652
+ m.vz = stored(m.vz);
1653
+ m.spin = unsignedZero(Math.fround(m.spin));
1654
+ m.fuel = unsignedZero(Math.fround(m.fuel));
1655
+ }
1656
+ function stepVehicle(body, input, dt, params, world = EMPTY_WORLD, posed = NO_POSED) {
1657
+ const w = compiledOf(world);
1658
+ const room = scratchFor(w);
1659
+ const f = Math.fround;
1660
+ const mstate = mask(body.mstate) & VEHICLE_MSTATE_BITS;
1661
+ const height = f(params.height);
1662
+ const steer = unsignedZero(-axis(input.moveX));
1663
+ const throttle = axis(input.moveY);
1664
+ const buttons = mask(input.buttons);
1665
+ const jump = (buttons & params.jumpBit) !== 0;
1666
+ const step = stepSeconds(dt, PRESET_MAX_DT_S);
1667
+ const readYaw = bodyAngle(body.yaw);
1668
+ const m = {
1669
+ w,
1670
+ p: posed,
1671
+ r: f(params.radius),
1672
+ standHeight: height,
1673
+ crouchHeight: height,
1674
+ stepHeight: f(params.stepHeight),
1675
+ standable: f(params.standable),
1676
+ groundTrace: f(params.groundTrace),
1677
+ groundSnap: f(params.groundSnap),
1678
+ skin: f(params.skin),
1679
+ maxSpeed: f(params.maxSpeed),
1680
+ accel: f(params.accel),
1681
+ brake: f(params.brake),
1682
+ coast: f(params.coast),
1683
+ boostAccel: f(params.boostAccel),
1684
+ boostMaxSpeed: f(params.boostMaxSpeed),
1685
+ boostBurn: f(params.boostBurn),
1686
+ grip: f(params.grip),
1687
+ driftGrip: f(params.driftGrip),
1688
+ turnRate: f(params.turnRate),
1689
+ turnSpeed: f(params.turnSpeed),
1690
+ spinAccel: f(params.spinAccel),
1691
+ airTurnRate: f(params.airTurnRate),
1692
+ airSpinAccel: f(params.airSpinAccel),
1693
+ gravity: f(params.gravity),
1694
+ jumpImpulse: f(params.jumpImpulse),
1695
+ maxVelocity: f(params.maxVelocity),
1696
+ x: bodyValue(body.x, 0, -MAX_ABS, MAX_ABS),
1697
+ y: bodyValue(body.y, 0, -MAX_ABS, MAX_ABS),
1698
+ z: bodyValue(body.z, 0, -MAX_ABS, MAX_ABS),
1699
+ vx: bodyValue(body.vx, 0, -MAX_ABS, MAX_ABS),
1700
+ vy: bodyValue(body.vy, 0, -MAX_ABS, MAX_ABS),
1701
+ vz: bodyValue(body.vz, 0, -MAX_ABS, MAX_ABS),
1702
+ yaw: Math.abs(readYaw) <= MAX_ABS ? wrap(readYaw) : 0,
1703
+ spin: bodyValue(body.spin, 0, -1e3, 1e3),
1704
+ fuel: bodyValue(body.fuel, 0, 0, 1),
1705
+ cap: bodyValue(body.cap, 1, 0, 4),
1706
+ duck: 0,
1707
+ onGround: (mstate & MoveState.ON_GROUND) !== 0,
1708
+ jumpHeld: (mstate & MoveState.JUMP_HELD) !== 0,
1709
+ pressed: false,
1710
+ boosting: false,
1711
+ steer,
1712
+ throttle,
1713
+ boost: (buttons & params.boostBit) !== 0,
1714
+ drift: (buttons & params.driftBit) !== 0,
1715
+ h: step / params.substeps,
1716
+ c: 1,
1717
+ s: 0,
1718
+ f: 0,
1719
+ l: 0,
1720
+ tMin: 0,
1721
+ tMax: 0,
1722
+ ax: -1,
1723
+ sg: 0,
1724
+ nx: 0,
1725
+ ny: 0,
1726
+ nz: 0,
1727
+ hit: false,
1728
+ depth: 0,
1729
+ cand: room.cand,
1730
+ nCand: -1,
1731
+ pc: POSED_SCRATCH,
1732
+ nPosed: 0,
1733
+ stack: room.stack
1734
+ };
1735
+ if (step > 0) {
1736
+ m.pressed = jump && !m.jumpHeld;
1737
+ m.jumpHeld = jump;
1738
+ for (let i = 0; i < params.substeps; i++) carSubStep(m);
1739
+ }
1740
+ return {
1741
+ x: m.x,
1742
+ y: m.y,
1743
+ z: m.z,
1744
+ vx: m.vx,
1745
+ vy: m.vy,
1746
+ vz: m.vz,
1747
+ yaw: m.yaw,
1748
+ spin: m.spin,
1749
+ fuel: m.fuel,
1750
+ mstate: (m.onGround ? MoveState.ON_GROUND : 0) + (m.jumpHeld ? MoveState.JUMP_HELD : 0)
1751
+ };
1752
+ }
1753
+ var TOPDOWN_PRESET = {
1754
+ defaults: TOPDOWN_DEFAULTS,
1755
+ ranges: TOPDOWN_RANGE,
1756
+ fields: ["x", "y", "yaw"],
1757
+ reads: [],
1758
+ world: false,
1759
+ step: (body, input, dt, params) => stepTopdown(body, input, dt, params)
1760
+ };
1761
+ var FIRSTPERSON_PRESET = {
1762
+ defaults: FIRSTPERSON_DEFAULTS,
1763
+ ranges: FIRSTPERSON_RANGE,
1764
+ fields: ["x", "y", "z", "vx", "vy", "vz", "yaw", "pitch", "duck", "mstate"],
1765
+ reads: ["cap"],
1766
+ world: true,
1767
+ step: (body, input, dt, params, world, posed) => stepFirstperson(body, input, dt, params, world, posed)
1768
+ };
1769
+ var VEHICLE_PRESET = {
1770
+ defaults: VEHICLE_DEFAULTS,
1771
+ ranges: VEHICLE_RANGE,
1772
+ fields: ["x", "y", "z", "vx", "vy", "vz", "yaw", "spin", "fuel", "mstate"],
1773
+ reads: ["cap"],
1774
+ world: true,
1775
+ step: (body, input, dt, params, world, posed) => stepVehicle(body, input, dt, params, world, posed)
1776
+ };
1777
+ var PRESETS = Object.freeze({
1778
+ [Preset.TOPDOWN]: Object.freeze(TOPDOWN_PRESET),
1779
+ [Preset.FIRSTPERSON]: Object.freeze(FIRSTPERSON_PRESET),
1780
+ [Preset.VEHICLE]: Object.freeze(VEHICLE_PRESET)
1781
+ });
1782
+ function definitionOf(name) {
1783
+ return typeof name === "string" && Object.hasOwn(PRESETS, name) ? PRESETS[name] : void 0;
1784
+ }
1785
+ function stepPreset(name, body, input, dt, params, world, posed) {
1786
+ const definition = definitionOf(name);
1787
+ if (definition === void 0) throw new RangeError(`unknown preset ${String(name)}`);
1788
+ return definition.step(body, input, dt, params, world, posed);
1789
+ }
1790
+
1791
+ // src/authority/physics.ts
1792
+ var PHYSICS_CONTRACT = "1.0";
1793
+ var PHYSICS_DEFAULT_LIMITS = Object.freeze({
1794
+ bodies: 512,
1795
+ collidersPerBody: 8,
1796
+ hullVertices: 64,
1797
+ triangles: 65536,
1798
+ joints: 64,
1799
+ ccdBodies: 16,
1800
+ queriesPerStep: 64,
1801
+ eventsPerStep: 64,
1802
+ heightfieldSamples: 1048576,
1803
+ staticPieces: 4608,
1804
+ memoryBytes: 64 * 1024 * 1024,
1805
+ worldExtent: 1e5,
1806
+ maxSpeed: 1e3,
1807
+ maxAngularSpeed: 1e3
1808
+ });
1809
+ var PHYSICS_LIMIT_NAMES = Object.freeze(Object.keys(PHYSICS_DEFAULT_LIMITS));
1810
+ var PHYSICS_SCALED_LIMITS = Object.freeze(["worldExtent", "maxSpeed"]);
1811
+ var PHYSICS_REAL_LIMITS = Object.freeze(["worldExtent", "maxSpeed", "maxAngularSpeed"]);
1812
+ var PHYSICS_CEILING_FACTOR = 4;
1813
+ var PHYSICS_DEFAULT_CEILING = PHYSICS_DEFAULT_LIMITS;
1814
+ var isPlainObject2 = (v) => {
1815
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
1816
+ const proto = Object.getPrototypeOf(v);
1817
+ return proto === Object.prototype || proto === null;
1818
+ };
1819
+ var isLimitName = (key) => Object.hasOwn(PHYSICS_DEFAULT_LIMITS, key);
1820
+ var isRealLimit = (key) => PHYSICS_REAL_LIMITS.includes(key);
1821
+ function limitValueValid(key, value2) {
1822
+ if (typeof value2 !== "number" || !Number.isFinite(value2) || value2 <= 0) return false;
1823
+ return isRealLimit(key) || Number.isInteger(value2);
1824
+ }
1825
+ function physicsCeilingValid(ceiling) {
1826
+ if (!isPlainObject2(ceiling)) return false;
1827
+ return PHYSICS_LIMIT_NAMES.every((key) => {
1828
+ const value2 = ceiling[key];
1829
+ const base = PHYSICS_DEFAULT_LIMITS[key];
1830
+ return limitValueValid(key, value2) && value2 >= base && value2 <= base * PHYSICS_CEILING_FACTOR;
1831
+ });
1832
+ }
1833
+ function physicsLimitsAt(perUnit, lengthUnit) {
1834
+ const out = { ...perUnit };
1835
+ for (const key of PHYSICS_SCALED_LIMITS) out[key] = perUnit[key] * lengthUnit;
1836
+ return out;
1837
+ }
1838
+ var PhysicsConfigRange = {
1839
+ /** `lengthUnit` is in (0, MAX_LENGTH_UNIT]. */
1840
+ MAX_LENGTH_UNIT: 1e6,
1841
+ /** `gravity` is in [0, MAX_GRAVITY]; a default is never refused. */
1842
+ MAX_GRAVITY: 1e7,
1843
+ /** Gravity without a world-stepping preset: this many metres per second squared. */
1844
+ EARTH_GRAVITY: 9.81,
1845
+ MIN_ITERATIONS: 1,
1846
+ MAX_ITERATIONS: 16,
1847
+ DEFAULT_ITERATIONS: 4,
1848
+ /** Distinct physics kinds `subscribe` may name. */
1849
+ MAX_SUBSCRIBE_KINDS: 8,
1850
+ /** `player.radius` and `player.height` are in (0, MAX_PLAYER_SIZE]. */
1851
+ MAX_PLAYER_SIZE: 1e6
1852
+ };
1853
+ var PHYSICS_PRESET_LENGTH_UNITS = Object.freeze({
1854
+ [Preset.FIRSTPERSON]: 39.37,
1855
+ [Preset.VEHICLE]: 100
1856
+ });
1857
+ var PHYSICS_PATCH_HZ = Object.freeze([10, 15, 20, 30, 60]);
1858
+ var PHYSICS_DEFAULT_PATCH_HZ = 20;
1859
+ var PhysicsPredictRange = {
1860
+ DEFAULT_REMOTE_PREDICT: RemotePredict.EXTRAPOLATE,
1861
+ DEFAULT_REMOTE_DECAY_MS: 50,
1862
+ MAX_REMOTE_DECAY_MS: 1e3,
1863
+ DEFAULT_LEAD_MS: 250,
1864
+ MIN_LEAD_MS: 50,
1865
+ MAX_LEAD_MS: 1e3
1866
+ };
1867
+ var REMOTE_PREDICTS = Object.values(RemotePredict);
1868
+ function isPhysicsPatchHz(v) {
1869
+ return typeof v === "number" && PHYSICS_PATCH_HZ.includes(v);
1870
+ }
1871
+ function effectivePatchHz(tickHz, patchHz) {
1872
+ const fits2 = PHYSICS_PATCH_HZ.filter((hz) => hz <= patchHz && hz <= tickHz && tickHz % hz === 0);
1873
+ return fits2.at(-1) ?? PHYSICS_PATCH_HZ[0];
1874
+ }
1875
+ function keyframeTicks(tickHz, patchHz) {
1876
+ return tickHz / effectivePatchHz(tickHz, patchHz);
1877
+ }
1878
+ var PHYSICS_PREDICT_MAX_LINEAR = 64;
1879
+ var PHYSICS_PREDICT_MAX_ANGULAR = 64;
1880
+ var PHYSICS_PREDICT_MIN_ITERATIONS = null;
1881
+ function solverIterationsOf(physics, predicting) {
1882
+ const floor = predicting ? PHYSICS_PREDICT_MIN_ITERATIONS : null;
1883
+ return floor === null ? physics.iterations : Math.max(physics.iterations, floor);
1884
+ }
1885
+ var PHYSICS_KEY_MAX_BYTES = 64;
1886
+ var PHYSICS_KIND_MAX_BYTES = 32;
1887
+ var PHYSICS_STATIC_PREFIX = "static:";
1888
+ var KEY_CHARSET = /^[A-Za-z0-9_.:-]+$/;
1889
+ var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1890
+ function isPhysicsKey(v) {
1891
+ if (typeof v !== "string" || v.length === 0 || v.length > PHYSICS_KEY_MAX_BYTES) return false;
1892
+ if (!KEY_CHARSET.test(v) || FORBIDDEN_KEYS.has(v)) return false;
1893
+ return !v.startsWith(PHYSICS_STATIC_PREFIX);
1894
+ }
1895
+ function isPhysicsKind(v) {
1896
+ return typeof v === "string" && v.length > 0 && v.length <= PHYSICS_KIND_MAX_BYTES && KEY_CHARSET.test(v);
1897
+ }
1898
+ var PATH = "$.config.physics";
1899
+ var REQUEST_KEYS = /* @__PURE__ */ new Set([
1900
+ "lengthUnit",
1901
+ "gravity",
1902
+ "iterations",
1903
+ "limits",
1904
+ "subscribe",
1905
+ "player",
1906
+ "predict",
1907
+ "patchHz",
1908
+ "remotePredict",
1909
+ "remoteDecayMs",
1910
+ "leadMs"
1911
+ ]);
1912
+ var PLAYER_KEYS = /* @__PURE__ */ new Set(["radius", "height", "events"]);
1913
+ var Bad = class {
1914
+ path;
1915
+ constructor(path) {
1916
+ this.path = path;
1917
+ }
1918
+ };
1919
+ var bad = (path) => {
1920
+ throw new Bad(path);
1921
+ };
1922
+ var Mode = {
1923
+ REQUEST: "request",
1924
+ RESOLVED: "resolved"
1925
+ };
1926
+ var asked = (v) => v !== void 0;
1927
+ function lengthUnitOf(v, preset, mode) {
1928
+ if (!asked(v)) {
1929
+ if (mode === Mode.RESOLVED) return bad(`${PATH}.lengthUnit`);
1930
+ return (preset !== null ? PHYSICS_PRESET_LENGTH_UNITS[preset.name] : void 0) ?? 1;
1931
+ }
1932
+ const ok2 = typeof v === "number" && Number.isFinite(v) && v > 0 && v <= PhysicsConfigRange.MAX_LENGTH_UNIT;
1933
+ return ok2 ? v : bad(`${PATH}.lengthUnit`);
1934
+ }
1935
+ function presetGravity(preset) {
1936
+ if (preset === null || !isPresetName(preset.name)) return null;
1937
+ if (preset.name !== Preset.FIRSTPERSON && preset.name !== Preset.VEHICLE) return null;
1938
+ return resolvePresetParams(preset.name, preset.params).gravity ?? null;
1939
+ }
1940
+ function gravityOf(v, lengthUnit, preset, mode) {
1941
+ if (!asked(v)) {
1942
+ if (mode === Mode.RESOLVED) return bad(`${PATH}.gravity`);
1943
+ return presetGravity(preset) ?? PhysicsConfigRange.EARTH_GRAVITY * lengthUnit;
1944
+ }
1945
+ const ok2 = typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= PhysicsConfigRange.MAX_GRAVITY;
1946
+ return ok2 ? v : bad(`${PATH}.gravity`);
1947
+ }
1948
+ function iterationsOf(v, mode) {
1949
+ if (!asked(v)) return mode === Mode.RESOLVED ? bad(`${PATH}.iterations`) : PhysicsConfigRange.DEFAULT_ITERATIONS;
1950
+ const ok2 = typeof v === "number" && Number.isInteger(v) && v >= PhysicsConfigRange.MIN_ITERATIONS && v <= PhysicsConfigRange.MAX_ITERATIONS;
1951
+ return ok2 ? v : bad(`${PATH}.iterations`);
1952
+ }
1953
+ function limitsOf(v, lengthUnit, ceiling, mode) {
1954
+ const path = `${PATH}.limits`;
1955
+ if (!asked(v) && mode === Mode.RESOLVED) return bad(path);
1956
+ const given = asked(v) ? v : {};
1957
+ if (!isPlainObject2(given)) return bad(path);
1958
+ if (mode === Mode.REQUEST) {
1959
+ const unknown = Object.keys(given).find((key) => !isLimitName(key));
1960
+ if (unknown !== void 0) return bad(`${path}.${unknown}`);
1961
+ }
1962
+ const top = physicsLimitsAt(ceiling, lengthUnit);
1963
+ const defaults = physicsLimitsAt(PHYSICS_DEFAULT_LIMITS, lengthUnit);
1964
+ const out = { ...defaults };
1965
+ for (const key of PHYSICS_LIMIT_NAMES) {
1966
+ const value2 = given[key];
1967
+ if (!asked(value2)) {
1968
+ if (mode === Mode.RESOLVED) return bad(`${path}.${key}`);
1969
+ out[key] = Math.min(defaults[key], top[key]);
1970
+ continue;
1971
+ }
1972
+ if (!limitValueValid(key, value2)) return bad(`${path}.${key}`);
1973
+ if (mode === Mode.RESOLVED && value2 > top[key]) return bad(`${path}.${key}`);
1974
+ out[key] = Math.min(value2, top[key]);
1975
+ }
1976
+ return out;
1977
+ }
1978
+ function subscribeOf(v, mode) {
1979
+ const path = `${PATH}.subscribe`;
1980
+ if (!asked(v)) return mode === Mode.RESOLVED ? bad(path) : [];
1981
+ if (!Array.isArray(v) || v.length > PhysicsConfigRange.MAX_SUBSCRIBE_KINDS) return bad(path);
1982
+ const seen = /* @__PURE__ */ new Set();
1983
+ v.forEach((kind, i) => {
1984
+ if (!isPhysicsKind(kind) || seen.has(kind)) bad(`${path}[${i}]`);
1985
+ seen.add(kind);
1986
+ });
1987
+ return [...seen];
1988
+ }
1989
+ var playerSize = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 && v <= PhysicsConfigRange.MAX_PLAYER_SIZE;
1990
+ function playerOf(v, preset, mode) {
1991
+ const path = `${PATH}.player`;
1992
+ if (!asked(v) && mode === Mode.RESOLVED) return bad(path);
1993
+ const given = asked(v) ? v : {};
1994
+ if (!isPlainObject2(given)) return bad(path);
1995
+ if (mode === Mode.REQUEST) {
1996
+ const unknown = Object.keys(given).find((key) => !PLAYER_KEYS.has(key));
1997
+ if (unknown !== void 0) return bad(`${path}.${unknown}`);
1998
+ }
1999
+ const out = { events: false };
2000
+ if (asked(given.events)) {
2001
+ if (typeof given.events !== "boolean") return bad(`${path}.events`);
2002
+ out.events = given.events;
2003
+ } else if (mode === Mode.RESOLVED) {
2004
+ return bad(`${path}.events`);
2005
+ }
2006
+ const topdown = preset?.name === Preset.TOPDOWN;
2007
+ for (const key of ["radius", "height"]) {
2008
+ if (!asked(given[key])) continue;
2009
+ if (!topdown || !playerSize(given[key])) return bad(`${path}.${key}`);
2010
+ out[key] = given[key];
2011
+ }
2012
+ return out;
2013
+ }
2014
+ function predictOf(v, mode) {
2015
+ if (!asked(v)) return mode === Mode.RESOLVED ? bad(`${PATH}.predict`) : true;
2016
+ return typeof v === "boolean" ? v : bad(`${PATH}.predict`);
2017
+ }
2018
+ function patchHzOf(v, mode) {
2019
+ if (!asked(v)) return mode === Mode.RESOLVED ? bad(`${PATH}.patchHz`) : PHYSICS_DEFAULT_PATCH_HZ;
2020
+ return isPhysicsPatchHz(v) ? v : bad(`${PATH}.patchHz`);
2021
+ }
2022
+ function remotePredictOf(v, mode) {
2023
+ if (!asked(v)) return mode === Mode.RESOLVED ? bad(`${PATH}.remotePredict`) : PhysicsPredictRange.DEFAULT_REMOTE_PREDICT;
2024
+ return REMOTE_PREDICTS.includes(v) ? v : bad(`${PATH}.remotePredict`);
2025
+ }
2026
+ function rangeOf(v, name, min, max, fallback, mode) {
2027
+ if (!asked(v)) return mode === Mode.RESOLVED ? bad(`${PATH}.${name}`) : fallback;
2028
+ const ok2 = typeof v === "number" && Number.isFinite(v) && v >= min && v <= max;
2029
+ return ok2 ? v : bad(`${PATH}.${name}`);
2030
+ }
2031
+ function physicsOf(v, preset, ceiling, mode) {
2032
+ const none = v === void 0 || v === null || v === false;
2033
+ if (none) return ceiling === null ? null : bad(PATH);
2034
+ if (ceiling === null) return bad(PATH);
2035
+ const given = v === true && mode === Mode.REQUEST ? {} : v;
2036
+ if (!isPlainObject2(given)) return bad(PATH);
2037
+ if (mode === Mode.REQUEST) {
2038
+ const unknown = Object.keys(given).find((key) => !REQUEST_KEYS.has(key));
2039
+ if (unknown !== void 0) return bad(`${PATH}.${unknown}`);
2040
+ }
2041
+ const lengthUnit = lengthUnitOf(given.lengthUnit, preset, mode);
2042
+ return {
2043
+ lengthUnit,
2044
+ gravity: gravityOf(given.gravity, lengthUnit, preset, mode),
2045
+ iterations: iterationsOf(given.iterations, mode),
2046
+ limits: limitsOf(given.limits, lengthUnit, ceiling, mode),
2047
+ subscribe: subscribeOf(given.subscribe, mode),
2048
+ player: playerOf(given.player, preset, mode),
2049
+ predict: predictOf(given.predict, mode),
2050
+ patchHz: patchHzOf(given.patchHz, mode),
2051
+ remotePredict: remotePredictOf(given.remotePredict, mode),
2052
+ remoteDecayMs: rangeOf(given.remoteDecayMs, "remoteDecayMs", 0, PhysicsPredictRange.MAX_REMOTE_DECAY_MS, PhysicsPredictRange.DEFAULT_REMOTE_DECAY_MS, mode),
2053
+ leadMs: rangeOf(given.leadMs, "leadMs", PhysicsPredictRange.MIN_LEAD_MS, PhysicsPredictRange.MAX_LEAD_MS, PhysicsPredictRange.DEFAULT_LEAD_MS, mode)
2054
+ };
2055
+ }
2056
+ function resolution(build) {
2057
+ try {
2058
+ return { ok: true, value: build() };
2059
+ } catch (err) {
2060
+ if (err instanceof Bad) return { ok: false, path: err.path };
2061
+ throw err;
2062
+ }
2063
+ }
2064
+ function resolvePhysicsConfig(request, preset, ceiling) {
2065
+ return resolution(() => physicsOf(request, preset, ceiling, Mode.REQUEST));
2066
+ }
2067
+ function checkResolvedPhysics(resolved, preset, ceiling) {
2068
+ return resolution(() => physicsOf(resolved, preset, ceiling, Mode.RESOLVED));
2069
+ }
2070
+ var PHYSICS_SUBSTEPS = Object.freeze({ 20: 3, 30: 2, 60: 1, 120: 1 });
2071
+ function physicsWorldDesc(physics, tickHz, predicting = false) {
2072
+ return {
2073
+ contract: PHYSICS_CONTRACT,
2074
+ dt: 1 / tickHz,
2075
+ substeps: PHYSICS_SUBSTEPS[tickHz],
2076
+ gravity: [0, 0, -physics.gravity],
2077
+ upAxis: "z",
2078
+ lengthUnit: physics.lengthUnit,
2079
+ limits: { ...physics.limits },
2080
+ solver: { iterations: solverIterationsOf(physics, predicting) }
2081
+ };
2082
+ }
2083
+ var PhysEvKind = {
2084
+ CONTACT_BEGIN: 0,
2085
+ CONTACT_END: 1,
2086
+ TRIGGER_ENTER: 2,
2087
+ TRIGGER_EXIT: 3
2088
+ };
2089
+ var PHYSICS_OVERLAP_MAX_KEYS = 64;
2090
+ var PHYSICS_BODY_FIELDS = Object.freeze([
2091
+ "qx",
2092
+ "qy",
2093
+ "qz",
2094
+ "qw",
2095
+ "lvx",
2096
+ "lvy",
2097
+ "lvz",
2098
+ "avx",
2099
+ "avy",
2100
+ "avz",
2101
+ "sleep"
2102
+ ]);
2103
+ var HOST_BODY_FIELDS = Object.freeze(["x", "y", "z", ...PHYSICS_BODY_FIELDS]);
2104
+ var BodyQuantization = {
2105
+ BITS: 16,
2106
+ /** Integer steps across a symmetric range (2^16 − 2). */
2107
+ SPAN: 65534,
2108
+ /** `qx qy qz qw`. */
2109
+ ORIENTATION: Object.freeze({ min: -1, max: 1 }),
2110
+ /** `lvx lvy lvz` (metres per second: velocity divided by lengthUnit) and `avx avy avz` (rad/s). */
2111
+ VELOCITY: Object.freeze({ min: -64, max: 64 })
2112
+ };
2113
+ function quantizeBodyValue(v, range) {
2114
+ if (Number.isNaN(v)) return 0;
2115
+ const clamped = v < range.min ? range.min : v > range.max ? range.max : v;
2116
+ return Math.floor((clamped - range.min) / (range.max - range.min) * BodyQuantization.SPAN + 0.5);
2117
+ }
2118
+ function dequantizeBodyValue(q, range) {
2119
+ return range.min + q / BodyQuantization.SPAN * (range.max - range.min);
2120
+ }
2121
+ var PHYSICS_CONTESTED_METRES = 6;
2122
+ var PHYSICS_FULL_RATE_BODIES = 100;
2123
+ var PHYSICS_ROTATING_BODIES = 25;
2124
+ var BODY_EXTRAPOLATE_MAX_MS = 250;
2125
+ var BODY_EXTRAPOLATE_BACK_TICKS = 1;
2126
+ var BODY_CORRECTION_TAU_MS = 100;
2127
+ var BODY_SNAP_METRES = 1;
2128
+
2129
+ // src/authority/projectile.ts
2130
+ var MAX_PROJECTILE_TICK = 2 ** 32 - 1;
2131
+ var MAX_PROJECTILE_G = 1e3;
2132
+ var isProjectile = (entity) => typeof entity.t0 === "number" && entity.t0 > 0;
2133
+ function projectileAt(launch, T, tickHz) {
2134
+ const elapsed = (T - launch.t0) / tickHz;
2135
+ const s = Number.isFinite(elapsed) && elapsed >= 0 ? elapsed : 0;
2136
+ return {
2137
+ x: unsignedZero(Math.fround(launch.x + launch.vx * s)),
2138
+ y: unsignedZero(Math.fround(launch.y + launch.vy * s)),
2139
+ z: unsignedZero(Math.fround(launch.z + launch.vz * s - 0.5 * launch.g * s * s)),
2140
+ vx: unsignedZero(launch.vx),
2141
+ vy: unsignedZero(launch.vy),
2142
+ vz: unsignedZero(Math.fround(launch.vz - launch.g * s))
2143
+ };
2144
+ }
2145
+ function serverTick(serverNowMs, tickOrigin, tickHz) {
2146
+ return (serverNowMs - tickOrigin) * tickHz / 1e3;
2147
+ }
2148
+ function tickInstant(tick, tickOrigin, tickHz) {
2149
+ return tickOrigin + tick * 1e3 / tickHz;
2150
+ }
2151
+
2152
+ // src/authority/contact-config.ts
2153
+ var ContactDrawing = { Predicted: "predicted", Separate: "separate", Timeline: "timeline" };
2154
+ var ContactPush = { Shared: "shared", Mover: "mover" };
2155
+ var ContactPreset = {
2156
+ Shooter: "shooter",
2157
+ Platformer: "platformer",
2158
+ VehicleArena: "vehicle-arena",
2159
+ PhysicsSandbox: "physics-sandbox",
2160
+ Party: "party"
2161
+ };
2162
+ var TopPolicy = {
2163
+ /** Rest on the head: a landing stands within `center` of the lower's half width and slides off by
2164
+ * `slide` elsewhere; a resting seat holds while its centre is over the footprint; `ride`: carried. */
2165
+ Stand: "stand",
2166
+ /** Frictionless: the upper seat is slid off within a bounded time. */
2167
+ Slide: "slide",
2168
+ /** A stomp: the upper seat bounces off, and the module hears it. */
2169
+ Bounce: "bounce",
2170
+ /** No collision at all. */
2171
+ Ghost: "ghost"
2172
+ };
2173
+ var ContactShapeKind = {
2174
+ /** A box turned with the seat's heading and laid on its support up (a vehicle). */
2175
+ Box: "box",
2176
+ /** An upright axis-aligned square footprint (a `firstperson` character). */
2177
+ Hull: "hull"
2178
+ };
2179
+ var HeadFriction = { Slide: "slide", Carry: "carry" };
2180
+ function drawingOf(given, path, base) {
2181
+ if (!has(given, "drawing")) return base;
2182
+ const v = given.drawing;
2183
+ if (!Object.values(ContactDrawing).includes(v)) throw new ContactConfigError(`${path}.drawing`, "must be predicted, separate or timeline");
2184
+ return v;
2185
+ }
2186
+ function pushOf(given, path, base) {
2187
+ if (!has(given, "push")) return base;
2188
+ const v = given.push;
2189
+ if (!Object.values(ContactPush).includes(v)) throw new ContactConfigError(`${path}.push`, "must be shared or mover");
2190
+ return v;
2191
+ }
2192
+ var ContactConfigError = class extends RangeError {
2193
+ path;
2194
+ constructor(path, why) {
2195
+ super(`${path}: ${why}`);
2196
+ this.name = "ContactConfigError";
2197
+ this.path = path;
2198
+ }
2199
+ };
2200
+ var CONTACT_PAIRS_MAX = 32;
2201
+ var CONTACT_TAG_MAX = 64;
2202
+ var CONTACT_PASSES_MAX = 64;
2203
+ var CONTACT_VALUE_MAX = 1e5;
2204
+ var slide = (speed, step, share) => ({ speed, step, share });
2205
+ var slideTop = (s) => ({ policy: TopPolicy.Slide, ...s });
2206
+ var stand = (ride, center, s) => ({ policy: TopPolicy.Stand, ride, center, slide: s });
2207
+ var CHARACTER_SLIDE = slide(300, 150, 1);
2208
+ var VEHICLE_SLIDE = slide(950, 480, 1);
2209
+ var ARENA_OFF_WALL = slide(600, null, 0.5);
2210
+ var SHOOTER_CENTER = 0.25;
2211
+ var PROP_SLIDE = Object.freeze({ speed: 300, step: 150 });
2212
+ var STOMP = { policy: TopPolicy.Bounce, stompMin: 150, bounceSpeed: 450, jumpBoost: 150, slide: CHARACTER_SLIDE };
2213
+ var CONTACT_PRESETS = {
2214
+ [ContactPreset.Shooter]: { character: stand(false, SHOOTER_CENTER, CHARACTER_SLIDE), vehicle: slideTop(VEHICLE_SLIDE), restitution: 0, passes: 16, vehicleOffWall: VEHICLE_SLIDE, stopSpin: false, drawing: ContactDrawing.Separate, push: ContactPush.Mover, headFriction: HeadFriction.Slide },
2215
+ [ContactPreset.Platformer]: { character: stand(true, null, CHARACTER_SLIDE), vehicle: slideTop(VEHICLE_SLIDE), restitution: 0, passes: 16, vehicleOffWall: VEHICLE_SLIDE, stopSpin: false, drawing: ContactDrawing.Separate, push: ContactPush.Mover, headFriction: HeadFriction.Slide },
2216
+ [ContactPreset.VehicleArena]: { character: slideTop(CHARACTER_SLIDE), vehicle: slideTop(VEHICLE_SLIDE), restitution: 0.6, passes: 32, vehicleOffWall: ARENA_OFF_WALL, stopSpin: true, drawing: ContactDrawing.Predicted, push: ContactPush.Shared, headFriction: HeadFriction.Carry },
2217
+ [ContactPreset.PhysicsSandbox]: { character: stand(true, null, CHARACTER_SLIDE), vehicle: stand(true, null, VEHICLE_SLIDE), restitution: 0.2, passes: 16, vehicleOffWall: VEHICLE_SLIDE, stopSpin: false, drawing: ContactDrawing.Separate, push: ContactPush.Shared, headFriction: HeadFriction.Carry },
2218
+ [ContactPreset.Party]: { character: stand(true, null, CHARACTER_SLIDE), vehicle: slideTop(VEHICLE_SLIDE), restitution: 0.3, passes: 16, vehicleOffWall: VEHICLE_SLIDE, stopSpin: false, drawing: ContactDrawing.Separate, push: ContactPush.Shared, headFriction: HeadFriction.Slide }
2219
+ };
2220
+ var CONTACT_DEFAULTS = Object.freeze({ supportCos: 0.7, entrySpeed: 1, entryWindow: 1 / 60, slop: 0.1, roofSkin: 0.01 });
2221
+ var KEYS = /* @__PURE__ */ new Set(["preset", "shape", "top", "offWall", "restitution", "passes", "slop", "supportCos", "entrySpeed", "entryWindow", "roofSkin", "maxVelocity", "stopSpin", "pairs", "drawing", "push", "headFriction", "propSlide"]);
2222
+ var TOP_KEYS = {
2223
+ [TopPolicy.Stand]: /* @__PURE__ */ new Set(["policy", "ride", "center", "slide"]),
2224
+ [TopPolicy.Slide]: /* @__PURE__ */ new Set(["policy", "speed", "step", "share"]),
2225
+ [TopPolicy.Bounce]: /* @__PURE__ */ new Set(["policy", "stompMin", "bounceSpeed", "jumpBoost"]),
2226
+ [TopPolicy.Ghost]: /* @__PURE__ */ new Set(["policy"])
2227
+ };
2228
+ var SLIDE_KEYS = /* @__PURE__ */ new Set(["speed", "step", "share"]);
2229
+ var PROP_SLIDE_KEYS = /* @__PURE__ */ new Set(["speed", "step"]);
2230
+ var PAIR_KEYS = /* @__PURE__ */ new Set(["a", "b", "top"]);
2231
+ var BOX_KEYS = /* @__PURE__ */ new Set(["kind", "length", "width", "top"]);
2232
+ var HULL_KEYS = /* @__PURE__ */ new Set(["kind", "radius", "height"]);
2233
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
2234
+ var has = (o, k) => Object.hasOwn(o, k) && o[k] !== void 0;
2235
+ function onlyKeys(o, keys, path) {
2236
+ const unknown = Object.keys(o).find((k) => !keys.has(k));
2237
+ if (unknown !== void 0) throw new ContactConfigError(`${path}.${unknown}`, "is not a contact setting");
2238
+ }
2239
+ function num(o, key, path, fallback, lo, hi, open = false) {
2240
+ if (!has(o, key)) return fallback;
2241
+ const v = o[key];
2242
+ const low = open ? v > lo : v >= lo;
2243
+ if (typeof v !== "number" || !Number.isFinite(v) || !low || v > hi) throw new ContactConfigError(`${path}.${key}`, `must be a number in ${open ? "(" : "["}${lo}, ${hi}]`);
2244
+ return v;
2245
+ }
2246
+ function slideOf(v, path, base, keys = SLIDE_KEYS) {
2247
+ if (!isRecord(v)) throw new ContactConfigError(path, "must be an object");
2248
+ onlyKeys(v, keys, path);
2249
+ const speed = num(v, "speed", path, base.speed, 0, CONTACT_VALUE_MAX);
2250
+ const step = has(v, "step") && v.step === null ? null : num(v, "step", path, base.step ?? CONTACT_VALUE_MAX, 0, CONTACT_VALUE_MAX, true);
2251
+ const keptStep = !has(v, "step") ? base.step : step;
2252
+ return { speed, step: keptStep, share: num(v, "share", path, base.share, 0, 1) };
2253
+ }
2254
+ var baseSlide = (top, vehicle) => {
2255
+ if (top.policy === TopPolicy.Slide) return { speed: top.speed, step: top.step, share: top.share };
2256
+ if (top.policy === TopPolicy.Bounce || top.policy === TopPolicy.Stand) return top.slide;
2257
+ return vehicle ? VEHICLE_SLIDE : CHARACTER_SLIDE;
2258
+ };
2259
+ function standOf(v, path, base, vehicle) {
2260
+ const from = base.policy === TopPolicy.Stand ? base : stand(true, null, baseSlide(base, vehicle));
2261
+ if (has(v, "ride") && typeof v.ride !== "boolean") throw new ContactConfigError(`${path}.ride`, "must be true or false");
2262
+ const ride = has(v, "ride") ? v.ride : from.ride;
2263
+ const center = !has(v, "center") ? from.center : v.center === null ? null : num(v, "center", path, 1, 0, 1, true);
2264
+ const s = has(v, "slide") ? slideOf(v.slide, `${path}.slide`, from.slide) : from.slide;
2265
+ return stand(ride, center, s);
2266
+ }
2267
+ function topOf2(v, path, base, vehicle) {
2268
+ if (!isRecord(v)) throw new ContactConfigError(path, "must be an object");
2269
+ const policy = v.policy;
2270
+ if (typeof policy !== "string" || !Object.hasOwn(TOP_KEYS, policy)) throw new ContactConfigError(`${path}.policy`, "must be stand, slide, bounce or ghost");
2271
+ const p = policy;
2272
+ onlyKeys(v, TOP_KEYS[p], path);
2273
+ if (p === TopPolicy.Stand) return standOf(v, path, base, vehicle);
2274
+ if (p === TopPolicy.Ghost) return { policy: p };
2275
+ if (p === TopPolicy.Slide) return slideTop(slideOf(v, path, baseSlide(base, vehicle), TOP_KEYS[p]));
2276
+ const stomp = base.policy === TopPolicy.Bounce ? base : STOMP;
2277
+ return {
2278
+ policy: TopPolicy.Bounce,
2279
+ stompMin: num(v, "stompMin", path, stomp.policy === TopPolicy.Bounce ? stomp.stompMin : 0, 0, CONTACT_VALUE_MAX),
2280
+ bounceSpeed: num(v, "bounceSpeed", path, stomp.policy === TopPolicy.Bounce ? stomp.bounceSpeed : 0, 0, CONTACT_VALUE_MAX),
2281
+ jumpBoost: num(v, "jumpBoost", path, stomp.policy === TopPolicy.Bounce ? stomp.jumpBoost : 0, 0, CONTACT_VALUE_MAX),
2282
+ slide: baseSlide(base, vehicle)
2283
+ };
2284
+ }
2285
+ function shapeOf(v, path, preset) {
2286
+ const p = preset.params;
2287
+ if (v === void 0) {
2288
+ if (preset.name === "vehicle") return { kind: ContactShapeKind.Box, length: 2 * (p.radius ?? 0), width: 2 * (p.radius ?? 0), top: p.height ?? 0 };
2289
+ return { kind: ContactShapeKind.Hull, radius: p.radius ?? 0, height: p.standHeight ?? p.height ?? 0 };
2290
+ }
2291
+ if (!isRecord(v)) throw new ContactConfigError(path, "must be an object");
2292
+ if (v.kind === ContactShapeKind.Box) {
2293
+ onlyKeys(v, BOX_KEYS, path);
2294
+ const dim = (k) => num(v, k, path, Number.NaN, 0, CONTACT_VALUE_MAX, true);
2295
+ return { kind: ContactShapeKind.Box, length: dim("length"), width: dim("width"), top: dim("top") };
2296
+ }
2297
+ if (v.kind === ContactShapeKind.Hull) {
2298
+ onlyKeys(v, HULL_KEYS, path);
2299
+ const dim = (k) => num(v, k, path, Number.NaN, 0, CONTACT_VALUE_MAX, true);
2300
+ return { kind: ContactShapeKind.Hull, radius: dim("radius"), height: dim("height") };
2301
+ }
2302
+ throw new ContactConfigError(`${path}.kind`, "must be box or hull");
2303
+ }
2304
+ function requireShape(shape, path) {
2305
+ for (const [k, v] of Object.entries(shape)) if (typeof v === "number" && Number.isNaN(v)) throw new ContactConfigError(`${path}.${k}`, "is required");
2306
+ return shape;
2307
+ }
2308
+ function headFrictionOf(given, path, base) {
2309
+ if (!has(given, "headFriction")) return base;
2310
+ const v = given.headFriction;
2311
+ if (!Object.values(HeadFriction).includes(v)) throw new ContactConfigError(`${path}.headFriction`, "must be slide or carry");
2312
+ return v;
2313
+ }
2314
+ function propSlideOf(given, path) {
2315
+ if (!has(given, "propSlide")) return PROP_SLIDE;
2316
+ const v = given.propSlide;
2317
+ const at = `${path}.propSlide`;
2318
+ if (!isRecord(v)) throw new ContactConfigError(at, "must be an object");
2319
+ onlyKeys(v, PROP_SLIDE_KEYS, at);
2320
+ return { speed: num(v, "speed", at, PROP_SLIDE.speed, 0, CONTACT_VALUE_MAX), step: num(v, "step", at, PROP_SLIDE.step, 0, CONTACT_VALUE_MAX, true) };
2321
+ }
2322
+ function pairsOf(v, path, base, vehicle) {
2323
+ if (v === void 0) return [];
2324
+ if (!Array.isArray(v) || v.length > CONTACT_PAIRS_MAX) throw new ContactConfigError(path, `must be a list of at most ${CONTACT_PAIRS_MAX} pair rules`);
2325
+ return v.map((r, i) => {
2326
+ const at = `${path}[${i}]`;
2327
+ if (!isRecord(r)) throw new ContactConfigError(at, "must be an object");
2328
+ onlyKeys(r, PAIR_KEYS, at);
2329
+ for (const k of ["a", "b"]) {
2330
+ const tag = r[k];
2331
+ if (typeof tag !== "string" || tag.length === 0 || tag.length > CONTACT_TAG_MAX) throw new ContactConfigError(`${at}.${k}`, `must be a tag of 1 to ${CONTACT_TAG_MAX} characters`);
2332
+ }
2333
+ return { a: r.a, b: r.b, top: topOf2(r.top, `${at}.top`, base, vehicle) };
2334
+ });
2335
+ }
2336
+ function passesOf(o, path, fallback) {
2337
+ const n = num(o, "passes", path, fallback, 1, CONTACT_PASSES_MAX);
2338
+ if (!Number.isInteger(n)) throw new ContactConfigError(`${path}.passes`, `must be an integer in [1, ${CONTACT_PASSES_MAX}]`);
2339
+ return n;
2340
+ }
2341
+ var WORLD_PRESETS = /* @__PURE__ */ new Set(["firstperson", "vehicle"]);
2342
+ function resolveContactConfig(given, preset, path = "$.config.contacts") {
2343
+ if (!isRecord(given)) throw new ContactConfigError(path, "must be an object");
2344
+ onlyKeys(given, KEYS, path);
2345
+ if (preset === null || !WORLD_PRESETS.has(preset.name)) throw new ContactConfigError(path, "needs a firstperson or vehicle movement preset");
2346
+ const name = given.preset;
2347
+ if (typeof name !== "string" || !Object.hasOwn(CONTACT_PRESETS, name)) throw new ContactConfigError(`${path}.preset`, "must be shooter, platformer, vehicle-arena, physics-sandbox or party");
2348
+ const table = CONTACT_PRESETS[name];
2349
+ const shape = requireShape(shapeOf(given.shape, `${path}.shape`, preset), `${path}.shape`);
2350
+ const vehicle = shape.kind === ContactShapeKind.Box;
2351
+ const base = vehicle ? table.vehicle : table.character;
2352
+ const top = given.top === void 0 ? base : topOf2(given.top, `${path}.top`, base, vehicle);
2353
+ const offBase = vehicle ? table.vehicleOffWall : baseSlide(base, false);
2354
+ const p = preset.params;
2355
+ const maxVelocity = has(given, "maxVelocity") && given.maxVelocity === null ? null : num(given, "maxVelocity", path, p.maxVelocity ?? CONTACT_VALUE_MAX, 0, CONTACT_VALUE_MAX, true);
2356
+ if (has(given, "stopSpin") && typeof given.stopSpin !== "boolean") throw new ContactConfigError(`${path}.stopSpin`, "must be true or false");
2357
+ return {
2358
+ preset: name,
2359
+ shape,
2360
+ hull: { radius: p.radius ?? 0, height: p.standHeight ?? p.height ?? 0, skin: p.skin ?? 0 },
2361
+ groundTrace: p.groundTrace ?? 0,
2362
+ supportCos: num(given, "supportCos", path, CONTACT_DEFAULTS.supportCos, 0, 1),
2363
+ entrySpeed: num(given, "entrySpeed", path, CONTACT_DEFAULTS.entrySpeed, 0, CONTACT_VALUE_MAX, true),
2364
+ entryWindow: num(given, "entryWindow", path, CONTACT_DEFAULTS.entryWindow, 0, 1, true),
2365
+ restitution: num(given, "restitution", path, table.restitution, 0, 1),
2366
+ passes: passesOf(given, path, table.passes),
2367
+ slop: num(given, "slop", path, CONTACT_DEFAULTS.slop, 0, CONTACT_VALUE_MAX),
2368
+ roofSkin: num(given, "roofSkin", path, CONTACT_DEFAULTS.roofSkin, 0, CONTACT_VALUE_MAX),
2369
+ maxVelocity,
2370
+ stopSpin: has(given, "stopSpin") ? given.stopSpin : table.stopSpin,
2371
+ top,
2372
+ offWall: given.offWall === void 0 ? offBase : slideOf(given.offWall, `${path}.offWall`, offBase),
2373
+ pairs: pairsOf(given.pairs, `${path}.pairs`, base, vehicle),
2374
+ drawing: drawingOf(given, path, table.drawing),
2375
+ push: pushOf(given, path, table.push),
2376
+ headFriction: headFrictionOf(given, path, table.headFriction),
2377
+ propSlide: propSlideOf(given, path)
2378
+ };
2379
+ }
2380
+
2381
+ // src/authority/lifts-config.ts
2382
+ var LiftShapeKind = {
2383
+ /** A box `half` (x, y, z): axis-aligned, or turned about z when it spins (a sweeper bar). */
2384
+ Box: "box",
2385
+ /** An upright disc of `radius`, `half` its half height (a turntable, a spinner). */
2386
+ Disc: "disc"
2387
+ };
2388
+ var LiftPathMode = {
2389
+ /** Out along the legs, then back along them. */
2390
+ PingPong: "pingpong",
2391
+ /** Round the points and from the last back to the first. */
2392
+ Loop: "loop"
2393
+ };
2394
+ var LiftEase = {
2395
+ /** At a steady speed. */
2396
+ Linear: "linear",
2397
+ /** Starting and stopping at rest (`s·s·(3 − 2s)`; its peak speed 1.5 times the mean). */
2398
+ Smooth: "smooth"
2399
+ };
2400
+ var LiftImpart = {
2401
+ /** The lift's velocity at the rider's point (Unreal's and Godot's default). */
2402
+ All: "all",
2403
+ /** Nothing: the rider leaves with its own velocity. */
2404
+ None: "none"
2405
+ };
2406
+ var NO_LIFTS = Object.freeze({ platforms: Object.freeze([]), bodies: false });
2407
+ var LiftsConfigError = class extends RangeError {
2408
+ path;
2409
+ fast;
2410
+ constructor(path, why, fast) {
2411
+ super(`${path}: ${why}`);
2412
+ this.name = "LiftsConfigError";
2413
+ this.path = path;
2414
+ this.fast = fast;
2415
+ }
2416
+ };
2417
+ var LIFTS_MAX = 32;
2418
+ var LIFT_POINTS_MAX = 16;
2419
+ var LIFT_ID_MAX = 32;
2420
+ var LIFT_VALUE_MAX = 1e6;
2421
+ var LIFT_TICKS_MAX = 2 ** 24;
2422
+ var LIFT_SPIN_MIN_PERIOD = 8;
2423
+ var SMOOTH_PEAK = 1.5;
2424
+ var TWO_PI2 = 2 * Math.PI;
2425
+ var ID = /^[a-z0-9-]+$/;
2426
+ var isLiftId = (id) => typeof id === "string" && id.length > 0 && id.length <= LIFT_ID_MAX && ID.test(id);
2427
+ var KEYS2 = /* @__PURE__ */ new Set(["platforms", "bodies"]);
2428
+ var PLATFORM_KEYS = /* @__PURE__ */ new Set(["id", "shape", "at", "path", "spin", "surface", "impart", "called"]);
2429
+ var PATH_KEYS = /* @__PURE__ */ new Set(["points", "legs", "waits", "mode", "ease", "phase"]);
2430
+ var SPIN_KEYS = /* @__PURE__ */ new Set(["period", "dir", "phase"]);
2431
+ var BOX_KEYS2 = /* @__PURE__ */ new Set(["kind", "half"]);
2432
+ var DISC_KEYS = /* @__PURE__ */ new Set(["kind", "radius", "half"]);
2433
+ var isRecord2 = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
2434
+ var has2 = (o, k) => Object.hasOwn(o, k) && o[k] !== void 0;
2435
+ var fround = Math.fround;
2436
+ function onlyKeys2(o, keys, path) {
2437
+ const unknown = Object.keys(o).find((k) => !keys.has(k));
2438
+ if (unknown !== void 0) throw new LiftsConfigError(`${path}.${unknown}`, "is not a lift setting");
2439
+ }
2440
+ function value(v, path, positive = false) {
2441
+ if (typeof v !== "number" || !Number.isFinite(v) || Math.abs(v) > LIFT_VALUE_MAX) throw new LiftsConfigError(path, `must be a number of magnitude at most ${LIFT_VALUE_MAX}`);
2442
+ const f = fround(v) === 0 ? 0 : fround(v);
2443
+ if (positive && !(f > 0)) throw new LiftsConfigError(path, "must be above 0");
2444
+ return f;
2445
+ }
2446
+ function ticks(v, path, lo) {
2447
+ if (typeof v !== "number" || !Number.isInteger(v) || v < lo || v > LIFT_TICKS_MAX) throw new LiftsConfigError(path, `must be an integer number of ticks in [${lo}, ${LIFT_TICKS_MAX}]`);
2448
+ return v;
2449
+ }
2450
+ function vector(v, path, n, positive = false) {
2451
+ if (!Array.isArray(v) || v.length !== n) throw new LiftsConfigError(path, `must be an array of ${n} numbers`);
2452
+ return v.map((x, i) => value(x, `${path}[${i}]`, positive));
2453
+ }
2454
+ function shapeOf2(v, path) {
2455
+ if (!isRecord2(v)) throw new LiftsConfigError(path, "must be an object");
2456
+ if (v.kind === LiftShapeKind.Box) {
2457
+ onlyKeys2(v, BOX_KEYS2, path);
2458
+ return { kind: LiftShapeKind.Box, half: vector(v.half, `${path}.half`, 3, true) };
2459
+ }
2460
+ if (v.kind === LiftShapeKind.Disc) {
2461
+ onlyKeys2(v, DISC_KEYS, path);
2462
+ return { kind: LiftShapeKind.Disc, radius: value(v.radius, `${path}.radius`, true), half: value(v.half, `${path}.half`, true) };
2463
+ }
2464
+ throw new LiftsConfigError(`${path}.kind`, "must be box or disc");
2465
+ }
2466
+ function enumOf(o, key, path, values, fallback) {
2467
+ if (!has2(o, key)) return fallback;
2468
+ const v = o[key];
2469
+ if (!Object.values(values).includes(v)) throw new LiftsConfigError(`${path}.${key}`, `must be ${Object.values(values).join(" or ")}`);
2470
+ return v;
2471
+ }
2472
+ function periodOf(legs, waits, mode) {
2473
+ const sum = (xs) => xs.reduce((a, b) => a + b, 0);
2474
+ if (mode === LiftPathMode.Loop) return sum(legs) + sum(waits);
2475
+ return 2 * sum(legs) + 2 * sum(waits) - (waits[0] ?? 0) - (waits[waits.length - 1] ?? 0);
2476
+ }
2477
+ function pathOf(v, path, called) {
2478
+ if (!isRecord2(v)) throw new LiftsConfigError(path, "must be an object");
2479
+ onlyKeys2(v, PATH_KEYS, path);
2480
+ const mode = enumOf(v, "mode", path, LiftPathMode, LiftPathMode.PingPong);
2481
+ const ease = enumOf(v, "ease", path, LiftEase, LiftEase.Linear);
2482
+ if (!Array.isArray(v.legs) || v.legs.length < 1 || v.legs.length > LIFT_POINTS_MAX) throw new LiftsConfigError(`${path}.legs`, `must be a list of 1 to ${LIFT_POINTS_MAX} leg tick counts`);
2483
+ const legs = v.legs.map((t, i) => ticks(t, `${path}.legs[${i}]`, 1));
2484
+ const wantPoints = mode === LiftPathMode.Loop ? legs.length : legs.length + 1;
2485
+ if (!Array.isArray(v.points) || v.points.length !== wantPoints || wantPoints < 2) {
2486
+ throw new LiftsConfigError(`${path}.points`, `must be ${wantPoints < 2 ? "at least 2" : wantPoints} points (${mode === LiftPathMode.Loop ? "one per leg" : "one more than the legs"})`);
2487
+ }
2488
+ const points = v.points.map((p, i) => vector(p, `${path}.points[${i}]`, 3));
2489
+ const waits = has2(v, "waits") ? v.waits : points.map(() => 0);
2490
+ if (!Array.isArray(waits) || waits.length !== points.length) throw new LiftsConfigError(`${path}.waits`, `must be one wait per point (${points.length})`);
2491
+ const waitTicks = waits.map((t, i) => ticks(t, `${path}.waits[${i}]`, 0));
2492
+ if (called && has2(v, "phase")) throw new LiftsConfigError(`${path}.phase`, "is not allowed on a called lift (its call sets its start)");
2493
+ const phase = has2(v, "phase") ? ticks(v.phase, `${path}.phase`, 0) : 0;
2494
+ return { points, legs, waits: waitTicks, mode, ease, phase, period: periodOf(legs, waitTicks, mode) };
2495
+ }
2496
+ function spinOf(v, path) {
2497
+ if (!isRecord2(v)) throw new LiftsConfigError(path, "must be an object");
2498
+ onlyKeys2(v, SPIN_KEYS, path);
2499
+ const period = ticks(v.period, `${path}.period`, LIFT_SPIN_MIN_PERIOD);
2500
+ if (has2(v, "dir") && v.dir !== 1 && v.dir !== -1) throw new LiftsConfigError(`${path}.dir`, "must be 1 or -1");
2501
+ return { period, dir: has2(v, "dir") ? v.dir : 1, phase: has2(v, "phase") ? ticks(v.phase, `${path}.phase`, 0) : 0 };
2502
+ }
2503
+ var reachOf = (s) => s.kind === LiftShapeKind.Disc ? s.radius : Math.sqrt(s.half[0] * s.half[0] + s.half[1] * s.half[1]);
2504
+ var speedText = (perTick, tickHz, rate = "") => `${Number(perTick.toFixed(3))} u a tick (${Number((perTick * tickHz).toFixed(1))} u/s${rate})`;
2505
+ function refuseFast(lift, path, radius, tickHz) {
2506
+ const spinTip = lift.spin === null ? 0 : reachOf(lift.shape) * TWO_PI2 / lift.spin.period;
2507
+ const allowed = `at most ${speedText(radius, tickHz, ` at ${tickHz} Hz`)} is allowed (the hull radius a tick)`;
2508
+ const legs = lift.path?.legs ?? [];
2509
+ let fastest = { at: `${path}.spin.period`, move: spinTip };
2510
+ legs.forEach((n, i) => {
2511
+ const p = lift.path;
2512
+ const a = p.points[i];
2513
+ const b = p.points[(i + 1) % p.points.length];
2514
+ const dx = b[0] - a[0];
2515
+ const dy = b[1] - a[1];
2516
+ const dz = b[2] - a[2];
2517
+ const mean = Math.sqrt(dx * dx + dy * dy + dz * dz) / n;
2518
+ const move = (p.ease === LiftEase.Smooth ? SMOOTH_PEAK * mean : mean) + spinTip;
2519
+ if (move > fastest.move) fastest = { at: `${path}.path.legs[${i}]`, move };
2520
+ });
2521
+ if (fastest.move > radius) {
2522
+ throw new LiftsConfigError(fastest.at, `platform '${lift.id}' moves up to ${speedText(fastest.move, tickHz)}; ${allowed}`, { platform: lift.id, move: fastest.move, allowed: radius, tickHz });
2523
+ }
2524
+ }
2525
+ function platformOf(v, path, ids, radius, tickHz) {
2526
+ if (!isRecord2(v)) throw new LiftsConfigError(path, "must be an object");
2527
+ onlyKeys2(v, PLATFORM_KEYS, path);
2528
+ const id = v.id;
2529
+ if (typeof id !== "string" || id.length === 0 || id.length > LIFT_ID_MAX || !ID.test(id)) throw new LiftsConfigError(`${path}.id`, `must be 1 to ${LIFT_ID_MAX} of a-z, 0-9 and -`);
2530
+ if (ids.has(id)) throw new LiftsConfigError(`${path}.id`, `repeats the id '${id}'`);
2531
+ ids.add(id);
2532
+ const shape = shapeOf2(v.shape, `${path}.shape`);
2533
+ if (has2(v, "called") && typeof v.called !== "boolean") throw new LiftsConfigError(`${path}.called`, "must be true or false");
2534
+ const called = v.called === true;
2535
+ const lpath = has2(v, "path") ? pathOf(v.path, `${path}.path`, called) : null;
2536
+ if (lpath === null && !has2(v, "at")) throw new LiftsConfigError(`${path}.at`, "is required without a path");
2537
+ const at = has2(v, "at") ? vector(v.at, `${path}.at`, 3) : lpath?.points[0];
2538
+ const lift = {
2539
+ id,
2540
+ shape,
2541
+ at,
2542
+ path: lpath,
2543
+ spin: has2(v, "spin") ? spinOf(v.spin, `${path}.spin`) : null,
2544
+ surface: has2(v, "surface") ? vector(v.surface, `${path}.surface`, 2) : null,
2545
+ impart: enumOf(v, "impart", path, LiftImpart, LiftImpart.All),
2546
+ called
2547
+ };
2548
+ refuseFast(lift, path, radius, tickHz);
2549
+ return lift;
2550
+ }
2551
+ var WORLD_PRESETS2 = /* @__PURE__ */ new Set(["firstperson", "vehicle"]);
2552
+ function resolveLifts(given, preset, tickHz, path = "$.config.lifts") {
2553
+ if (!isRecord2(given)) throw new LiftsConfigError(path, "must be an object");
2554
+ onlyKeys2(given, KEYS2, path);
2555
+ if (preset === null || !WORLD_PRESETS2.has(preset.name)) throw new LiftsConfigError(path, "needs a firstperson or vehicle movement preset");
2556
+ const list2 = given.platforms;
2557
+ if (!Array.isArray(list2) || list2.length < 1 || list2.length > LIFTS_MAX) throw new LiftsConfigError(`${path}.platforms`, `must be a list of 1 to ${LIFTS_MAX} platforms`);
2558
+ if (has2(given, "bodies") && typeof given.bodies !== "boolean") throw new LiftsConfigError(`${path}.bodies`, "must be true or false");
2559
+ const radius = preset.params.radius ?? 0;
2560
+ const ids = /* @__PURE__ */ new Set();
2561
+ const platforms = list2.map((p, i) => platformOf(p, `${path}.platforms[${i}]`, ids, radius, tickHz));
2562
+ return { platforms, bodies: given.bodies !== false };
2563
+ }
2564
+
2565
+ // src/authority/input-config.ts
2566
+ var InputRepeat = { On: "on", Off: "off" };
2567
+ var INPUT_PRESETS = {
2568
+ [ContactPreset.Shooter]: { repeat: InputRepeat.On },
2569
+ [ContactPreset.Platformer]: { repeat: InputRepeat.On },
2570
+ [ContactPreset.VehicleArena]: { repeat: InputRepeat.Off },
2571
+ [ContactPreset.PhysicsSandbox]: { repeat: InputRepeat.On },
2572
+ [ContactPreset.Party]: { repeat: InputRepeat.On }
2573
+ };
2574
+ var INPUTS_DEFAULT = { repeat: InputRepeat.On };
2575
+ var InputConfigError = class extends Error {
2576
+ path;
2577
+ constructor(path) {
2578
+ super(`bad config at ${path}`);
2579
+ this.name = "InputConfigError";
2580
+ this.path = path;
2581
+ }
2582
+ };
2583
+ var KEYS3 = /* @__PURE__ */ new Set(["preset", "repeat"]);
2584
+ var isPlainObject3 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
2585
+ var isPreset = (v) => Object.values(ContactPreset).some((p) => p === v);
2586
+ var isRepeat = (v) => v === InputRepeat.On || v === InputRepeat.Off;
2587
+ function resolveInputConfig(given, contactsPreset, path = "$.config.inputs") {
2588
+ if (given === void 0 || given === null) return contactsPreset === null ? null : INPUT_PRESETS[contactsPreset];
2589
+ if (!isPlainObject3(given)) throw new InputConfigError(path);
2590
+ for (const key of Object.keys(given)) if (!KEYS3.has(key)) throw new InputConfigError(`${path}.${key}`);
2591
+ if (given.preset !== void 0 && !isPreset(given.preset)) throw new InputConfigError(`${path}.preset`);
2592
+ if (given.repeat !== void 0 && !isRepeat(given.repeat)) throw new InputConfigError(`${path}.repeat`);
2593
+ const preset = given.preset ?? contactsPreset;
2594
+ const fromPreset = preset === null ? INPUTS_DEFAULT : INPUT_PRESETS[preset];
2595
+ return { repeat: given.repeat ?? fromPreset.repeat };
2596
+ }
2597
+ function presetInputRepeat(presetText) {
2598
+ let spec;
2599
+ try {
2600
+ spec = JSON.parse(presetText);
2601
+ } catch {
2602
+ return InputRepeat.On;
2603
+ }
2604
+ const inputs = isPlainObject3(spec) ? spec.inputs : void 0;
2605
+ return isPlainObject3(inputs) && inputs.repeat === InputRepeat.Off ? InputRepeat.Off : InputRepeat.On;
2606
+ }
2607
+
2608
+ // src/authority/wire.ts
2609
+ var GuestGlobal = {
2610
+ INIT: "__genex_init",
2611
+ TICK: "__genex_tick",
2612
+ /** A physics room's static world, called once after init (§17.10 *The static world*). */
2613
+ WORLD: "__genex_world"
2614
+ };
2615
+ var AuthorityLimits = {
2616
+ /** Entities one tick may update, and separately remove. */
2617
+ MAX_ENTITIES: 256,
2618
+ /** Entities one room may hold at once. The room state lives on the host, outside the sandbox's
2619
+ * memory limit, so this cap is what bounds it (and every client's patches). */
2620
+ MAX_ROOM_ENTITIES: 1024,
2621
+ /** One TickOut / InitOut as JSON text. */
2622
+ MAX_OUTPUT_BYTES: 32 * 1024,
2623
+ MAX_EVENTS_PER_TICK: 64,
2624
+ /** One entity's `data` JSON text. */
2625
+ MAX_ENTITY_DATA_BYTES: 256,
2626
+ MAX_ENTITY_ID_CHARS: 64,
2627
+ MAX_KIND_CHARS: 64,
2628
+ /** A session id (`owner`, an event's `to`). */
2629
+ MAX_SESSION_ID_CHARS: 64,
2630
+ /** Every entity number's magnitude (float32 holds it without overflowing). */
2631
+ MAX_ABS,
2632
+ /** `flags` is an integer below this. */
2633
+ MASK_LIMIT,
2634
+ /** `epoch` is an integer in [0, MAX_EPOCH]. */
2635
+ MAX_EPOCH: 2 ** 32 - 1,
2636
+ /** A projectile's launch tick `t0` is an integer in [0, MAX_PROJECTILE_TICK] (§17.9). */
2637
+ MAX_PROJECTILE_TICK,
2638
+ /** A projectile's gravity `g` is in [0, MAX_PROJECTILE_G] units/s². */
2639
+ MAX_PROJECTILE_G,
2640
+ /** The launches waiting to go out (since the last output the host applied) may hold at most this
2641
+ * many entities and this much of the output's JSON (§17.9): a launch past either is refused in
2642
+ * the sandbox, so a burst alone can never make an output the host must refuse (about a hundred
2643
+ * new projectiles whose floats print in full). The rest of MAX_ENTITIES and MAX_OUTPUT_BYTES
2644
+ * stays for everything else a tick writes. */
2645
+ MAX_LAUNCH_UPDATES: 224,
2646
+ MAX_LAUNCH_BYTES: 28 * 1024,
2647
+ /** One event: its type, and its `data` as JSON text. */
2648
+ MAX_EVENT_TYPE_CHARS: Limits.MAX_EVENT_TYPE_CHARS,
2649
+ MAX_EVENT_BYTES: Limits.MAX_EVENT_BYTES,
2650
+ MAX_JSON_DEPTH: Limits.MAX_JSON_DEPTH,
2651
+ MAX_JSON_NODES: Limits.MAX_JSON_NODES,
2652
+ /** Seats one authority room may hold. */
2653
+ MAX_PLAYERS: Limits.ROOM_MAX_CLIENTS,
2654
+ /** A physics room's output (§17.10): at most this many `phys` commands, and `phys` plus `physQ`
2655
+ * at most this much JSON, apart from the MAX_OUTPUT_BYTES the rest may hold, so the whole text is
2656
+ * at most MAX_PHYSICS_OUTPUT_BYTES. */
2657
+ MAX_PHYS_COMMANDS: 256,
2658
+ MAX_PHYS_BYTES: 32 * 1024,
2659
+ MAX_PHYSICS_OUTPUT_BYTES: 64 * 1024,
2660
+ /** `__genex_world()`'s text: base64 of the largest §17.7 world, 4 · ⌈(8 + 24 · 4096 + 36 · 512) / 3⌉. */
2661
+ MAX_WORLD_TEXT_CHARS: 155660
2662
+ };
2663
+ var TICK_RATES = [20, 30, 60, 120];
2664
+ var PHYSICS_ONLY_TICK_HZ = 120;
2665
+ var MAX_TICK_HZ = 120;
2666
+ var DEFAULT_TICK_HZ = 60;
2667
+ var TickOutFault = {
2668
+ NOT_JSON: "not_json",
2669
+ TOO_LARGE: "too_large",
2670
+ BAD_SHAPE: "bad_shape",
2671
+ NON_FINITE: "non_finite",
2672
+ OUT_OF_RANGE: "out_of_range",
2673
+ BAD_ID: "bad_id",
2674
+ DATA_TOO_LARGE: "data_too_large",
2675
+ TOO_MANY_ENTITIES: "too_many_entities",
2676
+ TOO_MANY_EVENTS: "too_many_events",
2677
+ RESERVED_EVENT: "reserved_event",
2678
+ BAD_CONFIG: "bad_config",
2679
+ /** More `phys` commands than MAX_PHYS_COMMANDS, or more `physQ` queries than `queriesPerStep`. */
2680
+ TOO_MANY_COMMANDS: "too_many_commands"
2681
+ };
2682
+ var InputUse = {
2683
+ /** A real input: the preset steps it, then `onInput` hears it. */
2684
+ STEP: "step",
2685
+ /** A starved tick: the preset steps the seat's last real input again; `onInput` never hears it. */
2686
+ REPEAT: "repeat",
2687
+ /** A late real input a repeat already stepped for: `onInput` hears it (its one-shots happen exactly
2688
+ * once), the preset does not step it again. Only an input whose buttons equal those the preset
2689
+ * last stepped is absorbed, so no press edge is ever lost or made. */
2690
+ ABSORB: "absorb"
2691
+ };
2692
+ function sanitizeInput(raw) {
2693
+ return {
2694
+ moveX: axis(raw.moveX),
2695
+ moveY: axis(raw.moveY),
2696
+ moveZ: axis(raw.moveZ),
2697
+ yaw: wrappedAngle(raw.yaw) ?? 0,
2698
+ pitch: wrappedAngle(raw.pitch) ?? 0,
2699
+ buttons: mask(raw.buttons)
2700
+ };
2701
+ }
2702
+ var Refusal = class {
2703
+ fault;
2704
+ path;
2705
+ constructor(fault, path) {
2706
+ this.fault = fault;
2707
+ this.path = path;
2708
+ }
2709
+ };
2710
+ var refuse = (fault, path) => {
2711
+ throw new Refusal(fault, path);
2712
+ };
2713
+ var FORBIDDEN_KEYS2 = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
2714
+ var isRecord3 = (v) => {
2715
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
2716
+ const proto = Object.getPrototypeOf(v);
2717
+ return proto === Object.prototype || proto === null;
2718
+ };
2719
+ var FLOAT32_FIELDS = ["x", "y", "z", "vx", "vy", "vz", "yaw"];
2720
+ var FIRSTPERSON_FLOAT32_FIELDS = ["pitch", "duck", "cap"];
2721
+ var VEHICLE_FLOAT32_FIELDS = ["spin", "fuel"];
2722
+ var SEAT_ID = /^[A-Za-z0-9_-]{1,64}$/;
2723
+ function finite(v, path) {
2724
+ if (typeof v !== "number") return refuse(TickOutFault.BAD_SHAPE, path);
2725
+ if (!Number.isFinite(v)) return refuse(TickOutFault.NON_FINITE, path);
2726
+ if (Math.abs(v) > MAX_ABS) return refuse(TickOutFault.OUT_OF_RANGE, path);
2727
+ return unsignedZero(v);
2728
+ }
2729
+ function integerIn2(v, max, path) {
2730
+ const n = finite(v, path);
2731
+ if (!Number.isInteger(n) || n < 0 || n > max) return refuse(TickOutFault.OUT_OF_RANGE, path);
2732
+ return n;
2733
+ }
2734
+ function launchTick(v, path) {
2735
+ if (typeof v !== "number") return refuse(TickOutFault.BAD_SHAPE, path);
2736
+ if (!Number.isFinite(v)) return refuse(TickOutFault.NON_FINITE, path);
2737
+ if (!Number.isInteger(v) || v < 0 || v > MAX_PROJECTILE_TICK) return refuse(TickOutFault.OUT_OF_RANGE, path);
2738
+ return unsignedZero(v);
2739
+ }
2740
+ function boundedString(v, max, path, fault) {
2741
+ if (typeof v !== "string" || v.length > max) return refuse(fault, path);
2742
+ return v;
2743
+ }
2744
+ function entityId(v, path) {
2745
+ const id = boundedString(v, AuthorityLimits.MAX_ENTITY_ID_CHARS, path, TickOutFault.BAD_ID);
2746
+ if (id.length === 0 || FORBIDDEN_KEYS2.has(id)) return refuse(TickOutFault.BAD_ID, path);
2747
+ return id;
2748
+ }
2749
+ function jsonValue(v, path, depth, budget) {
2750
+ budget.nodes += 1;
2751
+ if (depth > AuthorityLimits.MAX_JSON_DEPTH || budget.nodes > AuthorityLimits.MAX_JSON_NODES) {
2752
+ return refuse(TickOutFault.BAD_SHAPE, path);
2753
+ }
2754
+ if (v === null || typeof v === "boolean" || typeof v === "string") return v;
2755
+ if (typeof v === "number") {
2756
+ if (!Number.isFinite(v)) return refuse(TickOutFault.NON_FINITE, path);
2757
+ return v;
2758
+ }
2759
+ if (Array.isArray(v)) return v.map((item, i) => jsonValue(item, `${path}[${i}]`, depth + 1, budget));
2760
+ if (!isRecord3(v)) return refuse(TickOutFault.BAD_SHAPE, path);
2761
+ const out = {};
2762
+ for (const key of Object.keys(v)) {
2763
+ if (FORBIDDEN_KEYS2.has(key)) return refuse(TickOutFault.BAD_SHAPE, `${path}.${key}`);
2764
+ out[key] = jsonValue(v[key], `${path}.${key}`, depth + 1, budget);
2765
+ }
2766
+ return out;
2767
+ }
2768
+ function entityData(v, path) {
2769
+ if (typeof v !== "string") return refuse(TickOutFault.BAD_SHAPE, path);
2770
+ if (v.length > AuthorityLimits.MAX_ENTITY_DATA_BYTES) return refuse(TickOutFault.DATA_TOO_LARGE, path);
2771
+ if (v === "") return v;
2772
+ let parsed2;
2773
+ try {
2774
+ parsed2 = JSON.parse(v);
2775
+ } catch {
2776
+ return refuse(TickOutFault.BAD_SHAPE, path);
2777
+ }
2778
+ jsonValue(parsed2, path, 0, { nodes: 0 });
2779
+ return v;
2780
+ }
2781
+ function entityOut(v, path) {
2782
+ if (!isRecord3(v)) return refuse(TickOutFault.BAD_SHAPE, path);
2783
+ const out = { id: entityId(v.id, `${path}.id`) };
2784
+ if (v.kind !== void 0) {
2785
+ out.kind = boundedString(v.kind, AuthorityLimits.MAX_KIND_CHARS, `${path}.kind`, TickOutFault.BAD_SHAPE);
2786
+ }
2787
+ if (v.owner !== void 0) {
2788
+ out.owner = boundedString(v.owner, AuthorityLimits.MAX_SESSION_ID_CHARS, `${path}.owner`, TickOutFault.BAD_SHAPE);
2789
+ }
2790
+ for (const key of FLOAT32_FIELDS) {
2791
+ if (v[key] !== void 0) out[key] = unsignedZero(Math.fround(finite(v[key], `${path}.${key}`)));
2792
+ }
2793
+ if (v.hp !== void 0) out.hp = finite(v.hp, `${path}.hp`);
2794
+ if (v.flags !== void 0) out.flags = integerIn2(v.flags, MASK_LIMIT - 1, `${path}.flags`);
2795
+ if (v.epoch !== void 0) out.epoch = integerIn2(v.epoch, AuthorityLimits.MAX_EPOCH, `${path}.epoch`);
2796
+ if (v.data !== void 0) out.data = entityData(v.data, `${path}.data`);
2797
+ if (v.t0 !== void 0) out.t0 = launchTick(v.t0, `${path}.t0`);
2798
+ if (v.g !== void 0) {
2799
+ const g = finite(v.g, `${path}.g`);
2800
+ if (g < 0 || g > MAX_PROJECTILE_G) return refuse(TickOutFault.OUT_OF_RANGE, `${path}.g`);
2801
+ out.g = unsignedZero(Math.fround(g));
2802
+ }
2803
+ if (v.src !== void 0) {
2804
+ const src = boundedString(v.src, AuthorityLimits.MAX_SESSION_ID_CHARS, `${path}.src`, TickOutFault.BAD_SHAPE);
2805
+ if (src !== "" && !SEAT_ID.test(src)) return refuse(TickOutFault.BAD_SHAPE, `${path}.src`);
2806
+ out.src = src;
2807
+ }
2808
+ for (const key of FIRSTPERSON_FLOAT32_FIELDS) {
2809
+ if (v[key] !== void 0) out[key] = unsignedZero(Math.fround(finite(v[key], `${path}.${key}`)));
2810
+ }
2811
+ if (v.mstate !== void 0) out.mstate = integerIn2(v.mstate, MASK_LIMIT - 1, `${path}.mstate`);
2812
+ for (const key of VEHICLE_FLOAT32_FIELDS) {
2813
+ if (v[key] !== void 0) out[key] = unsignedZero(Math.fround(finite(v[key], `${path}.${key}`)));
2814
+ }
2815
+ return out;
2816
+ }
2817
+ function eventOut(v, path) {
2818
+ if (!isRecord3(v)) return refuse(TickOutFault.BAD_SHAPE, path);
2819
+ const type = boundedString(v.type, AuthorityLimits.MAX_EVENT_TYPE_CHARS, `${path}.type`, TickOutFault.BAD_SHAPE);
2820
+ if (type.length === 0) return refuse(TickOutFault.BAD_SHAPE, `${path}.type`);
2821
+ if (isReservedEventType(type)) return refuse(TickOutFault.RESERVED_EVENT, `${path}.type`);
2822
+ const out = { type };
2823
+ if (v.data !== void 0) {
2824
+ const data = jsonValue(v.data, `${path}.data`, 0, { nodes: 0 });
2825
+ if (JSON.stringify(data).length > AuthorityLimits.MAX_EVENT_BYTES) return refuse(TickOutFault.TOO_LARGE, `${path}.data`);
2826
+ out.data = data;
2827
+ }
2828
+ if (v.to !== void 0) {
2829
+ const to = boundedString(v.to, AuthorityLimits.MAX_SESSION_ID_CHARS, `${path}.to`, TickOutFault.BAD_SHAPE);
2830
+ if (to.length === 0) return refuse(TickOutFault.BAD_SHAPE, `${path}.to`);
2831
+ out.to = to;
2832
+ }
2833
+ return out;
2834
+ }
2835
+ function list(v, path) {
2836
+ if (v === void 0) return [];
2837
+ if (!Array.isArray(v)) return refuse(TickOutFault.BAD_SHAPE, path);
2838
+ return v;
2839
+ }
2840
+ function parsed(raw, maxChars) {
2841
+ if (typeof raw !== "string") return raw;
2842
+ if (raw.length > maxChars) return refuse(TickOutFault.TOO_LARGE, "$");
2843
+ try {
2844
+ return JSON.parse(raw);
2845
+ } catch {
2846
+ return refuse(TickOutFault.NOT_JSON, "$");
2847
+ }
2848
+ }
2849
+ var finiteOnly = (path) => (_key, v) => typeof v === "number" && !Number.isFinite(v) ? refuse(TickOutFault.NON_FINITE, path) : v;
2850
+ function commandList(v, path, max) {
2851
+ const items = list(v, path);
2852
+ if (items.length > max) return refuse(TickOutFault.TOO_MANY_COMMANDS, path);
2853
+ items.forEach((item, i) => {
2854
+ if (!isRecord3(item) || Object.keys(item).length !== 1) refuse(TickOutFault.BAD_SHAPE, `${path}[${i}]`);
2855
+ });
2856
+ const text = JSON.stringify(items, finiteOnly(path));
2857
+ return { text, items: JSON.parse(text) };
2858
+ }
2859
+ function physicsOut(value2, physics) {
2860
+ const out = {};
2861
+ let bytes = 0;
2862
+ if (value2.phys !== void 0) {
2863
+ const phys = commandList(value2.phys, "$.phys", AuthorityLimits.MAX_PHYS_COMMANDS);
2864
+ bytes += phys.text.length;
2865
+ out.phys = phys.items;
2866
+ }
2867
+ if (value2.physQ !== void 0) {
2868
+ if (physics.queries === null) return refuse(TickOutFault.BAD_SHAPE, "$.physQ");
2869
+ const physQ = commandList(value2.physQ, "$.physQ", physics.queries);
2870
+ bytes += physQ.text.length;
2871
+ out.physQ = physQ.items;
2872
+ }
2873
+ if (bytes > AuthorityLimits.MAX_PHYS_BYTES) return refuse(TickOutFault.TOO_LARGE, "$.phys");
2874
+ const { phys: _phys, physQ: _physQ, ...rest } = value2;
2875
+ if (JSON.stringify(rest).length > AuthorityLimits.MAX_OUTPUT_BYTES) return refuse(TickOutFault.TOO_LARGE, "$");
2876
+ return out;
2877
+ }
2878
+ function checkBodyEntities(value2, out, bodies) {
2879
+ const raw = value2.up;
2880
+ const removed = new Set(out.rm);
2881
+ out.up.forEach((e, i) => {
2882
+ if (!bodies.has(e.id) || removed.has(e.id)) return;
2883
+ const fields = raw?.[i];
2884
+ for (const field of HOST_BODY_FIELDS) {
2885
+ if (fields[field] !== void 0) refuse(TickOutFault.BAD_SHAPE, `$.up[${i}].${field}`);
2886
+ }
2887
+ if ((e.t0 ?? 0) > 0) refuse(TickOutFault.BAD_SHAPE, `$.up[${i}].t0`);
2888
+ });
2889
+ }
2890
+ function tickOutOf(value2, physics) {
2891
+ if (!isRecord3(value2)) return refuse(TickOutFault.BAD_SHAPE, "$");
2892
+ const up = list(value2.up, "$.up");
2893
+ const rm = list(value2.rm, "$.rm");
2894
+ const ev = list(value2.ev, "$.ev");
2895
+ if (up.length > AuthorityLimits.MAX_ENTITIES) return refuse(TickOutFault.TOO_MANY_ENTITIES, "$.up");
2896
+ if (rm.length > AuthorityLimits.MAX_ENTITIES) return refuse(TickOutFault.TOO_MANY_ENTITIES, "$.rm");
2897
+ if (ev.length > AuthorityLimits.MAX_EVENTS_PER_TICK) return refuse(TickOutFault.TOO_MANY_EVENTS, "$.ev");
2898
+ const out = {
2899
+ up: up.map((e, i) => entityOut(e, `$.up[${i}]`)),
2900
+ rm: rm.map((id, i) => entityId(id, `$.rm[${i}]`)),
2901
+ ev: ev.map((e, i) => eventOut(e, `$.ev[${i}]`))
2902
+ };
2903
+ if (value2.lifts !== void 0) out.lifts = liftStartsOut(value2.lifts);
2904
+ if (physics === void 0) return out;
2905
+ checkBodyEntities(value2, out, physics.bodies);
2906
+ return { ...out, ...physicsOut(value2, physics) };
2907
+ }
2908
+ var LIFT_START_MAX = 2 ** 31 - 1;
2909
+ function liftStartsOut(v) {
2910
+ const path = "$.lifts";
2911
+ if (!isRecord3(v)) return refuse(TickOutFault.BAD_SHAPE, path);
2912
+ const entries = Object.entries(v);
2913
+ if (entries.length > LIFTS_MAX) return refuse(TickOutFault.BAD_SHAPE, path);
2914
+ const out = {};
2915
+ for (const [id, start] of entries) {
2916
+ if (!isLiftId(id)) return refuse(TickOutFault.BAD_SHAPE, path);
2917
+ if (typeof start !== "number" || !Number.isInteger(start) || start < 0 || start > LIFT_START_MAX) return refuse(TickOutFault.BAD_SHAPE, `${path}.${id}`);
2918
+ out[id] = start;
2919
+ }
2920
+ return out;
2921
+ }
2922
+ function entityCountAfter(room, out) {
2923
+ const added = new Set(out.up.filter((e) => !room.has(e.id)).map((e) => e.id));
2924
+ const updated = new Set(out.up.map((e) => e.id));
2925
+ const removed = new Set(out.rm.filter((id) => room.has(id) && !updated.has(id)));
2926
+ return room.size + added.size - removed.size;
2927
+ }
2928
+ function checkProjectileOwners(room, out) {
2929
+ const merged = /* @__PURE__ */ new Map();
2930
+ out.up.forEach((e, i) => {
2931
+ const current = merged.get(e.id) ?? room?.get?.(e.id);
2932
+ const next = { t0: e.t0 ?? current?.t0 ?? 0, owner: e.owner ?? current?.owner ?? "" };
2933
+ if (next.t0 > 0 && next.owner !== "") refuse(TickOutFault.BAD_SHAPE, `$.up[${i}].owner`);
2934
+ merged.set(e.id, next);
2935
+ });
2936
+ }
2937
+ function roomTickOutOf(value2, room, physics) {
2938
+ const out = tickOutOf(value2, physics);
2939
+ if (room !== void 0 && entityCountAfter(room, out) > AuthorityLimits.MAX_ROOM_ENTITIES) {
2940
+ return refuse(TickOutFault.TOO_MANY_ENTITIES, "$.up");
2941
+ }
2942
+ checkProjectileOwners(room, out);
2943
+ return out;
2944
+ }
2945
+ var EMPTY_ROOM = { size: 0, has: () => false };
2946
+ function presetOf(v, path) {
2947
+ if (v === null || v === void 0) return null;
2948
+ if (!isRecord3(v) || !isPresetName(v.name)) return refuse(TickOutFault.BAD_CONFIG, path);
2949
+ if (!presetParamsValid(v.name, v.params)) return refuse(TickOutFault.BAD_CONFIG, `${path}.params`);
2950
+ if (definitionOf(v.name)?.world === true && v.params?.world === void 0) {
2951
+ return refuse(TickOutFault.BAD_CONFIG, `${path}.params.world`);
2952
+ }
2953
+ return v.params === void 0 ? { name: v.name } : { name: v.name, params: { ...v.params } };
2954
+ }
2955
+ function configOf(v, admission) {
2956
+ if (!isRecord3(v)) return refuse(TickOutFault.BAD_CONFIG, "$.config");
2957
+ const tickHz = TICK_RATES.find((hz) => hz === v.tickHz);
2958
+ if (tickHz === void 0) return refuse(TickOutFault.BAD_CONFIG, "$.config.tickHz");
2959
+ const maxPlayers = v.maxPlayers;
2960
+ const playersOk = typeof maxPlayers === "number" && Number.isInteger(maxPlayers) && maxPlayers >= 1 && maxPlayers <= AuthorityLimits.MAX_PLAYERS;
2961
+ if (!playersOk) return refuse(TickOutFault.BAD_CONFIG, "$.config.maxPlayers");
2962
+ let lagComp = null;
2963
+ if (v.lagComp !== null && v.lagComp !== void 0) {
2964
+ if (!isRecord3(v.lagComp) || !isMask(v.lagComp.buttons)) return refuse(TickOutFault.BAD_CONFIG, "$.config.lagComp");
2965
+ lagComp = { buttons: v.lagComp.buttons };
2966
+ }
2967
+ const preset = presetOf(v.preset, "$.config.preset");
2968
+ const physics = checkResolvedPhysics(v.physics, preset, admission?.ceiling ?? null);
2969
+ if (!physics.ok) return refuse(TickOutFault.BAD_CONFIG, physics.path);
2970
+ if (tickHz === PHYSICS_ONLY_TICK_HZ && physics.value === null) return refuse(TickOutFault.BAD_CONFIG, "$.config.tickHz");
2971
+ const contacts = contactsOf(v.contacts, preset);
2972
+ const inputs = inputsOf(v.inputs, contacts);
2973
+ const lifts = liftsOf(v.lifts, preset, tickHz);
2974
+ return {
2975
+ tickHz,
2976
+ maxPlayers,
2977
+ preset,
2978
+ lagComp,
2979
+ physics: physics.value,
2980
+ ...contacts === null ? {} : { contacts },
2981
+ ...inputs === null ? {} : { inputs },
2982
+ ...lifts === null ? {} : { lifts }
2983
+ };
2984
+ }
2985
+ function inputsOf(v, contacts) {
2986
+ try {
2987
+ return resolveInputConfig(v, contacts?.preset ?? null);
2988
+ } catch (e) {
2989
+ if (e instanceof InputConfigError) return refuse(TickOutFault.BAD_CONFIG, e.path);
2990
+ throw e;
2991
+ }
2992
+ }
2993
+ function liftsOf(v, preset, tickHz) {
2994
+ if (v === null || v === void 0) return null;
2995
+ const params = preset === null ? null : { name: preset.name, params: resolvePresetParams(preset.name, preset.params) };
2996
+ try {
2997
+ return resolveLifts(v, params, tickHz);
2998
+ } catch (e) {
2999
+ if (e instanceof LiftsConfigError) return refuse(TickOutFault.BAD_CONFIG, e.message);
3000
+ throw e;
3001
+ }
3002
+ }
3003
+ function contactsOf(v, preset) {
3004
+ if (v === null || v === void 0) return null;
3005
+ const params = preset === null ? null : { name: preset.name, params: resolvePresetParams(preset.name, preset.params) };
3006
+ try {
3007
+ return resolveContactConfig(v, params);
3008
+ } catch (e) {
3009
+ if (e instanceof ContactConfigError) return refuse(TickOutFault.BAD_CONFIG, e.path);
3010
+ throw e;
3011
+ }
3012
+ }
3013
+ function gate(raw, build, physics) {
3014
+ try {
3015
+ const maxChars = physics ? AuthorityLimits.MAX_PHYSICS_OUTPUT_BYTES : AuthorityLimits.MAX_OUTPUT_BYTES;
3016
+ const value2 = build(parsed(raw, maxChars));
3017
+ if (!physics && typeof raw !== "string" && JSON.stringify(value2).length > AuthorityLimits.MAX_OUTPUT_BYTES) {
3018
+ return { ok: false, fault: TickOutFault.TOO_LARGE, path: "$" };
3019
+ }
3020
+ return { ok: true, value: value2 };
3021
+ } catch (err) {
3022
+ if (err instanceof Refusal) return { ok: false, fault: err.fault, path: err.path };
3023
+ return { ok: false, fault: TickOutFault.BAD_SHAPE, path: "$" };
3024
+ }
3025
+ }
3026
+ function validateTickOut(raw, room, physics) {
3027
+ const wire = physics === void 0 ? void 0 : { queries: physics.queriesPerStep, bodies: physics.bodies };
3028
+ return gate(raw, (value2) => roomTickOutOf(value2, room, wire), wire !== void 0);
3029
+ }
3030
+ var NO_BODIES = { has: () => false };
3031
+ function validateInitOut(raw, physics) {
3032
+ const wire = physics === void 0 ? void 0 : { queries: null, bodies: NO_BODIES };
3033
+ return gate(
3034
+ raw,
3035
+ (value2) => {
3036
+ const out = roomTickOutOf(value2, EMPTY_ROOM, wire);
3037
+ return { ...out, config: configOf(value2.config, physics) };
3038
+ },
3039
+ wire !== void 0
3040
+ );
3041
+ }
3042
+
3043
+ // src/authority/fast-transport.ts
3044
+ var FAST_VERSION = 1;
3045
+ var FAST_MAX_DATAGRAM_BYTES = 1200;
3046
+ var FAST_KEY_BYTES = 8;
3047
+ var INPUT_REDUNDANCY_MS = 170;
3048
+ var INPUT_REDUNDANCY_MIN = 3;
3049
+ var INPUT_REDUNDANCY_MAX = 32;
3050
+ var FAST_MAX_ACTIONS = 8;
3051
+ var FAST_GAP_FILL_MS = 250;
3052
+ var FAST_SEQ_AHEAD_MS = 5e3;
3053
+ var FAST_SNAPSHOT_HISTORY = 32;
3054
+ var FAST_MAX_FRAGMENTS = 32;
3055
+ var FAST_REASSEMBLY_SLOTS = 4;
3056
+ var FAST_MAX_RECORD_BYTES = 1024;
3057
+ var FAST_HANDSHAKE_MS = 1e3;
3058
+ var FAST_STALL_FALLBACK_MS = 2e3;
3059
+ var FastMessage = {
3060
+ /** Server → client: `{ sid, key, url }`, the seat's binding. */
3061
+ Bind: "__fast",
3062
+ /** Client → server: `{ last }`, the last seq the client sent by datagram; its inputs go by
3063
+ * `room.input()` from now on. */
3064
+ Off: "__fast:off"
3065
+ };
3066
+ var FastKind = { Uplink: 1, Snapshot: 2 };
3067
+ var FastFault = {
3068
+ Oversized: "oversized",
3069
+ Truncated: "truncated",
3070
+ Trailing: "trailing",
3071
+ Version: "version",
3072
+ Kind: "kind",
3073
+ Session: "session",
3074
+ Key: "key",
3075
+ Count: "count",
3076
+ Fresh: "fresh",
3077
+ Actions: "actions",
3078
+ Flags: "flags",
3079
+ Order: "order",
3080
+ Field: "field",
3081
+ Ahead: "ahead",
3082
+ Fragment: "fragment",
3083
+ Mismatch: "mismatch",
3084
+ Duplicate: "duplicate",
3085
+ Keyframe: "keyframe",
3086
+ Body: "body",
3087
+ Base: "base",
3088
+ Stale: "stale"
3089
+ };
3090
+ var AcceptKind = {
3091
+ /** First sent in the datagram that brought it. */
3092
+ Fresh: "fresh",
3093
+ /** Sent first in an earlier datagram that was lost; carried again by redundancy or as an action. */
3094
+ Recovered: "recovered",
3095
+ /** No datagram the server got carried it: the last input held stands in for it. */
3096
+ Fill: "fill"
3097
+ };
3098
+ var UPLINK_HEADER = 29;
3099
+ var SNAPSHOT_HEADER = 29;
3100
+ var PAYLOAD = 24;
3101
+ var SLOT_SAME = 1;
3102
+ var SLOT_TICK_SAME = 2;
3103
+ var SLOT_TICK_NEXT = 4;
3104
+ var SLOT_FLAGS = SLOT_SAME | SLOT_TICK_SAME | SLOT_TICK_NEXT;
3105
+ var BACK_NONE = 255;
3106
+ var ACTION_HEAD = 10;
3107
+ var SLOT_MAX = 4 + PAYLOAD;
3108
+ var UINT32_MAX = 4294967295;
3109
+ var TICK_MOD = 65536;
3110
+ var KEYFRAME = 1;
3111
+ var NEUTRAL = { moveX: 0, moveY: 0, moveZ: 0, yaw: 0, pitch: 0, buttons: 0 };
3112
+ var ok = (value2) => ({ ok: true, value: value2 });
3113
+ var fail = (fault) => ({ ok: false, fault });
3114
+ var ticksOf = (ms, tickHz) => Math.ceil(ms * (Number.isFinite(tickHz) && tickHz > 0 ? tickHz : 0) / 1e3);
3115
+ var isUint = (v, max) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= max;
3116
+ function inputRedundancyTicks(tickHz) {
3117
+ return Math.min(INPUT_REDUNDANCY_MAX, Math.max(INPUT_REDUNDANCY_MIN, ticksOf(INPUT_REDUNDANCY_MS, tickHz)));
3118
+ }
3119
+ function uplinkMaxBytes(tickHz) {
3120
+ return UPLINK_HEADER + FAST_MAX_ACTIONS * (ACTION_HEAD + PAYLOAD) + inputRedundancyTicks(tickHz) * SLOT_MAX;
3121
+ }
3122
+ var gapFillMax = (tickHz) => ticksOf(FAST_GAP_FILL_MS, tickHz);
3123
+ var seqAheadMax = (tickHz) => ticksOf(FAST_SEQ_AHEAD_MS, tickHz);
3124
+ function toHex(bytes) {
3125
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
3126
+ }
3127
+ function fromHex(hex) {
3128
+ if (!/^(?:[0-9a-fA-F]{2})*$/.test(hex)) return new Uint8Array(0);
3129
+ return Uint8Array.from(hex.match(/../g) ?? [], (h) => Number.parseInt(h, 16));
3130
+ }
3131
+ var isKey = (key) => typeof key === "string" && key.length === 2 * FAST_KEY_BYTES && /^[0-9a-f]+$/.test(key);
3132
+ var isPress = (previous, input) => (input.buttons & ~(previous?.buttons ?? 0)) !== 0;
3133
+ var validPayload = (p) => [p.moveX, p.moveY, p.moveZ, p.yaw, p.pitch].every((x) => typeof x === "number" && Number.isFinite(Math.fround(x))) && isUint(p.buttons, UINT32_MAX);
3134
+ var validInput = (s) => isUint(s.seq, UINT32_MAX) && s.seq >= 1 && isUint(s.tick, TICK_MOD - 1) && isUint(s.render, UINT32_MAX) && validPayload(s.input);
3135
+ var windowRender = (window) => window.reduce((m, s) => Math.max(m, s.render), 0);
3136
+ var backOf = (render, s) => s.render === 0 || render - s.render >= BACK_NONE ? BACK_NONE : render - s.render;
3137
+ var renderOf = (render, back) => back === BACK_NONE || render - back <= 0 ? 0 : render - back;
3138
+ function validUplink(u) {
3139
+ const { window, actions } = u;
3140
+ if (!isUint(u.sid, UINT32_MAX) || !isKey(u.key) || !isUint(u.ackSnap, UINT32_MAX)) return false;
3141
+ if (window.length < 1 || window.length > INPUT_REDUNDANCY_MAX || actions.length > FAST_MAX_ACTIONS) return false;
3142
+ if (!isUint(u.fresh, window.length) || u.fresh < 1) return false;
3143
+ if (![...actions, ...window].every(validInput)) return false;
3144
+ const contiguous = window.every((s, i) => i === 0 || s.seq === window[i - 1].seq + 1);
3145
+ const ascending = actions.every((s, i) => s.seq < (i + 1 < actions.length ? actions[i + 1].seq : window[0].seq));
3146
+ return contiguous && ascending;
3147
+ }
3148
+ function writePayload(view, at, p) {
3149
+ view.setFloat32(at, p.moveX, true);
3150
+ view.setFloat32(at + 4, p.moveY, true);
3151
+ view.setFloat32(at + 8, p.moveZ, true);
3152
+ view.setFloat32(at + 12, p.yaw, true);
3153
+ view.setFloat32(at + 16, p.pitch, true);
3154
+ view.setUint32(at + 20, p.buttons, true);
3155
+ return at + PAYLOAD;
3156
+ }
3157
+ function readPayload(view, at) {
3158
+ return {
3159
+ moveX: view.getFloat32(at, true),
3160
+ moveY: view.getFloat32(at + 4, true),
3161
+ moveZ: view.getFloat32(at + 8, true),
3162
+ yaw: view.getFloat32(at + 12, true),
3163
+ pitch: view.getFloat32(at + 16, true),
3164
+ buttons: view.getUint32(at + 20, true)
3165
+ };
3166
+ }
3167
+ var payloadBits = (p) => {
3168
+ const b = new Uint8Array(PAYLOAD);
3169
+ writePayload(new DataView(b.buffer), 0, p);
3170
+ return toHex(b);
3171
+ };
3172
+ function slotFlags(previous, s) {
3173
+ if (previous === null) return 0;
3174
+ let flags = payloadBits(previous.input) === payloadBits(s.input) ? SLOT_SAME : 0;
3175
+ if (s.tick === previous.tick) flags |= SLOT_TICK_SAME;
3176
+ else if (s.tick === (previous.tick + 1) % TICK_MOD) flags |= SLOT_TICK_NEXT;
3177
+ return flags;
3178
+ }
3179
+ function encodeUplink(u) {
3180
+ if (!validUplink(u)) return fail(FastFault.Field);
3181
+ const out = new Uint8Array(FAST_MAX_DATAGRAM_BYTES);
3182
+ const view = new DataView(out.buffer);
3183
+ out[0] = FAST_VERSION;
3184
+ out[1] = FastKind.Uplink;
3185
+ view.setUint32(2, u.sid, true);
3186
+ out.set(fromHex(u.key), 6);
3187
+ view.setUint32(14, u.ackSnap, true);
3188
+ view.setUint32(18, u.window[u.window.length - 1].seq, true);
3189
+ out[22] = u.window.length;
3190
+ out[23] = u.fresh;
3191
+ out[24] = u.actions.length;
3192
+ const render = windowRender(u.window);
3193
+ view.setUint32(25, render, true);
3194
+ let at = UPLINK_HEADER;
3195
+ for (const a of u.actions) {
3196
+ view.setUint32(at, a.seq, true);
3197
+ view.setUint16(at + 4, a.tick, true);
3198
+ view.setUint32(at + 6, a.render, true);
3199
+ at = writePayload(view, at + ACTION_HEAD, a.input);
3200
+ }
3201
+ u.window.forEach((s, i) => {
3202
+ const flags = slotFlags(i === 0 ? null : u.window[i - 1], s);
3203
+ out[at] = flags;
3204
+ at += 1;
3205
+ if ((flags & (SLOT_TICK_SAME | SLOT_TICK_NEXT)) === 0) {
3206
+ view.setUint16(at, s.tick, true);
3207
+ at += 2;
3208
+ }
3209
+ out[at] = backOf(render, s);
3210
+ at += 1;
3211
+ if ((flags & SLOT_SAME) === 0) at = writePayload(view, at, s.input);
3212
+ });
3213
+ return ok(out.slice(0, at));
3214
+ }
3215
+ function frame(bytes, kind, header) {
3216
+ if (bytes.length > FAST_MAX_DATAGRAM_BYTES) return FastFault.Oversized;
3217
+ if (bytes.length < 2) return FastFault.Truncated;
3218
+ if (bytes[0] !== FAST_VERSION) return FastFault.Version;
3219
+ if (bytes[1] !== kind) return FastFault.Kind;
3220
+ return bytes.length < header ? FastFault.Truncated : null;
3221
+ }
3222
+ function decodeUplink(bytes, binding) {
3223
+ const framed = frame(bytes, FastKind.Uplink, UPLINK_HEADER);
3224
+ if (framed !== null) return fail(framed);
3225
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3226
+ const sid = view.getUint32(2, true);
3227
+ if (sid !== binding.sid) return fail(FastFault.Session);
3228
+ const key = toHex(bytes.subarray(6, 6 + FAST_KEY_BYTES));
3229
+ if (key !== binding.key) return fail(FastFault.Key);
3230
+ const newest = view.getUint32(18, true);
3231
+ const count = bytes[22];
3232
+ const fresh = bytes[23];
3233
+ const actionCount = bytes[24];
3234
+ if (count < 1 || count > INPUT_REDUNDANCY_MAX || newest < count) return fail(FastFault.Count);
3235
+ if (fresh < 1 || fresh > count) return fail(FastFault.Fresh);
3236
+ if (actionCount > FAST_MAX_ACTIONS) return fail(FastFault.Actions);
3237
+ const first = newest - count + 1;
3238
+ const read = readBody(view, actionCount, count, first, view.getUint32(25, true));
3239
+ if (!read.ok) return read;
3240
+ return ok({ sid, key, ackSnap: view.getUint32(14, true), fresh, actions: read.value.actions, window: read.value.window });
3241
+ }
3242
+ function readBody(view, actionCount, count, first, render) {
3243
+ let at = UPLINK_HEADER;
3244
+ const actions = [];
3245
+ for (let i = 0; i < actionCount; i += 1) {
3246
+ if (at + ACTION_HEAD + PAYLOAD > view.byteLength) return fail(FastFault.Truncated);
3247
+ const seq = view.getUint32(at, true);
3248
+ if (seq < 1 || seq >= first || i > 0 && seq <= actions[i - 1].seq) return fail(FastFault.Order);
3249
+ actions.push({ seq, tick: view.getUint16(at + 4, true), render: view.getUint32(at + 6, true), input: readPayload(view, at + ACTION_HEAD) });
3250
+ at += ACTION_HEAD + PAYLOAD;
3251
+ }
3252
+ const window = [];
3253
+ for (let i = 0; i < count; i += 1) {
3254
+ const slot = readSlot(view, at, i === 0 ? null : window[i - 1], first + i, render);
3255
+ if (!slot.ok) return slot;
3256
+ window.push(slot.value.input);
3257
+ at = slot.value.at;
3258
+ }
3259
+ return at === view.byteLength ? ok({ actions, window }) : fail(FastFault.Trailing);
3260
+ }
3261
+ function readSlot(view, start, previous, seq, render) {
3262
+ if (start >= view.byteLength) return fail(FastFault.Truncated);
3263
+ const flags = view.getUint8(start);
3264
+ const tickBits = flags & (SLOT_TICK_SAME | SLOT_TICK_NEXT);
3265
+ const badFlags = (flags & ~SLOT_FLAGS) !== 0 || tickBits === (SLOT_TICK_SAME | SLOT_TICK_NEXT) || previous === null && flags !== 0;
3266
+ if (badFlags) return fail(FastFault.Flags);
3267
+ let at = start + 1;
3268
+ let tick = 0;
3269
+ if (previous !== null && tickBits === SLOT_TICK_SAME) tick = previous.tick;
3270
+ else if (previous !== null && tickBits === SLOT_TICK_NEXT) tick = (previous.tick + 1) % TICK_MOD;
3271
+ else {
3272
+ if (at + 2 > view.byteLength) return fail(FastFault.Truncated);
3273
+ tick = view.getUint16(at, true);
3274
+ at += 2;
3275
+ }
3276
+ if (at + 1 > view.byteLength) return fail(FastFault.Truncated);
3277
+ const slotRender = renderOf(render, view.getUint8(at));
3278
+ at += 1;
3279
+ if (previous !== null && (flags & SLOT_SAME) !== 0) return ok({ input: { seq, tick, render: slotRender, input: { ...previous.input } }, at });
3280
+ if (at + PAYLOAD > view.byteLength) return fail(FastFault.Truncated);
3281
+ return ok({ input: { seq, tick, render: slotRender, input: readPayload(view, at) }, at: at + PAYLOAD });
3282
+ }
3283
+ var wholeMs = (x) => Math.floor(x + 0.5);
3284
+ function renderStamp(serverNow, renderDelay, rtt) {
3285
+ if (serverNow === null || !Number.isFinite(serverNow)) return 0;
3286
+ const now = Math.min(UINT32_MAX, Math.max(0, wholeMs(serverNow)));
3287
+ const lead = renderDelay + (Number.isFinite(rtt) ? rtt : 0) / 2;
3288
+ const delta = Number.isFinite(lead) ? Math.min(65535, Math.max(0, wholeMs(lead))) : 0;
3289
+ return now > delta ? now - delta : 0;
3290
+ }
3291
+ function uplinkFor(sent, o) {
3292
+ const window = sent.slice(-inputRedundancyTicks(o.tickHz));
3293
+ const first = window.length > 0 ? window[0].seq : Number.POSITIVE_INFINITY;
3294
+ const actions = sent.filter((s, i) => s.seq > o.inputAck && s.seq < first && isPress(i > 0 ? sent[i - 1].input : null, s.input)).slice(0, FAST_MAX_ACTIONS);
3295
+ return { sid: o.sid, key: o.key, ackSnap: o.ackSnap, fresh: Math.min(o.fresh, window.length), actions, window };
3296
+ }
3297
+ function acceptUplink(seat, u, o) {
3298
+ const newest = u.window[u.window.length - 1].seq;
3299
+ if (newest - seat.last > seqAheadMax(o.tickHz)) return { seat, accepted: [], skipped: 0, fault: FastFault.Ahead };
3300
+ const firstFresh = newest - u.fresh + 1;
3301
+ const accepted = [];
3302
+ let last = seat.last;
3303
+ let held = seat.input;
3304
+ let skipped = 0;
3305
+ for (const s of [...u.actions, ...u.window]) {
3306
+ if (s.seq <= last) continue;
3307
+ const gap = s.seq - last - 1;
3308
+ if (gap > gapFillMax(o.tickHz)) skipped += gap;
3309
+ else {
3310
+ for (let g = last + 1; g < s.seq; g += 1) {
3311
+ accepted.push({ seq: g, tick: ((s.tick - (s.seq - g)) % TICK_MOD + TICK_MOD) % TICK_MOD, render: s.render, input: { ...held ?? NEUTRAL }, kind: AcceptKind.Fill });
3312
+ }
3313
+ }
3314
+ const input = sanitizeInput({ ...s.input });
3315
+ accepted.push({ seq: s.seq, tick: s.tick, render: s.render, input, kind: s.seq >= firstFresh ? AcceptKind.Fresh : AcceptKind.Recovered });
3316
+ last = s.seq;
3317
+ held = input;
3318
+ }
3319
+ const ackSnap = u.ackSnap > seat.ackSnap && u.ackSnap <= o.snapSent ? u.ackSnap : seat.ackSnap;
3320
+ if (accepted.length === 0 && ackSnap === seat.ackSnap) return { seat, accepted, skipped };
3321
+ return { seat: { last, input: held, ackSnap }, accepted, skipped };
3322
+ }
3323
+ var sameBytes = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
3324
+ var byKey = (records) => [...records].sort((a, b) => a.key - b.key);
3325
+ function diffSnapshot(base, next) {
3326
+ if (base === null) return { removed: [], records: byKey(next) };
3327
+ const old = new Map(base.map((r) => [r.key, r.bytes]));
3328
+ const keys = new Set(next.map((r) => r.key));
3329
+ const removed = base.filter((r) => !keys.has(r.key)).map((r) => r.key).sort((a, b) => a - b);
3330
+ const changed = next.filter((r) => {
3331
+ const before = old.get(r.key);
3332
+ return before === void 0 || !sameBytes(before, r.bytes);
3333
+ });
3334
+ return { removed, records: byKey(changed) };
3335
+ }
3336
+ var deltaBase = (held, ackSnap) => ackSnap > 0 && held.includes(ackSnap) ? ackSnap : 0;
3337
+ function bodyBytes(body) {
3338
+ const valid = body.removed.every((k) => isUint(k, 65535)) && body.records.every((r) => isUint(r.key, 65535) && r.bytes.length <= FAST_MAX_RECORD_BYTES);
3339
+ if (!valid || body.removed.length > 65535 || body.records.length > 65535) return null;
3340
+ const size = 4 + 2 * body.removed.length + body.records.reduce((n, r) => n + 4 + r.bytes.length, 0);
3341
+ const out = new Uint8Array(size);
3342
+ const view = new DataView(out.buffer);
3343
+ view.setUint16(0, body.removed.length, true);
3344
+ let at = 2;
3345
+ for (const k of body.removed) {
3346
+ view.setUint16(at, k, true);
3347
+ at += 2;
3348
+ }
3349
+ view.setUint16(at, body.records.length, true);
3350
+ at += 2;
3351
+ for (const r of body.records) {
3352
+ view.setUint16(at, r.key, true);
3353
+ view.setUint16(at + 2, r.bytes.length, true);
3354
+ out.set(r.bytes, at + 4);
3355
+ at += 4 + r.bytes.length;
3356
+ }
3357
+ return out;
3358
+ }
3359
+ var validHead = (h) => isUint(h.sid, UINT32_MAX) && isUint(h.snap, UINT32_MAX) && h.snap >= 1 && isUint(h.base, UINT32_MAX) && h.base < h.snap && isUint(h.tick, UINT32_MAX) && isUint(h.inputAck, UINT32_MAX) && isUint(h.sent, UINT32_MAX);
3360
+ function encodeSnapshot(head, body) {
3361
+ const bytes = bodyBytes(body);
3362
+ const keyframeBody = head.base !== 0 || body.removed.length === 0;
3363
+ if (bytes === null || !validHead(head) || !keyframeBody) return fail(FastFault.Field);
3364
+ const room = FAST_MAX_DATAGRAM_BYTES - SNAPSHOT_HEADER;
3365
+ const frags = Math.max(1, Math.ceil(bytes.length / room));
3366
+ if (frags > FAST_MAX_FRAGMENTS) return fail(FastFault.Oversized);
3367
+ const out = [];
3368
+ for (let i = 0; i < frags; i += 1) {
3369
+ const chunk = bytes.subarray(i * room, (i + 1) * room);
3370
+ const d = new Uint8Array(SNAPSHOT_HEADER + chunk.length);
3371
+ const view = new DataView(d.buffer);
3372
+ d[0] = FAST_VERSION;
3373
+ d[1] = FastKind.Snapshot;
3374
+ view.setUint32(2, head.sid, true);
3375
+ view.setUint32(6, head.snap, true);
3376
+ view.setUint32(10, head.base, true);
3377
+ view.setUint32(14, head.tick, true);
3378
+ view.setUint32(18, head.inputAck, true);
3379
+ d[22] = head.base === 0 ? KEYFRAME : 0;
3380
+ d[23] = i;
3381
+ d[24] = frags;
3382
+ view.setUint32(25, head.sent, true);
3383
+ d.set(chunk, SNAPSHOT_HEADER);
3384
+ out.push(d);
3385
+ }
3386
+ return ok(out);
3387
+ }
3388
+ function decodeFragment(bytes, sid) {
3389
+ const framed = frame(bytes, FastKind.Snapshot, SNAPSHOT_HEADER);
3390
+ if (framed !== null) return fail(framed);
3391
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3392
+ if (view.getUint32(2, true) !== sid) return fail(FastFault.Session);
3393
+ const snap = view.getUint32(6, true);
3394
+ const base = view.getUint32(10, true);
3395
+ const flags = bytes[22];
3396
+ const frag = bytes[23];
3397
+ const frags = bytes[24];
3398
+ if (frags < 1 || frags > FAST_MAX_FRAGMENTS || frag >= frags) return fail(FastFault.Fragment);
3399
+ const keyframeAgrees = (flags & ~KEYFRAME) === 0 && flags === KEYFRAME === (base === 0);
3400
+ if (!keyframeAgrees || snap < 1 || base >= snap) return fail(FastFault.Keyframe);
3401
+ const chunk = bytes.slice(SNAPSHOT_HEADER);
3402
+ return ok({ snap, base, tick: view.getUint32(14, true), inputAck: view.getUint32(18, true), sent: view.getUint32(25, true), keyframe: flags === KEYFRAME, frag, frags, chunk });
3403
+ }
3404
+ function parseBody(bytes) {
3405
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
3406
+ if (bytes.length < 2) return fail(FastFault.Body);
3407
+ const removedCount = view.getUint16(0, true);
3408
+ let at = 2;
3409
+ const removed = [];
3410
+ for (let i = 0; i < removedCount; i += 1) {
3411
+ if (at + 2 > bytes.length) return fail(FastFault.Body);
3412
+ const k = view.getUint16(at, true);
3413
+ if (i > 0 && k <= removed[i - 1]) return fail(FastFault.Body);
3414
+ removed.push(k);
3415
+ at += 2;
3416
+ }
3417
+ if (at + 2 > bytes.length) return fail(FastFault.Body);
3418
+ const count = view.getUint16(at, true);
3419
+ at += 2;
3420
+ const records = [];
3421
+ for (let i = 0; i < count; i += 1) {
3422
+ if (at + 4 > bytes.length) return fail(FastFault.Body);
3423
+ const key = view.getUint16(at, true);
3424
+ const len = view.getUint16(at + 2, true);
3425
+ const bad2 = len > FAST_MAX_RECORD_BYTES || at + 4 + len > bytes.length || i > 0 && key <= records[i - 1].key;
3426
+ if (bad2) return fail(FastFault.Body);
3427
+ records.push({ key, bytes: bytes.slice(at + 4, at + 4 + len) });
3428
+ at += 4 + len;
3429
+ }
3430
+ return at === bytes.length ? ok({ removed, records }) : fail(FastFault.Body);
3431
+ }
3432
+ function applyBody(base, body) {
3433
+ const gone = new Set(body.removed);
3434
+ const out = new Map(base.filter((r) => !gone.has(r.key)).map((r) => [r.key, r]));
3435
+ for (const r of body.records) out.set(r.key, r);
3436
+ return byKey([...out.values()]);
3437
+ }
3438
+ var emptyClientRx = () => ({ partials: [], history: [], newest: 0 });
3439
+ var sameHead = (p, f) => p.base === f.base && p.tick === f.tick && p.inputAck === f.inputAck && p.sent === f.sent && p.keyframe === f.keyframe && p.frags === f.frags;
3440
+ function receiveFragment(rx, bytes, sid) {
3441
+ const decoded = decodeFragment(bytes, sid);
3442
+ if (!decoded.ok) return { rx, applied: null, fault: decoded.fault };
3443
+ const f = decoded.value;
3444
+ if (f.snap <= rx.newest) return { rx, applied: null, fault: FastFault.Stale };
3445
+ const existing = rx.partials.find((p) => p.snap === f.snap);
3446
+ if (existing !== void 0 && !sameHead(existing, f)) return { rx, applied: null, fault: FastFault.Mismatch };
3447
+ if (existing?.chunks[f.frag]) return { rx, applied: null, fault: FastFault.Duplicate };
3448
+ const set = existing ?? { snap: f.snap, base: f.base, tick: f.tick, inputAck: f.inputAck, sent: f.sent, keyframe: f.keyframe, frags: f.frags, chunks: Array(f.frags).fill(null) };
3449
+ const gathered = { ...set, chunks: set.chunks.map((c, i) => i === f.frag ? f.chunk : c) };
3450
+ const others = rx.partials.filter((p) => p.snap !== f.snap);
3451
+ if (gathered.chunks.some((c) => c === null)) {
3452
+ const partials = [...others, gathered].sort((a, b) => a.snap - b.snap).slice(-FAST_REASSEMBLY_SLOTS);
3453
+ return { rx: { ...rx, partials }, applied: null };
3454
+ }
3455
+ return applyComplete({ ...rx, partials: others }, rx, gathered);
3456
+ }
3457
+ function applyComplete(rest, given, set) {
3458
+ const dropped = rest.partials.length === given.partials.length ? given : rest;
3459
+ const body = parseBody(concat(set.chunks));
3460
+ if (!body.ok) return { rx: dropped, applied: null, fault: FastFault.Body };
3461
+ const base = set.keyframe ? null : given.history.find((h) => h.snap === set.base);
3462
+ if (base === void 0) return { rx: dropped, applied: null, fault: FastFault.Base };
3463
+ if (set.keyframe && body.value.removed.length > 0) return { rx: dropped, applied: null, fault: FastFault.Body };
3464
+ const records = applyBody(base?.records ?? [], body.value);
3465
+ const history = [...given.history, { snap: set.snap, tick: set.tick, inputAck: set.inputAck, sent: set.sent, records }].slice(-FAST_SNAPSHOT_HISTORY);
3466
+ const partials = rest.partials.filter((p) => p.snap > set.snap);
3467
+ return { rx: { partials, history, newest: set.snap }, applied: set.snap };
3468
+ }
3469
+ function concat(chunks) {
3470
+ const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
3471
+ let at = 0;
3472
+ for (const c of chunks) {
3473
+ out.set(c, at);
3474
+ at += c.length;
3475
+ }
3476
+ return out;
3477
+ }
3478
+
3479
+ export {
3480
+ unsignedZero,
3481
+ psincos,
3482
+ psin,
3483
+ pcos,
3484
+ Preset,
3485
+ PRESET_MAX_DT_S,
3486
+ TOPDOWN_DEFAULTS,
3487
+ isPresetName,
3488
+ presetParamsValid,
3489
+ resolvePresetParams,
3490
+ resolveTopdownParams,
3491
+ stepTopdown,
3492
+ EMPTY_WORLD_HASH,
3493
+ FIRSTPERSON_DEFAULTS,
3494
+ resolveFirstpersonParams,
3495
+ MoveState,
3496
+ WorldLimits,
3497
+ WorldError,
3498
+ firstpersonWorld,
3499
+ EMPTY_WORLD,
3500
+ POSED_MAX,
3501
+ posedWorld,
3502
+ NO_POSED,
3503
+ hullOps,
3504
+ stepFirstperson,
3505
+ VEHICLE_DEFAULTS,
3506
+ resolveVehicleParams,
3507
+ stepVehicle,
3508
+ PRESETS,
3509
+ definitionOf,
3510
+ stepPreset,
3511
+ PHYSICS_CONTRACT,
3512
+ PHYSICS_DEFAULT_LIMITS,
3513
+ PHYSICS_LIMIT_NAMES,
3514
+ PHYSICS_SCALED_LIMITS,
3515
+ PHYSICS_REAL_LIMITS,
3516
+ PHYSICS_CEILING_FACTOR,
3517
+ PHYSICS_DEFAULT_CEILING,
3518
+ physicsCeilingValid,
3519
+ physicsLimitsAt,
3520
+ PhysicsConfigRange,
3521
+ PHYSICS_PRESET_LENGTH_UNITS,
3522
+ PHYSICS_PATCH_HZ,
3523
+ PHYSICS_DEFAULT_PATCH_HZ,
3524
+ PhysicsPredictRange,
3525
+ isPhysicsPatchHz,
3526
+ effectivePatchHz,
3527
+ keyframeTicks,
3528
+ PHYSICS_PREDICT_MAX_LINEAR,
3529
+ PHYSICS_PREDICT_MAX_ANGULAR,
3530
+ PHYSICS_PREDICT_MIN_ITERATIONS,
3531
+ solverIterationsOf,
3532
+ PHYSICS_KEY_MAX_BYTES,
3533
+ PHYSICS_KIND_MAX_BYTES,
3534
+ PHYSICS_STATIC_PREFIX,
3535
+ isPhysicsKey,
3536
+ isPhysicsKind,
3537
+ resolvePhysicsConfig,
3538
+ checkResolvedPhysics,
3539
+ PHYSICS_SUBSTEPS,
3540
+ physicsWorldDesc,
3541
+ PhysEvKind,
3542
+ PHYSICS_OVERLAP_MAX_KEYS,
3543
+ PHYSICS_BODY_FIELDS,
3544
+ HOST_BODY_FIELDS,
3545
+ BodyQuantization,
3546
+ quantizeBodyValue,
3547
+ dequantizeBodyValue,
3548
+ PHYSICS_CONTESTED_METRES,
3549
+ PHYSICS_FULL_RATE_BODIES,
3550
+ PHYSICS_ROTATING_BODIES,
3551
+ BODY_EXTRAPOLATE_MAX_MS,
3552
+ BODY_EXTRAPOLATE_BACK_TICKS,
3553
+ BODY_CORRECTION_TAU_MS,
3554
+ BODY_SNAP_METRES,
3555
+ MAX_PROJECTILE_TICK,
3556
+ MAX_PROJECTILE_G,
3557
+ isProjectile,
3558
+ projectileAt,
3559
+ serverTick,
3560
+ tickInstant,
3561
+ ContactDrawing,
3562
+ ContactPush,
3563
+ ContactPreset,
3564
+ TopPolicy,
3565
+ ContactShapeKind,
3566
+ HeadFriction,
3567
+ ContactConfigError,
3568
+ CONTACT_PAIRS_MAX,
3569
+ CONTACT_TAG_MAX,
3570
+ CONTACT_PASSES_MAX,
3571
+ CONTACT_VALUE_MAX,
3572
+ SHOOTER_CENTER,
3573
+ CONTACT_PRESETS,
3574
+ CONTACT_DEFAULTS,
3575
+ resolveContactConfig,
3576
+ LiftShapeKind,
3577
+ LiftPathMode,
3578
+ LiftEase,
3579
+ LiftImpart,
3580
+ NO_LIFTS,
3581
+ LiftsConfigError,
3582
+ LIFTS_MAX,
3583
+ LIFT_POINTS_MAX,
3584
+ LIFT_ID_MAX,
3585
+ LIFT_VALUE_MAX,
3586
+ LIFT_TICKS_MAX,
3587
+ LIFT_SPIN_MIN_PERIOD,
3588
+ isLiftId,
3589
+ resolveLifts,
3590
+ InputRepeat,
3591
+ INPUT_PRESETS,
3592
+ INPUTS_DEFAULT,
3593
+ InputConfigError,
3594
+ resolveInputConfig,
3595
+ presetInputRepeat,
3596
+ GuestGlobal,
3597
+ AuthorityLimits,
3598
+ TICK_RATES,
3599
+ PHYSICS_ONLY_TICK_HZ,
3600
+ MAX_TICK_HZ,
3601
+ DEFAULT_TICK_HZ,
3602
+ TickOutFault,
3603
+ InputUse,
3604
+ sanitizeInput,
3605
+ validateTickOut,
3606
+ validateInitOut,
3607
+ FAST_VERSION,
3608
+ FAST_MAX_DATAGRAM_BYTES,
3609
+ FAST_KEY_BYTES,
3610
+ INPUT_REDUNDANCY_MS,
3611
+ INPUT_REDUNDANCY_MIN,
3612
+ INPUT_REDUNDANCY_MAX,
3613
+ FAST_MAX_ACTIONS,
3614
+ FAST_GAP_FILL_MS,
3615
+ FAST_SEQ_AHEAD_MS,
3616
+ FAST_SNAPSHOT_HISTORY,
3617
+ FAST_MAX_FRAGMENTS,
3618
+ FAST_REASSEMBLY_SLOTS,
3619
+ FAST_MAX_RECORD_BYTES,
3620
+ FAST_HANDSHAKE_MS,
3621
+ FAST_STALL_FALLBACK_MS,
3622
+ FastMessage,
3623
+ FastKind,
3624
+ FastFault,
3625
+ AcceptKind,
3626
+ inputRedundancyTicks,
3627
+ uplinkMaxBytes,
3628
+ gapFillMax,
3629
+ seqAheadMax,
3630
+ toHex,
3631
+ fromHex,
3632
+ isPress,
3633
+ encodeUplink,
3634
+ decodeUplink,
3635
+ renderStamp,
3636
+ uplinkFor,
3637
+ acceptUplink,
3638
+ diffSnapshot,
3639
+ deltaBase,
3640
+ encodeSnapshot,
3641
+ decodeFragment,
3642
+ parseBody,
3643
+ applyBody,
3644
+ emptyClientRx,
3645
+ receiveFragment
3646
+ };