@indiegems/gem-web-sdk 9.1.0 → 9.1.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/dist/gem-web-sdk.esm.js +1 -1
- package/dist/gem-web-sdk.function.js +1 -1
- package/dist/gem-web-sdk.game.js +1 -1
- package/dist/gem-web-sdk.min.js +1 -1
- package/dist/gem-web-sdk.sdk.js +1 -1
- package/dist/gem-web-sdk.server.js +1 -1
- package/docs/api/api-reference-game.md +20 -10
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +5 -5
- package/docs/arcade-economy.md +13 -8
- package/docs/changelog.md +12 -0
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +3 -1
- package/docs/manifest.json +14 -14
- package/docs/platform-rules.md +3 -3
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +39 -23
- package/package.json +1 -1
- package/schema/bridge.v1.json +25 -6
package/docs/platform-rules.md
CHANGED
|
@@ -328,9 +328,9 @@ longer one, are in [`server-bundles.md`](server-bundles.md#how-long-your-server-
|
|
|
328
328
|
|
|
329
329
|
Decided by the platform on **2026-09-26**, and a contract rather than an implementation fact.
|
|
330
330
|
|
|
331
|
-
The **Boost** is
|
|
332
|
-
**Gems and Tickets** faster — every grant is multiplied — and their earning limits
|
|
333
|
-
are raised by the same tier. It **never** touches achievement points. The platform applies all of
|
|
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 Tickets** faster — every grant is multiplied — and their earning limits
|
|
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
|
|
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/v9.1.
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/v9.1.
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v9.1.2/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v9.1.2/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -543,6 +543,12 @@ credit for it until you say so.
|
|
|
543
543
|
- **Text entry the on-screen keyboard can serve.** Focusing a field summons the TV's OSK, so keep
|
|
544
544
|
entry fields few and ordinary — and never gate first play behind one. A player made to type on a
|
|
545
545
|
remote before they may play mostly does not.
|
|
546
|
+
|
|
547
|
+
The same fields have a rule that is not about televisions at all: **give every focusable text
|
|
548
|
+
field a font size of at least 16px**, on every device. Below that, iOS Safari zooms the page in
|
|
549
|
+
when the field takes focus and does not zoom back out, so the game is left cropped off the side of
|
|
550
|
+
the screen until the player pinches it back. A 14px lobby-code box is enough to do it.
|
|
551
|
+
|
|
546
552
|
- **A TV performance budget.** TV-browser SoCs are weak next to any desktop: ship a player-settable
|
|
547
553
|
render-scale option (render at DPR 1 and upscale), bound memory, assume a fixed frame — 16:9, or
|
|
548
554
|
a centred 9:16 column when your store page makes you
|
|
@@ -589,6 +595,13 @@ there is no route by which the frame could request one. `createGem` forwards all
|
|
|
589
595
|
nothing after the pause. So the pause edge is your last guaranteed moment. Write anything the
|
|
590
596
|
player would mind losing there, and never hold it back for a terminate warning that may not come.
|
|
591
597
|
|
|
598
|
+
**Re-read on resume.** Your game is told that it was paused and resumed, never why, and never
|
|
599
|
+
what the player did in the meantime. Their Gems and Tickets can change while you are paused, so
|
|
600
|
+
if your game keeps anything worked out from a balance, such as a Buy button it greyed out or a
|
|
601
|
+
"not enough Gems" line, read `gem.platform.currencies.get()` again on every `onPause(false)`
|
|
602
|
+
rather than trusting what it read before the pause
|
|
603
|
+
([inventory-and-catalogs.md](inventory-and-catalogs.md#reading-the-players-balance)).
|
|
604
|
+
|
|
592
605
|
- **`onWillTerminate`** — persist now. `graceMs` is a promise about how long the parent will wait.
|
|
593
606
|
- **`onViewport`** — where your frame's own edges are under a cutout. **Most games need no
|
|
594
607
|
handler**: the SDK has already written the same numbers to `--gem-safe-top` and friends on your
|
|
@@ -694,8 +707,8 @@ On a phone your game is full-bleed, and the arcade keeps one row of its own over
|
|
|
694
707
|
[the Arcade's menu](#the-arcades-menu-your-settings-live-in-it): Return to the Arcade, your
|
|
695
708
|
settings, mute and the rest.
|
|
696
709
|
- **The player's wallets** (Gems, Tickets, their achievement score), in the **middle of the row**,
|
|
697
|
-
[
|
|
698
|
-
menus. They are translucent, so your game shows through them — but they are still the Arcade's,
|
|
710
|
+
[there unless your game hides them](#the-arcades-wallet-shown-unless-you-hide-it-never-drawn),
|
|
711
|
+
during play as well as on your menus. They are translucent, so your game shows through them — but they are still the Arcade's,
|
|
699
712
|
and still taps the Arcade takes.
|
|
700
713
|
|
|
701
714
|
**The top of the screen has one layout, in every game:**
|
|
@@ -1015,25 +1028,28 @@ its music out than cut it, say. The Arcade still shows its button for your game
|
|
|
1015
1028
|
so this is a promise: your `onPrefs` handler must go quiet on `muted: true` and come back on `false`,
|
|
1016
1029
|
and the release checklist checks that it does.
|
|
1017
1030
|
|
|
1018
|
-
## The Arcade's wallet:
|
|
1031
|
+
## The Arcade's wallet: shown unless you hide it, never drawn
|
|
1019
1032
|
|
|
1020
1033
|
The player's platform balances — Gems, Tickets (`ARCADE_TICKETS`) and their achievement score —
|
|
1021
1034
|
belong to the Arcade. **Your game never draws one**, and never shows a pending state for one: the
|
|
1022
1035
|
Arcade takes every figure from the platform and draws the wallets itself, in the middle of the row
|
|
1023
1036
|
across the top of your frame on a phone, with its menu button at the right end.
|
|
1024
1037
|
|
|
1025
|
-
**
|
|
1026
|
-
|
|
1038
|
+
**By default the Arcade shows every wallet the whole time, during play too.** They are small,
|
|
1039
|
+
translucent chips in the middle of the top row, and while shown they are
|
|
1027
1040
|
[reserved rectangles](#the-arcades-own-controls-reserved-areas): lay every screen out below both
|
|
1028
|
-
`--gem-reserved-tl-height` and `--gem-reserved-tr-height
|
|
1029
|
-
|
|
1041
|
+
`--gem-reserved-tl-height` and `--gem-reserved-tr-height`. A game that calls nothing keeps them.
|
|
1042
|
+
|
|
1043
|
+
**A game may hide them for gameplay** (honoured again since 2026-10-08): call
|
|
1044
|
+
`gem.setWalletVisible(false)` when play starts and `gem.setWalletVisible(true)` when a menu opens.
|
|
1045
|
+
While hidden, their rectangles leave `gem.viewport.reserved` and the top row is the game's; the
|
|
1046
|
+
Arcade still pops a wallet up for a moment when something happens to it (a reward you cued, a
|
|
1047
|
+
spend, a landing), floating over the game without moving anything. This applies on a phone, where
|
|
1048
|
+
the wallets sit over your game; where the Arcade docks them beside the frame it changes nothing.
|
|
1049
|
+
Read the `--gem-reserved-*` properties rather than assuming either state.
|
|
1030
1050
|
|
|
1031
|
-
⚠️
|
|
1032
|
-
|
|
1033
|
-
screen. The Arcade still accepts the call — it returns `true`, and existing games need no change —
|
|
1034
|
-
but the wallets stay up whatever it is told. **Do not design a screen around the top row being
|
|
1035
|
-
clear during play**: it is not. Another embedder may still honour the call, so leaving it in is
|
|
1036
|
-
harmless; new code does not need it.
|
|
1051
|
+
⚠️ **The Arcade ignores the `only` option**: a shown wallet is every wallet. Do not build a store
|
|
1052
|
+
screen that relies on narrowing them to one currency.
|
|
1037
1053
|
|
|
1038
1054
|
## No accidental text selection
|
|
1039
1055
|
|
|
@@ -1125,15 +1141,15 @@ player (`gem.player`), room members, leaderboard rows, friends, profiles, showca
|
|
|
1125
1141
|
async opponent you read through the raw client, once you pass the row through `mapIdentity`. One
|
|
1126
1142
|
renderer serves all of them.
|
|
1127
1143
|
|
|
1128
|
-
| Field | What it is
|
|
1129
|
-
| --------------- |
|
|
1130
|
-
| `playerId` | The player's id. **Absent on an anonymous row**
|
|
1131
|
-
| `displayName` | The name the player chose
|
|
1132
|
-
| `discriminator` | The digits that tell two same-named players apart — `0001`, no `#`
|
|
1133
|
-
| `displayTag` | The precomposed `Name#0001`
|
|
1134
|
-
| `avatarIconId` | The portrait item's id
|
|
1135
|
-
| `avatarIconUrl` | The portrait's thumbnail
|
|
1136
|
-
| `badges` | Equipped badges, at most two, in the player's order, each `{ itemId, iconUrl }`
|
|
1144
|
+
| Field | What it is | Type |
|
|
1145
|
+
| --------------- | ------------------------------------------------------------------------------- | ------------ |
|
|
1146
|
+
| `playerId` | The player's id. **Absent on an anonymous row** | `string` |
|
|
1147
|
+
| `displayName` | The name the player chose | `PlayerText` |
|
|
1148
|
+
| `discriminator` | The digits that tell two same-named players apart — `0001`, no `#` | `string` |
|
|
1149
|
+
| `displayTag` | The precomposed `Name#0001` | `PlayerText` |
|
|
1150
|
+
| `avatarIconId` | The portrait item's id | `string` |
|
|
1151
|
+
| `avatarIconUrl` | The portrait's thumbnail | `string` |
|
|
1152
|
+
| `badges` | Equipped badges, at most two, in the player's order, each `{ itemId, iconUrl }` | array |
|
|
1137
1153
|
|
|
1138
1154
|
**A player chooses what they wear**: a portrait and up to two badges, each an icon they own — one
|
|
1139
1155
|
an achievement in your game or any other may have awarded them ([achievements.md](achievements.md)
|
|
@@ -1189,7 +1205,7 @@ members. `gem.player.displayHandle` and `gem.player.avatarUrl` still work, and a
|
|
|
1189
1205
|
|
|
1190
1206
|
### The signed-in player's Boost: show it, never gate on it
|
|
1191
1207
|
|
|
1192
|
-
`gem.player` also carries **`boost`** — the player's Boost, the Arcade
|
|
1208
|
+
`gem.player` also carries **`boost`** — the player's Boost, which the Arcade sells for real money.
|
|
1193
1209
|
While it lasts the player earns Gems and Tickets faster (never achievement points), and the
|
|
1194
1210
|
platform raises their earning limits to match. The platform does all of that on its own side when
|
|
1195
1211
|
it pays; your game does nothing to make a boost work.
|
package/package.json
CHANGED
package/schema/bridge.v1.json
CHANGED
|
@@ -616,7 +616,7 @@
|
|
|
616
616
|
"optional": true,
|
|
617
617
|
"$comment": [
|
|
618
618
|
"The player's Boost, FOR DISPLAY ONLY — a platform rule, not advice. A",
|
|
619
|
-
"Boost is the Arcade
|
|
619
|
+
"Boost is something the Arcade sells for real money: it",
|
|
620
620
|
"multiplies what the player EARNS in platform currencies (Gems and",
|
|
621
621
|
"Tickets) and raises their earning limits. It never touches achievement",
|
|
622
622
|
"points.",
|
|
@@ -2813,11 +2813,8 @@
|
|
|
2813
2813
|
"the following is not offering a degraded store, it is offering a game a",
|
|
2814
2814
|
"button that charges a player:",
|
|
2815
2815
|
"",
|
|
2816
|
-
" PAYABLE
|
|
2817
|
-
"
|
|
2818
|
-
" this ship without a top-up flow binds a currency a player EARNS: short of",
|
|
2819
|
-
" Gems or Tickets the answer is to go and play, and neither is sold for",
|
|
2820
|
-
" money. It does not bind a currency a player BUYS. So resolve the slug",
|
|
2816
|
+
" PAYABLE CURRENCIES ONLY. `currencySlug` is frame-chosen and this schema",
|
|
2817
|
+
" does not constrain which currency it names. So resolve the slug",
|
|
2821
2818
|
" against the platform's own currency records - an active currency the",
|
|
2822
2819
|
" platform marks payable in games - and refuse the rest, `declined` with",
|
|
2823
2820
|
" `unknown_currency` or `currency_not_in_game_payable`. Resolve it EXACTLY,",
|
|
@@ -2828,6 +2825,28 @@
|
|
|
2828
2825
|
" the frame does choose which wallet pays; the prompt names that currency",
|
|
2829
2826
|
" and its price from platform records, which keeps the choice the player's.",
|
|
2830
2827
|
"",
|
|
2828
|
+
" A PAYABLE CURRENCY MAY BE ONE THE PLAYER BOUGHT. This shipped on the",
|
|
2829
|
+
" argument that a payable currency is only ever EARNED: short of Gems or",
|
|
2830
|
+
" Tickets the answer was to go and play, and neither was sold for money.",
|
|
2831
|
+
" That is no longer true of Gems. The arcade's own store sells them, and",
|
|
2832
|
+
" bought Gems stay payable in games (owner decision, 2026-10-06). So",
|
|
2833
|
+
" where a balance came from protects",
|
|
2834
|
+
" nobody, and no parent may lean on it. What stands between a game and a",
|
|
2835
|
+
" balance a player paid for is the rest of this list, and these four are",
|
|
2836
|
+
" the ones the old argument stood in for:",
|
|
2837
|
+
"",
|
|
2838
|
+
" - THE CONFIRMATION IS THE PARENT'S, and it costs a gesture. The sheet is",
|
|
2839
|
+
" drawn in the parent's own chrome from platform records, and only on",
|
|
2840
|
+
" user activation (below).",
|
|
2841
|
+
" - A DISMISSAL BACKS OFF, and the confirm control is quiet after paint",
|
|
2842
|
+
" (both below; the arcade holds the quiet period for 400 ms).",
|
|
2843
|
+
" - A SHORTFALL IS NEVER A SALE. `insufficient_funds` gets no top-up, no",
|
|
2844
|
+
" upsell and no way into a store, from the parent or from the game. A",
|
|
2845
|
+
" game cannot open the arcade's store, and no message here asks for it.",
|
|
2846
|
+
" - MONEY IS TAKEN ONLY ON THE PAYMENT PROVIDER'S PAGE, which a player",
|
|
2847
|
+
" reaches through the parent's own chrome. Nothing a frame sends starts",
|
|
2848
|
+
" a payment or carries a card field.",
|
|
2849
|
+
"",
|
|
2831
2850
|
" THE FRAME CHOOSES THE AMOUNT. Not the unit price - but `targetId` x",
|
|
2832
2851
|
" `currencySlug` x `quantity` is nevertheless the total, up to ten times the",
|
|
2833
2852
|
" most expensive item in a catalog the game itself publishes. Render unit",
|