@genex-ai/multiplayer 0.17.6 → 0.17.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROTOCOL.md +101 -30
- package/README.md +35 -14
- package/WORLD-GLTF.md +109 -0
- package/WORLD-SCENE.md +135 -0
- package/dist/authority/fast-udp.d.ts +4 -2
- package/dist/authority/fast-udp.js +8 -46
- package/dist/authority/fast-webtransport.d.ts +26 -4
- package/dist/authority/fast-webtransport.js +12 -115
- package/dist/authority/index.d.ts +379 -41
- package/dist/authority/index.js +247 -47
- package/dist/authority/module.d.ts +2 -1
- package/dist/authority/module.js +1 -1
- package/dist/bridge-authority.d.ts +3 -2
- package/dist/bridge-authority.global.js +10 -9
- package/dist/bridge-authority.js +11 -6
- package/dist/bridge.global.js +5 -5
- package/dist/bridge.js +3 -3
- package/dist/chunk-2NGXXQDS.js +1916 -0
- package/dist/{chunk-ZSWF4VCN.js → chunk-CSQEEANB.js} +78 -2
- package/dist/{chunk-UX3F6YTV.js → chunk-FPWP22Y7.js} +1 -1
- package/dist/{chunk-5JGRFT2N.js → chunk-NFMGIKFZ.js} +879 -1898
- package/dist/{chunk-K3SF5VDS.js → chunk-OD6NRSX2.js} +3 -1
- package/dist/{chunk-7QVOIHJF.js → chunk-QY3QQNDJ.js} +2 -2
- package/dist/{chunk-Y6XHXDIK.js → chunk-QZOCUQJT.js} +820 -537
- package/dist/chunk-TSYEWPVO.js +44 -0
- package/dist/chunk-TVTVIMIH.js +675 -0
- package/dist/chunk-YWQ3ALYY.js +69 -0
- package/dist/{fast-client-CZ41v-oB.d.ts → fast-client--A88q0kk.d.ts} +85 -4
- package/dist/index.js +3 -3
- package/dist/level-build-CbRqrYJq.d.ts +140 -0
- package/dist/{module-DqtFb5wA.d.ts → module-CSpXrzFc.d.ts} +151 -400
- package/dist/{physics-predict-49udN3a1.d.ts → physics-predict-C7ZB1gDG.d.ts} +18 -3
- package/dist/protocol.d.ts +87 -2
- package/dist/protocol.js +15 -1
- package/dist/shot-world-D22tqOUT.d.ts +485 -0
- package/dist/world-fit/index.d.ts +549 -0
- package/dist/world-fit/index.js +4763 -0
- package/dist/world-fit/three.d.ts +77 -0
- package/dist/world-fit/three.js +140 -0
- package/package.json +15 -3
|
@@ -1,1791 +1,81 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RemotePredict
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OD6NRSX2.js";
|
|
4
4
|
import {
|
|
5
|
+
MASK_LIMIT,
|
|
6
|
+
MAX_ABS,
|
|
7
|
+
Preset,
|
|
8
|
+
axis,
|
|
9
|
+
definitionOf,
|
|
10
|
+
isMask,
|
|
11
|
+
isPresetName,
|
|
12
|
+
mask,
|
|
13
|
+
presetParamsValid,
|
|
14
|
+
resolvePresetParams,
|
|
15
|
+
unsignedZero,
|
|
16
|
+
wrappedAngle
|
|
17
|
+
} from "./chunk-2NGXXQDS.js";
|
|
18
|
+
import {
|
|
19
|
+
JoinError,
|
|
5
20
|
Limits,
|
|
6
21
|
isReservedEventType
|
|
7
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-CSQEEANB.js";
|
|
8
23
|
|
|
9
|
-
// src/authority/
|
|
10
|
-
var
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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);
|
|
24
|
+
// src/authority/room-key.ts
|
|
25
|
+
var AuthorityRoomLimits = {
|
|
26
|
+
/** A match key's longest length. */
|
|
27
|
+
MAX_MATCH_KEY_CHARS: 32,
|
|
28
|
+
/** A room code's longest length (Colyseus's room ids are 9). */
|
|
29
|
+
MAX_CODE_CHARS: 32,
|
|
30
|
+
/** The most named worlds one module's `worlds` map may hold. */
|
|
31
|
+
MAX_WORLDS: 16
|
|
235
32
|
};
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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;
|
|
33
|
+
var ROOM_NOT_FOUND = 522;
|
|
34
|
+
var inRange = (c, lo, hi) => c >= lo.charCodeAt(0) && c <= hi.charCodeAt(0);
|
|
35
|
+
var isLowerOrDigit = (c) => inRange(c, "a", "z") || inRange(c, "0", "9");
|
|
36
|
+
var KEY_MARKS = "._:-";
|
|
37
|
+
var CODE_MARKS = "_-";
|
|
38
|
+
function validMatchKey(value2) {
|
|
39
|
+
if (typeof value2 !== "string" || value2.length === 0 || value2.length > AuthorityRoomLimits.MAX_MATCH_KEY_CHARS) return false;
|
|
40
|
+
for (let i = 0; i < value2.length; i++) {
|
|
41
|
+
if (isLowerOrDigit(value2.charCodeAt(i))) continue;
|
|
42
|
+
if (i === 0 || !KEY_MARKS.includes(value2.charAt(i))) return false;
|
|
1025
43
|
}
|
|
1026
44
|
return true;
|
|
1027
45
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
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
|
-
}
|
|
46
|
+
function validRoomCode(value2) {
|
|
47
|
+
if (typeof value2 !== "string" || value2.length === 0 || value2.length > AuthorityRoomLimits.MAX_CODE_CHARS) return false;
|
|
48
|
+
for (let i = 0; i < value2.length; i++) {
|
|
49
|
+
const c = value2.charCodeAt(i);
|
|
50
|
+
if (!isLowerOrDigit(c) && !inRange(c, "A", "Z") && !CODE_MARKS.includes(value2.charAt(i))) return false;
|
|
1070
51
|
}
|
|
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
52
|
return true;
|
|
1089
53
|
}
|
|
1090
|
-
function
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
};
|
|
54
|
+
function matchKeyOf(value2) {
|
|
55
|
+
if (value2 === void 0 || value2 === "") return "";
|
|
56
|
+
return validMatchKey(value2) ? value2 : null;
|
|
1418
57
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
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);
|
|
58
|
+
var AuthorityJoinMethod = { JoinOrCreate: "joinOrCreate", Create: "create", JoinById: "joinById" };
|
|
59
|
+
var AuthorityTargetRefusal = { BadMatchKey: "bad_match_key", BadCode: "bad_code", Conflict: "conflict" };
|
|
60
|
+
function planAuthorityJoin(target) {
|
|
61
|
+
const key = target.matchKey ?? "";
|
|
62
|
+
if (target.code !== void 0) {
|
|
63
|
+
if (target.createPrivate === true || key !== "") return { refused: AuthorityTargetRefusal.Conflict };
|
|
64
|
+
if (!validRoomCode(target.code)) return { refused: AuthorityTargetRefusal.BadCode };
|
|
65
|
+
return { method: AuthorityJoinMethod.JoinById, roomId: target.code, options: {} };
|
|
1501
66
|
}
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
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
|
-
};
|
|
67
|
+
const pool = matchKeyOf(key);
|
|
68
|
+
if (pool === null) return { refused: AuthorityTargetRefusal.BadMatchKey };
|
|
69
|
+
const options = pool === "" ? {} : { matchKey: pool };
|
|
70
|
+
if (target.createPrivate === true) return { method: AuthorityJoinMethod.Create, options: { ...options, private: true } };
|
|
71
|
+
return { method: AuthorityJoinMethod.JoinOrCreate, options };
|
|
1514
72
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
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
|
-
}
|
|
73
|
+
function nextCodeRegion(error, region, regions) {
|
|
74
|
+
const elsewhere = error === JoinError.WRONG_REGION || error === ROOM_NOT_FOUND;
|
|
75
|
+
return elsewhere && region + 1 < regions ? region + 1 : null;
|
|
1616
76
|
}
|
|
1617
|
-
function
|
|
1618
|
-
|
|
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);
|
|
77
|
+
function selectMatchWorld(worlds, world, key) {
|
|
78
|
+
return worlds !== void 0 && Object.hasOwn(worlds, key) ? worlds[key] : world;
|
|
1789
79
|
}
|
|
1790
80
|
|
|
1791
81
|
// src/authority/physics.ts
|
|
@@ -1810,8 +100,18 @@ var PHYSICS_LIMIT_NAMES = Object.freeze(Object.keys(PHYSICS_DEFAULT_LIMITS));
|
|
|
1810
100
|
var PHYSICS_SCALED_LIMITS = Object.freeze(["worldExtent", "maxSpeed"]);
|
|
1811
101
|
var PHYSICS_REAL_LIMITS = Object.freeze(["worldExtent", "maxSpeed", "maxAngularSpeed"]);
|
|
1812
102
|
var PHYSICS_CEILING_FACTOR = 4;
|
|
103
|
+
var PHYSICS_STATIC_PIECE_BYTES = 2048;
|
|
104
|
+
var PHYSICS_PREDICT_MAX_BOXES = 4096;
|
|
105
|
+
var PHYSICS_PREDICT_MAX_RAMPS = 512;
|
|
106
|
+
var physicsWorldPredicts = (boxes, ramps) => boxes <= PHYSICS_PREDICT_MAX_BOXES && ramps <= PHYSICS_PREDICT_MAX_RAMPS;
|
|
107
|
+
function physicsLimitsForWorld(limits, pieces) {
|
|
108
|
+
const lowered = limits.staticPieces < PHYSICS_DEFAULT_LIMITS.staticPieces;
|
|
109
|
+
if (lowered || pieces <= limits.staticPieces) return limits;
|
|
110
|
+
const memoryCeiling = PHYSICS_DEFAULT_LIMITS.memoryBytes * PHYSICS_CEILING_FACTOR;
|
|
111
|
+
return { ...limits, staticPieces: pieces, memoryBytes: Math.min(memoryCeiling, limits.memoryBytes + pieces * PHYSICS_STATIC_PIECE_BYTES) };
|
|
112
|
+
}
|
|
1813
113
|
var PHYSICS_DEFAULT_CEILING = PHYSICS_DEFAULT_LIMITS;
|
|
1814
|
-
var
|
|
114
|
+
var isPlainObject = (v) => {
|
|
1815
115
|
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
1816
116
|
const proto = Object.getPrototypeOf(v);
|
|
1817
117
|
return proto === Object.prototype || proto === null;
|
|
@@ -1823,7 +123,7 @@ function limitValueValid(key, value2) {
|
|
|
1823
123
|
return isRealLimit(key) || Number.isInteger(value2);
|
|
1824
124
|
}
|
|
1825
125
|
function physicsCeilingValid(ceiling) {
|
|
1826
|
-
if (!
|
|
126
|
+
if (!isPlainObject(ceiling)) return false;
|
|
1827
127
|
return PHYSICS_LIMIT_NAMES.every((key) => {
|
|
1828
128
|
const value2 = ceiling[key];
|
|
1829
129
|
const base = PHYSICS_DEFAULT_LIMITS[key];
|
|
@@ -1869,8 +169,8 @@ function isPhysicsPatchHz(v) {
|
|
|
1869
169
|
return typeof v === "number" && PHYSICS_PATCH_HZ.includes(v);
|
|
1870
170
|
}
|
|
1871
171
|
function effectivePatchHz(tickHz, patchHz) {
|
|
1872
|
-
const
|
|
1873
|
-
return
|
|
172
|
+
const fits = PHYSICS_PATCH_HZ.filter((hz) => hz <= patchHz && hz <= tickHz && tickHz % hz === 0);
|
|
173
|
+
return fits.at(-1) ?? PHYSICS_PATCH_HZ[0];
|
|
1874
174
|
}
|
|
1875
175
|
function keyframeTicks(tickHz, patchHz) {
|
|
1876
176
|
return tickHz / effectivePatchHz(tickHz, patchHz);
|
|
@@ -1954,7 +254,7 @@ function limitsOf(v, lengthUnit, ceiling, mode) {
|
|
|
1954
254
|
const path = `${PATH}.limits`;
|
|
1955
255
|
if (!asked(v) && mode === Mode.RESOLVED) return bad(path);
|
|
1956
256
|
const given = asked(v) ? v : {};
|
|
1957
|
-
if (!
|
|
257
|
+
if (!isPlainObject(given)) return bad(path);
|
|
1958
258
|
if (mode === Mode.REQUEST) {
|
|
1959
259
|
const unknown = Object.keys(given).find((key) => !isLimitName(key));
|
|
1960
260
|
if (unknown !== void 0) return bad(`${path}.${unknown}`);
|
|
@@ -1991,7 +291,7 @@ function playerOf(v, preset, mode) {
|
|
|
1991
291
|
const path = `${PATH}.player`;
|
|
1992
292
|
if (!asked(v) && mode === Mode.RESOLVED) return bad(path);
|
|
1993
293
|
const given = asked(v) ? v : {};
|
|
1994
|
-
if (!
|
|
294
|
+
if (!isPlainObject(given)) return bad(path);
|
|
1995
295
|
if (mode === Mode.REQUEST) {
|
|
1996
296
|
const unknown = Object.keys(given).find((key) => !PLAYER_KEYS.has(key));
|
|
1997
297
|
if (unknown !== void 0) return bad(`${path}.${unknown}`);
|
|
@@ -2033,7 +333,7 @@ function physicsOf(v, preset, ceiling, mode) {
|
|
|
2033
333
|
if (none) return ceiling === null ? null : bad(PATH);
|
|
2034
334
|
if (ceiling === null) return bad(PATH);
|
|
2035
335
|
const given = v === true && mode === Mode.REQUEST ? {} : v;
|
|
2036
|
-
if (!
|
|
336
|
+
if (!isPlainObject(given)) return bad(PATH);
|
|
2037
337
|
if (mode === Mode.REQUEST) {
|
|
2038
338
|
const unknown = Object.keys(given).find((key) => !REQUEST_KEYS.has(key));
|
|
2039
339
|
if (unknown !== void 0) return bad(`${PATH}.${unknown}`);
|
|
@@ -2068,7 +368,7 @@ function checkResolvedPhysics(resolved, preset, ceiling) {
|
|
|
2068
368
|
return resolution(() => physicsOf(resolved, preset, ceiling, Mode.RESOLVED));
|
|
2069
369
|
}
|
|
2070
370
|
var PHYSICS_SUBSTEPS = Object.freeze({ 20: 3, 30: 2, 60: 1, 120: 1 });
|
|
2071
|
-
function physicsWorldDesc(physics, tickHz, predicting = false) {
|
|
371
|
+
function physicsWorldDesc(physics, tickHz, predicting = false, worldPieces = 0) {
|
|
2072
372
|
return {
|
|
2073
373
|
contract: PHYSICS_CONTRACT,
|
|
2074
374
|
dt: 1 / tickHz,
|
|
@@ -2076,7 +376,7 @@ function physicsWorldDesc(physics, tickHz, predicting = false) {
|
|
|
2076
376
|
gravity: [0, 0, -physics.gravity],
|
|
2077
377
|
upAxis: "z",
|
|
2078
378
|
lengthUnit: physics.lengthUnit,
|
|
2079
|
-
limits: { ...physics.limits },
|
|
379
|
+
limits: { ...physicsLimitsForWorld(physics.limits, worldPieces) },
|
|
2080
380
|
solver: { iterations: solverIterationsOf(physics, predicting) }
|
|
2081
381
|
};
|
|
2082
382
|
}
|
|
@@ -2126,29 +426,6 @@ var BODY_EXTRAPOLATE_BACK_TICKS = 1;
|
|
|
2126
426
|
var BODY_CORRECTION_TAU_MS = 100;
|
|
2127
427
|
var BODY_SNAP_METRES = 1;
|
|
2128
428
|
|
|
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
429
|
// src/authority/contact-config.ts
|
|
2153
430
|
var ContactDrawing = { Predicted: "predicted", Separate: "separate", Timeline: "timeline" };
|
|
2154
431
|
var ContactPush = { Shared: "shared", Mover: "mover" };
|
|
@@ -2264,7 +541,7 @@ function standOf(v, path, base, vehicle) {
|
|
|
2264
541
|
const s = has(v, "slide") ? slideOf(v.slide, `${path}.slide`, from.slide) : from.slide;
|
|
2265
542
|
return stand(ride, center, s);
|
|
2266
543
|
}
|
|
2267
|
-
function
|
|
544
|
+
function topOf(v, path, base, vehicle) {
|
|
2268
545
|
if (!isRecord(v)) throw new ContactConfigError(path, "must be an object");
|
|
2269
546
|
const policy = v.policy;
|
|
2270
547
|
if (typeof policy !== "string" || !Object.hasOwn(TOP_KEYS, policy)) throw new ContactConfigError(`${path}.policy`, "must be stand, slide, bounce or ghost");
|
|
@@ -2330,7 +607,7 @@ function pairsOf(v, path, base, vehicle) {
|
|
|
2330
607
|
const tag = r[k];
|
|
2331
608
|
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
609
|
}
|
|
2333
|
-
return { a: r.a, b: r.b, top:
|
|
610
|
+
return { a: r.a, b: r.b, top: topOf(r.top, `${at}.top`, base, vehicle) };
|
|
2334
611
|
});
|
|
2335
612
|
}
|
|
2336
613
|
function passesOf(o, path, fallback) {
|
|
@@ -2349,7 +626,7 @@ function resolveContactConfig(given, preset, path = "$.config.contacts") {
|
|
|
2349
626
|
const shape = requireShape(shapeOf(given.shape, `${path}.shape`, preset), `${path}.shape`);
|
|
2350
627
|
const vehicle = shape.kind === ContactShapeKind.Box;
|
|
2351
628
|
const base = vehicle ? table.vehicle : table.character;
|
|
2352
|
-
const top = given.top === void 0 ? base :
|
|
629
|
+
const top = given.top === void 0 ? base : topOf(given.top, `${path}.top`, base, vehicle);
|
|
2353
630
|
const offBase = vehicle ? table.vehicleOffWall : baseSlide(base, false);
|
|
2354
631
|
const p = preset.params;
|
|
2355
632
|
const maxVelocity = has(given, "maxVelocity") && given.maxVelocity === null ? null : num(given, "maxVelocity", path, p.maxVelocity ?? CONTACT_VALUE_MAX, 0, CONTACT_VALUE_MAX, true);
|
|
@@ -2378,6 +655,84 @@ function resolveContactConfig(given, preset, path = "$.config.contacts") {
|
|
|
2378
655
|
};
|
|
2379
656
|
}
|
|
2380
657
|
|
|
658
|
+
// src/authority/input-config.ts
|
|
659
|
+
var InputRepeat = { On: "on", Off: "off" };
|
|
660
|
+
var INPUT_SETTLE_MS = { MIN: 100, MAX: 6e4 };
|
|
661
|
+
var INPUT_PRESETS = {
|
|
662
|
+
[ContactPreset.Shooter]: { repeat: InputRepeat.On },
|
|
663
|
+
[ContactPreset.Platformer]: { repeat: InputRepeat.On },
|
|
664
|
+
[ContactPreset.VehicleArena]: { repeat: InputRepeat.Off },
|
|
665
|
+
[ContactPreset.PhysicsSandbox]: { repeat: InputRepeat.On },
|
|
666
|
+
[ContactPreset.Party]: { repeat: InputRepeat.On }
|
|
667
|
+
};
|
|
668
|
+
var INPUTS_DEFAULT = { repeat: InputRepeat.On };
|
|
669
|
+
var InputConfigError = class extends Error {
|
|
670
|
+
path;
|
|
671
|
+
constructor(path) {
|
|
672
|
+
super(`bad config at ${path}`);
|
|
673
|
+
this.name = "InputConfigError";
|
|
674
|
+
this.path = path;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
var KEYS2 = /* @__PURE__ */ new Set(["preset", "repeat", "settleMs"]);
|
|
678
|
+
var isPlainObject2 = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
679
|
+
var isPreset = (v) => Object.values(ContactPreset).some((p) => p === v);
|
|
680
|
+
var isRepeat = (v) => v === InputRepeat.On || v === InputRepeat.Off;
|
|
681
|
+
var isSettleMs = (v) => typeof v === "number" && Number.isFinite(v) && v >= INPUT_SETTLE_MS.MIN && v <= INPUT_SETTLE_MS.MAX;
|
|
682
|
+
function resolveInputConfig(given, contactsPreset, path = "$.config.inputs") {
|
|
683
|
+
if (given === void 0 || given === null) return contactsPreset === null ? null : INPUT_PRESETS[contactsPreset];
|
|
684
|
+
if (!isPlainObject2(given)) throw new InputConfigError(path);
|
|
685
|
+
for (const key of Object.keys(given)) if (!KEYS2.has(key)) throw new InputConfigError(`${path}.${key}`);
|
|
686
|
+
if (given.preset !== void 0 && !isPreset(given.preset)) throw new InputConfigError(`${path}.preset`);
|
|
687
|
+
if (given.repeat !== void 0 && !isRepeat(given.repeat)) throw new InputConfigError(`${path}.repeat`);
|
|
688
|
+
if (given.settleMs !== void 0 && !isSettleMs(given.settleMs)) throw new InputConfigError(`${path}.settleMs`);
|
|
689
|
+
const preset = given.preset ?? contactsPreset;
|
|
690
|
+
const fromPreset = preset === null ? INPUTS_DEFAULT : INPUT_PRESETS[preset];
|
|
691
|
+
const repeat = given.repeat ?? fromPreset.repeat;
|
|
692
|
+
return given.settleMs === void 0 ? { repeat } : { repeat, settleMs: given.settleMs };
|
|
693
|
+
}
|
|
694
|
+
function presetInputRepeat(presetText) {
|
|
695
|
+
let spec;
|
|
696
|
+
try {
|
|
697
|
+
spec = JSON.parse(presetText);
|
|
698
|
+
} catch {
|
|
699
|
+
return InputRepeat.On;
|
|
700
|
+
}
|
|
701
|
+
const inputs = isPlainObject2(spec) ? spec.inputs : void 0;
|
|
702
|
+
return isPlainObject2(inputs) && inputs.repeat === InputRepeat.Off ? InputRepeat.Off : InputRepeat.On;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// src/authority/lifetime-config.ts
|
|
706
|
+
var AuthorityLifetime = {
|
|
707
|
+
IDLE_SEAT_S: 30 * 60,
|
|
708
|
+
IDLE_SEAT_MIN_S: 30,
|
|
709
|
+
IDLE_SEAT_MAX_S: 24 * 60 * 60,
|
|
710
|
+
ROOM_AGE_S: 24 * 60 * 60,
|
|
711
|
+
ROOM_AGE_MIN_S: 60,
|
|
712
|
+
ROOM_AGE_MAX_S: 7 * 24 * 60 * 60
|
|
713
|
+
};
|
|
714
|
+
var LIFETIME_DEFAULT = { idleSeatS: AuthorityLifetime.IDLE_SEAT_S, maxRoomAgeS: AuthorityLifetime.ROOM_AGE_S };
|
|
715
|
+
var LifetimeConfigError = class extends Error {
|
|
716
|
+
path;
|
|
717
|
+
constructor(path) {
|
|
718
|
+
super(`bad config at ${path}`);
|
|
719
|
+
this.name = "LifetimeConfigError";
|
|
720
|
+
this.path = path;
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
var KEYS3 = /* @__PURE__ */ new Set(["idleSeatS", "maxRoomAgeS"]);
|
|
724
|
+
var inRange2 = (v, min, max) => typeof v === "number" && Number.isInteger(v) && v >= min && v <= max;
|
|
725
|
+
function resolveLifetime(given, path = "$.config.lifetime") {
|
|
726
|
+
if (given === void 0 || given === null) return null;
|
|
727
|
+
if (typeof given !== "object" || Array.isArray(given)) throw new LifetimeConfigError(path);
|
|
728
|
+
const declared = given;
|
|
729
|
+
for (const key of Object.keys(declared)) if (!KEYS3.has(key)) throw new LifetimeConfigError(`${path}.${key}`);
|
|
730
|
+
const { idleSeatS, maxRoomAgeS } = declared;
|
|
731
|
+
if (idleSeatS !== void 0 && !inRange2(idleSeatS, AuthorityLifetime.IDLE_SEAT_MIN_S, AuthorityLifetime.IDLE_SEAT_MAX_S)) throw new LifetimeConfigError(`${path}.idleSeatS`);
|
|
732
|
+
if (maxRoomAgeS !== void 0 && !inRange2(maxRoomAgeS, AuthorityLifetime.ROOM_AGE_MIN_S, AuthorityLifetime.ROOM_AGE_MAX_S)) throw new LifetimeConfigError(`${path}.maxRoomAgeS`);
|
|
733
|
+
return { idleSeatS: idleSeatS ?? LIFETIME_DEFAULT.idleSeatS, maxRoomAgeS: maxRoomAgeS ?? LIFETIME_DEFAULT.maxRoomAgeS };
|
|
734
|
+
}
|
|
735
|
+
|
|
2381
736
|
// src/authority/lifts-config.ts
|
|
2382
737
|
var LiftShapeKind = {
|
|
2383
738
|
/** A box `half` (x, y, z): axis-aligned, or turned about z when it spins (a sweeper bar). */
|
|
@@ -2421,10 +776,10 @@ var LIFT_VALUE_MAX = 1e6;
|
|
|
2421
776
|
var LIFT_TICKS_MAX = 2 ** 24;
|
|
2422
777
|
var LIFT_SPIN_MIN_PERIOD = 8;
|
|
2423
778
|
var SMOOTH_PEAK = 1.5;
|
|
2424
|
-
var
|
|
779
|
+
var TWO_PI = 2 * Math.PI;
|
|
2425
780
|
var ID = /^[a-z0-9-]+$/;
|
|
2426
781
|
var isLiftId = (id) => typeof id === "string" && id.length > 0 && id.length <= LIFT_ID_MAX && ID.test(id);
|
|
2427
|
-
var
|
|
782
|
+
var KEYS4 = /* @__PURE__ */ new Set(["platforms", "bodies"]);
|
|
2428
783
|
var PLATFORM_KEYS = /* @__PURE__ */ new Set(["id", "shape", "at", "path", "spin", "surface", "impart", "called"]);
|
|
2429
784
|
var PATH_KEYS = /* @__PURE__ */ new Set(["points", "legs", "waits", "mode", "ease", "phase"]);
|
|
2430
785
|
var SPIN_KEYS = /* @__PURE__ */ new Set(["period", "dir", "phase"]);
|
|
@@ -2503,7 +858,7 @@ function spinOf(v, path) {
|
|
|
2503
858
|
var reachOf = (s) => s.kind === LiftShapeKind.Disc ? s.radius : Math.sqrt(s.half[0] * s.half[0] + s.half[1] * s.half[1]);
|
|
2504
859
|
var speedText = (perTick, tickHz, rate = "") => `${Number(perTick.toFixed(3))} u a tick (${Number((perTick * tickHz).toFixed(1))} u/s${rate})`;
|
|
2505
860
|
function refuseFast(lift, path, radius, tickHz) {
|
|
2506
|
-
const spinTip = lift.spin === null ? 0 : reachOf(lift.shape) *
|
|
861
|
+
const spinTip = lift.spin === null ? 0 : reachOf(lift.shape) * TWO_PI / lift.spin.period;
|
|
2507
862
|
const allowed = `at most ${speedText(radius, tickHz, ` at ${tickHz} Hz`)} is allowed (the hull radius a tick)`;
|
|
2508
863
|
const legs = lift.path?.legs ?? [];
|
|
2509
864
|
let fastest = { at: `${path}.spin.period`, move: spinTip };
|
|
@@ -2551,7 +906,7 @@ function platformOf(v, path, ids, radius, tickHz) {
|
|
|
2551
906
|
var WORLD_PRESETS2 = /* @__PURE__ */ new Set(["firstperson", "vehicle"]);
|
|
2552
907
|
function resolveLifts(given, preset, tickHz, path = "$.config.lifts") {
|
|
2553
908
|
if (!isRecord2(given)) throw new LiftsConfigError(path, "must be an object");
|
|
2554
|
-
onlyKeys2(given,
|
|
909
|
+
onlyKeys2(given, KEYS4, path);
|
|
2555
910
|
if (preset === null || !WORLD_PRESETS2.has(preset.name)) throw new LiftsConfigError(path, "needs a firstperson or vehicle movement preset");
|
|
2556
911
|
const list2 = given.platforms;
|
|
2557
912
|
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`);
|
|
@@ -2562,47 +917,27 @@ function resolveLifts(given, preset, tickHz, path = "$.config.lifts") {
|
|
|
2562
917
|
return { platforms, bodies: given.bodies !== false };
|
|
2563
918
|
}
|
|
2564
919
|
|
|
2565
|
-
// src/authority/
|
|
2566
|
-
var
|
|
2567
|
-
var
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
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 };
|
|
920
|
+
// src/authority/projectile.ts
|
|
921
|
+
var MAX_PROJECTILE_TICK = 2 ** 32 - 1;
|
|
922
|
+
var MAX_PROJECTILE_G = 1e3;
|
|
923
|
+
var isProjectile = (entity) => typeof entity.t0 === "number" && entity.t0 > 0;
|
|
924
|
+
function projectileAt(launch, T, tickHz) {
|
|
925
|
+
const elapsed = (T - launch.t0) / tickHz;
|
|
926
|
+
const s = Number.isFinite(elapsed) && elapsed >= 0 ? elapsed : 0;
|
|
927
|
+
return {
|
|
928
|
+
x: unsignedZero(Math.fround(launch.x + launch.vx * s)),
|
|
929
|
+
y: unsignedZero(Math.fround(launch.y + launch.vy * s)),
|
|
930
|
+
z: unsignedZero(Math.fround(launch.z + launch.vz * s - 0.5 * launch.g * s * s)),
|
|
931
|
+
vx: unsignedZero(launch.vx),
|
|
932
|
+
vy: unsignedZero(launch.vy),
|
|
933
|
+
vz: unsignedZero(Math.fround(launch.vz - launch.g * s))
|
|
934
|
+
};
|
|
2596
935
|
}
|
|
2597
|
-
function
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
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;
|
|
936
|
+
function serverTick(serverNowMs, tickOrigin, tickHz) {
|
|
937
|
+
return (serverNowMs - tickOrigin) * tickHz / 1e3;
|
|
938
|
+
}
|
|
939
|
+
function tickInstant(tick, tickOrigin, tickHz) {
|
|
940
|
+
return tickOrigin + tick * 1e3 / tickHz;
|
|
2606
941
|
}
|
|
2607
942
|
|
|
2608
943
|
// src/authority/wire.ts
|
|
@@ -2610,11 +945,15 @@ var GuestGlobal = {
|
|
|
2610
945
|
INIT: "__genex_init",
|
|
2611
946
|
TICK: "__genex_tick",
|
|
2612
947
|
/** A physics room's static world, called once after init (§17.10 *The static world*). */
|
|
2613
|
-
WORLD: "__genex_world"
|
|
948
|
+
WORLD: "__genex_world",
|
|
949
|
+
/** The room's hand-off at the end of a drain (PROTOCOL.md §17.13), called once. */
|
|
950
|
+
HANDOFF: "__genex_handoff"
|
|
2614
951
|
};
|
|
2615
952
|
var AuthorityLimits = {
|
|
2616
953
|
/** Entities one tick may update, and separately remove. */
|
|
2617
954
|
MAX_ENTITIES: 256,
|
|
955
|
+
/** A room's hand-off (`onDrain`, PROTOCOL.md §17.13) as JSON text. Over it the successor starts clean. */
|
|
956
|
+
MAX_HANDOFF_CHARS: 256 * 1024,
|
|
2618
957
|
/** Entities one room may hold at once. The room state lives on the host, outside the sandbox's
|
|
2619
958
|
* memory limit, so this cap is what bounds it (and every client's patches). */
|
|
2620
959
|
MAX_ROOM_ENTITIES: 1024,
|
|
@@ -2657,8 +996,12 @@ var AuthorityLimits = {
|
|
|
2657
996
|
MAX_PHYS_COMMANDS: 256,
|
|
2658
997
|
MAX_PHYS_BYTES: 32 * 1024,
|
|
2659
998
|
MAX_PHYSICS_OUTPUT_BYTES: 64 * 1024,
|
|
2660
|
-
/**
|
|
2661
|
-
|
|
999
|
+
/** The most a physics room's static world holds (§17.10 *The static world*): §17.7's caps, so any world a
|
|
1000
|
+
* module may carry has its physics core. `limits.staticPieces` follows a world past its default. */
|
|
1001
|
+
MAX_PHYSICS_WORLD_BOXES: 32768,
|
|
1002
|
+
MAX_PHYSICS_WORLD_RAMPS: 4096,
|
|
1003
|
+
/** `__genex_world()`'s text: base64 of the largest such world, 4 · ⌈(8 + 24 · 32768 + 36 · 4096) / 3⌉. */
|
|
1004
|
+
MAX_WORLD_TEXT_CHARS: 1245196
|
|
2662
1005
|
};
|
|
2663
1006
|
var TICK_RATES = [20, 30, 60, 120];
|
|
2664
1007
|
var PHYSICS_ONLY_TICK_HZ = 120;
|
|
@@ -2676,6 +1019,8 @@ var TickOutFault = {
|
|
|
2676
1019
|
TOO_MANY_EVENTS: "too_many_events",
|
|
2677
1020
|
RESERVED_EVENT: "reserved_event",
|
|
2678
1021
|
BAD_CONFIG: "bad_config",
|
|
1022
|
+
/** The module's maps leave a room without a shot world (§17.7 *The shot world*). */
|
|
1023
|
+
NO_SHOT_WORLD: "no_shot_world",
|
|
2679
1024
|
/** More `phys` commands than MAX_PHYS_COMMANDS, or more `physQ` queries than `queriesPerStep`. */
|
|
2680
1025
|
TOO_MANY_COMMANDS: "too_many_commands"
|
|
2681
1026
|
};
|
|
@@ -2726,7 +1071,7 @@ function finite(v, path) {
|
|
|
2726
1071
|
if (Math.abs(v) > MAX_ABS) return refuse(TickOutFault.OUT_OF_RANGE, path);
|
|
2727
1072
|
return unsignedZero(v);
|
|
2728
1073
|
}
|
|
2729
|
-
function
|
|
1074
|
+
function integerIn(v, max, path) {
|
|
2730
1075
|
const n = finite(v, path);
|
|
2731
1076
|
if (!Number.isInteger(n) || n < 0 || n > max) return refuse(TickOutFault.OUT_OF_RANGE, path);
|
|
2732
1077
|
return n;
|
|
@@ -2791,8 +1136,8 @@ function entityOut(v, path) {
|
|
|
2791
1136
|
if (v[key] !== void 0) out[key] = unsignedZero(Math.fround(finite(v[key], `${path}.${key}`)));
|
|
2792
1137
|
}
|
|
2793
1138
|
if (v.hp !== void 0) out.hp = finite(v.hp, `${path}.hp`);
|
|
2794
|
-
if (v.flags !== void 0) out.flags =
|
|
2795
|
-
if (v.epoch !== void 0) out.epoch =
|
|
1139
|
+
if (v.flags !== void 0) out.flags = integerIn(v.flags, MASK_LIMIT - 1, `${path}.flags`);
|
|
1140
|
+
if (v.epoch !== void 0) out.epoch = integerIn(v.epoch, AuthorityLimits.MAX_EPOCH, `${path}.epoch`);
|
|
2796
1141
|
if (v.data !== void 0) out.data = entityData(v.data, `${path}.data`);
|
|
2797
1142
|
if (v.t0 !== void 0) out.t0 = launchTick(v.t0, `${path}.t0`);
|
|
2798
1143
|
if (v.g !== void 0) {
|
|
@@ -2808,7 +1153,7 @@ function entityOut(v, path) {
|
|
|
2808
1153
|
for (const key of FIRSTPERSON_FLOAT32_FIELDS) {
|
|
2809
1154
|
if (v[key] !== void 0) out[key] = unsignedZero(Math.fround(finite(v[key], `${path}.${key}`)));
|
|
2810
1155
|
}
|
|
2811
|
-
if (v.mstate !== void 0) out.mstate =
|
|
1156
|
+
if (v.mstate !== void 0) out.mstate = integerIn(v.mstate, MASK_LIMIT - 1, `${path}.mstate`);
|
|
2812
1157
|
for (const key of VEHICLE_FLOAT32_FIELDS) {
|
|
2813
1158
|
if (v[key] !== void 0) out[key] = unsignedZero(Math.fround(finite(v[key], `${path}.${key}`)));
|
|
2814
1159
|
}
|
|
@@ -2971,6 +1316,7 @@ function configOf(v, admission) {
|
|
|
2971
1316
|
const contacts = contactsOf(v.contacts, preset);
|
|
2972
1317
|
const inputs = inputsOf(v.inputs, contacts);
|
|
2973
1318
|
const lifts = liftsOf(v.lifts, preset, tickHz);
|
|
1319
|
+
const lifetime = lifetimeOf(v.lifetime);
|
|
2974
1320
|
return {
|
|
2975
1321
|
tickHz,
|
|
2976
1322
|
maxPlayers,
|
|
@@ -2979,9 +1325,18 @@ function configOf(v, admission) {
|
|
|
2979
1325
|
physics: physics.value,
|
|
2980
1326
|
...contacts === null ? {} : { contacts },
|
|
2981
1327
|
...inputs === null ? {} : { inputs },
|
|
2982
|
-
...lifts === null ? {} : { lifts }
|
|
1328
|
+
...lifts === null ? {} : { lifts },
|
|
1329
|
+
...lifetime === null ? {} : { lifetime }
|
|
2983
1330
|
};
|
|
2984
1331
|
}
|
|
1332
|
+
function lifetimeOf(v) {
|
|
1333
|
+
try {
|
|
1334
|
+
return resolveLifetime(v);
|
|
1335
|
+
} catch (e) {
|
|
1336
|
+
if (e instanceof LifetimeConfigError) return refuse(TickOutFault.BAD_CONFIG, e.path);
|
|
1337
|
+
throw e;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
2985
1340
|
function inputsOf(v, contacts) {
|
|
2986
1341
|
try {
|
|
2987
1342
|
return resolveInputConfig(v, contacts?.preset ?? null);
|
|
@@ -3028,13 +1383,26 @@ function validateTickOut(raw, room, physics) {
|
|
|
3028
1383
|
return gate(raw, (value2) => roomTickOutOf(value2, room, wire), wire !== void 0);
|
|
3029
1384
|
}
|
|
3030
1385
|
var NO_BODIES = { has: () => false };
|
|
1386
|
+
function shotCountOf(value2) {
|
|
1387
|
+
if (value2 === null || typeof value2 !== "object") return void 0;
|
|
1388
|
+
const { solids, opsPerRay } = value2;
|
|
1389
|
+
return Number.isInteger(solids) && Number.isInteger(opsPerRay) ? { solids, opsPerRay } : void 0;
|
|
1390
|
+
}
|
|
1391
|
+
function worldNamesOf(value2) {
|
|
1392
|
+
if (!Array.isArray(value2) || value2.length > AuthorityRoomLimits.MAX_WORLDS) return void 0;
|
|
1393
|
+
return value2.every(validMatchKey) ? value2 : void 0;
|
|
1394
|
+
}
|
|
3031
1395
|
function validateInitOut(raw, physics) {
|
|
3032
1396
|
const wire = physics === void 0 ? void 0 : { queries: null, bodies: NO_BODIES };
|
|
3033
1397
|
return gate(
|
|
3034
1398
|
raw,
|
|
3035
1399
|
(value2) => {
|
|
3036
1400
|
const out = roomTickOutOf(value2, EMPTY_ROOM, wire);
|
|
3037
|
-
|
|
1401
|
+
const { config, worlds, shotWorld, noShotWorld } = value2;
|
|
1402
|
+
if (typeof noShotWorld === "string") return refuse(TickOutFault.NO_SHOT_WORLD, `$.shotWorld: ${noShotWorld}`);
|
|
1403
|
+
const named = worldNamesOf(worlds);
|
|
1404
|
+
const counted = shotCountOf(shotWorld);
|
|
1405
|
+
return { ...out, config: configOf(config, physics), ...named === void 0 ? {} : { worlds: named }, ...counted === void 0 ? {} : { shotWorld: counted } };
|
|
3038
1406
|
},
|
|
3039
1407
|
wire !== void 0
|
|
3040
1408
|
);
|
|
@@ -3056,12 +1424,31 @@ var FAST_REASSEMBLY_SLOTS = 4;
|
|
|
3056
1424
|
var FAST_MAX_RECORD_BYTES = 1024;
|
|
3057
1425
|
var FAST_HANDSHAKE_MS = 1e3;
|
|
3058
1426
|
var FAST_STALL_FALLBACK_MS = 2e3;
|
|
1427
|
+
var FAST_KEEPALIVE_MS = 250;
|
|
1428
|
+
var FAST_KEEPALIVE_BIND_MS = 50;
|
|
1429
|
+
var FAST_ACK_EAGER_RECORDS = 32;
|
|
1430
|
+
var FAST_ACK_EAGER_GAP_MS = 20;
|
|
1431
|
+
var FAST_CHALLENGE_JUMP = 65536;
|
|
1432
|
+
var FAST_FRAME_GAP_MS = 250;
|
|
1433
|
+
var FAST_BIND_REV = 2;
|
|
1434
|
+
var FAST_RETURN_PROOF = 3;
|
|
1435
|
+
var FAST_RETRY_BASE_MS = 1e3;
|
|
1436
|
+
var FAST_RETRY_MAX_MS = 6e4;
|
|
1437
|
+
var FAST_RETURN_STABLE_MS = 3e4;
|
|
1438
|
+
function fastRetryDelayMs(attempt, unit) {
|
|
1439
|
+
const full = Math.min(FAST_RETRY_MAX_MS, FAST_RETRY_BASE_MS * 2 ** Math.min(attempt, 16));
|
|
1440
|
+
return full / 2 + full / 2 * unit;
|
|
1441
|
+
}
|
|
3059
1442
|
var FastMessage = {
|
|
3060
1443
|
/** Server → client: `{ sid, key, url }`, the seat's binding. */
|
|
3061
1444
|
Bind: "__fast",
|
|
3062
1445
|
/** Client → server: `{ last }`, the last seq the client sent by datagram; its inputs go by
|
|
3063
1446
|
* `room.input()` from now on. */
|
|
3064
|
-
Off: "__fast:off"
|
|
1447
|
+
Off: "__fast:off",
|
|
1448
|
+
/** Client → server: `{}`, a seat off the channel asks for a fresh binding (§17.12 *Return to the channel*). */
|
|
1449
|
+
Again: "__fast:again",
|
|
1450
|
+
/** Client → server: `{ last }`, the last seq it sent by WebSocket; its inputs go by datagram from now on. */
|
|
1451
|
+
On: "__fast:on"
|
|
3065
1452
|
};
|
|
3066
1453
|
var FastKind = { Uplink: 1, Snapshot: 2 };
|
|
3067
1454
|
var FastFault = {
|
|
@@ -3138,7 +1525,8 @@ var renderOf = (render, back) => back === BACK_NONE || render - back <= 0 ? 0 :
|
|
|
3138
1525
|
function validUplink(u) {
|
|
3139
1526
|
const { window, actions } = u;
|
|
3140
1527
|
if (!isUint(u.sid, UINT32_MAX) || !isKey(u.key) || !isUint(u.ackSnap, UINT32_MAX)) return false;
|
|
3141
|
-
if (window.length
|
|
1528
|
+
if (window.length === 0) return u.fresh === 0 && actions.length === 0;
|
|
1529
|
+
if (window.length > INPUT_REDUNDANCY_MAX || actions.length > FAST_MAX_ACTIONS) return false;
|
|
3142
1530
|
if (!isUint(u.fresh, window.length) || u.fresh < 1) return false;
|
|
3143
1531
|
if (![...actions, ...window].every(validInput)) return false;
|
|
3144
1532
|
const contiguous = window.every((s, i) => i === 0 || s.seq === window[i - 1].seq + 1);
|
|
@@ -3185,7 +1573,7 @@ function encodeUplink(u) {
|
|
|
3185
1573
|
view.setUint32(2, u.sid, true);
|
|
3186
1574
|
out.set(fromHex(u.key), 6);
|
|
3187
1575
|
view.setUint32(14, u.ackSnap, true);
|
|
3188
|
-
view.setUint32(18, u.window
|
|
1576
|
+
view.setUint32(18, u.window.at(-1)?.seq ?? 0, true);
|
|
3189
1577
|
out[22] = u.window.length;
|
|
3190
1578
|
out[23] = u.fresh;
|
|
3191
1579
|
out[24] = u.actions.length;
|
|
@@ -3231,13 +1619,18 @@ function decodeUplink(bytes, binding) {
|
|
|
3231
1619
|
const count = bytes[22];
|
|
3232
1620
|
const fresh = bytes[23];
|
|
3233
1621
|
const actionCount = bytes[24];
|
|
1622
|
+
const ackSnap = view.getUint32(14, true);
|
|
1623
|
+
if (count === 0 && newest === 0 && fresh === 0 && actionCount === 0 && view.getUint32(25, true) === 0) {
|
|
1624
|
+
if (bytes.length !== UPLINK_HEADER) return fail(FastFault.Trailing);
|
|
1625
|
+
return ok({ sid, key, ackSnap, fresh: 0, actions: [], window: [] });
|
|
1626
|
+
}
|
|
3234
1627
|
if (count < 1 || count > INPUT_REDUNDANCY_MAX || newest < count) return fail(FastFault.Count);
|
|
3235
1628
|
if (fresh < 1 || fresh > count) return fail(FastFault.Fresh);
|
|
3236
1629
|
if (actionCount > FAST_MAX_ACTIONS) return fail(FastFault.Actions);
|
|
3237
1630
|
const first = newest - count + 1;
|
|
3238
1631
|
const read = readBody(view, actionCount, count, first, view.getUint32(25, true));
|
|
3239
1632
|
if (!read.ok) return read;
|
|
3240
|
-
return ok({ sid, key, ackSnap
|
|
1633
|
+
return ok({ sid, key, ackSnap, fresh, actions: read.value.actions, window: read.value.window });
|
|
3241
1634
|
}
|
|
3242
1635
|
function readBody(view, actionCount, count, first, render) {
|
|
3243
1636
|
let at = UPLINK_HEADER;
|
|
@@ -3295,7 +1688,12 @@ function uplinkFor(sent, o) {
|
|
|
3295
1688
|
return { sid: o.sid, key: o.key, ackSnap: o.ackSnap, fresh: Math.min(o.fresh, window.length), actions, window };
|
|
3296
1689
|
}
|
|
3297
1690
|
function acceptUplink(seat, u, o) {
|
|
3298
|
-
const
|
|
1691
|
+
const newestSlot = u.window.at(-1);
|
|
1692
|
+
if (newestSlot === void 0) {
|
|
1693
|
+
const moved = u.ackSnap > seat.ackSnap && u.ackSnap <= o.snapSent;
|
|
1694
|
+
return { seat: moved ? { ...seat, ackSnap: u.ackSnap } : seat, accepted: [], skipped: 0 };
|
|
1695
|
+
}
|
|
1696
|
+
const newest = newestSlot.seq;
|
|
3299
1697
|
if (newest - seat.last > seqAheadMax(o.tickHz)) return { seat, accepted: [], skipped: 0, fault: FastFault.Ahead };
|
|
3300
1698
|
const firstFresh = newest - u.fresh + 1;
|
|
3301
1699
|
const accepted = [];
|
|
@@ -3334,12 +1732,14 @@ function diffSnapshot(base, next) {
|
|
|
3334
1732
|
return { removed, records: byKey(changed) };
|
|
3335
1733
|
}
|
|
3336
1734
|
var deltaBase = (held, ackSnap) => ackSnap > 0 && held.includes(ackSnap) ? ackSnap : 0;
|
|
1735
|
+
var bodyScratch = new Uint8Array(4096);
|
|
3337
1736
|
function bodyBytes(body) {
|
|
3338
1737
|
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
1738
|
if (!valid || body.removed.length > 65535 || body.records.length > 65535) return null;
|
|
3340
1739
|
const size = 4 + 2 * body.removed.length + body.records.reduce((n, r) => n + 4 + r.bytes.length, 0);
|
|
3341
|
-
|
|
3342
|
-
const
|
|
1740
|
+
if (size > bodyScratch.length) bodyScratch = new Uint8Array(size * 2);
|
|
1741
|
+
const out = bodyScratch.subarray(0, size);
|
|
1742
|
+
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
|
3343
1743
|
view.setUint16(0, body.removed.length, true);
|
|
3344
1744
|
let at = 2;
|
|
3345
1745
|
for (const k of body.removed) {
|
|
@@ -3476,44 +1876,588 @@ function concat(chunks) {
|
|
|
3476
1876
|
return out;
|
|
3477
1877
|
}
|
|
3478
1878
|
|
|
1879
|
+
// src/authority/fast-records.ts
|
|
1880
|
+
var MotionRecordKind = { Entity: 1, Seat: 2 };
|
|
1881
|
+
var FieldWire = {
|
|
1882
|
+
F32: "f32",
|
|
1883
|
+
U32: "u32",
|
|
1884
|
+
U16: "u16",
|
|
1885
|
+
/** A quantized orientation component in [−1, 1], uint16. */
|
|
1886
|
+
Orientation: "q-orientation",
|
|
1887
|
+
/** A quantized velocity in [−64, 64], uint16. */
|
|
1888
|
+
Velocity: "q-velocity",
|
|
1889
|
+
/** A boolean: the mask bit alone. */
|
|
1890
|
+
Flag: "flag"
|
|
1891
|
+
};
|
|
1892
|
+
var ENTITY_FIELDS = [
|
|
1893
|
+
["x", FieldWire.F32],
|
|
1894
|
+
["y", FieldWire.F32],
|
|
1895
|
+
["z", FieldWire.F32],
|
|
1896
|
+
["vx", FieldWire.F32],
|
|
1897
|
+
["vy", FieldWire.F32],
|
|
1898
|
+
["vz", FieldWire.F32],
|
|
1899
|
+
["yaw", FieldWire.F32],
|
|
1900
|
+
["pitch", FieldWire.F32],
|
|
1901
|
+
["duck", FieldWire.F32],
|
|
1902
|
+
["spin", FieldWire.F32],
|
|
1903
|
+
["fuel", FieldWire.F32],
|
|
1904
|
+
["epoch", FieldWire.U32],
|
|
1905
|
+
["mstate", FieldWire.U32],
|
|
1906
|
+
["qx", FieldWire.Orientation],
|
|
1907
|
+
["qy", FieldWire.Orientation],
|
|
1908
|
+
["qz", FieldWire.Orientation],
|
|
1909
|
+
["qw", FieldWire.Orientation],
|
|
1910
|
+
["lvx", FieldWire.Velocity],
|
|
1911
|
+
["lvy", FieldWire.Velocity],
|
|
1912
|
+
["lvz", FieldWire.Velocity],
|
|
1913
|
+
["avx", FieldWire.Velocity],
|
|
1914
|
+
["avy", FieldWire.Velocity],
|
|
1915
|
+
["avz", FieldWire.Velocity],
|
|
1916
|
+
["sleep", FieldWire.Flag],
|
|
1917
|
+
["rest", FieldWire.U16]
|
|
1918
|
+
];
|
|
1919
|
+
var SEAT_FIELDS = [
|
|
1920
|
+
["inMoveX", FieldWire.F32],
|
|
1921
|
+
["inMoveY", FieldWire.F32],
|
|
1922
|
+
["inMoveZ", FieldWire.F32],
|
|
1923
|
+
["inYaw", FieldWire.F32],
|
|
1924
|
+
["inPitch", FieldWire.F32],
|
|
1925
|
+
["inButtons", FieldWire.U32],
|
|
1926
|
+
["inRep", FieldWire.U32],
|
|
1927
|
+
["inStarve", FieldWire.U32],
|
|
1928
|
+
["inBuf", FieldWire.U32],
|
|
1929
|
+
["inAck", FieldWire.U32]
|
|
1930
|
+
];
|
|
1931
|
+
var RECORD_ID_MAX_CHARS = 64;
|
|
1932
|
+
var HEAD = 2;
|
|
1933
|
+
var MASK_BYTES = 4;
|
|
1934
|
+
var UINT32_MAX2 = 4294967295;
|
|
1935
|
+
var UINT16_MAX = 65535;
|
|
1936
|
+
var Q_SPAN = 65534;
|
|
1937
|
+
var Q_ZERO = Q_SPAN / 2;
|
|
1938
|
+
var Q_RANGE = { [FieldWire.Orientation]: 1, [FieldWire.Velocity]: 64 };
|
|
1939
|
+
var blankEntityMotion = () => ({
|
|
1940
|
+
x: 0,
|
|
1941
|
+
y: 0,
|
|
1942
|
+
z: 0,
|
|
1943
|
+
vx: 0,
|
|
1944
|
+
vy: 0,
|
|
1945
|
+
vz: 0,
|
|
1946
|
+
yaw: 0,
|
|
1947
|
+
pitch: 0,
|
|
1948
|
+
duck: 0,
|
|
1949
|
+
spin: 0,
|
|
1950
|
+
fuel: 0,
|
|
1951
|
+
epoch: 0,
|
|
1952
|
+
mstate: 0,
|
|
1953
|
+
qx: 0,
|
|
1954
|
+
qy: 0,
|
|
1955
|
+
qz: 0,
|
|
1956
|
+
qw: 0,
|
|
1957
|
+
lvx: 0,
|
|
1958
|
+
lvy: 0,
|
|
1959
|
+
lvz: 0,
|
|
1960
|
+
avx: 0,
|
|
1961
|
+
avy: 0,
|
|
1962
|
+
avz: 0,
|
|
1963
|
+
sleep: false,
|
|
1964
|
+
rest: 0
|
|
1965
|
+
});
|
|
1966
|
+
var blankSeatEcho = () => ({ inMoveX: 0, inMoveY: 0, inMoveZ: 0, inYaw: 0, inPitch: 0, inButtons: 0, inRep: 0, inStarve: 0, inBuf: 0, inAck: 0 });
|
|
1967
|
+
function quantizeCode(half, value2) {
|
|
1968
|
+
const v = Math.min(half, Math.max(-half, value2));
|
|
1969
|
+
return Math.floor((v + half) / (2 * half) * Q_SPAN + 0.5);
|
|
1970
|
+
}
|
|
1971
|
+
var dequantizeCode = (half, code) => -half + code / Q_SPAN * (2 * half);
|
|
1972
|
+
var fieldsOf = (kind) => {
|
|
1973
|
+
if (kind === MotionRecordKind.Entity) return ENTITY_FIELDS;
|
|
1974
|
+
if (kind === MotionRecordKind.Seat) return SEAT_FIELDS;
|
|
1975
|
+
return null;
|
|
1976
|
+
};
|
|
1977
|
+
var isUint2 = (v, max) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= max;
|
|
1978
|
+
function wireValue(wire, v) {
|
|
1979
|
+
switch (wire) {
|
|
1980
|
+
case FieldWire.F32: {
|
|
1981
|
+
if (typeof v !== "number" || !Number.isFinite(Math.fround(v))) return null;
|
|
1982
|
+
return Math.fround(v) === 0 ? void 0 : Math.fround(v);
|
|
1983
|
+
}
|
|
1984
|
+
case FieldWire.U32:
|
|
1985
|
+
if (!isUint2(v, UINT32_MAX2)) return null;
|
|
1986
|
+
return v === 0 ? void 0 : v;
|
|
1987
|
+
case FieldWire.U16:
|
|
1988
|
+
if (!isUint2(v, UINT16_MAX)) return null;
|
|
1989
|
+
return v === 0 ? void 0 : v;
|
|
1990
|
+
case FieldWire.Flag:
|
|
1991
|
+
if (typeof v !== "boolean") return null;
|
|
1992
|
+
return v ? 1 : void 0;
|
|
1993
|
+
default: {
|
|
1994
|
+
if (typeof v !== "number" || Number.isNaN(v)) return null;
|
|
1995
|
+
const code = quantizeCode(Q_RANGE[wire], v);
|
|
1996
|
+
return code === Q_ZERO ? void 0 : code;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
var WIDTH = { f32: 4, u32: 4, u16: 2, "q-orientation": 2, "q-velocity": 2, flag: 0 };
|
|
2001
|
+
var ID_MAX_BYTES = 255;
|
|
2002
|
+
var FIELD_MAX_BYTES = 4;
|
|
2003
|
+
var FIELDS_MAX = 32;
|
|
2004
|
+
var RECORD_SCRATCH = new Uint8Array(HEAD + ID_MAX_BYTES + MASK_BYTES + FIELDS_MAX * FIELD_MAX_BYTES);
|
|
2005
|
+
var RECORD_VIEW = new DataView(RECORD_SCRATCH.buffer);
|
|
2006
|
+
var ID_BYTES = /* @__PURE__ */ new Map();
|
|
2007
|
+
var ID_BYTES_MAX = 4096;
|
|
2008
|
+
var ID_ENCODER = new TextEncoder();
|
|
2009
|
+
var idBytesOf = (id) => {
|
|
2010
|
+
const known = ID_BYTES.get(id);
|
|
2011
|
+
if (known !== void 0) return known;
|
|
2012
|
+
if (ID_BYTES.size >= ID_BYTES_MAX) ID_BYTES.clear();
|
|
2013
|
+
const bytes = ID_ENCODER.encode(id);
|
|
2014
|
+
ID_BYTES.set(id, bytes);
|
|
2015
|
+
return bytes;
|
|
2016
|
+
};
|
|
2017
|
+
function encodeRecord(r) {
|
|
2018
|
+
const fields = fieldsOf(r.kind);
|
|
2019
|
+
const idOk = typeof r.id === "string" && r.id.length >= 1 && r.id.length <= RECORD_ID_MAX_CHARS;
|
|
2020
|
+
const id = idOk ? idBytesOf(r.id) : null;
|
|
2021
|
+
if (fields === null || id === null || id.length > ID_MAX_BYTES || r.motion === null || typeof r.motion !== "object") return { ok: false, fault: FastFault.Field };
|
|
2022
|
+
const motion = r.motion;
|
|
2023
|
+
RECORD_SCRATCH[0] = r.kind;
|
|
2024
|
+
RECORD_SCRATCH[1] = id.length;
|
|
2025
|
+
RECORD_SCRATCH.set(id, HEAD);
|
|
2026
|
+
const maskAt = HEAD + id.length;
|
|
2027
|
+
let at = maskAt + MASK_BYTES;
|
|
2028
|
+
let mask2 = 0;
|
|
2029
|
+
for (let i = 0; i < fields.length; i += 1) {
|
|
2030
|
+
const [name, wire] = fields[i];
|
|
2031
|
+
const value2 = wireValue(wire, motion[name]);
|
|
2032
|
+
if (value2 === null) return { ok: false, fault: FastFault.Field };
|
|
2033
|
+
if (value2 === void 0) continue;
|
|
2034
|
+
mask2 = (mask2 | 1 << i) >>> 0;
|
|
2035
|
+
at = writeField(RECORD_VIEW, at, wire, value2);
|
|
2036
|
+
}
|
|
2037
|
+
RECORD_VIEW.setUint32(maskAt, mask2, true);
|
|
2038
|
+
return { ok: true, value: RECORD_SCRATCH.slice(0, at) };
|
|
2039
|
+
}
|
|
2040
|
+
function writeField(view, at, wire, value2) {
|
|
2041
|
+
if (wire === FieldWire.F32) view.setFloat32(at, value2, true);
|
|
2042
|
+
else if (wire === FieldWire.U32) view.setUint32(at, value2, true);
|
|
2043
|
+
else if (wire !== FieldWire.Flag) view.setUint16(at, value2, true);
|
|
2044
|
+
return at + WIDTH[wire];
|
|
2045
|
+
}
|
|
2046
|
+
function readField(view, at, wire) {
|
|
2047
|
+
if (at + WIDTH[wire] > view.byteLength) return { ok: false, fault: FastFault.Truncated };
|
|
2048
|
+
switch (wire) {
|
|
2049
|
+
case FieldWire.Flag:
|
|
2050
|
+
return { ok: true, value: true };
|
|
2051
|
+
case FieldWire.F32: {
|
|
2052
|
+
const v = view.getFloat32(at, true);
|
|
2053
|
+
return Number.isFinite(v) && v !== 0 ? { ok: true, value: v } : { ok: false, fault: FastFault.Field };
|
|
2054
|
+
}
|
|
2055
|
+
case FieldWire.U32: {
|
|
2056
|
+
const v = view.getUint32(at, true);
|
|
2057
|
+
return v !== 0 ? { ok: true, value: v } : { ok: false, fault: FastFault.Field };
|
|
2058
|
+
}
|
|
2059
|
+
case FieldWire.U16: {
|
|
2060
|
+
const v = view.getUint16(at, true);
|
|
2061
|
+
return v !== 0 ? { ok: true, value: v } : { ok: false, fault: FastFault.Field };
|
|
2062
|
+
}
|
|
2063
|
+
default: {
|
|
2064
|
+
const code = view.getUint16(at, true);
|
|
2065
|
+
const canonical = code !== Q_ZERO && code <= Q_SPAN;
|
|
2066
|
+
return canonical ? { ok: true, value: dequantizeCode(Q_RANGE[wire], code) } : { ok: false, fault: FastFault.Field };
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
var UTF8 = new TextDecoder("utf-8", { fatal: true });
|
|
2071
|
+
function readId(bytes) {
|
|
2072
|
+
const len = bytes[1];
|
|
2073
|
+
if (HEAD + len > bytes.length) return { ok: false, fault: FastFault.Truncated };
|
|
2074
|
+
if (len === 0) return { ok: false, fault: FastFault.Field };
|
|
2075
|
+
try {
|
|
2076
|
+
const id = UTF8.decode(bytes.subarray(HEAD, HEAD + len));
|
|
2077
|
+
return id.length <= RECORD_ID_MAX_CHARS ? { ok: true, value: id } : { ok: false, fault: FastFault.Field };
|
|
2078
|
+
} catch {
|
|
2079
|
+
return { ok: false, fault: FastFault.Field };
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
function decodeRecord(bytes) {
|
|
2083
|
+
if (bytes.length < HEAD) return { ok: false, fault: FastFault.Truncated };
|
|
2084
|
+
const fields = fieldsOf(bytes[0]);
|
|
2085
|
+
if (fields === null) return { ok: false, fault: FastFault.Kind };
|
|
2086
|
+
const id = readId(bytes);
|
|
2087
|
+
if (!id.ok) return id;
|
|
2088
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
2089
|
+
let at = HEAD + bytes[1];
|
|
2090
|
+
if (at + MASK_BYTES > bytes.length) return { ok: false, fault: FastFault.Truncated };
|
|
2091
|
+
const mask2 = view.getUint32(at, true);
|
|
2092
|
+
at += MASK_BYTES;
|
|
2093
|
+
if (fields.length < 32 && mask2 >>> fields.length !== 0) return { ok: false, fault: FastFault.Flags };
|
|
2094
|
+
if (bytes[0] === MotionRecordKind.Entity) {
|
|
2095
|
+
const motion2 = readMotion(view, at, mask2, blankEntityMotion(), fields);
|
|
2096
|
+
return motion2.ok ? { ok: true, value: { kind: MotionRecordKind.Entity, id: id.value, motion: motion2.value } } : motion2;
|
|
2097
|
+
}
|
|
2098
|
+
const motion = readMotion(view, at, mask2, blankSeatEcho(), fields);
|
|
2099
|
+
return motion.ok ? { ok: true, value: { kind: MotionRecordKind.Seat, id: id.value, motion: motion.value } } : motion;
|
|
2100
|
+
}
|
|
2101
|
+
function readMotion(view, start, mask2, blank, fields) {
|
|
2102
|
+
const values = {};
|
|
2103
|
+
let at = start;
|
|
2104
|
+
for (const [i, [name, wire]] of fields.entries()) {
|
|
2105
|
+
if ((mask2 & 1 << i) === 0) continue;
|
|
2106
|
+
const v = readField(view, at, wire);
|
|
2107
|
+
if (!v.ok) return v;
|
|
2108
|
+
values[name] = v.value;
|
|
2109
|
+
at += WIDTH[wire];
|
|
2110
|
+
}
|
|
2111
|
+
if (at !== view.byteLength) return { ok: false, fault: FastFault.Trailing };
|
|
2112
|
+
return { ok: true, value: Object.assign(blank, values) };
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
// src/authority/fast-client.ts
|
|
2116
|
+
var UINT32_MAX3 = 4294967295;
|
|
2117
|
+
var FAST_OPEN_ROUND_TRIPS = 4;
|
|
2118
|
+
var FastFallbackReason = {
|
|
2119
|
+
/** The `__fast` binding was malformed, this client cannot open its url, or its socket failed. */
|
|
2120
|
+
Unreachable: "unreachable",
|
|
2121
|
+
/** No snapshot within {@link FAST_HANDSHAKE_MS} of the first datagram. */
|
|
2122
|
+
Handshake: "handshake",
|
|
2123
|
+
/** No snapshot for {@link FAST_STALL_FALLBACK_MS} after the last one. */
|
|
2124
|
+
Stall: "stall",
|
|
2125
|
+
/** The server asked for it (`__fast:off` from the server). */
|
|
2126
|
+
Server: "server",
|
|
2127
|
+
/** A browser with no `WebTransport` (or not a secure context): the seat never asked for the channel. */
|
|
2128
|
+
NoWebTransport: "no-webtransport",
|
|
2129
|
+
/** The binding carries no `wt` this browser can open: the room's server runs no relay. */
|
|
2130
|
+
NoRelay: "no-relay",
|
|
2131
|
+
/** The seat asked and no binding came within {@link FAST_HANDSHAKE_MS} of its first frame: the room's
|
|
2132
|
+
* server has no channel, or is too old to offer it. The seat was on the WebSocket all along, and a
|
|
2133
|
+
* binding that comes after all still opens. */
|
|
2134
|
+
NotOffered: "not-offered"
|
|
2135
|
+
};
|
|
2136
|
+
var isUint3 = (v, max) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= max;
|
|
2137
|
+
function isFastBind(m) {
|
|
2138
|
+
if (typeof m !== "object" || m === null) return false;
|
|
2139
|
+
const { sid, key, url } = m;
|
|
2140
|
+
const keyOk = typeof key === "string" && key.length === 2 * FAST_KEY_BYTES && /^[0-9a-f]+$/.test(key);
|
|
2141
|
+
return isUint3(sid, UINT32_MAX3) && keyOk && typeof url === "string" && url.length > 0;
|
|
2142
|
+
}
|
|
2143
|
+
function heldInputs(sent, inputAck, tickHz) {
|
|
2144
|
+
if (sent.length === 0) return [];
|
|
2145
|
+
const newest = sent[sent.length - 1].seq;
|
|
2146
|
+
const keepFrom = Math.min(inputAck, newest - inputRedundancyTicks(tickHz) + 1);
|
|
2147
|
+
const capped = Math.max(keepFrom, newest - seqAheadMax(tickHz) + 1);
|
|
2148
|
+
return sent.filter((s) => s.seq >= capped);
|
|
2149
|
+
}
|
|
2150
|
+
var recordName = (kind, id) => `${kind}:${id}`;
|
|
2151
|
+
function openFastClient(o) {
|
|
2152
|
+
let rx = emptyClientRx();
|
|
2153
|
+
let sent = [];
|
|
2154
|
+
const sentTimes = /* @__PURE__ */ new Map();
|
|
2155
|
+
let fresh = 0;
|
|
2156
|
+
let closed = false;
|
|
2157
|
+
let firstSentAt = null;
|
|
2158
|
+
let lastAppliedAt = null;
|
|
2159
|
+
let lastUplinkAt = null;
|
|
2160
|
+
let lastFrameAt = null;
|
|
2161
|
+
const keepsAlive = typeof o.bind.rev === "number" && o.bind.rev >= FAST_BIND_REV;
|
|
2162
|
+
let openedAt = null;
|
|
2163
|
+
let decodedByKey = /* @__PURE__ */ new Map();
|
|
2164
|
+
let latest = /* @__PURE__ */ new Map();
|
|
2165
|
+
const inputAck = () => rx.history.at(-1)?.inputAck ?? 0;
|
|
2166
|
+
const decodeChanged = (records) => {
|
|
2167
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
2168
|
+
const nextLatest = /* @__PURE__ */ new Map();
|
|
2169
|
+
const changed = [];
|
|
2170
|
+
for (const r of records) {
|
|
2171
|
+
const before = decodedByKey.get(r.key);
|
|
2172
|
+
const same = before !== void 0 && (before.bytes === r.bytes || sameBytes2(before.bytes, r.bytes));
|
|
2173
|
+
const record = same ? before.record : decodeOrNull(r.bytes);
|
|
2174
|
+
nextByKey.set(r.key, { bytes: r.bytes, record });
|
|
2175
|
+
if (record === null) continue;
|
|
2176
|
+
nextLatest.set(recordName(record.kind, record.id), record);
|
|
2177
|
+
if (!same) changed.push(record);
|
|
2178
|
+
}
|
|
2179
|
+
decodedByKey = nextByKey;
|
|
2180
|
+
latest = nextLatest;
|
|
2181
|
+
return changed;
|
|
2182
|
+
};
|
|
2183
|
+
function receive(bytes) {
|
|
2184
|
+
if (closed) return { applied: null };
|
|
2185
|
+
const r = receiveFragment(rx, bytes, o.bind.sid);
|
|
2186
|
+
rx = r.rx;
|
|
2187
|
+
if (r.fault !== void 0 || r.applied === null) return { applied: null, fault: r.fault };
|
|
2188
|
+
const held = rx.history.at(-1);
|
|
2189
|
+
if (held === void 0) return { applied: null };
|
|
2190
|
+
const before = lastAppliedSnap;
|
|
2191
|
+
lastAppliedSnap = held.snap;
|
|
2192
|
+
lastAppliedAt = o.now();
|
|
2193
|
+
sent = heldInputs(sent, held.inputAck, o.tickHz);
|
|
2194
|
+
const applied = { snap: held.snap, tick: held.tick, inputAck: held.inputAck, sent: held.sent, changed: decodeChanged(held.records) };
|
|
2195
|
+
o.onSnapshot?.(applied);
|
|
2196
|
+
const eager = applied.changed.length >= FAST_ACK_EAGER_RECORDS && (lastUplinkAt === null || o.now() - lastUplinkAt >= FAST_ACK_EAGER_GAP_MS);
|
|
2197
|
+
if (held.snap - before >= FAST_CHALLENGE_JUMP || eager) sendEmpty();
|
|
2198
|
+
return { applied };
|
|
2199
|
+
}
|
|
2200
|
+
function sendEmpty() {
|
|
2201
|
+
if (closed || !keepsAlive || socket === null) return;
|
|
2202
|
+
const bytes = encodeUplink({ sid: o.bind.sid, key: o.bind.key, ackSnap: rx.newest, fresh: 0, actions: [], window: [] });
|
|
2203
|
+
if (!bytes.ok) return;
|
|
2204
|
+
const now = o.now();
|
|
2205
|
+
firstSentAt ??= now;
|
|
2206
|
+
lastUplinkAt = now;
|
|
2207
|
+
socket.send(bytes.value);
|
|
2208
|
+
}
|
|
2209
|
+
let lastAppliedSnap = 0;
|
|
2210
|
+
const socket = o.open(
|
|
2211
|
+
o.bind.url,
|
|
2212
|
+
(bytes) => {
|
|
2213
|
+
receive(bytes);
|
|
2214
|
+
},
|
|
2215
|
+
o.bind
|
|
2216
|
+
);
|
|
2217
|
+
if (socket === null) return null;
|
|
2218
|
+
const largestUplink = uplinkMaxBytes(o.tickHz);
|
|
2219
|
+
return {
|
|
2220
|
+
bind: o.bind,
|
|
2221
|
+
get rx() {
|
|
2222
|
+
return rx;
|
|
2223
|
+
},
|
|
2224
|
+
get lastQueued() {
|
|
2225
|
+
return sent.at(-1)?.seq ?? 0;
|
|
2226
|
+
},
|
|
2227
|
+
get closed() {
|
|
2228
|
+
return closed;
|
|
2229
|
+
},
|
|
2230
|
+
opening: () => socket.opening?.() === true,
|
|
2231
|
+
queue(input) {
|
|
2232
|
+
if (closed) return;
|
|
2233
|
+
sent.push({ seq: input.seq, tick: input.tick, render: input.render, input: { ...input.input } });
|
|
2234
|
+
fresh += 1;
|
|
2235
|
+
},
|
|
2236
|
+
flush() {
|
|
2237
|
+
if (closed || fresh === 0) return;
|
|
2238
|
+
const now = o.now();
|
|
2239
|
+
for (const s of sent.slice(-fresh)) sentTimes.set(s.seq, now);
|
|
2240
|
+
forgetSendTimes(sentTimes, (sent.at(-1)?.seq ?? 0) - seqAheadMax(o.tickHz) + 1);
|
|
2241
|
+
const window = inputRedundancyTicks(o.tickHz);
|
|
2242
|
+
let from = sent.length - fresh;
|
|
2243
|
+
fresh = 0;
|
|
2244
|
+
while (from < sent.length) {
|
|
2245
|
+
const to = Math.min(from + window, sent.length);
|
|
2246
|
+
const u = uplinkFor(sent.slice(0, to), { sid: o.bind.sid, key: o.bind.key, ackSnap: rx.newest, inputAck: inputAck(), fresh: to - from, tickHz: o.tickHz });
|
|
2247
|
+
from = to;
|
|
2248
|
+
const bytes = encodeUplink(u);
|
|
2249
|
+
if (!bytes.ok) continue;
|
|
2250
|
+
firstSentAt ??= now;
|
|
2251
|
+
lastUplinkAt = now;
|
|
2252
|
+
socket.send(bytes.value);
|
|
2253
|
+
}
|
|
2254
|
+
},
|
|
2255
|
+
framed() {
|
|
2256
|
+
const now = o.now();
|
|
2257
|
+
const gap = lastFrameAt === null ? 0 : now - lastFrameAt;
|
|
2258
|
+
lastFrameAt = now;
|
|
2259
|
+
if (gap <= FAST_FRAME_GAP_MS) return;
|
|
2260
|
+
if (lastAppliedAt !== null) lastAppliedAt = Math.min(now, lastAppliedAt + gap);
|
|
2261
|
+
if (firstSentAt !== null) firstSentAt = Math.min(now, firstSentAt + gap);
|
|
2262
|
+
},
|
|
2263
|
+
keepalive() {
|
|
2264
|
+
if (closed || !keepsAlive) return;
|
|
2265
|
+
const every = lastAppliedAt === null ? FAST_KEEPALIVE_BIND_MS : FAST_KEEPALIVE_MS;
|
|
2266
|
+
if (lastUplinkAt === null || o.now() - lastUplinkAt >= every) sendEmpty();
|
|
2267
|
+
},
|
|
2268
|
+
inputAt: (seq) => sent.find((s) => s.seq === seq),
|
|
2269
|
+
sentAt: (seq) => sentTimes.get(seq),
|
|
2270
|
+
motionOf: (kind, id) => latest.get(recordName(kind, id)),
|
|
2271
|
+
records: () => [...latest.values()],
|
|
2272
|
+
due() {
|
|
2273
|
+
if (closed) return null;
|
|
2274
|
+
if (socket.failed?.() === true) return FastFallbackReason.Unreachable;
|
|
2275
|
+
const room = socket.maxDatagramBytes?.();
|
|
2276
|
+
if (room !== void 0 && room < largestUplink) return FastFallbackReason.Unreachable;
|
|
2277
|
+
const now = o.now();
|
|
2278
|
+
if (lastAppliedAt !== null) return now - lastAppliedAt > FAST_STALL_FALLBACK_MS ? FastFallbackReason.Stall : null;
|
|
2279
|
+
if (firstSentAt === null) return null;
|
|
2280
|
+
const opening = socket.opening?.();
|
|
2281
|
+
if (opening === true) {
|
|
2282
|
+
const openBound = Math.max(FAST_HANDSHAKE_MS, FAST_OPEN_ROUND_TRIPS * (o.roundTripMs?.() ?? 0));
|
|
2283
|
+
return now - firstSentAt > openBound ? FastFallbackReason.Handshake : null;
|
|
2284
|
+
}
|
|
2285
|
+
if (opening === false) openedAt ??= now;
|
|
2286
|
+
const from = opening === false ? Math.max(firstSentAt, openedAt ?? now) : firstSentAt;
|
|
2287
|
+
return now - from > FAST_HANDSHAKE_MS ? FastFallbackReason.Handshake : null;
|
|
2288
|
+
},
|
|
2289
|
+
receive,
|
|
2290
|
+
close() {
|
|
2291
|
+
if (closed) return;
|
|
2292
|
+
closed = true;
|
|
2293
|
+
socket.close();
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
function forgetSendTimes(times, from) {
|
|
2298
|
+
for (const seq of times.keys()) {
|
|
2299
|
+
if (seq >= from) return;
|
|
2300
|
+
times.delete(seq);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
function decodeOrNull(bytes) {
|
|
2304
|
+
const d = decodeRecord(bytes);
|
|
2305
|
+
return d.ok ? d.value : null;
|
|
2306
|
+
}
|
|
2307
|
+
var sameBytes2 = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
2308
|
+
|
|
2309
|
+
// src/authority/fast-webtransport.ts
|
|
2310
|
+
var FAST_WT_HASH_HEX_DIGITS = 64;
|
|
2311
|
+
var FAST_WT_HASHES_MAX = 4;
|
|
2312
|
+
var WT_SCHEME = "https:";
|
|
2313
|
+
var CONNECT_QUEUE_MAX = 32;
|
|
2314
|
+
var HASH_PATTERN = /^[0-9a-f]+$/;
|
|
2315
|
+
var RELIABLE_ONLY = "reliable-only";
|
|
2316
|
+
function fastWebTransportOf(bind) {
|
|
2317
|
+
const wt = bind.wt;
|
|
2318
|
+
if (typeof wt !== "object" || wt === null) return null;
|
|
2319
|
+
const { url, hashes } = wt;
|
|
2320
|
+
if (typeof url !== "string" || !Array.isArray(hashes)) return null;
|
|
2321
|
+
if (hashes.length === 0 || hashes.length > FAST_WT_HASHES_MAX) return null;
|
|
2322
|
+
const hashOk = (h) => typeof h === "string" && h.length === FAST_WT_HASH_HEX_DIGITS && HASH_PATTERN.test(h);
|
|
2323
|
+
if (!hashes.every(hashOk)) return null;
|
|
2324
|
+
let parsed2;
|
|
2325
|
+
try {
|
|
2326
|
+
parsed2 = new URL(url);
|
|
2327
|
+
} catch {
|
|
2328
|
+
return null;
|
|
2329
|
+
}
|
|
2330
|
+
const bare = parsed2.username === "" && parsed2.password === "" && parsed2.search === "" && parsed2.hash === "";
|
|
2331
|
+
if (parsed2.protocol !== WT_SCHEME || parsed2.hostname === "" || !bare) return null;
|
|
2332
|
+
return { url, hashes: hashes.map((h) => fromHex(h)) };
|
|
2333
|
+
}
|
|
2334
|
+
function webTransportOptions(target) {
|
|
2335
|
+
return {
|
|
2336
|
+
serverCertificateHashes: target.hashes.map((value2) => ({ algorithm: "sha-256", value: value2 })),
|
|
2337
|
+
requireUnreliable: true,
|
|
2338
|
+
allowPooling: false,
|
|
2339
|
+
congestionControl: "low-latency"
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
var carriesFast = (t) => t.reliability !== RELIABLE_ONLY;
|
|
2343
|
+
function datagramWriter(t) {
|
|
2344
|
+
const writable = t.datagrams.createWritable?.() ?? t.datagrams.writable;
|
|
2345
|
+
return writable?.getWriter() ?? null;
|
|
2346
|
+
}
|
|
2347
|
+
var SocketState = { Connecting: "connecting", Open: "open", Failed: "failed", Closed: "closed" };
|
|
2348
|
+
function webTransportFastOpener(WebTransport) {
|
|
2349
|
+
const open = (_url, receive, bind) => {
|
|
2350
|
+
const target = bind === void 0 ? null : fastWebTransportOf(bind);
|
|
2351
|
+
if (target === null || WebTransport === void 0) return null;
|
|
2352
|
+
let t;
|
|
2353
|
+
try {
|
|
2354
|
+
t = new WebTransport(target.url, webTransportOptions(target));
|
|
2355
|
+
} catch {
|
|
2356
|
+
return null;
|
|
2357
|
+
}
|
|
2358
|
+
let state = SocketState.Connecting;
|
|
2359
|
+
let waiting = [];
|
|
2360
|
+
let writer = null;
|
|
2361
|
+
const fail2 = () => {
|
|
2362
|
+
if (state === SocketState.Closed || state === SocketState.Failed) return;
|
|
2363
|
+
state = SocketState.Failed;
|
|
2364
|
+
writer = null;
|
|
2365
|
+
waiting = [];
|
|
2366
|
+
try {
|
|
2367
|
+
t.close();
|
|
2368
|
+
} catch {
|
|
2369
|
+
}
|
|
2370
|
+
};
|
|
2371
|
+
const write = (w, bytes) => void w.write(bytes).catch(() => {
|
|
2372
|
+
});
|
|
2373
|
+
const readAll = async (reader) => {
|
|
2374
|
+
for (; ; ) {
|
|
2375
|
+
const r = await reader.read();
|
|
2376
|
+
if (r.done) return;
|
|
2377
|
+
if (state === SocketState.Open) receive(r.value);
|
|
2378
|
+
}
|
|
2379
|
+
};
|
|
2380
|
+
t.ready.then(
|
|
2381
|
+
() => {
|
|
2382
|
+
if (state !== SocketState.Connecting) return;
|
|
2383
|
+
if (!carriesFast(t)) return fail2();
|
|
2384
|
+
const w = datagramWriter(t);
|
|
2385
|
+
if (w === null) return fail2();
|
|
2386
|
+
state = SocketState.Open;
|
|
2387
|
+
writer = w;
|
|
2388
|
+
for (const bytes of waiting) write(w, bytes);
|
|
2389
|
+
waiting = [];
|
|
2390
|
+
readAll(t.datagrams.readable.getReader()).then(fail2, fail2);
|
|
2391
|
+
},
|
|
2392
|
+
() => fail2()
|
|
2393
|
+
);
|
|
2394
|
+
t.closed.then(fail2, fail2);
|
|
2395
|
+
const socket = {
|
|
2396
|
+
send(bytes) {
|
|
2397
|
+
if (state === SocketState.Open && writer !== null) write(writer, bytes);
|
|
2398
|
+
else if (state === SocketState.Connecting) waiting = [...waiting, bytes].slice(-CONNECT_QUEUE_MAX);
|
|
2399
|
+
},
|
|
2400
|
+
close() {
|
|
2401
|
+
if (state === SocketState.Closed) return;
|
|
2402
|
+
const was = state;
|
|
2403
|
+
state = SocketState.Closed;
|
|
2404
|
+
writer = null;
|
|
2405
|
+
waiting = [];
|
|
2406
|
+
if (was === SocketState.Failed) return;
|
|
2407
|
+
try {
|
|
2408
|
+
t.close();
|
|
2409
|
+
} catch {
|
|
2410
|
+
}
|
|
2411
|
+
},
|
|
2412
|
+
failed: () => state === SocketState.Failed,
|
|
2413
|
+
opening: () => state === SocketState.Connecting,
|
|
2414
|
+
// The browser's outgoing limit, known once the session is open: the browser sends only uplinks,
|
|
2415
|
+
// so the seat's largest uplink must fit (Chromium reports 1024; a 120 Hz seat's is 889).
|
|
2416
|
+
maxDatagramBytes() {
|
|
2417
|
+
const size = state === SocketState.Open ? t.datagrams.maxDatagramSize : void 0;
|
|
2418
|
+
return typeof size === "number" ? size : void 0;
|
|
2419
|
+
}
|
|
2420
|
+
};
|
|
2421
|
+
return socket;
|
|
2422
|
+
};
|
|
2423
|
+
open.whyNull = (bind) => {
|
|
2424
|
+
if (WebTransport === void 0) return FastFallbackReason.NoWebTransport;
|
|
2425
|
+
return fastWebTransportOf(bind) === null ? FastFallbackReason.NoRelay : FastFallbackReason.Unreachable;
|
|
2426
|
+
};
|
|
2427
|
+
return open;
|
|
2428
|
+
}
|
|
2429
|
+
var webTransportOf = (scope) => typeof scope.WebTransport === "function" ? scope.WebTransport : void 0;
|
|
2430
|
+
var openWebTransportFastSocket = (url, receive, bind) => webTransportFastOpener(webTransportOf(globalThis))(url, receive, bind);
|
|
2431
|
+
openWebTransportFastSocket.whyNull = (bind) => webTransportFastOpener(webTransportOf(globalThis)).whyNull?.(bind) ?? FastFallbackReason.Unreachable;
|
|
2432
|
+
function chooseFastSocket(given, scope = globalThis) {
|
|
2433
|
+
if (given === false) return { open: void 0, fallback: null };
|
|
2434
|
+
if (given !== void 0) return { open: given, fallback: null };
|
|
2435
|
+
if (webTransportOf(scope) !== void 0) return { open: openWebTransportFastSocket, fallback: null };
|
|
2436
|
+
const inBrowser = scope.document !== void 0 || typeof scope.importScripts === "function";
|
|
2437
|
+
return { open: void 0, fallback: inBrowser ? FastFallbackReason.NoWebTransport : null };
|
|
2438
|
+
}
|
|
2439
|
+
|
|
3479
2440
|
export {
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
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,
|
|
2441
|
+
AuthorityRoomLimits,
|
|
2442
|
+
validMatchKey,
|
|
2443
|
+
validRoomCode,
|
|
2444
|
+
matchKeyOf,
|
|
2445
|
+
AuthorityJoinMethod,
|
|
2446
|
+
AuthorityTargetRefusal,
|
|
2447
|
+
planAuthorityJoin,
|
|
2448
|
+
nextCodeRegion,
|
|
2449
|
+
selectMatchWorld,
|
|
3511
2450
|
PHYSICS_CONTRACT,
|
|
3512
2451
|
PHYSICS_DEFAULT_LIMITS,
|
|
3513
2452
|
PHYSICS_LIMIT_NAMES,
|
|
3514
2453
|
PHYSICS_SCALED_LIMITS,
|
|
3515
2454
|
PHYSICS_REAL_LIMITS,
|
|
3516
2455
|
PHYSICS_CEILING_FACTOR,
|
|
2456
|
+
PHYSICS_STATIC_PIECE_BYTES,
|
|
2457
|
+
PHYSICS_PREDICT_MAX_BOXES,
|
|
2458
|
+
PHYSICS_PREDICT_MAX_RAMPS,
|
|
2459
|
+
physicsWorldPredicts,
|
|
2460
|
+
physicsLimitsForWorld,
|
|
3517
2461
|
PHYSICS_DEFAULT_CEILING,
|
|
3518
2462
|
physicsCeilingValid,
|
|
3519
2463
|
physicsLimitsAt,
|
|
@@ -3588,11 +2532,16 @@ export {
|
|
|
3588
2532
|
isLiftId,
|
|
3589
2533
|
resolveLifts,
|
|
3590
2534
|
InputRepeat,
|
|
2535
|
+
INPUT_SETTLE_MS,
|
|
3591
2536
|
INPUT_PRESETS,
|
|
3592
2537
|
INPUTS_DEFAULT,
|
|
3593
2538
|
InputConfigError,
|
|
3594
2539
|
resolveInputConfig,
|
|
3595
2540
|
presetInputRepeat,
|
|
2541
|
+
AuthorityLifetime,
|
|
2542
|
+
LIFETIME_DEFAULT,
|
|
2543
|
+
LifetimeConfigError,
|
|
2544
|
+
resolveLifetime,
|
|
3596
2545
|
GuestGlobal,
|
|
3597
2546
|
AuthorityLimits,
|
|
3598
2547
|
TICK_RATES,
|
|
@@ -3619,6 +2568,18 @@ export {
|
|
|
3619
2568
|
FAST_MAX_RECORD_BYTES,
|
|
3620
2569
|
FAST_HANDSHAKE_MS,
|
|
3621
2570
|
FAST_STALL_FALLBACK_MS,
|
|
2571
|
+
FAST_KEEPALIVE_MS,
|
|
2572
|
+
FAST_KEEPALIVE_BIND_MS,
|
|
2573
|
+
FAST_ACK_EAGER_RECORDS,
|
|
2574
|
+
FAST_ACK_EAGER_GAP_MS,
|
|
2575
|
+
FAST_CHALLENGE_JUMP,
|
|
2576
|
+
FAST_FRAME_GAP_MS,
|
|
2577
|
+
FAST_BIND_REV,
|
|
2578
|
+
FAST_RETURN_PROOF,
|
|
2579
|
+
FAST_RETRY_BASE_MS,
|
|
2580
|
+
FAST_RETRY_MAX_MS,
|
|
2581
|
+
FAST_RETURN_STABLE_MS,
|
|
2582
|
+
fastRetryDelayMs,
|
|
3622
2583
|
FastMessage,
|
|
3623
2584
|
FastKind,
|
|
3624
2585
|
FastFault,
|
|
@@ -3642,5 +2603,25 @@ export {
|
|
|
3642
2603
|
parseBody,
|
|
3643
2604
|
applyBody,
|
|
3644
2605
|
emptyClientRx,
|
|
3645
|
-
receiveFragment
|
|
2606
|
+
receiveFragment,
|
|
2607
|
+
MotionRecordKind,
|
|
2608
|
+
RECORD_ID_MAX_CHARS,
|
|
2609
|
+
blankEntityMotion,
|
|
2610
|
+
blankSeatEcho,
|
|
2611
|
+
quantizeCode,
|
|
2612
|
+
dequantizeCode,
|
|
2613
|
+
encodeRecord,
|
|
2614
|
+
decodeRecord,
|
|
2615
|
+
FAST_OPEN_ROUND_TRIPS,
|
|
2616
|
+
FastFallbackReason,
|
|
2617
|
+
isFastBind,
|
|
2618
|
+
heldInputs,
|
|
2619
|
+
openFastClient,
|
|
2620
|
+
FAST_WT_HASH_HEX_DIGITS,
|
|
2621
|
+
FAST_WT_HASHES_MAX,
|
|
2622
|
+
fastWebTransportOf,
|
|
2623
|
+
webTransportOptions,
|
|
2624
|
+
webTransportFastOpener,
|
|
2625
|
+
openWebTransportFastSocket,
|
|
2626
|
+
chooseFastSocket
|
|
3646
2627
|
};
|