@indiegems/gem-web-sdk 8.0.0 → 9.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 +23 -0
- package/README.md +1 -0
- package/dist/function.d.ts +1 -1
- package/dist/game.d.ts +14 -2
- package/dist/gem-web-sdk.bridge.js +1 -1
- package/dist/gem-web-sdk.esm.js +2 -2
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +2 -2
- package/dist/gem-web-sdk.min.js +2 -2
- package/dist/gem-web-sdk.sdk.js +2 -2
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +12 -3
- package/dist/sdk.d.ts +14 -2
- package/dist/server.d.ts +1 -1
- package/docs/achievements.md +72 -5
- package/docs/api/api-reference-game.md +329 -39
- package/docs/api/api-reference-server.md +6 -2
- package/docs/api/reference.json +241 -21
- package/docs/changelog.md +23 -0
- package/docs/cross-promotion.md +87 -31
- package/docs/getting-started.md +3 -2
- package/docs/manifest.json +25 -20
- package/docs/match-session.md +1 -1
- package/docs/rewarded-ads.md +464 -0
- package/docs/room-shapes.md +54 -76
- package/docs/rooms-and-matches.md +2 -3
- package/docs/server-bundles.md +2 -2
- package/docs/serverless-functions.md +42 -32
- package/docs/what-a-game-must-do.md +132 -5
- package/package.json +1 -1
- package/schema/bridge.v1.json +72 -7
- package/schema/capabilities.public.json +6 -0
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "9.0.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -375,6 +375,29 @@
|
|
|
375
375
|
"defaultValue": null,
|
|
376
376
|
"guides": []
|
|
377
377
|
},
|
|
378
|
+
{
|
|
379
|
+
"id": "GameConfig.rewarded_placements",
|
|
380
|
+
"name": "rewarded_placements",
|
|
381
|
+
"anchor": "gameconfig-rewarded_placements",
|
|
382
|
+
"group": "config",
|
|
383
|
+
"url": "https://gemarcade.com/developer/api/game#gameconfig-rewarded_placements",
|
|
384
|
+
"signature": "rewarded_placements?: SnapshotRewardedPlacement[]",
|
|
385
|
+
"documented": true,
|
|
386
|
+
"summary": "Your rewarded placements — what pressing a rewarded button grants. An\narray, one row per placement, each naming itself in `placement`, with its\n`grants`, the caps the platform enforces on it (`max_per_day`,\n`cooldown_seconds`, `max_held`) and what happens where no ad serves\n(`without_ads`).\n\nRead it to LABEL your button — \"+1 Revive\" — never to decide a reward. A\ngrant is an `item_grant`, with a `quantity` of the item its\n`item_definition_id` names, or a `currency_grant`, with an `amount` of the\ncurrency its `currency_id` names; the word on the button is that\ndefinition's `display_name`, from [GameConfig.item](https://gemarcade.com/developer/api/game#gameconfig-item) or\n[GameConfig.currency](https://gemarcade.com/developer/api/game#gameconfig-currency):\n\n```js\nconst row = config?.rewarded_placements?.find((p) => p.placement === 'classic_revive');\nconst grant = row?.grants[0];\nconst label =\n grant?.type === 'item_grant'\n ? `+${grant.quantity} ${config.item(grant.item_definition_id)?.display_name ?? ''}`\n : grant?.type === 'currency_grant'\n ? `+${grant.amount} ${config.currency(grant.currency_id)?.display_name ?? ''}`\n : undefined;\n```\n\nWhether an ask grants anything is the platform's decision, made against\nthis config server-side; the grant lands on your game's inventory, and\nre-reading `gem.me.inventory` is the only way to learn that it did. Whether\nthe button could pay right now is [AdsApi.rewardedStatus](https://gemarcade.com/developer/api/game#adsapi-rewardedstatus).\n\nAbsent when your game declares none.",
|
|
387
|
+
"deprecated": null,
|
|
388
|
+
"params": [],
|
|
389
|
+
"returns": null,
|
|
390
|
+
"throws": null,
|
|
391
|
+
"example": null,
|
|
392
|
+
"defaultValue": null,
|
|
393
|
+
"guides": [
|
|
394
|
+
{
|
|
395
|
+
"slug": "cross-promotion",
|
|
396
|
+
"anchor": null,
|
|
397
|
+
"url": "https://gemarcade.com/developer/docs/cross-promotion"
|
|
398
|
+
}
|
|
399
|
+
]
|
|
400
|
+
},
|
|
378
401
|
{
|
|
379
402
|
"id": "GameConfig.version",
|
|
380
403
|
"name": "version",
|
|
@@ -3955,14 +3978,20 @@
|
|
|
3955
3978
|
"url": "https://gemarcade.com/developer/api/game#adformat",
|
|
3956
3979
|
"signature": "type AdFormat = \"interstitial\" | \"rewarded\"",
|
|
3957
3980
|
"documented": true,
|
|
3958
|
-
"summary": "Which kind of break this is.\n\n`interstitial` is a panel the player dismisses and nothing follows from.\n\n
|
|
3981
|
+
"summary": "Which kind of break this is.\n\n`interstitial` is a panel the player dismisses and nothing follows from.\n\n`rewarded` is a button the player pressed for a reward — a revive, an extra\nattempt — that your game config declares for the placement in\n`rewarded_placements`. It is served where this arcade serves it, and answered\n`no_ad` where it does not. ⚠️ The outcome grants nothing: the platform grants\nthe reward, against its own record, never your game reading `shown`. It\narrives on your game's inventory for this player, written by the platform,\nso re-read `gem.me.inventory` after a `rewarded` ask settles rather than\nwaiting for a signal on this pair; there will not be one.",
|
|
3959
3982
|
"deprecated": null,
|
|
3960
3983
|
"params": [],
|
|
3961
3984
|
"returns": null,
|
|
3962
3985
|
"throws": null,
|
|
3963
3986
|
"example": null,
|
|
3964
3987
|
"defaultValue": null,
|
|
3965
|
-
"guides": [
|
|
3988
|
+
"guides": [
|
|
3989
|
+
{
|
|
3990
|
+
"slug": "rewarded-ads",
|
|
3991
|
+
"anchor": null,
|
|
3992
|
+
"url": "https://gemarcade.com/developer/docs/rewarded-ads"
|
|
3993
|
+
}
|
|
3994
|
+
]
|
|
3966
3995
|
},
|
|
3967
3996
|
{
|
|
3968
3997
|
"id": "AdRequest",
|
|
@@ -3989,7 +4018,7 @@
|
|
|
3989
4018
|
"url": "https://gemarcade.com/developer/api/game#adrequest-placement",
|
|
3990
4019
|
"signature": "placement: string",
|
|
3991
4020
|
"documented": true,
|
|
3992
|
-
"summary": "Your own name for the break — `level-complete`, `between-rounds`.\n\n**1 to 32 characters of `a-z`, `0-9`, `-` and `_`.** Lowercase, no spaces,\nno accents: `'Boss Fight'` is not a placement, `'boss-fight'` is. The\ngrammar is the platform's own, and
|
|
4021
|
+
"summary": "Your own name for the break — `level-complete`, `between-rounds`.\n\n**1 to 32 characters of `a-z`, `0-9`, `-` and `_`.** Lowercase, no spaces,\nno accents: `'Boss Fight'` is not a placement, `'boss-fight'` is. The\ngrammar is the platform's own, and the SDK enforces it so a name that does\nnot fit is refused at your keyboard rather than in the field. A placement\noutside it is answered `no_ad` and reported to your `onError` handler.\n\nFor `rewarded`, it names the reward your game config declares in\n`rewarded_placements`: the platform matches it against that config, and a\nplacement the config does not declare is answered `no_ad`. For\n`interstitial`, the platform matches it against nothing; there it is for\nyour own telemetry, so two break points in your game can be told apart.\nEither way it is never shown to a player and **never sent to an ad\nnetwork**.",
|
|
3993
4022
|
"deprecated": null,
|
|
3994
4023
|
"params": [],
|
|
3995
4024
|
"returns": null,
|
|
@@ -4023,7 +4052,7 @@
|
|
|
4023
4052
|
"url": "https://gemarcade.com/developer/api/game#adoutcomekind",
|
|
4024
4053
|
"signature": "type AdOutcomeKind = \"shown\" | \"no_ad\" | \"unavailable\"",
|
|
4025
4054
|
"documented": true,
|
|
4026
|
-
"summary": "The complete vocabulary of answers. Three values, and there will not be a\nfourth.\n\n- `shown` — a panel was painted and has now closed. Resume. **It grants\n nothing.**\n- `no_ad` — nothing was painted. Carry on exactly as if you had not asked.\n This is the common answer.\n- `unavailable` — the arcade could not render just now. Not a softer no; read\n it exactly as you read `no_ad`.\n\nNo reason is given for a `no_ad`, and one will never be added: a reason would\ntell your game something about the player — an age, a consent state, a\nsubscription — that game code is deliberately not told.",
|
|
4055
|
+
"summary": "The complete vocabulary of answers. Three values, and there will not be a\nfourth.\n\n- `shown` — a panel was painted and has now closed. Resume. **It grants\n nothing.**\n- `no_ad` — nothing was painted. Carry on exactly as if you had not asked.\n This is the common answer. After a `rewarded` ask the reward may still\n have landed, with no ad, so re-read your inventory either way.\n- `unavailable` — the arcade could not render just now. Not a softer no; read\n it exactly as you read `no_ad`.\n\nNo reason is given for a `no_ad`, and one will never be added: a reason would\ntell your game something about the player — an age, a consent state, a\nsubscription — that game code is deliberately not told.\n\nThe one thing you can learn is about **your own economy**, and not from this\nanswer: [AdsApi.rewardedStatus](https://gemarcade.com/developer/api/game#adsapi-rewardedstatus) reports your own declared rewarded caps\nas the platform holds them (a platform-wide per-placement maximum, where one\nis set, can lower a cap). It never reports anything about the player.",
|
|
4027
4056
|
"deprecated": null,
|
|
4028
4057
|
"params": [],
|
|
4029
4058
|
"returns": null,
|
|
@@ -4091,7 +4120,7 @@
|
|
|
4091
4120
|
"url": "https://gemarcade.com/developer/api/game#adsapi-requestad",
|
|
4092
4121
|
"signature": "requestAd(req: AdRequest): Promise<AdOutcome>",
|
|
4093
4122
|
"documented": true,
|
|
4094
|
-
"summary": "Ask whether the arcade has anything to show at this break in play.\n\nNOTHING REJECTS, and the answer is usually `no_ad`. Every local refusal —\nan arcade that does not do this at all, a call while another is still in\nflight, a `placement` this module will not send — resolves `no_ad` rather\nthan throwing, so a game needs exactly one branch. Only \"no answer ever\narrived\" resolves `unavailable`.\n\nThe arcade pauses your game while a panel is up and resumes it when this\nsettles, so you do not need to pause anything yourself.\n\n⚠️ `shown` grants nothing. Never hand out a reward, an unlock or a currency\non the strength of it.",
|
|
4123
|
+
"summary": "Ask whether the arcade has anything to show at this break in play.\n\nNOTHING REJECTS, and the answer is usually `no_ad`. Every local refusal —\nan arcade that does not do this at all, a call while another is still in\nflight, a `placement` this module will not send — resolves `no_ad` rather\nthan throwing, so a game needs exactly one branch. Only \"no answer ever\narrived\" resolves `unavailable`.\n\nThe arcade pauses your game while a panel is up and resumes it when this\nsettles, so you do not need to pause anything yourself.\n\n⚠️ `shown` grants nothing. Never hand out a reward, an unlock or a currency\non the strength of it. A `rewarded` placement's reward is granted by the\nplatform into your game's inventory; re-read `gem.me.inventory` once this\nsettles, whatever it settled as.",
|
|
4095
4124
|
"deprecated": null,
|
|
4096
4125
|
"params": [
|
|
4097
4126
|
{
|
|
@@ -4105,6 +4134,30 @@
|
|
|
4105
4134
|
"throws": null,
|
|
4106
4135
|
"example": "```ts\nconst { outcome } = await gem.ads.requestAd({\n placement: 'level-complete',\n format: 'interstitial',\n});\n// `no_ad` is the ordinary answer. There is nothing to show and nothing\n// to draw; carry straight on to the next level.\nstartNextLevel();\n```",
|
|
4107
4136
|
"defaultValue": null,
|
|
4137
|
+
"guides": []
|
|
4138
|
+
},
|
|
4139
|
+
{
|
|
4140
|
+
"id": "AdsApi.rewardedStatus",
|
|
4141
|
+
"name": "rewardedStatus",
|
|
4142
|
+
"anchor": "adsapi-rewardedstatus",
|
|
4143
|
+
"group": "ads",
|
|
4144
|
+
"url": "https://gemarcade.com/developer/api/game#adsapi-rewardedstatus",
|
|
4145
|
+
"signature": "rewardedStatus(placement: string): Promise<RewardedStatus>",
|
|
4146
|
+
"documented": true,
|
|
4147
|
+
"summary": "Whether a rewarded placement could pay right now, so you can decide whether\nto draw its button. **It never says whether an ad will play.**\n\nThe answer comes from your own `rewarded_placements` config — its daily\ncap, cooldown and `max_held` — applied to this player's use of it, as the\nplatform holds them. Nothing in it describes the player. `grantable: true`\nmeans the platform would grant if an ad is watched or, where no ad serves,\nunder the placement's `without_ads: grant`; a player can still press the\nbutton and get `no_ad`, for reasons your game is not told.\n\nNOTHING REJECTS, like everything else on `gem.ads`. Without a play token,\nwhen the read fails or takes longer than ten seconds, or for a `placement`\noutside the grammar [AdRequest.placement](https://gemarcade.com/developer/api/game#adrequest-placement) documents, it resolves not\ndeclared and not grantable, with no `resetsAt`, and the reason goes to your\n`onError` handler.\n\nIt is a read of the platform, so call it when you draw the button — on a\nresults or death screen — not every frame. Calls for one placement that\noverlap share one request. Draw the rest of the screen first rather than\nholding it on this answer.\n\n⚠️ It grants nothing and reserves nothing. The reward is granted by the\nplatform when the player asks with `requestAd`; re-read `gem.me.inventory`\nafter that ask settles, whatever it settled as.",
|
|
4148
|
+
"deprecated": null,
|
|
4149
|
+
"params": [
|
|
4150
|
+
{
|
|
4151
|
+
"name": "placement",
|
|
4152
|
+
"type": "string",
|
|
4153
|
+
"optional": false,
|
|
4154
|
+
"summary": "A placement your `rewarded_placements` config declares."
|
|
4155
|
+
}
|
|
4156
|
+
],
|
|
4157
|
+
"returns": "The placement's state. Always resolves; never rejects.",
|
|
4158
|
+
"throws": null,
|
|
4159
|
+
"example": "```ts\nconst status = await gem.ads.rewardedStatus('classic_revive');\nreviveButton.disabled = !status.grantable;\nif (status.nextEligibleAt !== undefined) {\n reviveButton.title = `Back at ${new Date(status.nextEligibleAt).toLocaleTimeString()}`;\n}\n```",
|
|
4160
|
+
"defaultValue": null,
|
|
4108
4161
|
"guides": [
|
|
4109
4162
|
{
|
|
4110
4163
|
"slug": "cross-promotion",
|
|
@@ -4113,6 +4166,125 @@
|
|
|
4113
4166
|
}
|
|
4114
4167
|
]
|
|
4115
4168
|
},
|
|
4169
|
+
{
|
|
4170
|
+
"id": "RewardedStatus",
|
|
4171
|
+
"name": "RewardedStatus",
|
|
4172
|
+
"anchor": "rewardedstatus",
|
|
4173
|
+
"group": "ads",
|
|
4174
|
+
"url": "https://gemarcade.com/developer/api/game#rewardedstatus",
|
|
4175
|
+
"signature": "interface RewardedStatus",
|
|
4176
|
+
"documented": true,
|
|
4177
|
+
"summary": "What [AdsApi.rewardedStatus](https://gemarcade.com/developer/api/game#adsapi-rewardedstatus) answers: the state of one rewarded\nplacement for this player, from your own declared caps.\n\nEvery figure comes from **your** `rewarded_placements` config — `max_per_day`,\n`cooldown_seconds`, `max_held` — applied to this player's use of it, as the\nplatform holds them. The platform can hold a placement's daily cap below what\nyou declared, never above it. Nothing here describes the player.\n\n⚠️ **It never says whether an ad will play.** Whether one does depends on\nthings your game is deliberately not told. A player can see your button,\npress it, and get `no_ad`: read that `no_ad` like any other, and never guess\na reason.",
|
|
4178
|
+
"deprecated": null,
|
|
4179
|
+
"params": [],
|
|
4180
|
+
"returns": null,
|
|
4181
|
+
"throws": null,
|
|
4182
|
+
"example": null,
|
|
4183
|
+
"defaultValue": null,
|
|
4184
|
+
"guides": []
|
|
4185
|
+
},
|
|
4186
|
+
{
|
|
4187
|
+
"id": "RewardedStatus.declared",
|
|
4188
|
+
"name": "declared",
|
|
4189
|
+
"anchor": "rewardedstatus-declared",
|
|
4190
|
+
"group": "ads",
|
|
4191
|
+
"url": "https://gemarcade.com/developer/api/game#rewardedstatus-declared",
|
|
4192
|
+
"signature": "readonly declared: boolean",
|
|
4193
|
+
"documented": true,
|
|
4194
|
+
"summary": "Whether this channel's published config declares the placement.\n\n`false` for a placement your config does not name, a config that is not\npromoted to the channel being played, and every answer the platform did not\ngive (see [RewardedStatus.resetsAt](https://gemarcade.com/developer/api/game#rewardedstatus-resetsat)).",
|
|
4195
|
+
"deprecated": null,
|
|
4196
|
+
"params": [],
|
|
4197
|
+
"returns": null,
|
|
4198
|
+
"throws": null,
|
|
4199
|
+
"example": null,
|
|
4200
|
+
"defaultValue": null,
|
|
4201
|
+
"guides": []
|
|
4202
|
+
},
|
|
4203
|
+
{
|
|
4204
|
+
"id": "RewardedStatus.grantable",
|
|
4205
|
+
"name": "grantable",
|
|
4206
|
+
"anchor": "rewardedstatus-grantable",
|
|
4207
|
+
"group": "ads",
|
|
4208
|
+
"url": "https://gemarcade.com/developer/api/game#rewardedstatus-grantable",
|
|
4209
|
+
"signature": "readonly grantable: boolean",
|
|
4210
|
+
"documented": true,
|
|
4211
|
+
"summary": "Whether pressing your button could pay right now: the platform would grant\nthe reward if an ad is watched or, where no ad serves, under the\nplacement's `without_ads: grant`.\n\n**Not a promise that an ad will play, and not a promise of a reward.** It\nis `false` whenever a cap is spent, a cooldown is running, the player holds\n`max_held`, rewarded is off on this channel, or the arcade running your\ngame does not take ad requests at all.",
|
|
4212
|
+
"deprecated": null,
|
|
4213
|
+
"params": [],
|
|
4214
|
+
"returns": null,
|
|
4215
|
+
"throws": null,
|
|
4216
|
+
"example": null,
|
|
4217
|
+
"defaultValue": null,
|
|
4218
|
+
"guides": []
|
|
4219
|
+
},
|
|
4220
|
+
{
|
|
4221
|
+
"id": "RewardedStatus.remainingToday",
|
|
4222
|
+
"name": "remainingToday",
|
|
4223
|
+
"anchor": "rewardedstatus-remainingtoday",
|
|
4224
|
+
"group": "ads",
|
|
4225
|
+
"url": "https://gemarcade.com/developer/api/game#rewardedstatus-remainingtoday",
|
|
4226
|
+
"signature": "readonly remainingToday?: number",
|
|
4227
|
+
"documented": true,
|
|
4228
|
+
"summary": "Grants left today under the placement's daily cap, a non-negative integer.\n\nABSENT — no key, never a sentinel — when the placement has no daily cap,\nand on any answer where `declared` is `false`.",
|
|
4229
|
+
"deprecated": null,
|
|
4230
|
+
"params": [],
|
|
4231
|
+
"returns": null,
|
|
4232
|
+
"throws": null,
|
|
4233
|
+
"example": null,
|
|
4234
|
+
"defaultValue": null,
|
|
4235
|
+
"guides": []
|
|
4236
|
+
},
|
|
4237
|
+
{
|
|
4238
|
+
"id": "RewardedStatus.nextEligibleAt",
|
|
4239
|
+
"name": "nextEligibleAt",
|
|
4240
|
+
"anchor": "rewardedstatus-nexteligibleat",
|
|
4241
|
+
"group": "ads",
|
|
4242
|
+
"url": "https://gemarcade.com/developer/api/game#rewardedstatus-nexteligibleat",
|
|
4243
|
+
"signature": "readonly nextEligibleAt?: number",
|
|
4244
|
+
"documented": true,
|
|
4245
|
+
"summary": "When the placement's cooldown ends, in epoch milliseconds — render it with\n`new Date(nextEligibleAt)`.\n\nABSENT when no cooldown is running.",
|
|
4246
|
+
"deprecated": null,
|
|
4247
|
+
"params": [],
|
|
4248
|
+
"returns": null,
|
|
4249
|
+
"throws": null,
|
|
4250
|
+
"example": null,
|
|
4251
|
+
"defaultValue": null,
|
|
4252
|
+
"guides": []
|
|
4253
|
+
},
|
|
4254
|
+
{
|
|
4255
|
+
"id": "RewardedStatus.heldLimitReached",
|
|
4256
|
+
"name": "heldLimitReached",
|
|
4257
|
+
"anchor": "rewardedstatus-heldlimitreached",
|
|
4258
|
+
"group": "ads",
|
|
4259
|
+
"url": "https://gemarcade.com/developer/api/game#rewardedstatus-heldlimitreached",
|
|
4260
|
+
"signature": "readonly heldLimitReached: boolean",
|
|
4261
|
+
"documented": true,
|
|
4262
|
+
"summary": "Whether the player already holds the placement's `max_held` of the item it\ngrants. Spend one first.",
|
|
4263
|
+
"deprecated": null,
|
|
4264
|
+
"params": [],
|
|
4265
|
+
"returns": null,
|
|
4266
|
+
"throws": null,
|
|
4267
|
+
"example": null,
|
|
4268
|
+
"defaultValue": null,
|
|
4269
|
+
"guides": []
|
|
4270
|
+
},
|
|
4271
|
+
{
|
|
4272
|
+
"id": "RewardedStatus.resetsAt",
|
|
4273
|
+
"name": "resetsAt",
|
|
4274
|
+
"anchor": "rewardedstatus-resetsat",
|
|
4275
|
+
"group": "ads",
|
|
4276
|
+
"url": "https://gemarcade.com/developer/api/game#rewardedstatus-resetsat",
|
|
4277
|
+
"signature": "readonly resetsAt?: number",
|
|
4278
|
+
"documented": true,
|
|
4279
|
+
"summary": "When the daily count resets — the next 00:00 UTC — in epoch milliseconds.\nTake it from here rather than computing midnight in game code.\n\nABSENT when the platform gave no answer: no play token yet, a read that\nfailed or took longer than ten seconds, or a platform that does not serve\nthis read. Then the rest of the status reads not declared and not\ngrantable, and the reason went to your `onError` handler.",
|
|
4280
|
+
"deprecated": null,
|
|
4281
|
+
"params": [],
|
|
4282
|
+
"returns": null,
|
|
4283
|
+
"throws": null,
|
|
4284
|
+
"example": null,
|
|
4285
|
+
"defaultValue": null,
|
|
4286
|
+
"guides": []
|
|
4287
|
+
},
|
|
4116
4288
|
{
|
|
4117
4289
|
"id": "AnalyticsValue",
|
|
4118
4290
|
"name": "AnalyticsValue",
|
|
@@ -6104,6 +6276,30 @@
|
|
|
6104
6276
|
"defaultValue": null,
|
|
6105
6277
|
"guides": []
|
|
6106
6278
|
},
|
|
6279
|
+
{
|
|
6280
|
+
"id": "GemFrame.showPortraitBorders",
|
|
6281
|
+
"name": "showPortraitBorders",
|
|
6282
|
+
"anchor": "gemframe-showportraitborders",
|
|
6283
|
+
"group": "frame",
|
|
6284
|
+
"url": "https://gemarcade.com/developer/api/game#gemframe-showportraitborders",
|
|
6285
|
+
"signature": "showPortraitBorders(name: string | null): boolean",
|
|
6286
|
+
"documented": true,
|
|
6287
|
+
"summary": "Tell the arcade which of your store page's portrait borders to paint beside\nyour game, or `null` for none.\n\nPrefer `gem.showPortraitBorders`, which holds your choice until the bridge\nis up and paces a burst of changes for you. This one sends every call it\naccepts, only while connected, and only to an arcade that paints borders.",
|
|
6288
|
+
"deprecated": null,
|
|
6289
|
+
"params": [
|
|
6290
|
+
{
|
|
6291
|
+
"name": "name",
|
|
6292
|
+
"type": "string | null",
|
|
6293
|
+
"optional": false,
|
|
6294
|
+
"summary": null
|
|
6295
|
+
}
|
|
6296
|
+
],
|
|
6297
|
+
"returns": "whether the message left the frame — `false` for a string that is\n not a border name (with a console warning), before the bridge is up, for\n an arcade that paints no borders, and past this frame's own limit.",
|
|
6298
|
+
"throws": null,
|
|
6299
|
+
"example": null,
|
|
6300
|
+
"defaultValue": null,
|
|
6301
|
+
"guides": []
|
|
6302
|
+
},
|
|
6107
6303
|
{
|
|
6108
6304
|
"id": "GemFrame.setWalletVisible",
|
|
6109
6305
|
"name": "setWalletVisible",
|
|
@@ -6387,7 +6583,7 @@
|
|
|
6387
6583
|
"url": "https://gemarcade.com/developer/api/game#friend-badges",
|
|
6388
6584
|
"signature": "readonly badges: readonly PlayerBadge[]",
|
|
6389
6585
|
"documented": true,
|
|
6390
|
-
"summary": "The player's equipped badges, in
|
|
6586
|
+
"summary": "The player's equipped badges, at most two, in the order the player\narranged them. Always a list — empty when they wear none, or when their\nidentity is withheld — so draw what is there and key on `itemId`.",
|
|
6391
6587
|
"deprecated": null,
|
|
6392
6588
|
"params": [],
|
|
6393
6589
|
"returns": null,
|
|
@@ -7338,9 +7534,9 @@
|
|
|
7338
7534
|
"anchor": "functionfailure",
|
|
7339
7535
|
"group": "functions",
|
|
7340
7536
|
"url": "https://gemarcade.com/developer/api/game#functionfailure",
|
|
7341
|
-
"signature": "type FunctionFailure = \"trap\" | \"memory_exceeded\" | \"cpu_budget_exceeded\" | \"host_call_limit\"",
|
|
7537
|
+
"signature": "type FunctionFailure = \"trap\" | \"memory_exceeded\" | \"cpu_budget_exceeded\" | \"host_call_limit\" | \"output_too_large\" | \"argument_too_large\"",
|
|
7342
7538
|
"documented": true,
|
|
7343
|
-
"summary": "Why a call that reached your function did not finish — carried on\n[GemFunctionError.failure](https://gemarcade.com/developer/api/game#gemfunctionerror-failure).\n\n- `trap` — a panic, an `unreachable`, an out-of-bounds access or a stack\n overflow. No stack trace comes back.\n- `memory_exceeded` — past the memory limit.\n- `cpu_budget_exceeded` — past the CPU budget.\n- `host_call_limit` — more platform API calls than one invocation may make.",
|
|
7539
|
+
"summary": "Why a call that reached your function did not finish — carried on\n[GemFunctionError.failure](https://gemarcade.com/developer/api/game#gemfunctionerror-failure).\n\n- `trap` — a panic, an `unreachable`, an out-of-bounds access or a stack\n overflow. No stack trace comes back.\n- `memory_exceeded` — past the memory limit.\n- `cpu_budget_exceeded` — past the CPU budget.\n- `host_call_limit` — more platform API calls than one invocation may make.\n- `output_too_large` — your function's result was over the 1 MiB output cap.\n- `argument_too_large` — a value your function handed to a platform call\n (`log`, `call-api`, `download-link` or `upload-link`) was over the 16 MiB\n per-call limit.",
|
|
7344
7540
|
"deprecated": null,
|
|
7345
7541
|
"params": [],
|
|
7346
7542
|
"returns": null,
|
|
@@ -7574,7 +7770,7 @@
|
|
|
7574
7770
|
"url": "https://gemarcade.com/developer/api/game#arcade_embedders",
|
|
7575
7771
|
"signature": "ARCADE_EMBEDDERS: readonly string[]",
|
|
7576
7772
|
"documented": true,
|
|
7577
|
-
"summary": "The origins the
|
|
7773
|
+
"summary": "The origins the platform embeds a game from, which every game accepts\nwithout naming them: the Gem Arcade website, the Meme Games website, and\nthe Gem Arcade app.\n\nYou do not pass these. They are always allowed, and\n[GemOptions.allowedEmbedders](https://gemarcade.com/developer/api/game#gemoptions-allowedembedders) adds to them rather than replacing them.",
|
|
7578
7774
|
"deprecated": null,
|
|
7579
7775
|
"params": [],
|
|
7580
7776
|
"returns": null,
|
|
@@ -7608,7 +7804,7 @@
|
|
|
7608
7804
|
"url": "https://gemarcade.com/developer/api/game#gemoptions-allowedembedders",
|
|
7609
7805
|
"signature": "readonly allowedEmbedders?: readonly string[]",
|
|
7610
7806
|
"documented": true,
|
|
7611
|
-
"summary": "Extra origins allowed to embed your game, **added to** the ones the Gem\nArcade uses. Most games leave this out.\n\nThe
|
|
7807
|
+
"summary": "Extra origins allowed to embed your game, **added to** the ones the Gem\nArcade uses. Most games leave this out.\n\nThe platform's websites and the Gem Arcade app are always allowed — see\n[ARCADE_EMBEDDERS](https://gemarcade.com/developer/api/game#arcade_embedders) — so do not list them. Set this only when a site\nof your own embeds your game.\n\n```js\ncreateGem({ allowedEmbedders: ['https://my-own-site.example'] })\n```\n\nA page whose origin is not allowed gets no connection, and — deliberately —\nis told nothing about why. That means a typo here looks exactly like a\nhandshake that has not arrived: pass [GemOptions.onRefused](https://gemarcade.com/developer/api/game#gemoptions-onrefused) while you\nare integrating, because it is the only place the difference shows.\n\n⚠️ **Bake these in. Never build them from anything the page can\ninfluence** — not `location`, not a query parameter, not\n`document.referrer`. A value the embedder supplies is a check that can never\nfail. Do not add a `localhost:<port>` entry for your dev host either; the\nSDK admits one on your own machine by rule.",
|
|
7612
7808
|
"deprecated": null,
|
|
7613
7809
|
"params": [],
|
|
7614
7810
|
"returns": null,
|
|
@@ -8266,7 +8462,7 @@
|
|
|
8266
8462
|
"url": "https://gemarcade.com/developer/api/game#gemplayer-badges",
|
|
8267
8463
|
"signature": "readonly badges: readonly PlayerBadge[]",
|
|
8268
8464
|
"documented": true,
|
|
8269
|
-
"summary": "The player's equipped badges, in
|
|
8465
|
+
"summary": "The player's equipped badges, at most two, in the order the player\narranged them. Always a list — empty when they wear none, or when their\nidentity is withheld — so draw what is there and key on `itemId`.",
|
|
8270
8466
|
"deprecated": null,
|
|
8271
8467
|
"params": [],
|
|
8272
8468
|
"returns": null,
|
|
@@ -9189,7 +9385,7 @@
|
|
|
9189
9385
|
"url": "https://gemarcade.com/developer/api/game#gem-ads",
|
|
9190
9386
|
"signature": "readonly ads: AdsApi",
|
|
9191
9387
|
"documented": true,
|
|
9192
|
-
"summary": "One full-screen cross-promotion at a natural break in play, rendered by\nthe arcade over your paused game.\n\nAsk at a break the player already expects — a level ending, a round\nfinishing — never mid-input. **The answer is usually no, and that is\nordinary rather than a failure:** carry straight on, and draw no\nplaceholder while you wait.\n\nNothing here rejects. The arcade pauses your game while a panel is up and\nresumes it when the promise settles, so you do not pause anything yourself.\n\n⚠️ `shown` **grants nothing.** It is not a receipt and not an entitlement,\nand a reward must never be handed out on the strength of it.\n\nYou are told no reason for a no, and there is nothing to infer from one.",
|
|
9388
|
+
"summary": "One full-screen cross-promotion at a natural break in play, rendered by\nthe arcade over your paused game.\n\nAsk at a break the player already expects — a level ending, a round\nfinishing — never mid-input. **The answer is usually no, and that is\nordinary rather than a failure:** carry straight on, and draw no\nplaceholder while you wait.\n\nNothing here rejects. The arcade pauses your game while a panel is up and\nresumes it when the promise settles, so you do not pause anything yourself.\n\n⚠️ `shown` **grants nothing.** It is not a receipt and not an entitlement,\nand a reward must never be handed out on the strength of it.\n\nYou are told no reason for a no, and there is nothing to infer from one\nabout the player. What you can read is your own economy:\n`rewardedStatus(placement)` says whether a rewarded placement could pay\nright now under your own declared caps — never whether an ad will play.",
|
|
9193
9389
|
"deprecated": null,
|
|
9194
9390
|
"params": [],
|
|
9195
9391
|
"returns": null,
|
|
@@ -9544,6 +9740,30 @@
|
|
|
9544
9740
|
"defaultValue": null,
|
|
9545
9741
|
"guides": []
|
|
9546
9742
|
},
|
|
9743
|
+
{
|
|
9744
|
+
"id": "Gem.showPortraitBorders",
|
|
9745
|
+
"name": "showPortraitBorders",
|
|
9746
|
+
"anchor": "gem-showportraitborders",
|
|
9747
|
+
"group": "start",
|
|
9748
|
+
"url": "https://gemarcade.com/developer/api/game#gem-showportraitborders",
|
|
9749
|
+
"signature": "showPortraitBorders(name: string | null): void",
|
|
9750
|
+
"documented": true,
|
|
9751
|
+
"summary": "Choose which of your store page's **portrait borders** the arcade paints on\neither side of your game, or `null` for none.\n\nOn a desktop browser, the arcade's desktop app or a television, the arcade\nshows a portrait-only game as a centred portrait column. With borders\ndeclared on your store page (`media.portrait_borders`), it paints one of\nthose images beside the column instead of its own bars. Call this at\nstart-up and on every theme change, with the border that goes with what is\non screen:\n\n```js\nconst BORDERS = new Set(['meadow', 'alpine']); // the names your store page declares\n\nfunction applyTheme(id) {\n document.body.dataset.theme = id;\n gem.showPortraitBorders(BORDERS.has(id) ? id : null); // null: a theme with no art\n}\n```\n\nUntil your first call the arcade shows the page's first border. Whether\nyour game is letterboxed at all is decided by your store page's\n`orientations`, never by this call, so call it unconditionally: before the\nconnection exists your choice is held and sent when it connects, a burst of\nchanges (a picker previewing on hover) reaches the arcade as its final\nvalue, and an arcade that paints no borders is simply not told. The arcade\nowns the change itself — when and how the image swaps — and sends no\nacknowledgement.\n\nA name is 1 to 40 characters of `a-z`, `0-9`, `_` and `-`, starting with a\nletter or digit — the store page's own rule. Anything else is refused with\na console warning and never sent; a valid name your page does not declare\nis ignored by the arcade, which keeps the image it is showing.",
|
|
9752
|
+
"deprecated": null,
|
|
9753
|
+
"params": [
|
|
9754
|
+
{
|
|
9755
|
+
"name": "name",
|
|
9756
|
+
"type": "string | null",
|
|
9757
|
+
"optional": false,
|
|
9758
|
+
"summary": null
|
|
9759
|
+
}
|
|
9760
|
+
],
|
|
9761
|
+
"returns": null,
|
|
9762
|
+
"throws": null,
|
|
9763
|
+
"example": null,
|
|
9764
|
+
"defaultValue": null,
|
|
9765
|
+
"guides": []
|
|
9766
|
+
},
|
|
9547
9767
|
{
|
|
9548
9768
|
"id": "Gem.cueReward",
|
|
9549
9769
|
"name": "cueReward",
|
|
@@ -10789,7 +11009,7 @@
|
|
|
10789
11009
|
"url": "https://gemarcade.com/developer/api/game#playerbadge",
|
|
10790
11010
|
"signature": "interface PlayerBadge",
|
|
10791
11011
|
"documented": true,
|
|
10792
|
-
"summary": "One badge a player has equipped — an icon the platform shows beside their\nname.\n\
|
|
11012
|
+
"summary": "One badge a player has equipped — an icon the platform shows beside their\nname.\n\nA player wears at most two, in the order they arranged them. Each is an icon\nthey own — an achievement in any game may have awarded it.",
|
|
10793
11013
|
"deprecated": null,
|
|
10794
11014
|
"params": [],
|
|
10795
11015
|
"returns": null,
|
|
@@ -10959,7 +11179,7 @@
|
|
|
10959
11179
|
"url": "https://gemarcade.com/developer/api/game#playeridentity-badges",
|
|
10960
11180
|
"signature": "readonly badges: readonly PlayerBadge[]",
|
|
10961
11181
|
"documented": true,
|
|
10962
|
-
"summary": "The player's equipped badges, in
|
|
11182
|
+
"summary": "The player's equipped badges, at most two, in the order the player\narranged them. Always a list — empty when they wear none, or when their\nidentity is withheld — so draw what is there and key on `itemId`.",
|
|
10963
11183
|
"deprecated": null,
|
|
10964
11184
|
"params": [],
|
|
10965
11185
|
"returns": null,
|
|
@@ -13886,7 +14106,7 @@
|
|
|
13886
14106
|
"url": "https://gemarcade.com/developer/api/game#leaderboardentry-badges",
|
|
13887
14107
|
"signature": "readonly badges: readonly PlayerBadge[]",
|
|
13888
14108
|
"documented": true,
|
|
13889
|
-
"summary": "The player's equipped badges, in
|
|
14109
|
+
"summary": "The player's equipped badges, at most two, in the order the player\narranged them. Always a list — empty when they wear none, or when their\nidentity is withheld — so draw what is there and key on `itemId`.",
|
|
13890
14110
|
"deprecated": null,
|
|
13891
14111
|
"params": [],
|
|
13892
14112
|
"returns": null,
|
|
@@ -15759,7 +15979,7 @@
|
|
|
15759
15979
|
"url": "https://gemarcade.com/developer/api/game#playerprofile-badges",
|
|
15760
15980
|
"signature": "readonly badges: readonly PlayerBadge[]",
|
|
15761
15981
|
"documented": true,
|
|
15762
|
-
"summary": "The player's equipped badges, in
|
|
15982
|
+
"summary": "The player's equipped badges, at most two, in the order the player\narranged them. Always a list — empty when they wear none, or when their\nidentity is withheld — so draw what is there and key on `itemId`.",
|
|
15763
15983
|
"deprecated": null,
|
|
15764
15984
|
"params": [],
|
|
15765
15985
|
"returns": null,
|
|
@@ -18454,7 +18674,7 @@
|
|
|
18454
18674
|
"url": "https://gemarcade.com/developer/api/game#roommember-badges",
|
|
18455
18675
|
"signature": "readonly badges: readonly PlayerBadge[]",
|
|
18456
18676
|
"documented": true,
|
|
18457
|
-
"summary": "The player's equipped badges, in
|
|
18677
|
+
"summary": "The player's equipped badges, at most two, in the order the player\narranged them. Always a list — empty when they wear none, or when their\nidentity is withheld — so draw what is there and key on `itemId`.",
|
|
18458
18678
|
"deprecated": null,
|
|
18459
18679
|
"params": [],
|
|
18460
18680
|
"returns": null,
|
|
@@ -19605,7 +19825,7 @@
|
|
|
19605
19825
|
"url": "https://gemarcade.com/developer/api/game#showcasefriendentry-badges",
|
|
19606
19826
|
"signature": "readonly badges: readonly PlayerBadge[]",
|
|
19607
19827
|
"documented": true,
|
|
19608
|
-
"summary": "The player's equipped badges, in
|
|
19828
|
+
"summary": "The player's equipped badges, at most two, in the order the player\narranged them. Always a list — empty when they wear none, or when their\nidentity is withheld — so draw what is there and key on `itemId`.",
|
|
19609
19829
|
"deprecated": null,
|
|
19610
19830
|
"params": [],
|
|
19611
19831
|
"returns": null,
|
|
@@ -21731,9 +21951,9 @@
|
|
|
21731
21951
|
"anchor": "functionfailure",
|
|
21732
21952
|
"group": "functions",
|
|
21733
21953
|
"url": "https://gemarcade.com/developer/api/server#functionfailure",
|
|
21734
|
-
"signature": "type FunctionFailure = \"trap\" | \"memory_exceeded\" | \"cpu_budget_exceeded\" | \"host_call_limit\"",
|
|
21954
|
+
"signature": "type FunctionFailure = \"trap\" | \"memory_exceeded\" | \"cpu_budget_exceeded\" | \"host_call_limit\" | \"output_too_large\" | \"argument_too_large\"",
|
|
21735
21955
|
"documented": true,
|
|
21736
|
-
"summary": "Why a call that reached your function did not finish — carried on\n[GemFunctionError.failure](https://gemarcade.com/developer/api/server#gemfunctionerror-failure).\n\n- `trap` — a panic, an `unreachable`, an out-of-bounds access or a stack\n overflow. No stack trace comes back.\n- `memory_exceeded` — past the memory limit.\n- `cpu_budget_exceeded` — past the CPU budget.\n- `host_call_limit` — more platform API calls than one invocation may make.",
|
|
21956
|
+
"summary": "Why a call that reached your function did not finish — carried on\n[GemFunctionError.failure](https://gemarcade.com/developer/api/server#gemfunctionerror-failure).\n\n- `trap` — a panic, an `unreachable`, an out-of-bounds access or a stack\n overflow. No stack trace comes back.\n- `memory_exceeded` — past the memory limit.\n- `cpu_budget_exceeded` — past the CPU budget.\n- `host_call_limit` — more platform API calls than one invocation may make.\n- `output_too_large` — your function's result was over the 1 MiB output cap.\n- `argument_too_large` — a value your function handed to a platform call\n (`log`, `call-api`, `download-link` or `upload-link`) was over the 16 MiB\n per-call limit.",
|
|
21737
21957
|
"deprecated": null,
|
|
21738
21958
|
"params": [],
|
|
21739
21959
|
"returns": null,
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,29 @@ 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
|
+
## 9.0.0
|
|
20
|
+
|
|
21
|
+
Upgrading from 8.1.0. Impact: required.
|
|
22
|
+
|
|
23
|
+
### Must change
|
|
24
|
+
|
|
25
|
+
- FunctionFailure now has two more reasons, output_too_large and argument_too_large. A function that returns more than the output cap, or passes a platform call a value over the per-call size limit, used to fail as trap. It now fails with the matching new reason. Update any code that switches on FunctionFailure so it handles both new reasons. An exhaustive check over the old four reasons will stop compiling until you do. If your game treated trap as the sign of an oversized output or argument, handle the new reasons there too. Then rebuild your game against this release.
|
|
26
|
+
|
|
27
|
+
## 8.1.0
|
|
28
|
+
|
|
29
|
+
Upgrading from 8.0.0. Impact: required.
|
|
30
|
+
|
|
31
|
+
### Rebuild
|
|
32
|
+
|
|
33
|
+
- The list of messages a frame sends to its parent, the FrameMessage type and the GemCapability type each gained a portrait borders member. Recompiling is enough for most games. If your code handles every FrameMessage or GemCapability member exhaustively, add a case for the new one so it keeps compiling.
|
|
34
|
+
|
|
35
|
+
### New capability
|
|
36
|
+
|
|
37
|
+
- A game can now ask whether a rewarded placement could pay right now, before it draws its reward button, by calling AdsApi.rewardedStatus with the placement. The answer is a RewardedStatus saying whether the placement is declared, whether it is grantable now, how many grants remain today, when it is next eligible, whether the held limit is reached, and when the allowance resets. Times are in epoch milliseconds. The call never rejects. A failed read, a missing play token or a bad placement resolves as not declared and not grantable, and the read gives up after ten seconds. A placement only counts as grantable when the Arcade you run in supports ads. Failures are reported once per kind through your error handler.
|
|
38
|
+
- A game can now choose which border image from its store page the Arcade paints beside it in portrait by calling Gem.showPortraitBorders with the image's name. The name is a plain string of at most 40 characters, and one that fails the store page's naming rule is refused whole with a console warning. The choice is held until the bridge connects, sent at most once a second with the latest choice winning, and not resent if it matches what was last posted. If the Arcade does not advertise the portraitBorders capability, nothing is sent and nothing is raised.
|
|
39
|
+
- If you embed a game in your own frame, GemFrame.showPortraitBorders lets you send the same border choice, or clear it. It sends only while connected and only to a parent that advertised the portraitBorders capability, and it applies the same name rule as the game side.
|
|
40
|
+
- If you host games, you can now pass onPortraitBorders in your host options to receive the border name a game chose. The new GemPortraitBorders message carries it, and validateGemPortraitBorders checks a message you receive. The host re-checks the name on receipt and drops one that no store page could declare before your callback sees it.
|
|
41
|
+
|
|
19
42
|
## 8.0.0
|
|
20
43
|
|
|
21
44
|
*Written from the API facts alone; no summary was generated for this release.*
|