@indiegems/gem-web-sdk 20.0.0 → 21.0.0
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/CHANGELOG.md +31 -0
- package/brand/ticket-hero-128.png +0 -0
- package/brand/ticket-hero-256.png +0 -0
- package/brand/ticket-hero-384.png +0 -0
- package/brand/ticket-hero-512.png +0 -0
- package/brand/ticket-hero-768.png +0 -0
- package/brand/ticket-icon-20-1.5x.png +0 -0
- package/brand/ticket-icon-20-1x.png +0 -0
- package/brand/ticket-icon-20-2x.png +0 -0
- package/brand/ticket-icon-20-3x.png +0 -0
- package/brand/ticket-icon-32-1.5x.png +0 -0
- package/brand/ticket-icon-32-1x.png +0 -0
- package/brand/ticket-icon-32-2x.png +0 -0
- package/brand/ticket-icon-32-3x.png +0 -0
- package/brand/ticket-icon-ingame-20-1.5x.png +0 -0
- package/brand/ticket-icon-ingame-20-1x.png +0 -0
- package/brand/ticket-icon-ingame-20-2x.png +0 -0
- package/brand/ticket-icon-ingame-20-3x.png +0 -0
- package/brand/ticket-icon-ingame-32-1.5x.png +0 -0
- package/brand/ticket-icon-ingame-32-1x.png +0 -0
- package/brand/ticket-icon-ingame-32-2x.png +0 -0
- package/brand/ticket-icon-ingame-32-3x.png +0 -0
- package/brand/ticket-pile-1-128.png +0 -0
- package/brand/ticket-pile-1-256.png +0 -0
- package/brand/ticket-pile-1-384.png +0 -0
- package/brand/ticket-pile-1-512.png +0 -0
- package/brand/ticket-pile-1-768.png +0 -0
- package/brand/ticket-pile-2-128.png +0 -0
- package/brand/ticket-pile-2-256.png +0 -0
- package/brand/ticket-pile-2-384.png +0 -0
- package/brand/ticket-pile-2-512.png +0 -0
- package/brand/ticket-pile-2-768.png +0 -0
- package/dist/function.d.ts +1 -1
- package/dist/game.d.ts +52 -4
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +3 -3
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +57 -6
- package/dist/sdk.d.ts +52 -4
- package/docs/analytics.md +11 -0
- package/docs/api/api-reference-function.md +7 -2
- package/docs/api/api-reference-game.md +15 -9
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +8 -8
- package/docs/brand-art.md +65 -16
- package/docs/changelog.md +31 -0
- package/docs/dedicated-server-flow.md +30 -2
- package/docs/getting-started.md +3 -3
- package/docs/manifest.json +18 -18
- package/docs/match-session.md +9 -0
- package/docs/p2p-and-parties.md +79 -6
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +8 -5
- package/package.json +1 -1
- package/schema/bridge.v1.json +67 -2
- package/schema/capabilities.public.json +6 -0
package/dist/sdk.d.ts
CHANGED
|
@@ -1511,7 +1511,7 @@ declare class FunctionsApi {
|
|
|
1511
1511
|
}
|
|
1512
1512
|
type SessionEndReason = "grant_expiring" | "session_ended" | "navigating" | "kill_switch" | "pagehide";
|
|
1513
1513
|
type MatchTransport = "dedicated" | "p2p";
|
|
1514
|
-
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost";
|
|
1514
|
+
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost" | "gem_graphics_context_lost";
|
|
1515
1515
|
declare const PLATFORM_EVENTS: readonly PlatformEventName[];
|
|
1516
1516
|
type AnalyticsValue = string | number;
|
|
1517
1517
|
interface AnalyticsRejection$1 {
|
|
@@ -2254,6 +2254,15 @@ interface GemSignalEvent {
|
|
|
2254
2254
|
readonly matchId: string;
|
|
2255
2255
|
readonly data: unknown;
|
|
2256
2256
|
}
|
|
2257
|
+
interface GemRelayDraining {
|
|
2258
|
+
readonly gem: typeof PROTOCOL;
|
|
2259
|
+
readonly type: "gem.relayDraining";
|
|
2260
|
+
readonly matchId: string;
|
|
2261
|
+
readonly clusterTargetId: string;
|
|
2262
|
+
readonly relayIps: string[];
|
|
2263
|
+
readonly drainingSince: string;
|
|
2264
|
+
readonly deadline: string;
|
|
2265
|
+
}
|
|
2257
2266
|
interface GemDedicatedEvent {
|
|
2258
2267
|
readonly gem: typeof PROTOCOL;
|
|
2259
2268
|
readonly type: "gem.dedicatedEvent";
|
|
@@ -2570,6 +2579,7 @@ interface TelemetrySample {
|
|
|
2570
2579
|
readonly type: string;
|
|
2571
2580
|
readonly value?: number;
|
|
2572
2581
|
readonly transport?: TransportLabel;
|
|
2582
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
2573
2583
|
}
|
|
2574
2584
|
interface P2pApiOptions {
|
|
2575
2585
|
readonly client: GemClient;
|
|
@@ -2686,9 +2696,12 @@ interface IceCandidateLike {
|
|
|
2686
2696
|
readonly sdpMid?: string | null;
|
|
2687
2697
|
readonly sdpMLineIndex?: number | null;
|
|
2688
2698
|
}
|
|
2699
|
+
type EventListenerLike = {
|
|
2700
|
+
bivarianceHack(event: unknown): void;
|
|
2701
|
+
}["bivarianceHack"];
|
|
2689
2702
|
interface EventTargetLike {
|
|
2690
|
-
addEventListener(type: string, listener:
|
|
2691
|
-
removeEventListener(type: string, listener:
|
|
2703
|
+
addEventListener(type: string, listener: EventListenerLike): void;
|
|
2704
|
+
removeEventListener(type: string, listener: EventListenerLike): void;
|
|
2692
2705
|
}
|
|
2693
2706
|
interface DataChannelLike extends EventTargetLike {
|
|
2694
2707
|
readonly readyState: string;
|
|
@@ -2703,6 +2716,16 @@ interface DataChannelInitLike {
|
|
|
2703
2716
|
readonly negotiated?: boolean;
|
|
2704
2717
|
readonly id?: number;
|
|
2705
2718
|
}
|
|
2719
|
+
interface IceConfigurationLike {
|
|
2720
|
+
iceServers?: {
|
|
2721
|
+
urls: string | string[];
|
|
2722
|
+
username?: string;
|
|
2723
|
+
credential?: string;
|
|
2724
|
+
}[];
|
|
2725
|
+
}
|
|
2726
|
+
interface StatsReportLike {
|
|
2727
|
+
forEach(callback: (value: unknown, key: string) => void): void;
|
|
2728
|
+
}
|
|
2706
2729
|
interface PeerConnectionLike extends EventTargetLike {
|
|
2707
2730
|
readonly localDescription: SessionDescriptionLike | null;
|
|
2708
2731
|
readonly remoteDescription: SessionDescriptionLike | null;
|
|
@@ -2715,6 +2738,10 @@ interface PeerConnectionLike extends EventTargetLike {
|
|
|
2715
2738
|
setRemoteDescription(description: SessionDescriptionLike): Promise<void>;
|
|
2716
2739
|
addIceCandidate(candidate: IceCandidateLike): Promise<void>;
|
|
2717
2740
|
close(): void;
|
|
2741
|
+
setConfiguration?(configuration: IceConfigurationLike): void;
|
|
2742
|
+
getConfiguration?(): IceConfigurationLike;
|
|
2743
|
+
restartIce?(): void;
|
|
2744
|
+
getStats?(): Promise<StatsReportLike>;
|
|
2718
2745
|
}
|
|
2719
2746
|
interface PeerConnectionConfig {
|
|
2720
2747
|
readonly iceServers: readonly {
|
|
@@ -2749,6 +2776,7 @@ interface P2pSessionOptions {
|
|
|
2749
2776
|
readonly baseUrl: string;
|
|
2750
2777
|
readonly signaling: SignalingClient;
|
|
2751
2778
|
readonly clusterTargetId?: ClusterTargetLike;
|
|
2779
|
+
readonly clusterTargetIds?: readonly string[];
|
|
2752
2780
|
readonly peerConnectionFactory?: PeerConnectionFactory;
|
|
2753
2781
|
readonly certificate?: unknown;
|
|
2754
2782
|
readonly connectTimeoutMs?: number;
|
|
@@ -2765,7 +2793,11 @@ declare class P2pSession {
|
|
|
2765
2793
|
private readonly keys;
|
|
2766
2794
|
private localPlayerId;
|
|
2767
2795
|
private migration;
|
|
2768
|
-
private
|
|
2796
|
+
private lease;
|
|
2797
|
+
private clusterTargetIds;
|
|
2798
|
+
private leaseRenewal;
|
|
2799
|
+
private leaseAskedAt;
|
|
2800
|
+
private leaseRenewalWarned;
|
|
2769
2801
|
private credential;
|
|
2770
2802
|
private nextPeerId;
|
|
2771
2803
|
private ended;
|
|
@@ -2779,6 +2811,7 @@ declare class P2pSession {
|
|
|
2779
2811
|
private readonly answeredSessions;
|
|
2780
2812
|
private credentialGeneration;
|
|
2781
2813
|
private credentialDeadline;
|
|
2814
|
+
private readonly drainTimers;
|
|
2782
2815
|
constructor(options: P2pSessionOptions);
|
|
2783
2816
|
get matchId(): string;
|
|
2784
2817
|
get playerId(): string;
|
|
@@ -2804,6 +2837,7 @@ declare class P2pSession {
|
|
|
2804
2837
|
private fetchCredential;
|
|
2805
2838
|
private waitForCredential;
|
|
2806
2839
|
private onOffer;
|
|
2840
|
+
private answer;
|
|
2807
2841
|
private onAnswer;
|
|
2808
2842
|
private hostVerified;
|
|
2809
2843
|
private maybeAuthenticate;
|
|
@@ -2814,6 +2848,17 @@ declare class P2pSession {
|
|
|
2814
2848
|
private watchMigration;
|
|
2815
2849
|
private onMatchEnded;
|
|
2816
2850
|
private onSignalingError;
|
|
2851
|
+
private onRelayDraining;
|
|
2852
|
+
private onDrainDeadline;
|
|
2853
|
+
private withoutDrainingRelays;
|
|
2854
|
+
private restartIceToHost;
|
|
2855
|
+
private offerIceRestart;
|
|
2856
|
+
private progressRestart;
|
|
2857
|
+
private reportRestart;
|
|
2858
|
+
private takeLease;
|
|
2859
|
+
private refreshLease;
|
|
2860
|
+
private reconfigureLinks;
|
|
2861
|
+
private iceServers;
|
|
2817
2862
|
private openLink;
|
|
2818
2863
|
private flushCandidates;
|
|
2819
2864
|
private onReliableOpen;
|
|
@@ -2822,6 +2867,8 @@ declare class P2pSession {
|
|
|
2822
2867
|
private refuseLink;
|
|
2823
2868
|
private finishAdmission;
|
|
2824
2869
|
private onIceState;
|
|
2870
|
+
private recoverHostLink;
|
|
2871
|
+
private voteHostUnreachable;
|
|
2825
2872
|
private onConnectionState;
|
|
2826
2873
|
private expose;
|
|
2827
2874
|
private dropLink;
|
|
@@ -4726,6 +4773,7 @@ interface FrameCallbacks {
|
|
|
4726
4773
|
onTicketEvent?(msg: GemTicketEvent): void;
|
|
4727
4774
|
onSocketState?(msg: GemSocketState): void;
|
|
4728
4775
|
onSignalEvent?(msg: GemSignalEvent): void;
|
|
4776
|
+
onRelayDraining?(msg: GemRelayDraining): void;
|
|
4729
4777
|
onDedicatedEvent?(msg: GemDedicatedEvent): void;
|
|
4730
4778
|
onClusterTarget?(msg: GemClusterTarget): void;
|
|
4731
4779
|
onPartyResult?(msg: GemPartyResult): void;
|
package/docs/analytics.md
CHANGED
|
@@ -131,10 +131,21 @@ and a game that declares no events at all still gets them:
|
|
|
131
131
|
| `gem_boot_ready` | — | `boot_ms` |
|
|
132
132
|
| `gem_match_started` / `gem_match_ended` | `transport` (`dedicated`, `p2p`) | — |
|
|
133
133
|
| `gem_auth_lost` | — | — |
|
|
134
|
+
| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |
|
|
134
135
|
|
|
135
136
|
`reason` on a session end is one of `grant_expiring`, `session_ended`, `navigating`,
|
|
136
137
|
`kill_switch` or `pagehide`.
|
|
137
138
|
|
|
139
|
+
`gem_graphics_context_lost` is the browser taking one of your WebGL contexts away — on a phone
|
|
140
|
+
usually a GPU driver fault rather than your code. `gpu` is the GPU family (`powervr_d_series`,
|
|
141
|
+
`powervr_other`, `adreno`, `mali`, `xclipse`, `apple`, `nvidia`, `amd`, `intel`, `software`,
|
|
142
|
+
`other`, `unknown`), `route` is how the browser drives it (`angle_gles`, `angle_vulkan`,
|
|
143
|
+
`angle_d3d11`, `angle_d3d9`, `angle_metal`, `angle_opengl`, `angle_other`, `native_gl`, `webgpu`,
|
|
144
|
+
`unknown`) and `host` is `android_webview` (the Arcade app) or `other`. `hidden` is 1 when the page
|
|
145
|
+
was hidden at the time, which is a phone reclaiming a backgrounded game's memory rather than a
|
|
146
|
+
crash in front of the player. It is sent whether or not your game recovers from the loss, at most
|
|
147
|
+
five times a page.
|
|
148
|
+
|
|
138
149
|
**The `gem_` prefix is reserved.** Your config cannot declare an id beginning with it and
|
|
139
150
|
`gem.analytics.track` refuses one rather than sending it, so pick another name.
|
|
140
151
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The function surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/function`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/function` v21.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/function` exports exactly the symbols on this page and nothing else.
|
|
6
6
|
|
|
@@ -470,7 +470,7 @@ the reading a funnel should treat as approximate.
|
|
|
470
470
|
## PlatformEventName
|
|
471
471
|
|
|
472
472
|
```ts
|
|
473
|
-
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost"
|
|
473
|
+
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost" | "gem_graphics_context_lost"
|
|
474
474
|
```
|
|
475
475
|
|
|
476
476
|
Every event the SDK emits by itself.
|
|
@@ -488,6 +488,11 @@ config names them as steps.
|
|
|
488
488
|
| `gem_boot_ready` | — | `boot_ms` |
|
|
489
489
|
| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |
|
|
490
490
|
| `gem_auth_lost` | — | — |
|
|
491
|
+
| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |
|
|
492
|
+
|
|
493
|
+
`gem_graphics_context_lost` is the browser taking a WebGL context away —
|
|
494
|
+
usually a GPU driver fault, sized per device across every game
|
|
495
|
+
(`analytics/graphics.ts` lists the dimension values).
|
|
491
496
|
|
|
492
497
|
<a id="gemerrorreason"></a>
|
|
493
498
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The browser surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v21.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
|
@@ -4012,7 +4012,7 @@ How a match was hosted — the closed set `gem_match_started` and
|
|
|
4012
4012
|
## PlatformEventName
|
|
4013
4013
|
|
|
4014
4014
|
```ts
|
|
4015
|
-
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost"
|
|
4015
|
+
type PlatformEventName = "gem_session_start" | "gem_session_end" | "gem_pause" | "gem_resume" | "gem_boot_ready" | "gem_match_started" | "gem_match_ended" | "gem_auth_lost" | "gem_graphics_context_lost"
|
|
4016
4016
|
```
|
|
4017
4017
|
|
|
4018
4018
|
Every event the SDK emits by itself.
|
|
@@ -4030,6 +4030,11 @@ config names them as steps.
|
|
|
4030
4030
|
| `gem_boot_ready` | — | `boot_ms` |
|
|
4031
4031
|
| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](#matchtransport)) | — |
|
|
4032
4032
|
| `gem_auth_lost` | — | — |
|
|
4033
|
+
| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |
|
|
4034
|
+
|
|
4035
|
+
`gem_graphics_context_lost` is the browser taking a WebGL context away —
|
|
4036
|
+
usually a GPU driver fault, sized per device across every game
|
|
4037
|
+
(`analytics/graphics.ts` lists the dimension values).
|
|
4033
4038
|
|
|
4034
4039
|
<a id="platform_events"></a>
|
|
4035
4040
|
|
|
@@ -9504,8 +9509,9 @@ ARCADE_EMBEDDERS: readonly string[]
|
|
|
9504
9509
|
```
|
|
9505
9510
|
|
|
9506
9511
|
The origins the Gem Arcade embeds a game from, which every game accepts
|
|
9507
|
-
without naming them: the website
|
|
9508
|
-
|
|
9512
|
+
without naming them: the website and the Gem Arcade app. The list also
|
|
9513
|
+
holds `https://developer.gemarcade.com`, which is reserved and not live —
|
|
9514
|
+
no page is served there, so nothing frames a game from it.
|
|
9509
9515
|
|
|
9510
9516
|
You do not pass these. They are always allowed, and
|
|
9511
9517
|
[GemOptions.allowedEmbedders](#gemoptions-allowedembedders) adds to them rather than replacing them.
|
|
@@ -9534,8 +9540,8 @@ readonly allowedEmbedders?: readonly string[]
|
|
|
9534
9540
|
Extra origins allowed to embed your game, **added to** the ones the Gem
|
|
9535
9541
|
Arcade uses. Most games leave this out.
|
|
9536
9542
|
|
|
9537
|
-
The website
|
|
9538
|
-
|
|
9543
|
+
The website and the Gem Arcade app are always allowed — see
|
|
9544
|
+
[ARCADE_EMBEDDERS](#arcade_embedders) — so do not list them. Set this only when a site
|
|
9539
9545
|
of your own embeds your game.
|
|
9540
9546
|
|
|
9541
9547
|
```js
|
|
@@ -12593,9 +12599,9 @@ await gem.awaitBridge(); // before loading your main bundle
|
|
|
12593
12599
|
const me = await gem.awaitIdentity(); // who is playing, once sign-in completes
|
|
12594
12600
|
```
|
|
12595
12601
|
|
|
12596
|
-
No options are needed. The Gem Arcade's website
|
|
12597
|
-
|
|
12598
|
-
|
|
12602
|
+
No options are needed. The Gem Arcade's website and app are always allowed to
|
|
12603
|
+
embed your game ([ARCADE_EMBEDDERS](#arcade_embedders)), and the arcade tells your game
|
|
12604
|
+
which build it is.
|
|
12599
12605
|
|
|
12600
12606
|
A dev host on your own machine (`gemctl serve`, the Gem Editor) needs no
|
|
12601
12607
|
entry either: it frames a locally-served game from a loopback origin whose
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The server surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v21.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 2,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "21.0.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -4733,9 +4733,9 @@
|
|
|
4733
4733
|
"name": "PlatformEventName",
|
|
4734
4734
|
"anchor": "platformeventname",
|
|
4735
4735
|
"group": "analytics",
|
|
4736
|
-
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\"",
|
|
4736
|
+
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\" | \"gem_graphics_context_lost\"",
|
|
4737
4737
|
"documented": true,
|
|
4738
|
-
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-game.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](api-reference-game.md#matchtransport)) | — |\n| `gem_auth_lost` | — | — |",
|
|
4738
|
+
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-game.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](api-reference-game.md#matchtransport)) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away —\nusually a GPU driver fault, sized per device across every game\n(`analytics/graphics.ts` lists the dimension values).",
|
|
4739
4739
|
"deprecated": null,
|
|
4740
4740
|
"params": [],
|
|
4741
4741
|
"returns": null,
|
|
@@ -11139,7 +11139,7 @@
|
|
|
11139
11139
|
"group": "start",
|
|
11140
11140
|
"signature": "ARCADE_EMBEDDERS: readonly string[]",
|
|
11141
11141
|
"documented": true,
|
|
11142
|
-
"summary": "The origins the Gem Arcade embeds a game from, which every game accepts\nwithout naming them: the website
|
|
11142
|
+
"summary": "The origins the Gem Arcade embeds a game from, which every game accepts\nwithout naming them: the website and the Gem Arcade app. The list also\nholds `https://developer.gemarcade.com`, which is reserved and not live —\nno page is served there, so nothing frames a game from it.\n\nYou do not pass these. They are always allowed, and\n[GemOptions.allowedEmbedders](api-reference-game.md#gemoptions-allowedembedders) adds to them rather than replacing them.",
|
|
11143
11143
|
"deprecated": null,
|
|
11144
11144
|
"params": [],
|
|
11145
11145
|
"returns": null,
|
|
@@ -11171,7 +11171,7 @@
|
|
|
11171
11171
|
"group": "start",
|
|
11172
11172
|
"signature": "readonly allowedEmbedders?: readonly string[]",
|
|
11173
11173
|
"documented": true,
|
|
11174
|
-
"summary": "Extra origins allowed to embed your game, **added to** the ones the Gem\nArcade uses. Most games leave this out.\n\nThe website
|
|
11174
|
+
"summary": "Extra origins allowed to embed your game, **added to** the ones the Gem\nArcade uses. Most games leave this out.\n\nThe website and the Gem Arcade app are always allowed — see\n[ARCADE_EMBEDDERS](api-reference-game.md#arcade_embedders) — so do not list them. Set this only when a site\nof your own embeds your game.\n\n```js\ncreateGem({ allowedEmbedders: ['https://my-own-site.example'] })\n```\n\nA page whose origin is not allowed gets no connection, and — deliberately —\nis told nothing about why. That means a typo here looks exactly like a\nhandshake that has not arrived: pass [GemOptions.onRefused](api-reference-game.md#gemoptions-onrefused) while you\nare integrating, because it is the only place the difference shows.\n\n⚠️ **Bake these in. Never build them from anything the page can\ninfluence** — not `location`, not a query parameter, not\n`document.referrer`. A value the embedder supplies is a check that can never\nfail. Do not add a `localhost:<port>` entry for your dev host either; the\nSDK admits one on your own machine by rule.",
|
|
11175
11175
|
"deprecated": null,
|
|
11176
11176
|
"params": [],
|
|
11177
11177
|
"returns": null,
|
|
@@ -13974,7 +13974,7 @@
|
|
|
13974
13974
|
"group": "start",
|
|
13975
13975
|
"signature": "function createGem(options?: GemOptions): Gem",
|
|
13976
13976
|
"documented": true,
|
|
13977
|
-
"summary": "Connect your game to the Gem Arcade. This is the one call every game makes.\n\nIt assembles everything and connects to the arcade that framed your game. The\nsame code runs on your machine and in production without a flag — but it needs\na frame either way. Opened at the top level it throws\n[GemUnframedError](api-reference-game.md#gemunframederror), which says how to run it: in the Arcade, the Editor,\nor the Gem dev host on your machine (`npm run dev`, signed in to `gemctl`).\n\nOne dependency, imported like anything else. The install is in\n[getting-started](../getting-started.md#3-add-the-sdk).\n\n```js\nimport { createGem } from '@indiegems/gem-web-sdk/sdk';\n\nconst gem = createGem();\n\nawait gem.awaitBridge(); // before loading your main bundle\nconst me = await gem.awaitIdentity(); // who is playing, once sign-in completes\n```\n\nNo options are needed. The Gem Arcade's website
|
|
13977
|
+
"summary": "Connect your game to the Gem Arcade. This is the one call every game makes.\n\nIt assembles everything and connects to the arcade that framed your game. The\nsame code runs on your machine and in production without a flag — but it needs\na frame either way. Opened at the top level it throws\n[GemUnframedError](api-reference-game.md#gemunframederror), which says how to run it: in the Arcade, the Editor,\nor the Gem dev host on your machine (`npm run dev`, signed in to `gemctl`).\n\nOne dependency, imported like anything else. The install is in\n[getting-started](../getting-started.md#3-add-the-sdk).\n\n```js\nimport { createGem } from '@indiegems/gem-web-sdk/sdk';\n\nconst gem = createGem();\n\nawait gem.awaitBridge(); // before loading your main bundle\nconst me = await gem.awaitIdentity(); // who is playing, once sign-in completes\n```\n\nNo options are needed. The Gem Arcade's website and app are always allowed to\nembed your game ([ARCADE_EMBEDDERS](api-reference-game.md#arcade_embedders)), and the arcade tells your game\nwhich build it is.\n\nA dev host on your own machine (`gemctl serve`, the Gem Editor) needs no\nentry either: it frames a locally-served game from a loopback origin whose\nport changes every run, and the SDK admits it by rule — only while the game\nitself is served from loopback, so nothing changes once you publish.\n\nPass `allowedEmbedders` only when a site of your own frames your game. It\nadds to the arcade's origins; see [GemOptions.allowedEmbedders](api-reference-game.md#gemoptions-allowedembedders).",
|
|
13978
13978
|
"deprecated": null,
|
|
13979
13979
|
"params": [
|
|
13980
13980
|
{
|
|
@@ -30371,9 +30371,9 @@
|
|
|
30371
30371
|
"name": "PlatformEventName",
|
|
30372
30372
|
"anchor": "platformeventname",
|
|
30373
30373
|
"group": "analytics",
|
|
30374
|
-
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\"",
|
|
30374
|
+
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\" | \"gem_graphics_context_lost\"",
|
|
30375
30375
|
"documented": true,
|
|
30376
|
-
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-function.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |\n| `gem_auth_lost` | — | — |",
|
|
30376
|
+
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-function.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away —\nusually a GPU driver fault, sized per device across every game\n(`analytics/graphics.ts` lists the dimension values).",
|
|
30377
30377
|
"deprecated": null,
|
|
30378
30378
|
"params": [],
|
|
30379
30379
|
"returns": null,
|
package/docs/brand-art.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The SDK ships the platform's own art for its two currencies, **Gems** and **Coins**, and for
|
|
4
4
|
**trophies**. You need it only if your game draws Gems, Coins or a trophy on its own screens. Read
|
|
5
|
-
§1 before you pick a file: each currency has
|
|
6
|
-
the screen is for.
|
|
5
|
+
§1 before you pick a file: each currency has more than one kind of art, and which one you use
|
|
6
|
+
depends on what the screen is for.
|
|
7
7
|
|
|
8
8
|
> **Draw the platform's current marks with `gem.branding`, and keep these files as the fallback.**
|
|
9
9
|
> `gem.branding.currencyMark('GEMS', { size: 20 })` and `gem.branding.trophy()` hand you the same
|
|
@@ -19,25 +19,35 @@ screen, and how much more your game can still pay (the earn allowance) — and n
|
|
|
19
19
|
own. §5 is the creator's ruling and is not optional.
|
|
20
20
|
|
|
21
21
|
> **The Coins art is a 3D render, open to a designer pass.** It is a gold coin with the platform's
|
|
22
|
-
> gem
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
> gem at its centre, so a player reads it as the Arcade's currency in every game rather than one
|
|
23
|
+
> game's gold. The hero and the two piles (§3) are one coin — painted gold, a thick rounded edge, a
|
|
24
|
+
> faceted gem standing on its face; the hero's also has a thin groove inside its rim and its gem
|
|
25
|
+
> raised on a sloped base — and the icons keep the earlier gold coin, whose rings and gem channel
|
|
26
|
+
> read better in a few pixels. Every coin is turned so its edge shows on the right. This
|
|
27
|
+
> art replaced the gold coin of 2026-10-25, which replaced the tangerine coin of 2026-10-21. The
|
|
28
|
+
> files keep their `ticket-*` names, as the currency keeps its slug `ARCADE_TICKETS`. If the art is
|
|
29
|
+
> redrawn again, the new art replaces these files **under the same names** in a later SDK release,
|
|
30
|
+
> so a game that imports them picks it up on its next SDK update. Lay your screens out from the
|
|
31
|
+
> sizes on this page, not from the drawing.
|
|
32
|
+
|
|
33
|
+
## 1. Pick the currency art by the role of the screen
|
|
30
34
|
|
|
31
35
|
| You are drawing | Use | Drawn at |
|
|
32
36
|
| --------------- | --- | -------- |
|
|
33
37
|
| Gems **inline**, beside a number: a price, a "+15" reward label, a small chip | the **icon**, `gem-icon-20-*` or `gem-icon-32-*` | 16–40 CSS px |
|
|
34
38
|
| Gems as **the subject of a reward presentation**: a victory or reward screen where the gem is what the player looks at, such as a victory overlay with a sunburst behind the reward | the **hero**, `gem-hero-*` | 64–256 CSS px |
|
|
35
|
-
| Coins **inline
|
|
39
|
+
| Coins **inline** in your game's screens, beside a number | the **in-game icon**, `ticket-icon-ingame-20-*` or `ticket-icon-ingame-32-*` | 16–40 CSS px |
|
|
40
|
+
| Coins as **a reward sized by how much it is**: a reward card, a results screen's payout | a **pile**, `ticket-pile-1-*` or `ticket-pile-2-*` (§3) | 64–256 CSS px |
|
|
36
41
|
| Coins as **the subject of a reward presentation** | the **hero**, `ticket-hero-*` | 64–256 CSS px |
|
|
37
42
|
|
|
38
43
|
The role decides first and the size follows from it. A price tag is inline even if you make it
|
|
39
44
|
large; a reward screen is a presentation even on a small phone.
|
|
40
45
|
|
|
46
|
+
`ticket-icon-20-*` and `ticket-icon-32-*` are the **arcade's wallet icon**: the same coin inside the
|
|
47
|
+
violet rim the Gems icon wears, which the arcade draws in its own wallet. Inside your game that rim
|
|
48
|
+
reads as the arcade's chrome, so draw the in-game icon there; a game that already draws the wallet
|
|
49
|
+
icon keeps working, and moves to the in-game one when it next touches the screen.
|
|
50
|
+
|
|
41
51
|
- **Never use the hero at icon sizes.** It is one render drawn to be seen large; shrunk to 20 px its
|
|
42
52
|
outline goes under a pixel, its facet lines vanish and the gem smudges into the background.
|
|
43
53
|
- **Never use the icon at hero sizes.** Each icon file is a separate render drawn for one small box,
|
|
@@ -114,12 +124,30 @@ outline weight — 1.5 px at 20, 2 px at 32.
|
|
|
114
124
|
| | | `ticket-icon-32-2x.png` | 64 × 64 |
|
|
115
125
|
| | | `ticket-icon-32-3x.png` | 96 × 96 |
|
|
116
126
|
|
|
117
|
-
Like the Gems icon, each size is **its own render
|
|
118
|
-
|
|
119
|
-
groove inside the rim, a crease above the edge, a channel round the gem —
|
|
127
|
+
Like the Gems icon, each size is **its own render**, not the hero scaled down: the earlier gold
|
|
128
|
+
coin, turned a little so its edge shows on the right, with the lines that part its rim, field and
|
|
129
|
+
gem — a groove inside the rim, a crease above the edge, a channel round the gem — drawn wider and
|
|
120
130
|
darker the smaller the size, so they still read in a few pixels. The 20 px coin has a slightly
|
|
121
131
|
smaller gem and no ridges on its edge. Each is drawn on its own pixel grid inside the gem icon's
|
|
122
|
-
violet outline.
|
|
132
|
+
violet outline. These are the **wallet icon**: the arcade's wallet draws the same files.
|
|
133
|
+
|
|
134
|
+
### The in-game Coins icon
|
|
135
|
+
|
|
136
|
+
The same coin for **your game's own screens**, without the violet rim. A burnt-orange edge runs
|
|
137
|
+
round its light right-hand side instead, so it stays distinct on a pale background, and its edge
|
|
138
|
+
band is smooth. It comes in the same renditions and ratios, framed exactly as the wallet icon, so
|
|
139
|
+
one swaps for the other in the same box.
|
|
140
|
+
|
|
141
|
+
| Rendition | For a CSS box of | File | Pixels |
|
|
142
|
+
| --------- | ---------------- | ---- | ------ |
|
|
143
|
+
| 20 | up to about 24 px | `ticket-icon-ingame-20-1x.png` | 20 × 20 |
|
|
144
|
+
| | | `ticket-icon-ingame-20-1.5x.png` | 30 × 30 |
|
|
145
|
+
| | | `ticket-icon-ingame-20-2x.png` | 40 × 40 |
|
|
146
|
+
| | | `ticket-icon-ingame-20-3x.png` | 60 × 60 |
|
|
147
|
+
| 32 | above 24, up to about 40 px | `ticket-icon-ingame-32-1x.png` | 32 × 32 |
|
|
148
|
+
| | | `ticket-icon-ingame-32-1.5x.png` | 48 × 48 |
|
|
149
|
+
| | | `ticket-icon-ingame-32-2x.png` | 64 × 64 |
|
|
150
|
+
| | | `ticket-icon-ingame-32-3x.png` | 96 × 96 |
|
|
123
151
|
|
|
124
152
|
## 3. The hero art
|
|
125
153
|
|
|
@@ -147,10 +175,31 @@ size you draw it multiplied by the screen's `devicePixelRatio`:
|
|
|
147
175
|
**Do not draw it larger than 256 CSS pixels.** On a 3× screen that needs more than the largest file,
|
|
148
176
|
and the art softens as it is stretched. Below 64 px, use the icon (§2).
|
|
149
177
|
|
|
178
|
+
### The coin piles
|
|
179
|
+
|
|
180
|
+
Two heaps of the hero's coin, for a Coins reward your game **offers or has just paid** where the
|
|
181
|
+
size of the heap says how much: a reward card, or a results screen's payout. Pile 1 is a few short
|
|
182
|
+
stacks; pile 2 holds about twice the coins.
|
|
183
|
+
|
|
184
|
+
| Pile | Use it for | Files |
|
|
185
|
+
| ---- | ---------- | ----- |
|
|
186
|
+
| 1 | an everyday reward | `ticket-pile-1-128.png`, `ticket-pile-1-256.png`, `ticket-pile-1-384.png`, `ticket-pile-1-512.png`, `ticket-pile-1-768.png` |
|
|
187
|
+
| 2 | a bigger reward | `ticket-pile-2-128.png`, `ticket-pile-2-256.png`, `ticket-pile-2-384.png`, `ticket-pile-2-512.png`, `ticket-pile-2-768.png` |
|
|
188
|
+
|
|
189
|
+
**Pick the pile by where the reward sits among your game's own rewards, not by a number of Coins.**
|
|
190
|
+
Where a reward's size doesn't matter, the hero does as well.
|
|
191
|
+
|
|
192
|
+
Both piles share one frame: a square, transparent PNG with a margin all round, each heap centred
|
|
193
|
+
across and standing on the same baseline. Pile 2 fills the frame and pile 1 is drawn smaller, so the
|
|
194
|
+
two side by side climb from left to right with no scaling in your layout. Draw them at the hero's
|
|
195
|
+
sizes, **64 to 256 CSS pixels**, and pick the file as for the hero above.
|
|
196
|
+
|
|
150
197
|
## 4. Loading and drawing the art
|
|
151
198
|
|
|
152
|
-
The platform serves
|
|
199
|
+
The platform serves the icons and the heroes through `gem.branding` ([branding.md](branding.md)),
|
|
153
200
|
which is what to draw first. The copies below are the fallback for when it answers `unavailable`.
|
|
201
|
+
The in-game Coins icon and the coin piles are not in the platform's manifest yet: draw them from
|
|
202
|
+
here.
|
|
154
203
|
|
|
155
204
|
All of it is in the package you already install, under `brand/`, and published beside it on the CDN —
|
|
156
205
|
the same bytes either way, for the version you are on.
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,37 @@ Its entries are grouped under up to five headings:
|
|
|
16
16
|
- **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
|
|
17
17
|
- **New capability**: something a game can now do. Nothing changes unless you use it.
|
|
18
18
|
|
|
19
|
+
## 21.0.0
|
|
20
|
+
|
|
21
|
+
Upgrading from 20.1.0. Impact: required.
|
|
22
|
+
|
|
23
|
+
### May stop
|
|
24
|
+
|
|
25
|
+
- You do not need to write your own code to count lost WebGL contexts for analytics. Every game now reports each loss automatically, with no code of its own. The SDK only observes the loss, so whether your game restores its context stays your decision, and your own handling keeps working unchanged.
|
|
26
|
+
|
|
27
|
+
### Rebuild
|
|
28
|
+
|
|
29
|
+
- The set of platform event names now includes a new member, gem_graphics_context_lost, which the SDK emits when the browser takes away one of the game's WebGL contexts. Recompiling is enough for most games. If your game switches over every platform event name exhaustively, or maps each name to a value, add a case for the new name so it still compiles.
|
|
30
|
+
|
|
31
|
+
### New capability
|
|
32
|
+
|
|
33
|
+
- A new in-game Coins icon is available in two sizes, 20 and 32, each at four pixel ratios: 1x, 1.5x, 2x and 3x. It is the Coins coin without the violet rim, so it does not look like the Arcade's own chrome inside a game. Use it wherever your game shows Coins. The wallet icon keeps its violet rim.
|
|
34
|
+
- Two coin piles are now available for showing a Coins reward sized by how much it is: pile 1 for an everyday reward and pile 2 for a bigger one. Each comes in five widths: 128, 256, 384, 512 and 768. They share one frame and baseline, so a row of them climbs from the smaller to the larger. Larger piles are reserved for the Arcade's own shop. The Coins hero art remains the choice when the size of the reward does not matter.
|
|
35
|
+
|
|
36
|
+
## 20.1.0
|
|
37
|
+
|
|
38
|
+
Upgrading from 20.0.0. Impact: required.
|
|
39
|
+
|
|
40
|
+
### Rebuild
|
|
41
|
+
|
|
42
|
+
- The list of capability names a game can advertise gained one value, p2pRelayDrain. Recompile. If the game switches exhaustively over capability names, add a case for it. Otherwise nothing else changes.
|
|
43
|
+
- The list of messages the Arcade page sends to the game frame gained one entry, gem.relayDraining, and the parent message type gained the matching member. Recompile. If the game handles every parent message in an exhaustive switch, add a case for the new one. Otherwise nothing else changes.
|
|
44
|
+
|
|
45
|
+
### New capability
|
|
46
|
+
|
|
47
|
+
- A game can now be told when a relay carrying its peer-to-peer match is draining and by when. Pass onRelayDraining in the frame options, or implement it on the frame callbacks, to receive the notice. The SDK already moves the connection off the draining relay by itself, so use this only to inform the player or log it. The notice is sent only by an Arcade page that supports it.
|
|
48
|
+
- Telemetry samples can now carry labels, so a game that reads or forwards telemetry samples can see per-sample label values such as outcome and reason.
|
|
49
|
+
|
|
19
50
|
## 20.0.0
|
|
20
51
|
|
|
21
52
|
Upgrading from 19.0.2. Impact: required.
|
|
@@ -715,11 +715,39 @@ host does, and the SDK does it for you:
|
|
|
715
715
|
|
|
716
716
|
| What gem-api answers | Where | What the SDK does |
|
|
717
717
|
| --- | --- | --- |
|
|
718
|
-
| `invalid_cluster_target` | `create()
|
|
718
|
+
| `invalid_cluster_target` | `create()` | Creates in the next cluster the page sent, at once, until every one has been refused; then waits up to five seconds for the page to send a different list, and tries once more with a cluster it has not yet tried |
|
|
719
|
+
| `invalid_cluster_target` | `quickJoin()` | Waits up to five seconds for the page to send a different list, and tries once more with it |
|
|
719
720
|
| `invalid_cluster_target` | `startMatch()` — the platform does not run the room's cluster | Starts once in this player's best other cluster, naming it, which moves the room |
|
|
720
721
|
| `capacity_unavailable` twice in a row | `startMatch()` | Moves to the next acceptable cluster, once, and keeps waiting there |
|
|
721
722
|
|
|
722
|
-
**Each
|
|
723
|
+
**Each wait and each move happens once.** `create()` trying each cluster the page sent counts as one
|
|
724
|
+
step, and it tries three at most, because the page sends three at most. A refusal after that is the
|
|
725
|
+
answer, and it reaches you as the error.
|
|
726
|
+
|
|
727
|
+
### When the platform hides a cluster
|
|
728
|
+
|
|
729
|
+
An operator can **hide** a cluster to take it out of service without stopping it: it leaves the list
|
|
730
|
+
the page measures, it takes no new rooms, no new matches and no new TURN relay leases, and the
|
|
731
|
+
matches already running on it carry on. A page that measured before the hide still lists it until
|
|
732
|
+
it measures again, so the SDK is what moves that player on:
|
|
733
|
+
|
|
734
|
+
- **`create()`** is refused with `invalid_cluster_target` for the hidden cluster and creates in the
|
|
735
|
+
next one the page sent, with no wait.
|
|
736
|
+
- **`quickJoin()` and matchmaking** need nothing: the platform drops the hidden cluster from the list
|
|
737
|
+
they send and serves the rest.
|
|
738
|
+
- **`startMatch()`** in a room on the hidden cluster is refused the same way and starts once in the
|
|
739
|
+
host's best other cluster, which moves the room — as for any cluster the platform stopped running.
|
|
740
|
+
- **A P2P relay lease** is answered from another cluster in the same region when there is one, and
|
|
741
|
+
otherwise refused; the SDK then leases from the next cluster the page sent. See
|
|
742
|
+
`p2p-and-parties.md` §4.
|
|
743
|
+
|
|
744
|
+
An earlier SDK version reaches the same place more slowly or not at all. Its `create()` names only the
|
|
745
|
+
best cluster, waits up to five seconds for a list the page will not send, and then gives you the
|
|
746
|
+
refusal, so a game that creates rooms shows an error until the page measures again. Its P2P lease asks
|
|
747
|
+
the hidden cluster once and, with no other cluster in that region to answer for it, connects without
|
|
748
|
+
a relay. Its `startMatch()` and matchmaking behave as above, and so does its `quickJoin()` from
|
|
749
|
+
5.11.0 on, when the SDK began sending the page's whole list; before that a quick-join names one
|
|
750
|
+
cluster, and one naming only the hidden cluster is refused.
|
|
723
751
|
|
|
724
752
|
**An automatic start has nobody to answer, so the host's SDK answers it.** When a room's template
|
|
725
753
|
starts the match as it fills to `min_players` and the platform refuses that start, no call is waiting
|
package/docs/getting-started.md
CHANGED
|
@@ -50,7 +50,7 @@ either way.
|
|
|
50
50
|
```jsonc
|
|
51
51
|
// package.json
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@indiegems/gem-web-sdk": "
|
|
53
|
+
"@indiegems/gem-web-sdk": "21.0.0"
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
@@ -137,8 +137,8 @@ const me = await gem.awaitIdentity();
|
|
|
137
137
|
|
|
138
138
|
Two things about that call are worth getting right the first time.
|
|
139
139
|
|
|
140
|
-
**You do not list who may embed your game.** The Gem Arcade's website
|
|
141
|
-
|
|
140
|
+
**You do not list who may embed your game.** The Gem Arcade's website and app are always
|
|
141
|
+
allowed, and so is a dev host on your own machine (`gemctl serve`, the Gem Editor) while your
|
|
142
142
|
game is served from loopback. `allowedEmbedders` exists only for a page beyond those — see
|
|
143
143
|
[the embedder allowlist](what-a-game-must-do.md#1-the-embedder-allowlist) — and it adds to the
|
|
144
144
|
arcade's origins rather than replacing them.
|