@indiegems/gem-web-sdk 21.1.1 → 23.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.
@@ -1,6 +1,6 @@
1
1
  # The function surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/function` v21.1.1, 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/function` v23.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
 
@@ -2039,8 +2039,9 @@ Throw away what is cached, so the next [InventoryApi.snapshot](#inventoryapi-sna
2039
2039
  asks.
2040
2040
 
2041
2041
  For when something you did not do changed the inventory — a purchase on
2042
- another device, a grant from a server. The SDK already drops the cache
2043
- itself after any change it made.
2042
+ another device, a grant from a server — and nothing told you. The SDK
2043
+ already drops the cache itself after any change it made, and on an
2044
+ Arcade's hint that this game's inventory changed on the server.
2044
2045
 
2045
2046
  <a id="inventoryapi-onchanged"></a>
2046
2047
 
@@ -2053,12 +2054,17 @@ onChanged(listener: () => void): () => void
2053
2054
  Run something whenever this inventory changes, and get back a function that
2054
2055
  stops listening.
2055
2056
 
2056
- ⚠️ LOCAL CHANGES ONLY — this fires for changes THIS game made through this
2057
- SDK. It does NOT fire when an inventory changes anywhere else: another
2058
- device, a game server, an admin tool, or a gift from another player. Do not
2059
- build "keep the bag live" on it; use it to react to your own writes, and
2060
- call [InventoryApi.refresh](#inventoryapi-refresh) when you have reason to think something
2061
- else moved.
2057
+ Fires for every change THIS game made through this SDK, and — on an Arcade
2058
+ that relays it — for any change to this game's inventory on the server: a
2059
+ grant from your function or server, a purchase, a gift accepted, a support
2060
+ grant, another device. It is a cue to re-read, not a receipt: it carries
2061
+ nothing, so call [InventoryApi.snapshot](#inventoryapi-snapshot) for what changed. After your
2062
+ own function's grant, show the function's answer and let this redraw.
2063
+
2064
+ An older Arcade sends no hint, and there it fires for your own writes
2065
+ only. Keep a fallback that does not depend on it: refresh on
2066
+ `visibilitychange` to `visible` and after any action that could involve
2067
+ another player. Do not poll.
2062
2068
 
2063
2069
  | Parameter | Type |
2064
2070
  | --- | --- |
@@ -5030,6 +5036,31 @@ readonly configVersion?: number
5030
5036
  On `template_not_on_channel`, when a config version is promoted to that
5031
5037
  channel: the version the template is missing from.
5032
5038
 
5039
+ <a id="playinviterequest"></a>
5040
+
5041
+ ## PlayInviteRequest
5042
+
5043
+ ```ts
5044
+ interface PlayInviteRequest
5045
+ ```
5046
+
5047
+ What `gem.friends.inviteToPlay` takes.
5048
+
5049
+ <a id="playinviterequest-params"></a>
5050
+
5051
+ ## PlayInviteRequest.params
5052
+
5053
+ ```ts
5054
+ readonly params?: Readonly<Record<string, string>>
5055
+ ```
5056
+
5057
+ Your own launch parameters for what the Arcade's link and invites open —
5058
+ `{ room: joinCode }` for a room joined by code — exactly as they should
5059
+ reach your game, on `gem.share.request`'s rules: at most 16, keys a letter
5060
+ then up to 63 letters, digits, `_` or `-`, string values of at most 512
5061
+ characters. For this one opening, they replace what
5062
+ `gem.friends.setInviteParams` declared. Omit it to use that declaration.
5063
+
5033
5064
  <a id="friendsapi"></a>
5034
5065
 
5035
5066
  ## FriendsApi
@@ -5144,6 +5175,95 @@ challengeButton.onclick = async () => {
5144
5175
  };
5145
5176
  ```
5146
5177
 
5178
+ <a id="friendsapi-invitetoplay"></a>
5179
+
5180
+ ## FriendsApi.inviteToPlay
5181
+
5182
+ ```ts
5183
+ inviteToPlay(request?: PlayInviteRequest): Promise<boolean>
5184
+ ```
5185
+
5186
+ Ask the Arcade to open ITS invite sheet, so the player can invite friends
5187
+ to come and play this game with them.
5188
+
5189
+ The Arcade lists the player's friends in its own chrome and sends an
5190
+ invite only when the player presses Invite there. A friend who accepts is
5191
+ opened into this game by the Arcade, and their quick-join seats them in
5192
+ the player's room when there is a seat: your game makes no call for it,
5193
+ and reads them as a room member who shares the player's `groupId`. Your
5194
+ game names nobody and writes no text.
5195
+
5196
+ An Arcade that does not offer the sheet answers `false` at once, and asks
5197
+ nothing. Hide the button on `gem.frame.parentSupports('playInvites')`
5198
+ rather than letting a player press one that cannot work.
5199
+
5200
+ ⚠️ **Call it ONLY from a player's press**, never on a timer or as a screen
5201
+ opens. The Arcade answers an ask with no press behind it `false`, and
5202
+ shows nothing.
5203
+
5204
+ RESOLVES `true` when the Arcade opened its sheet, and `false` when it did
5205
+ not: outside the Arcade, without `playInvites`, with another sheet
5206
+ already open, without a press, or when no answer arrived. **That is all it
5207
+ says** — never whom the player invited, how many, or whether any invite
5208
+ was sent — so never gate a reward on it. Never rejects.
5209
+
5210
+ Calls made while an ask is open share its answer.
5211
+
5212
+ **The window's share link and its invites are one system.** Pass
5213
+ `params` — your own launch parameters, `{ room: joinCode }` for a room
5214
+ joined by code — and a friend who accepts and anyone who opens the link
5215
+ arrive with them, on `gem.share.request`'s rules. Omitted, the window uses
5216
+ what [FriendsApi.setInviteParams](#friendsapi-setinviteparams) declared. A param those rules
5217
+ refuse is said on the console and nothing is asked: the call resolves
5218
+ `false`.
5219
+
5220
+ | Parameter | Type |
5221
+ | --- | --- |
5222
+ | `request` | `PlayInviteRequest` |
5223
+
5224
+ ```ts
5225
+ inviteButton.onclick = async () => {
5226
+ const shown = await gem.friends.inviteToPlay({ params: { room: joinCode } });
5227
+ if (!shown) showJoinCode(); // the Arcade's sheet did not open: your own fallback
5228
+ };
5229
+ ```
5230
+
5231
+ <a id="friendsapi-setinviteparams"></a>
5232
+
5233
+ ## FriendsApi.setInviteParams
5234
+
5235
+ ```ts
5236
+ setInviteParams(params: Readonly<Record<string, string>> | null): boolean
5237
+ ```
5238
+
5239
+ Say what the Arcade's Friends window opens when the PLAYER opens it from
5240
+ the Arcade's own menu over your game: your launch parameters, carried by
5241
+ its share link and its friend invites alike, on `gem.share.request`'s
5242
+ rules. Pass `null` to clear.
5243
+
5244
+ Call it when the player enters a room joined by code —
5245
+ `setInviteParams({ room: joinCode })` — and `setInviteParams(null)` when
5246
+ they leave it, so the window never invites anyone into a room that is
5247
+ gone. The latest call stands until the next; the SDK holds it and says it
5248
+ again after a reconnect. An explicit `inviteToPlay({ params })` wins for
5249
+ that one opening. Nothing comes back, and nothing changes on screen.
5250
+
5251
+ | Parameter | Type |
5252
+ | --- | --- |
5253
+ | `params` | `Readonly<Record<string, string>> \| null` |
5254
+
5255
+ **Returns.** whether the declaration reached the Arcade now. `false` outside
5256
+ the Arcade, without `playInvites`, before the bridge is up (it is sent on
5257
+ connect) and for params the share rules refuse, which are said on the
5258
+ console; the last declaration then stands.
5259
+
5260
+ ```ts
5261
+ // On entering a room joined by code:
5262
+ gem.friends.setInviteParams({ room: joinCode });
5263
+ // ...and on leaving it:
5264
+ gem.friends.setInviteParams(null);
5265
+ ```
5266
+
5147
5267
  <a id="friendsapi-inbox"></a>
5148
5268
 
5149
5269
  ## FriendsApi.inbox
@@ -1,6 +1,6 @@
1
1
  # The browser surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v21.1.1, 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` v23.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
 
@@ -907,8 +907,9 @@ Throw away what is cached, so the next [InventoryApi.snapshot](#inventoryapi-sna
907
907
  asks.
908
908
 
909
909
  For when something you did not do changed the inventory — a purchase on
910
- another device, a grant from a server. The SDK already drops the cache
911
- itself after any change it made.
910
+ another device, a grant from a server — and nothing told you. The SDK
911
+ already drops the cache itself after any change it made, and on an
912
+ Arcade's hint that this game's inventory changed on the server.
912
913
 
913
914
  <a id="inventoryapi-onchanged"></a>
914
915
 
@@ -921,12 +922,17 @@ onChanged(listener: () => void): () => void
921
922
  Run something whenever this inventory changes, and get back a function that
922
923
  stops listening.
923
924
 
924
- ⚠️ LOCAL CHANGES ONLY — this fires for changes THIS game made through this
925
- SDK. It does NOT fire when an inventory changes anywhere else: another
926
- device, a game server, an admin tool, or a gift from another player. Do not
927
- build "keep the bag live" on it; use it to react to your own writes, and
928
- call [InventoryApi.refresh](#inventoryapi-refresh) when you have reason to think something
929
- else moved.
925
+ Fires for every change THIS game made through this SDK, and — on an Arcade
926
+ that relays it — for any change to this game's inventory on the server: a
927
+ grant from your function or server, a purchase, a gift accepted, a support
928
+ grant, another device. It is a cue to re-read, not a receipt: it carries
929
+ nothing, so call [InventoryApi.snapshot](#inventoryapi-snapshot) for what changed. After your
930
+ own function's grant, show the function's answer and let this redraw.
931
+
932
+ An older Arcade sends no hint, and there it fires for your own writes
933
+ only. Keep a fallback that does not depend on it: refresh on
934
+ `visibilitychange` to `visible` and after any action that could involve
935
+ another player. Do not poll.
930
936
 
931
937
  | Parameter | Type |
932
938
  | --- | --- |
@@ -7180,6 +7186,24 @@ The platform currency's slug, as `gem.platform.currencies.get()` lists it —
7180
7186
  `GEMS` or `ARCADE_TICKETS`. It decides what flies — gems or coins — and
7181
7187
  which of the player's totals the payout moves.
7182
7188
 
7189
+ <a id="rewardcue-cueid"></a>
7190
+
7191
+ ## RewardCue.cueId
7192
+
7193
+ ```ts
7194
+ cueId?: string
7195
+ ```
7196
+
7197
+ Your id for this award, so the arcade flies it once. Make one per award
7198
+ with `crypto.randomUUID()` and pass the same one as the `cueId` of the
7199
+ `gem.functions.invoke` that pays it; then the cue and the payout may reach
7200
+ the arcade in either order. 1 to 64 letters, digits, `_` or `-`; anything
7201
+ else is left out (with a console warning, from `gem.cueReward`), and the
7202
+ cue still goes.
7203
+
7204
+ For animation only: it moves no balance. Leave it out and the arcade pairs
7205
+ the cue with the payout by currency and timing.
7206
+
7183
7207
  <a id="rewardcue-expected"></a>
7184
7208
 
7185
7209
  ## RewardCue.expected
@@ -7238,6 +7262,18 @@ A corner of your frame, for `gem.setGemCorner`: where you would like the
7238
7262
  arcade's Gem button to sit. `'top-left'` is where it sits unless you ask.
7239
7263
  Physical corners: `'top-left'` is the top-left in every language.
7240
7264
 
7265
+ <a id="rewardpresentation"></a>
7266
+
7267
+ ## RewardPresentation
7268
+
7269
+ ```ts
7270
+ type RewardPresentation = "arcade" | "game"
7271
+ ```
7272
+
7273
+ Who decides when your platform-currency payouts are shown landing, for
7274
+ `gem.setRewardPresentation`: `'arcade'`, the default, or `'game'`, when each
7275
+ payout waits for your `gem.cueReward`.
7276
+
7241
7277
  <a id="menustyle"></a>
7242
7278
 
7243
7279
  ## MenuStyle
@@ -7646,6 +7682,29 @@ its control.
7646
7682
  up, for an arcade that cannot move its control, for a value that is not
7647
7683
  one of the four corners, and past this frame's own limit.
7648
7684
 
7685
+ <a id="gemframe-setrewardpresentation"></a>
7686
+
7687
+ ## GemFrame.setRewardPresentation
7688
+
7689
+ ```ts
7690
+ setRewardPresentation(mode: RewardPresentation): boolean
7691
+ ```
7692
+
7693
+ Tell the arcade who decides when your platform-currency payouts are shown
7694
+ landing: the whole answer each time, the latest one winning.
7695
+
7696
+ Prefer `gem.setRewardPresentation`, which holds the answer across a
7697
+ reconnect. This one sends only while connected, and only to an arcade
7698
+ that can hold a payout for your cue.
7699
+
7700
+ | Parameter | Type |
7701
+ | --- | --- |
7702
+ | `mode` | `RewardPresentation` |
7703
+
7704
+ **Returns.** whether the message left the frame: `false` before the bridge is
7705
+ up, for an arcade that cannot hold a payout, for a value that is neither
7706
+ `'arcade'` nor `'game'`, and past this frame's own limit.
7707
+
7649
7708
  <a id="gemframe-setmenustyle"></a>
7650
7709
 
7651
7710
  ## GemFrame.setMenuStyle
@@ -8475,6 +8534,31 @@ readonly configVersion?: number
8475
8534
  On `template_not_on_channel`, when a config version is promoted to that
8476
8535
  channel: the version the template is missing from.
8477
8536
 
8537
+ <a id="playinviterequest"></a>
8538
+
8539
+ ## PlayInviteRequest
8540
+
8541
+ ```ts
8542
+ interface PlayInviteRequest
8543
+ ```
8544
+
8545
+ What `gem.friends.inviteToPlay` takes.
8546
+
8547
+ <a id="playinviterequest-params"></a>
8548
+
8549
+ ## PlayInviteRequest.params
8550
+
8551
+ ```ts
8552
+ readonly params?: Readonly<Record<string, string>>
8553
+ ```
8554
+
8555
+ Your own launch parameters for what the Arcade's link and invites open —
8556
+ `{ room: joinCode }` for a room joined by code — exactly as they should
8557
+ reach your game, on `gem.share.request`'s rules: at most 16, keys a letter
8558
+ then up to 63 letters, digits, `_` or `-`, string values of at most 512
8559
+ characters. For this one opening, they replace what
8560
+ `gem.friends.setInviteParams` declared. Omit it to use that declaration.
8561
+
8478
8562
  <a id="friendsapi"></a>
8479
8563
 
8480
8564
  ## FriendsApi
@@ -8589,6 +8673,95 @@ challengeButton.onclick = async () => {
8589
8673
  };
8590
8674
  ```
8591
8675
 
8676
+ <a id="friendsapi-invitetoplay"></a>
8677
+
8678
+ ## FriendsApi.inviteToPlay
8679
+
8680
+ ```ts
8681
+ inviteToPlay(request?: PlayInviteRequest): Promise<boolean>
8682
+ ```
8683
+
8684
+ Ask the Arcade to open ITS invite sheet, so the player can invite friends
8685
+ to come and play this game with them.
8686
+
8687
+ The Arcade lists the player's friends in its own chrome and sends an
8688
+ invite only when the player presses Invite there. A friend who accepts is
8689
+ opened into this game by the Arcade, and their quick-join seats them in
8690
+ the player's room when there is a seat: your game makes no call for it,
8691
+ and reads them as a room member who shares the player's `groupId`. Your
8692
+ game names nobody and writes no text.
8693
+
8694
+ An Arcade that does not offer the sheet answers `false` at once, and asks
8695
+ nothing. Hide the button on `gem.frame.parentSupports('playInvites')`
8696
+ rather than letting a player press one that cannot work.
8697
+
8698
+ ⚠️ **Call it ONLY from a player's press**, never on a timer or as a screen
8699
+ opens. The Arcade answers an ask with no press behind it `false`, and
8700
+ shows nothing.
8701
+
8702
+ RESOLVES `true` when the Arcade opened its sheet, and `false` when it did
8703
+ not: outside the Arcade, without `playInvites`, with another sheet
8704
+ already open, without a press, or when no answer arrived. **That is all it
8705
+ says** — never whom the player invited, how many, or whether any invite
8706
+ was sent — so never gate a reward on it. Never rejects.
8707
+
8708
+ Calls made while an ask is open share its answer.
8709
+
8710
+ **The window's share link and its invites are one system.** Pass
8711
+ `params` — your own launch parameters, `{ room: joinCode }` for a room
8712
+ joined by code — and a friend who accepts and anyone who opens the link
8713
+ arrive with them, on `gem.share.request`'s rules. Omitted, the window uses
8714
+ what [FriendsApi.setInviteParams](#friendsapi-setinviteparams) declared. A param those rules
8715
+ refuse is said on the console and nothing is asked: the call resolves
8716
+ `false`.
8717
+
8718
+ | Parameter | Type |
8719
+ | --- | --- |
8720
+ | `request` | `PlayInviteRequest` |
8721
+
8722
+ ```ts
8723
+ inviteButton.onclick = async () => {
8724
+ const shown = await gem.friends.inviteToPlay({ params: { room: joinCode } });
8725
+ if (!shown) showJoinCode(); // the Arcade's sheet did not open: your own fallback
8726
+ };
8727
+ ```
8728
+
8729
+ <a id="friendsapi-setinviteparams"></a>
8730
+
8731
+ ## FriendsApi.setInviteParams
8732
+
8733
+ ```ts
8734
+ setInviteParams(params: Readonly<Record<string, string>> | null): boolean
8735
+ ```
8736
+
8737
+ Say what the Arcade's Friends window opens when the PLAYER opens it from
8738
+ the Arcade's own menu over your game: your launch parameters, carried by
8739
+ its share link and its friend invites alike, on `gem.share.request`'s
8740
+ rules. Pass `null` to clear.
8741
+
8742
+ Call it when the player enters a room joined by code —
8743
+ `setInviteParams({ room: joinCode })` — and `setInviteParams(null)` when
8744
+ they leave it, so the window never invites anyone into a room that is
8745
+ gone. The latest call stands until the next; the SDK holds it and says it
8746
+ again after a reconnect. An explicit `inviteToPlay({ params })` wins for
8747
+ that one opening. Nothing comes back, and nothing changes on screen.
8748
+
8749
+ | Parameter | Type |
8750
+ | --- | --- |
8751
+ | `params` | `Readonly<Record<string, string>> \| null` |
8752
+
8753
+ **Returns.** whether the declaration reached the Arcade now. `false` outside
8754
+ the Arcade, without `playInvites`, before the bridge is up (it is sent on
8755
+ connect) and for params the share rules refuse, which are said on the
8756
+ console; the last declaration then stands.
8757
+
8758
+ ```ts
8759
+ // On entering a room joined by code:
8760
+ gem.friends.setInviteParams({ room: joinCode });
8761
+ // ...and on leaving it:
8762
+ gem.friends.setInviteParams(null);
8763
+ ```
8764
+
8592
8765
  <a id="friendsapi-inbox"></a>
8593
8766
 
8594
8767
  ## FriendsApi.inbox
@@ -8726,6 +8899,24 @@ readonly signal?: AbortSignal
8726
8899
 
8727
8900
  Cancel the call. A call already cancelled sends nothing.
8728
8901
 
8902
+ <a id="functioninvokeoptions-cueid"></a>
8903
+
8904
+ ## FunctionInvokeOptions.cueId
8905
+
8906
+ ```ts
8907
+ readonly cueId?: string
8908
+ ```
8909
+
8910
+ The id of the reward this call pays, so the Gem Arcade flies it once:
8911
+ the same `cueId` you pass to `gem.cueReward` for it, one
8912
+ `crypto.randomUUID()` per award. The platform hands it back on the payout
8913
+ it shows the arcade, and nowhere else: your function never sees it, and
8914
+ neither the answer nor a receipt carries it.
8915
+
8916
+ 1 to 64 letters, digits, `_` or `-`. Anything else is left out with a
8917
+ console warning, and the call still goes: it decides an animation, never
8918
+ whether the call pays.
8919
+
8729
8920
  <a id="functionresult"></a>
8730
8921
 
8731
8922
  ## FunctionResult
@@ -9004,11 +9195,26 @@ The call is made once. It is never repeated for you, because your function
9004
9195
  may have granted or written something: see [GemFunctionError.retryable](#gemfunctionerror-retryable)
9005
9196
  for whether repeating it could help.
9006
9197
 
9198
+ The answer is the receipt: when this resolves, what your function says it
9199
+ granted has been written. A game shows it at once and re-reads the
9200
+ inventory in the background (`gem.me.inventory.refresh()`, then
9201
+ `snapshot()`, or on `gem.me.inventory.onChanged`); never hold the player on
9202
+ a loop that re-reads until the grant shows.
9203
+
9007
9204
  ```js
9008
9205
  const result = await gem.functions.invoke({ v: 2, runId, finishMs });
9009
9206
  const { accepted, why } = result.json();
9010
9207
  ```
9011
9208
 
9209
+ When the call pays a reward the player sees, pass the reward's `cueId`, the
9210
+ same one the page gives `gem.cueReward` for it, and the Gem Arcade flies it
9211
+ once:
9212
+
9213
+ ```js
9214
+ const cueId = crypto.randomUUID(); // one per award, also passed to the cue
9215
+ await gem.functions.invoke({ claim: 'daily' }, { cueId });
9216
+ ```
9217
+
9012
9218
  | Parameter | Type | Description |
9013
9219
  | --- | --- | --- |
9014
9220
  | `input` | `unknown` | Bytes, or a value to send as JSON. |
@@ -11935,10 +12141,12 @@ readonly friends: FriendsApi
11935
12141
  ```
11936
12142
 
11937
12143
  The signed-in player's accepted friends — display identity only,
11938
- read-only, paginated — and the one ask that reaches past them:
12144
+ read-only, paginated — and the asks that reach past them:
11939
12145
  [FriendsApi.challenge](#friendsapi-challenge), which asks the Arcade to send a friend a
11940
- challenge after the player confirms it in the Arcade's own sheet. The
11941
- receiving side reads the challenges this player holds in this game:
12146
+ challenge after the player confirms it in the Arcade's own sheet, and
12147
+ [FriendsApi.inviteToPlay](#friendsapi-invitetoplay), which asks the Arcade to open its own
12148
+ invite-to-play sheet. The receiving side reads the challenges this player
12149
+ holds in this game:
11942
12150
  [FriendsApi.launchedChallenge](#friendsapi-launchedchallenge) for the one this frame was launched
11943
12151
  with, [FriendsApi.inbox](#friendsapi-inbox) for all of them, and
11944
12152
  [FriendsApi.markAnswered](#friendsapi-markanswered) once one has been played.
@@ -12231,6 +12439,30 @@ gem.cueReward({
12231
12439
  });
12232
12440
  ```
12233
12441
 
12442
+ **When your function pays it, give both calls one `cueId`.** Make one per
12443
+ award with `crypto.randomUUID()`, pass it here and to the
12444
+ `gem.functions.invoke` that pays, and the arcade flies that reward once,
12445
+ whichever reaches it first — so you may cue before the call or after it
12446
+ returns:
12447
+
12448
+ ```js
12449
+ const cueId = crypto.randomUUID();
12450
+ const result = await gem.functions.invoke({ claim: 'daily' }, { cueId });
12451
+ gem.cueReward({ currencySlug: 'GEMS', expected: 15, origin, cueId });
12452
+ ```
12453
+
12454
+ A payout that is not a function call (your own game server's) has no id
12455
+ to share: cue it with none, and the arcade pairs the two by currency and
12456
+ timing.
12457
+
12458
+ **When is up to the arcade unless you say otherwise.** By default the cue
12459
+ is paired with its payout only within about half a second either side of
12460
+ your server's write; a payout with no cue by then flies from the middle of
12461
+ your frame, and a cue that comes later adds nothing. To show the payout at
12462
+ a later moment of your own, such as a Claim tap on the win card or the
12463
+ screen the player returns to, call `gem.setRewardPresentation('game')` once
12464
+ at boot: each payout then waits for your cue.
12465
+
12234
12466
  **This is the platform-currency animation, and the only one.** The display
12235
12467
  of every platform currency — Gems and Coins alike — belongs to the
12236
12468
  arcade: a game draws no flight or celebration of its own for one and keeps
@@ -12252,6 +12484,53 @@ landed — which it does on every channel, dev and test included.
12252
12484
  before the bridge is up, or under an embedder with no wallet. On `false`
12253
12485
  the game does nothing: it draws no animation of its own in its place.
12254
12486
 
12487
+ <a id="gem-setrewardpresentation"></a>
12488
+
12489
+ ## Gem.setRewardPresentation
12490
+
12491
+ ```ts
12492
+ setRewardPresentation(mode: RewardPresentation): boolean
12493
+ ```
12494
+
12495
+ Say who decides when your platform-currency payouts are shown landing: the
12496
+ arcade (`'arcade'`, the default) or your game (`'game'`). Call it once at
12497
+ boot when your reward moment comes after your server pays, such as a Claim
12498
+ tap on the win card or the next screen the player sees.
12499
+
12500
+ ```js
12501
+ gem.setRewardPresentation('game'); // at boot
12502
+ // ...the server pays when the board is won; later, on Claim:
12503
+ gem.cueReward({ currencySlug: 'ARCADE_TICKETS', expected: 20, origin });
12504
+ ```
12505
+
12506
+ **With `'game'`, your cue supplies the moment and the payout supplies the
12507
+ number.** The arcade holds each payout's presentation for its currency
12508
+ rather than flying it from the middle of your frame. Each `cueReward`
12509
+ releases up to its `expected` of what is held (all of it when it names no
12510
+ amount) and flies it from its `origin`, and the arcade's wallet chip over
12511
+ your game counts up as the marks land. A cue that finds nothing held waits
12512
+ for the next payout of its currency, so cue before or after the write, in
12513
+ either order.
12514
+
12515
+ **It delays the show, never the money.** What is paid, when, and every
12516
+ ceiling are unchanged, and the arcade's menu and every purchase see the
12517
+ player's true total at once. A payout no cue releases within five minutes
12518
+ lands quietly, with no flight, so a game can never hide one.
12519
+
12520
+ **It is screen state, not a toggle**, held before the bridge is up and
12521
+ re-sent after every reconnect. Switching back to `'arcade'` lands whatever
12522
+ is held. A value that is neither is ignored, with a warning on the console,
12523
+ and changes nothing.
12524
+
12525
+ | Parameter | Type | Description |
12526
+ | --- | --- | --- |
12527
+ | `mode` | `RewardPresentation` | `'game'` to present each payout on your cue, `'arcade'` for the default. |
12528
+
12529
+ **Returns.** `true` when the arcade was told now; `false` before the bridge is
12530
+ up (the answer is held and sent when it connects), under an embedder
12531
+ that presents every payout its own way, and for a value that is neither.
12532
+ On `false` from an embedder, keep cueing as usual: nothing else changes.
12533
+
12255
12534
  <a id="gem-setwalletvisible"></a>
12256
12535
 
12257
12536
  ## Gem.setWalletVisible
@@ -19951,7 +20230,7 @@ readonly playWith: PlayWithOutcome | null
19951
20230
 
19952
20231
  What the last [RoomManager.quickJoin](#roommanager-quickjoin) did when the Arcade opened
19953
20232
  your game to play with someone — a friend whose Join the player pressed,
19954
- or their party's leader. Read-only.
20233
+ a friend's invite, or their party's leader. Read-only.
19955
20234
 
19956
20235
  - `'joined'` — the player was seated in that person's room, as a member
19957
20236
  sharing their `groupId`.
@@ -1,6 +1,6 @@
1
1
  # The server surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v21.1.1, 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` v23.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
 
@@ -1489,6 +1489,24 @@ readonly signal?: AbortSignal
1489
1489
 
1490
1490
  Cancel the call. A call already cancelled sends nothing.
1491
1491
 
1492
+ <a id="functioninvokeoptions-cueid"></a>
1493
+
1494
+ ## FunctionInvokeOptions.cueId
1495
+
1496
+ ```ts
1497
+ readonly cueId?: string
1498
+ ```
1499
+
1500
+ The id of the reward this call pays, so the Gem Arcade flies it once:
1501
+ the same `cueId` you pass to `gem.cueReward` for it, one
1502
+ `crypto.randomUUID()` per award. The platform hands it back on the payout
1503
+ it shows the arcade, and nowhere else: your function never sees it, and
1504
+ neither the answer nor a receipt carries it.
1505
+
1506
+ 1 to 64 letters, digits, `_` or `-`. Anything else is left out with a
1507
+ console warning, and the call still goes: it decides an animation, never
1508
+ whether the call pays.
1509
+
1492
1510
  <a id="functionresult"></a>
1493
1511
 
1494
1512
  ## FunctionResult
@@ -1767,11 +1785,26 @@ The call is made once. It is never repeated for you, because your function
1767
1785
  may have granted or written something: see [GemFunctionError.retryable](#gemfunctionerror-retryable)
1768
1786
  for whether repeating it could help.
1769
1787
 
1788
+ The answer is the receipt: when this resolves, what your function says it
1789
+ granted has been written. A game shows it at once and re-reads the
1790
+ inventory in the background (`gem.me.inventory.refresh()`, then
1791
+ `snapshot()`, or on `gem.me.inventory.onChanged`); never hold the player on
1792
+ a loop that re-reads until the grant shows.
1793
+
1770
1794
  ```js
1771
1795
  const result = await gem.functions.invoke({ v: 2, runId, finishMs });
1772
1796
  const { accepted, why } = result.json();
1773
1797
  ```
1774
1798
 
1799
+ When the call pays a reward the player sees, pass the reward's `cueId`, the
1800
+ same one the page gives `gem.cueReward` for it, and the Gem Arcade flies it
1801
+ once:
1802
+
1803
+ ```js
1804
+ const cueId = crypto.randomUUID(); // one per award, also passed to the cue
1805
+ await gem.functions.invoke({ claim: 'daily' }, { cueId });
1806
+ ```
1807
+
1775
1808
  | Parameter | Type | Description |
1776
1809
  | --- | --- | --- |
1777
1810
  | `input` | `unknown` | Bytes, or a value to send as JSON. |