@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
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// src/authority/fast-udp-socket.ts
|
|
2
|
+
var IPV6_FAMILY = 6;
|
|
3
|
+
var lookupTurns = /* @__PURE__ */ new Map();
|
|
4
|
+
var UDP_SCHEME = "udp:";
|
|
5
|
+
var PORT_MAX = 65535;
|
|
6
|
+
var CONNECT_QUEUE_MAX = 32;
|
|
7
|
+
var BARE_PATHS = ["", "/"];
|
|
8
|
+
var onlyHostAndPort = (u) => u.username === "" && u.password === "" && BARE_PATHS.includes(u.pathname) && u.search === "" && u.hash === "";
|
|
9
|
+
function udpEndpoint(url) {
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = new URL(url);
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const port = Number(parsed.port);
|
|
17
|
+
const portOk = Number.isInteger(port) && port >= 1 && port <= PORT_MAX;
|
|
18
|
+
if (parsed.protocol !== UDP_SCHEME || !portOk || parsed.hostname === "" || !onlyHostAndPort(parsed)) return null;
|
|
19
|
+
return { host: parsed.hostname.replace(/^\[(.*)\]$/, "$1"), port };
|
|
20
|
+
}
|
|
21
|
+
var udpFastOpener = ({ createSocket, lookup, isIP }) => (url, receive) => {
|
|
22
|
+
const to = udpEndpoint(url);
|
|
23
|
+
if (to === null) return null;
|
|
24
|
+
let socket = null;
|
|
25
|
+
let connected = false;
|
|
26
|
+
let closed = false;
|
|
27
|
+
let waiting = [];
|
|
28
|
+
const attach = (address, family) => {
|
|
29
|
+
if (closed) return;
|
|
30
|
+
const s = createSocket(family === IPV6_FAMILY ? "udp6" : "udp4");
|
|
31
|
+
socket = s;
|
|
32
|
+
s.on("message", (msg) => receive(new Uint8Array(msg.buffer, msg.byteOffset, msg.byteLength)));
|
|
33
|
+
s.on("error", () => {
|
|
34
|
+
});
|
|
35
|
+
s.connect(to.port, address, () => {
|
|
36
|
+
connected = true;
|
|
37
|
+
if (!closed) for (const bytes of waiting) s.send(bytes);
|
|
38
|
+
waiting = [];
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
const literal = isIP(to.host);
|
|
42
|
+
if (literal !== 0) attach(to.host, literal);
|
|
43
|
+
else {
|
|
44
|
+
lookup(to.host, { all: true, verbatim: true }, (err, found) => {
|
|
45
|
+
if (err !== null || found.length === 0) return;
|
|
46
|
+
const turn = (lookupTurns.get(to.host) ?? 0) % found.length;
|
|
47
|
+
lookupTurns.set(to.host, turn + 1);
|
|
48
|
+
attach(found[turn].address, found[turn].family);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const fast = {
|
|
52
|
+
send(bytes) {
|
|
53
|
+
if (closed) return;
|
|
54
|
+
if (connected) socket?.send(bytes);
|
|
55
|
+
else waiting = [...waiting, bytes].slice(-CONNECT_QUEUE_MAX);
|
|
56
|
+
},
|
|
57
|
+
close() {
|
|
58
|
+
if (closed) return;
|
|
59
|
+
closed = true;
|
|
60
|
+
socket?.close();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
return fast;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
udpEndpoint,
|
|
68
|
+
udpFastOpener
|
|
69
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { I as InputData } from './module-
|
|
1
|
+
import { I as InputData } from './module-CSpXrzFc.js';
|
|
2
2
|
|
|
3
3
|
/** The datagram format's version, the first byte of every datagram. */
|
|
4
4
|
declare const FAST_VERSION = 1;
|
|
@@ -21,7 +21,7 @@ declare const FAST_GAP_FILL_MS = 250;
|
|
|
21
21
|
declare const FAST_SEQ_AHEAD_MS = 5000;
|
|
22
22
|
/** Snapshots each side holds as bases: 533 ms at 60 Hz, over a 409 ms p99 round trip plus an episode. */
|
|
23
23
|
declare const FAST_SNAPSHOT_HISTORY = 32;
|
|
24
|
-
/** The most fragments one snapshot may have (about 37 KB); a larger body
|
|
24
|
+
/** The most fragments one snapshot may have (about 37 KB); a larger body goes as a partial snapshot (the host cuts it, §17.12). */
|
|
25
25
|
declare const FAST_MAX_FRAGMENTS = 32;
|
|
26
26
|
/** Incomplete snapshots a client gathers at once; the oldest gives way. */
|
|
27
27
|
declare const FAST_REASSEMBLY_SLOTS = 4;
|
|
@@ -31,6 +31,41 @@ declare const FAST_MAX_RECORD_BYTES = 1024;
|
|
|
31
31
|
declare const FAST_HANDSHAKE_MS = 1000;
|
|
32
32
|
/** A client with no snapshot for this long falls back to the WebSocket. */
|
|
33
33
|
declare const FAST_STALL_FALLBACK_MS = 2000;
|
|
34
|
+
/** A seat with nothing to send sends an empty uplink this often while snapshots reach it (§17.12
|
|
35
|
+
* *Keepalive*): under {@link FAST_SNAPSHOT_HISTORY} snapshots at 120 Hz, so its deltas stay deltas, and far
|
|
36
|
+
* under the shortest NAT timer measured, 10 s (Richter et al., IMC 2016: 74% of NATs expire idle UDP state
|
|
37
|
+
* in 1 min or less, range 10 to 200 s; RFC 9308 §3.2: assume 30 s). */
|
|
38
|
+
declare const FAST_KEEPALIVE_MS = 250;
|
|
39
|
+
/** Likewise before its first snapshot: a lost first datagram or challenge costs no more than this. */
|
|
40
|
+
declare const FAST_KEEPALIVE_BIND_MS = 50;
|
|
41
|
+
/** A snapshot changing this many records is acknowledged as it lands by a seat with nothing else to send:
|
|
42
|
+
* its next delta shrinks by far more than the 57 bytes. */
|
|
43
|
+
declare const FAST_ACK_EAGER_RECORDS = 32;
|
|
44
|
+
/** No eager ack within this long of another uplink: a seat sending inputs every frame sends none. */
|
|
45
|
+
declare const FAST_ACK_EAGER_GAP_MS = 20;
|
|
46
|
+
/** A snapshot id at least this far past the client's newest is a return-path challenge (§17.12 *Return
|
|
47
|
+
* path*): the client echoes it at once, whatever the keepalive's clock says. */
|
|
48
|
+
declare const FAST_CHALLENGE_JUMP = 65536;
|
|
49
|
+
/** No frame for this long: the stall deadline's clock did not run, as the seat could not have read a
|
|
50
|
+
* snapshot or sent an uplink (a background tab, a suspended app, a long hitch). */
|
|
51
|
+
declare const FAST_FRAME_GAP_MS = 250;
|
|
52
|
+
/** The binding's `rev` from a host that takes empty uplinks and challenges addresses (§17.12 *Keepalive*)
|
|
53
|
+
* and offers a fresh binding to a seat that asks (*Return to the channel*). */
|
|
54
|
+
declare const FAST_BIND_REV = 2;
|
|
55
|
+
/** Snapshots a seat must apply on a fresh binding before its inputs and motion move to it: enough to trust
|
|
56
|
+
* the path both ways, little enough to swap within a tenth of a second. */
|
|
57
|
+
declare const FAST_RETURN_PROOF = 3;
|
|
58
|
+
/** A retry's delay at attempt 0, before jitter: a dropped-out Wi-Fi is usually back. */
|
|
59
|
+
declare const FAST_RETRY_BASE_MS = 1000;
|
|
60
|
+
/** Its most, before jitter: a blocked network costs a few datagrams a minute, forever. */
|
|
61
|
+
declare const FAST_RETRY_MAX_MS = 60000;
|
|
62
|
+
/** A seat that held the channel this long starts its next backoff from attempt 0; one that lost it sooner
|
|
63
|
+
* goes on climbing, so a flapping path does not swap back and forth. */
|
|
64
|
+
declare const FAST_RETURN_STABLE_MS = 30000;
|
|
65
|
+
/** The delay before retry number `attempt` (from 0), jittered by `unit` in [0, 1): doubling from
|
|
66
|
+
* {@link FAST_RETRY_BASE_MS} to {@link FAST_RETRY_MAX_MS}, half of it random ("equal jitter"), so seats
|
|
67
|
+
* behind one failed link do not come back as one. */
|
|
68
|
+
declare function fastRetryDelayMs(attempt: number, unit: number): number;
|
|
34
69
|
/** The reliable messages of the fast channel (§17.12): the server's binding at join and the client's
|
|
35
70
|
* fallback. Reserved names (`__`), so no game event can be mistaken for one. */
|
|
36
71
|
declare const FastMessage: {
|
|
@@ -39,6 +74,10 @@ declare const FastMessage: {
|
|
|
39
74
|
/** Client → server: `{ last }`, the last seq the client sent by datagram; its inputs go by
|
|
40
75
|
* `room.input()` from now on. */
|
|
41
76
|
readonly Off: "__fast:off";
|
|
77
|
+
/** Client → server: `{}`, a seat off the channel asks for a fresh binding (§17.12 *Return to the channel*). */
|
|
78
|
+
readonly Again: "__fast:again";
|
|
79
|
+
/** Client → server: `{ last }`, the last seq it sent by WebSocket; its inputs go by datagram from now on. */
|
|
80
|
+
readonly On: "__fast:on";
|
|
42
81
|
};
|
|
43
82
|
type FastMessage = (typeof FastMessage)[keyof typeof FastMessage];
|
|
44
83
|
/** Where a browser reaches the seat's channel (§17.12 *WebTransport*): a relay's WebTransport session
|
|
@@ -56,11 +95,21 @@ interface FastBindMessage {
|
|
|
56
95
|
key: string;
|
|
57
96
|
url: string;
|
|
58
97
|
wt?: FastWebTransport;
|
|
98
|
+
/** The host's revision of the channel ({@link FAST_BIND_REV}); absent from a host older than it, to which
|
|
99
|
+
* a client sends no empty uplink and asks for no fresh binding. */
|
|
100
|
+
rev?: number;
|
|
101
|
+
/** A fresh binding for a seat on the WebSocket (§17.12 *Return to the channel*): the WebSocket carries
|
|
102
|
+
* everything until the seat's `__fast:on`. */
|
|
103
|
+
probe?: boolean;
|
|
59
104
|
}
|
|
60
105
|
/** The `__fast:off` message's payload. */
|
|
61
106
|
interface FastOffMessage {
|
|
62
107
|
last: number;
|
|
63
108
|
}
|
|
109
|
+
/** The `__fast:on` message's payload. */
|
|
110
|
+
interface FastOnMessage {
|
|
111
|
+
last: number;
|
|
112
|
+
}
|
|
64
113
|
/** The datagram kinds, the second byte. */
|
|
65
114
|
declare const FastKind: {
|
|
66
115
|
readonly Uplink: 1;
|
|
@@ -368,6 +417,10 @@ declare function encodeRecord(r: MotionRecord): FastResult<Uint8Array>;
|
|
|
368
417
|
* left out reads as its default. */
|
|
369
418
|
declare function decodeRecord(bytes: Uint8Array): FastResult<MotionRecord>;
|
|
370
419
|
|
|
420
|
+
/** Round trips a socket that opens asynchronously may take to open, where that is longer than
|
|
421
|
+
* {@link FAST_HANDSHAKE_MS}: WebTransport over QUIC and HTTP/3 took three in Safari 26 (Chromium's two
|
|
422
|
+
* are within it), so a long route's seat is not sent back for its session's own handshake. */
|
|
423
|
+
declare const FAST_OPEN_ROUND_TRIPS = 4;
|
|
371
424
|
/** A datagram socket to the room's fast endpoint. `send` never throws; a lost datagram is the
|
|
372
425
|
* channel's normal case. */
|
|
373
426
|
interface FastDatagramSocket {
|
|
@@ -380,12 +433,20 @@ interface FastDatagramSocket {
|
|
|
380
433
|
* `maxDatagramSize`): below the seat's largest uplink ({@link uplinkMaxBytes}), the seat falls back
|
|
381
434
|
* at once. */
|
|
382
435
|
maxDatagramBytes?(): number | undefined;
|
|
436
|
+
/** True while a socket that opens asynchronously is still opening (WebTransport: round trips before a
|
|
437
|
+
* first datagram can leave). The handshake then runs twice: to the socket opening, {@link FAST_HANDSHAKE_MS}
|
|
438
|
+
* or {@link FAST_OPEN_ROUND_TRIPS} of the seat's round trips, whichever is longer; and from then to the
|
|
439
|
+
* first snapshot, {@link FAST_HANDSHAKE_MS}. */
|
|
440
|
+
opening?(): boolean;
|
|
383
441
|
}
|
|
384
442
|
/** Opens a socket to a `__fast` `url`, handing each datagram that arrives to `receive`; null when this
|
|
385
443
|
* client cannot reach that kind of url (the seat then falls back at once). `bind` is the whole
|
|
386
444
|
* binding (the client always passes it), for an opener that reaches the channel another way
|
|
387
445
|
* (`bind.wt`, WebTransport). */
|
|
388
|
-
type FastSocketOpener = (url: string, receive: (bytes: Uint8Array) => void, bind?: FastBindMessage) => FastDatagramSocket | null
|
|
446
|
+
type FastSocketOpener = ((url: string, receive: (bytes: Uint8Array) => void, bind?: FastBindMessage) => FastDatagramSocket | null) & {
|
|
447
|
+
/** Why this opener answers null for `bind` (default {@link FastFallbackReason.Unreachable}). */
|
|
448
|
+
whyNull?: (bind: FastBindMessage) => FastFallbackReason;
|
|
449
|
+
};
|
|
389
450
|
/** Why a fast seat went back to the WebSocket. */
|
|
390
451
|
declare const FastFallbackReason: {
|
|
391
452
|
/** The `__fast` binding was malformed, this client cannot open its url, or its socket failed. */
|
|
@@ -396,6 +457,14 @@ declare const FastFallbackReason: {
|
|
|
396
457
|
readonly Stall: "stall";
|
|
397
458
|
/** The server asked for it (`__fast:off` from the server). */
|
|
398
459
|
readonly Server: "server";
|
|
460
|
+
/** A browser with no `WebTransport` (or not a secure context): the seat never asked for the channel. */
|
|
461
|
+
readonly NoWebTransport: "no-webtransport";
|
|
462
|
+
/** The binding carries no `wt` this browser can open: the room's server runs no relay. */
|
|
463
|
+
readonly NoRelay: "no-relay";
|
|
464
|
+
/** The seat asked and no binding came within {@link FAST_HANDSHAKE_MS} of its first frame: the room's
|
|
465
|
+
* server has no channel, or is too old to offer it. The seat was on the WebSocket all along, and a
|
|
466
|
+
* binding that comes after all still opens. */
|
|
467
|
+
readonly NotOffered: "not-offered";
|
|
399
468
|
};
|
|
400
469
|
type FastFallbackReason = (typeof FastFallbackReason)[keyof typeof FastFallbackReason];
|
|
401
470
|
/** One applied snapshot as the session uses it: its header and the motion records that differ from the
|
|
@@ -420,6 +489,8 @@ interface FastClientOptions {
|
|
|
420
489
|
open: FastSocketOpener;
|
|
421
490
|
/** The clock the deadlines run on (ms). */
|
|
422
491
|
now: () => number;
|
|
492
|
+
/** The seat's round trip as measured so far (ms; 0 or absent: not yet known). */
|
|
493
|
+
roundTripMs?: () => number;
|
|
423
494
|
/** Each applied snapshot, after the receive state took it. */
|
|
424
495
|
onSnapshot?: (s: FastSnapshot) => void;
|
|
425
496
|
}
|
|
@@ -432,11 +503,16 @@ interface FastClient {
|
|
|
432
503
|
readonly lastQueued: number;
|
|
433
504
|
/** True once closed: nothing is sent or received again. */
|
|
434
505
|
readonly closed: boolean;
|
|
506
|
+
/** True while the socket is still opening. */
|
|
507
|
+
opening(): boolean;
|
|
435
508
|
/** An input sent this frame: seqs ascend by one from the first queued. */
|
|
436
509
|
queue(input: FastInput): void;
|
|
437
510
|
/** One datagram carrying the inputs queued since the last flush and the redundant window (one more for
|
|
438
511
|
* each further window's worth of new inputs); nothing when none was queued. */
|
|
439
512
|
flush(): void;
|
|
513
|
+
/** An empty uplink when one is due (§17.12 *Keepalive*), from a host that takes them: the seat binds with
|
|
514
|
+
* no input, its snapshots stay acknowledged and its NAT mapping stays open. */
|
|
515
|
+
keepalive(): void;
|
|
440
516
|
/** The input of seq `seq` while it is still held. */
|
|
441
517
|
inputAt(seq: number): FastInput | undefined;
|
|
442
518
|
/** When the input of seq `seq` was first sent (by `now`), for each of the newest {@link seqAheadMax}
|
|
@@ -445,6 +521,11 @@ interface FastClient {
|
|
|
445
521
|
sentAt(seq: number): number | undefined;
|
|
446
522
|
/** The latest decoded record of `id` of `kind` (from the newest applied snapshot), else undefined. */
|
|
447
523
|
motionOf(kind: MotionRecordKind, id: string): MotionRecord | undefined;
|
|
524
|
+
/** Every record of the newest applied snapshot. */
|
|
525
|
+
records(): MotionRecord[];
|
|
526
|
+
/** A frame begins: time since the last frame past {@link FAST_FRAME_GAP_MS} is taken off both deadlines'
|
|
527
|
+
* clocks (§17.12 *Keepalive*), so a seat whose frames stopped does not read its own stop as a dead path. */
|
|
528
|
+
framed(): void;
|
|
448
529
|
/** Whether a deadline has passed (§17.12 *Fallback*), by the injected clock. */
|
|
449
530
|
due(): FastFallbackReason | null;
|
|
450
531
|
/** One datagram from the socket. A fault, a stale or duplicate fragment or a datagram for another
|
|
@@ -462,4 +543,4 @@ declare function heldInputs(sent: readonly FastInput[], inputAck: number, tickHz
|
|
|
462
543
|
/** Opens a seat's fast channel on `o.bind`; null when the opener cannot reach its url. */
|
|
463
544
|
declare function openFastClient(o: FastClientOptions): FastClient | null;
|
|
464
545
|
|
|
465
|
-
export {
|
|
546
|
+
export { MotionRecordKind as $, AcceptKind as A, FAST_SNAPSHOT_HISTORY as B, type ClientRx as C, FAST_STALL_FALLBACK_MS as D, type EntityMotion as E, type FastSocketOpener as F, FAST_VERSION as G, type FastBinding as H, type FastClient as I, type FastClientOptions as J, type FastDatagramSocket as K, FastFault as L, type FastInput as M, FastKind as N, FastMessage as O, type FastOffMessage as P, type FastOnMessage as Q, type FastReceipt as R, type FastResult as S, type FastSnapshot as T, type FastWebTransport as U, type Fragment as V, type HeldSnapshot as W, INPUT_REDUNDANCY_MAX as X, INPUT_REDUNDANCY_MIN as Y, INPUT_REDUNDANCY_MS as Z, type MotionRecord as _, FastFallbackReason as a, type PartialSnapshot as a0, RECORD_ID_MAX_CHARS as a1, type ReceiveResult as a2, type Records as a3, type SeatEcho as a4, type SeatRx as a5, type SnapshotBody as a6, type SnapshotHead as a7, type Uplink as a8, type UplinkOptions as a9, seqAheadMax as aA, toHex as aB, uplinkFor as aC, uplinkMaxBytes as aD, acceptUplink as aa, applyBody as ab, blankEntityMotion as ac, blankSeatEcho as ad, decodeFragment as ae, decodeRecord as af, decodeUplink as ag, deltaBase as ah, dequantizeCode as ai, diffSnapshot as aj, emptyClientRx as ak, encodeRecord as al, encodeSnapshot as am, encodeUplink as an, fastRetryDelayMs as ao, fromHex as ap, gapFillMax as aq, heldInputs as ar, inputRedundancyTicks as as, isFastBind as at, isPress as au, openFastClient as av, parseBody as aw, quantizeCode as ax, receiveFragment as ay, renderStamp as az, type FastBindMessage as b, type AcceptOptions as c, type AcceptResult as d, type AcceptedInput as e, FAST_ACK_EAGER_GAP_MS as f, FAST_ACK_EAGER_RECORDS as g, FAST_BIND_REV as h, FAST_CHALLENGE_JUMP as i, FAST_FRAME_GAP_MS as j, FAST_GAP_FILL_MS as k, FAST_HANDSHAKE_MS as l, FAST_KEEPALIVE_BIND_MS as m, FAST_KEEPALIVE_MS as n, FAST_KEY_BYTES as o, FAST_MAX_ACTIONS as p, FAST_MAX_DATAGRAM_BYTES as q, FAST_MAX_FRAGMENTS as r, FAST_MAX_RECORD_BYTES as s, FAST_OPEN_ROUND_TRIPS as t, FAST_REASSEMBLY_SLOTS as u, FAST_RETRY_BASE_MS as v, FAST_RETRY_MAX_MS as w, FAST_RETURN_PROOF as x, FAST_RETURN_STABLE_MS as y, FAST_SEQ_AHEAD_MS as z };
|
package/dist/index.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
createPrivate,
|
|
4
4
|
joinPrivate,
|
|
5
5
|
matchmake
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-QY3QQNDJ.js";
|
|
7
|
+
import "./chunk-FPWP22Y7.js";
|
|
8
|
+
import "./chunk-CSQEEANB.js";
|
|
9
9
|
export {
|
|
10
10
|
connect,
|
|
11
11
|
createPrivate,
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/** Why a level was refused. A refusal says this code and names what to change. */
|
|
2
|
+
declare const LevelRefusalCode: {
|
|
3
|
+
/** Not a glb or a glTF, or not a file that can be read. */
|
|
4
|
+
readonly NotGltf: "not_gltf";
|
|
5
|
+
/** A glb's chunks do not add up. */
|
|
6
|
+
readonly BadContainer: "bad_container";
|
|
7
|
+
/** glTF 1 or a version this reader does not take. */
|
|
8
|
+
readonly BadVersion: "bad_version";
|
|
9
|
+
/** An index that names nothing: a scene, a node, a mesh, an accessor, a buffer. */
|
|
10
|
+
readonly BadReference: "bad_reference";
|
|
11
|
+
/** An accessor or a view that does not fit what it reads or is not what it must be. */
|
|
12
|
+
readonly BadAccessor: "bad_accessor";
|
|
13
|
+
/** A triangle's index past its vertices. */
|
|
14
|
+
readonly BadIndex: "bad_index";
|
|
15
|
+
/** A transform or a field of the wrong shape. */
|
|
16
|
+
readonly BadValue: "bad_value";
|
|
17
|
+
/** NaN, an infinity, or a coordinate past the format's range. */
|
|
18
|
+
readonly NotFinite: "not_finite";
|
|
19
|
+
/** A cycle, or a node with two parents. */
|
|
20
|
+
readonly NodeCycle: "node_cycle";
|
|
21
|
+
/** Past a stated limit (LEVEL_LIMITS). */
|
|
22
|
+
readonly TooLarge: "too_large";
|
|
23
|
+
/** Draco or meshopt on something that collides. */
|
|
24
|
+
readonly Compressed: "compressed";
|
|
25
|
+
/** A required extension this reader cannot honour. */
|
|
26
|
+
readonly UnknownExtension: "unknown_extension";
|
|
27
|
+
/** A buffer URI that leaves the input's folder, names the network, or is not a plain file. */
|
|
28
|
+
readonly UriRefused: "uri_refused";
|
|
29
|
+
/** A skinned mesh that collides. */
|
|
30
|
+
readonly Skinned: "skinned";
|
|
31
|
+
/** A mesh posed by morph targets that collides. */
|
|
32
|
+
readonly Morphed: "morphed";
|
|
33
|
+
/** A kind of three.js object the helper cannot read. */
|
|
34
|
+
readonly Unsupported: "unsupported";
|
|
35
|
+
/** A mesh with no triangle left of any area. */
|
|
36
|
+
readonly Degenerate: "degenerate";
|
|
37
|
+
/** More triangles on one edge than any surface has. */
|
|
38
|
+
readonly NonManifold: "non_manifold";
|
|
39
|
+
/** No spawn: a fit starts from where a hull can stand. */
|
|
40
|
+
readonly NoSpawn: "no_spawn";
|
|
41
|
+
/** Nothing collides. */
|
|
42
|
+
readonly NoSurface: "no_surface";
|
|
43
|
+
/** A mark that is not one. */
|
|
44
|
+
readonly BadMark: "bad_mark";
|
|
45
|
+
};
|
|
46
|
+
type LevelRefusalCode = (typeof LevelRefusalCode)[keyof typeof LevelRefusalCode];
|
|
47
|
+
/** A level refused by name: `code` says why, the message names the node or field and what to do. */
|
|
48
|
+
declare class LevelRefusal extends Error {
|
|
49
|
+
readonly code: LevelRefusalCode;
|
|
50
|
+
constructor(code: LevelRefusalCode, message: string);
|
|
51
|
+
}
|
|
52
|
+
/** What this path takes. Each is refused before anything is decoded or allocated for it. */
|
|
53
|
+
declare const LEVEL_LIMITS: {
|
|
54
|
+
/** One glb, or one external buffer, and all buffers together. */
|
|
55
|
+
readonly maxFileBytes: number;
|
|
56
|
+
/** Triangles in the level, instances counted. A fit reads a scene of this size; art past it needs a proxy. */
|
|
57
|
+
readonly maxTriangles: 8000000;
|
|
58
|
+
/** Vertices one accessor may read. */
|
|
59
|
+
readonly maxVertices: 24000000;
|
|
60
|
+
readonly maxNodes: 1000000;
|
|
61
|
+
readonly maxDepth: 1024;
|
|
62
|
+
/** Points a `hull` holds, as the scene format says. */
|
|
63
|
+
readonly maxHullPoints: 4096;
|
|
64
|
+
/** No surface puts more triangles on one edge: past this it is junk, or an attack on what reads it. */
|
|
65
|
+
readonly maxTrianglesOnEdge: 64;
|
|
66
|
+
};
|
|
67
|
+
/** What a surface answers, as a creator marks it. `both` is the default; `none` is only drawn. */
|
|
68
|
+
declare const LevelCollide: {
|
|
69
|
+
readonly Both: "both";
|
|
70
|
+
readonly Move: "move";
|
|
71
|
+
readonly Shot: "shot";
|
|
72
|
+
readonly None: "none";
|
|
73
|
+
};
|
|
74
|
+
type LevelCollide = (typeof LevelCollide)[keyof typeof LevelCollide];
|
|
75
|
+
/** How a mesh becomes surfaces. Unmarked, a closed axis-aligned cube becomes an exact box and the rest stays triangles. */
|
|
76
|
+
declare const LevelShape: {
|
|
77
|
+
readonly Mesh: "mesh";
|
|
78
|
+
readonly Box: "box";
|
|
79
|
+
readonly Hull: "hull";
|
|
80
|
+
};
|
|
81
|
+
type LevelShape = (typeof LevelShape)[keyof typeof LevelShape];
|
|
82
|
+
/** A node's own mark, from its name and its `extras.genex` (`userData.genex` in three.js). */
|
|
83
|
+
interface LevelMark {
|
|
84
|
+
readonly collide?: LevelCollide;
|
|
85
|
+
readonly shape?: LevelShape;
|
|
86
|
+
readonly movable?: boolean;
|
|
87
|
+
readonly prop?: boolean;
|
|
88
|
+
readonly spawn?: boolean;
|
|
89
|
+
readonly tags?: readonly string[];
|
|
90
|
+
}
|
|
91
|
+
/** A node's own mark: what its name spells, then its `extras.genex`, which wins. Refuses a mark that is not one. */
|
|
92
|
+
declare function levelMarkOf(name: string, genex: unknown, where?: string, allowSettings?: boolean): LevelMark;
|
|
93
|
+
interface LevelHull {
|
|
94
|
+
readonly radius: number;
|
|
95
|
+
readonly height: number;
|
|
96
|
+
readonly crouchHeight: number;
|
|
97
|
+
readonly step: number;
|
|
98
|
+
readonly jump: number;
|
|
99
|
+
readonly leap: number;
|
|
100
|
+
}
|
|
101
|
+
/** The scene as JSON holds it, base64 for the big arrays: `parseWorldScene` takes exactly this. */
|
|
102
|
+
interface RawWorldScene {
|
|
103
|
+
readonly format: 'genex-world-scene';
|
|
104
|
+
readonly version: 1;
|
|
105
|
+
readonly unitsPerMeter: number;
|
|
106
|
+
readonly source: Readonly<Record<string, string>>;
|
|
107
|
+
readonly hull?: LevelHull;
|
|
108
|
+
readonly actors: readonly RawActor[];
|
|
109
|
+
readonly spawns: readonly {
|
|
110
|
+
readonly at: readonly number[];
|
|
111
|
+
readonly name?: string;
|
|
112
|
+
readonly tags?: readonly string[];
|
|
113
|
+
}[];
|
|
114
|
+
}
|
|
115
|
+
interface RawActor {
|
|
116
|
+
readonly name: string;
|
|
117
|
+
readonly tags?: readonly string[];
|
|
118
|
+
readonly surfaces: readonly RawSurface[];
|
|
119
|
+
}
|
|
120
|
+
type RawSurface = {
|
|
121
|
+
readonly kind: string;
|
|
122
|
+
} & Readonly<Record<string, unknown>>;
|
|
123
|
+
interface LevelStats {
|
|
124
|
+
readonly actors: number;
|
|
125
|
+
readonly boxes: number;
|
|
126
|
+
readonly hulls: number;
|
|
127
|
+
readonly meshes: number;
|
|
128
|
+
readonly triangles: number;
|
|
129
|
+
readonly spawns: number;
|
|
130
|
+
}
|
|
131
|
+
interface LevelScene {
|
|
132
|
+
readonly scene: RawWorldScene;
|
|
133
|
+
/** What was kept or dropped that a creator may want to look at. Never in the scene's bytes. */
|
|
134
|
+
readonly notes: readonly string[];
|
|
135
|
+
readonly stats: LevelStats;
|
|
136
|
+
/** The scene's bytes, as text: one file, the same on every OS. */
|
|
137
|
+
readonly json: string;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export { type LevelScene as L, type RawWorldScene as R, type LevelHull as a, LEVEL_LIMITS as b, LevelCollide as c, type LevelMark as d, LevelRefusal as e, LevelRefusalCode as f, LevelShape as g, levelMarkOf as l };
|