@indiegems/gem-web-sdk 10.3.0 → 10.4.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 +20 -0
- package/dist/game.d.ts +7 -1
- package/dist/gem-web-sdk.bridge.js +1 -1
- 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.quality.js +2 -0
- package/dist/gem-web-sdk.sdk.js +2 -2
- package/dist/gem-web-sdk.server.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/quality.d.ts +49 -0
- package/dist/sdk.d.ts +7 -1
- package/docs/api/api-reference-game.md +92 -36
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +74 -10
- package/docs/brand-art.md +6 -4
- package/docs/changelog.md +20 -0
- package/docs/daily-challenges.md +4 -4
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +11 -7
- package/docs/manifest.json +14 -14
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +136 -92
- package/package.json +5 -1
- package/schema/bridge.v1.json +52 -9
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/v10.
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/v10.
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v10.4.0/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v10.4.0/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -702,53 +702,49 @@ the `var()` above, or read `gem.viewport`. The release checklist looks for the b
|
|
|
702
702
|
|
|
703
703
|
### The arcade's own controls: reserved areas
|
|
704
704
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
705
|
+
The Arcade keeps **one control of its own over every game**, on every device — a phone's full-bleed
|
|
706
|
+
game, a framed or full-window game on a desktop, a tablet or a TV in native fullscreen:
|
|
707
|
+
|
|
708
|
+
- **The Gem button**, the Gem Arcade's gem, in the **top-left corner**, always there (a touch target, about
|
|
709
|
+
50px across). It opens [the Arcade's menu](#the-arcades-menu): Return to the Arcade, the player's Gems,
|
|
710
|
+
Tickets and achievement score, mute, the player's Arcade-wide settings and the rest. Not your
|
|
711
|
+
settings: those are [your own button](#game-settings).
|
|
712
|
+
- **Nothing else.** The player's wallets (Gems, Tickets, their achievement score) are **not** drawn
|
|
713
|
+
over your game: the player opens the Gem menu to see them, and every platform-currency payout
|
|
714
|
+
flies into the Gem button ([the Arcade's wallet](#the-arcades-wallet-in-the-gem-menu-never-drawn-by-you)).
|
|
715
|
+
When the player switches the frame-rate readout on, it sits beside the gem and is reserved the
|
|
716
|
+
same way.
|
|
714
717
|
|
|
715
718
|
**The top of the screen has one layout, in every game:**
|
|
716
719
|
|
|
717
|
-
| Where
|
|
718
|
-
|
|
|
719
|
-
| **Top-left**
|
|
720
|
-
| **
|
|
721
|
-
| **Centre
|
|
722
|
-
|
|
723
|
-
Keep the left for Home / Back even on a screen that has neither today: a player learns where "back"
|
|
724
|
-
is once, across every game, and information that sits there on one screen teaches them the wrong
|
|
725
|
-
thing on the next.
|
|
720
|
+
| Where | Whose | What goes there |
|
|
721
|
+
| ------------------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
722
|
+
| **Top-left corner** | the Arcade's | Its Gem button. Nothing of yours under it. |
|
|
723
|
+
| **Top-left, right of the Gem** | yours | Your **Home / Back** buttons, drawn when a screen needs them, level with the Gem and just to its right. They move around _inside_ your game — back to your level select or title screen — and never leave it. |
|
|
724
|
+
| **Centre of the top row** | yours | Your **information**: score, moves, a timer, a level name. |
|
|
725
|
+
| **Top-right** | yours | Your **settings** button ([Game settings](#game-settings)). |
|
|
726
726
|
|
|
727
|
-
|
|
728
|
-
|
|
727
|
+
Keep the spot beside the Gem for Home / Back even on a screen that has neither today: a player
|
|
728
|
+
learns where "back" is once, across every game, and information that sits there on one screen
|
|
729
|
+
teaches them the wrong thing on the next.
|
|
729
730
|
|
|
730
|
-
**
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
the Arcade's. Do not copy those numbers — read the space, as below.
|
|
731
|
+
**Your settings button goes top-right**, clear of any Arcade control: under the Gem Arcade there is
|
|
732
|
+
none in that corner, but another embedder may keep one there. [Game settings](#game-settings) shows
|
|
733
|
+
how to place it.
|
|
734
734
|
|
|
735
735
|
Those rectangles of your frame are **reserved**: keep essential UI and every touch target out of
|
|
736
|
-
them. **Never hard-code their size or
|
|
737
|
-
|
|
736
|
+
them. **Never hard-code their size or position.** They differ by phone, and in native fullscreen the
|
|
737
|
+
gem may rise into the camera band — read them, as below.
|
|
738
738
|
|
|
739
|
-
An inset cannot say that — as a top inset, a button a few dozen pixels wide would take
|
|
740
|
-
strip. So `gem.viewport` also carries the rectangles, in the same CSS pixels of your
|
|
741
|
-
from its top-left corner — one
|
|
739
|
+
An inset cannot say that — as a left or top inset, a button a few dozen pixels wide would take a whole
|
|
740
|
+
strip of your screen. So `gem.viewport` also carries the rectangles, in the same CSS pixels of your
|
|
741
|
+
frame, measured from its top-left corner — one for the Gem button, and one for the frame-rate
|
|
742
|
+
readout while the player shows it:
|
|
742
743
|
|
|
743
744
|
```json
|
|
744
745
|
{
|
|
745
746
|
"insets": { "top": 47, "right": 0, "bottom": 34, "left": 0 },
|
|
746
|
-
"reserved": [
|
|
747
|
-
{ "x": 68, "y": 51, "width": 72, "height": 28 },
|
|
748
|
-
{ "x": 144, "y": 51, "width": 72, "height": 28 },
|
|
749
|
-
{ "x": 220, "y": 51, "width": 72, "height": 28 },
|
|
750
|
-
{ "x": 304, "y": 51, "width": 48, "height": 48 }
|
|
751
|
-
]
|
|
747
|
+
"reserved": [{ "x": 8, "y": 55, "width": 52, "height": 52 }]
|
|
752
748
|
}
|
|
753
749
|
```
|
|
754
750
|
|
|
@@ -756,59 +752,44 @@ from its top-left corner — one per wallet shown, and one for the menu button:
|
|
|
756
752
|
`--gem-safe-*` before anything else hears the push: `--gem-reserved-tl-width` and
|
|
757
753
|
`--gem-reserved-tl-height`, and the same pair for `tr`, `bl` and `br` — how far that corner's
|
|
758
754
|
reserved rectangles reach in from its two edges. A rectangle belongs to the corner its centre lies
|
|
759
|
-
nearest to.
|
|
760
|
-
|
|
761
|
-
⚠️ **The wallets sit in the middle, so they count toward BOTH top corners.** A wallet left of
|
|
762
|
-
centre is a top-left rectangle, one right of centre a top-right one. While wallets are shown,
|
|
763
|
-
`--gem-reserved-tl-height` is the bottom of the wallets and `--gem-reserved-tl-width` reaches to the
|
|
764
|
-
middle of the screen — even though the corner itself, left of the first wallet, is clear. So:
|
|
755
|
+
nearest to. Under the Gem Arcade the Gem button is a top-left rectangle, so:
|
|
765
756
|
|
|
766
757
|
```css
|
|
767
|
-
/*
|
|
768
|
-
the two top corners. It moves up by itself when gameplay hides the wallets. */
|
|
769
|
-
#info {
|
|
770
|
-
top: max(
|
|
771
|
-
var(--gem-safe-top, env(safe-area-inset-top, 0px)),
|
|
772
|
-
var(--gem-reserved-tl-height, 0px),
|
|
773
|
-
var(--gem-reserved-tr-height, 0px)
|
|
774
|
-
);
|
|
775
|
-
right: var(--gem-safe-right, 0px);
|
|
776
|
-
}
|
|
777
|
-
/* ONE Home or Back button, level with the arcade's row, in the clear corner to the wallets' left */
|
|
758
|
+
/* ONE Home or Back button, level with the Gem, just to its right */
|
|
778
759
|
#back {
|
|
779
760
|
top: var(--gem-safe-top, env(safe-area-inset-top, 0px));
|
|
780
|
-
left: var(--gem-safe-left, 0px);
|
|
761
|
+
left: max(var(--gem-safe-left, 0px), var(--gem-reserved-tl-width, 0px));
|
|
781
762
|
}
|
|
782
|
-
/*
|
|
783
|
-
|
|
784
|
-
#nav {
|
|
763
|
+
/* your information — score, moves, a timer — clear of every top corner the arcade reserves */
|
|
764
|
+
#info {
|
|
785
765
|
top: max(
|
|
786
766
|
var(--gem-safe-top, env(safe-area-inset-top, 0px)),
|
|
787
|
-
var(--gem-reserved-tl-height, 0px),
|
|
788
767
|
var(--gem-reserved-tr-height, 0px)
|
|
789
768
|
);
|
|
769
|
+
left: max(var(--gem-safe-left, 0px), var(--gem-reserved-tl-width, 0px));
|
|
770
|
+
right: var(--gem-safe-right, 0px);
|
|
790
771
|
}
|
|
791
772
|
```
|
|
792
773
|
|
|
793
|
-
The per-corner properties
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
774
|
+
The per-corner properties say how far a corner's rectangles reach, not how much room is left beside
|
|
775
|
+
them. To know whether a row of your own buttons fits beside the Gem, read the rectangles from
|
|
776
|
+
script: the room to the right of the top-left ones is the frame's width less the furthest right
|
|
777
|
+
edge among them.
|
|
797
778
|
|
|
798
779
|
```js
|
|
799
780
|
const reserved = gem.viewport?.reserved ?? [];
|
|
800
|
-
const
|
|
801
|
-
|
|
781
|
+
const topLeft = reserved.filter((r) => r.x + r.width / 2 < window.innerWidth / 2);
|
|
782
|
+
const from = topLeft.length ? Math.max(...topLeft.map((r) => r.x + r.width)) : 0;
|
|
783
|
+
nav.classList.toggle('below-arcade-row', window.innerWidth - from < nav.offsetWidth + 8);
|
|
802
784
|
```
|
|
803
785
|
|
|
804
786
|
All eight properties are written on every push, `0px` for a clear corner, so a rotation never leaves
|
|
805
787
|
a stale value; they are never set under an embedder that sends no viewport, which is what the `0px`
|
|
806
788
|
fallback is for. `gem.viewport.reserved` is `[]` when nothing is covered.
|
|
807
789
|
|
|
808
|
-
**Lay out against it whenever it changes.** The
|
|
809
|
-
session
|
|
810
|
-
|
|
811
|
-
all four is right under any embedder.
|
|
790
|
+
**Lay out against it whenever it changes.** The Gem button's rectangle is there for the whole
|
|
791
|
+
session, and it moves with a rotation, a resize or the frame-rate readout. Read every corner, not
|
|
792
|
+
only the one this page names today: a layout that honours all four is right under any embedder.
|
|
812
793
|
|
|
813
794
|
**Older SDKs get it folded into the top inset.** A game on an SDK from before reserved areas cannot
|
|
814
795
|
read the rectangles, so the arcade sends it none and instead raises `insets.top` — and so
|
|
@@ -943,12 +924,18 @@ same push as `muted` and `reducedMotion`, and again whenever the player changes
|
|
|
943
924
|
| `gamepadLookSensitivity` | `20` to `400` | Look speed for a gamepad stick, as a percentage: `100` is 1×. |
|
|
944
925
|
| `invertLookY` | `true` / `false` | Up/down look inverted: pushing up or dragging up looks down. Pitch only, never yaw. |
|
|
945
926
|
| `touchMovement` | `'floating'` / `'fixed'` | On a touch screen, a movement stick that appears where the thumb lands, or one fixed in place. |
|
|
927
|
+
| `graphicsQuality` | `'low'` / `'medium'` / `'high'` | The graphics quality to start at on this device. |
|
|
928
|
+
| `graphicsQualityAuto` | `true` / `false` | `true`: the Arcade picked that tier for this device, so you may adapt from it. `false`: the player picked it, so hold it. |
|
|
946
929
|
|
|
947
|
-
|
|
930
|
+
`lookSensitivity` to `touchMovement` are the camera and controls settings: see
|
|
948
931
|
[camera and look controls](#camera-and-look-controls) for telling the Arcade which of them your game
|
|
949
932
|
uses, and for `gem.controls.look()`, which applies the two sensitivities and the invert for you. An
|
|
950
933
|
older Arcade sends `recommended` without them; the SDK then fills each with its default (`100`,
|
|
951
|
-
`100`, `false`, `'floating'`)
|
|
934
|
+
`100`, `false`, `'floating'`). The last two are the starting graphics quality: see
|
|
935
|
+
[graphics quality](#graphics-quality-the-arcade-picks-the-start-your-game-adapts). An older Arcade
|
|
936
|
+
sends neither; the SDK then fills `graphicsQuality` from `batterySaver` (`'low'` when it is on,
|
|
937
|
+
`'medium'` otherwise) and `graphicsQualityAuto` with `true`, and reads a tier it does not know as
|
|
938
|
+
`'medium'`. So whenever `recommended` is not `null`, all eleven fields are there.
|
|
952
939
|
|
|
953
940
|
**They are recommendations, and the player's own choice in your game always wins:**
|
|
954
941
|
|
|
@@ -968,6 +955,7 @@ older Arcade sends `recommended` without them; the SDK then fills each with its
|
|
|
968
955
|
const DEFAULTS = {
|
|
969
956
|
volume: 100, batterySaver: false, keyboardLayout: 'wasd', gamepadConfirm: 'south', vibration: true,
|
|
970
957
|
lookSensitivity: 100, gamepadLookSensitivity: 100, invertLookY: false, touchMovement: 'floating',
|
|
958
|
+
graphicsQuality: 'medium',
|
|
971
959
|
};
|
|
972
960
|
let chosen = {}; // ONLY what the player picked in your settings screen
|
|
973
961
|
|
|
@@ -1021,7 +1009,7 @@ gem.controls.declare({
|
|
|
1021
1009
|
});
|
|
1022
1010
|
```
|
|
1023
1011
|
|
|
1024
|
-
It is screen state, like [`gem.setWalletVisible`](#the-arcades-wallet-
|
|
1012
|
+
It is screen state, like [`gem.setWalletVisible`](#the-arcades-wallet-in-the-gem-menu-never-drawn-by-you):
|
|
1025
1013
|
each call is the whole answer, the latest wins, and the SDK holds it and sends it again after a
|
|
1026
1014
|
reconnect. It returns `false` before the bridge is up (it is held and sent on connect) and under an
|
|
1027
1015
|
embedder that does not show these settings; it never throws. A key or a value other than these is
|
|
@@ -1086,11 +1074,71 @@ function frame(dt) {
|
|
|
1086
1074
|
`touchMovement: 'game'` if your game only has one kind.
|
|
1087
1075
|
- **No camera, no call.** A game with no camera to turn declares nothing and never calls `look`.
|
|
1088
1076
|
|
|
1077
|
+
## Graphics quality: the Arcade picks the start, your game adapts
|
|
1078
|
+
|
|
1079
|
+
Players open a lot of games they have never played. Each one should open at a quality that suits
|
|
1080
|
+
the device instead of guessing and stuttering for the first minute. The work is split three ways:
|
|
1081
|
+
|
|
1082
|
+
- **The Arcade picks the starting tier, per device**, and sends it as
|
|
1083
|
+
`gem.prefs.recommended.graphicsQuality`: `'low'`, `'medium'` or `'high'`. On Auto, the player's
|
|
1084
|
+
default, it is the tier the Arcade measured for this browser on this device. Otherwise it is the
|
|
1085
|
+
player's own choice in the Arcade's Settings: Battery saver (`'low'`, and cap your frame rate and
|
|
1086
|
+
cut effects too), Balanced (`'medium'`) or Best looking (`'high'`).
|
|
1087
|
+
- **Your game adapts from there while it runs**: one level down when frames stay slow, one level up
|
|
1088
|
+
when there is plenty of room. `createQualityGovernor` does this for you, so you do not write a
|
|
1089
|
+
frame-time monitor of your own.
|
|
1090
|
+
- **A choice the player made by hand is held.** `graphicsQualityAuto` is `false` when the player
|
|
1091
|
+
picked the tier, and then you render at exactly that tier and never move it yourself. Your own
|
|
1092
|
+
quality option, if your settings screen has one, still wins over the Arcade's, as every
|
|
1093
|
+
[recommended setting](#start-from-the-players-arcade-wide-defaults-gemprefsrecommended) does.
|
|
1094
|
+
|
|
1095
|
+
The governor is a separate import, so a game that does not use it carries none of it:
|
|
1096
|
+
|
|
1097
|
+
```js
|
|
1098
|
+
import { createQualityGovernor } from '@indiegems/gem-web-sdk/quality';
|
|
1099
|
+
|
|
1100
|
+
// Your own levels, lowest first. Names, or a count: `levels: 4` means levels 0 to 3.
|
|
1101
|
+
const governor = createQualityGovernor({
|
|
1102
|
+
gem,
|
|
1103
|
+
levels: ['low', 'medium', 'high'],
|
|
1104
|
+
onChange: (level) => applyQuality(level), // shadows, resolution scale, particles…
|
|
1105
|
+
});
|
|
1106
|
+
applyQuality(governor.level); // where it starts; onChange is not called for that
|
|
1107
|
+
```
|
|
1108
|
+
|
|
1109
|
+
What it does, so you know what to expect:
|
|
1110
|
+
|
|
1111
|
+
- **It starts at the Arcade's tier.** A tier whose name is one of your levels starts there;
|
|
1112
|
+
otherwise `'low'` is your lowest level, `'high'` your highest and `'medium'` your middle one. With
|
|
1113
|
+
no Arcade (`gem.prefs` is `null`, or no `gem`), it starts in the middle.
|
|
1114
|
+
- **It measures your frames itself**, with `requestAnimationFrame`. If your loop already knows its
|
|
1115
|
+
frame time, pass `manual: true` and call `governor.frame(dtMs)` once a frame instead; the first
|
|
1116
|
+
`frame` call switches it to your numbers anyway, so frames are never counted twice.
|
|
1117
|
+
- **Down fast, up slow.** One level down after about 2 seconds of slow frames (the 90th-percentile
|
|
1118
|
+
frame over 1.25 times your frame budget), one level up after about 10 seconds with room to spare
|
|
1119
|
+
(under 0.7 times the budget), and never more than one level above where it started. A single
|
|
1120
|
+
hitch is not slow frames. After every change it ignores 2 seconds of frames, and a level it
|
|
1121
|
+
already had to leave takes twice as long to climb back into.
|
|
1122
|
+
- **Your budget is `1000 / targetFps`**, and `targetFps` is 60 unless you say. A game that caps
|
|
1123
|
+
itself at 30 frames a second on battery saver passes `targetFps: 30`, or every frame reads as
|
|
1124
|
+
slow.
|
|
1125
|
+
- **It never counts a paused game**: not while the Arcade has you paused (`gem.paused`), and not
|
|
1126
|
+
while the tab is hidden. It also ignores the first 2 seconds, which are your game loading.
|
|
1127
|
+
- **It holds a hand-picked tier.** While `graphicsQualityAuto` is `false` it stays at the start and
|
|
1128
|
+
never calls `onChange` on its own. Pass `auto: false` to hold whatever the Arcade says, or
|
|
1129
|
+
`start: 'high'` to start from a level the player chose in your own settings.
|
|
1130
|
+
- **A new recommendation starts it over.** When the player changes Graphics in the Arcade's
|
|
1131
|
+
Settings, the governor goes back to the new start and calls `onChange(level, 'reset')`. It reads
|
|
1132
|
+
`gem.prefs` live, so you do not have to tell it.
|
|
1133
|
+
- **`governor.stats`** has what it measured (`fps`, `p90Ms` and `budgetMs`, the level's index,
|
|
1134
|
+
whether it is holding or paused), for your own debug overlay. Call `governor.dispose()` when your
|
|
1135
|
+
game stops rendering for good.
|
|
1136
|
+
|
|
1089
1137
|
## The Arcade's menu
|
|
1090
1138
|
|
|
1091
|
-
The Arcade keeps one
|
|
1092
|
-
(**Return to the Arcade**),
|
|
1093
|
-
settings — the ones that reach you as [`gem.prefs.recommended`](#game-settings). You can add up to
|
|
1139
|
+
The Arcade keeps one button over every game, on every device: the Gem, at the top-left. Its menu is
|
|
1140
|
+
how a player leaves (**Return to the Arcade**), sees their Gems, Tickets and achievement score, mutes,
|
|
1141
|
+
goes fullscreen, reports a problem and changes their Arcade-wide settings — the ones that reach you as [`gem.prefs.recommended`](#game-settings). You can add up to
|
|
1094
1142
|
four entries of your own:
|
|
1095
1143
|
|
|
1096
1144
|
```js
|
|
@@ -1207,28 +1255,24 @@ its music out than cut it, say. The Arcade still shows its button for your game
|
|
|
1207
1255
|
so this is a promise: your `onPrefs` handler must go quiet on `muted: true` and come back on `false`,
|
|
1208
1256
|
and the release checklist checks that it does.
|
|
1209
1257
|
|
|
1210
|
-
## The Arcade's wallet:
|
|
1258
|
+
## The Arcade's wallet: in the Gem menu, never drawn by you
|
|
1211
1259
|
|
|
1212
1260
|
The player's platform balances — Gems, Tickets (`ARCADE_TICKETS`) and their achievement score —
|
|
1213
1261
|
belong to the Arcade. **Your game never draws one**, and never shows a pending state for one: the
|
|
1214
|
-
Arcade takes every figure from the platform and
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
[
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
Read the `--gem-reserved-*` properties rather than assuming either state.
|
|
1229
|
-
|
|
1230
|
-
⚠️ **The Arcade ignores the `only` option**: a shown wallet is every wallet. Do not build a store
|
|
1231
|
-
screen that relies on narrowing them to one currency.
|
|
1262
|
+
Arcade takes every figure from the platform and shows them itself.
|
|
1263
|
+
|
|
1264
|
+
**The Gem Arcade draws no wallet over your game** (since 2026-10-11). Nothing of it sits in your top
|
|
1265
|
+
row, at rest or when a balance moves. The player opens the Gem menu — the Gem button at the
|
|
1266
|
+
top-left — to see their Gems, Tickets and achievement score, and every payout
|
|
1267
|
+
[`gem.cueReward`](inventory-and-catalogs.md#showing-the-reward-land-in-the-arcades-wallet) announces
|
|
1268
|
+
flies into the Gem button. A payout you do not cue flies too, from the middle of your frame, so cue
|
|
1269
|
+
the ones the player earns on screen and the flight starts where they are looking.
|
|
1270
|
+
|
|
1271
|
+
**`gem.setWalletVisible` changes nothing on the Gem Arcade.** It is still part of the contract, so a
|
|
1272
|
+
game that calls it keeps working, and it is the call to make for an embedder that draws wallets over
|
|
1273
|
+
games: hide them while you play, show them on your menus. Under the Gem Arcade there is no wallet to
|
|
1274
|
+
show or hide and no wallet rectangle in `gem.viewport.reserved`. The `only` option is ignored there
|
|
1275
|
+
as well.
|
|
1232
1276
|
|
|
1233
1277
|
## No accidental text selection
|
|
1234
1278
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indiegems/gem-web-sdk",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"description": "postMessage bridge contract between gem-web and browser-playable games",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"types": "./dist/function.d.ts",
|
|
36
36
|
"import": "./dist/gem-web-sdk.function.js"
|
|
37
37
|
},
|
|
38
|
+
"./quality": {
|
|
39
|
+
"types": "./dist/quality.d.ts",
|
|
40
|
+
"import": "./dist/gem-web-sdk.quality.js"
|
|
41
|
+
},
|
|
38
42
|
"./schema": "./schema/bridge.v1.json",
|
|
39
43
|
"./brand/*": "./brand/*"
|
|
40
44
|
},
|
package/schema/bridge.v1.json
CHANGED
|
@@ -804,7 +804,8 @@
|
|
|
804
804
|
"$comment": [
|
|
805
805
|
"The player's ARCADE-WIDE DEFAULTS for this device, which the Arcade",
|
|
806
806
|
"keeps in its own Settings: a starting volume, battery saver, a keyboard",
|
|
807
|
-
"layout, the gamepad's confirm button,
|
|
807
|
+
"layout, the gamepad's confirm button, vibration, the camera and",
|
|
808
|
+
"controls settings and a starting graphics quality. RECOMMENDATIONS",
|
|
808
809
|
"ONLY. The game's own saved settings win: a game starts a setting from",
|
|
809
810
|
"here only while the player has made no choice of their own in that",
|
|
810
811
|
"game, and never overwrites one the player made because this changed.",
|
|
@@ -824,6 +825,16 @@
|
|
|
824
825
|
"the game side all four are present whenever the object is. A game says",
|
|
825
826
|
"which of them it leaves to the player with `gem.controlsDeclare`.",
|
|
826
827
|
"",
|
|
828
|
+
"THE GRAPHICS TWO ARE OPTIONAL INSIDE IT TOO (owner decision",
|
|
829
|
+
"2026-10-11): `graphicsQuality` and `graphicsQualityAuto`, the quality",
|
|
830
|
+
"a game STARTS at on this device. The Arcade picks the start (the",
|
|
831
|
+
"player's Graphics setting, or the tier it detected for this device on",
|
|
832
|
+
"Auto); the game adapts from there while it runs, and holds the start",
|
|
833
|
+
"when the player picked it by hand. A parent built against an older SDK",
|
|
834
|
+
"omits them, and the SDK fills `graphicsQuality` from `batterySaver`",
|
|
835
|
+
"(`low` when it is on, `medium` otherwise) and `graphicsQualityAuto`",
|
|
836
|
+
"with true, so on the game side both are present whenever the object is.",
|
|
837
|
+
"",
|
|
827
838
|
"`muted`, `reducedMotion` and `showFrameStats` stay where they are, at",
|
|
828
839
|
"the top of this message, and keep their meaning: they are the Arcade's",
|
|
829
840
|
"own state the frame acts on, not a recommendation. The Arcade starts",
|
|
@@ -900,6 +911,31 @@
|
|
|
900
911
|
"only `floating` or `fixed` today, and the SDK maps any other value to",
|
|
901
912
|
"the default, `floating`, so a game only ever sees those two."
|
|
902
913
|
]
|
|
914
|
+
},
|
|
915
|
+
"graphicsQuality": {
|
|
916
|
+
"kind": "string",
|
|
917
|
+
"maxLength": 16,
|
|
918
|
+
"format": "code",
|
|
919
|
+
"optional": true,
|
|
920
|
+
"$comment": [
|
|
921
|
+
"The graphics quality a game should START at on this device: `low`,",
|
|
922
|
+
"`medium` or `high`. The player's Graphics setting decides it --",
|
|
923
|
+
"Battery saver sends `low`, Balanced `medium`, Best looking `high`,",
|
|
924
|
+
"and Auto (the default) sends the tier the Arcade detected for this",
|
|
925
|
+
"device. A starting point, not a cap: a game adapts from here while it",
|
|
926
|
+
"runs unless `graphicsQualityAuto` is false.",
|
|
927
|
+
"",
|
|
928
|
+
"A CODE, NOT AN ENUM, so the vocabulary can grow without costing an",
|
|
929
|
+
"older frame the whole message (see WIDENING above): a parent sends",
|
|
930
|
+
"only `low`, `medium` or `high` today, and the SDK maps any other",
|
|
931
|
+
"value to `medium`, so a game only ever sees those three. When it is",
|
|
932
|
+
"absent the SDK fills `low` if `batterySaver` is true, else `medium`."
|
|
933
|
+
]
|
|
934
|
+
},
|
|
935
|
+
"graphicsQualityAuto": {
|
|
936
|
+
"kind": "boolean",
|
|
937
|
+
"optional": true,
|
|
938
|
+
"$comment": "True when the Arcade detected `graphicsQuality` for this device (the player left Graphics on Auto), so the game may step its quality up or down from there as it measures frames; false when the player picked it by hand, so the game holds it and changes it only when the player does (in the Arcade or in the game's own settings). Default true when absent."
|
|
903
939
|
}
|
|
904
940
|
}
|
|
905
941
|
}
|
|
@@ -2437,9 +2473,10 @@
|
|
|
2437
2473
|
"$comment": [
|
|
2438
2474
|
"A platform-currency reward is on its way — the game's SERVER has just",
|
|
2439
2475
|
"moved (or is about to move) a progression mapped onto a platform currency,",
|
|
2440
|
-
"and the game would like the arcade
|
|
2441
|
-
"
|
|
2442
|
-
"
|
|
2476
|
+
"and the game would like the arcade to show it landing: the currency flying",
|
|
2477
|
+
"from `origin` into the arcade's chrome — in the Gem Arcade, into its Gem",
|
|
2478
|
+
"button at the frame's top-left — and the total moving when the platform",
|
|
2479
|
+
"confirms the grant.",
|
|
2443
2480
|
"",
|
|
2444
2481
|
"A PRESENTATION CUE, NOT A CLAIM. Nothing here is trusted as a number. The",
|
|
2445
2482
|
"amount that lands and the player's new total reach the parent from gem-api",
|
|
@@ -2452,7 +2489,8 @@
|
|
|
2452
2489
|
"`origin` is where the flight starts, in CSS px of the frame's own",
|
|
2453
2490
|
"coordinate space — the same space `gem.viewport` speaks — measured from",
|
|
2454
2491
|
"the frame's top-left corner; the parent translates it into its own page.",
|
|
2455
|
-
"Absent, the
|
|
2492
|
+
"Absent, the Gem Arcade flies the confirmed grant from the middle of the",
|
|
2493
|
+
"frame, as it does a grant no cue announced. Integers, like",
|
|
2456
2494
|
"`gem.viewport`'s insets: a fractional pixel is not a placement, and the",
|
|
2457
2495
|
"SDK rounds and clamps on the way out. 16384 is a bound, not a size.",
|
|
2458
2496
|
"",
|
|
@@ -2472,10 +2510,9 @@
|
|
|
2472
2510
|
"other, `gem.cueReward` returns false and the game does NOTHING. There is",
|
|
2473
2511
|
"no fallback celebration: Gems display belongs to the arcade (the",
|
|
2474
2512
|
"creator's ruling, 2026-09-24), so a game draws no Gems flight, counter or",
|
|
2475
|
-
"celebration of its own. The same holds for Tickets (`ARCADE_TICKETS`):
|
|
2476
|
-
"
|
|
2477
|
-
"
|
|
2478
|
-
"that currency — its gem, or its ticket."
|
|
2513
|
+
"celebration of its own. The same holds for Tickets (`ARCADE_TICKETS`):",
|
|
2514
|
+
"`currencySlug` decides what flies and which total moves. `origin` is the",
|
|
2515
|
+
"centre of the game's icon for that currency — its gem, or its ticket."
|
|
2479
2516
|
],
|
|
2480
2517
|
"fields": {
|
|
2481
2518
|
"currencySlug": { "kind": "string", "maxLength": 32 },
|
|
@@ -2531,6 +2568,12 @@
|
|
|
2531
2568
|
"docks its wallets beside the frame rather than over it, this changes",
|
|
2532
2569
|
"nothing.",
|
|
2533
2570
|
"",
|
|
2571
|
+
"THE GEM ARCADE DRAWS NO WALLET OVER A GAME (since 2026-10-11): the player",
|
|
2572
|
+
"sees their balances in its Gem menu and payouts fly into its Gem button, so",
|
|
2573
|
+
"it advertises this message, accepts it, and changes nothing on screen — no",
|
|
2574
|
+
"wallet is shown, popped up or reserved whatever the game says. The rules",
|
|
2575
|
+
"above are for a parent that does draw wallets over the frame.",
|
|
2576
|
+
"",
|
|
2534
2577
|
"A PRESENTATION REQUEST ONLY. Not an authorization control, and it carries no",
|
|
2535
2578
|
"numbers: the parent takes every figure it shows from the platform. A game",
|
|
2536
2579
|
"never draws a platform balance itself and never implements a pending state.",
|