@crowdedkingdoms/crowdyjs 18.19.0-test.1 → 18.20.0-dev.1
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/README.md +8 -1
- package/dist/default-origin.d.ts +6 -6
- package/dist/default-origin.d.ts.map +1 -1
- package/dist/default-origin.js +6 -6
- package/dist/domains/udp.d.ts +47 -7
- package/dist/domains/udp.d.ts.map +1 -1
- package/dist/domains/udp.js +93 -42
- package/dist/generated/graphql-schema.d.ts +1 -1
- package/dist/generated/graphql-schema.d.ts.map +1 -1
- package/dist/generated/graphql-schema.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/realtime.d.ts +35 -0
- package/dist/realtime.d.ts.map +1 -1
- package/dist/realtime.js +69 -9
- package/dist/world.d.ts +5 -3
- package/dist/world.d.ts.map +1 -1
- package/dist/world.js +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -556,7 +556,14 @@ The plain `sendActorUpdate`, `sendVoxelUpdate`, `sendAudioPacket`,
|
|
|
556
556
|
`sendTextPacket`, and `sendClientEvent` methods return the GraphQL mutation
|
|
557
557
|
result immediately. The `AndWait` variants allocate a `sequenceNumber` when
|
|
558
558
|
one is missing and wait for either a matching notification or
|
|
559
|
-
`GenericErrorResponse
|
|
559
|
+
`GenericErrorResponse`, timing the wait from when the send was accepted. The
|
|
560
|
+
first awaited send of each session (the first connect, a reconnect, or a move to
|
|
561
|
+
another instance) is retried once with a fresh `sequenceNumber` if it is refused
|
|
562
|
+
`UNAUTHORIZED` or, for the actor and voxel updates the server echoes, if it times
|
|
563
|
+
out (`UDP_SEQUENCE_TIMEOUT`), which is how a realtime server older than v0.42.0
|
|
564
|
+
answers a session's first message. A send whose `sequenceNumber` you chose is
|
|
565
|
+
not retried, and an audio, text or event send's timeout (its normal success) is
|
|
566
|
+
never retried. `sendChannelMessage` broadcasts an opaque payload on a
|
|
560
567
|
channel. `sendRangedChannelMessage` publishes on a channel too, but only members
|
|
561
568
|
with a live actor within `maxDistance` chunks (straight-line distance between
|
|
562
569
|
chunk coordinates) of `chunk` receive it; they get the same `channelMessage`
|
package/dist/default-origin.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GENERATED — do not edit. The operator tooling's
|
|
3
|
-
* `sync-client-origins.mjs --write --tier
|
|
3
|
+
* `sync-client-origins.mjs --write --tier dev` writes this file, and
|
|
4
4
|
* `check-sdk-default-origin.mjs` refuses it when it names the wrong tier or a
|
|
5
5
|
* host that declaration does not carry. Regenerate; never hand-edit.
|
|
6
6
|
*
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
* than no default at all. Every fallback this SDK ever removed named a host that
|
|
11
11
|
* was already dead. A generated file plus a gate is what makes this one different.
|
|
12
12
|
*
|
|
13
|
-
* Source: the operator's per-tier public CK API origin declaration, tier '
|
|
13
|
+
* Source: the operator's per-tier public CK API origin declaration, tier 'dev'
|
|
14
14
|
*/
|
|
15
15
|
/** The tier this build of the SDK is published for. */
|
|
16
|
-
export declare const CROWDY_DEFAULT_TIER = "
|
|
16
|
+
export declare const CROWDY_DEFAULT_TIER = "dev";
|
|
17
17
|
/** The public CK API origin for that tier. */
|
|
18
|
-
export declare const CROWDY_DEFAULT_HTTP_ORIGIN = "https://ck.
|
|
18
|
+
export declare const CROWDY_DEFAULT_HTTP_ORIGIN = "https://ck.dev.crowdedkingdoms.com";
|
|
19
19
|
/** The same host over WebSocket. A scheme is composed; a hostname is looked up. */
|
|
20
|
-
export declare const CROWDY_DEFAULT_WS_ORIGIN = "wss://ck.
|
|
20
|
+
export declare const CROWDY_DEFAULT_WS_ORIGIN = "wss://ck.dev.crowdedkingdoms.com";
|
|
21
21
|
/** The bare hostname, for callers that need to compare rather than dial. */
|
|
22
|
-
export declare const CROWDY_DEFAULT_HOST = "ck.
|
|
22
|
+
export declare const CROWDY_DEFAULT_HOST = "ck.dev.crowdedkingdoms.com";
|
|
23
23
|
//# sourceMappingURL=default-origin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-origin.d.ts","sourceRoot":"","sources":["../src/default-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"default-origin.d.ts","sourceRoot":"","sources":["../src/default-origin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,uDAAuD;AACvD,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,8CAA8C;AAC9C,eAAO,MAAM,0BAA0B,uCAAuC,CAAC;AAE/E,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,qCAAqC,CAAC;AAE3E,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,+BAA+B,CAAC"}
|
package/dist/default-origin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GENERATED — do not edit. The operator tooling's
|
|
3
|
-
* `sync-client-origins.mjs --write --tier
|
|
3
|
+
* `sync-client-origins.mjs --write --tier dev` writes this file, and
|
|
4
4
|
* `check-sdk-default-origin.mjs` refuses it when it names the wrong tier or a
|
|
5
5
|
* host that declaration does not carry. Regenerate; never hand-edit.
|
|
6
6
|
*
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
* than no default at all. Every fallback this SDK ever removed named a host that
|
|
11
11
|
* was already dead. A generated file plus a gate is what makes this one different.
|
|
12
12
|
*
|
|
13
|
-
* Source: the operator's per-tier public CK API origin declaration, tier '
|
|
13
|
+
* Source: the operator's per-tier public CK API origin declaration, tier 'dev'
|
|
14
14
|
*/
|
|
15
15
|
/** The tier this build of the SDK is published for. */
|
|
16
|
-
export const CROWDY_DEFAULT_TIER = '
|
|
16
|
+
export const CROWDY_DEFAULT_TIER = 'dev';
|
|
17
17
|
/** The public CK API origin for that tier. */
|
|
18
|
-
export const CROWDY_DEFAULT_HTTP_ORIGIN = 'https://ck.
|
|
18
|
+
export const CROWDY_DEFAULT_HTTP_ORIGIN = 'https://ck.dev.crowdedkingdoms.com';
|
|
19
19
|
/** The same host over WebSocket. A scheme is composed; a hostname is looked up. */
|
|
20
|
-
export const CROWDY_DEFAULT_WS_ORIGIN = 'wss://ck.
|
|
20
|
+
export const CROWDY_DEFAULT_WS_ORIGIN = 'wss://ck.dev.crowdedkingdoms.com';
|
|
21
21
|
/** The bare hostname, for callers that need to compare rather than dial. */
|
|
22
|
-
export const CROWDY_DEFAULT_HOST = 'ck.
|
|
22
|
+
export const CROWDY_DEFAULT_HOST = 'ck.dev.crowdedkingdoms.com';
|
package/dist/domains/udp.d.ts
CHANGED
|
@@ -33,10 +33,13 @@ import { type VideoCodec } from '../media/video-frames.js';
|
|
|
33
33
|
*
|
|
34
34
|
* Spatial caveats: a `sequenceNumber` is a uint8 (0-255) used for
|
|
35
35
|
* **correlation only** — not an idempotency key, and the server does not dedupe
|
|
36
|
-
* replays. The
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* replays. The game server holds a session's first messages, and the first
|
|
37
|
+
* message into a newly entered region, until the permissions they need have
|
|
38
|
+
* loaded (realtime server v0.42.0 and later); an older server dropped or
|
|
39
|
+
* refused them. Against one, the SDK retries the first awaited send of each
|
|
40
|
+
* session once (see {@link sendActorUpdateAndWait}), but a plain `send*` may
|
|
41
|
+
* still be lost. To receive any `...AndWait` echo or notification you must have
|
|
42
|
+
* an active {@link subscribe}.
|
|
40
43
|
*/
|
|
41
44
|
export declare class UdpAPI {
|
|
42
45
|
private gql;
|
|
@@ -50,6 +53,8 @@ export declare class UdpAPI {
|
|
|
50
53
|
private waitForGameplayTokenRefresh?;
|
|
51
54
|
private readonly sequences;
|
|
52
55
|
private readonly inFlightActorUpdates;
|
|
56
|
+
/** The session epoch of the last awaited send that was answered. */
|
|
57
|
+
private confirmedEpoch;
|
|
53
58
|
constructor(gql: GraphQLClient, subs: SubscriptionManager, metrics?: RealtimeMetrics | undefined,
|
|
54
59
|
/**
|
|
55
60
|
* When set, every UDP send waits for this promise (a gameplay-token
|
|
@@ -159,8 +164,22 @@ export declare class UdpAPI {
|
|
|
159
164
|
* @param input - {@link ActorUpdateRequestInput} (see {@link sendActorUpdate}
|
|
160
165
|
* for field units/encoding). Omit `sequenceNumber` to let the SDK allocate
|
|
161
166
|
* one.
|
|
162
|
-
*
|
|
163
|
-
*
|
|
167
|
+
* Every `send*AndWait` behaves as follows:
|
|
168
|
+
* - `timeoutMs` counts from when the send was accepted, not from the call, so
|
|
169
|
+
* a slow send leg (the first send opening the proxy session, the binary
|
|
170
|
+
* relay finishing its handshake) does not shorten the wait for the echo.
|
|
171
|
+
* - The first awaited send of a new session (the first connect, a reconnect,
|
|
172
|
+
* a move to another instance) is retried once with a fresh sequence number
|
|
173
|
+
* if it is refused `UNAUTHORIZED` or, for the actor and voxel updates the
|
|
174
|
+
* server echoes, if its echo times out (`UDP_SEQUENCE_TIMEOUT`), which is
|
|
175
|
+
* how a game server older than v0.42.0 answers a session's first messages;
|
|
176
|
+
* the resolved notification then carries the retry's sequence number. A
|
|
177
|
+
* send whose `sequenceNumber` you chose is not retried, and neither is the
|
|
178
|
+
* timeout of an audio, text or event send, which is how those succeed.
|
|
179
|
+
*
|
|
180
|
+
* @param options - `timeoutMs`: how long to wait for the echo once the send
|
|
181
|
+
* is accepted, in milliseconds; defaults to the client's realtime
|
|
182
|
+
* `waitTimeoutMs` (5000).
|
|
164
183
|
* @returns The correlated {@link SpatialNotification} (your own
|
|
165
184
|
* `ActorUpdateNotification` self-echo).
|
|
166
185
|
* @throws {CrowdyGraphQLError} if the underlying send is rejected.
|
|
@@ -168,7 +187,8 @@ export declare class UdpAPI {
|
|
|
168
187
|
* request timeout.
|
|
169
188
|
* @throws {CrowdyRealtimeError} if no echo arrives within `timeoutMs`
|
|
170
189
|
* (`code === 'UDP_SEQUENCE_TIMEOUT'`, retryable), or the server returns a
|
|
171
|
-
* matching `GenericErrorResponse` (its `code` is the server's `errorCode`)
|
|
190
|
+
* matching `GenericErrorResponse` (its `code` is the server's `errorCode`),
|
|
191
|
+
* after the one retry described above when it applies.
|
|
172
192
|
* @example
|
|
173
193
|
* ```ts
|
|
174
194
|
* const unsubscribe = client.udp.subscribe({}, appId); // echoes need an open WS
|
|
@@ -588,6 +608,26 @@ export declare class UdpAPI {
|
|
|
588
608
|
* ```
|
|
589
609
|
*/
|
|
590
610
|
subscribe(handlers: UdpNotificationHandlers, appId: string): () => void;
|
|
611
|
+
/**
|
|
612
|
+
* What every `send*AndWait` does.
|
|
613
|
+
*
|
|
614
|
+
* The wait is registered before the send, so an echo that beats the send's
|
|
615
|
+
* own response is not missed, and its clock starts once the send has been
|
|
616
|
+
* accepted, so a lazy proxy-session open or the relay's handshake on the send
|
|
617
|
+
* leg is not taken from the echo's window.
|
|
618
|
+
*
|
|
619
|
+
* The first awaited send of a new session (the first connect, a reconnect, or
|
|
620
|
+
* a move to another instance) is retried once, with a fresh sequence number,
|
|
621
|
+
* when it is refused `UNAUTHORIZED` or, for a send the server echoes, when
|
|
622
|
+
* its echo times out: a game server from before the one that holds a
|
|
623
|
+
* session's first messages refuses or drops them while the session's
|
|
624
|
+
* permissions load. A send the server does not echo (audio, text, a client
|
|
625
|
+
* event) succeeds by timing out, so its timeout is never retried. Only when
|
|
626
|
+
* the SDK chose the sequence number, because the retry needs one of its own
|
|
627
|
+
* and a caller who chose theirs is correlating by it.
|
|
628
|
+
*/
|
|
629
|
+
private sendAndWait;
|
|
630
|
+
private sendOnceAndWait;
|
|
591
631
|
private withSequence;
|
|
592
632
|
}
|
|
593
633
|
//# sourceMappingURL=udp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"udp.d.ts","sourceRoot":"","sources":["../../src/domains/udp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAEL,KAAK,uBAAuB,EAG5B,KAAK,6BAA6B,EAElC,KAAK,gCAAgC,EAErC,KAAK,gCAAgC,EAErC,KAAK,gCAAgC,EAErC,KAAK,gCAAgC,EAErC,KAAK,+BAA+B,EAEpC,KAAK,gCAAgC,EAErC,KAAK,uCAAuC,EAE5C,KAAK,mCAAmC,EAExC,KAAK,iCAAiC,EAEtC,KAAK,yCAAyC,EAC/C,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAkB1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAG1E
|
|
1
|
+
{"version":3,"file":"udp.d.ts","sourceRoot":"","sources":["../../src/domains/udp.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAEL,KAAK,uBAAuB,EAG5B,KAAK,6BAA6B,EAElC,KAAK,gCAAgC,EAErC,KAAK,gCAAgC,EAErC,KAAK,gCAAgC,EAErC,KAAK,gCAAgC,EAErC,KAAK,+BAA+B,EAEpC,KAAK,gCAAgC,EAErC,KAAK,uCAAuC,EAE5C,KAAK,mCAAmC,EAExC,KAAK,iCAAiC,EAEtC,KAAK,yCAAyC,EAC/C,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAkB1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAiB,KAAK,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAG1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,MAAM;IAOf,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,OAAO,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,2BAA2B,CAAC;IAdtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;IACrD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAA+B;IACpE,oEAAoE;IACpE,OAAO,CAAC,cAAc,CAAM;gBAGlB,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,mBAAmB,EACzB,OAAO,CAAC,EAAE,eAAe,YAAA;IACjC;;;;OAIG;IACK,2BAA2B,CAAC,GAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,aAAA;IAGjF;;;;OAIG;IACG,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC;YAKpC,uBAAuB;IAWrC,OAAO,CAAC,gBAAgB;IAOxB,mEAAmE;IACnE,OAAO,CAAC,MAAM;IAId;;;;OAIG;YACW,YAAY;IAoB1B;;;;;;;;;;;;;OAaG;IACG,OAAO,IAAI,OAAO,CAAC,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;IAkBpE;;;;;;;;OAQG;IACH;;;;;OAKG;IACH,UAAU,IAAI,IAAI;IAIZ,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IASpC;;;;;;;OAOG;IACG,gBAAgB,IAAI,OAAO,CAC/B,6BAA6B,CAAC,0BAA0B,CAAC,CAC1D;IAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACG,eAAe,CACnB,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAC/C,OAAO,CAAC,OAAO,CAAC;YAKL,mBAAmB;IAyCjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiDG;IACG,sBAAsB,CAC1B,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,EAChD,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAM/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACG,eAAe,CACnB,KAAK,EAAE,IAAI,CAAC,gCAAgC,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,GAAG;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GACA,OAAO,CAAC,OAAO,CAAC;IAiBnB;;;;;;;;;;;;;;;;;;OAkBG;IACG,sBAAsB,CAC1B,KAAK,EAAE,IAAI,CAAC,gCAAgC,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,GAAG;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,EACD,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAM/B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,eAAe,CACnB,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAC/C,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;;;;;;;;;;;OAiBG;IACG,sBAAsB,CAC1B,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,EAChD,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAM/B;;;;;;;;;;;;;;;;OAgBG;IACG,eAAe,CACnB,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAC/C,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;;;;;;OAYG;IACG,cAAc,CAAC,KAAK,EAAE;QAC1B,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3C,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,UAAU,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,UAAU,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBnB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,cAAc,CAClB,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,GAC9C,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;;;;;;;;;;;OAiBG;IACG,qBAAqB,CACzB,KAAK,EAAE,+BAA+B,CAAC,OAAO,CAAC,EAC/C,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAM/B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,eAAe,CACnB,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,GAC/C,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;;;;;;;;;;;;OAkBG;IACG,sBAAsB,CAC1B,KAAK,EAAE,gCAAgC,CAAC,OAAO,CAAC,EAChD,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAM/B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,sBAAsB,CAC1B,KAAK,EAAE,uCAAuC,CAAC,OAAO,CAAC,GACtD,OAAO,CAAC,OAAO,CAAC;IAgBnB;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,kBAAkB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IActE;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,kBAAkB,CACtB,KAAK,EAAE,mCAAmC,CAAC,OAAO,CAAC,GAClD,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;;;;;;;;;OAeG;IACG,gBAAgB,CACpB,KAAK,EAAE,iCAAiC,CAAC,OAAO,CAAC,GAChD,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACG,wBAAwB,CAC5B,KAAK,EAAE,yCAAyC,CAAC,OAAO,CAAC,GACxD,OAAO,CAAC,OAAO,CAAC;IAcnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,SAAS,CAAC,QAAQ,EAAE,uBAAuB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,IAAI;IAIvE;;;;;;;;;;;;;;;;;OAiBG;YACW,WAAW;YAgCX,eAAe;IAmB7B,OAAO,CAAC,YAAY;CAQrB"}
|
package/dist/domains/udp.js
CHANGED
|
@@ -33,10 +33,13 @@ import { encodeBase64 } from '../utils.js';
|
|
|
33
33
|
*
|
|
34
34
|
* Spatial caveats: a `sequenceNumber` is a uint8 (0-255) used for
|
|
35
35
|
* **correlation only** — not an idempotency key, and the server does not dedupe
|
|
36
|
-
* replays. The
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* replays. The game server holds a session's first messages, and the first
|
|
37
|
+
* message into a newly entered region, until the permissions they need have
|
|
38
|
+
* loaded (realtime server v0.42.0 and later); an older server dropped or
|
|
39
|
+
* refused them. Against one, the SDK retries the first awaited send of each
|
|
40
|
+
* session once (see {@link sendActorUpdateAndWait}), but a plain `send*` may
|
|
41
|
+
* still be lost. To receive any `...AndWait` echo or notification you must have
|
|
42
|
+
* an active {@link subscribe}.
|
|
40
43
|
*/
|
|
41
44
|
export class UdpAPI {
|
|
42
45
|
constructor(gql, subs, metrics,
|
|
@@ -52,6 +55,8 @@ export class UdpAPI {
|
|
|
52
55
|
this.waitForGameplayTokenRefresh = waitForGameplayTokenRefresh;
|
|
53
56
|
this.sequences = new SequenceAllocator();
|
|
54
57
|
this.inFlightActorUpdates = new Set();
|
|
58
|
+
/** The session epoch of the last awaited send that was answered. */
|
|
59
|
+
this.confirmedEpoch = -1;
|
|
55
60
|
}
|
|
56
61
|
/**
|
|
57
62
|
* Resolves when every in-flight {@link sendActorUpdate} has settled.
|
|
@@ -261,8 +266,22 @@ export class UdpAPI {
|
|
|
261
266
|
* @param input - {@link ActorUpdateRequestInput} (see {@link sendActorUpdate}
|
|
262
267
|
* for field units/encoding). Omit `sequenceNumber` to let the SDK allocate
|
|
263
268
|
* one.
|
|
264
|
-
*
|
|
265
|
-
*
|
|
269
|
+
* Every `send*AndWait` behaves as follows:
|
|
270
|
+
* - `timeoutMs` counts from when the send was accepted, not from the call, so
|
|
271
|
+
* a slow send leg (the first send opening the proxy session, the binary
|
|
272
|
+
* relay finishing its handshake) does not shorten the wait for the echo.
|
|
273
|
+
* - The first awaited send of a new session (the first connect, a reconnect,
|
|
274
|
+
* a move to another instance) is retried once with a fresh sequence number
|
|
275
|
+
* if it is refused `UNAUTHORIZED` or, for the actor and voxel updates the
|
|
276
|
+
* server echoes, if its echo times out (`UDP_SEQUENCE_TIMEOUT`), which is
|
|
277
|
+
* how a game server older than v0.42.0 answers a session's first messages;
|
|
278
|
+
* the resolved notification then carries the retry's sequence number. A
|
|
279
|
+
* send whose `sequenceNumber` you chose is not retried, and neither is the
|
|
280
|
+
* timeout of an audio, text or event send, which is how those succeed.
|
|
281
|
+
*
|
|
282
|
+
* @param options - `timeoutMs`: how long to wait for the echo once the send
|
|
283
|
+
* is accepted, in milliseconds; defaults to the client's realtime
|
|
284
|
+
* `waitTimeoutMs` (5000).
|
|
266
285
|
* @returns The correlated {@link SpatialNotification} (your own
|
|
267
286
|
* `ActorUpdateNotification` self-echo).
|
|
268
287
|
* @throws {CrowdyGraphQLError} if the underlying send is rejected.
|
|
@@ -270,7 +289,8 @@ export class UdpAPI {
|
|
|
270
289
|
* request timeout.
|
|
271
290
|
* @throws {CrowdyRealtimeError} if no echo arrives within `timeoutMs`
|
|
272
291
|
* (`code === 'UDP_SEQUENCE_TIMEOUT'`, retryable), or the server returns a
|
|
273
|
-
* matching `GenericErrorResponse` (its `code` is the server's `errorCode`)
|
|
292
|
+
* matching `GenericErrorResponse` (its `code` is the server's `errorCode`),
|
|
293
|
+
* after the one retry described above when it applies.
|
|
274
294
|
* @example
|
|
275
295
|
* ```ts
|
|
276
296
|
* const unsubscribe = client.udp.subscribe({}, appId); // echoes need an open WS
|
|
@@ -285,13 +305,7 @@ export class UdpAPI {
|
|
|
285
305
|
* ```
|
|
286
306
|
*/
|
|
287
307
|
async sendActorUpdateAndWait(input, options = {}) {
|
|
288
|
-
|
|
289
|
-
const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
|
|
290
|
-
await this.sendActorUpdate(request);
|
|
291
|
-
// Nothing can be echoed that has not left: the bundled relay path holds a
|
|
292
|
-
// send for up to bundleWindowMs, so put it on the wire before waiting.
|
|
293
|
-
this.subs.flushSends();
|
|
294
|
-
return wait;
|
|
308
|
+
return this.sendAndWait(input, options.timeoutMs, true, (request) => this.sendActorUpdate(request));
|
|
295
309
|
}
|
|
296
310
|
/**
|
|
297
311
|
* Send a single voxel (block) update for spatial replication to nearby
|
|
@@ -358,13 +372,7 @@ export class UdpAPI {
|
|
|
358
372
|
* (its `code` is the server's `errorCode`).
|
|
359
373
|
*/
|
|
360
374
|
async sendVoxelUpdateAndWait(input, options = {}) {
|
|
361
|
-
|
|
362
|
-
const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
|
|
363
|
-
await this.sendVoxelUpdate(request);
|
|
364
|
-
// Nothing can be echoed that has not left: the bundled relay path holds a
|
|
365
|
-
// send for up to bundleWindowMs, so put it on the wire before waiting.
|
|
366
|
-
this.subs.flushSends();
|
|
367
|
-
return wait;
|
|
375
|
+
return this.sendAndWait(input, options.timeoutMs, true, (request) => this.sendVoxelUpdate(request));
|
|
368
376
|
}
|
|
369
377
|
/**
|
|
370
378
|
* Send a spatial voice/audio packet, fanned out to nearby actors as a
|
|
@@ -423,13 +431,7 @@ export class UdpAPI {
|
|
|
423
431
|
* `errorCode`, e.g. `UNAUTHORIZED` for a missing voice permission).
|
|
424
432
|
*/
|
|
425
433
|
async sendAudioPacketAndWait(input, options = {}) {
|
|
426
|
-
|
|
427
|
-
const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
|
|
428
|
-
await this.sendAudioPacket(request);
|
|
429
|
-
// Nothing can be echoed that has not left: the bundled relay path holds a
|
|
430
|
-
// send for up to bundleWindowMs, so put it on the wire before waiting.
|
|
431
|
-
this.subs.flushSends();
|
|
432
|
-
return wait;
|
|
434
|
+
return this.sendAndWait(input, options.timeoutMs, false, (request) => this.sendAudioPacket(request));
|
|
433
435
|
}
|
|
434
436
|
/**
|
|
435
437
|
* Send ONE webcam video fragment, fanned out to nearby actors as a
|
|
@@ -540,13 +542,7 @@ export class UdpAPI {
|
|
|
540
542
|
* `errorCode`).
|
|
541
543
|
*/
|
|
542
544
|
async sendTextPacketAndWait(input, options = {}) {
|
|
543
|
-
|
|
544
|
-
const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
|
|
545
|
-
await this.sendTextPacket(request);
|
|
546
|
-
// Nothing can be echoed that has not left: the bundled relay path holds a
|
|
547
|
-
// send for up to bundleWindowMs, so put it on the wire before waiting.
|
|
548
|
-
this.subs.flushSends();
|
|
549
|
-
return wait;
|
|
545
|
+
return this.sendAndWait(input, options.timeoutMs, false, (request) => this.sendTextPacket(request));
|
|
550
546
|
}
|
|
551
547
|
/**
|
|
552
548
|
* Send a custom, app-defined client event for spatial replication to nearby
|
|
@@ -603,13 +599,7 @@ export class UdpAPI {
|
|
|
603
599
|
* `errorCode`).
|
|
604
600
|
*/
|
|
605
601
|
async sendClientEventAndWait(input, options = {}) {
|
|
606
|
-
|
|
607
|
-
const wait = this.subs.waitForSequence(request.sequenceNumber, options.timeoutMs);
|
|
608
|
-
await this.sendClientEvent(request);
|
|
609
|
-
// Nothing can be echoed that has not left: the bundled relay path holds a
|
|
610
|
-
// send for up to bundleWindowMs, so put it on the wire before waiting.
|
|
611
|
-
this.subs.flushSends();
|
|
612
|
-
return wait;
|
|
602
|
+
return this.sendAndWait(input, options.timeoutMs, false, (request) => this.sendClientEvent(request));
|
|
613
603
|
}
|
|
614
604
|
/**
|
|
615
605
|
* Send a direct actor-to-actor message, delivered only to the actor whose
|
|
@@ -824,6 +814,59 @@ export class UdpAPI {
|
|
|
824
814
|
subscribe(handlers, appId) {
|
|
825
815
|
return this.subs.subscribe(handlers, appId);
|
|
826
816
|
}
|
|
817
|
+
/**
|
|
818
|
+
* What every `send*AndWait` does.
|
|
819
|
+
*
|
|
820
|
+
* The wait is registered before the send, so an echo that beats the send's
|
|
821
|
+
* own response is not missed, and its clock starts once the send has been
|
|
822
|
+
* accepted, so a lazy proxy-session open or the relay's handshake on the send
|
|
823
|
+
* leg is not taken from the echo's window.
|
|
824
|
+
*
|
|
825
|
+
* The first awaited send of a new session (the first connect, a reconnect, or
|
|
826
|
+
* a move to another instance) is retried once, with a fresh sequence number,
|
|
827
|
+
* when it is refused `UNAUTHORIZED` or, for a send the server echoes, when
|
|
828
|
+
* its echo times out: a game server from before the one that holds a
|
|
829
|
+
* session's first messages refuses or drops them while the session's
|
|
830
|
+
* permissions load. A send the server does not echo (audio, text, a client
|
|
831
|
+
* event) succeeds by timing out, so its timeout is never retried. Only when
|
|
832
|
+
* the SDK chose the sequence number, because the retry needs one of its own
|
|
833
|
+
* and a caller who chose theirs is correlating by it.
|
|
834
|
+
*/
|
|
835
|
+
async sendAndWait(input, timeoutMs, echoed, send) {
|
|
836
|
+
const epoch = this.subs.sessionEpoch();
|
|
837
|
+
const retryable = epoch !== this.confirmedEpoch && input.sequenceNumber == null;
|
|
838
|
+
try {
|
|
839
|
+
const echo = await this.sendOnceAndWait(this.withSequence(input), timeoutMs, send).catch((error) => {
|
|
840
|
+
if (!retryable || !isFirstSendFailure(error, echoed))
|
|
841
|
+
throw error;
|
|
842
|
+
return this.sendOnceAndWait(this.withSequence({ ...input, sequenceNumber: undefined }), timeoutMs, send);
|
|
843
|
+
});
|
|
844
|
+
this.confirmedEpoch = epoch;
|
|
845
|
+
return echo;
|
|
846
|
+
}
|
|
847
|
+
catch (error) {
|
|
848
|
+
// A send the server does not echo succeeds by timing out, and that
|
|
849
|
+
// answers for the session as well as an echo would.
|
|
850
|
+
if (!echoed && isTimeout(error))
|
|
851
|
+
this.confirmedEpoch = epoch;
|
|
852
|
+
throw error;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
async sendOnceAndWait(request, timeoutMs, send) {
|
|
856
|
+
const wait = this.subs.expectSequence(request.sequenceNumber, timeoutMs);
|
|
857
|
+
try {
|
|
858
|
+
await send(request);
|
|
859
|
+
}
|
|
860
|
+
catch (error) {
|
|
861
|
+
wait.cancel();
|
|
862
|
+
throw error;
|
|
863
|
+
}
|
|
864
|
+
// Nothing can be echoed that has not left: the bundled relay path holds a
|
|
865
|
+
// send for up to bundleWindowMs, so put it on the wire before waiting.
|
|
866
|
+
this.subs.flushSends();
|
|
867
|
+
wait.start();
|
|
868
|
+
return wait.promise;
|
|
869
|
+
}
|
|
827
870
|
withSequence(input) {
|
|
828
871
|
return {
|
|
829
872
|
...input,
|
|
@@ -831,3 +874,11 @@ export class UdpAPI {
|
|
|
831
874
|
};
|
|
832
875
|
}
|
|
833
876
|
}
|
|
877
|
+
/** How a session's first message failed on a game server that did not hold it. */
|
|
878
|
+
function isFirstSendFailure(error, echoed) {
|
|
879
|
+
return (error instanceof CrowdyRealtimeError &&
|
|
880
|
+
(error.code === 'UNAUTHORIZED' || (echoed && isTimeout(error))));
|
|
881
|
+
}
|
|
882
|
+
function isTimeout(error) {
|
|
883
|
+
return error instanceof CrowdyRealtimeError && error.code === 'UDP_SEQUENCE_TIMEOUT';
|
|
884
|
+
}
|
|
@@ -8951,7 +8951,7 @@ export declare enum UdpErrorCode {
|
|
|
8951
8951
|
PasswordTooShort = "PASSWORD_TOO_SHORT",
|
|
8952
8952
|
/** The app-scoped gameplay token has expired. Refresh it (same app, via refreshAppToken) before it lapses, or re-portal through the Overworld for a fresh token, then re-authorize the realtime session. */
|
|
8953
8953
|
TokenExpired = "TOKEN_EXPIRED",
|
|
8954
|
-
/** The caller lacks the runtime/grid permission required for this action.
|
|
8954
|
+
/** The caller lacks the runtime/grid permission required for this action. A message sent right after connecting or entering a new region waits for that permission to load rather than being refused, so this means the permission is missing or could not be confirmed in time; after a move or a new session, one retry is enough to tell the two apart. */
|
|
8955
8955
|
Unauthorized = "UNAUTHORIZED",
|
|
8956
8956
|
/** Unspecified server error (1). Retry; if it persists, report it. */
|
|
8957
8957
|
UnknownError = "UNKNOWN_ERROR",
|