@indiegems/gem-web-sdk 9.1.4 → 10.1.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/dist/sdk.d.ts CHANGED
@@ -157,6 +157,11 @@ type AffixSelection = {
157
157
  "pool": AffixPoolRef;
158
158
  "respect_groups"?: boolean;
159
159
  };
160
+ type BackfillPolicy = "fill_existing_first" | "prefer_new_match";
161
+ type BackfillSettings = {
162
+ "enabled": boolean;
163
+ "policy"?: BackfillPolicy;
164
+ };
160
165
  type Boundary = {
161
166
  "boundary"?: string;
162
167
  "reset_time"?: string;
@@ -230,11 +235,20 @@ type LevelDefinition = {
230
235
  "rewards"?: RecipeOperation[];
231
236
  "threshold": number;
232
237
  };
238
+ type MatchmakingSettings = {
239
+ "backfill"?: null | BackfillSettings;
240
+ "cluster_stagger_seconds"?: number;
241
+ "enabled": boolean;
242
+ "fill_window_seconds"?: number;
243
+ "strategy"?: MatchmakingStrategy;
244
+ "ticket_expiry_seconds"?: number | null;
245
+ };
246
+ type MatchmakingStrategy = "fifo";
233
247
  type OrderBySpec = {
234
248
  "column": RunValueColumn;
235
249
  "direction": SortDirection;
236
250
  };
237
- type OrderSource$1 = "real_money_purchase" | "real_money_refund" | "full_game_purchase" | "full_game_refund" | "platform_purchase" | "reward" | "admin" | "system" | "promo" | "game_consume" | "play" | "recipe" | "transfer" | "platform_funded_game_inventory_purchase" | "earning_reset";
251
+ type OrderSource$1 = "real_money_purchase" | "real_money_refund" | "full_game_purchase" | "full_game_refund" | "platform_purchase" | "reward" | "admin" | "system" | "promo" | "game_consume" | "play" | "recipe" | "transfer" | "platform_funded_game_inventory_purchase" | "earning_reset" | "test_account_provision" | "developer_grant";
238
252
  type OverflowBehavior$1 = "fail" | "clamp" | "allow";
239
253
  type PityEffect = "guarantee_tier" | {
240
254
  "weight_boost": {
@@ -452,6 +466,7 @@ type SnapshotGameRoomTemplate = {
452
466
  "id": string;
453
467
  "idle_timeout_seconds"?: number | null;
454
468
  "match_type": string;
469
+ "matchmaking"?: null | MatchmakingSettings;
455
470
  "max_match_duration_seconds"?: number | null;
456
471
  "max_players": number;
457
472
  "min_players": number;
@@ -523,6 +538,7 @@ type SnapshotProgression = {
523
538
  "display_name"?: string | null;
524
539
  "icon_url"?: string | null;
525
540
  "id": string;
541
+ "localized"?: Record<string, unknown>;
526
542
  "progression_type": string;
527
543
  "slug"?: string | null;
528
544
  "sort_order": number;
@@ -560,6 +576,7 @@ type SnapshotRewardedPlacement = {
560
576
  "grants": SnapshotRewardGrant[];
561
577
  "max_held"?: number | null;
562
578
  "max_per_day"?: number | null;
579
+ "min_watch_secs"?: number | null;
563
580
  "placement": string;
564
581
  "reward_class": string;
565
582
  "without_ads": string;
@@ -1524,26 +1541,6 @@ interface ClusterTarget {
1524
1541
  readonly id: string;
1525
1542
  readonly pressure: string;
1526
1543
  }
1527
- type RoomArgScalar = string | number | boolean;
1528
- type RoomArgValue$1 = RoomArgScalar | readonly RoomArgScalar[];
1529
- type RoomArgs = Readonly<Record<string, RoomArgValue$1>>;
1530
- type RoomArgType$1 = "string" | "int" | "float" | "bool" | (string & {});
1531
- interface RoomArgEntry {
1532
- readonly name: string;
1533
- readonly type: RoomArgType$1;
1534
- readonly list: boolean;
1535
- readonly required: boolean;
1536
- readonly mutable: boolean;
1537
- readonly fixed?: RoomArgValue$1;
1538
- readonly default?: RoomArgValue$1;
1539
- readonly description?: string;
1540
- readonly allowed?: readonly RoomArgScalar[];
1541
- readonly min?: number;
1542
- readonly max?: number;
1543
- readonly minItems?: number;
1544
- readonly maxItems?: number;
1545
- readonly maxLength?: number;
1546
- }
1547
1544
  type DedicatedServerFailure = "timeout" | "aborted" | "allocation_failed" | "no_server_ready" | "start_rate_limited" | "p2p_unavailable" | "unsupported_protocol" | "credential_expired" | "credential_reused" | "join_rate_limited" | "signaling_unavailable" | "signaling_failed" | "offer_refused" | "invalid_sdp" | "media_refused" | "connection_failed" | "join_refused" | "match_ended" | "misconfigured";
1548
1545
  declare class DedicatedServerError extends Error {
1549
1546
  readonly reason: DedicatedServerFailure;
@@ -1652,6 +1649,26 @@ interface TransportAdvert {
1652
1649
  declare function readTemplateTransports(value: unknown): readonly TransportKind[] | null;
1653
1650
  declare function readTransportAdvert(settings: unknown, forMatch?: string): TransportAdvert | null;
1654
1651
  declare function webTransportAvailable(): boolean;
1652
+ type RoomArgScalar = string | number | boolean;
1653
+ type RoomArgValue$1 = RoomArgScalar | readonly RoomArgScalar[];
1654
+ type RoomArgs = Readonly<Record<string, RoomArgValue$1>>;
1655
+ type RoomArgType$1 = "string" | "int" | "float" | "bool" | (string & {});
1656
+ interface RoomArgEntry {
1657
+ readonly name: string;
1658
+ readonly type: RoomArgType$1;
1659
+ readonly list: boolean;
1660
+ readonly required: boolean;
1661
+ readonly mutable: boolean;
1662
+ readonly fixed?: RoomArgValue$1;
1663
+ readonly default?: RoomArgValue$1;
1664
+ readonly description?: string;
1665
+ readonly allowed?: readonly RoomArgScalar[];
1666
+ readonly min?: number;
1667
+ readonly max?: number;
1668
+ readonly minItems?: number;
1669
+ readonly maxItems?: number;
1670
+ readonly maxLength?: number;
1671
+ }
1655
1672
  type RoomStatus = "open" | "in_game" | "closed" | (string & {});
1656
1673
  type MatchConnectionState = "disconnected" | "connecting" | "connected" | "suspended";
1657
1674
  type MatchSessionPhase = "none" | "starting" | "ready" | "connected" | "suspended" | "ended" | (string & {});
@@ -1749,6 +1766,38 @@ interface RoomOperationError {
1749
1766
  readonly message: string;
1750
1767
  readonly retryable: boolean;
1751
1768
  }
1769
+ interface QueueForMatchOptions {
1770
+ readonly templateSlugs: readonly string[];
1771
+ readonly partyId?: string;
1772
+ readonly signal?: AbortSignal;
1773
+ }
1774
+ type MatchTicketStatus = "queued" | "matched" | "expired" | "cancelled" | "failed" | "lost" | "unavailable";
1775
+ type MatchTicketOutcome = {
1776
+ readonly status: "matched";
1777
+ readonly room: Room;
1778
+ } | {
1779
+ readonly status: "expired";
1780
+ readonly reason: string;
1781
+ } | {
1782
+ readonly status: "cancelled";
1783
+ readonly reason: string;
1784
+ } | {
1785
+ readonly status: "failed";
1786
+ readonly reason: string;
1787
+ } | {
1788
+ readonly status: "lost";
1789
+ } | {
1790
+ readonly status: "unavailable";
1791
+ };
1792
+ interface MatchTicket {
1793
+ readonly id: string;
1794
+ readonly status: MatchTicketStatus;
1795
+ readonly partySize: number;
1796
+ readonly queuedAt: number;
1797
+ readonly expiresAt: number;
1798
+ readonly outcome: Promise<MatchTicketOutcome>;
1799
+ cancel(): Promise<MatchTicketOutcome>;
1800
+ }
1752
1801
  interface CreateRoomOptions {
1753
1802
  readonly name: string;
1754
1803
  readonly maxPlayers?: number;
@@ -1891,6 +1940,7 @@ interface RoomManager {
1891
1940
  join(roomId: string, options?: JoinRoomOptions): Promise<Room>;
1892
1941
  joinByCode(code: string, options?: JoinRoomOptions): Promise<Room>;
1893
1942
  quickJoin(options: QuickJoinOptions): Promise<Room>;
1943
+ queueForMatch(options: QueueForMatchOptions): Promise<MatchTicket>;
1894
1944
  list(options?: ListRoomsOptions): Promise<RoomPage>;
1895
1945
  myRooms(signal?: AbortSignal): Promise<readonly RoomSummary[]>;
1896
1946
  leave(options?: {
@@ -1916,6 +1966,7 @@ interface RoomManager {
1916
1966
  }): Promise<void>;
1917
1967
  readonly gameId: string | null;
1918
1968
  readonly currentRoom: Room | null;
1969
+ readonly currentTicket: MatchTicket | null;
1919
1970
  readonly currentMatch: ReadyMatch | null;
1920
1971
  readonly matchConnectionState: MatchConnectionState;
1921
1972
  readonly matchSession: MatchSession;
@@ -1973,6 +2024,13 @@ interface GemPrefs$1 {
1973
2024
  readonly muted: boolean;
1974
2025
  readonly showFrameStats?: boolean;
1975
2026
  readonly locale: string;
2027
+ readonly recommended?: {
2028
+ volume: number;
2029
+ batterySaver: boolean;
2030
+ keyboardLayout: "wasd" | "arrows";
2031
+ gamepadConfirm: "south" | "east";
2032
+ vibration: boolean;
2033
+ };
1976
2034
  }
1977
2035
  interface GemCohorts$1 {
1978
2036
  readonly gem: typeof PROTOCOL;
@@ -4385,6 +4443,14 @@ interface GemPrefs {
4385
4443
  readonly muted: boolean;
4386
4444
  readonly showFrameStats: boolean;
4387
4445
  readonly locale: string;
4446
+ readonly recommended: GemRecommendedSettings | null;
4447
+ }
4448
+ interface GemRecommendedSettings {
4449
+ readonly volume: number;
4450
+ readonly batterySaver: boolean;
4451
+ readonly keyboardLayout: "wasd" | "arrows";
4452
+ readonly gamepadConfirm: "south" | "east";
4453
+ readonly vibration: boolean;
4388
4454
  }
4389
4455
  interface GemCohort {
4390
4456
  readonly experimentId: string;
@@ -4487,4 +4553,4 @@ interface ExitAnalytics {
4487
4553
  }
4488
4554
  declare function leaveAndExit(rooms: ExitRooms, bridge: ExitBridge | null, reason?: string, analytics?: ExitAnalytics): Promise<void>;
4489
4555
  declare function createGem(options?: GemOptions): Gem;
4490
- export { ARCADE_EMBEDDERS, type AccountApi, type AdFormat, type AdOutcome, type AdOutcomeKind, type AdRequest, type AdsApi, AnalyticsApi, type AnalyticsFlushOptions, type AnalyticsFlushResult, type AnalyticsOptions, type AnalyticsRejection$1 as AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Base64String, type BootMark, type BootTimeline, type BroadcastOptions, type BytesResponse, CONDITIONAL_HEADERS, type Challenge, type ChallengeWindow, ChallengesApi, type ClientOptions, type Clock, type ConditionalRequestHeaders, type ConfiguredPurchasePayment, type ConfiguredPurchaseRequest, type ConfiguredPurchaseResult, DEVICE_SETTINGS_MAX_BYTES, DedicatedServerError, type DedicatedServerFailure, type DedicatedServerSession, type DevToolsOpener, type DeviceSettings, type DeviceSettingsApi, type DeviceSettingsPutOutcome, type DirectPurchaseRequest, type DirectPurchaseResult, type EarnAllowance, type EarnAllowanceEvent, type EarnAllowancePeriod, type EarnAllowanceReadOptions, type EntityLeaderboard, type EntityLeaderboardEntry, type EntityLeaderboardOptions, type EntityType$1 as EntityType, type ExitAnalytics, type ExitBridge, type ExitRooms, type FrameState, type Friend, type FriendChallenge, FriendChallengeError, type FriendChallengeErrorCode, type FriendChallengeOutcome, type FriendChallengeOutcomeKind, type FriendChallengeRequest, type FriendChallengesPage, FriendsApi, type FriendsPage, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameApi, type GameConfig, type GameMenu, type GameMenuItem, type GameServerLink, type Gem, GemApiError, GemClient, type GemCohort, type GemCohorts, GemConfigError, type GemDevTools, type GemErrorReason, GemFrame, GemFunctionError, type GemIdentity, type GemInsets, type GemMatchApi, type GemMatchServer, type GemMatchServerModule, type GemOptions, type GemPlayer, type GemPrefs, type GemReservedArea, GemSaveError, type GemServer, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerIdentity, type GemServerLifecycle, type GemServerOptions, type GemServerProfile, type GemTerminationNotice, GemUnframedError, type GemViewport, type GetLeaderboardOptions, type HostTokenSource, type HostTokenSourceOptions, type JoinWelcome, type Leaderboard, type LeaderboardEntry, type LeaderboardPeriod, LeaderboardsApi, type ListChallengesOptions, type ListFriendChallengesOptions, type ListFriendsOptions, MAX_PERIODS_BACK, type MatchConnectOptions, type MatchConnection, type MatchHosting, type MatchHostingMode, type MatchServeOptions, type MatchTransport, type MessageMeta, type MultiTransportSession, PLATFORM_EVENTS, type PeriodOptions, PlatformApi, PlatformCurrenciesApi, type PlatformCurrency, type PlatformCurrencyAccess, type PlatformCurrencyPurchasePayment, type PlatformCurrencyPurchaseRequest, type PlatformCurrencyPurchaseResult, type PlatformCurrencyReadOptions, PlatformEarnAllowanceApi, type PlatformEventName, type PlayerBadge$1 as PlayerBadge, type PlayerBoost, type PlayerBoostMultiplier, type PlayerIdentity, type PlayerProfile, PlayerText, type ProfileCallOptions, type ProfileShowcaseEntry$1 as ProfileShowcaseEntry, ProfilesApi, type PurchaseApi, type PurchaseOptions, type PurchaseOutcome, PurchaseRefusal, type PurchaseRefusalCode, type PurchaseRequest, type PutBytesOptions, type PutSaveOptions, type RankedRow, type RecipeAvailability, type RecipeLock, type RequestOptions, type RewardCue, type RewardedStatus, type RoomArgValue, type SavePage, type SaveRecord, type SendOptions$1 as SendOptions, type ServerApi, type ServerBundleHost, type ServerTokenGrant, type SessionEndReason, type ShareApi, type ShareRequest, ShowcaseApi, type ShowcaseCallOptions, type ShowcaseFriendEntry, type ShowcaseFriends, type ShowcaseValue, type SignInOutcome, type SignInOutcomeKind, type SnapshotCurrency, type SnapshotItem, type TokenGrant, type Transport, type TransportAdvert, type TransportKind, TransportRefusal, type TransportRequest, type TransportResponse, UNRELIABLE_CHANNEL_LABEL, type VersionedGetOptions, type VersionedResponse, type VibrationApi, type WalletVisibleOptions, asPlayerText, createBootTimeline, createGem, createGemServer, createHostTokenSource, createMatchServer, defineServer, leaveAndExit, mapIdentity, markOnResolve, platformIconUrl, presentJoinCredential, readTemplateTransports, readTransportAdvert, reportOnResolve, systemClock, text, toBase64, webTransportAvailable };
4556
+ export { ARCADE_EMBEDDERS, type AccountApi, type AdFormat, type AdOutcome, type AdOutcomeKind, type AdRequest, type AdsApi, AnalyticsApi, type AnalyticsFlushOptions, type AnalyticsFlushResult, type AnalyticsOptions, type AnalyticsRejection$1 as AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Base64String, type BootMark, type BootTimeline, type BroadcastOptions, type BytesResponse, CONDITIONAL_HEADERS, type Challenge, type ChallengeWindow, ChallengesApi, type ClientOptions, type Clock, type ConditionalRequestHeaders, type ConfiguredPurchasePayment, type ConfiguredPurchaseRequest, type ConfiguredPurchaseResult, DEVICE_SETTINGS_MAX_BYTES, DedicatedServerError, type DedicatedServerFailure, type DedicatedServerSession, type DevToolsOpener, type DeviceSettings, type DeviceSettingsApi, type DeviceSettingsPutOutcome, type DirectPurchaseRequest, type DirectPurchaseResult, type EarnAllowance, type EarnAllowanceEvent, type EarnAllowancePeriod, type EarnAllowanceReadOptions, type EntityLeaderboard, type EntityLeaderboardEntry, type EntityLeaderboardOptions, type EntityType$1 as EntityType, type ExitAnalytics, type ExitBridge, type ExitRooms, type FrameState, type Friend, type FriendChallenge, FriendChallengeError, type FriendChallengeErrorCode, type FriendChallengeOutcome, type FriendChallengeOutcomeKind, type FriendChallengeRequest, type FriendChallengesPage, FriendsApi, type FriendsPage, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameApi, type GameConfig, type GameMenu, type GameMenuItem, type GameServerLink, type Gem, GemApiError, GemClient, type GemCohort, type GemCohorts, GemConfigError, type GemDevTools, type GemErrorReason, GemFrame, GemFunctionError, type GemIdentity, type GemInsets, type GemMatchApi, type GemMatchServer, type GemMatchServerModule, type GemOptions, type GemPlayer, type GemPrefs, type GemRecommendedSettings, type GemReservedArea, GemSaveError, type GemServer, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerIdentity, type GemServerLifecycle, type GemServerOptions, type GemServerProfile, type GemTerminationNotice, GemUnframedError, type GemViewport, type GetLeaderboardOptions, type HostTokenSource, type HostTokenSourceOptions, type JoinWelcome, type Leaderboard, type LeaderboardEntry, type LeaderboardPeriod, LeaderboardsApi, type ListChallengesOptions, type ListFriendChallengesOptions, type ListFriendsOptions, MAX_PERIODS_BACK, type MatchConnectOptions, type MatchConnection, type MatchHosting, type MatchHostingMode, type MatchServeOptions, type MatchTransport, type MessageMeta, type MultiTransportSession, PLATFORM_EVENTS, type PeriodOptions, PlatformApi, PlatformCurrenciesApi, type PlatformCurrency, type PlatformCurrencyAccess, type PlatformCurrencyPurchasePayment, type PlatformCurrencyPurchaseRequest, type PlatformCurrencyPurchaseResult, type PlatformCurrencyReadOptions, PlatformEarnAllowanceApi, type PlatformEventName, type PlayerBadge$1 as PlayerBadge, type PlayerBoost, type PlayerBoostMultiplier, type PlayerIdentity, type PlayerProfile, PlayerText, type ProfileCallOptions, type ProfileShowcaseEntry$1 as ProfileShowcaseEntry, ProfilesApi, type PurchaseApi, type PurchaseOptions, type PurchaseOutcome, PurchaseRefusal, type PurchaseRefusalCode, type PurchaseRequest, type PutBytesOptions, type PutSaveOptions, type RankedRow, type RecipeAvailability, type RecipeLock, type RequestOptions, type RewardCue, type RewardedStatus, type RoomArgValue, type SavePage, type SaveRecord, type SendOptions$1 as SendOptions, type ServerApi, type ServerBundleHost, type ServerTokenGrant, type SessionEndReason, type ShareApi, type ShareRequest, ShowcaseApi, type ShowcaseCallOptions, type ShowcaseFriendEntry, type ShowcaseFriends, type ShowcaseValue, type SignInOutcome, type SignInOutcomeKind, type SnapshotCurrency, type SnapshotItem, type TokenGrant, type Transport, type TransportAdvert, type TransportKind, TransportRefusal, type TransportRequest, type TransportResponse, UNRELIABLE_CHANNEL_LABEL, type VersionedGetOptions, type VersionedResponse, type VibrationApi, type WalletVisibleOptions, asPlayerText, createBootTimeline, createGem, createGemServer, createHostTokenSource, createMatchServer, defineServer, leaveAndExit, mapIdentity, markOnResolve, platformIconUrl, presentJoinCredential, readTemplateTransports, readTransportAdvert, reportOnResolve, systemClock, text, toBase64, webTransportAvailable };
@@ -1,6 +1,6 @@
1
1
  # The browser surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v9.1.4, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v10.1.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
 
@@ -6733,6 +6733,10 @@ checklist.
6733
6733
  here so the two agree, and see [Gem.reportMuted](#gem-reportmuted) for the other
6734
6734
  direction.
6735
6735
 
6736
+ **`recommended` changing is advice changing.** Apply it only to a setting
6737
+ the player has not chosen in your game; never overwrite their own choice
6738
+ with it.
6739
+
6736
6740
  <a id="gemoptions-oncohorts"></a>
6737
6741
 
6738
6742
  ## GemOptions.onCohorts
@@ -7175,6 +7179,10 @@ not for your game — respecting them is part of what a game must do.
7175
7179
  They can change mid-session, so read them when you need them rather than
7176
7180
  caching at boot.
7177
7181
 
7182
+ `recommended` is the exception: the player's Arcade-wide defaults, which
7183
+ your game may start its own settings from or ignore — never a reason to
7184
+ overwrite a choice the player made in your game.
7185
+
7178
7186
  **Guides.** [what-a-game-must-do](../what-a-game-must-do.md)
7179
7187
 
7180
7188
  <a id="gemprefs-reducedmotion"></a>
@@ -7227,6 +7235,100 @@ Use it to pick your game's language, and pass it to
7227
7235
  [text.compare](#text-compare) when sorting names — that call has no other way to
7228
7236
  learn a locale.
7229
7237
 
7238
+ <a id="gemprefs-recommended"></a>
7239
+
7240
+ ## GemPrefs.recommended
7241
+
7242
+ ```ts
7243
+ readonly recommended: GemRecommendedSettings | null
7244
+ ```
7245
+
7246
+ The player's Arcade-wide defaults for this device — a volume, battery
7247
+ saver, a keyboard layout, the gamepad's confirm button and vibration — or
7248
+ `null` when the Arcade did not send them.
7249
+
7250
+ **Recommendations only; the player's own choice in your game wins.** Start
7251
+ a setting from here while the player has made no choice of their own for
7252
+ it, and never overwrite one they made because this changed:
7253
+
7254
+ ```js
7255
+ const saved = gem.deviceSettings.current ?? {};
7256
+ const volume = saved.volume ?? gem.prefs?.recommended?.volume ?? 100;
7257
+ ```
7258
+
7259
+ Unlike `muted` and `reducedMotion`, nothing here is applied for you, and
7260
+ nothing obliges you to use it. See the guide's "Game settings" section.
7261
+
7262
+ <a id="gemrecommendedsettings"></a>
7263
+
7264
+ ## GemRecommendedSettings
7265
+
7266
+ ```ts
7267
+ interface GemRecommendedSettings
7268
+ ```
7269
+
7270
+ The player's Arcade-wide defaults for this device, as `gem.prefs.recommended`.
7271
+
7272
+ The player sets these once in the Arcade's Settings, and every game that
7273
+ reads them starts in the right place. They are advice: a game may ignore
7274
+ them, and a choice the player made in your game always beats them.
7275
+
7276
+ **Guides.** [what-a-game-must-do](../what-a-game-must-do.md)
7277
+
7278
+ <a id="gemrecommendedsettings-volume"></a>
7279
+
7280
+ ## GemRecommendedSettings.volume
7281
+
7282
+ ```ts
7283
+ readonly volume: number
7284
+ ```
7285
+
7286
+ The volume the player wants, as a percentage from 0 to 100. A level, not a
7287
+ mute — `gem.prefs.muted` is the mute.
7288
+
7289
+ <a id="gemrecommendedsettings-batterysaver"></a>
7290
+
7291
+ ## GemRecommendedSettings.batterySaver
7292
+
7293
+ ```ts
7294
+ readonly batterySaver: boolean
7295
+ ```
7296
+
7297
+ The player asked games to save power on this device: a lower frame rate,
7298
+ fewer effects, a lower resolution.
7299
+
7300
+ <a id="gemrecommendedsettings-keyboardlayout"></a>
7301
+
7302
+ ## GemRecommendedSettings.keyboardLayout
7303
+
7304
+ ```ts
7305
+ readonly keyboardLayout: "wasd" | "arrows"
7306
+ ```
7307
+
7308
+ Which keys the player expects to move with: `'wasd'` or `'arrows'`.
7309
+
7310
+ <a id="gemrecommendedsettings-gamepadconfirm"></a>
7311
+
7312
+ ## GemRecommendedSettings.gamepadConfirm
7313
+
7314
+ ```ts
7315
+ readonly gamepadConfirm: "south" | "east"
7316
+ ```
7317
+
7318
+ Which gamepad face button the player expects to confirm with, by position:
7319
+ `'south'` (the bottom button, A on an Xbox controller) or `'east'` (the
7320
+ right one, B on an Xbox controller). The other one goes back.
7321
+
7322
+ <a id="gemrecommendedsettings-vibration"></a>
7323
+
7324
+ ## GemRecommendedSettings.vibration
7325
+
7326
+ ```ts
7327
+ readonly vibration: boolean
7328
+ ```
7329
+
7330
+ Whether the player wants vibration and controller rumble.
7331
+
7230
7332
  <a id="gemcohort"></a>
7231
7333
 
7232
7334
  ## GemCohort
@@ -7536,8 +7638,10 @@ What your game adds to the arcade's menu — see `gem.setMenu`.
7536
7638
  readonly onSettings?: () => void
7537
7639
  ```
7538
7640
 
7539
- Open your settings screen. Give this and the arcade's menu gains a
7540
- Settings entry that calls it; leave it out and there is no such entry.
7641
+ Open your settings screen. Another embedder may show a Settings entry that
7642
+ calls it, but **the Gem Arcade no longer does**: draw a settings button of
7643
+ your own, always (top-right is the recommendation). Passing this is still
7644
+ accepted and harmless.
7541
7645
 
7542
7646
  <a id="gamemenu-items"></a>
7543
7647
 
@@ -8484,25 +8588,21 @@ for one: the arcade takes every figure from the platform. If this returns
8484
8588
  setMenu(menu: GameMenu): boolean
8485
8589
  ```
8486
8590
 
8487
- Put your settings, and up to four entries of your own, in the arcade's
8488
- menu. The arcade keeps one menu button over your game, in the top-right
8489
- corner: it is how a player leaves, mutes, goes fullscreen and reports a
8490
- problem — and, once you call this, how they open **your** settings.
8591
+ Put up to four entries of your own in the arcade's menu. The arcade keeps
8592
+ one menu button over your game: it is how a player leaves, mutes, goes
8593
+ fullscreen, reports a problem and changes their Arcade-wide settings.
8491
8594
 
8492
8595
  ```js
8493
- const inArcadeMenu = gem.setMenu({
8494
- onSettings: () => settingsScreen.open(),
8596
+ gem.setMenu({
8495
8597
  items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],
8496
8598
  });
8497
- ownSettingsButton.hidden = inArcadeMenu;
8498
8599
  ```
8499
8600
 
8500
- **Do not draw a settings button of your own while this returns `true`** —
8501
- the arcade's menu is the one place a player looks, in every game. Draw
8502
- your own only when it returns `false` once the bridge has settled
8503
- (`await gem.awaitBridge()`): an embedder with no menu, or no embedder at
8504
- all. The top-left corner is yours, for a back or home button that moves
8505
- around inside your game.
8601
+ **Your settings are not in it: draw your own settings button, always** —
8602
+ top-right is the recommendation. `onSettings` is still accepted, and
8603
+ another embedder may show an entry for it, but the Gem Arcade no longer
8604
+ shows a Settings entry, so a game whose settings open only from there has
8605
+ none a player can reach.
8506
8606
 
8507
8607
  **It is screen state, not a toggle.** Each call is the whole answer and
8508
8608
  replaces the last, so call it again when the entries change — a new
@@ -14668,6 +14768,58 @@ session — [RoomManager.leaveAll](#roommanager-leaveall) is what clears those.
14668
14768
 
14669
14769
  **Returns.** The room joined or created.
14670
14770
 
14771
+ <a id="roommanager-queueformatch"></a>
14772
+
14773
+ ## RoomManager.queueForMatch
14774
+
14775
+ ```ts
14776
+ queueForMatch(options: QueueForMatchOptions): Promise<MatchTicket>
14777
+ ```
14778
+
14779
+ Queue for a match with other players, and get a ticket to wait on.
14780
+
14781
+ FOR A MODE THAT NEEDS A FULL LOBBY BEFORE IT STARTS. The platform holds the
14782
+ player in a queue until it has enough players for one of the templates you
14783
+ named, then makes the room, seats everyone and starts the match — nobody
14784
+ hosts and nobody presses start. For a drop-in mode, where a player should
14785
+ be in a room at once and others join around them, use
14786
+ [RoomManager.quickJoin](#roommanager-quickjoin) instead.
14787
+
14788
+ This call answers as soon as the player is queued. What happens next is on
14789
+ the ticket: await [MatchTicket.outcome](#matchticket-outcome), and when it says `matched`
14790
+ this manager is already in the room — `joined` has fired and the match
14791
+ events follow as for any other room.
14792
+
14793
+ A PLAYER MAY QUEUE FROM INSIDE A ROOM — a hub, or a social lobby they wait
14794
+ in. When the ticket is matched the manager moves to the match's room:
14795
+ `currentRoom` becomes it and `joined` fires. The seat in the room they
14796
+ queued from is kept on the platform, not given up, so when the match is
14797
+ over `join()` with that room's id puts them back in it. If the tab closes
14798
+ before they go back, the SDK leaves both rooms.
14799
+
14800
+ What it does refuse is a second thing in flight: while a ticket waits,
14801
+ `create`, `join`, `joinByCode`, `quickJoin` and another `queueForMatch`
14802
+ refuse until it ends or is cancelled.
14803
+
14804
+ CLOSING THE TAB CANCELS THE TICKET. The SDK sends the cancel as the page
14805
+ goes away, so a player who left is not matched into a lobby they will
14806
+ never arrive in.
14807
+
14808
+ | Parameter | Type | Description |
14809
+ | --- | --- | --- |
14810
+ | `options` | `QueueForMatchOptions` | The templates the player accepts. |
14811
+
14812
+ **Returns.** The ticket, still `queued` unless a lobby was waiting for exactly
14813
+ this player.
14814
+
14815
+ **Throws.** `GemApiError` when the platform refuses the ticket. Two refusals
14816
+ are worth their own branch, by [GemApiError.code](#gemapierror-code):
14817
+ `already_queued` — this player, or someone in their party, is already
14818
+ on a ticket, possibly in another tab or another game — and
14819
+ `matchmaking_unavailable`, which means matchmaking is turned off or not
14820
+ answering. Tell the player; do not loop on it, and do not quietly
14821
+ substitute `quickJoin`, which puts them in a different kind of room.
14822
+
14671
14823
  <a id="roommanager-list"></a>
14672
14824
 
14673
14825
  ## RoomManager.list
@@ -15002,6 +15154,20 @@ The room this player is in, or `null`. Kept fresh for you.
15002
15154
  Read it whenever you render the lobby rather than caching it — the manager
15003
15155
  updates it as players join, ready up and leave, and emits events as it does.
15004
15156
 
15157
+ <a id="roommanager-currentticket"></a>
15158
+
15159
+ ## RoomManager.currentTicket
15160
+
15161
+ ```ts
15162
+ readonly currentTicket: MatchTicket | null
15163
+ ```
15164
+
15165
+ The matchmaking ticket this player is waiting on, or `null`.
15166
+
15167
+ Set from the moment [RoomManager.queueForMatch](#roommanager-queueformatch) answers until the
15168
+ ticket ends — so it is `null` again by the time a matched ticket's room is
15169
+ [RoomManager.currentRoom](#roommanager-currentroom).
15170
+
15005
15171
  <a id="roommanager-currentmatch"></a>
15006
15172
 
15007
15173
  ## RoomManager.currentMatch
@@ -15212,6 +15378,220 @@ Stop polling and drop every listener.
15212
15378
  Call this when tearing your game down. It does not leave the room — use
15213
15379
  [RoomManager.leave](#roommanager-leave) or `gem.exit()` for that.
15214
15380
 
15381
+ <a id="queueformatchoptions"></a>
15382
+
15383
+ ## QueueForMatchOptions
15384
+
15385
+ ```ts
15386
+ interface QueueForMatchOptions
15387
+ ```
15388
+
15389
+ What you pass to `gem.rooms.queueForMatch(...)`.
15390
+
15391
+ NAME EVERY MODE THE PLAYER WILL ACCEPT. The ticket waits for all of them at
15392
+ once and is matched under whichever fills first, so "any mode" is one ticket
15393
+ naming several templates rather than several tickets — a player can only be
15394
+ on one.
15395
+
15396
+ ```js
15397
+ const ticket = await gem.rooms.queueForMatch({ templateSlugs: ['duel', 'brawl'] });
15398
+ ```
15399
+
15400
+ **Guides.** [rooms-and-matches](../rooms-and-matches.md)
15401
+
15402
+ <a id="queueformatchoptions-templateslugs"></a>
15403
+
15404
+ ## QueueForMatchOptions.templateSlugs
15405
+
15406
+ ```ts
15407
+ readonly templateSlugs: readonly string[]
15408
+ ```
15409
+
15410
+ The room templates this player accepts, by slug — one to eight of them.
15411
+
15412
+ EVERY ONE MUST HAVE MATCHMAKING TURNED ON in your game config. A template
15413
+ without it refuses the whole ticket rather than being skipped, so a typo
15414
+ is an error you see and not a mode nobody is ever matched into.
15415
+
15416
+ <a id="queueformatchoptions-partyid"></a>
15417
+
15418
+ ## QueueForMatchOptions.partyId
15419
+
15420
+ ```ts
15421
+ readonly partyId?: string
15422
+ ```
15423
+
15424
+ A party to queue as one unit. Normally omit it — the SDK brings the
15425
+ player's party along when they lead one, exactly as a room call does.
15426
+
15427
+ A party is seated together or not at all. If it gains or loses a member
15428
+ while it waits, the ticket ends `cancelled` with reason `party_changed`.
15429
+
15430
+ <a id="queueformatchoptions-signal"></a>
15431
+
15432
+ ## QueueForMatchOptions.signal
15433
+
15434
+ ```ts
15435
+ readonly signal?: AbortSignal
15436
+ ```
15437
+
15438
+ Cancel the request that creates the ticket.
15439
+
15440
+ Only that request. Once you hold a ticket, [MatchTicket.cancel](#matchticket-cancel) is
15441
+ what takes the player out of the queue.
15442
+
15443
+ <a id="matchticketstatus"></a>
15444
+
15445
+ ## MatchTicketStatus
15446
+
15447
+ ```ts
15448
+ type MatchTicketStatus = "queued" | "matched" | "expired" | "cancelled" | "failed" | "lost" | "unavailable"
15449
+ ```
15450
+
15451
+ Where a ticket is: `queued` while it waits, and anything else for good.
15452
+
15453
+ - `matched` — the player is seated and the match is starting.
15454
+ - `expired` — it waited too long, or stopped being kept alive.
15455
+ - `cancelled` — the player backed out, or their party changed.
15456
+ - `failed` — the platform formed a match and could not start it, repeatedly.
15457
+ - `lost` — the platform no longer has the ticket. Queue again.
15458
+ - `unavailable` — matchmaking is turned off or not answering.
15459
+
15460
+ <a id="matchticketoutcome"></a>
15461
+
15462
+ ## MatchTicketOutcome
15463
+
15464
+ ```ts
15465
+ type MatchTicketOutcome = { status: "matched"; room: Room } | { status: "expired"; reason: string } | { status: "cancelled"; reason: string } | { status: "failed"; reason: string } | { status: "lost" } | { status: "unavailable" }
15466
+ ```
15467
+
15468
+ How a ticket ended. Branch on `status`.
15469
+
15470
+ ONLY `matched` CARRIES A ROOM, and by the time you see it the SDK is already
15471
+ in that room: `gem.rooms.currentRoom` is it, `joined` has fired, and the
15472
+ match events follow as they do for any other room.
15473
+
15474
+ Every other ending leaves the player in no room and on no ticket, free to
15475
+ queue again. None of them is retried for you, and nothing falls back to
15476
+ `quickJoin` — a game that wants either does it on purpose.
15477
+
15478
+ `reason` says why, where the platform gave one: `timed_out` or
15479
+ `heartbeat_lapsed` for `expired`, `requested` or `party_changed` for
15480
+ `cancelled`, `handoff_failed` for `failed`. Treat a value you do not
15481
+ recognise as the status alone.
15482
+
15483
+ <a id="matchticket"></a>
15484
+
15485
+ ## MatchTicket
15486
+
15487
+ ```ts
15488
+ interface MatchTicket
15489
+ ```
15490
+
15491
+ A place in the matchmaking queue — what `gem.rooms.queueForMatch(...)`
15492
+ answers with.
15493
+
15494
+ You do not keep it alive; the SDK does, for as long as it waits. What you do
15495
+ with it is wait for [MatchTicket.outcome](#matchticket-outcome), and offer the player a
15496
+ button that calls [MatchTicket.cancel](#matchticket-cancel).
15497
+
15498
+ ```js
15499
+ const ticket = await gem.rooms.queueForMatch({ templateSlugs: ['duel'] });
15500
+ showSearching({ onCancel: () => ticket.cancel() });
15501
+
15502
+ const outcome = await ticket.outcome;
15503
+ if (outcome.status === 'matched') enterMatch(outcome.room);
15504
+ else backToMenu(outcome.status);
15505
+ ```
15506
+
15507
+ **Guides.** [rooms-and-matches](../rooms-and-matches.md)
15508
+
15509
+ <a id="matchticket-id"></a>
15510
+
15511
+ ## MatchTicket.id
15512
+
15513
+ ```ts
15514
+ readonly id: string
15515
+ ```
15516
+
15517
+ The ticket's id. For logs and bug reports; nothing you call takes it.
15518
+
15519
+ <a id="matchticket-status"></a>
15520
+
15521
+ ## MatchTicket.status
15522
+
15523
+ ```ts
15524
+ readonly status: MatchTicketStatus
15525
+ ```
15526
+
15527
+ Where the ticket is right now. Changes once, from `queued` to how it ended.
15528
+
15529
+ <a id="matchticket-partysize"></a>
15530
+
15531
+ ## MatchTicket.partySize
15532
+
15533
+ ```ts
15534
+ readonly partySize: number
15535
+ ```
15536
+
15537
+ How many players the ticket seats: 1, or the size of the party.
15538
+
15539
+ <a id="matchticket-queuedat"></a>
15540
+
15541
+ ## MatchTicket.queuedAt
15542
+
15543
+ ```ts
15544
+ readonly queuedAt: number
15545
+ ```
15546
+
15547
+ When the ticket joined the queue, in Unix seconds.
15548
+
15549
+ <a id="matchticket-expiresat"></a>
15550
+
15551
+ ## MatchTicket.expiresAt
15552
+
15553
+ ```ts
15554
+ readonly expiresAt: number
15555
+ ```
15556
+
15557
+ When the ticket gives up if it is still waiting, in Unix seconds. Good for
15558
+ a countdown; the platform decides the length, per template.
15559
+
15560
+ <a id="matchticket-outcome"></a>
15561
+
15562
+ ## MatchTicket.outcome
15563
+
15564
+ ```ts
15565
+ readonly outcome: Promise<MatchTicketOutcome>
15566
+ ```
15567
+
15568
+ How the ticket ends. Settles exactly once, and RESOLVES for every ending
15569
+ the platform reports — matched, expired, cancelled and the rest are
15570
+ outcomes to branch on, not errors to catch.
15571
+
15572
+ It rejects in one case only: the ticket was matched and the SDK could not
15573
+ read the room it was seated in. The player holds a seat then, so
15574
+ `gem.rooms.myRooms()` shows the room and `gem.rooms.join(id)` enters it.
15575
+
15576
+ <a id="matchticket-cancel"></a>
15577
+
15578
+ ## MatchTicket.cancel
15579
+
15580
+ ```ts
15581
+ cancel(): Promise<MatchTicketOutcome>
15582
+ ```
15583
+
15584
+ Take the player — and their party — out of the queue.
15585
+
15586
+ A CANCEL CAN LOSE THE RACE, and then the answer is `matched`. A ticket the
15587
+ platform has already seated is never undone, so read the outcome this
15588
+ returns rather than assuming: when it says `matched` the player is in a
15589
+ room, and backing out is `gem.rooms.leave()`.
15590
+
15591
+ Safe to call more than once, and after the ticket has ended.
15592
+
15593
+ **Returns.** The same promise as [MatchTicket.outcome](#matchticket-outcome).
15594
+
15215
15595
  <a id="roomstatus"></a>
15216
15596
 
15217
15597
  ## RoomStatus