@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.
- package/CHANGELOG.md +61 -0
- package/dist/conformance.d.ts +4 -1
- package/dist/function.d.ts +10 -1
- package/dist/game.d.ts +46 -2
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +3 -3
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server-api.js +1 -1
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +66 -6
- package/dist/sdk.d.ts +47 -2
- package/dist/server-api.d.ts +1 -0
- package/dist/server.d.ts +3 -0
- package/docs/api/api-reference-function.md +129 -9
- package/docs/api/api-reference-game.md +292 -13
- package/docs/api/api-reference-server.md +34 -1
- package/docs/api/reference.json +276 -10
- package/docs/arcade-economy.md +3 -1
- package/docs/changelog.md +61 -0
- package/docs/daily-challenges.md +3 -1
- package/docs/friends.md +120 -15
- package/docs/getting-started.md +7 -1
- package/docs/inventory-and-catalogs.md +86 -13
- package/docs/manifest.json +22 -22
- package/docs/rooms-and-matches.md +6 -3
- package/docs/server-bundles.md +2 -2
- package/docs/serverless-functions.md +38 -0
- package/docs/trading-and-gifting.md +12 -9
- package/docs/what-a-game-must-do.md +22 -1
- package/package.json +1 -1
- package/schema/bridge.v1.json +293 -1
- package/schema/capabilities.public.json +20 -0
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 2,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "23.0.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -1155,7 +1155,7 @@
|
|
|
1155
1155
|
"group": "saves",
|
|
1156
1156
|
"signature": "refresh(): void",
|
|
1157
1157
|
"documented": true,
|
|
1158
|
-
"summary": "Throw away what is cached, so the next [InventoryApi.snapshot](api-reference-game.md#inventoryapi-snapshot) really\nasks.\n\nFor when something you did not do changed the inventory — a purchase on\nanother device, a grant from a server. The SDK
|
|
1158
|
+
"summary": "Throw away what is cached, so the next [InventoryApi.snapshot](api-reference-game.md#inventoryapi-snapshot) really\nasks.\n\nFor when something you did not do changed the inventory — a purchase on\nanother device, a grant from a server — and nothing told you. The SDK\nalready drops the cache itself after any change it made, and on an\nArcade's hint that this game's inventory changed on the server.",
|
|
1159
1159
|
"deprecated": null,
|
|
1160
1160
|
"params": [],
|
|
1161
1161
|
"returns": null,
|
|
@@ -1171,7 +1171,7 @@
|
|
|
1171
1171
|
"group": "saves",
|
|
1172
1172
|
"signature": "onChanged(listener: () => void): () => void",
|
|
1173
1173
|
"documented": true,
|
|
1174
|
-
"summary": "Run something whenever this inventory changes, and get back a function that\nstops listening.\n\
|
|
1174
|
+
"summary": "Run something whenever this inventory changes, and get back a function that\nstops listening.\n\nFires for every change THIS game made through this SDK, and — on an Arcade\nthat relays it — for any change to this game's inventory on the server: a\ngrant from your function or server, a purchase, a gift accepted, a support\ngrant, another device. It is a cue to re-read, not a receipt: it carries\nnothing, so call [InventoryApi.snapshot](api-reference-game.md#inventoryapi-snapshot) for what changed. After your\nown function's grant, show the function's answer and let this redraw.\n\nAn older Arcade sends no hint, and there it fires for your own writes\nonly. Keep a fallback that does not depend on it: refresh on\n`visibilitychange` to `visible` and after any action that could involve\nanother player. Do not poll.",
|
|
1175
1175
|
"deprecated": null,
|
|
1176
1176
|
"params": [
|
|
1177
1177
|
{
|
|
@@ -8556,6 +8556,22 @@
|
|
|
8556
8556
|
"defaultValue": null,
|
|
8557
8557
|
"guides": []
|
|
8558
8558
|
},
|
|
8559
|
+
{
|
|
8560
|
+
"id": "RewardCue.cueId",
|
|
8561
|
+
"name": "cueId",
|
|
8562
|
+
"anchor": "rewardcue-cueid",
|
|
8563
|
+
"group": "platform",
|
|
8564
|
+
"signature": "cueId?: string",
|
|
8565
|
+
"documented": true,
|
|
8566
|
+
"summary": "Your id for this award, so the arcade flies it once. Make one per award\nwith `crypto.randomUUID()` and pass the same one as the `cueId` of the\n`gem.functions.invoke` that pays it; then the cue and the payout may reach\nthe arcade in either order. 1 to 64 letters, digits, `_` or `-`; anything\nelse is left out (with a console warning, from `gem.cueReward`), and the\ncue still goes.\n\nFor animation only: it moves no balance. Leave it out and the arcade pairs\nthe cue with the payout by currency and timing.",
|
|
8567
|
+
"deprecated": null,
|
|
8568
|
+
"params": [],
|
|
8569
|
+
"returns": null,
|
|
8570
|
+
"throws": null,
|
|
8571
|
+
"example": null,
|
|
8572
|
+
"defaultValue": null,
|
|
8573
|
+
"guides": []
|
|
8574
|
+
},
|
|
8559
8575
|
{
|
|
8560
8576
|
"id": "RewardCue.expected",
|
|
8561
8577
|
"name": "expected",
|
|
@@ -8636,6 +8652,22 @@
|
|
|
8636
8652
|
"defaultValue": null,
|
|
8637
8653
|
"guides": []
|
|
8638
8654
|
},
|
|
8655
|
+
{
|
|
8656
|
+
"id": "RewardPresentation",
|
|
8657
|
+
"name": "RewardPresentation",
|
|
8658
|
+
"anchor": "rewardpresentation",
|
|
8659
|
+
"group": "platform",
|
|
8660
|
+
"signature": "type RewardPresentation = \"arcade\" | \"game\"",
|
|
8661
|
+
"documented": true,
|
|
8662
|
+
"summary": "Who decides when your platform-currency payouts are shown landing, for\n`gem.setRewardPresentation`: `'arcade'`, the default, or `'game'`, when each\npayout waits for your `gem.cueReward`.",
|
|
8663
|
+
"deprecated": null,
|
|
8664
|
+
"params": [],
|
|
8665
|
+
"returns": null,
|
|
8666
|
+
"throws": null,
|
|
8667
|
+
"example": null,
|
|
8668
|
+
"defaultValue": null,
|
|
8669
|
+
"guides": []
|
|
8670
|
+
},
|
|
8639
8671
|
{
|
|
8640
8672
|
"id": "MenuStyle",
|
|
8641
8673
|
"name": "MenuStyle",
|
|
@@ -9064,6 +9096,29 @@
|
|
|
9064
9096
|
"defaultValue": null,
|
|
9065
9097
|
"guides": []
|
|
9066
9098
|
},
|
|
9099
|
+
{
|
|
9100
|
+
"id": "GemFrame.setRewardPresentation",
|
|
9101
|
+
"name": "setRewardPresentation",
|
|
9102
|
+
"anchor": "gemframe-setrewardpresentation",
|
|
9103
|
+
"group": "frame",
|
|
9104
|
+
"signature": "setRewardPresentation(mode: RewardPresentation): boolean",
|
|
9105
|
+
"documented": true,
|
|
9106
|
+
"summary": "Tell the arcade who decides when your platform-currency payouts are shown\nlanding: the whole answer each time, the latest one winning.\n\nPrefer `gem.setRewardPresentation`, which holds the answer across a\nreconnect. This one sends only while connected, and only to an arcade\nthat can hold a payout for your cue.",
|
|
9107
|
+
"deprecated": null,
|
|
9108
|
+
"params": [
|
|
9109
|
+
{
|
|
9110
|
+
"name": "mode",
|
|
9111
|
+
"type": "RewardPresentation",
|
|
9112
|
+
"optional": false,
|
|
9113
|
+
"summary": null
|
|
9114
|
+
}
|
|
9115
|
+
],
|
|
9116
|
+
"returns": "whether the message left the frame: `false` before the bridge is\n up, for an arcade that cannot hold a payout, for a value that is neither\n `'arcade'` nor `'game'`, and past this frame's own limit.",
|
|
9117
|
+
"throws": null,
|
|
9118
|
+
"example": null,
|
|
9119
|
+
"defaultValue": null,
|
|
9120
|
+
"guides": []
|
|
9121
|
+
},
|
|
9067
9122
|
{
|
|
9068
9123
|
"id": "GemFrame.setMenuStyle",
|
|
9069
9124
|
"name": "setMenuStyle",
|
|
@@ -10040,6 +10095,38 @@
|
|
|
10040
10095
|
"defaultValue": null,
|
|
10041
10096
|
"guides": []
|
|
10042
10097
|
},
|
|
10098
|
+
{
|
|
10099
|
+
"id": "PlayInviteRequest",
|
|
10100
|
+
"name": "PlayInviteRequest",
|
|
10101
|
+
"anchor": "playinviterequest",
|
|
10102
|
+
"group": "friends",
|
|
10103
|
+
"signature": "interface PlayInviteRequest",
|
|
10104
|
+
"documented": true,
|
|
10105
|
+
"summary": "What `gem.friends.inviteToPlay` takes.",
|
|
10106
|
+
"deprecated": null,
|
|
10107
|
+
"params": [],
|
|
10108
|
+
"returns": null,
|
|
10109
|
+
"throws": null,
|
|
10110
|
+
"example": null,
|
|
10111
|
+
"defaultValue": null,
|
|
10112
|
+
"guides": []
|
|
10113
|
+
},
|
|
10114
|
+
{
|
|
10115
|
+
"id": "PlayInviteRequest.params",
|
|
10116
|
+
"name": "params",
|
|
10117
|
+
"anchor": "playinviterequest-params",
|
|
10118
|
+
"group": "friends",
|
|
10119
|
+
"signature": "readonly params?: Readonly<Record<string, string>>",
|
|
10120
|
+
"documented": true,
|
|
10121
|
+
"summary": "Your own launch parameters for what the Arcade's link and invites open —\n`{ room: joinCode }` for a room joined by code — exactly as they should\nreach your game, on `gem.share.request`'s rules: at most 16, keys a letter\nthen up to 63 letters, digits, `_` or `-`, string values of at most 512\ncharacters. For this one opening, they replace what\n`gem.friends.setInviteParams` declared. Omit it to use that declaration.",
|
|
10122
|
+
"deprecated": null,
|
|
10123
|
+
"params": [],
|
|
10124
|
+
"returns": null,
|
|
10125
|
+
"throws": null,
|
|
10126
|
+
"example": null,
|
|
10127
|
+
"defaultValue": null,
|
|
10128
|
+
"guides": []
|
|
10129
|
+
},
|
|
10043
10130
|
{
|
|
10044
10131
|
"id": "FriendsApi",
|
|
10045
10132
|
"name": "FriendsApi",
|
|
@@ -10102,6 +10189,52 @@
|
|
|
10102
10189
|
"defaultValue": null,
|
|
10103
10190
|
"guides": []
|
|
10104
10191
|
},
|
|
10192
|
+
{
|
|
10193
|
+
"id": "FriendsApi.inviteToPlay",
|
|
10194
|
+
"name": "inviteToPlay",
|
|
10195
|
+
"anchor": "friendsapi-invitetoplay",
|
|
10196
|
+
"group": "friends",
|
|
10197
|
+
"signature": "inviteToPlay(request?: PlayInviteRequest): Promise<boolean>",
|
|
10198
|
+
"documented": true,
|
|
10199
|
+
"summary": "Ask the Arcade to open ITS invite sheet, so the player can invite friends\nto come and play this game with them.\n\nThe Arcade lists the player's friends in its own chrome and sends an\ninvite only when the player presses Invite there. A friend who accepts is\nopened into this game by the Arcade, and their quick-join seats them in\nthe player's room when there is a seat: your game makes no call for it,\nand reads them as a room member who shares the player's `groupId`. Your\ngame names nobody and writes no text.\n\nAn Arcade that does not offer the sheet answers `false` at once, and asks\nnothing. Hide the button on `gem.frame.parentSupports('playInvites')`\nrather than letting a player press one that cannot work.\n\n⚠️ **Call it ONLY from a player's press**, never on a timer or as a screen\nopens. The Arcade answers an ask with no press behind it `false`, and\nshows nothing.\n\nRESOLVES `true` when the Arcade opened its sheet, and `false` when it did\nnot: outside the Arcade, without `playInvites`, with another sheet\nalready open, without a press, or when no answer arrived. **That is all it\nsays** — never whom the player invited, how many, or whether any invite\nwas sent — so never gate a reward on it. Never rejects.\n\nCalls made while an ask is open share its answer.\n\n**The window's share link and its invites are one system.** Pass\n`params` — your own launch parameters, `{ room: joinCode }` for a room\njoined by code — and a friend who accepts and anyone who opens the link\narrive with them, on `gem.share.request`'s rules. Omitted, the window uses\nwhat [FriendsApi.setInviteParams](api-reference-game.md#friendsapi-setinviteparams) declared. A param those rules\nrefuse is said on the console and nothing is asked: the call resolves\n`false`.",
|
|
10200
|
+
"deprecated": null,
|
|
10201
|
+
"params": [
|
|
10202
|
+
{
|
|
10203
|
+
"name": "request",
|
|
10204
|
+
"type": "PlayInviteRequest",
|
|
10205
|
+
"optional": true,
|
|
10206
|
+
"summary": null
|
|
10207
|
+
}
|
|
10208
|
+
],
|
|
10209
|
+
"returns": null,
|
|
10210
|
+
"throws": null,
|
|
10211
|
+
"example": "```ts\ninviteButton.onclick = async () => {\n const shown = await gem.friends.inviteToPlay({ params: { room: joinCode } });\n if (!shown) showJoinCode(); // the Arcade's sheet did not open: your own fallback\n};\n```",
|
|
10212
|
+
"defaultValue": null,
|
|
10213
|
+
"guides": []
|
|
10214
|
+
},
|
|
10215
|
+
{
|
|
10216
|
+
"id": "FriendsApi.setInviteParams",
|
|
10217
|
+
"name": "setInviteParams",
|
|
10218
|
+
"anchor": "friendsapi-setinviteparams",
|
|
10219
|
+
"group": "friends",
|
|
10220
|
+
"signature": "setInviteParams(params: Readonly<Record<string, string>> | null): boolean",
|
|
10221
|
+
"documented": true,
|
|
10222
|
+
"summary": "Say what the Arcade's Friends window opens when the PLAYER opens it from\nthe Arcade's own menu over your game: your launch parameters, carried by\nits share link and its friend invites alike, on `gem.share.request`'s\nrules. Pass `null` to clear.\n\nCall it when the player enters a room joined by code —\n`setInviteParams({ room: joinCode })` — and `setInviteParams(null)` when\nthey leave it, so the window never invites anyone into a room that is\ngone. The latest call stands until the next; the SDK holds it and says it\nagain after a reconnect. An explicit `inviteToPlay({ params })` wins for\nthat one opening. Nothing comes back, and nothing changes on screen.",
|
|
10223
|
+
"deprecated": null,
|
|
10224
|
+
"params": [
|
|
10225
|
+
{
|
|
10226
|
+
"name": "params",
|
|
10227
|
+
"type": "Readonly<Record<string, string>> | null",
|
|
10228
|
+
"optional": false,
|
|
10229
|
+
"summary": null
|
|
10230
|
+
}
|
|
10231
|
+
],
|
|
10232
|
+
"returns": "whether the declaration reached the Arcade now. `false` outside\n the Arcade, without `playInvites`, before the bridge is up (it is sent on\n connect) and for params the share rules refuse, which are said on the\n console; the last declaration then stands.",
|
|
10233
|
+
"throws": null,
|
|
10234
|
+
"example": "```ts\n// On entering a room joined by code:\ngem.friends.setInviteParams({ room: joinCode });\n// ...and on leaving it:\ngem.friends.setInviteParams(null);\n```",
|
|
10235
|
+
"defaultValue": null,
|
|
10236
|
+
"guides": []
|
|
10237
|
+
},
|
|
10105
10238
|
{
|
|
10106
10239
|
"id": "FriendsApi.inbox",
|
|
10107
10240
|
"name": "inbox",
|
|
@@ -10203,6 +10336,22 @@
|
|
|
10203
10336
|
"defaultValue": null,
|
|
10204
10337
|
"guides": []
|
|
10205
10338
|
},
|
|
10339
|
+
{
|
|
10340
|
+
"id": "FunctionInvokeOptions.cueId",
|
|
10341
|
+
"name": "cueId",
|
|
10342
|
+
"anchor": "functioninvokeoptions-cueid",
|
|
10343
|
+
"group": "functions",
|
|
10344
|
+
"signature": "readonly cueId?: string",
|
|
10345
|
+
"documented": true,
|
|
10346
|
+
"summary": "The id of the reward this call pays, so the Gem Arcade flies it once:\nthe same `cueId` you pass to `gem.cueReward` for it, one\n`crypto.randomUUID()` per award. The platform hands it back on the payout\nit shows the arcade, and nowhere else: your function never sees it, and\nneither the answer nor a receipt carries it.\n\n1 to 64 letters, digits, `_` or `-`. Anything else is left out with a\nconsole warning, and the call still goes: it decides an animation, never\nwhether the call pays.",
|
|
10347
|
+
"deprecated": null,
|
|
10348
|
+
"params": [],
|
|
10349
|
+
"returns": null,
|
|
10350
|
+
"throws": null,
|
|
10351
|
+
"example": null,
|
|
10352
|
+
"defaultValue": null,
|
|
10353
|
+
"guides": []
|
|
10354
|
+
},
|
|
10206
10355
|
{
|
|
10207
10356
|
"id": "FunctionResult",
|
|
10208
10357
|
"name": "FunctionResult",
|
|
@@ -10482,7 +10631,7 @@
|
|
|
10482
10631
|
"group": "functions",
|
|
10483
10632
|
"signature": "invoke(input: unknown, options?: FunctionInvokeOptions): Promise<FunctionResult>",
|
|
10484
10633
|
"documented": true,
|
|
10485
|
-
"summary": "Call the function published on your channel, and get its answer.\n\nThe input is sent as your function's `input`, byte for byte. Pass a\n`Uint8Array`, an `ArrayBuffer` or another typed array to send bytes as they\nare. Pass anything else and it is sent as its JSON text — a string too, so\n`'ping'` arrives as `\"ping\"` with its quotes.\n\nThe call is made once. It is never repeated for you, because your function\nmay have granted or written something: see [GemFunctionError.retryable](api-reference-game.md#gemfunctionerror-retryable)\nfor whether repeating it could help.\n\n```js\nconst result = await gem.functions.invoke({ v: 2, runId, finishMs });\nconst { accepted, why } = result.json();\n```",
|
|
10634
|
+
"summary": "Call the function published on your channel, and get its answer.\n\nThe input is sent as your function's `input`, byte for byte. Pass a\n`Uint8Array`, an `ArrayBuffer` or another typed array to send bytes as they\nare. Pass anything else and it is sent as its JSON text — a string too, so\n`'ping'` arrives as `\"ping\"` with its quotes.\n\nThe call is made once. It is never repeated for you, because your function\nmay have granted or written something: see [GemFunctionError.retryable](api-reference-game.md#gemfunctionerror-retryable)\nfor whether repeating it could help.\n\nThe answer is the receipt: when this resolves, what your function says it\ngranted has been written. A game shows it at once and re-reads the\ninventory in the background (`gem.me.inventory.refresh()`, then\n`snapshot()`, or on `gem.me.inventory.onChanged`); never hold the player on\na loop that re-reads until the grant shows.\n\n```js\nconst result = await gem.functions.invoke({ v: 2, runId, finishMs });\nconst { accepted, why } = result.json();\n```\n\nWhen the call pays a reward the player sees, pass the reward's `cueId`, the\nsame one the page gives `gem.cueReward` for it, and the Gem Arcade flies it\nonce:\n\n```js\nconst cueId = crypto.randomUUID(); // one per award, also passed to the cue\nawait gem.functions.invoke({ claim: 'daily' }, { cueId });\n```",
|
|
10486
10635
|
"deprecated": null,
|
|
10487
10636
|
"params": [
|
|
10488
10637
|
{
|
|
@@ -13483,7 +13632,7 @@
|
|
|
13483
13632
|
"group": "start",
|
|
13484
13633
|
"signature": "readonly friends: FriendsApi",
|
|
13485
13634
|
"documented": true,
|
|
13486
|
-
"summary": "The signed-in player's accepted friends — display identity only,\nread-only, paginated — and the
|
|
13635
|
+
"summary": "The signed-in player's accepted friends — display identity only,\nread-only, paginated — and the asks that reach past them:\n[FriendsApi.challenge](api-reference-game.md#friendsapi-challenge), which asks the Arcade to send a friend a\nchallenge after the player confirms it in the Arcade's own sheet, and\n[FriendsApi.inviteToPlay](api-reference-game.md#friendsapi-invitetoplay), which asks the Arcade to open its own\ninvite-to-play sheet. The receiving side reads the challenges this player\nholds in this game:\n[FriendsApi.launchedChallenge](api-reference-game.md#friendsapi-launchedchallenge) for the one this frame was launched\nwith, [FriendsApi.inbox](api-reference-game.md#friendsapi-inbox) for all of them, and\n[FriendsApi.markAnswered](api-reference-game.md#friendsapi-markanswered) once one has been played.\n\nEvery accepted friend appears whether or not they have played this game;\nan invite-a-friend flow needs exactly the ones who have not. The graph\nitself — requests, accepts, blocks, presence — stays on the Arcade: there\nis nothing here to mutate but a challenge's own answered mark, and nothing\nthat says who is online.\n\nA player surface only. In a context with no player — a game server — the\nplatform refuses the read; a server has no friend list.",
|
|
13487
13636
|
"deprecated": null,
|
|
13488
13637
|
"params": [],
|
|
13489
13638
|
"returns": null,
|
|
@@ -13703,7 +13852,7 @@
|
|
|
13703
13852
|
"group": "start",
|
|
13704
13853
|
"signature": "cueReward(cue: RewardCue): boolean",
|
|
13705
13854
|
"documented": true,
|
|
13706
|
-
"summary": "Tell the arcade a platform-currency reward is on its way, so it **shows it\nlanding**: in the Gem Arcade the currency flies from `origin` into the Gem\nbutton, wherever on your frame it sits, which pops as each mark lands, and\nthe player's total moves once the platform confirms the grant. The Gem\nArcade draws no wallet over your game unless you ask (`setWalletVisible`):\nthe player sees their Gems and Coins in the Gem menu. A payout you do not\ncue still flies, from the middle of your frame, so cue the ones the player\nearns on screen.\n\nCall it the moment your game decides to award — when your server has moved\n(or is about to move) the progression mapped onto a platform currency —\nand pass the centre of that currency's icon (or hero) on your screen, so\nthe flight starts from the mark the player is looking at:\n\n```js\nconst { x, y, width, height } = rewardGem.getBoundingClientRect(); // the gem's own box\ngem.cueReward({\n currencySlug: 'GEMS', // or 'ARCADE_TICKETS', from the coin\n expected: 15,\n origin: { x: x + width / 2, y: y + height / 2 },\n});\n```\n\n**This is the platform-currency animation, and the only one.** The display\nof every platform currency — Gems and Coins alike — belongs to the\narcade: a game draws no flight or celebration of its own for one and keeps\nno counter of one. **If this returns `false`, do nothing** — there is no\nfallback celebration.\n\n**The arcade never takes your word for the amount.** What lands, and the\nplayer's new total, come from the platform itself; `expected` is drawn as\nprovisional, and a cue the platform never confirms fades on its own — a\ngrant clamped to zero, say, or one the receipt reports `suppressed`. So\nthe cue can only ever make the wallet move for a reward that really\nlanded — which it does on every channel, dev and test included.",
|
|
13855
|
+
"summary": "Tell the arcade a platform-currency reward is on its way, so it **shows it\nlanding**: in the Gem Arcade the currency flies from `origin` into the Gem\nbutton, wherever on your frame it sits, which pops as each mark lands, and\nthe player's total moves once the platform confirms the grant. The Gem\nArcade draws no wallet over your game unless you ask (`setWalletVisible`):\nthe player sees their Gems and Coins in the Gem menu. A payout you do not\ncue still flies, from the middle of your frame, so cue the ones the player\nearns on screen.\n\nCall it the moment your game decides to award — when your server has moved\n(or is about to move) the progression mapped onto a platform currency —\nand pass the centre of that currency's icon (or hero) on your screen, so\nthe flight starts from the mark the player is looking at:\n\n```js\nconst { x, y, width, height } = rewardGem.getBoundingClientRect(); // the gem's own box\ngem.cueReward({\n currencySlug: 'GEMS', // or 'ARCADE_TICKETS', from the coin\n expected: 15,\n origin: { x: x + width / 2, y: y + height / 2 },\n});\n```\n\n**When your function pays it, give both calls one `cueId`.** Make one per\naward with `crypto.randomUUID()`, pass it here and to the\n`gem.functions.invoke` that pays, and the arcade flies that reward once,\nwhichever reaches it first — so you may cue before the call or after it\nreturns:\n\n```js\nconst cueId = crypto.randomUUID();\nconst result = await gem.functions.invoke({ claim: 'daily' }, { cueId });\ngem.cueReward({ currencySlug: 'GEMS', expected: 15, origin, cueId });\n```\n\nA payout that is not a function call (your own game server's) has no id\nto share: cue it with none, and the arcade pairs the two by currency and\ntiming.\n\n**When is up to the arcade unless you say otherwise.** By default the cue\nis paired with its payout only within about half a second either side of\nyour server's write; a payout with no cue by then flies from the middle of\nyour frame, and a cue that comes later adds nothing. To show the payout at\na later moment of your own, such as a Claim tap on the win card or the\nscreen the player returns to, call `gem.setRewardPresentation('game')` once\nat boot: each payout then waits for your cue.\n\n**This is the platform-currency animation, and the only one.** The display\nof every platform currency — Gems and Coins alike — belongs to the\narcade: a game draws no flight or celebration of its own for one and keeps\nno counter of one. **If this returns `false`, do nothing** — there is no\nfallback celebration.\n\n**The arcade never takes your word for the amount.** What lands, and the\nplayer's new total, come from the platform itself; `expected` is drawn as\nprovisional, and a cue the platform never confirms fades on its own — a\ngrant clamped to zero, say, or one the receipt reports `suppressed`. So\nthe cue can only ever make the wallet move for a reward that really\nlanded — which it does on every channel, dev and test included.",
|
|
13707
13856
|
"deprecated": null,
|
|
13708
13857
|
"params": [
|
|
13709
13858
|
{
|
|
@@ -13719,6 +13868,29 @@
|
|
|
13719
13868
|
"defaultValue": null,
|
|
13720
13869
|
"guides": []
|
|
13721
13870
|
},
|
|
13871
|
+
{
|
|
13872
|
+
"id": "Gem.setRewardPresentation",
|
|
13873
|
+
"name": "setRewardPresentation",
|
|
13874
|
+
"anchor": "gem-setrewardpresentation",
|
|
13875
|
+
"group": "start",
|
|
13876
|
+
"signature": "setRewardPresentation(mode: RewardPresentation): boolean",
|
|
13877
|
+
"documented": true,
|
|
13878
|
+
"summary": "Say who decides when your platform-currency payouts are shown landing: the\narcade (`'arcade'`, the default) or your game (`'game'`). Call it once at\nboot when your reward moment comes after your server pays, such as a Claim\ntap on the win card or the next screen the player sees.\n\n```js\ngem.setRewardPresentation('game'); // at boot\n// ...the server pays when the board is won; later, on Claim:\ngem.cueReward({ currencySlug: 'ARCADE_TICKETS', expected: 20, origin });\n```\n\n**With `'game'`, your cue supplies the moment and the payout supplies the\nnumber.** The arcade holds each payout's presentation for its currency\nrather than flying it from the middle of your frame. Each `cueReward`\nreleases up to its `expected` of what is held (all of it when it names no\namount) and flies it from its `origin`, and the arcade's wallet chip over\nyour game counts up as the marks land. A cue that finds nothing held waits\nfor the next payout of its currency, so cue before or after the write, in\neither order.\n\n**It delays the show, never the money.** What is paid, when, and every\nceiling are unchanged, and the arcade's menu and every purchase see the\nplayer's true total at once. A payout no cue releases within five minutes\nlands quietly, with no flight, so a game can never hide one.\n\n**It is screen state, not a toggle**, held before the bridge is up and\nre-sent after every reconnect. Switching back to `'arcade'` lands whatever\nis held. A value that is neither is ignored, with a warning on the console,\nand changes nothing.",
|
|
13879
|
+
"deprecated": null,
|
|
13880
|
+
"params": [
|
|
13881
|
+
{
|
|
13882
|
+
"name": "mode",
|
|
13883
|
+
"type": "RewardPresentation",
|
|
13884
|
+
"optional": false,
|
|
13885
|
+
"summary": "`'game'` to present each payout on your cue, `'arcade'` for the default."
|
|
13886
|
+
}
|
|
13887
|
+
],
|
|
13888
|
+
"returns": "`true` when the arcade was told now; `false` before the bridge is\n up (the answer is held and sent when it connects), under an embedder\n that presents every payout its own way, and for a value that is neither.\n On `false` from an embedder, keep cueing as usual: nothing else changes.",
|
|
13889
|
+
"throws": null,
|
|
13890
|
+
"example": null,
|
|
13891
|
+
"defaultValue": null,
|
|
13892
|
+
"guides": []
|
|
13893
|
+
},
|
|
13722
13894
|
{
|
|
13723
13895
|
"id": "Gem.setWalletVisible",
|
|
13724
13896
|
"name": "setWalletVisible",
|
|
@@ -22405,7 +22577,7 @@
|
|
|
22405
22577
|
"group": "rooms",
|
|
22406
22578
|
"signature": "readonly playWith: PlayWithOutcome | null",
|
|
22407
22579
|
"documented": true,
|
|
22408
|
-
"summary": "What the last [RoomManager.quickJoin](api-reference-game.md#roommanager-quickjoin) did when the Arcade opened\nyour game to play with someone — a friend whose Join the player pressed,\
|
|
22580
|
+
"summary": "What the last [RoomManager.quickJoin](api-reference-game.md#roommanager-quickjoin) did when the Arcade opened\nyour game to play with someone — a friend whose Join the player pressed,\na friend's invite, or their party's leader. Read-only.\n\n- `'joined'` — the player was seated in that person's room, as a member\n sharing their `groupId`.\n- `'unavailable'` — that room could not be joined (no seat, a match that\n admits nobody late, another mode), so the quick-join went on as it\n always does. No reason is given.\n- `null` — no quick-join has asked: the Arcade did not open the game to\n play with anyone, or nothing has quick-joined yet.\n\nYour game makes no call for this, names nobody and is told nobody's name:\nthe Arcade adds a launch key of its own, an opaque ticket, and every\n`quickJoin` passes it on. Nothing else\nchanges — the room is the one `quickJoin` answers with, `joined` fires as\nfor any quick-join, and no `seated` follows, because your own call made\nthe seat. Use it for a line of text (\"You joined your friend's game\") or\nto skip a mode picker, never to decide anything of value.",
|
|
22409
22581
|
"deprecated": null,
|
|
22410
22582
|
"params": [],
|
|
22411
22583
|
"returns": null,
|
|
@@ -26721,6 +26893,22 @@
|
|
|
26721
26893
|
"defaultValue": null,
|
|
26722
26894
|
"guides": []
|
|
26723
26895
|
},
|
|
26896
|
+
{
|
|
26897
|
+
"id": "FunctionInvokeOptions.cueId",
|
|
26898
|
+
"name": "cueId",
|
|
26899
|
+
"anchor": "functioninvokeoptions-cueid",
|
|
26900
|
+
"group": "functions",
|
|
26901
|
+
"signature": "readonly cueId?: string",
|
|
26902
|
+
"documented": true,
|
|
26903
|
+
"summary": "The id of the reward this call pays, so the Gem Arcade flies it once:\nthe same `cueId` you pass to `gem.cueReward` for it, one\n`crypto.randomUUID()` per award. The platform hands it back on the payout\nit shows the arcade, and nowhere else: your function never sees it, and\nneither the answer nor a receipt carries it.\n\n1 to 64 letters, digits, `_` or `-`. Anything else is left out with a\nconsole warning, and the call still goes: it decides an animation, never\nwhether the call pays.",
|
|
26904
|
+
"deprecated": null,
|
|
26905
|
+
"params": [],
|
|
26906
|
+
"returns": null,
|
|
26907
|
+
"throws": null,
|
|
26908
|
+
"example": null,
|
|
26909
|
+
"defaultValue": null,
|
|
26910
|
+
"guides": []
|
|
26911
|
+
},
|
|
26724
26912
|
{
|
|
26725
26913
|
"id": "FunctionResult",
|
|
26726
26914
|
"name": "FunctionResult",
|
|
@@ -27000,7 +27188,7 @@
|
|
|
27000
27188
|
"group": "functions",
|
|
27001
27189
|
"signature": "invoke(input: unknown, options?: FunctionInvokeOptions): Promise<FunctionResult>",
|
|
27002
27190
|
"documented": true,
|
|
27003
|
-
"summary": "Call the function published on your channel, and get its answer.\n\nThe input is sent as your function's `input`, byte for byte. Pass a\n`Uint8Array`, an `ArrayBuffer` or another typed array to send bytes as they\nare. Pass anything else and it is sent as its JSON text — a string too, so\n`'ping'` arrives as `\"ping\"` with its quotes.\n\nThe call is made once. It is never repeated for you, because your function\nmay have granted or written something: see [GemFunctionError.retryable](api-reference-server.md#gemfunctionerror-retryable)\nfor whether repeating it could help.\n\n```js\nconst result = await gem.functions.invoke({ v: 2, runId, finishMs });\nconst { accepted, why } = result.json();\n```",
|
|
27191
|
+
"summary": "Call the function published on your channel, and get its answer.\n\nThe input is sent as your function's `input`, byte for byte. Pass a\n`Uint8Array`, an `ArrayBuffer` or another typed array to send bytes as they\nare. Pass anything else and it is sent as its JSON text — a string too, so\n`'ping'` arrives as `\"ping\"` with its quotes.\n\nThe call is made once. It is never repeated for you, because your function\nmay have granted or written something: see [GemFunctionError.retryable](api-reference-server.md#gemfunctionerror-retryable)\nfor whether repeating it could help.\n\nThe answer is the receipt: when this resolves, what your function says it\ngranted has been written. A game shows it at once and re-reads the\ninventory in the background (`gem.me.inventory.refresh()`, then\n`snapshot()`, or on `gem.me.inventory.onChanged`); never hold the player on\na loop that re-reads until the grant shows.\n\n```js\nconst result = await gem.functions.invoke({ v: 2, runId, finishMs });\nconst { accepted, why } = result.json();\n```\n\nWhen the call pays a reward the player sees, pass the reward's `cueId`, the\nsame one the page gives `gem.cueReward` for it, and the Gem Arcade flies it\nonce:\n\n```js\nconst cueId = crypto.randomUUID(); // one per award, also passed to the cue\nawait gem.functions.invoke({ claim: 'daily' }, { cueId });\n```",
|
|
27004
27192
|
"deprecated": null,
|
|
27005
27193
|
"params": [
|
|
27006
27194
|
{
|
|
@@ -32401,7 +32589,7 @@
|
|
|
32401
32589
|
"group": "inventory",
|
|
32402
32590
|
"signature": "refresh(): void",
|
|
32403
32591
|
"documented": true,
|
|
32404
|
-
"summary": "Throw away what is cached, so the next [InventoryApi.snapshot](api-reference-function.md#inventoryapi-snapshot) really\nasks.\n\nFor when something you did not do changed the inventory — a purchase on\nanother device, a grant from a server. The SDK
|
|
32592
|
+
"summary": "Throw away what is cached, so the next [InventoryApi.snapshot](api-reference-function.md#inventoryapi-snapshot) really\nasks.\n\nFor when something you did not do changed the inventory — a purchase on\nanother device, a grant from a server — and nothing told you. The SDK\nalready drops the cache itself after any change it made, and on an\nArcade's hint that this game's inventory changed on the server.",
|
|
32405
32593
|
"deprecated": null,
|
|
32406
32594
|
"params": [],
|
|
32407
32595
|
"returns": null,
|
|
@@ -32417,7 +32605,7 @@
|
|
|
32417
32605
|
"group": "inventory",
|
|
32418
32606
|
"signature": "onChanged(listener: () => void): () => void",
|
|
32419
32607
|
"documented": true,
|
|
32420
|
-
"summary": "Run something whenever this inventory changes, and get back a function that\nstops listening.\n\
|
|
32608
|
+
"summary": "Run something whenever this inventory changes, and get back a function that\nstops listening.\n\nFires for every change THIS game made through this SDK, and — on an Arcade\nthat relays it — for any change to this game's inventory on the server: a\ngrant from your function or server, a purchase, a gift accepted, a support\ngrant, another device. It is a cue to re-read, not a receipt: it carries\nnothing, so call [InventoryApi.snapshot](api-reference-function.md#inventoryapi-snapshot) for what changed. After your\nown function's grant, show the function's answer and let this redraw.\n\nAn older Arcade sends no hint, and there it fires for your own writes\nonly. Keep a fallback that does not depend on it: refresh on\n`visibilitychange` to `visible` and after any action that could involve\nanother player. Do not poll.",
|
|
32421
32609
|
"deprecated": null,
|
|
32422
32610
|
"params": [
|
|
32423
32611
|
{
|
|
@@ -36169,6 +36357,38 @@
|
|
|
36169
36357
|
"defaultValue": null,
|
|
36170
36358
|
"guides": []
|
|
36171
36359
|
},
|
|
36360
|
+
{
|
|
36361
|
+
"id": "PlayInviteRequest",
|
|
36362
|
+
"name": "PlayInviteRequest",
|
|
36363
|
+
"anchor": "playinviterequest",
|
|
36364
|
+
"group": "friends",
|
|
36365
|
+
"signature": "interface PlayInviteRequest",
|
|
36366
|
+
"documented": true,
|
|
36367
|
+
"summary": "What `gem.friends.inviteToPlay` takes.",
|
|
36368
|
+
"deprecated": null,
|
|
36369
|
+
"params": [],
|
|
36370
|
+
"returns": null,
|
|
36371
|
+
"throws": null,
|
|
36372
|
+
"example": null,
|
|
36373
|
+
"defaultValue": null,
|
|
36374
|
+
"guides": []
|
|
36375
|
+
},
|
|
36376
|
+
{
|
|
36377
|
+
"id": "PlayInviteRequest.params",
|
|
36378
|
+
"name": "params",
|
|
36379
|
+
"anchor": "playinviterequest-params",
|
|
36380
|
+
"group": "friends",
|
|
36381
|
+
"signature": "readonly params?: Readonly<Record<string, string>>",
|
|
36382
|
+
"documented": true,
|
|
36383
|
+
"summary": "Your own launch parameters for what the Arcade's link and invites open —\n`{ room: joinCode }` for a room joined by code — exactly as they should\nreach your game, on `gem.share.request`'s rules: at most 16, keys a letter\nthen up to 63 letters, digits, `_` or `-`, string values of at most 512\ncharacters. For this one opening, they replace what\n`gem.friends.setInviteParams` declared. Omit it to use that declaration.",
|
|
36384
|
+
"deprecated": null,
|
|
36385
|
+
"params": [],
|
|
36386
|
+
"returns": null,
|
|
36387
|
+
"throws": null,
|
|
36388
|
+
"example": null,
|
|
36389
|
+
"defaultValue": null,
|
|
36390
|
+
"guides": []
|
|
36391
|
+
},
|
|
36172
36392
|
{
|
|
36173
36393
|
"id": "FriendsApi",
|
|
36174
36394
|
"name": "FriendsApi",
|
|
@@ -36231,6 +36451,52 @@
|
|
|
36231
36451
|
"defaultValue": null,
|
|
36232
36452
|
"guides": []
|
|
36233
36453
|
},
|
|
36454
|
+
{
|
|
36455
|
+
"id": "FriendsApi.inviteToPlay",
|
|
36456
|
+
"name": "inviteToPlay",
|
|
36457
|
+
"anchor": "friendsapi-invitetoplay",
|
|
36458
|
+
"group": "friends",
|
|
36459
|
+
"signature": "inviteToPlay(request?: PlayInviteRequest): Promise<boolean>",
|
|
36460
|
+
"documented": true,
|
|
36461
|
+
"summary": "Ask the Arcade to open ITS invite sheet, so the player can invite friends\nto come and play this game with them.\n\nThe Arcade lists the player's friends in its own chrome and sends an\ninvite only when the player presses Invite there. A friend who accepts is\nopened into this game by the Arcade, and their quick-join seats them in\nthe player's room when there is a seat: your game makes no call for it,\nand reads them as a room member who shares the player's `groupId`. Your\ngame names nobody and writes no text.\n\nAn Arcade that does not offer the sheet answers `false` at once, and asks\nnothing. Hide the button on `gem.frame.parentSupports('playInvites')`\nrather than letting a player press one that cannot work.\n\n⚠️ **Call it ONLY from a player's press**, never on a timer or as a screen\nopens. The Arcade answers an ask with no press behind it `false`, and\nshows nothing.\n\nRESOLVES `true` when the Arcade opened its sheet, and `false` when it did\nnot: outside the Arcade, without `playInvites`, with another sheet\nalready open, without a press, or when no answer arrived. **That is all it\nsays** — never whom the player invited, how many, or whether any invite\nwas sent — so never gate a reward on it. Never rejects.\n\nCalls made while an ask is open share its answer.\n\n**The window's share link and its invites are one system.** Pass\n`params` — your own launch parameters, `{ room: joinCode }` for a room\njoined by code — and a friend who accepts and anyone who opens the link\narrive with them, on `gem.share.request`'s rules. Omitted, the window uses\nwhat [FriendsApi.setInviteParams](api-reference-function.md#friendsapi-setinviteparams) declared. A param those rules\nrefuse is said on the console and nothing is asked: the call resolves\n`false`.",
|
|
36462
|
+
"deprecated": null,
|
|
36463
|
+
"params": [
|
|
36464
|
+
{
|
|
36465
|
+
"name": "request",
|
|
36466
|
+
"type": "PlayInviteRequest",
|
|
36467
|
+
"optional": true,
|
|
36468
|
+
"summary": null
|
|
36469
|
+
}
|
|
36470
|
+
],
|
|
36471
|
+
"returns": null,
|
|
36472
|
+
"throws": null,
|
|
36473
|
+
"example": "```ts\ninviteButton.onclick = async () => {\n const shown = await gem.friends.inviteToPlay({ params: { room: joinCode } });\n if (!shown) showJoinCode(); // the Arcade's sheet did not open: your own fallback\n};\n```",
|
|
36474
|
+
"defaultValue": null,
|
|
36475
|
+
"guides": []
|
|
36476
|
+
},
|
|
36477
|
+
{
|
|
36478
|
+
"id": "FriendsApi.setInviteParams",
|
|
36479
|
+
"name": "setInviteParams",
|
|
36480
|
+
"anchor": "friendsapi-setinviteparams",
|
|
36481
|
+
"group": "friends",
|
|
36482
|
+
"signature": "setInviteParams(params: Readonly<Record<string, string>> | null): boolean",
|
|
36483
|
+
"documented": true,
|
|
36484
|
+
"summary": "Say what the Arcade's Friends window opens when the PLAYER opens it from\nthe Arcade's own menu over your game: your launch parameters, carried by\nits share link and its friend invites alike, on `gem.share.request`'s\nrules. Pass `null` to clear.\n\nCall it when the player enters a room joined by code —\n`setInviteParams({ room: joinCode })` — and `setInviteParams(null)` when\nthey leave it, so the window never invites anyone into a room that is\ngone. The latest call stands until the next; the SDK holds it and says it\nagain after a reconnect. An explicit `inviteToPlay({ params })` wins for\nthat one opening. Nothing comes back, and nothing changes on screen.",
|
|
36485
|
+
"deprecated": null,
|
|
36486
|
+
"params": [
|
|
36487
|
+
{
|
|
36488
|
+
"name": "params",
|
|
36489
|
+
"type": "Readonly<Record<string, string>> | null",
|
|
36490
|
+
"optional": false,
|
|
36491
|
+
"summary": null
|
|
36492
|
+
}
|
|
36493
|
+
],
|
|
36494
|
+
"returns": "whether the declaration reached the Arcade now. `false` outside\n the Arcade, without `playInvites`, before the bridge is up (it is sent on\n connect) and for params the share rules refuse, which are said on the\n console; the last declaration then stands.",
|
|
36495
|
+
"throws": null,
|
|
36496
|
+
"example": "```ts\n// On entering a room joined by code:\ngem.friends.setInviteParams({ room: joinCode });\n// ...and on leaving it:\ngem.friends.setInviteParams(null);\n```",
|
|
36497
|
+
"defaultValue": null,
|
|
36498
|
+
"guides": []
|
|
36499
|
+
},
|
|
36234
36500
|
{
|
|
36235
36501
|
"id": "FriendsApi.inbox",
|
|
36236
36502
|
"name": "inbox",
|
package/docs/arcade-economy.md
CHANGED
|
@@ -237,7 +237,9 @@ animates every payout into the right total. Gems and Coins follow the same rules
|
|
|
237
237
|
`onPause(false)` if you keep anything worked out from it, such as a greyed-out Buy button.
|
|
238
238
|
- **No celebration of your own.** At a payout, cue the arcade's wallet with `gem.cueReward`, naming
|
|
239
239
|
the currency that paid — `currencySlug: 'GEMS'` from the gem on your reward screen,
|
|
240
|
-
`'ARCADE_TICKETS'` from the coin. If it returns `false`, do nothing
|
|
240
|
+
`'ARCADE_TICKETS'` from the coin. If it returns `false`, do nothing. When your serverless function
|
|
241
|
+
pays it, pass one `cueId` (a `crypto.randomUUID()`) to both the cue and the `gem.functions.invoke`
|
|
242
|
+
call, and the reward flies once
|
|
241
243
|
([inventory-and-catalogs.md](inventory-and-catalogs.md#showing-the-reward-land-in-the-arcades-wallet)).
|
|
242
244
|
- **No "daily max" of your own making.** Your game cannot see what every other game paid the player,
|
|
243
245
|
and must not compute a maximum. To show how much more your game can pay, read
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,67 @@ 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
|
+
## 23.0.0
|
|
20
|
+
|
|
21
|
+
*Written from the API facts alone; no summary was generated for this release.*
|
|
22
|
+
|
|
23
|
+
Upgrading from 22.0.0. Impact: required.
|
|
24
|
+
|
|
25
|
+
### Must change
|
|
26
|
+
|
|
27
|
+
- PARENT_CONFORMANCE_VERSION changed its signature.
|
|
28
|
+
- GemFrame.requestArcade changed its signature.
|
|
29
|
+
- FRAME_TO_PARENT changed its signature.
|
|
30
|
+
- FrameMessage changed its signature.
|
|
31
|
+
- GemCapability changed its signature.
|
|
32
|
+
- PARENT_TO_FRAME changed its signature.
|
|
33
|
+
- ParentMessage changed its signature.
|
|
34
|
+
|
|
35
|
+
### New capability
|
|
36
|
+
|
|
37
|
+
- ParentFixture.channel was added.
|
|
38
|
+
- ParentFixture.gameId was added.
|
|
39
|
+
- ParentUnderTest.deliverSocket was added.
|
|
40
|
+
- CharactersApi.refreshHeld was added.
|
|
41
|
+
- FriendsApi.inviteToPlay was added.
|
|
42
|
+
- FriendsApi.setInviteParams was added.
|
|
43
|
+
- FriendsApiDependencies.inviteToPlay was added.
|
|
44
|
+
- FriendsApiDependencies.setInviteParams was added.
|
|
45
|
+
- PlayInviteRequest was added.
|
|
46
|
+
- FrameCallbacks.onInventoryChanged was added.
|
|
47
|
+
- FrameCallbacks.onPlayInviteResult was added.
|
|
48
|
+
- FrameOptions.onInventoryChanged was added.
|
|
49
|
+
- FrameOptions.onPlayInviteResult was added.
|
|
50
|
+
- GemHost.answerPlayInvite was added.
|
|
51
|
+
- GemHost.relayInventoryEvent was added.
|
|
52
|
+
- GemHost.sendInventoryChanged was added.
|
|
53
|
+
- GemInventoryChanged was added.
|
|
54
|
+
- GemInviteParams was added.
|
|
55
|
+
- GemPlayInviteResult was added.
|
|
56
|
+
- GemRequestPlayInvite was added.
|
|
57
|
+
- HostCallbacks.onInviteParams was added.
|
|
58
|
+
- HostCallbacks.onRequestPlayInvite was added.
|
|
59
|
+
- HostOptions.onInviteParams was added.
|
|
60
|
+
- HostOptions.onRequestPlayInvite was added.
|
|
61
|
+
- validateGemInventoryChanged was added.
|
|
62
|
+
- validateGemInviteParams was added.
|
|
63
|
+
- validateGemPlayInviteResult was added.
|
|
64
|
+
- validateGemRequestPlayInvite was added.
|
|
65
|
+
|
|
66
|
+
## 22.0.0
|
|
67
|
+
|
|
68
|
+
Upgrading from 21.1.1. Impact: required.
|
|
69
|
+
|
|
70
|
+
### Rebuild
|
|
71
|
+
|
|
72
|
+
- The function invoke options gained an optional cueId field, which changes the signatures of the functions API constructor, the server API invoke function member and the standalone invoke function. Nothing is required of existing calls; recompiling is enough.
|
|
73
|
+
- The frame message union, the list of frame-to-parent messages and the capability type each gained a reward presentation entry. Recompiling is enough, unless your code switches exhaustively over these types, in which case add a case for the new entry.
|
|
74
|
+
|
|
75
|
+
### New capability
|
|
76
|
+
|
|
77
|
+
- A game can now make one id per award and pass it to both the reward cue call and the function invoke call, so the Arcade pairs the cue with its payout and the reward flies once. An id that is malformed is left out with a console warning and never throws. Passing an id is optional and existing calls behave as before.
|
|
78
|
+
- A server-paid game whose reward moment comes after the payout can now declare that its own reward cue decides when the payout is shown, instead of leaving the timing to the Arcade. Under the game choice the Arcade holds each payout's presentation until a cue releases it, and a payout that no cue releases within a limited time lands quietly. Only the presentation changes; what is paid, when it is paid, and every limit stay the same. The declaration is remembered until the Arcade connection is ready and sent again after each reconnect, and it takes effect only when the Arcade offers the matching capability.
|
|
79
|
+
|
|
19
80
|
## 21.1.1
|
|
20
81
|
|
|
21
82
|
Upgrading from 21.1.0. Impact: none.
|
package/docs/daily-challenges.md
CHANGED
|
@@ -324,7 +324,9 @@ Four things to know before you ship one:
|
|
|
324
324
|
player's Gems and Coins in its own chrome, and without a cue a payout flies from the middle of
|
|
325
325
|
your frame, not from the payout the player is looking at. At the moment your game shows the challenge complete, call
|
|
326
326
|
`gem.cueReward` with the currency that paid (`GEMS` or `ARCADE_TICKETS`) and the centre of its icon
|
|
327
|
-
beside the payout on your completion screen, and the payout flies from there into the Gem button
|
|
327
|
+
beside the payout on your completion screen, and the payout flies from there into the Gem button.
|
|
328
|
+
When a serverless function pays it, give the cue and the `gem.functions.invoke` call one `cueId`
|
|
329
|
+
so it flies once:
|
|
328
330
|
[inventory-and-catalogs.md](inventory-and-catalogs.md#showing-the-reward-land-in-the-arcades-wallet).
|
|
329
331
|
**If it returns `false`, do nothing** — no celebration of your own, no counter, no flight;
|
|
330
332
|
platform-currency display belongs to the arcade, and a game not connected to it shows no
|