@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.
@@ -1,6 +1,6 @@
1
1
  # The server 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/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
6
6
 
@@ -2,7 +2,7 @@
2
2
  "schema_version": 2,
3
3
  "source": {
4
4
  "repo": "IndieGems/gem-web-sdk",
5
- "version": "9.1.4"
5
+ "version": "10.1.0"
6
6
  },
7
7
  "entries": [
8
8
  {
@@ -7630,7 +7630,7 @@
7630
7630
  "group": "start",
7631
7631
  "signature": "readonly onPrefs?: (prefs: GemPrefs) => void",
7632
7632
  "documented": true,
7633
- "summary": "The player's accessibility and locale preferences arrived, or changed.\n\n**A game that animates owes this a handler.** Reduced motion is the\nplayer's own account setting, and honouring it is on the platform's release\nchecklist.\n\n**`muted` is honoured for you** — the SDK silences your audio itself (see\n`manageAudio`). If your game has its own mute control, set it from `muted`\nhere so the two agree, and see [Gem.reportMuted](api-reference-game.md#gem-reportmuted) for the other\ndirection.",
7633
+ "summary": "The player's accessibility and locale preferences arrived, or changed.\n\n**A game that animates owes this a handler.** Reduced motion is the\nplayer's own account setting, and honouring it is on the platform's release\nchecklist.\n\n**`muted` is honoured for you** — the SDK silences your audio itself (see\n`manageAudio`). If your game has its own mute control, set it from `muted`\nhere so the two agree, and see [Gem.reportMuted](api-reference-game.md#gem-reportmuted) for the other\ndirection.\n\n**`recommended` changing is advice changing.** Apply it only to a setting\nthe player has not chosen in your game; never overwrite their own choice\nwith it.",
7634
7634
  "deprecated": null,
7635
7635
  "params": [],
7636
7636
  "returns": null,
@@ -8008,7 +8008,7 @@
8008
8008
  "group": "player",
8009
8009
  "signature": "interface GemPrefs",
8010
8010
  "documented": true,
8011
- "summary": "The player's arcade-level preferences, which your game is expected to honour.\n\nReached as `gem.prefs`. These are the player's settings for the whole arcade,\nnot for your game — respecting them is part of what a game must do.\n\nThey can change mid-session, so read them when you need them rather than\ncaching at boot.",
8011
+ "summary": "The player's arcade-level preferences, which your game is expected to honour.\n\nReached as `gem.prefs`. These are the player's settings for the whole arcade,\nnot for your game — respecting them is part of what a game must do.\n\nThey can change mid-session, so read them when you need them rather than\ncaching at boot.\n\n`recommended` is the exception: the player's Arcade-wide defaults, which\nyour game may start its own settings from or ignore — never a reason to\noverwrite a choice the player made in your game.",
8012
8012
  "deprecated": null,
8013
8013
  "params": [],
8014
8014
  "returns": null,
@@ -8086,6 +8086,123 @@
8086
8086
  "defaultValue": null,
8087
8087
  "guides": []
8088
8088
  },
8089
+ {
8090
+ "id": "GemPrefs.recommended",
8091
+ "name": "recommended",
8092
+ "anchor": "gemprefs-recommended",
8093
+ "group": "player",
8094
+ "signature": "readonly recommended: GemRecommendedSettings | null",
8095
+ "documented": true,
8096
+ "summary": "The player's Arcade-wide defaults for this device — a volume, battery\nsaver, a keyboard layout, the gamepad's confirm button and vibration — or\n`null` when the Arcade did not send them.\n\n**Recommendations only; the player's own choice in your game wins.** Start\na setting from here while the player has made no choice of their own for\nit, and never overwrite one they made because this changed:\n\n```js\nconst saved = gem.deviceSettings.current ?? {};\nconst volume = saved.volume ?? gem.prefs?.recommended?.volume ?? 100;\n```\n\nUnlike `muted` and `reducedMotion`, nothing here is applied for you, and\nnothing obliges you to use it. See the guide's \"Game settings\" section.",
8097
+ "deprecated": null,
8098
+ "params": [],
8099
+ "returns": null,
8100
+ "throws": null,
8101
+ "example": null,
8102
+ "defaultValue": null,
8103
+ "guides": []
8104
+ },
8105
+ {
8106
+ "id": "GemRecommendedSettings",
8107
+ "name": "GemRecommendedSettings",
8108
+ "anchor": "gemrecommendedsettings",
8109
+ "group": "player",
8110
+ "signature": "interface GemRecommendedSettings",
8111
+ "documented": true,
8112
+ "summary": "The player's Arcade-wide defaults for this device, as `gem.prefs.recommended`.\n\nThe player sets these once in the Arcade's Settings, and every game that\nreads them starts in the right place. They are advice: a game may ignore\nthem, and a choice the player made in your game always beats them.",
8113
+ "deprecated": null,
8114
+ "params": [],
8115
+ "returns": null,
8116
+ "throws": null,
8117
+ "example": null,
8118
+ "defaultValue": null,
8119
+ "guides": [
8120
+ {
8121
+ "slug": "what-a-game-must-do",
8122
+ "anchor": null
8123
+ }
8124
+ ]
8125
+ },
8126
+ {
8127
+ "id": "GemRecommendedSettings.volume",
8128
+ "name": "volume",
8129
+ "anchor": "gemrecommendedsettings-volume",
8130
+ "group": "player",
8131
+ "signature": "readonly volume: number",
8132
+ "documented": true,
8133
+ "summary": "The volume the player wants, as a percentage from 0 to 100. A level, not a\nmute — `gem.prefs.muted` is the mute.",
8134
+ "deprecated": null,
8135
+ "params": [],
8136
+ "returns": null,
8137
+ "throws": null,
8138
+ "example": null,
8139
+ "defaultValue": null,
8140
+ "guides": []
8141
+ },
8142
+ {
8143
+ "id": "GemRecommendedSettings.batterySaver",
8144
+ "name": "batterySaver",
8145
+ "anchor": "gemrecommendedsettings-batterysaver",
8146
+ "group": "player",
8147
+ "signature": "readonly batterySaver: boolean",
8148
+ "documented": true,
8149
+ "summary": "The player asked games to save power on this device: a lower frame rate,\nfewer effects, a lower resolution.",
8150
+ "deprecated": null,
8151
+ "params": [],
8152
+ "returns": null,
8153
+ "throws": null,
8154
+ "example": null,
8155
+ "defaultValue": null,
8156
+ "guides": []
8157
+ },
8158
+ {
8159
+ "id": "GemRecommendedSettings.keyboardLayout",
8160
+ "name": "keyboardLayout",
8161
+ "anchor": "gemrecommendedsettings-keyboardlayout",
8162
+ "group": "player",
8163
+ "signature": "readonly keyboardLayout: \"wasd\" | \"arrows\"",
8164
+ "documented": true,
8165
+ "summary": "Which keys the player expects to move with: `'wasd'` or `'arrows'`.",
8166
+ "deprecated": null,
8167
+ "params": [],
8168
+ "returns": null,
8169
+ "throws": null,
8170
+ "example": null,
8171
+ "defaultValue": null,
8172
+ "guides": []
8173
+ },
8174
+ {
8175
+ "id": "GemRecommendedSettings.gamepadConfirm",
8176
+ "name": "gamepadConfirm",
8177
+ "anchor": "gemrecommendedsettings-gamepadconfirm",
8178
+ "group": "player",
8179
+ "signature": "readonly gamepadConfirm: \"south\" | \"east\"",
8180
+ "documented": true,
8181
+ "summary": "Which gamepad face button the player expects to confirm with, by position:\n`'south'` (the bottom button, A on an Xbox controller) or `'east'` (the\nright one, B on an Xbox controller). The other one goes back.",
8182
+ "deprecated": null,
8183
+ "params": [],
8184
+ "returns": null,
8185
+ "throws": null,
8186
+ "example": null,
8187
+ "defaultValue": null,
8188
+ "guides": []
8189
+ },
8190
+ {
8191
+ "id": "GemRecommendedSettings.vibration",
8192
+ "name": "vibration",
8193
+ "anchor": "gemrecommendedsettings-vibration",
8194
+ "group": "player",
8195
+ "signature": "readonly vibration: boolean",
8196
+ "documented": true,
8197
+ "summary": "Whether the player wants vibration and controller rumble.",
8198
+ "deprecated": null,
8199
+ "params": [],
8200
+ "returns": null,
8201
+ "throws": null,
8202
+ "example": null,
8203
+ "defaultValue": null,
8204
+ "guides": []
8205
+ },
8089
8206
  {
8090
8207
  "id": "GemCohort",
8091
8208
  "name": "GemCohort",
@@ -8487,7 +8604,7 @@
8487
8604
  "group": "frame",
8488
8605
  "signature": "readonly onSettings?: () => void",
8489
8606
  "documented": true,
8490
- "summary": "Open your settings screen. Give this and the arcade's menu gains a\nSettings entry that calls it; leave it out and there is no such entry.",
8607
+ "summary": "Open your settings screen. Another embedder may show a Settings entry that\ncalls it, but **the Gem Arcade no longer does**: draw a settings button of\nyour own, always (top-right is the recommendation). Passing this is still\naccepted and harmless.",
8491
8608
  "deprecated": null,
8492
8609
  "params": [],
8493
8610
  "returns": null,
@@ -9271,7 +9388,7 @@
9271
9388
  "group": "start",
9272
9389
  "signature": "setMenu(menu: GameMenu): boolean",
9273
9390
  "documented": true,
9274
- "summary": "Put your settings, and up to four entries of your own, in the arcade's\nmenu. The arcade keeps one menu button over your game, in the top-right\ncorner: it is how a player leaves, mutes, goes fullscreen and reports a\nproblem — and, once you call this, how they open **your** settings.\n\n```js\nconst inArcadeMenu = gem.setMenu({\n onSettings: () => settingsScreen.open(),\n items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],\n});\nownSettingsButton.hidden = inArcadeMenu;\n```\n\n**Do not draw a settings button of your own while this returns `true`** —\nthe arcade's menu is the one place a player looks, in every game. Draw\nyour own only when it returns `false` once the bridge has settled\n(`await gem.awaitBridge()`): an embedder with no menu, or no embedder at\nall. The top-left corner is yours, for a back or home button that moves\naround inside your game.\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last, so call it again when the entries change — a new\nlanguage, a screen with different options. `gem.setMenu({})` takes\neverything of yours back out. The arcade's own entries are not yours to\nchange, and Return to the Arcade is always among them.\n\n**The arcade pauses your game while its menu is open** (`onPause`), and\nresumes it before a handler of yours runs. A handler opens a screen; it\nshould not assume the game was paused by you.",
9391
+ "summary": "Put up to four entries of your own in the arcade's menu. The arcade keeps\none menu button over your game: it is how a player leaves, mutes, goes\nfullscreen, reports a problem and changes their Arcade-wide settings.\n\n```js\ngem.setMenu({\n items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],\n});\n```\n\n**Your settings are not in it: draw your own settings button, always** —\ntop-right is the recommendation. `onSettings` is still accepted, and\nanother embedder may show an entry for it, but the Gem Arcade no longer\nshows a Settings entry, so a game whose settings open only from there has\nnone a player can reach.\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last, so call it again when the entries change — a new\nlanguage, a screen with different options. `gem.setMenu({})` takes\neverything of yours back out. The arcade's own entries are not yours to\nchange, and Return to the Arcade is always among them.\n\n**The arcade pauses your game while its menu is open** (`onPause`), and\nresumes it before a handler of yours runs. A handler opens a screen; it\nshould not assume the game was paused by you.",
9275
9392
  "deprecated": null,
9276
9393
  "params": [
9277
9394
  {
@@ -16350,6 +16467,29 @@
16350
16467
  "defaultValue": null,
16351
16468
  "guides": []
16352
16469
  },
16470
+ {
16471
+ "id": "RoomManager.queueForMatch",
16472
+ "name": "queueForMatch",
16473
+ "anchor": "roommanager-queueformatch",
16474
+ "group": "rooms",
16475
+ "signature": "queueForMatch(options: QueueForMatchOptions): Promise<MatchTicket>",
16476
+ "documented": true,
16477
+ "summary": "Queue for a match with other players, and get a ticket to wait on.\n\nFOR A MODE THAT NEEDS A FULL LOBBY BEFORE IT STARTS. The platform holds the\nplayer in a queue until it has enough players for one of the templates you\nnamed, then makes the room, seats everyone and starts the match — nobody\nhosts and nobody presses start. For a drop-in mode, where a player should\nbe in a room at once and others join around them, use\n[RoomManager.quickJoin](api-reference-game.md#roommanager-quickjoin) instead.\n\nThis call answers as soon as the player is queued. What happens next is on\nthe ticket: await [MatchTicket.outcome](api-reference-game.md#matchticket-outcome), and when it says `matched`\nthis manager is already in the room — `joined` has fired and the match\nevents follow as for any other room.\n\nA PLAYER MAY QUEUE FROM INSIDE A ROOM — a hub, or a social lobby they wait\nin. When the ticket is matched the manager moves to the match's room:\n`currentRoom` becomes it and `joined` fires. The seat in the room they\nqueued from is kept on the platform, not given up, so when the match is\nover `join()` with that room's id puts them back in it. If the tab closes\nbefore they go back, the SDK leaves both rooms.\n\nWhat it does refuse is a second thing in flight: while a ticket waits,\n`create`, `join`, `joinByCode`, `quickJoin` and another `queueForMatch`\nrefuse until it ends or is cancelled.\n\nCLOSING THE TAB CANCELS THE TICKET. The SDK sends the cancel as the page\ngoes away, so a player who left is not matched into a lobby they will\nnever arrive in.",
16478
+ "deprecated": null,
16479
+ "params": [
16480
+ {
16481
+ "name": "options",
16482
+ "type": "QueueForMatchOptions",
16483
+ "optional": false,
16484
+ "summary": "The templates the player accepts."
16485
+ }
16486
+ ],
16487
+ "returns": "The ticket, still `queued` unless a lobby was waiting for exactly\nthis player.",
16488
+ "throws": "`GemApiError` when the platform refuses the ticket. Two refusals\nare worth their own branch, by [GemApiError.code](api-reference-game.md#gemapierror-code):\n`already_queued` — this player, or someone in their party, is already\non a ticket, possibly in another tab or another game — and\n`matchmaking_unavailable`, which means matchmaking is turned off or not\nanswering. Tell the player; do not loop on it, and do not quietly\nsubstitute `quickJoin`, which puts them in a different kind of room.",
16489
+ "example": null,
16490
+ "defaultValue": null,
16491
+ "guides": []
16492
+ },
16353
16493
  {
16354
16494
  "id": "RoomManager.list",
16355
16495
  "name": "list",
@@ -16790,6 +16930,22 @@
16790
16930
  "defaultValue": null,
16791
16931
  "guides": []
16792
16932
  },
16933
+ {
16934
+ "id": "RoomManager.currentTicket",
16935
+ "name": "currentTicket",
16936
+ "anchor": "roommanager-currentticket",
16937
+ "group": "rooms",
16938
+ "signature": "readonly currentTicket: MatchTicket | null",
16939
+ "documented": true,
16940
+ "summary": "The matchmaking ticket this player is waiting on, or `null`.\n\nSet from the moment [RoomManager.queueForMatch](api-reference-game.md#roommanager-queueformatch) answers until the\nticket ends — so it is `null` again by the time a matched ticket's room is\n[RoomManager.currentRoom](api-reference-game.md#roommanager-currentroom).",
16941
+ "deprecated": null,
16942
+ "params": [],
16943
+ "returns": null,
16944
+ "throws": null,
16945
+ "example": null,
16946
+ "defaultValue": null,
16947
+ "guides": []
16948
+ },
16793
16949
  {
16794
16950
  "id": "RoomManager.currentMatch",
16795
16951
  "name": "currentMatch",
@@ -17083,6 +17239,240 @@
17083
17239
  "defaultValue": null,
17084
17240
  "guides": []
17085
17241
  },
17242
+ {
17243
+ "id": "QueueForMatchOptions",
17244
+ "name": "QueueForMatchOptions",
17245
+ "anchor": "queueformatchoptions",
17246
+ "group": "rooms",
17247
+ "signature": "interface QueueForMatchOptions",
17248
+ "documented": true,
17249
+ "summary": "What you pass to `gem.rooms.queueForMatch(...)`.\n\nNAME EVERY MODE THE PLAYER WILL ACCEPT. The ticket waits for all of them at\nonce and is matched under whichever fills first, so \"any mode\" is one ticket\nnaming several templates rather than several tickets — a player can only be\non one.\n\n```js\nconst ticket = await gem.rooms.queueForMatch({ templateSlugs: ['duel', 'brawl'] });\n```",
17250
+ "deprecated": null,
17251
+ "params": [],
17252
+ "returns": null,
17253
+ "throws": null,
17254
+ "example": null,
17255
+ "defaultValue": null,
17256
+ "guides": [
17257
+ {
17258
+ "slug": "rooms-and-matches",
17259
+ "anchor": null
17260
+ }
17261
+ ]
17262
+ },
17263
+ {
17264
+ "id": "QueueForMatchOptions.templateSlugs",
17265
+ "name": "templateSlugs",
17266
+ "anchor": "queueformatchoptions-templateslugs",
17267
+ "group": "rooms",
17268
+ "signature": "readonly templateSlugs: readonly string[]",
17269
+ "documented": true,
17270
+ "summary": "The room templates this player accepts, by slug — one to eight of them.\n\nEVERY ONE MUST HAVE MATCHMAKING TURNED ON in your game config. A template\nwithout it refuses the whole ticket rather than being skipped, so a typo\nis an error you see and not a mode nobody is ever matched into.",
17271
+ "deprecated": null,
17272
+ "params": [],
17273
+ "returns": null,
17274
+ "throws": null,
17275
+ "example": null,
17276
+ "defaultValue": null,
17277
+ "guides": []
17278
+ },
17279
+ {
17280
+ "id": "QueueForMatchOptions.partyId",
17281
+ "name": "partyId",
17282
+ "anchor": "queueformatchoptions-partyid",
17283
+ "group": "rooms",
17284
+ "signature": "readonly partyId?: string",
17285
+ "documented": true,
17286
+ "summary": "A party to queue as one unit. Normally omit it — the SDK brings the\nplayer's party along when they lead one, exactly as a room call does.\n\nA party is seated together or not at all. If it gains or loses a member\nwhile it waits, the ticket ends `cancelled` with reason `party_changed`.",
17287
+ "deprecated": null,
17288
+ "params": [],
17289
+ "returns": null,
17290
+ "throws": null,
17291
+ "example": null,
17292
+ "defaultValue": null,
17293
+ "guides": []
17294
+ },
17295
+ {
17296
+ "id": "QueueForMatchOptions.signal",
17297
+ "name": "signal",
17298
+ "anchor": "queueformatchoptions-signal",
17299
+ "group": "rooms",
17300
+ "signature": "readonly signal?: AbortSignal",
17301
+ "documented": true,
17302
+ "summary": "Cancel the request that creates the ticket.\n\nOnly that request. Once you hold a ticket, [MatchTicket.cancel](api-reference-game.md#matchticket-cancel) is\nwhat takes the player out of the queue.",
17303
+ "deprecated": null,
17304
+ "params": [],
17305
+ "returns": null,
17306
+ "throws": null,
17307
+ "example": null,
17308
+ "defaultValue": null,
17309
+ "guides": []
17310
+ },
17311
+ {
17312
+ "id": "MatchTicketStatus",
17313
+ "name": "MatchTicketStatus",
17314
+ "anchor": "matchticketstatus",
17315
+ "group": "rooms",
17316
+ "signature": "type MatchTicketStatus = \"queued\" | \"matched\" | \"expired\" | \"cancelled\" | \"failed\" | \"lost\" | \"unavailable\"",
17317
+ "documented": true,
17318
+ "summary": "Where a ticket is: `queued` while it waits, and anything else for good.\n\n- `matched` — the player is seated and the match is starting.\n- `expired` — it waited too long, or stopped being kept alive.\n- `cancelled` — the player backed out, or their party changed.\n- `failed` — the platform formed a match and could not start it, repeatedly.\n- `lost` — the platform no longer has the ticket. Queue again.\n- `unavailable` — matchmaking is turned off or not answering.",
17319
+ "deprecated": null,
17320
+ "params": [],
17321
+ "returns": null,
17322
+ "throws": null,
17323
+ "example": null,
17324
+ "defaultValue": null,
17325
+ "guides": []
17326
+ },
17327
+ {
17328
+ "id": "MatchTicketOutcome",
17329
+ "name": "MatchTicketOutcome",
17330
+ "anchor": "matchticketoutcome",
17331
+ "group": "rooms",
17332
+ "signature": "type MatchTicketOutcome = { status: \"matched\"; room: Room } | { status: \"expired\"; reason: string } | { status: \"cancelled\"; reason: string } | { status: \"failed\"; reason: string } | { status: \"lost\" } | { status: \"unavailable\" }",
17333
+ "documented": true,
17334
+ "summary": "How a ticket ended. Branch on `status`.\n\nONLY `matched` CARRIES A ROOM, and by the time you see it the SDK is already\nin that room: `gem.rooms.currentRoom` is it, `joined` has fired, and the\nmatch events follow as they do for any other room.\n\nEvery other ending leaves the player in no room and on no ticket, free to\nqueue again. None of them is retried for you, and nothing falls back to\n`quickJoin` — a game that wants either does it on purpose.\n\n`reason` says why, where the platform gave one: `timed_out` or\n`heartbeat_lapsed` for `expired`, `requested` or `party_changed` for\n`cancelled`, `handoff_failed` for `failed`. Treat a value you do not\nrecognise as the status alone.",
17335
+ "deprecated": null,
17336
+ "params": [],
17337
+ "returns": null,
17338
+ "throws": null,
17339
+ "example": null,
17340
+ "defaultValue": null,
17341
+ "guides": []
17342
+ },
17343
+ {
17344
+ "id": "MatchTicket",
17345
+ "name": "MatchTicket",
17346
+ "anchor": "matchticket",
17347
+ "group": "rooms",
17348
+ "signature": "interface MatchTicket",
17349
+ "documented": true,
17350
+ "summary": "A place in the matchmaking queue — what `gem.rooms.queueForMatch(...)`\nanswers with.\n\nYou do not keep it alive; the SDK does, for as long as it waits. What you do\nwith it is wait for [MatchTicket.outcome](api-reference-game.md#matchticket-outcome), and offer the player a\nbutton that calls [MatchTicket.cancel](api-reference-game.md#matchticket-cancel).\n\n```js\nconst ticket = await gem.rooms.queueForMatch({ templateSlugs: ['duel'] });\nshowSearching({ onCancel: () => ticket.cancel() });\n\nconst outcome = await ticket.outcome;\nif (outcome.status === 'matched') enterMatch(outcome.room);\nelse backToMenu(outcome.status);\n```",
17351
+ "deprecated": null,
17352
+ "params": [],
17353
+ "returns": null,
17354
+ "throws": null,
17355
+ "example": null,
17356
+ "defaultValue": null,
17357
+ "guides": [
17358
+ {
17359
+ "slug": "rooms-and-matches",
17360
+ "anchor": null
17361
+ }
17362
+ ]
17363
+ },
17364
+ {
17365
+ "id": "MatchTicket.id",
17366
+ "name": "id",
17367
+ "anchor": "matchticket-id",
17368
+ "group": "rooms",
17369
+ "signature": "readonly id: string",
17370
+ "documented": true,
17371
+ "summary": "The ticket's id. For logs and bug reports; nothing you call takes it.",
17372
+ "deprecated": null,
17373
+ "params": [],
17374
+ "returns": null,
17375
+ "throws": null,
17376
+ "example": null,
17377
+ "defaultValue": null,
17378
+ "guides": []
17379
+ },
17380
+ {
17381
+ "id": "MatchTicket.status",
17382
+ "name": "status",
17383
+ "anchor": "matchticket-status",
17384
+ "group": "rooms",
17385
+ "signature": "readonly status: MatchTicketStatus",
17386
+ "documented": true,
17387
+ "summary": "Where the ticket is right now. Changes once, from `queued` to how it ended.",
17388
+ "deprecated": null,
17389
+ "params": [],
17390
+ "returns": null,
17391
+ "throws": null,
17392
+ "example": null,
17393
+ "defaultValue": null,
17394
+ "guides": []
17395
+ },
17396
+ {
17397
+ "id": "MatchTicket.partySize",
17398
+ "name": "partySize",
17399
+ "anchor": "matchticket-partysize",
17400
+ "group": "rooms",
17401
+ "signature": "readonly partySize: number",
17402
+ "documented": true,
17403
+ "summary": "How many players the ticket seats: 1, or the size of the party.",
17404
+ "deprecated": null,
17405
+ "params": [],
17406
+ "returns": null,
17407
+ "throws": null,
17408
+ "example": null,
17409
+ "defaultValue": null,
17410
+ "guides": []
17411
+ },
17412
+ {
17413
+ "id": "MatchTicket.queuedAt",
17414
+ "name": "queuedAt",
17415
+ "anchor": "matchticket-queuedat",
17416
+ "group": "rooms",
17417
+ "signature": "readonly queuedAt: number",
17418
+ "documented": true,
17419
+ "summary": "When the ticket joined the queue, in Unix seconds.",
17420
+ "deprecated": null,
17421
+ "params": [],
17422
+ "returns": null,
17423
+ "throws": null,
17424
+ "example": null,
17425
+ "defaultValue": null,
17426
+ "guides": []
17427
+ },
17428
+ {
17429
+ "id": "MatchTicket.expiresAt",
17430
+ "name": "expiresAt",
17431
+ "anchor": "matchticket-expiresat",
17432
+ "group": "rooms",
17433
+ "signature": "readonly expiresAt: number",
17434
+ "documented": true,
17435
+ "summary": "When the ticket gives up if it is still waiting, in Unix seconds. Good for\na countdown; the platform decides the length, per template.",
17436
+ "deprecated": null,
17437
+ "params": [],
17438
+ "returns": null,
17439
+ "throws": null,
17440
+ "example": null,
17441
+ "defaultValue": null,
17442
+ "guides": []
17443
+ },
17444
+ {
17445
+ "id": "MatchTicket.outcome",
17446
+ "name": "outcome",
17447
+ "anchor": "matchticket-outcome",
17448
+ "group": "rooms",
17449
+ "signature": "readonly outcome: Promise<MatchTicketOutcome>",
17450
+ "documented": true,
17451
+ "summary": "How the ticket ends. Settles exactly once, and RESOLVES for every ending\nthe platform reports — matched, expired, cancelled and the rest are\noutcomes to branch on, not errors to catch.\n\nIt rejects in one case only: the ticket was matched and the SDK could not\nread the room it was seated in. The player holds a seat then, so\n`gem.rooms.myRooms()` shows the room and `gem.rooms.join(id)` enters it.",
17452
+ "deprecated": null,
17453
+ "params": [],
17454
+ "returns": null,
17455
+ "throws": null,
17456
+ "example": null,
17457
+ "defaultValue": null,
17458
+ "guides": []
17459
+ },
17460
+ {
17461
+ "id": "MatchTicket.cancel",
17462
+ "name": "cancel",
17463
+ "anchor": "matchticket-cancel",
17464
+ "group": "rooms",
17465
+ "signature": "cancel(): Promise<MatchTicketOutcome>",
17466
+ "documented": true,
17467
+ "summary": "Take the player — and their party — out of the queue.\n\nA CANCEL CAN LOSE THE RACE, and then the answer is `matched`. A ticket the\nplatform has already seated is never undone, so read the outcome this\nreturns rather than assuming: when it says `matched` the player is in a\nroom, and backing out is `gem.rooms.leave()`.\n\nSafe to call more than once, and after the ticket has ended.",
17468
+ "deprecated": null,
17469
+ "params": [],
17470
+ "returns": "The same promise as [MatchTicket.outcome](api-reference-game.md#matchticket-outcome).",
17471
+ "throws": null,
17472
+ "example": null,
17473
+ "defaultValue": null,
17474
+ "guides": []
17475
+ },
17086
17476
  {
17087
17477
  "id": "RoomStatus",
17088
17478
  "name": "RoomStatus",
package/docs/changelog.md CHANGED
@@ -16,6 +16,32 @@ 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
+ ## 10.1.0
20
+
21
+ Upgrading from 10.0.0. Impact: required.
22
+
23
+ ### Rebuild
24
+
25
+ - The host-side method for sending preferences now accepts an optional recommended object with the five default settings. Existing calls remain valid, so recompiling is enough. Pass the new field only if your test host should simulate the Arcade's recommended defaults.
26
+
27
+ ### New capability
28
+
29
+ - GemPrefs now carries an optional recommended field holding the player's Arcade-wide defaults as advice: volume from 0 to 100, battery saver, keyboard layout (wasd or arrows), gamepad confirm button (south or east) and vibration. It is null when the Arcade does not send it. Changes arrive through the existing preferences callback, and the latest value is always available on the current preferences. Use these values as starting points when the player has no saved choice in your game, and never overwrite a choice the player made in your own settings. Muted, reduced motion and frame stats stay where they were.
30
+ - A new GemRecommendedSettings type describes the recommended defaults object. You can use it to type your own code that reads or stores the recommended values. The object is frozen, so copy values into your own settings rather than modifying it.
31
+
32
+ ## 10.0.0
33
+
34
+ Upgrading from 9.1.4. Impact: required.
35
+
36
+ ### Rebuild
37
+
38
+ - The OrderSource type now accepts two more values, test_account_provision and developer_grant. Recompile your game. If your code handles order sources in an exhaustive switch or a lookup table typed by OrderSource, add handling for both new values, or the build may fail to compile.
39
+
40
+ ### New capability
41
+
42
+ - A game can now put a player in a matchmaking queue for a mode that needs a formed lobby. Call RoomManager.queueForMatch with the template slugs you want. It returns a MatchTicket whose outcome settles once and whose cancel method leaves the queue. When the platform finds a match it creates the room, seats everyone and starts the match, and you follow it with the same code you use for any joined room. Options are described by QueueForMatchOptions. Ticket endings are described by MatchTicketOutcome, and the waiting state by MatchTicketStatus. RoomManager.currentTicket exposes the ticket the player is waiting on. The SDK keeps the ticket alive by itself, cancels it when the tab closes, and lets a player queue from inside a room. After a match the player leaves the room they queued from when the tab closes.
43
+ - The lower-level rooms API now has createTicket, heartbeatTicket and cancelTicket calls for working with matchmaking tickets directly. Most games should use RoomManager.queueForMatch, which sends the heartbeats and cancellation for you.
44
+
19
45
  ## 9.1.4
20
46
 
21
47
  Upgrading from 9.1.3. Impact: none.