@genex-ai/multiplayer 0.17.8 → 0.17.10
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 +3 -2
- package/README.md +1 -1
- package/dist/authority/fast-webtransport.js +2 -2
- package/dist/authority/index.d.ts +57 -1
- package/dist/authority/index.js +16 -4
- package/dist/bridge-authority.global.js +9 -9
- package/dist/bridge-authority.js +4 -4
- package/dist/bridge.js +3 -3
- package/dist/{chunk-FPWP22Y7.js → chunk-42R7NREA.js} +1 -1
- package/dist/{chunk-CSQEEANB.js → chunk-7J3YWPCA.js} +3 -1
- package/dist/{chunk-NFMGIKFZ.js → chunk-HHETHPGL.js} +2 -2
- package/dist/{chunk-QY3QQNDJ.js → chunk-NSQQCLMA.js} +2 -2
- package/dist/{chunk-QZOCUQJT.js → chunk-SQC6WTMF.js} +119 -13
- package/dist/index.js +3 -3
- package/dist/protocol.d.ts +5 -2
- package/dist/protocol.js +3 -1
- package/package.json +1 -1
package/PROTOCOL.md
CHANGED
|
@@ -633,6 +633,7 @@ The TypeScript client session is `connectAuthority` (`src/authority/session.ts`)
|
|
|
633
633
|
- An authority client sends `protocol: 4` (`AUTHORITY_PROTOCOL_VERSION`). Authority rooms accept `AUTHORITY_WINDOW` = `[4, 4]`; anything else is refused with **426** before a room is found or created. A relay room refuses 4 the same way (§2), so neither kind of client can land in the other kind of room.
|
|
634
634
|
- Authority rooms are served by their own cluster, never by a relay. The embed layer supplies its URLs next to the relay URLs (`colyseus.authorityUrls`; in local test mode, a loopback `genex_authority_url` on a loopback page, with a fixed local credential in place of a token), and the same cluster by region (`colyseus.authorityRegions`, below).
|
|
635
635
|
- *Regions.* `colyseus.authorityRegions` is a list of `{ region, urls[] }`; each region is its own matchmaking pool, because the server filters rooms by the region a join entered through (the server-owned `region` option, §17.2). A client reads it leniently: an entry without a non-empty name or without a non-empty url string is dropped, and a repeated name keeps its first entry. When no entry is usable, every `authorityUrls` entry is a region of its own (an older stand, local test mode). With one region the client joins through its first url. With several, it MUST probe as §3.1 does, a preference and never a gate: 3 samples per region, sample n going to the region's url n modulo its url count; a sample is failed when it has not answered after 4000 ms, which is also the ceiling of the whole probe, and once any sample has answered the others get 1000 ms more. A url's latency is its best answered sample (a finite number of ms, at least 0). Regions are tried in this order: those with an answer, nearest first (a tie keeps list order), each entered through its best url (the first on a tie); then the silent ones in list order, through their first url. A dead path moves to the next region at once: no answer at all, a gateway's 502 or 504, or Cloudflare's 520 to 526 and 530. Anything but those and 503 (with 4213, a process shutting down, which a sibling answers on the next try) is the server's answer and final. A join refused 503 twice in a row on one region moves to the next region at once; any other 503 waits the capacity schedule's next step on the same region, and the join gives up with the last refusal when the schedule is spent. An explicit `url` (an invite: the inviter's session url) skips the probe, so a party plays in its leader's region. `netcode-bench/conformance/authority/authority-regions.json` pins every rule; the TypeScript, Unreal, Unity and Rust clients pass it exactly. The Godot client joins its first url (labelled in CI).
|
|
636
|
+
- *Finding players.* Because each region is its own pool, a client that only ever entered its nearest region would leave two players in different regions alone, each in an empty room. So a pool join (the `joinOrCreate` of the next bullet: not a join by code, a private create, a rejoin or an explicit `url`) first asks for a room that already exists, and opens one only when no region in reach has one. A room a join can still enter has a player in it, so this is where the players are. The regions asked are the answered ones whose latency (the probe's best sample) is at most `maxPingMs`, nearest first; the default is 350 ms, and anything but a finite number of ms, at least 0, is the default. Fewer than two such regions is no search, since the join-or-create finds its own region's room itself. Each is asked with Colyseus's `join`, carrying the options the `joinOrCreate` would: seated, the player is in that region; refused, the next region is asked when the refusal is one of *Regions*' dead paths or capacity answers (Colyseus's own answer for no matching room is 521, one of them), and any other refusal is the server's answer about the player or the game, which ends the join with no room opened. When no region asked has a room, the join goes on as *Regions* says: `joinOrCreate`, nearest region first. A game may turn the search off (the rule `nearest`; any other value is `players`, the default), and a player may pin one region by its name: then that region alone is joined and created in, through its best url, and a name the list does not have is no pin. A join by code ignores a pin: its room is where it is. Nothing here is sent to a server or changes one: a client that searches plays with clients that do not. `authority-regions.json` pins the regions asked, where the room is then opened and what each refused ask does (`searches`, `searchRefusals`); the TypeScript, Unreal, Unity and Rust clients pass it exactly. The Godot client joins its first url and looks for no one.
|
|
636
637
|
- A client joins with `joinOrCreate('authority', { gameId, embedToken, name, protocol: 4 })`, or names a pool or a private room (§17.2 *Choosing a room*). The token rules of §3.2 apply. A preview (staging) token joins the game's staging module, in rooms apart from the live game's (§3.4). A join lands in a room running the module the game currently ships: once a newer module has a room, new joins go there, while rooms on the older one keep their players until they empty.
|
|
637
638
|
- Appending a schema field, adding a message or adding an optional key is not a bump; the §2 bump rules apply to this section with `AUTHORITY_PROTOCOL_VERSION` in place of `PROTOCOL_VERSION`.
|
|
638
639
|
|
|
@@ -1318,7 +1319,7 @@ Then `actions` held inputs, ascending seq, each `seq` uint32, `tick` uint16, `re
|
|
|
1318
1319
|
**Downlink snapshot** (`kind` 2). Each seat gets its own snapshots: the room's *motion records* (below), delta-coded against the newest snapshot that seat acknowledged. A fragment has 29 bytes of header: `sid` uint32 · `snap` uint32 (the snapshot id, from 1, rising by at least one per snapshot) · `base` uint32 (the snapshot the delta is against; 0 for a keyframe) · `tick` uint32 (the server tick it was written at) · `inputAck` uint32 (the seat's last accepted input seq) · `flags` uint8 (bit 0 *keyframe*, set exactly when `base` is 0) · `frag` uint8 · `frags` uint8 (`frag < frags ≤ FAST_MAX_FRAGMENTS`, 32) · `sent` uint32 (the room clock, ms since room start, when the server wrote the snapshot: the `sNow` a WebSocket patch's TIMED prefix carries, §16.1), then that fragment's slice of the body. The body, split into slices of at most `FAST_MAX_DATAGRAM_BYTES − 29` bytes, is `removed` uint16 and that many record keys uint16, ascending, then `records` uint16 and that many records, ascending by key, each `key` uint16, `len` uint16 (at most `FAST_MAX_RECORD_BYTES`, 1024) and `len` bytes. A delta lists the records new or changed since `base` and the keys gone since it; a keyframe lists every record and removes nothing. The server keeps its last `FAST_SNAPSHOT_HISTORY` (32) snapshots per seat and writes a delta against the seat's `ackSnap` while it holds that snapshot, else a keyframe.
|
|
1319
1320
|
**Partial snapshots** (authority-host `fast-snapshots.ts`). Each seat has a byte budget, a token bucket at its share of the room's rate (§17.6 *The downlink budget*) with a burst of one snapshot of `FAST_MAX_FRAGMENTS` full fragments. A snapshot that fits both is written as above, byte for byte. One past either is never dropped: it goes as a partial snapshot, its body cut to what the budget holds (at least one fragment, so the seat's ack always flows and the channel never looks dead). It needs no wire change and every client applies it. The records it carries are chosen by weight times age: the seat's own record and its own entity always go, then the other seats, then each entity by how near yours it is (`motionWeights`), and each record passed over ages, so none starves. A record that changed and is passed over is listed under `removed` when the base holds it: the client's newest snapshot no longer carries it, its state keeps the last value and a predicting client keeps the body out of the keyframe it adopts (`motionKnown`, *Client* below), never adopting old motion as current. The next delta is written against what the client will hold, so what was passed over goes out later. The endpoint counts each snapshot by shape (`stats().snapshots`: `whole`, `partial`, `refused`, and `deferred` records), under `fast` on the metrics endpoint. Measured on loopback UDP (`test/fast-many.test.ts`): a seat joining a physics room of 1,016 moving entities, whose keyframe is about 90 KB, used to get nothing and fell back at the handshake deadline; it now stays on the channel with every entity moving and no gap between snapshots over 500 ms, and so does one held to 48 KiB/s for a whole take.
|
|
1320
1321
|
|
|
1321
|
-
**Motion records** (`src/authority/fast-records.ts`, `encodeRecord`/`decodeRecord`). Each record is one entity's or one seat's: `kind` uint8 (`MotionRecordKind`: 1 entity, 2 seat), `idLen` uint8 and that many UTF-8 bytes of the entity id or seat session id (1 to 64 characters), a `mask` uint32 whose bit *i* says field *i* is present, then each present field in table order. An entity's fields, bit 0 up: `x` `y` `z` `vx` `vy` `vz` `yaw` `pitch` `duck` `spin` `fuel` float32; `epoch` `mstate` uint32; `qx` `qy` `qz` `qw` (orientation, [−1, 1]) and `lvx` `lvy` `lvz` `avx` `avy` `avz` (velocity, [−64, 64]) as uint16 codes of the schema's own quantize (clamp, span 2^16 − 2, half-up, so 0 is code 32767 and a fast seat decodes bit for bit what a WebSocket seat decodes); `sleep` (the bit alone); `rest` uint16. A seat's: `inMoveX` `inMoveY` `inMoveZ` `inYaw` `inPitch` float32; `inButtons` `inRep` `inStarve` `inBuf` `inAck` uint32, `inAck` being, in the receiving seat's own record only, its last input the server consumed (stepped or skipped) as of the snapshot, which its reconciler replays from (the header's `inputAck` is the last *accepted*, which trims actions). A field at its default (0, −0, false, never written) is left out and a field present at its default is a fault, so the bytes are canonical and a delta carries a record only when one of its fields changed; a field left out reads as its default. The entity's other fields (`kind`, `owner`, `hp`, `flags`, `data`, projectile and `cap` fields) and everything else in the state stay on the reliable channel. The server gives each live id a record key (uint16) of its own for the room; a client reads the id from the record, never from the key. A decoder drops, with its `FastFault`, a record of another kind, one that is truncated or has bytes left over, an empty, over-long or malformed id, an unknown mask bit, or a present field at its default, non-finite or past its span; a client leaves that id as it was.
|
|
1322
|
+
**Motion records** (`src/authority/fast-records.ts`, `encodeRecord`/`decodeRecord`). Each record is one entity's or one seat's: `kind` uint8 (`MotionRecordKind`: 1 entity, 2 seat), `idLen` uint8 and that many UTF-8 bytes of the entity id or seat session id (1 to 64 characters), a `mask` uint32 whose bit *i* says field *i* is present, then each present field in table order. An entity's fields, bit 0 up: `x` `y` `z` `vx` `vy` `vz` `yaw` `pitch` `duck` `spin` `fuel` float32; `epoch` `mstate` uint32; `qx` `qy` `qz` `qw` (orientation, [−1, 1]) and `lvx` `lvy` `lvz` `avx` `avy` `avz` (velocity, [−64, 64]) as uint16 codes of the schema's own quantize (clamp, span 2^16 − 2, half-up, so 0 is code 32767 and a fast seat decodes bit for bit what a WebSocket seat decodes); `sleep` (the bit alone); `rest` uint16. A seat's: `inMoveX` `inMoveY` `inMoveZ` `inYaw` `inPitch` float32; `inButtons` `inRep` `inStarve` `inBuf` `inAck` uint32, `inAck` being, in the receiving seat's own record only, its last input the server consumed (stepped or skipped) as of the snapshot, which its reconciler replays from (the header's `inputAck` is the last *accepted*, which trims actions). A field at its default (0, −0, false, never written) is left out and a field present at its default is a fault, so the bytes are canonical and a delta carries a record only when one of its fields changed; a field left out reads as its default. The entity's other fields (`kind`, `owner`, `hp`, `flags`, `data`, projectile and `cap` fields) and everything else in the state stay on the reliable channel. A projectile (an entity with `t0` > 0) has no record at all: its `x`..`vz` are its launch, read with `t0`, `g` and `src`, so the whole launch stays on the reliable channel and is never muted there (*Downlink*). Split across the two channels, a bounce's re-launch was drawn as the new flight from the old `t0` (or the old flight from the new one) until the other half landed: 7 to 38 units off on Unreal's projectile smoke over UDP. The server gives each live id a record key (uint16) of its own for the room; a client reads the id from the record, never from the key. A decoder drops, with its `FastFault`, a record of another kind, one that is truncated or has bytes left over, an empty, over-long or malformed id, an unknown mask bit, or a present field at its default, non-finite or past its span; a client leaves that id as it was.
|
|
1322
1323
|
|
|
1323
1324
|
**Applying a snapshot** (`receiveFragment`, the client). A fragment whose `sid` is not the seat's is dropped. Fragments are gathered by `snap`; one whose header differs from its set's first (`base`, `tick`, `inputAck`, `flags`, `frags`, `sent`), or a duplicate, is dropped; at most `FAST_REASSEMBLY_SLOTS` (4) sets are gathered at once, the oldest giving way. A complete set is applied when its `snap` is newer than the newest applied and its base is a snapshot the client still holds (its last `FAST_SNAPSHOT_HISTORY` applied) or it is a keyframe; otherwise it is dropped, and so is a body that does not parse to the byte. Applying it copies the base's records, removes the removed keys, writes the records, and makes it the newest: its id is the next `ackSnap`, and its `inputAck` the ack `uplinkFor` trims actions by. Applying drops every incomplete set at or below the new snapshot: a lost fragment loses its snapshot, never the next one.
|
|
1324
1325
|
|
|
@@ -1329,7 +1330,7 @@ Then `actions` held inputs, ascending seq, each `seq` uint32, `tick` uint16, `re
|
|
|
1329
1330
|
**Return to the channel** (`fast-session.ts`, authority-host `room.ts`). A fallback is not for the rest of the socket: a client that sends `fast-return@1` beside `fast@1`, in a room whose binding says `rev` ≥ 2, comes back by itself. It never moves until the path is proven, at the start too, so a network that blocks UDP costs it nothing. (1) The server offers such a seat `__fast` with `probe: true`, at join and at each reconnect, and answers its `__fast:again` `{}` the same way: a fresh `sid` and `key`, at most one per `FAST_AGAIN_MIN_MS` (500 ms) a seat, none while the seat is live or the switch is off; once the switch is on it offers one, unasked, to a seat that has none. An older client's first binding is as it always was. (2) The WebSocket carries everything while the seat proves the path: it sends empty uplinks, its address is challenged (*Return path*) and it applies snapshots, writing none of them anywhere, until it holds `FAST_RETURN_PROOF` (3). Anything it sends by datagram before step 3 waits on the server. (3) At the third it swaps, at one instant: `__fast:on` `{ last }` (the last seq it sent by WebSocket) goes on the socket after its last input frame, its next input goes by datagram as `last + 1`, and that snapshot is written whole, as a first one is. (4) The server reads `__fast:on` after every WebSocket input it follows, because TCP keeps their order: what its buffer still holds goes first, numbered as it would have been, then the parked datagrams from `max(last, received)` (at most 512 parked, a longer stall being the gap rule's), so no input is lost or stepped twice, however long TCP held the message. A WebSocket input stepped this way is judged at the seat's newest stamp. A physics room sends motion by WebSocket until the seat is live on the new binding and has acknowledged a snapshot that carries motion. A `__fast:on` that is malformed, past `FAST_SEQ_AHEAD_MS` of what was received or with no binding being proven changes nothing. (5) A socket that has to open (WebTransport) gets the same deadline on a fresh binding as on any: `FAST_HANDSHAKE_MS` or `FAST_OPEN_ROUND_TRIPS` of the seat's round trips, whichever is longer, to open, then `FAST_HANDSHAKE_MS` to the first snapshot; none of it costs an input, as the WebSocket has them. Measured on a session taking 1.5 s to open at 500 ms round trip: 86 and 87 of about 508 inputs lost with the seat's inputs on the datagrams while it opened (only the newest 32 survive the wait), 0 of 511 and 512 here. A proof that misses a deadline (*Fallback*) goes back: `__fast:off` `{ last }` closes that binding, the numbering unchanged, and `fallback` says why the seat is on the WebSocket. The retries: `fastRetryDelayMs(attempt, unit)`, doubling from `FAST_RETRY_BASE_MS` (1 s) to `FAST_RETRY_MAX_MS` (60 s), half of it random, forever; an ask that is answered does not climb, and a channel held for `FAST_RETURN_STABLE_MS` (30 s) starts over from 0, so a flapping path backs off; after the server's own `__fast:off` the first is 4 to 8 s on. An older host's binding has no `rev`: its seat sends no empty uplink and no `__fast:again` (Colyseus closes a socket over a message with no handler), and falls back as it always did. `status.fallbacks` and `status.returns` count. Measured on loopback UDP through the NAT rig (`test/fast-return.test.ts`): snapshots cut for 3 s, back on UDP within 3 s, every input once in order, 3 cuts in a row the same; on UDP 135 to 151 ms after the seat appears at 80 ms round trip; UDP blocked from the first datagram, 0 of 139 inputs lost, against 56 of 138 before (the first ones to reach the module were seq 57). Both ways cut, the inputs sent by datagram into the cut are still skipped by the gap rule (119 of 391): they are 2 s old, and a late step is worse.
|
|
1330
1331
|
**Downlink.** In a physics room (§17.10: every client has a view) the state's entities are `AuthorityPhysicsEntity`, `AuthorityEntity`'s fields in its order under its name (decoders and the handshake's type description are unchanged), whose motion fields (an entity record's, *Motion records* below) carry the Colyseus view tag `MOTION_VIEW_TAG`; every entity enters a view with it. Once a fast seat has acknowledged a snapshot (`ackSnap` past 0) while its binding is open, each entity its view introduced in an earlier patch leaves its view's tag, silently (no delete reaches the client): its WebSocket carries that entity's other fields, the seat echoes and the root's `tick` and `keyframe`, never its motion. When the channel closes (the seat's `__fast:off` or the server's), or the socket reconnects, every such entity's motion is sent again in full in the next patch (or the reconnect's first state). A room without physics writes motion to every seat's WebSocket as before. **Liveness.** A datagram that passed a seat's binding proves its client alive: it answers the WebSocket transport's heartbeat for the seat's socket (uWebSockets.js, the production transport, closes a socket only after `idleTimeout`, 60 s, without a frame; every server on Colyseus's ws transport, the dev server, the bench's rooms and parks, runs its heartbeat at that deadline, 4 unanswered 15 s pings, `WS_TRANSPORT_HEARTBEAT`, where its default of 2 unanswered 3 s pings closed seats production keeps through a long route's TCP stall), so a TCP stall the datagrams outlive never closes a playing seat. A fast seat whose socket did drop keeps its snapshots, of the view it dropped with, through the reconnection grace, while its datagrams still feed the module.
|
|
1331
1332
|
|
|
1332
|
-
**Client** (`src/authority/fast-client.ts`, `fast-session.ts`; `fast-udp.ts` the Node socket, `fast-default.ts` finding it at run time so no browser bundle links it, `fast-webtransport.ts` the browser's). A client with a socket opener sends `fast@1`: in Node that is every client that did not say `fast: false`; in a browser, WebTransport unless the game said `fast: false`. `session.fast` says `live`, `snapshots` and `fallback`, why the seat is on the WebSocket: `no-webtransport` (the page has none: it never asked), `no-relay` (a binding with no usable `wt`), `not-offered` (no binding within `FAST_HANDSHAKE_MS` of its first frame: a room or server with no channel; a binding that comes later still opens), `unreachable` (a malformed binding, a socket that cannot open, or a session that failed or cannot carry the seat), `handshake` (no snapshot in time: a blocked network), `stall` (snapshots stopped) or `server` (the server's `__fast:off`); null on the channel, while connecting, or when the game turned it off. It takes `__fast` once the socket's state is decoded (its `seqBase` is the player's), and an unusable one (not a uint32 sid, a 16-hex-digit key and a url, or a url it cannot open: in Node anything but `udp://host:port` with a port in 1..65535, an IPv6 host in brackets, and no credentials, path, query or fragment) is a fallback at once. Its wire seq is `seqBase` plus the input's count on the socket. The inputs it sent by WebSocket before the binding arrived were dropped by the server unstepped: its first datagrams carry them again, and whenever more new inputs are due than one window holds it sends one datagram per window's worth, oldest first, so none is stepped as a fill. While the channel is live the server drops its WebSocket inputs unstepped; its ack is its own seat record's `inAck
|
|
1333
|
+
**Client** (`src/authority/fast-client.ts`, `fast-session.ts`; `fast-udp.ts` the Node socket, `fast-default.ts` finding it at run time so no browser bundle links it, `fast-webtransport.ts` the browser's). A client with a socket opener sends `fast@1`: in Node that is every client that did not say `fast: false`; in a browser, WebTransport unless the game said `fast: false`. `session.fast` says `live`, `snapshots` and `fallback`, why the seat is on the WebSocket: `no-webtransport` (the page has none: it never asked), `no-relay` (a binding with no usable `wt`), `not-offered` (no binding within `FAST_HANDSHAKE_MS` of its first frame: a room or server with no channel; a binding that comes later still opens), `unreachable` (a malformed binding, a socket that cannot open, or a session that failed or cannot carry the seat), `handshake` (no snapshot in time: a blocked network), `stall` (snapshots stopped) or `server` (the server's `__fast:off`); null on the channel, while connecting, or when the game turned it off. It takes `__fast` once the socket's state is decoded (its `seqBase` is the player's), and an unusable one (not a uint32 sid, a 16-hex-digit key and a url, or a url it cannot open: in Node anything but `udp://host:port` with a port in 1..65535, an IPv6 host in brackets, and no credentials, path, query or fragment) is a fallback at once. Its wire seq is `seqBase` plus the input's count on the socket. The inputs it sent by WebSocket before the binding arrived were dropped by the server unstepped: its first datagrams carry them again, and whenever more new inputs are due than one window holds it sends one datagram per window's worth, oldest first, so none is stepped as a fill. While the channel is live the server drops its WebSocket inputs unstepped; its ack is its own seat record's `inAck`, except where a projectile prediction is settled (§17.9: its input acknowledged and its id not in the state), which reads the ack of the newest applied snapshot at or before the WebSocket's latest `tick` (the launch comes by WebSocket and can land after the snapshot of its tick; settled against the newest snapshot, a shot was dropped as refused before its launch arrived; held to the `settleAck` vectors); and it writes each applied snapshot into its state as a patch would (each record's fields through the schema, a field the record leaves at its default unwritten where the state never wrote one, with the state's `tick` and, in a predicting room, which patches only on keyframes, `keyframe` set to the snapshot's `tick`), then runs the state's change listeners, so prediction, reconcile and interpolation never see which channel delivered a state. A WebSocket patch that would write one of those fields for an id the channel holds is put back before any listener runs: a patch held behind a lost TCP segment never moves an entity or the tick back. The client keeps the WebSocket's latest value of each field it holds, and decodes each patch against those values (Colyseus sends a field only when it changes and reports a decoded field only when it differs from the one held). Motion comes by snapshot and the entity list by WebSocket, so an entity can be listed while the newest snapshot does not carry its record: it left your view and the WebSocket's removal is still on its way, or it came back into view by WebSocket first. Its fields then hold another keyframe's motion, so a predicting client keeps its body out of the keyframe it adopts, as one out of view (§17.11 *The client world*), until a snapshot carries it again. Every fast client with body prediction does (TypeScript, Unreal, Rust, Unity), held to the `motionKnown` vectors. Adopted as part of the keyframe, a body a keyframe behind failed the canary's exact comparison: on Mechanism Hall at `near-wifi`, where every radio episode releases snapshots and patches in separate bursts, prediction turned `diverged` for 7 of 28 fast seats a batch and for none on WebSocket. At a fallback, every held field takes the WebSocket's latest value in one patch, so no field keeps a snapshot's value the server has since moved on from (a seat that stopped while the snapshots were cut is drawn where it stopped); in a physics room an entity's motion instead keeps the channel's values until the server's full resend lands (*Downlink*: its WebSocket values date from when the channel went live), so nothing is drawn back where it was then. Each snapshot is a clock sample at its `sent`, with the round trip of the input its `inAck` newly acknowledges (timed from that input's send, which the client keeps for its newest `FAST_SEQ_AHEAD_MS` of inputs: the server consumes an input well after it received it), so a fast seat's clock reads what a WebSocket seat's does; the tick's nominal instant would read up to a step early, by however late the server ran that step. After a fallback the WebSocket ack counts that socket's WebSocket inputs (the dropped ones too), so the client's ack is its count at the fallback plus the WebSocket ack past the inputs it sent by WebSocket before it. A client may keep sending its input frames while live (Unreal and Rust, whose core's input handle exposes neither baseline below) or keep them off the socket (TypeScript and Unity, so a TCP stall holds no inputs); either way an input frame is a delta against the last frame the client *encoded* (the fields that changed since) and its lag-compensation stamp a delta against the last stamp it wrote, so a frame kept off the socket moves both baselines past what the server decoded. A client that keeps frames off must therefore, at the fallback, send its next frame whole (every field) and its next stamp as a delta against the last stamp the server got (the one before the binding); otherwise each input it sends after the fallback is stepped with the fields the server last decoded (a held axis read as 0) and judged seconds in the past. The server may end the channel with `__fast:off` `{}`; the client answers with its own `__fast:off { last }` and falls back as above (no server sends it yet). A client without `fast-return@1` that falls back before its first datagram loses the inputs it sent by WebSocket before the binding; one with it never does (*Return to the channel*).
|
|
1333
1334
|
|
|
1334
1335
|
**Pacing** (§17.11 *Input pacing*, unchanged in P1). A recovered input usually arrives one datagram late, so recovery after a short loss is no burst; what stays bursty is a radio queue's delay (70–170 ms episodes on `real-mac-wifi`), which is the same on either transport. Fills and recovered inputs enter the seat's buffer like any other and the pacer is the same: `MAX_INPUTS_PER_TICK_PER_PLAYER` (4, `limits.ts`) and `PACE_MAX_DEPTH_S` (50 ms, `input-pacing.ts`) apply. Proposal, to be measured in P4 before it is adopted: a starve that ends with recovered inputs was loss, not jitter, so it should not hold the cushion for `PACE_KEEP_S` (the excess drains at the next `PACE_WINDOW_S`), and with `repeat@1` recovered inputs and fills pay owed repeats back first (absorbed, `feedWithPayback`), so a recovered burst is not stepped twice. P4 measures starved ticks, own popP99 and remote lurches on fast seats with the cap at 4 and at 2 and `PACE_MAX_DEPTH_S` at 0.05 and 0.1, against the WebSocket base.
|
|
1335
1336
|
|
package/README.md
CHANGED
|
@@ -638,7 +638,7 @@ import * as genexEmbed from '@genex-ai/embed-sdk';
|
|
|
638
638
|
await genexEmbed.waitForPlayer();
|
|
639
639
|
const s = await connectAuthority({
|
|
640
640
|
urls: genexEmbed.getAuthorityUrls(), // the authority cluster, NOT getColyseusUrls()
|
|
641
|
-
regions: genexEmbed.getAuthorityRegions(), // its regions: the nearest
|
|
641
|
+
regions: genexEmbed.getAuthorityRegions(), // its regions: a room with players in reach, else the nearest (PROTOCOL.md §17.1)
|
|
642
642
|
room: 'my-game',
|
|
643
643
|
auth: () => genexEmbed.getColyseusAuth(),
|
|
644
644
|
});
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
openWebTransportFastSocket,
|
|
7
7
|
webTransportFastOpener,
|
|
8
8
|
webTransportOptions
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-HHETHPGL.js";
|
|
10
10
|
import "../chunk-OD6NRSX2.js";
|
|
11
11
|
import "../chunk-2NGXXQDS.js";
|
|
12
|
-
import "../chunk-
|
|
12
|
+
import "../chunk-7J3YWPCA.js";
|
|
13
13
|
export {
|
|
14
14
|
FAST_WT_HASHES_MAX,
|
|
15
15
|
FAST_WT_HASH_HEX_DIGITS,
|
|
@@ -1020,6 +1020,7 @@ declare const AuthorityJoinMethod: {
|
|
|
1020
1020
|
readonly JoinOrCreate: "joinOrCreate";
|
|
1021
1021
|
readonly Create: "create";
|
|
1022
1022
|
readonly JoinById: "joinById";
|
|
1023
|
+
readonly Join: "join";
|
|
1023
1024
|
};
|
|
1024
1025
|
type AuthorityJoinMethod = (typeof AuthorityJoinMethod)[keyof typeof AuthorityJoinMethod];
|
|
1025
1026
|
/** Why a client refuses a target before anything reaches the network. */
|
|
@@ -2143,6 +2144,33 @@ type CapacityAttempt = {
|
|
|
2143
2144
|
};
|
|
2144
2145
|
/** Refusals (503) in a row on one region before the next attempt goes to the next-best region. */
|
|
2145
2146
|
declare const AUTHORITY_REFUSALS_PER_REGION = 2;
|
|
2147
|
+
/** How far a join looks for players by default, as a probe's round trip in ms (§17.1 *Finding players*):
|
|
2148
|
+
* far enough that a player in one region reaches the others. */
|
|
2149
|
+
declare const AUTHORITY_MAX_PING_MS = 350;
|
|
2150
|
+
/** How a pool join picks its region (§17.1 *Finding players*). */
|
|
2151
|
+
declare const RegionRule: {
|
|
2152
|
+
/** A room with players in any region in reach, nearest first; else a new room in the nearest region. */
|
|
2153
|
+
readonly Players: "players";
|
|
2154
|
+
/** The nearest region alone, whoever is elsewhere. */
|
|
2155
|
+
readonly Nearest: "nearest";
|
|
2156
|
+
};
|
|
2157
|
+
type RegionRule = (typeof RegionRule)[keyof typeof RegionRule];
|
|
2158
|
+
/** What a game or its player chose about regions; every field is read leniently. */
|
|
2159
|
+
type AuthoritySearchOptions = {
|
|
2160
|
+
/** Default {@link RegionRule.Players}; anything but `nearest` is that. */
|
|
2161
|
+
rule?: unknown;
|
|
2162
|
+
/** The farthest region asked for players, as a probe's round trip in ms. Default
|
|
2163
|
+
* {@link AUTHORITY_MAX_PING_MS}; anything but a finite number ≥ 0 is the default. */
|
|
2164
|
+
maxPingMs?: unknown;
|
|
2165
|
+
/** The one region to play in, by name (a player's own choice): no other is joined or created in. A
|
|
2166
|
+
* name the cluster does not have is no choice. */
|
|
2167
|
+
region?: unknown;
|
|
2168
|
+
};
|
|
2169
|
+
/** Where a pool join goes: the regions asked for an existing room, then where one is opened. */
|
|
2170
|
+
type AuthoritySearchPlan = {
|
|
2171
|
+
search: AuthorityRegionPick[];
|
|
2172
|
+
create: AuthorityRegionPick[];
|
|
2173
|
+
};
|
|
2146
2174
|
/** The regions a client uses, read leniently from `colyseus.authorityRegions`, else one per flat url. */
|
|
2147
2175
|
declare function normalizeAuthorityRegions(regions: unknown, urls: unknown): AuthorityRegion[];
|
|
2148
2176
|
/** The url each probe sample of a region goes to: sample n to url n modulo the url count. */
|
|
@@ -2150,6 +2178,11 @@ declare function regionProbePlan(urls: readonly string[], samples: number): stri
|
|
|
2150
2178
|
/** The order regions are tried in, from each url's best sample (`best[region][url]`; anything but a
|
|
2151
2179
|
* finite number ≥ 0 is no answer). */
|
|
2152
2180
|
declare function rankAuthorityRegions(regions: readonly AuthorityRegion[], best: readonly (readonly unknown[] | undefined)[]): AuthorityRegionPick[];
|
|
2181
|
+
/** Where a pool join goes (§17.1 *Finding players*), from each url's best sample: the answered regions
|
|
2182
|
+
* no farther than the limit are asked for an existing room, nearest first, and the room is opened in
|
|
2183
|
+
* the ranked order. Fewer than two regions in reach is no search (the join-or-create finds its own
|
|
2184
|
+
* region's room), as is the rule `nearest`. A pinned region is the only one joined. */
|
|
2185
|
+
declare function planAuthoritySearch(regions: readonly AuthorityRegion[], best: readonly (readonly unknown[] | undefined)[], options?: AuthoritySearchOptions): AuthoritySearchPlan;
|
|
2153
2186
|
/** Every attempt of a join that every server refuses 503, over `regionCount` ranked regions and the
|
|
2154
2187
|
* capacity schedule `delays`: a prefix of it is the walk of a join that is seated. */
|
|
2155
2188
|
declare function capacityWalk(regionCount: number, delays: readonly number[]): CapacityAttempt[];
|
|
@@ -2165,9 +2198,23 @@ declare const RefusalKind: {
|
|
|
2165
2198
|
type RefusalKind = (typeof RefusalKind)[keyof typeof RefusalKind];
|
|
2166
2199
|
/** What a join refused with `code` does next; no number at all is a network that failed. */
|
|
2167
2200
|
declare function refusalKind(code: unknown): RefusalKind;
|
|
2201
|
+
/** What a refused ask for an existing room does next (§17.1 *Finding players*). */
|
|
2202
|
+
declare const SearchRefusal: {
|
|
2203
|
+
/** No room here (Colyseus's 521, no room matches the criteria), a dead path, a full or draining
|
|
2204
|
+
* region: the next region is asked. */
|
|
2205
|
+
readonly Next: "next";
|
|
2206
|
+
/** The server's answer about this player or game, which every region would repeat. */
|
|
2207
|
+
readonly Final: "final";
|
|
2208
|
+
};
|
|
2209
|
+
type SearchRefusal = (typeof SearchRefusal)[keyof typeof SearchRefusal];
|
|
2210
|
+
/** What an ask refused with `code` does next; no number at all is a network that failed. */
|
|
2211
|
+
declare function searchRefusalKind(code: unknown): SearchRefusal;
|
|
2168
2212
|
/** Probes every region (relay-select.ts's tolerant samples, one url per sample) and answers the order
|
|
2169
2213
|
* to try them in. One region is not probed; none answers []. Never rejects on a probe. */
|
|
2170
2214
|
declare function selectAuthorityRegions(regions: readonly AuthorityRegion[], options?: RelaySelectOptions): Promise<AuthorityRegionPick[]>;
|
|
2215
|
+
/** {@link selectAuthorityRegions} for a pool join: the same probe, answering where to look for players
|
|
2216
|
+
* and where to open a room. One region is not probed. Never rejects on a probe. */
|
|
2217
|
+
declare function selectAuthoritySearch(regions: readonly AuthorityRegion[], search?: AuthoritySearchOptions, options?: RelaySelectOptions): Promise<AuthoritySearchPlan>;
|
|
2171
2218
|
|
|
2172
2219
|
/** The rejoin's schedule and windows, ms. */
|
|
2173
2220
|
declare const REJOIN: {
|
|
@@ -2284,6 +2331,15 @@ type AuthorityConnectOptions = {
|
|
|
2284
2331
|
/** Override: connect to THIS exact url, skipping latency selection: an invitee passes the inviter's
|
|
2285
2332
|
* `session.url` and lands in the inviter's region (a party plays in its leader's region). */
|
|
2286
2333
|
url?: string;
|
|
2334
|
+
/** How a pool join picks its region (§17.1 *Finding players*). Default `players`: the regions in reach
|
|
2335
|
+
* are asked for a room that already has players, nearest first, and a room is opened in the nearest
|
|
2336
|
+
* one only when none has. `nearest` joins the nearest region whoever is elsewhere. */
|
|
2337
|
+
regionRule?: RegionRule;
|
|
2338
|
+
/** The farthest region asked for players, as a probe's round trip in ms. Default 350. */
|
|
2339
|
+
maxPingMs?: number;
|
|
2340
|
+
/** The one region to play in, by its name in `regions` (a player's own choice): no other is joined
|
|
2341
|
+
* or created in. A name the cluster does not have is ignored. A join by code still finds its room. */
|
|
2342
|
+
region?: string;
|
|
2287
2343
|
/** One latency sample of a url (relay-select.ts `RelayProbe`). Default: one Colyseus ping. For tests
|
|
2288
2344
|
* and benches; a game leaves it unset. */
|
|
2289
2345
|
probe?: RelayProbe;
|
|
@@ -2668,4 +2724,4 @@ declare function createPrivateAuthority(opts: AuthorityConnectOptions): Promise<
|
|
|
2668
2724
|
*/
|
|
2669
2725
|
declare function joinPrivateAuthority(code: string, opts: AuthorityConnectOptions): Promise<AuthoritySession>;
|
|
2670
2726
|
|
|
2671
|
-
export { AUTHORITY_REFUSALS_PER_REGION, AuthorityBody, AuthorityCloseCode, type AuthorityConnectOptions, AuthorityContactRule, type AuthorityDraining, AuthorityEntity, type AuthorityEntityView, AuthorityInput, AuthorityJoinMethod, type AuthorityJoinPlan, AuthorityJoint, AuthorityLimits, AuthorityPhysicsEntity, AuthorityPhysicsRoomState, type AuthorityPhysicsStatus, AuthorityPlayer, type AuthorityPlayerView, type AuthorityPrediction, type AuthorityPredictionStatus, type AuthorityRegion, type AuthorityRegionPick, type AuthorityRejoined, type AuthorityRejoining, AuthorityRoomLimits, AuthorityRoomState, type AuthorityRoomTarget, type AuthoritySeatPass, AuthoritySeatRule, type AuthoritySession, AuthorityShape, type AuthorityStep, AuthorityTargetRefusal, type AuthorityTelemetry, CONVEYOR_RATE, type Carried, type CarryReport, type CastHit, ContactBody, ContactDrawing, type ContactEnv, ContactEvent, ContactKind, type ContactPassOptions, ContactPolicy, type ContactReport, type ContactWorld, type ContactWorldSpec, type ConveyorBody, DEFAULT_SEAT_DRAWING, DEFAULT_TICK_HZ, type DeclaredContacts, type DrawnPose, EntityData, type EntityIndex, type EntityOut, type EventOut, FAST_OFFER_WAIT_MS, type FaceFilter, FastFallbackReason, type FastLineAnnouncer, type FastLineObservation, FastNotAsked, type FastSeatStatus, FastSocketOpener, FastTransport, FirstpersonWorld, FirstpersonWorldSpec, type Frame, GuestGlobal, HEAD_LANDING_SPEED, HeadHold, type HeadSlabs, type HeadsContext, type HeldSeat, HullBody, HullOps, INPUT_REPEAT_MAX_MS, INPUT_REPEAT_RUN_MS, INPUT_REPEAT_STEADY, INPUT_TICK_MOD, INPUT_TICK_SLACK_S, type InitOut, InputData, InputRepeat, type InputThrottle, InputUse, Json, type KinematicSupport, LeaveReason, Lift, type LiftBodyPose, type LiftCollider, type LiftPose, type LiftSeat, LiftSolidsCache, type LiftTick, LiftVec, LiftsPolicy, MAX_PROJECTILE_G, MAX_PROJECTILE_TICK, MAX_TICK_HZ, MOTION_VIEW_TAG, PHYSICS_ONLY_TICK_HZ, type PairContact, type PairOutcome, PhysAnswerRow, PhysBodyRow, type PhysCommand, PhysEvRow, type PhysicsAdmission, PhysicsCeiling, PhysicsErrorCode, type PhysicsPredictionStatus, type PhysicsTickIndex, type Plane, PlayerInfo, type PolicyBody, PosedSolid, PosedWorld, PredictBodyRule, PredictCore, PredictKinematicRule, PredictStatus, type PredictedBody, type PredictedField, PresetName, type ProjectileLaunch, type ProjectilePrediction, type ProjectileState, type PropBody, type PropNudge, type PropSeat, PropSlide, REJOIN, RIDE_CHAIN_MAX, type ReconcilerAckHold, RefusalKind, type RejoinInput, type RejoinStep, RejoinStop, RejoinWay, RemoteInput, RemotePredict, ResolvedModuleConfig, type Ride, type RiderPose, SEAT_DRAWINGS, SHOVE_EASE_DONE, STALL_PREDICT_MS, STEADY_BUCKETS, SUCCESSOR_OPEN_MS, type SeatChange, type SeatClock, type SeatDrawing, type SeatLifts, type SeatMotion, type SeatOffset, type SeatPass, type SeatPose, type SeatRate, type SeatRepeats, type SeatStep, SeatWorld, type SeatWorldLifts, type SeatWorldOptions, ShotWorldCount, SlideWay, type SolidGrid, StandRule, THROTTLE_DILATION, THROTTLE_LOW_INPUTS, THROTTLE_SEAT_LEAD, TICK_RATES, TickHz, type TickIn, type TickInput, type TickOut, TickOutFault, type TickSolids, TopRule, UNSTICK_SOLIDS, type V3, type WireResult, WorldCost, WorldCostMeter, type WorldCostReport, capacityWalk, carryOnLift, carrySlot, carryTick, castWorld, comesBackAfter, compileContactWorld, connectAuthority, consumedUse, contactOf, contactPair, contactPass, contactPassOf, contactRuleOf, conveyorNudges, conveyorUnder, createPrivateAuthority, declaredContacts, farApart, fastLineAnnouncer, feedWithPayback, forgiveRepeats, frameOf, hasStand, headHold, headOffset, headSlab, headSlabs, headStands, impartSlot, impartTick, inLiftFrame, inputThrottle, inputTickField, inputTickLag, inputTickSlack, insideFace, insideWorld, isProjectile, joinPrivateAuthority, liftBodyKey, liftBodyPose, liftBodySpawn, liftCollider, liftDrawnAt, liftPose, liftPoses, liftPosesAt, liftStartsOf, liftTopAt, liftUnder, matchKeyOf, mayStand, nextCodeRegion, normalizeAuthorityRegions, pcos, planAuthorityJoin, planSuccessorJoin, posedFor, posedSolids, projectileAt, propSlideNudges, psin, psincos, rankAuthorityRegions, reconcileThrough, reconcilerAck, reconcilerAckHold, refusalKind, regionProbePlan, rejoinStep, repeatMaxTicks, repeatRunTicks, repeatStarved, repeatsSeat, rideSlot, rideStep, rideTick, ridesOn, rodeBy, roomAlong, sameHeldInput, sanitizeInput, seatClock, seatLeadClock, seatLiftsOf, seatOffset, seatRepeats, seatTauMs, seatTouchShare, seatUnderProp, selectAuthorityRegions, selectMatchWorld, serverTick, shoveEase, spinStep, stateThrough, stepOnLifts, steppedThrough, surfaceUp, tagsOf, throttleState, throttleSteps, tickInstant, tickSolids, topRuleOf, udpLine, unstickBody, validMatchKey, validRoomCode, validSuccessorOf, validateInitOut, validateTickOut, viewOnLift, webSocketLine, worldCost, yieldShares };
|
|
2727
|
+
export { AUTHORITY_MAX_PING_MS, AUTHORITY_REFUSALS_PER_REGION, AuthorityBody, AuthorityCloseCode, type AuthorityConnectOptions, AuthorityContactRule, type AuthorityDraining, AuthorityEntity, type AuthorityEntityView, AuthorityInput, AuthorityJoinMethod, type AuthorityJoinPlan, AuthorityJoint, AuthorityLimits, AuthorityPhysicsEntity, AuthorityPhysicsRoomState, type AuthorityPhysicsStatus, AuthorityPlayer, type AuthorityPlayerView, type AuthorityPrediction, type AuthorityPredictionStatus, type AuthorityRegion, type AuthorityRegionPick, type AuthorityRejoined, type AuthorityRejoining, AuthorityRoomLimits, AuthorityRoomState, type AuthorityRoomTarget, type AuthoritySearchOptions, type AuthoritySearchPlan, type AuthoritySeatPass, AuthoritySeatRule, type AuthoritySession, AuthorityShape, type AuthorityStep, AuthorityTargetRefusal, type AuthorityTelemetry, CONVEYOR_RATE, type Carried, type CarryReport, type CastHit, ContactBody, ContactDrawing, type ContactEnv, ContactEvent, ContactKind, type ContactPassOptions, ContactPolicy, type ContactReport, type ContactWorld, type ContactWorldSpec, type ConveyorBody, DEFAULT_SEAT_DRAWING, DEFAULT_TICK_HZ, type DeclaredContacts, type DrawnPose, EntityData, type EntityIndex, type EntityOut, type EventOut, FAST_OFFER_WAIT_MS, type FaceFilter, FastFallbackReason, type FastLineAnnouncer, type FastLineObservation, FastNotAsked, type FastSeatStatus, FastSocketOpener, FastTransport, FirstpersonWorld, FirstpersonWorldSpec, type Frame, GuestGlobal, HEAD_LANDING_SPEED, HeadHold, type HeadSlabs, type HeadsContext, type HeldSeat, HullBody, HullOps, INPUT_REPEAT_MAX_MS, INPUT_REPEAT_RUN_MS, INPUT_REPEAT_STEADY, INPUT_TICK_MOD, INPUT_TICK_SLACK_S, type InitOut, InputData, InputRepeat, type InputThrottle, InputUse, Json, type KinematicSupport, LeaveReason, Lift, type LiftBodyPose, type LiftCollider, type LiftPose, type LiftSeat, LiftSolidsCache, type LiftTick, LiftVec, LiftsPolicy, MAX_PROJECTILE_G, MAX_PROJECTILE_TICK, MAX_TICK_HZ, MOTION_VIEW_TAG, PHYSICS_ONLY_TICK_HZ, type PairContact, type PairOutcome, PhysAnswerRow, PhysBodyRow, type PhysCommand, PhysEvRow, type PhysicsAdmission, PhysicsCeiling, PhysicsErrorCode, type PhysicsPredictionStatus, type PhysicsTickIndex, type Plane, PlayerInfo, type PolicyBody, PosedSolid, PosedWorld, PredictBodyRule, PredictCore, PredictKinematicRule, PredictStatus, type PredictedBody, type PredictedField, PresetName, type ProjectileLaunch, type ProjectilePrediction, type ProjectileState, type PropBody, type PropNudge, type PropSeat, PropSlide, REJOIN, RIDE_CHAIN_MAX, type ReconcilerAckHold, RefusalKind, RegionRule, type RejoinInput, type RejoinStep, RejoinStop, RejoinWay, RemoteInput, RemotePredict, ResolvedModuleConfig, type Ride, type RiderPose, SEAT_DRAWINGS, SHOVE_EASE_DONE, STALL_PREDICT_MS, STEADY_BUCKETS, SUCCESSOR_OPEN_MS, SearchRefusal, type SeatChange, type SeatClock, type SeatDrawing, type SeatLifts, type SeatMotion, type SeatOffset, type SeatPass, type SeatPose, type SeatRate, type SeatRepeats, type SeatStep, SeatWorld, type SeatWorldLifts, type SeatWorldOptions, ShotWorldCount, SlideWay, type SolidGrid, StandRule, THROTTLE_DILATION, THROTTLE_LOW_INPUTS, THROTTLE_SEAT_LEAD, TICK_RATES, TickHz, type TickIn, type TickInput, type TickOut, TickOutFault, type TickSolids, TopRule, UNSTICK_SOLIDS, type V3, type WireResult, WorldCost, WorldCostMeter, type WorldCostReport, capacityWalk, carryOnLift, carrySlot, carryTick, castWorld, comesBackAfter, compileContactWorld, connectAuthority, consumedUse, contactOf, contactPair, contactPass, contactPassOf, contactRuleOf, conveyorNudges, conveyorUnder, createPrivateAuthority, declaredContacts, farApart, fastLineAnnouncer, feedWithPayback, forgiveRepeats, frameOf, hasStand, headHold, headOffset, headSlab, headSlabs, headStands, impartSlot, impartTick, inLiftFrame, inputThrottle, inputTickField, inputTickLag, inputTickSlack, insideFace, insideWorld, isProjectile, joinPrivateAuthority, liftBodyKey, liftBodyPose, liftBodySpawn, liftCollider, liftDrawnAt, liftPose, liftPoses, liftPosesAt, liftStartsOf, liftTopAt, liftUnder, matchKeyOf, mayStand, nextCodeRegion, normalizeAuthorityRegions, pcos, planAuthorityJoin, planAuthoritySearch, planSuccessorJoin, posedFor, posedSolids, projectileAt, propSlideNudges, psin, psincos, rankAuthorityRegions, reconcileThrough, reconcilerAck, reconcilerAckHold, refusalKind, regionProbePlan, rejoinStep, repeatMaxTicks, repeatRunTicks, repeatStarved, repeatsSeat, rideSlot, rideStep, rideTick, ridesOn, rodeBy, roomAlong, sameHeldInput, sanitizeInput, searchRefusalKind, seatClock, seatLeadClock, seatLiftsOf, seatOffset, seatRepeats, seatTauMs, seatTouchShare, seatUnderProp, selectAuthorityRegions, selectAuthoritySearch, selectMatchWorld, serverTick, shoveEase, spinStep, stateThrough, stepOnLifts, steppedThrough, surfaceUp, tagsOf, throttleState, throttleSteps, tickInstant, tickSolids, topRuleOf, udpLine, unstickBody, validMatchKey, validRoomCode, validSuccessorOf, validateInitOut, validateTickOut, viewOnLift, webSocketLine, worldCost, yieldShares };
|
package/dist/authority/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
claimToAxis
|
|
7
7
|
} from "../chunk-TSYEWPVO.js";
|
|
8
8
|
import {
|
|
9
|
+
AUTHORITY_MAX_PING_MS,
|
|
9
10
|
AUTHORITY_REFUSALS_PER_REGION,
|
|
10
11
|
AuthorityBody,
|
|
11
12
|
AuthorityEntity,
|
|
@@ -58,6 +59,7 @@ import {
|
|
|
58
59
|
RULED_SLEW,
|
|
59
60
|
RULED_SNAP_TICKS,
|
|
60
61
|
RefusalKind,
|
|
62
|
+
RegionRule,
|
|
61
63
|
RejoinStop,
|
|
62
64
|
RejoinWay,
|
|
63
65
|
SEAT_DRAWINGS,
|
|
@@ -69,6 +71,7 @@ import {
|
|
|
69
71
|
STALL_PREDICT_MS,
|
|
70
72
|
STEADY_BUCKETS,
|
|
71
73
|
SUCCESSOR_OPEN_MS,
|
|
74
|
+
SearchRefusal,
|
|
72
75
|
SeatContacts,
|
|
73
76
|
SeatWorld,
|
|
74
77
|
SlideWay,
|
|
@@ -148,6 +151,7 @@ import {
|
|
|
148
151
|
parsePhysDesc,
|
|
149
152
|
physDescText,
|
|
150
153
|
physicsBudgetMs,
|
|
154
|
+
planAuthoritySearch,
|
|
151
155
|
planSuccessorJoin,
|
|
152
156
|
posedFor,
|
|
153
157
|
posedSolids,
|
|
@@ -178,6 +182,7 @@ import {
|
|
|
178
182
|
ruledPoseAt,
|
|
179
183
|
sameBodyState,
|
|
180
184
|
sameHeldInput,
|
|
185
|
+
searchRefusalKind,
|
|
181
186
|
seatClock,
|
|
182
187
|
seatLeadClock,
|
|
183
188
|
seatLiftsOf,
|
|
@@ -189,6 +194,7 @@ import {
|
|
|
189
194
|
seatTouchShare,
|
|
190
195
|
seatUnderProp,
|
|
191
196
|
selectAuthorityRegions,
|
|
197
|
+
selectAuthoritySearch,
|
|
192
198
|
selfTestPredictCore,
|
|
193
199
|
separateAround,
|
|
194
200
|
separateDrawn,
|
|
@@ -217,8 +223,8 @@ import {
|
|
|
217
223
|
wireRowOfState,
|
|
218
224
|
writeWireRow,
|
|
219
225
|
yieldShares
|
|
220
|
-
} from "../chunk-
|
|
221
|
-
import "../chunk-
|
|
226
|
+
} from "../chunk-SQC6WTMF.js";
|
|
227
|
+
import "../chunk-42R7NREA.js";
|
|
222
228
|
import "../chunk-YWQ3ALYY.js";
|
|
223
229
|
import {
|
|
224
230
|
AcceptKind,
|
|
@@ -400,7 +406,7 @@ import {
|
|
|
400
406
|
validRoomCode,
|
|
401
407
|
validateInitOut,
|
|
402
408
|
validateTickOut
|
|
403
|
-
} from "../chunk-
|
|
409
|
+
} from "../chunk-HHETHPGL.js";
|
|
404
410
|
import {
|
|
405
411
|
LeaveReason,
|
|
406
412
|
MODULE_CONTRACT,
|
|
@@ -446,7 +452,7 @@ import {
|
|
|
446
452
|
AUTHORITY_ROOM_ENDED_TEXT,
|
|
447
453
|
AuthorityCloseCode,
|
|
448
454
|
authorityCloseCodeOf
|
|
449
|
-
} from "../chunk-
|
|
455
|
+
} from "../chunk-7J3YWPCA.js";
|
|
450
456
|
|
|
451
457
|
// src/authority/world-cost.ts
|
|
452
458
|
var WorldCost = {
|
|
@@ -518,6 +524,7 @@ function worldCost(spec, preset = "firstperson", params) {
|
|
|
518
524
|
return { ...meter, solids: solids.length, steps, opsPerStep, accepted: opsPerStep <= WorldCost.MAX_OPS_PER_STEP };
|
|
519
525
|
}
|
|
520
526
|
export {
|
|
527
|
+
AUTHORITY_MAX_PING_MS,
|
|
521
528
|
AUTHORITY_REFUSALS_PER_REGION,
|
|
522
529
|
AUTHORITY_ROOM_ENDED_TEXT,
|
|
523
530
|
AcceptKind,
|
|
@@ -695,6 +702,7 @@ export {
|
|
|
695
702
|
RULED_SLEW,
|
|
696
703
|
RULED_SNAP_TICKS,
|
|
697
704
|
RefusalKind,
|
|
705
|
+
RegionRule,
|
|
698
706
|
RejoinStop,
|
|
699
707
|
RejoinWay,
|
|
700
708
|
RemotePredict,
|
|
@@ -708,6 +716,7 @@ export {
|
|
|
708
716
|
STALL_PREDICT_MS,
|
|
709
717
|
STEADY_BUCKETS,
|
|
710
718
|
SUCCESSOR_OPEN_MS,
|
|
719
|
+
SearchRefusal,
|
|
711
720
|
SeatContacts,
|
|
712
721
|
SeatWorld,
|
|
713
722
|
ShotWorldCost,
|
|
@@ -847,6 +856,7 @@ export {
|
|
|
847
856
|
physicsWorldDesc,
|
|
848
857
|
physicsWorldPredicts,
|
|
849
858
|
planAuthorityJoin,
|
|
859
|
+
planAuthoritySearch,
|
|
850
860
|
planSuccessorJoin,
|
|
851
861
|
posedFor,
|
|
852
862
|
posedSolids,
|
|
@@ -897,6 +907,7 @@ export {
|
|
|
897
907
|
sameBodyState,
|
|
898
908
|
sameHeldInput,
|
|
899
909
|
sanitizeInput,
|
|
910
|
+
searchRefusalKind,
|
|
900
911
|
seatClock,
|
|
901
912
|
seatLeadClock,
|
|
902
913
|
seatLiftsOf,
|
|
@@ -908,6 +919,7 @@ export {
|
|
|
908
919
|
seatTouchShare,
|
|
909
920
|
seatUnderProp,
|
|
910
921
|
selectAuthorityRegions,
|
|
922
|
+
selectAuthoritySearch,
|
|
911
923
|
selectMatchWorld,
|
|
912
924
|
selfTestPredictCore,
|
|
913
925
|
separateAround,
|