@indiegems/gem-web-sdk 14.0.0 → 14.0.2
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/README.md +2 -2
- package/brand/ticket-hero-128.png +0 -0
- package/brand/ticket-hero-256.png +0 -0
- package/brand/ticket-hero-384.png +0 -0
- package/brand/ticket-hero-512.png +0 -0
- package/brand/ticket-hero-768.png +0 -0
- package/brand/ticket-icon-20-1.5x.png +0 -0
- package/brand/ticket-icon-20-1x.png +0 -0
- package/brand/ticket-icon-20-2x.png +0 -0
- package/brand/ticket-icon-20-3x.png +0 -0
- package/brand/ticket-icon-32-1.5x.png +0 -0
- package/brand/ticket-icon-32-1x.png +0 -0
- package/brand/ticket-icon-32-2x.png +0 -0
- package/brand/ticket-icon-32-3x.png +0 -0
- package/dist/gem-web-sdk.esm.js +1 -1
- package/dist/gem-web-sdk.function.js +1 -1
- 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 +1 -1
- package/docs/achievements.md +2 -2
- package/docs/api/api-reference-function.md +3 -3
- package/docs/api/api-reference-game.md +65 -32
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +21 -21
- package/docs/arcade-avatars.md +3 -3
- package/docs/arcade-economy.md +37 -36
- package/docs/brand-art.md +31 -30
- package/docs/changelog.md +12 -0
- package/docs/daily-challenges.md +1 -1
- package/docs/game-economy-tutorial.md +14 -14
- package/docs/getting-started.md +3 -3
- package/docs/inventory-and-catalogs.md +29 -29
- package/docs/manifest.json +32 -32
- package/docs/platform-rules.md +2 -2
- package/docs/rewarded-ads.md +1 -1
- package/docs/saving-player-progress.md +1 -1
- package/docs/server-bundles.md +2 -2
- package/docs/serverless-functions.md +2 -2
- package/docs/trading-and-gifting.md +1 -1
- package/docs/what-a-game-must-do.md +57 -36
- package/package.json +1 -1
- package/schema/bridge.v1.json +7 -7
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
|
+
## 14.0.2
|
|
20
|
+
|
|
21
|
+
Upgrading from 14.0.1. Impact: none.
|
|
22
|
+
|
|
23
|
+
Nothing in this release asks anything of a game.
|
|
24
|
+
|
|
25
|
+
## 14.0.1
|
|
26
|
+
|
|
27
|
+
Upgrading from 14.0.0. Impact: none.
|
|
28
|
+
|
|
29
|
+
Nothing in this release asks anything of a game.
|
|
30
|
+
|
|
19
31
|
## 14.0.0
|
|
20
32
|
|
|
21
33
|
Upgrading from 13.4.3. Impact: required.
|
package/docs/daily-challenges.md
CHANGED
|
@@ -321,7 +321,7 @@ Four things to know before you ship one:
|
|
|
321
321
|
the player does to their time zone.
|
|
322
322
|
|
|
323
323
|
**When a completion pays a platform currency, show it where it happened.** The arcade shows the
|
|
324
|
-
player's Gems and
|
|
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
327
|
beside the payout on your completion screen, and the payout flies from there into the Gem button:
|
|
@@ -35,8 +35,8 @@ document, because the schema is versioned and a copied list goes stale.
|
|
|
35
35
|
|
|
36
36
|
```yaml
|
|
37
37
|
currencies:
|
|
38
|
-
- id:
|
|
39
|
-
display_name:
|
|
38
|
+
- id: gold
|
|
39
|
+
display_name: Gold
|
|
40
40
|
min_balance: 0
|
|
41
41
|
max_balance: 999999
|
|
42
42
|
|
|
@@ -51,7 +51,7 @@ catalogs:
|
|
|
51
51
|
- id: buy-potion
|
|
52
52
|
recipe_type: purchase
|
|
53
53
|
costs:
|
|
54
|
-
- currency_id:
|
|
54
|
+
- currency_id: gold
|
|
55
55
|
amount: 25
|
|
56
56
|
grants:
|
|
57
57
|
- item_definition_id: healing-potion
|
|
@@ -106,12 +106,12 @@ them.
|
|
|
106
106
|
|
|
107
107
|
```js
|
|
108
108
|
const store = config.catalogs.find((c) => c.id === 'general-store');
|
|
109
|
-
const balance = (await gem.me.currencies.get('
|
|
109
|
+
const balance = (await gem.me.currencies.get('gold')).total; // from the snapshot, not a fourth request
|
|
110
110
|
// `null` means no record — decide at the render what that shows, never `?? 0` at the read.
|
|
111
111
|
|
|
112
112
|
for (const recipe of store.recipes) {
|
|
113
113
|
const cost =
|
|
114
|
-
recipe.costs?.find((c) => 'currency_id' in c && c.currency_id === '
|
|
114
|
+
recipe.costs?.find((c) => 'currency_id' in c && c.currency_id === 'gold')?.amount ?? 0;
|
|
115
115
|
const affordable = balance !== null && balance >= cost; // null is "no record", and null >= 0 is true in JS
|
|
116
116
|
const used = usage.usages.find((u) => u.recipeId === recipe.id); // camelCase: SDK shape
|
|
117
117
|
const onCooldown = used?.isOnCooldown ?? false;
|
|
@@ -205,10 +205,10 @@ const order = await gem.me.orders.get(result.orderId);
|
|
|
205
205
|
This is also the answer after a timeout: you got no result back, so list recent orders and look for
|
|
206
206
|
the one you attempted, rather than resending the purchase.
|
|
207
207
|
|
|
208
|
-
## Selling a cosmetic for
|
|
208
|
+
## Selling a cosmetic for Coins
|
|
209
209
|
|
|
210
210
|
Your shop can also sell for the arcade's own currencies, and the platform draws one line through
|
|
211
|
-
them: **
|
|
211
|
+
them: **Coins buy cosmetics, Gems buy anything.** Each platform currency has a spend scope — Coins'
|
|
212
212
|
is `cosmetic_only`, Gems' is `any`. Which currencies your game may price in, and what each may buy,
|
|
213
213
|
is `gemctl platform config`'s answer. Mark a cosmetic in the config, and price it in
|
|
214
214
|
`ARCADE_TICKETS`:
|
|
@@ -219,7 +219,7 @@ items:
|
|
|
219
219
|
display_name: Wizard Hat
|
|
220
220
|
category: cosmetic # it changes how the player looks, never what they can do
|
|
221
221
|
platform_prices:
|
|
222
|
-
- currency: ARCADE_TICKETS #
|
|
222
|
+
- currency: ARCADE_TICKETS # Coins
|
|
223
223
|
price: 40
|
|
224
224
|
- id: dragon-skin
|
|
225
225
|
display_name: Dragon Skin
|
|
@@ -265,7 +265,7 @@ if (av.locked) {
|
|
|
265
265
|
}
|
|
266
266
|
```
|
|
267
267
|
|
|
268
|
-
The platform checks the prerequisite **before** it takes any
|
|
268
|
+
The platform checks the prerequisite **before** it takes any Coins: a locked player who buys
|
|
269
269
|
anyway is refused `recipe_locked` and charged nothing. See
|
|
270
270
|
[`inventory-and-catalogs.md`](inventory-and-catalogs.md#16-selling-for-a-platform-currency) for the
|
|
271
271
|
two purchase paths and the full rule.
|
|
@@ -285,7 +285,7 @@ const res = await gem.purchases.request({
|
|
|
285
285
|
if (res.outcome === 'ok' || res.outcome === 'unavailable') {
|
|
286
286
|
gem.me.inventory.refresh(); // `unavailable` means no answer arrived, not that nothing happened
|
|
287
287
|
} else if (res.reason === 'recipe_locked' || res.reason === 'insufficient_funds') {
|
|
288
|
-
updateShopRow(res.reason); // your shop's own words: "Locked", "Not enough
|
|
288
|
+
updateShopRow(res.reason); // your shop's own words: "Locked", "Not enough Coins"
|
|
289
289
|
} else if (res.reason !== 'cancelled' && res.reason !== 'busy') {
|
|
290
290
|
console.warn('purchase declined:', res.reason); // unknown_currency, no_price_in_currency … are yours to fix
|
|
291
291
|
}
|
|
@@ -294,13 +294,13 @@ if (res.outcome === 'ok' || res.outcome === 'unavailable') {
|
|
|
294
294
|
With your own purchase screen (`gem.me.mutations.purchase`), the same strings arrive as `err.code`
|
|
295
295
|
— see [`inventory-and-catalogs.md`](inventory-and-catalogs.md#why-a-purchase-was-refused).
|
|
296
296
|
|
|
297
|
-
**Never draw a top-up or payment screen of your own** — no "get more
|
|
298
|
-
shaped like the arcade's purchase sheet. A player short of
|
|
299
|
-
says "Not enough
|
|
297
|
+
**Never draw a top-up or payment screen of your own** — no "get more Coins", no checkout, nothing
|
|
298
|
+
shaped like the arcade's purchase sheet. A player short of Coins earns more by playing; your shop
|
|
299
|
+
says "Not enough Coins" and stops there.
|
|
300
300
|
|
|
301
301
|
## Rewarding a player — still a recipe
|
|
302
302
|
|
|
303
|
-
When your game _gives_ a player something — "you earned 100
|
|
303
|
+
When your game _gives_ a player something — "you earned 100 gold," a quest reward — reach for a
|
|
304
304
|
recipe too, not a direct grant. A reward flow is the same client-authored economy as a shop under a
|
|
305
305
|
friendlier name, and the same rule applies: if the value matters, the server has to be the one that
|
|
306
306
|
awards it. Author a reward recipe in your config and execute it exactly as above.
|
package/docs/getting-started.md
CHANGED
|
@@ -50,7 +50,7 @@ either way.
|
|
|
50
50
|
```jsonc
|
|
51
51
|
// package.json
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@indiegems/gem-web-sdk": "14.0.
|
|
53
|
+
"@indiegems/gem-web-sdk": "14.0.2"
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
@@ -178,8 +178,8 @@ And draw a settings button of your own — top-right, with your Back button just
|
|
|
178
178
|
([the top of the screen](what-a-game-must-do.md#the-top-of-the-screen)) — whose values start from
|
|
179
179
|
the player's Arcade-wide defaults, `gem.prefs.recommended`, until the player chooses their own
|
|
180
180
|
([what a game must do](what-a-game-must-do.md#game-settings)). The Arcade's menu does not open
|
|
181
|
-
your settings screen;
|
|
182
|
-
|
|
181
|
+
your settings screen; declare your settings to it with `gem.settings.declare` too, so its
|
|
182
|
+
Settings screen lists them all, but never instead of your own button. Outside the Arcade there is no Arcade menu at all, so also give players a way out — a
|
|
183
183
|
control that calls `gem.exit()`. A game a player cannot leave is one they leave by closing the tab.
|
|
184
184
|
|
|
185
185
|
## 6. Play it
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Inventory and catalogs
|
|
2
2
|
|
|
3
3
|
Games do not have to have an economy. A game with no currency, no items and nothing to buy is a
|
|
4
|
-
complete game. Read this if yours has any of them — a
|
|
4
|
+
complete game. Read this if yours has any of them — a gold count, a bag of items, a shop, a loot
|
|
5
5
|
box, a battle pass.
|
|
6
6
|
|
|
7
7
|
If it does, the platform holds it, the same way it holds a save. Balances and items live on the
|
|
@@ -42,8 +42,8 @@ which is the same rule with a save slot in place of a currency.
|
|
|
42
42
|
|
|
43
43
|
Hold the distinction, because the whole surface is built on it.
|
|
44
44
|
|
|
45
|
-
- A **currency definition** is "
|
|
46
|
-
is an instance: "this player has 250
|
|
45
|
+
- A **currency definition** is "gold exists, minimum 0, maximum a million." A player's **balance**
|
|
46
|
+
is an instance: "this player has 250 gold."
|
|
47
47
|
- An **item definition** is "a healing potion, stackable to 99." An **item instance** is "this
|
|
48
48
|
player holds 3 of them," or, for a non-stacking item, "this specific sword, rolled with these
|
|
49
49
|
properties."
|
|
@@ -60,7 +60,7 @@ because it never loaded the definitions that name them.
|
|
|
60
60
|
Everything a player owns is reached through `gem.me`, the signed-in player:
|
|
61
61
|
|
|
62
62
|
```js
|
|
63
|
-
await gem.me.currencies.get('
|
|
63
|
+
await gem.me.currencies.get('gold');
|
|
64
64
|
await gem.me.inventory.snapshot();
|
|
65
65
|
```
|
|
66
66
|
|
|
@@ -69,7 +69,7 @@ scoped to that character:
|
|
|
69
69
|
|
|
70
70
|
```js
|
|
71
71
|
const hero = await gem.characters.get(characterId);
|
|
72
|
-
await hero.currencies.get('
|
|
72
|
+
await hero.currencies.get('gold'); // the hero's gold, not the account's
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
Code written against `gem.me` works unchanged against a character, because it is one interface on
|
|
@@ -85,7 +85,7 @@ lookups are memory:
|
|
|
85
85
|
```js
|
|
86
86
|
const config = await gem.gameConfig.load(); // a round trip; do it once
|
|
87
87
|
const potion = config.item('healing-potion'); // synchronous, from memory, by id or slug
|
|
88
|
-
const
|
|
88
|
+
const gold = config.currency('gold'); // synchronous
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
Two things that will bite:
|
|
@@ -162,8 +162,8 @@ A balance read is a lookup, not a list to iterate, because **a currency the play
|
|
|
162
162
|
is absent, not present as zero**:
|
|
163
163
|
|
|
164
164
|
```js
|
|
165
|
-
const
|
|
166
|
-
|
|
165
|
+
const gold = await gem.me.currencies.get('gold');
|
|
166
|
+
gold.total; // a number, or null when the player has no record of this currency
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
**`null` is not `0`.** Reach for `total ?? 0` and you have decided that "has never held any" and
|
|
@@ -175,7 +175,7 @@ is visible.
|
|
|
175
175
|
real-money pool. They are off by default and asked for per read:
|
|
176
176
|
|
|
177
177
|
```js
|
|
178
|
-
const c = await gem.me.currencies.get('
|
|
178
|
+
const c = await gem.me.currencies.get('gold', { buckets: true });
|
|
179
179
|
c.buckets; // [{ bucket: 'earned', balance: 200 }, { bucket: 'purchased', balance: 50 }], or absent
|
|
180
180
|
```
|
|
181
181
|
|
|
@@ -409,7 +409,7 @@ await gem.me.mutations.claimProgression('season-xp'); // collect what milestones
|
|
|
409
409
|
await gem.me.mutations.resetProgression('season-xp'); // back to the start; claimed rewards stay
|
|
410
410
|
```
|
|
411
411
|
|
|
412
|
-
`addProgression` is for things that accumulate — experience, kills,
|
|
412
|
+
`addProgression` is for things that accumulate — experience, kills, gold collected across a
|
|
413
413
|
season. `maxProgression` is for things that are a _best_ — stars on a level, a high score, a
|
|
414
414
|
furthest checkpoint. It raises the value if the new one is higher and does nothing otherwise, so
|
|
415
415
|
replaying a level and doing worse leaves the record alone, and replaying it and doing the same
|
|
@@ -439,7 +439,7 @@ so this page does not list them. Name one that is not permitted and the config u
|
|
|
439
439
|
with an error that enumerates the ones that are.
|
|
440
440
|
|
|
441
441
|
Gems can be your game's soft currency, awarded through a mapping. A game currency of your own suits
|
|
442
|
-
what lives inside one play loop —
|
|
442
|
+
what lives inside one play loop — gold picked up and spent within a run, energy, crafting
|
|
443
443
|
materials: you control it completely, no other game can spend it, and none of the constraints below
|
|
444
444
|
apply to it.
|
|
445
445
|
|
|
@@ -551,7 +551,7 @@ nobody else can touch them. The arcade also has currencies that span every game,
|
|
|
551
551
|
That changes what a shop can be. A player earns in someone else's game and spends in yours.
|
|
552
552
|
|
|
553
553
|
There are two such currencies, and they are not interchangeable: **Gems** (`GEMS`) buy progress and
|
|
554
|
-
**
|
|
554
|
+
**Coins** (`ARCADE_TICKETS`) buy cosmetics only. [arcade-economy.md](arcade-economy.md) says which
|
|
555
555
|
to use for what.
|
|
556
556
|
|
|
557
557
|
### Awarding one
|
|
@@ -583,13 +583,13 @@ A browser-only game cannot award a platform currency without a server **or a ser
|
|
|
583
583
|
That is the price of the platform never trusting a client. A game with no dedicated server writes
|
|
584
584
|
the source progression from a function the player calls, on a progression that opts in with
|
|
585
585
|
`function_may_grant`, and the platform then pays that player only, in any listed platform currency
|
|
586
|
-
— Gems (`GEMS`) and
|
|
586
|
+
— Gems (`GEMS`) and Coins (`ARCADE_TICKETS`) alike
|
|
587
587
|
([serverless-functions.md](serverless-functions.md#paying-the-player-who-called)).
|
|
588
588
|
|
|
589
589
|
### Showing the reward land in the arcade's wallet
|
|
590
590
|
|
|
591
591
|
**Platform-currency display belongs to the arcade** (the creator's ruling, 2026-09-24) — Gems and
|
|
592
|
-
|
|
592
|
+
Coins alike. The arcade shows the player's platform currencies in its own chrome — in the Gem
|
|
593
593
|
Arcade, the Gem menu the player opens from the Gem button over your frame; no wallet
|
|
594
594
|
is drawn over your game — and moves a total when the platform confirms a grant. Your game keeps no counter or balance
|
|
595
595
|
of a platform currency of its own, draws no flight or celebration of its own, and shows no
|
|
@@ -609,8 +609,8 @@ gem.cueReward({
|
|
|
609
609
|
```
|
|
610
610
|
|
|
611
611
|
`origin` is the **centre of the gem on screen** — measure the gem element itself, not the row or
|
|
612
|
-
panel around it. A
|
|
613
|
-
|
|
612
|
+
panel around it. A Coins payout is the same call with `currencySlug: 'ARCADE_TICKETS'`, from the
|
|
613
|
+
coin: the slug decides what flies and which total moves. **A payout you do not cue still flies**
|
|
614
614
|
in the Gem Arcade, from the middle of your frame, once the platform confirms it — cue it so the
|
|
615
615
|
flight starts where the player is looking. **If `cueReward` returns `false`, do nothing**: there is
|
|
616
616
|
no fallback celebration.
|
|
@@ -829,14 +829,14 @@ that looks like the arcade's purchase sheet or wallet. A game screen shaped like
|
|
|
829
829
|
chrome is an imitation of it. Your Buy button is yours; asking a player to add currency or pay is
|
|
830
830
|
the arcade's alone.
|
|
831
831
|
|
|
832
|
-
###
|
|
832
|
+
### Coins buy cosmetics; Gems buy anything
|
|
833
833
|
|
|
834
|
-
The arcade has two currencies a player spends: **Gems**, for progress, and **
|
|
834
|
+
The arcade has two currencies a player spends: **Gems**, for progress, and **Coins**
|
|
835
835
|
(`ARCADE_TICKETS`), for cosmetics. The platform holds that line with two settings: each platform
|
|
836
836
|
currency's **spend scope**, which the platform sets, and each of your items' **category**, which you
|
|
837
837
|
set.
|
|
838
838
|
|
|
839
|
-
- A currency whose spend scope is **`cosmetic_only`** may price cosmetics only.
|
|
839
|
+
- A currency whose spend scope is **`cosmetic_only`** may price cosmetics only. Coins' is.
|
|
840
840
|
- A currency whose spend scope is **`any`** may price anything. Gems' is.
|
|
841
841
|
|
|
842
842
|
The platform owns each currency's scope and can change it, so do not hard-code which is which: a
|
|
@@ -862,9 +862,9 @@ items:
|
|
|
862
862
|
cosmetics: anything that changes how the player looks and never what they can do — or
|
|
863
863
|
`progression`, anything that affects play. It defaults to `progression`, so an item is only ever a
|
|
864
864
|
cosmetic because you said so.
|
|
865
|
-
- **A price in a `cosmetic_only` currency, such as
|
|
865
|
+
- **A price in a `cosmetic_only` currency, such as Coins, is accepted only on a cosmetic.** On an
|
|
866
866
|
item, that item must be `category: cosmetic` and a plain `item_type: item` — a `bundle` or
|
|
867
|
-
`loot_box` opens into things its own row does not describe, so it cannot be sold for
|
|
867
|
+
`loot_box` opens into things its own row does not describe, so it cannot be sold for Coins.
|
|
868
868
|
- **A recipe priced in a `cosmetic_only` currency must grant at least one cosmetic item and nothing
|
|
869
869
|
else.** It is refused if it grants any currency, moves any progression, grants a non-cosmetic item,
|
|
870
870
|
or rolls a loot table — a loot table is refused for now even when every entry is cosmetic. Taking
|
|
@@ -884,11 +884,11 @@ purchase catalog stops listing that currency's price on anything the scope does
|
|
|
884
884
|
purchase naming such a price is refused `not_purchasable` before anything is charged.
|
|
885
885
|
|
|
886
886
|
The category is a declaration the platform takes at your word, and the release review checks it.
|
|
887
|
-
Labelling something that affects play `cosmetic` so it can be sold for
|
|
887
|
+
Labelling something that affects play `cosmetic` so it can be sold for Coins breaks platform policy.
|
|
888
888
|
|
|
889
889
|
**To sell a cosmetic that needs game progress first, sell it through a recipe.** An item's own
|
|
890
890
|
`platform_prices` cannot carry prerequisites; a recipe's can. Grant the cosmetic from a recipe that
|
|
891
|
-
declares the prerequisite and carries the
|
|
891
|
+
declares the prerequisite and carries the Coins price:
|
|
892
892
|
|
|
893
893
|
```yaml
|
|
894
894
|
catalogs:
|
|
@@ -909,7 +909,7 @@ catalogs:
|
|
|
909
909
|
price: 120
|
|
910
910
|
```
|
|
911
911
|
|
|
912
|
-
A player below level 10 is refused `recipe_locked` before any
|
|
912
|
+
A player below level 10 is refused `recipe_locked` before any Coins are taken, and
|
|
913
913
|
`recipeAvailability` tells your shop the recipe is locked and what it needs (§9) — so show the lock
|
|
914
914
|
("Reach level 10 to unlock") instead of offering the purchase.
|
|
915
915
|
|
|
@@ -922,7 +922,7 @@ buyButton.disabled = !gems || gems.balance < price; // can the player afford it?
|
|
|
922
922
|
```
|
|
923
923
|
|
|
924
924
|
**Read it to decide, never to display.** Platform-currency display belongs to the arcade: its
|
|
925
|
-
wallet — the Gem menu, in the Gem Arcade — shows the player's Gems and
|
|
925
|
+
wallet — the Gem menu, in the Gem Arcade — shows the player's Gems and Coins totals while your
|
|
926
926
|
game runs, and your game keeps no
|
|
927
927
|
counter, balance or wallet total of either of its own ([brand-art.md](brand-art.md) §5). Use the
|
|
928
928
|
balance to enable a purchase or to say "not enough Gems" — not to draw a number.
|
|
@@ -952,7 +952,7 @@ if (gems && gems.remaining > 0) {
|
|
|
952
952
|
}
|
|
953
953
|
```
|
|
954
954
|
|
|
955
|
-
One entry per earned platform currency (Gems and
|
|
955
|
+
One entry per earned platform currency (Gems and Coins today), for **this game and this player**:
|
|
956
956
|
how much more your game can still grant before the platform's ceilings trim the grant. It is the
|
|
957
957
|
lower of what is left of your game's own cap and what is left of the player's daily maximum across
|
|
958
958
|
all games, with the player's Boost applied — one figure, never a breakdown.
|
|
@@ -971,8 +971,8 @@ all games, with the player's Boost applied — one figure, never a breakdown.
|
|
|
971
971
|
**Only `remaining` and `resetsAt` are stable to display.** Do not build an `earned / limit` progress
|
|
972
972
|
bar: the bar switches referent when the binding ceiling changes.
|
|
973
973
|
|
|
974
|
-
A player whose
|
|
975
|
-
arrives (from `resetsAt`), not "no
|
|
974
|
+
A player whose Coins are spent for today reads `remaining: 0` with a `resetsAt`: say when more
|
|
975
|
+
arrives (from `resetsAt`), not "no Coins". When the platform has paused a currency's grants for
|
|
976
976
|
your game the entry reads `limit: 0, remaining: 0` with **no** `resetsAt`.
|
|
977
977
|
|
|
978
978
|
**For a one-day allowance, `resetsAt` is the player's own next midnight — do not assume 00:00 UTC.**
|
|
@@ -1054,7 +1054,7 @@ grants itself.
|
|
|
1054
1054
|
|
|
1055
1055
|
## What to read next
|
|
1056
1056
|
|
|
1057
|
-
- [`arcade-economy.md`](arcade-economy.md) — Gems,
|
|
1057
|
+
- [`arcade-economy.md`](arcade-economy.md) — Gems, Coins and achievement points: which to use for
|
|
1058
1058
|
what, and the rules each carries.
|
|
1059
1059
|
- [`achievements.md`](achievements.md) — platform achievements on your progressions' thresholds.
|
|
1060
1060
|
- [`game-economy-tutorial.md`](game-economy-tutorial.md) — build a shop, step by step, the safe way.
|
package/docs/manifest.json
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"documents": [
|
|
9
9
|
{
|
|
10
10
|
"file": "achievements.md",
|
|
11
|
-
"sha256": "
|
|
12
|
-
"updated": "2026-10-
|
|
11
|
+
"sha256": "c1ba25c834f5679dc5ce1c7c3b51d4219b5ac77fc898bf5d6fbf23e35614e35c",
|
|
12
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"file": "analytics.md",
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"file": "arcade-avatars.md",
|
|
21
|
-
"sha256": "
|
|
22
|
-
"updated": "2026-10-
|
|
21
|
+
"sha256": "e02e435d91e389f9cc2025945ebc0ac11882bac85a4e6d5d74278f11e38fee91",
|
|
22
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"file": "arcade-economy.md",
|
|
26
|
-
"sha256": "
|
|
27
|
-
"updated": "2026-10-
|
|
26
|
+
"sha256": "2a73e2cb4e82de78a9d57c614da67bfd6a79537163deca551c5beeb8e9693c56",
|
|
27
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"file": "async-opponents.md",
|
|
@@ -33,18 +33,18 @@
|
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"file": "brand-art.md",
|
|
36
|
-
"sha256": "
|
|
37
|
-
"updated": "2026-10-
|
|
36
|
+
"sha256": "151ece62736136e0acd5c4e3e2dd7e33eb6936923c89340670844452d34acf02",
|
|
37
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"file": "changelog.md",
|
|
41
|
-
"sha256": "
|
|
42
|
-
"updated": "2026-10-
|
|
41
|
+
"sha256": "22d1e4aba2cdd10d9ff33e1d5fded140194a96fabe24f95aa673e9af1f07e72c",
|
|
42
|
+
"updated": "2026-10-21T19:49:02Z"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"file": "daily-challenges.md",
|
|
46
|
-
"sha256": "
|
|
47
|
-
"updated": "2026-10-
|
|
46
|
+
"sha256": "fd1c44a923b8f7b82a52b958481e82ba5d62dc27de6a196a0776b0d34563ec54",
|
|
47
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"file": "dedicated-server-flow.md",
|
|
@@ -63,18 +63,18 @@
|
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
"file": "game-economy-tutorial.md",
|
|
66
|
-
"sha256": "
|
|
67
|
-
"updated": "2026-10-
|
|
66
|
+
"sha256": "5f921001150ea47a92d97e943282988826ea27506107c71846e5ff79f0d26f15",
|
|
67
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
"file": "getting-started.md",
|
|
71
|
-
"sha256": "
|
|
72
|
-
"updated": "2026-10-
|
|
71
|
+
"sha256": "f70765a705776bea4f11c8067669204db31e992515d15a76c465785e71bc6abe",
|
|
72
|
+
"updated": "2026-10-21T19:49:02Z"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"file": "inventory-and-catalogs.md",
|
|
76
|
-
"sha256": "
|
|
77
|
-
"updated": "2026-10-
|
|
76
|
+
"sha256": "e3968af5f3af88122db84ac372afd82e39c04c98cf6661f83e429243837ee6ff",
|
|
77
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"file": "leaderboards.md",
|
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"file": "platform-rules.md",
|
|
101
|
-
"sha256": "
|
|
102
|
-
"updated": "2026-10-
|
|
101
|
+
"sha256": "ab5b971555490956ef50f6141b0f58a6a34335a7e4cc1ef0e4d339a259c9a062",
|
|
102
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"file": "player-sign-in.md",
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
},
|
|
109
109
|
{
|
|
110
110
|
"file": "rewarded-ads.md",
|
|
111
|
-
"sha256": "
|
|
112
|
-
"updated": "2026-10-
|
|
111
|
+
"sha256": "b413abb899016c9a6d0c288c39d804bdabcebceec1081bdcfbb1f3feb0073406",
|
|
112
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"file": "room-shapes.md",
|
|
@@ -123,13 +123,13 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"file": "saving-player-progress.md",
|
|
126
|
-
"sha256": "
|
|
127
|
-
"updated": "2026-10-
|
|
126
|
+
"sha256": "2b51c68651b8973531fb1fda4afc4442e1450443871800ca71126605bf4b39db",
|
|
127
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
"file": "server-bundles.md",
|
|
131
|
-
"sha256": "
|
|
132
|
-
"updated": "2026-10-
|
|
131
|
+
"sha256": "95d84093c49693576297f2a6917064a1efbb6824c82acfc9f210d64a3b437d2f",
|
|
132
|
+
"updated": "2026-10-21T19:49:02Z"
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"file": "serverless-functions-js.md",
|
|
@@ -138,13 +138,13 @@
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"file": "serverless-functions.md",
|
|
141
|
-
"sha256": "
|
|
142
|
-
"updated": "2026-10-
|
|
141
|
+
"sha256": "9a33e93c90c963fd82e4848d403f27134ec15636428f6cf896488089fec3ede1",
|
|
142
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"file": "trading-and-gifting.md",
|
|
146
|
-
"sha256": "
|
|
147
|
-
"updated": "2026-
|
|
146
|
+
"sha256": "e5a77bcae4243fbf69a34fa6440114677401255b40ee9825c15b87c6570e5030",
|
|
147
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"file": "vibration.md",
|
|
@@ -153,8 +153,8 @@
|
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
"file": "what-a-game-must-do.md",
|
|
156
|
-
"sha256": "
|
|
157
|
-
"updated": "2026-10-
|
|
156
|
+
"sha256": "a0f1fd58acd28c72cca3574032c087ffbe2cb34e311086c99eceec7e2f9b7f20",
|
|
157
|
+
"updated": "2026-10-21T19:36:16Z"
|
|
158
158
|
}
|
|
159
159
|
]
|
|
160
160
|
}
|
package/docs/platform-rules.md
CHANGED
|
@@ -329,13 +329,13 @@ longer one, are in [`server-bundles.md`](server-bundles.md#how-long-your-server-
|
|
|
329
329
|
Decided by the platform on **2026-09-26**, and a contract rather than an implementation fact.
|
|
330
330
|
|
|
331
331
|
The **Boost** is something the Arcade sells for real money, in its own store. While a player has
|
|
332
|
-
one, they earn **Gems and
|
|
332
|
+
one, they earn **Gems and Coins** faster — every grant is multiplied — and their earning limits
|
|
333
333
|
and holdings cap are raised by the same tier. It **never** touches achievement points. The platform applies all of
|
|
334
334
|
it on its own side, when it pays: a game does nothing to make a boost work, and cannot make one
|
|
335
335
|
work harder.
|
|
336
336
|
|
|
337
337
|
A game **may show** it. `gem.player.boost` carries each boostable currency's multiplier and the
|
|
338
|
-
whole days left, so a reward screen can say "2×
|
|
338
|
+
whole days left, so a reward screen can say "2× Coins"; see
|
|
339
339
|
[what a game must do](what-a-game-must-do.md#the-signed-in-players-boost-show-it-never-gate-on-it).
|
|
340
340
|
A dedicated server can read the same effective boost for a player in its match, and the same rule
|
|
341
341
|
holds there.
|
package/docs/rewarded-ads.md
CHANGED
|
@@ -39,7 +39,7 @@ config where it is not:
|
|
|
39
39
|
- **Soulbound.** Every item or currency it grants has `transfer_policy: none`. A reward cannot be
|
|
40
40
|
traded, gifted or moved.
|
|
41
41
|
- **An ordinary item or a currency.** Not a bundle, not a loot box, and not a progression.
|
|
42
|
-
- **Never a platform currency.** The upload refuses a reward that names Gems,
|
|
42
|
+
- **Never a platform currency.** The upload refuses a reward that names Gems, Coins or any
|
|
43
43
|
other platform currency.
|
|
44
44
|
- **Never convertible into one of those.** The upload follows every recipe and catalog entry that
|
|
45
45
|
spends the reward, then everything that spends what those grant, and so on. If that chain reaches
|
|
@@ -474,7 +474,7 @@ The order matters, and two of the steps are the ones people skip:
|
|
|
474
474
|
The reset clears this player's state **in this game, on that one channel**: saves,
|
|
475
475
|
`gem.me.settings`, inventory and progressions, and the rest of what the game keeps for them.
|
|
476
476
|
It does not touch your other channels, your other games, achievements, or your Gems and
|
|
477
|
-
|
|
477
|
+
Coins. Your purchases and what you paid stay on record, but an item a purchase delivered on
|
|
478
478
|
that channel is cleared with the rest, and the answer lists those purchases.
|
|
479
479
|
`gemctl support reset --help` has the full list, and the answer names whatever it left alone.
|
|
480
480
|
|
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/v14.0.
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/v14.0.
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v14.0.2/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v14.0.2/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -18,7 +18,7 @@ change: checking that a finished run was plausible, or granting a reward whose c
|
|
|
18
18
|
state only your code can judge.
|
|
19
19
|
|
|
20
20
|
It is also how a game **with no dedicated server** pays a platform currency — Gems (`GEMS`) or
|
|
21
|
-
|
|
21
|
+
Coins (`ARCADE_TICKETS`) — or unlocks an achievement. A browser write
|
|
22
22
|
never does either. A function a player calls may, for **that player only**, on a progression you
|
|
23
23
|
opt in. Section 2's
|
|
24
24
|
[Paying the player who called](#paying-the-player-who-called) is the whole of it. Opting in makes
|
|
@@ -160,7 +160,7 @@ fires them only when you opt in:
|
|
|
160
160
|
request answers `200`, and the transfer stays `pending` for the recipient to accept or reject
|
|
161
161
|
(the previous subsection).
|
|
162
162
|
- **Any currency the platform lists.** A function may pay every currency `gemctl platform config`
|
|
163
|
-
lists under `platform_grants` — Gems (`GEMS`) and
|
|
163
|
+
lists under `platform_grants` — Gems (`GEMS`) and Coins (`ARCADE_TICKETS`) alike — so a daily
|
|
164
164
|
that pays Gems needs no dedicated server. (`function_gems_not_enabled` on an older receipt is from
|
|
165
165
|
before functions could pay Gems.)
|
|
166
166
|
- **No guests.** A guest account's call grants and unlocks nothing (`function_invoker_is_guest`).
|
|
@@ -13,7 +13,7 @@ as `gem.transfers` rather than through `gem.me`.
|
|
|
13
13
|
await gem.transfers.initiate({
|
|
14
14
|
to: { type: 'players', id: recipientId },
|
|
15
15
|
items: [{ itemDefinitionId: 'iron-sword', quantity: 1 }],
|
|
16
|
-
currencies: [{ currencyId: '
|
|
16
|
+
currencies: [{ currencyId: 'gold', amount: 50 }],
|
|
17
17
|
message: 'for the boss fight',
|
|
18
18
|
});
|
|
19
19
|
```
|