@indiegems/gem-web-sdk 23.1.3 → 23.2.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 +12 -0
- package/dist/game.d.ts +10 -1
- package/dist/gem-web-sdk.conformance.js +1 -1
- package/dist/gem-web-sdk.esm.js +1 -1
- 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.js +1 -1
- package/dist/sdk.d.ts +10 -1
- package/docs/api/api-reference-function.md +1 -1
- package/docs/api/api-reference-game.md +101 -6
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +100 -4
- package/docs/changelog.md +12 -0
- package/docs/getting-started.md +1 -1
- package/docs/manifest.json +8 -8
- package/docs/rewarded-ads.md +82 -8
- package/docs/server-bundles.md +2 -2
- package/package.json +1 -1
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": "23.
|
|
5
|
+
"version": "23.2.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -397,7 +397,7 @@
|
|
|
397
397
|
"group": "config",
|
|
398
398
|
"signature": "rewarded_placements?: SnapshotRewardedPlacement[]",
|
|
399
399
|
"documented": true,
|
|
400
|
-
"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`, which is `nothing` when the config leaves it out).\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](api-reference-game.md#gameconfig-item) or\n[GameConfig.currency](api-reference-game.md#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](api-reference-game.md#adsapi-rewardedstatus).\n\nAbsent when your game declares none.",
|
|
400
|
+
"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`, which is `nothing` when the config leaves it out).\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](api-reference-game.md#gameconfig-item) or\n[GameConfig.currency](api-reference-game.md#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](api-reference-game.md#adsapi-rewardedstatus).\n\nA row may also carry `platform_prices`, the Coin and Gem prices that unlock\nit beside the ad, and `ad: false` when only a price does (`ad` is absent\notherwise, which means the ad unlocks it). Both are what you declared, not\nwhat is offered now: label the button from `rewardedStatus`'s\n`adGrantable` and `options`, which say what this platform serves.\n\nAbsent when your game declares none.",
|
|
401
401
|
"deprecated": null,
|
|
402
402
|
"params": [],
|
|
403
403
|
"returns": null,
|
|
@@ -4170,7 +4170,7 @@
|
|
|
4170
4170
|
"group": "ads",
|
|
4171
4171
|
"signature": "rewardedStatus(placement: string): Promise<RewardedStatus>",
|
|
4172
4172
|
"documented": true,
|
|
4173
|
-
"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](api-reference-game.md#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.",
|
|
4173
|
+
"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\nA placement your config also prices in Coins or Gems (`platform_prices`)\ncarries those prices in `options`, where the platform lets players pay for\none, and `adGrantable` says whether the ad itself could pay. Label your\nbutton from both: name the ad only when `adGrantable` is `true`, and each\nprice in `options`. The Arcade then shows the player those choices when\nthey press, and the press answers and is re-read exactly as an ad's is.\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](api-reference-game.md#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.",
|
|
4174
4174
|
"deprecated": null,
|
|
4175
4175
|
"params": [
|
|
4176
4176
|
{
|
|
@@ -4191,6 +4191,70 @@
|
|
|
4191
4191
|
}
|
|
4192
4192
|
]
|
|
4193
4193
|
},
|
|
4194
|
+
{
|
|
4195
|
+
"id": "RewardedOption",
|
|
4196
|
+
"name": "RewardedOption",
|
|
4197
|
+
"anchor": "rewardedoption",
|
|
4198
|
+
"group": "ads",
|
|
4199
|
+
"signature": "interface RewardedOption",
|
|
4200
|
+
"documented": true,
|
|
4201
|
+
"summary": "One price that also unlocks a rewarded placement, in\n[RewardedStatus.options](api-reference-game.md#rewardedstatus-options): what your config's `platform_prices` set for\nit, as the platform serves it. Label your button from it (\"5 Gems\").\n\nIt says nothing about the player: not their balance, and not whether they\ncan afford it. The Arcade shows them that when they press.",
|
|
4202
|
+
"deprecated": null,
|
|
4203
|
+
"params": [],
|
|
4204
|
+
"returns": null,
|
|
4205
|
+
"throws": null,
|
|
4206
|
+
"example": null,
|
|
4207
|
+
"defaultValue": null,
|
|
4208
|
+
"guides": []
|
|
4209
|
+
},
|
|
4210
|
+
{
|
|
4211
|
+
"id": "RewardedOption.kind",
|
|
4212
|
+
"name": "kind",
|
|
4213
|
+
"anchor": "rewardedoption-kind",
|
|
4214
|
+
"group": "ads",
|
|
4215
|
+
"signature": "readonly kind: \"coins\" | \"gems\"",
|
|
4216
|
+
"documented": true,
|
|
4217
|
+
"summary": "Which platform currency: `coins` (Coins, `ARCADE_TICKETS`) or `gems`\n(Gems, `GEMS`).",
|
|
4218
|
+
"deprecated": null,
|
|
4219
|
+
"params": [],
|
|
4220
|
+
"returns": null,
|
|
4221
|
+
"throws": null,
|
|
4222
|
+
"example": null,
|
|
4223
|
+
"defaultValue": null,
|
|
4224
|
+
"guides": []
|
|
4225
|
+
},
|
|
4226
|
+
{
|
|
4227
|
+
"id": "RewardedOption.currencyId",
|
|
4228
|
+
"name": "currencyId",
|
|
4229
|
+
"anchor": "rewardedoption-currencyid",
|
|
4230
|
+
"group": "ads",
|
|
4231
|
+
"signature": "readonly currencyId: string",
|
|
4232
|
+
"documented": true,
|
|
4233
|
+
"summary": "The platform currency's id, as your config's `platform_prices` resolved it.",
|
|
4234
|
+
"deprecated": null,
|
|
4235
|
+
"params": [],
|
|
4236
|
+
"returns": null,
|
|
4237
|
+
"throws": null,
|
|
4238
|
+
"example": null,
|
|
4239
|
+
"defaultValue": null,
|
|
4240
|
+
"guides": []
|
|
4241
|
+
},
|
|
4242
|
+
{
|
|
4243
|
+
"id": "RewardedOption.price",
|
|
4244
|
+
"name": "price",
|
|
4245
|
+
"anchor": "rewardedoption-price",
|
|
4246
|
+
"group": "ads",
|
|
4247
|
+
"signature": "readonly price: number",
|
|
4248
|
+
"documented": true,
|
|
4249
|
+
"summary": "The price, a positive whole number of the currency.",
|
|
4250
|
+
"deprecated": null,
|
|
4251
|
+
"params": [],
|
|
4252
|
+
"returns": null,
|
|
4253
|
+
"throws": null,
|
|
4254
|
+
"example": null,
|
|
4255
|
+
"defaultValue": null,
|
|
4256
|
+
"guides": []
|
|
4257
|
+
},
|
|
4194
4258
|
{
|
|
4195
4259
|
"id": "RewardedStatus",
|
|
4196
4260
|
"name": "RewardedStatus",
|
|
@@ -4230,7 +4294,39 @@
|
|
|
4230
4294
|
"group": "ads",
|
|
4231
4295
|
"signature": "readonly grantable: boolean",
|
|
4232
4296
|
"documented": true,
|
|
4233
|
-
"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
|
|
4297
|
+
"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` — or, where [RewardedStatus.options](api-reference-game.md#rewardedstatus-options)\nis present, if the player pays one of its prices.\n\n**Not a promise that an ad will play, and not a promise of a reward.** For\nthe ad, it is `false` whenever a cap is spent, a cooldown is running, the\nplayer holds `max_held`, rewarded is off on this channel, or the arcade\nrunning your game does not take ad requests at all. The cap, the cooldown\nand `max_held` limit the ad only: a placement with `options` stays\ngrantable through them, because the player can still pay. It never says\nwhether the player can afford a price.",
|
|
4298
|
+
"deprecated": null,
|
|
4299
|
+
"params": [],
|
|
4300
|
+
"returns": null,
|
|
4301
|
+
"throws": null,
|
|
4302
|
+
"example": null,
|
|
4303
|
+
"defaultValue": null,
|
|
4304
|
+
"guides": []
|
|
4305
|
+
},
|
|
4306
|
+
{
|
|
4307
|
+
"id": "RewardedStatus.adGrantable",
|
|
4308
|
+
"name": "adGrantable",
|
|
4309
|
+
"anchor": "rewardedstatus-adgrantable",
|
|
4310
|
+
"group": "ads",
|
|
4311
|
+
"signature": "readonly adGrantable: boolean",
|
|
4312
|
+
"documented": true,
|
|
4313
|
+
"summary": "Whether the AD could pay right now: [RewardedStatus.grantable](api-reference-game.md#rewardedstatus-grantable) for\nthe ad alone, leaving out the prices. Name the ad on your button only when\nthis is `true`.\n\nThe same as `grantable` for a placement with no `options`. `false` for a\nplacement whose config switches the ad off (`ad: false`) where the\nplatform lets players pay for it, and on every answer where `grantable` is.",
|
|
4314
|
+
"deprecated": null,
|
|
4315
|
+
"params": [],
|
|
4316
|
+
"returns": null,
|
|
4317
|
+
"throws": null,
|
|
4318
|
+
"example": null,
|
|
4319
|
+
"defaultValue": null,
|
|
4320
|
+
"guides": []
|
|
4321
|
+
},
|
|
4322
|
+
{
|
|
4323
|
+
"id": "RewardedStatus.options",
|
|
4324
|
+
"name": "options",
|
|
4325
|
+
"anchor": "rewardedstatus-options",
|
|
4326
|
+
"group": "ads",
|
|
4327
|
+
"signature": "readonly options?: readonly RewardedOption[]",
|
|
4328
|
+
"documented": true,
|
|
4329
|
+
"summary": "The Coin and Gem prices that also unlock the placement, from your config's\n`platform_prices`, at most one per currency.\n\nABSENT — no key — for a placement with no prices, on any answer where\n`declared` is `false`, and wherever the platform does not yet let players\npay for a placement: there your prices are not offered and the placement\nunlocks by its ad alone. Nothing here says what the player holds.",
|
|
4234
4330
|
"deprecated": null,
|
|
4235
4331
|
"params": [],
|
|
4236
4332
|
"returns": null,
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,18 @@ 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.2.0
|
|
20
|
+
|
|
21
|
+
*Written from the API facts alone; no summary was generated for this release.*
|
|
22
|
+
|
|
23
|
+
Upgrading from 23.1.3. Impact: none.
|
|
24
|
+
|
|
25
|
+
### New capability
|
|
26
|
+
|
|
27
|
+
- RewardedOption was added.
|
|
28
|
+
- RewardedStatus.adGrantable was added.
|
|
29
|
+
- RewardedStatus.options was added.
|
|
30
|
+
|
|
19
31
|
## 23.1.3
|
|
20
32
|
|
|
21
33
|
Upgrading from 23.1.2. Impact: none.
|
package/docs/getting-started.md
CHANGED
package/docs/manifest.json
CHANGED
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"file": "changelog.md",
|
|
51
|
-
"sha256": "
|
|
52
|
-
"updated": "2026-11-
|
|
51
|
+
"sha256": "819160e36c0258c34aa3e8b06ea0f1e26a50bcc83c5919780de2b33f4fac1cd9",
|
|
52
|
+
"updated": "2026-11-09T16:19:51Z"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"file": "daily-challenges.md",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"file": "getting-started.md",
|
|
81
|
-
"sha256": "
|
|
82
|
-
"updated": "2026-11-
|
|
81
|
+
"sha256": "763113c6da4ae1d154ba655e44e87f32912a66ea7db40a61dca9c17f51fcc234",
|
|
82
|
+
"updated": "2026-11-09T16:19:51Z"
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"file": "graphics-route.md",
|
|
@@ -123,8 +123,8 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"file": "rewarded-ads.md",
|
|
126
|
-
"sha256": "
|
|
127
|
-
"updated": "2026-11-
|
|
126
|
+
"sha256": "f632d54aa605b34dc071b59d49e7ecfd35ab95eb6f601885ceb0d3d6fc41a949",
|
|
127
|
+
"updated": "2026-11-09T15:28:20Z"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
"file": "room-shapes.md",
|
|
@@ -143,8 +143,8 @@
|
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"file": "server-bundles.md",
|
|
146
|
-
"sha256": "
|
|
147
|
-
"updated": "2026-11-
|
|
146
|
+
"sha256": "b712e407e79482f1edc596ebcaab00852429fbaacebb05d04a2a2f9cbe1f5b9c",
|
|
147
|
+
"updated": "2026-11-09T16:19:51Z"
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"file": "serverless-functions-js.md",
|
package/docs/rewarded-ads.md
CHANGED
|
@@ -111,6 +111,8 @@ rewarded_placements:
|
|
|
111
111
|
| `max_held` | No grant while the player already holds at least this many of the granted item. 1 to 100, and only on a placement whose grants are all items. |
|
|
112
112
|
| `without_ads` | **Optional:** `grant` or `nothing`, and `nothing` when you leave it out. What a press gets when no ad plays: where no ad serves your channel at all, and where one does but no ad was available for this press or it failed to play (§8). `grant` pays the reward with no ad, within your caps, so write it out only for a reward that is fine to give free. |
|
|
113
113
|
| `allowance_currency_slug` | Optional: one of your own currencies that each grant also spends 1 of. Use it when another path in your game must share the same daily cap. It must not be `client_writable` and must have `transfer_policy: none`. A grant is refused while it is empty (§3). ⚠️ For an allowance your game grants per use, put it in a bucket that never resets (`reset_interval: never`) for now, even though the upload warns `rewarded_placement_allowance_no_reset` (below). |
|
|
114
|
+
| `ad` | **Optional:** `true` or `false`, and `true` when you leave it out. Whether watching an ad unlocks the placement. Write `false` only beside `platform_prices`, for a placement a price alone unlocks. |
|
|
115
|
+
| `platform_prices` | **Optional:** Coin and Gem prices that also unlock the placement, beside the ad or instead of it, written as an item's prices are. Leave it out for none. See [Coins and Gems beside the ad](#coins-and-gems-beside-the-ad), below. |
|
|
114
116
|
|
|
115
117
|
⚠️ **Until a gem-api fix ships, an allowance in a resetting bucket refuses a player's first
|
|
116
118
|
press.** A grant that creates the player's balance of the allowance currency is not stamped with
|
|
@@ -170,6 +172,60 @@ pays without one. If both said `grant`, the bigger reward would cost the player
|
|
|
170
172
|
everyone would press that button. Leaving it out of the bigger one, which is `nothing`, keeps it a
|
|
171
173
|
reward for watching.
|
|
172
174
|
|
|
175
|
+
### Coins and Gems beside the ad
|
|
176
|
+
|
|
177
|
+
A placement can also be unlocked for the player's **Coins** (`ARCADE_TICKETS`) or **Gems** (`GEMS`),
|
|
178
|
+
beside its ad or instead of it. Give it `platform_prices`, the same shape an item's prices take,
|
|
179
|
+
at most one per currency, each a positive whole number:
|
|
180
|
+
|
|
181
|
+
```yaml
|
|
182
|
+
rewarded_placements:
|
|
183
|
+
- placement: classic_revive
|
|
184
|
+
grants:
|
|
185
|
+
- { type: item_grant, item_definition_slug: revive_token, quantity: 1 }
|
|
186
|
+
max_per_day: 5 # limits the ad only
|
|
187
|
+
platform_prices:
|
|
188
|
+
- { currency: ARCADE_TICKETS, price: 50 } # 50 Coins
|
|
189
|
+
- { currency: GEMS, price: 5 } # 5 Gems
|
|
190
|
+
- placement: skip_level
|
|
191
|
+
grants:
|
|
192
|
+
- { type: item_grant, item_definition_slug: level_skip, quantity: 1 }
|
|
193
|
+
ad: false # a price alone unlocks this one
|
|
194
|
+
platform_prices:
|
|
195
|
+
- { currency: GEMS, price: 10 }
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Your game asks exactly as it does for an ad: `requestAd` from the press (§3), then re-read your
|
|
199
|
+
inventory. The Arcade, not your game, shows the player the choices that press has: a chooser when
|
|
200
|
+
there are several, such as **Watch an ad**, **50 Coins** and **5 Gems**, or a confirmation when a
|
|
201
|
+
price is the only one. A price the player cannot afford is shown but cannot be picked. Paying takes
|
|
202
|
+
the price from the player's wallet and grants the reward in one step: never the reward without the
|
|
203
|
+
payment, never charged twice, and the payment given back if the reward cannot be granted. Nothing
|
|
204
|
+
is bought with real money here. The answer is `shown` whenever the Arcade put a choice up, whatever
|
|
205
|
+
the player then did, so **branch on your inventory, never on the outcome**, as for an ad.
|
|
206
|
+
|
|
207
|
+
- **Your caps limit the ad only.** `max_per_day`, `cooldown_seconds`, `max_held` and
|
|
208
|
+
`allowance_currency_slug` decide when the ad can pay. The prices stay on offer when the ad
|
|
209
|
+
cannot, and a paid unlock does not count toward them. Only the player's balance limits how often
|
|
210
|
+
they pay, so price the reward for that. The grant still refuses a reward that would not fit
|
|
211
|
+
(a full stack, say), and the payment is then given back.
|
|
212
|
+
- **Not with `without_ads: grant`,** which would give the reward free and make the price pointless.
|
|
213
|
+
The upload refuses it, and `ad: false` with no `platform_prices` too, since then nothing unlocks
|
|
214
|
+
the placement.
|
|
215
|
+
- **Each currency's spend scope applies,** as it does to an item's price
|
|
216
|
+
([`inventory-and-catalogs.md`](inventory-and-catalogs.md#what-coins-and-gems-may-buy-the-spend-scope)):
|
|
217
|
+
where Coins may price cosmetics only, a Coin price on a placement that grants anything else is
|
|
218
|
+
refused. Gems may price any placement. `gemctl platform config` lists which currencies your
|
|
219
|
+
game may price in, and each one's spend scope: author against it, never against a list you
|
|
220
|
+
remember.
|
|
221
|
+
- **A placement's own prices do not make its reward "sold for a platform currency"** in §1's sense,
|
|
222
|
+
so the ad keeps serving beside them as it did before.
|
|
223
|
+
|
|
224
|
+
⚠️ **The upload warns `rewarded_paid_unlocks_not_live` while the platform does not let players pay
|
|
225
|
+
for a placement yet.** Until then your prices are not offered, the placement unlocks by its ad
|
|
226
|
+
alone, `ad: false` included, and `rewardedStatus` carries no `options` (§5). Nothing in your game
|
|
227
|
+
needs to change when it goes live: draw the button from `rewardedStatus` and it follows.
|
|
228
|
+
|
|
173
229
|
## 3. Ask from the press, then re-read
|
|
174
230
|
|
|
175
231
|
```js
|
|
@@ -342,14 +398,32 @@ const status = await gem.ads.rewardedStatus('classic_revive');
|
|
|
342
398
|
watchAdButton.hidden = !status.grantable;
|
|
343
399
|
```
|
|
344
400
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
401
|
+
**For a placement with prices** (§2), `grantable` is `true` when the ad could pay **or** a price
|
|
402
|
+
could, so the button stays up through the ad's caps. Label it from the answer: name the ad only
|
|
403
|
+
when `adGrantable` is `true`, and each price in `options`, whose `kind` is `coins` or `gems`.
|
|
404
|
+
Neither says what the player holds; the Arcade shows that when they press. Both are in SDK
|
|
405
|
+
releases after 23.1.3.
|
|
406
|
+
|
|
407
|
+
```js
|
|
408
|
+
const status = await gem.ads.rewardedStatus('classic_revive');
|
|
409
|
+
const ways = [
|
|
410
|
+
...(status.adGrantable ? ['watch an ad'] : []),
|
|
411
|
+
...(status.options ?? []).map((o) => `${o.price} ${o.kind === 'coins' ? 'Coins' : 'Gems'}`),
|
|
412
|
+
];
|
|
413
|
+
reviveButton.textContent = `Revive: ${ways.join(' or ')}`; // "Revive: watch an ad or 5 Gems"
|
|
414
|
+
reviveButton.hidden = !status.grantable;
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
| field | what it says |
|
|
418
|
+
| ------------------ | ------------------------------------------------------------------------------------------ |
|
|
419
|
+
| `declared` | the config promoted to the channel being played names this placement |
|
|
420
|
+
| `grantable` | pressing the button could pay right now — **not** that an ad will play |
|
|
421
|
+
| `adGrantable` | the ad itself could pay right now: `grantable` without the prices |
|
|
422
|
+
| `options` | the Coin and Gem prices on offer, each `{ kind, currencyId, price }`; absent when none are |
|
|
423
|
+
| `remainingToday` | grants left today under the daily cap; absent when the placement has none |
|
|
424
|
+
| `nextEligibleAt` | when a running cooldown ends, in epoch milliseconds; absent when none runs |
|
|
425
|
+
| `heldLimitReached` | the player already holds the placement's `max_held` — spend one first |
|
|
426
|
+
| `resetsAt` | when today's count resets, the next 00:00 UTC, in epoch milliseconds |
|
|
353
427
|
|
|
354
428
|
Without a play token, when the read fails or takes longer than ten seconds, or for a placement
|
|
355
429
|
outside the grammar, it answers not declared and not grantable with no `resetsAt`, and the reason
|
package/docs/server-bundles.md
CHANGED
|
@@ -80,8 +80,8 @@ To check the download yourself: the package npm serves is, byte for byte, the fi
|
|
|
80
80
|
also publishes on its download host, with the hash beside it:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
curl -fsSL -O https://downloads.gemarcade.com/sdk/v23.
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/v23.
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v23.2.0/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v23.2.0/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|