@indiegems/gem-web-sdk 10.1.1 → 10.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 +16 -0
- package/dist/game.d.ts +3 -0
- package/dist/gem-web-sdk.bridge.js +1 -1
- package/dist/gem-web-sdk.esm.js +2 -2
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +2 -2
- package/dist/gem-web-sdk.min.js +2 -2
- package/dist/gem-web-sdk.sdk.js +2 -2
- package/dist/gem-web-sdk.server-api.js +1 -1
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +13 -3
- package/dist/sdk.d.ts +114 -11
- package/dist/server-api.d.ts +70 -2
- package/dist/server.d.ts +103 -3
- package/docs/api/api-reference-game.md +104 -8
- package/docs/api/api-reference-server.md +244 -4
- package/docs/api/reference.json +319 -11
- package/docs/changelog.md +16 -0
- package/docs/getting-started.md +1 -1
- package/docs/manifest.json +10 -10
- package/docs/rooms-and-matches.md +7 -0
- package/docs/server-bundles.md +46 -2
- package/docs/what-a-game-must-do.md +38 -0
- package/package.json +1 -1
- package/schema/bridge.v1.json +93 -1
- package/schema/capabilities.public.json +6 -0
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": "10.
|
|
5
|
+
"version": "10.2.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -6034,6 +6034,22 @@
|
|
|
6034
6034
|
"defaultValue": null,
|
|
6035
6035
|
"guides": []
|
|
6036
6036
|
},
|
|
6037
|
+
{
|
|
6038
|
+
"id": "GemFrame.requestAvatarPicker",
|
|
6039
|
+
"name": "requestAvatarPicker",
|
|
6040
|
+
"anchor": "gemframe-requestavatarpicker",
|
|
6041
|
+
"group": "frame",
|
|
6042
|
+
"signature": "requestAvatarPicker(): boolean",
|
|
6043
|
+
"documented": true,
|
|
6044
|
+
"summary": "Ask the arcade to open its own avatar picker over your game. Nothing comes\nback: a changed portrait arrives as a new `gem.player`, through `onPlayer`.\n\nPrefer `gem.openAvatarPicker`, which documents what a game does with the\nchange.",
|
|
6045
|
+
"deprecated": null,
|
|
6046
|
+
"params": [],
|
|
6047
|
+
"returns": "whether the message left the frame — `false` before the bridge is\n up, for an arcade that has no avatar picker here, and past this frame's\n own limit of six a minute.",
|
|
6048
|
+
"throws": null,
|
|
6049
|
+
"example": null,
|
|
6050
|
+
"defaultValue": null,
|
|
6051
|
+
"guides": []
|
|
6052
|
+
},
|
|
6037
6053
|
{
|
|
6038
6054
|
"id": "GemFrame.requestVibrate",
|
|
6039
6055
|
"name": "requestVibrate",
|
|
@@ -6190,7 +6206,7 @@
|
|
|
6190
6206
|
"group": "friends",
|
|
6191
6207
|
"signature": "readonly avatarIconId?: string",
|
|
6192
6208
|
"documented": true,
|
|
6193
|
-
"summary": "The id of the player's portrait item, when they have one.",
|
|
6209
|
+
"summary": "The id of the player's portrait item, when they have one — what a game\nthat draws Arcade avatars keys its own model on. On `gem.player` it\nchanges when the player wears another portrait, and `onPlayer` runs again;\nsee `gem.openAvatarPicker`.",
|
|
6194
6210
|
"deprecated": null,
|
|
6195
6211
|
"params": [],
|
|
6196
6212
|
"returns": null,
|
|
@@ -7960,7 +7976,7 @@
|
|
|
7960
7976
|
"group": "player",
|
|
7961
7977
|
"signature": "readonly avatarIconId?: string",
|
|
7962
7978
|
"documented": true,
|
|
7963
|
-
"summary": "The id of the player's portrait item, when they have one.",
|
|
7979
|
+
"summary": "The id of the player's portrait item, when they have one — what a game\nthat draws Arcade avatars keys its own model on. On `gem.player` it\nchanges when the player wears another portrait, and `onPlayer` runs again;\nsee `gem.openAvatarPicker`.",
|
|
7964
7980
|
"deprecated": null,
|
|
7965
7981
|
"params": [],
|
|
7966
7982
|
"returns": null,
|
|
@@ -9030,6 +9046,38 @@
|
|
|
9030
9046
|
}
|
|
9031
9047
|
]
|
|
9032
9048
|
},
|
|
9049
|
+
{
|
|
9050
|
+
"id": "Gem.avatarPickerAvailable",
|
|
9051
|
+
"name": "avatarPickerAvailable",
|
|
9052
|
+
"anchor": "gem-avatarpickeravailable",
|
|
9053
|
+
"group": "start",
|
|
9054
|
+
"signature": "readonly avatarPickerAvailable: boolean",
|
|
9055
|
+
"documented": true,
|
|
9056
|
+
"summary": "Whether the arcade will open its avatar picker for [Gem.openAvatarPicker](api-reference-game.md#gem-openavatarpicker)\nhere. Decide from it whether to show your own \"Change avatar\" button, and\nread it again in the click handler — it is read live.\n\n`false` outside the arcade, before the bridge is up, after the session\nends, and under an embedder that has no avatar picker.",
|
|
9057
|
+
"deprecated": null,
|
|
9058
|
+
"params": [],
|
|
9059
|
+
"returns": null,
|
|
9060
|
+
"throws": null,
|
|
9061
|
+
"example": null,
|
|
9062
|
+
"defaultValue": null,
|
|
9063
|
+
"guides": []
|
|
9064
|
+
},
|
|
9065
|
+
{
|
|
9066
|
+
"id": "Gem.openAvatarPicker",
|
|
9067
|
+
"name": "openAvatarPicker",
|
|
9068
|
+
"anchor": "gem-openavatarpicker",
|
|
9069
|
+
"group": "start",
|
|
9070
|
+
"signature": "openAvatarPicker(): boolean",
|
|
9071
|
+
"documented": true,
|
|
9072
|
+
"summary": "Open the arcade's avatar picker over your game, so the player can change\nthe Arcade avatar they wear without leaving it. For a game that draws\nArcade avatars — whole characters the player owns as Arcade items — put a\n\"Change avatar\" button in your own UI and call this from it:\n\n```js\nchangeAvatar.hidden = !gem.avatarPickerAvailable;\nchangeAvatar.onclick = () => {\n if (!gem.openAvatarPicker()) changeAvatar.hidden = true;\n};\n```\n\n**The change arrives as a new player, not as an answer.** Nothing comes\nback from this call. When the player picks a different avatar, the arcade\npauses your game while the picker is open, then sends the player again —\nyour `onPlayer` callback runs and [Gem.player](api-reference-game.md#gem-player) holds the new\n[PlayerIdentity.avatarIconId](api-reference-game.md#playeridentity-avatariconid) — and resumes it. Redraw from\n`onPlayer`, which also runs when the player changes their avatar from the\narcade's own menu without your game asking. A player who closes the picker\nunchanged produces nothing at all.\n\n**Your server learns it only when it reads the room again.** A\nserver-authoritative game reads each member's `avatar_icon_id` from the\nroom read (`GET /v1/games/{game_id}/game-rooms/{game_room_id}`, or\n`gem.room.get()` on a dedicated server); nothing tells the server the\nplayer changed. So when `onPlayer` brings a different `avatarIconId`, have\nyour client tell your server, and have the server re-read the room — never\ntrust the id the client sends.",
|
|
9073
|
+
"deprecated": null,
|
|
9074
|
+
"params": [],
|
|
9075
|
+
"returns": "`true` when the arcade was asked; `false` when it was not and\n never will be for this call — outside the arcade, before the bridge is\n up, under an embedder with no avatar picker, or past six asks a minute.\n It never throws and never waits.",
|
|
9076
|
+
"throws": null,
|
|
9077
|
+
"example": null,
|
|
9078
|
+
"defaultValue": null,
|
|
9079
|
+
"guides": []
|
|
9080
|
+
},
|
|
9033
9081
|
{
|
|
9034
9082
|
"id": "Gem.vibration",
|
|
9035
9083
|
"name": "vibration",
|
|
@@ -10625,7 +10673,7 @@
|
|
|
10625
10673
|
"group": "player",
|
|
10626
10674
|
"signature": "readonly avatarIconId?: string",
|
|
10627
10675
|
"documented": true,
|
|
10628
|
-
"summary": "The id of the player's portrait item, when they have one.",
|
|
10676
|
+
"summary": "The id of the player's portrait item, when they have one — what a game\nthat draws Arcade avatars keys its own model on. On `gem.player` it\nchanges when the player wears another portrait, and `onPlayer` runs again;\nsee `gem.openAvatarPicker`.",
|
|
10629
10677
|
"deprecated": null,
|
|
10630
10678
|
"params": [],
|
|
10631
10679
|
"returns": null,
|
|
@@ -13397,7 +13445,7 @@
|
|
|
13397
13445
|
"group": "leaderboards",
|
|
13398
13446
|
"signature": "readonly avatarIconId?: string",
|
|
13399
13447
|
"documented": true,
|
|
13400
|
-
"summary": "The id of the player's portrait item, when they have one.",
|
|
13448
|
+
"summary": "The id of the player's portrait item, when they have one — what a game\nthat draws Arcade avatars keys its own model on. On `gem.player` it\nchanges when the player wears another portrait, and `onPlayer` runs again;\nsee `gem.openAvatarPicker`.",
|
|
13401
13449
|
"deprecated": null,
|
|
13402
13450
|
"params": [],
|
|
13403
13451
|
"returns": null,
|
|
@@ -15160,7 +15208,7 @@
|
|
|
15160
15208
|
"group": "profiles",
|
|
15161
15209
|
"signature": "readonly avatarIconId?: string",
|
|
15162
15210
|
"documented": true,
|
|
15163
|
-
"summary": "The id of the player's portrait item, when they have one.",
|
|
15211
|
+
"summary": "The id of the player's portrait item, when they have one — what a game\nthat draws Arcade avatars keys its own model on. On `gem.player` it\nchanges when the player wears another portrait, and `onPlayer` runs again;\nsee `gem.openAvatarPicker`.",
|
|
15164
15212
|
"deprecated": null,
|
|
15165
15213
|
"params": [],
|
|
15166
15214
|
"returns": null,
|
|
@@ -17976,7 +18024,7 @@
|
|
|
17976
18024
|
"group": "rooms",
|
|
17977
18025
|
"signature": "readonly avatarIconId?: string",
|
|
17978
18026
|
"documented": true,
|
|
17979
|
-
"summary": "The id of the player's portrait item, when they have one.",
|
|
18027
|
+
"summary": "The id of the player's portrait item, when they have one — what a game\nthat draws Arcade avatars keys its own model on. On `gem.player` it\nchanges when the player wears another portrait, and `onPlayer` runs again;\nsee `gem.openAvatarPicker`.",
|
|
17980
18028
|
"deprecated": null,
|
|
17981
18029
|
"params": [],
|
|
17982
18030
|
"returns": null,
|
|
@@ -19058,7 +19106,7 @@
|
|
|
19058
19106
|
"group": "showcase",
|
|
19059
19107
|
"signature": "readonly avatarIconId?: string",
|
|
19060
19108
|
"documented": true,
|
|
19061
|
-
"summary": "The id of the player's portrait item, when they have one.",
|
|
19109
|
+
"summary": "The id of the player's portrait item, when they have one — what a game\nthat draws Arcade avatars keys its own model on. On `gem.player` it\nchanges when the player wears another portrait, and `onPlayer` runs again;\nsee `gem.openAvatarPicker`.",
|
|
19062
19110
|
"deprecated": null,
|
|
19063
19111
|
"params": [],
|
|
19064
19112
|
"returns": null,
|
|
@@ -21744,6 +21792,54 @@
|
|
|
21744
21792
|
"defaultValue": null,
|
|
21745
21793
|
"guides": []
|
|
21746
21794
|
},
|
|
21795
|
+
{
|
|
21796
|
+
"id": "MatchBackfillState",
|
|
21797
|
+
"name": "MatchBackfillState",
|
|
21798
|
+
"anchor": "matchbackfillstate",
|
|
21799
|
+
"group": "match",
|
|
21800
|
+
"signature": "interface MatchBackfillState",
|
|
21801
|
+
"documented": true,
|
|
21802
|
+
"summary": "Whether this match is being topped up from the matchmaking queue, as the\nplatform now has it.",
|
|
21803
|
+
"deprecated": null,
|
|
21804
|
+
"params": [],
|
|
21805
|
+
"returns": null,
|
|
21806
|
+
"throws": null,
|
|
21807
|
+
"example": null,
|
|
21808
|
+
"defaultValue": null,
|
|
21809
|
+
"guides": []
|
|
21810
|
+
},
|
|
21811
|
+
{
|
|
21812
|
+
"id": "MatchPlayerReport",
|
|
21813
|
+
"name": "MatchPlayerReport",
|
|
21814
|
+
"anchor": "matchplayerreport",
|
|
21815
|
+
"group": "match",
|
|
21816
|
+
"signature": "interface MatchPlayerReport",
|
|
21817
|
+
"documented": true,
|
|
21818
|
+
"summary": "What your server says about one player of its match, for\n[GemServer.match](api-reference-server.md#gemserver-match)'s `reportPlayers`.",
|
|
21819
|
+
"deprecated": null,
|
|
21820
|
+
"params": [],
|
|
21821
|
+
"returns": null,
|
|
21822
|
+
"throws": null,
|
|
21823
|
+
"example": null,
|
|
21824
|
+
"defaultValue": null,
|
|
21825
|
+
"guides": []
|
|
21826
|
+
},
|
|
21827
|
+
{
|
|
21828
|
+
"id": "MatchPlayerReportResult",
|
|
21829
|
+
"name": "MatchPlayerReportResult",
|
|
21830
|
+
"anchor": "matchplayerreportresult",
|
|
21831
|
+
"group": "match",
|
|
21832
|
+
"signature": "interface MatchPlayerReportResult",
|
|
21833
|
+
"documented": true,
|
|
21834
|
+
"summary": "What a report did for one player, in the order you reported them.",
|
|
21835
|
+
"deprecated": null,
|
|
21836
|
+
"params": [],
|
|
21837
|
+
"returns": null,
|
|
21838
|
+
"throws": null,
|
|
21839
|
+
"example": null,
|
|
21840
|
+
"defaultValue": null,
|
|
21841
|
+
"guides": []
|
|
21842
|
+
},
|
|
21747
21843
|
{
|
|
21748
21844
|
"id": "GemServerOptions",
|
|
21749
21845
|
"name": "GemServerOptions",
|
|
@@ -22535,6 +22631,102 @@
|
|
|
22535
22631
|
"defaultValue": null,
|
|
22536
22632
|
"guides": []
|
|
22537
22633
|
},
|
|
22634
|
+
{
|
|
22635
|
+
"id": "MatchPlayerReport.playerId",
|
|
22636
|
+
"name": "playerId",
|
|
22637
|
+
"anchor": "matchplayerreport-playerid",
|
|
22638
|
+
"group": "match",
|
|
22639
|
+
"signature": "readonly playerId: string",
|
|
22640
|
+
"documented": true,
|
|
22641
|
+
"summary": "The player. Take it from `join()`, `leave()` or the room, never from a client.",
|
|
22642
|
+
"deprecated": null,
|
|
22643
|
+
"params": [],
|
|
22644
|
+
"returns": null,
|
|
22645
|
+
"throws": null,
|
|
22646
|
+
"example": null,
|
|
22647
|
+
"defaultValue": null,
|
|
22648
|
+
"guides": []
|
|
22649
|
+
},
|
|
22650
|
+
{
|
|
22651
|
+
"id": "MatchPlayerReport.state",
|
|
22652
|
+
"name": "state",
|
|
22653
|
+
"anchor": "matchplayerreport-state",
|
|
22654
|
+
"group": "match",
|
|
22655
|
+
"signature": "readonly state: \"connected\" | \"no_show\" | \"left\"",
|
|
22656
|
+
"documented": true,
|
|
22657
|
+
"summary": "- `connected` — the player has connected to this server.\n- `no_show` — the player was given a seat and never connected.\n- `left` — the player was in the match and is gone FOR GOOD.\n\nThe last two remove the player from the room, which frees the seat.",
|
|
22658
|
+
"deprecated": null,
|
|
22659
|
+
"params": [],
|
|
22660
|
+
"returns": null,
|
|
22661
|
+
"throws": null,
|
|
22662
|
+
"example": null,
|
|
22663
|
+
"defaultValue": null,
|
|
22664
|
+
"guides": []
|
|
22665
|
+
},
|
|
22666
|
+
{
|
|
22667
|
+
"id": "MatchPlayerReportResult.playerId",
|
|
22668
|
+
"name": "playerId",
|
|
22669
|
+
"anchor": "matchplayerreportresult-playerid",
|
|
22670
|
+
"group": "match",
|
|
22671
|
+
"signature": "readonly playerId: string",
|
|
22672
|
+
"documented": true,
|
|
22673
|
+
"summary": "The player this result is for.",
|
|
22674
|
+
"deprecated": null,
|
|
22675
|
+
"params": [],
|
|
22676
|
+
"returns": null,
|
|
22677
|
+
"throws": null,
|
|
22678
|
+
"example": null,
|
|
22679
|
+
"defaultValue": null,
|
|
22680
|
+
"guides": []
|
|
22681
|
+
},
|
|
22682
|
+
{
|
|
22683
|
+
"id": "MatchPlayerReportResult.outcome",
|
|
22684
|
+
"name": "outcome",
|
|
22685
|
+
"anchor": "matchplayerreportresult-outcome",
|
|
22686
|
+
"group": "match",
|
|
22687
|
+
"signature": "readonly outcome: \"recorded\" | \"removed\" | \"not_a_member\" | string & object",
|
|
22688
|
+
"documented": true,
|
|
22689
|
+
"summary": "- `recorded` — `connected` was noted.\n- `removed` — the player was taken out of the room.\n- `not_a_member` — the player is not in this match's room; nothing changed.",
|
|
22690
|
+
"deprecated": null,
|
|
22691
|
+
"params": [],
|
|
22692
|
+
"returns": null,
|
|
22693
|
+
"throws": null,
|
|
22694
|
+
"example": null,
|
|
22695
|
+
"defaultValue": null,
|
|
22696
|
+
"guides": []
|
|
22697
|
+
},
|
|
22698
|
+
{
|
|
22699
|
+
"id": "MatchBackfillState.open",
|
|
22700
|
+
"name": "open",
|
|
22701
|
+
"anchor": "matchbackfillstate-open",
|
|
22702
|
+
"group": "match",
|
|
22703
|
+
"signature": "readonly open: boolean",
|
|
22704
|
+
"documented": true,
|
|
22705
|
+
"summary": "Whether the match is taking players from the queue.",
|
|
22706
|
+
"deprecated": null,
|
|
22707
|
+
"params": [],
|
|
22708
|
+
"returns": null,
|
|
22709
|
+
"throws": null,
|
|
22710
|
+
"example": null,
|
|
22711
|
+
"defaultValue": null,
|
|
22712
|
+
"guides": []
|
|
22713
|
+
},
|
|
22714
|
+
{
|
|
22715
|
+
"id": "MatchBackfillState.seats",
|
|
22716
|
+
"name": "seats",
|
|
22717
|
+
"anchor": "matchbackfillstate-seats",
|
|
22718
|
+
"group": "match",
|
|
22719
|
+
"signature": "readonly seats: number | null",
|
|
22720
|
+
"documented": true,
|
|
22721
|
+
"summary": "The most players still wanted, or `null` for every open seat.",
|
|
22722
|
+
"deprecated": null,
|
|
22723
|
+
"params": [],
|
|
22724
|
+
"returns": null,
|
|
22725
|
+
"throws": null,
|
|
22726
|
+
"example": null,
|
|
22727
|
+
"defaultValue": null,
|
|
22728
|
+
"guides": []
|
|
22729
|
+
},
|
|
22538
22730
|
{
|
|
22539
22731
|
"id": "GemServer.identity",
|
|
22540
22732
|
"name": "identity",
|
|
@@ -22588,7 +22780,7 @@
|
|
|
22588
22780
|
"name": "match",
|
|
22589
22781
|
"anchor": "gemserver-match",
|
|
22590
22782
|
"group": "api",
|
|
22591
|
-
"signature": "readonly match: { end: null }",
|
|
22783
|
+
"signature": "readonly match: { end: null; setBackfill: null; reportPlayers: null }",
|
|
22592
22784
|
"documented": true,
|
|
22593
22785
|
"summary": "Ending the match.\n\n`end()` is HOW A SERVER-AUTHORITATIVE GAME FINISHES. It tells the platform\nthe match is over; the platform then tells the runtime, and your server\nwinds down through its ordinary shutdown. You do not stop the process\nyourself.\n\nIt always means THIS match — the id is already known, so there is nothing to\npass and no way to end someone else's.\n\nThe platform half makes the request, not your process: it holds the proof\nthat this server is the one allocated to the match, which the platform\nrequires. On a platform image too old to offer that, `end()` throws rather\nthan sending a request without the proof.",
|
|
22594
22786
|
"deprecated": null,
|
|
@@ -22622,6 +22814,64 @@
|
|
|
22622
22814
|
"defaultValue": null,
|
|
22623
22815
|
"guides": []
|
|
22624
22816
|
},
|
|
22817
|
+
{
|
|
22818
|
+
"id": "GemServer.match.setBackfill",
|
|
22819
|
+
"name": "setBackfill",
|
|
22820
|
+
"anchor": "gemserver-match-setbackfill",
|
|
22821
|
+
"group": "api",
|
|
22822
|
+
"signature": "setBackfill(options: { open: boolean; seats?: number }, signal?: AbortSignal): Promise<MatchBackfillState>",
|
|
22823
|
+
"documented": true,
|
|
22824
|
+
"summary": "Stop, or restart, the platform topping this match up from the\nmatchmaking queue.\n\nA match the platform matchmade from a room type with backfill turned on\ntakes waiting players into its open seats from the moment it starts.\nThis is how your server takes control of that: close it once joining\nlate no longer makes sense — the last round, a boss fight — and open it\nagain if it does.\n\nClosing removes nobody, and a player already on the way in still\narrives. The setting lasts for the match.",
|
|
22825
|
+
"deprecated": null,
|
|
22826
|
+
"params": [
|
|
22827
|
+
{
|
|
22828
|
+
"name": "options",
|
|
22829
|
+
"type": "{ open: boolean; seats?: number }",
|
|
22830
|
+
"optional": false,
|
|
22831
|
+
"summary": "`open` — whether to take players. `seats` — with\n `open: true`, the most players still wanted; it counts down as seats\n fill. Omit it for every open seat."
|
|
22832
|
+
},
|
|
22833
|
+
{
|
|
22834
|
+
"name": "signal",
|
|
22835
|
+
"type": "AbortSignal",
|
|
22836
|
+
"optional": true,
|
|
22837
|
+
"summary": "Cancels the request."
|
|
22838
|
+
}
|
|
22839
|
+
],
|
|
22840
|
+
"returns": null,
|
|
22841
|
+
"throws": "A `GemApiError` with code `backfill_not_enabled` when this match\n is not one the platform tops up: its room type has no backfill, or the\n room was not matchmade.",
|
|
22842
|
+
"example": "```js\n// Nobody new once the final round starts.\nawait gem.match.setBackfill({ open: false });\n```",
|
|
22843
|
+
"defaultValue": null,
|
|
22844
|
+
"guides": []
|
|
22845
|
+
},
|
|
22846
|
+
{
|
|
22847
|
+
"id": "GemServer.match.reportPlayers",
|
|
22848
|
+
"name": "reportPlayers",
|
|
22849
|
+
"anchor": "gemserver-match-reportplayers",
|
|
22850
|
+
"group": "api",
|
|
22851
|
+
"signature": "reportPlayers(reports: readonly MatchPlayerReport[], signal?: AbortSignal): Promise<MatchPlayerReportResult[]>",
|
|
22852
|
+
"documented": true,
|
|
22853
|
+
"summary": "Tell the platform which players have connected, never arrived, or left.\n\n`no_show` and `left` REMOVE THE PLAYER FROM THE ROOM, which frees the\nseat — for the queue, when the match is being topped up. Report `left`\nonly for a player who is gone for good: reconnecting does not put a\nremoved player back, they have to join again.\n\nYOU DO NOT REPORT `connected` YOURSELF in a bundle built with\n[defineServer](api-reference-server.md#defineserver): it reports each player as they join. That report is\nwhat gives a seat the platform filled from the queue a time limit — a\nplayer who is seated and never connects is removed, and the seat goes to\nthe next one waiting.",
|
|
22854
|
+
"deprecated": null,
|
|
22855
|
+
"params": [
|
|
22856
|
+
{
|
|
22857
|
+
"name": "reports",
|
|
22858
|
+
"type": "readonly MatchPlayerReport[]",
|
|
22859
|
+
"optional": false,
|
|
22860
|
+
"summary": "1 to 32 players."
|
|
22861
|
+
},
|
|
22862
|
+
{
|
|
22863
|
+
"name": "signal",
|
|
22864
|
+
"type": "AbortSignal",
|
|
22865
|
+
"optional": true,
|
|
22866
|
+
"summary": "Cancels the request."
|
|
22867
|
+
}
|
|
22868
|
+
],
|
|
22869
|
+
"returns": "One result per report, in the same order.",
|
|
22870
|
+
"throws": null,
|
|
22871
|
+
"example": "```js\nleave(playerId) {\n // This game does not let a player back in once they quit.\n void gem.match.reportPlayers([{ playerId, state: 'left' }]);\n}\n```",
|
|
22872
|
+
"defaultValue": null,
|
|
22873
|
+
"guides": []
|
|
22874
|
+
},
|
|
22625
22875
|
{
|
|
22626
22876
|
"id": "GemServer.analytics",
|
|
22627
22877
|
"name": "analytics",
|
|
@@ -23340,7 +23590,7 @@
|
|
|
23340
23590
|
"name": "endMatch",
|
|
23341
23591
|
"anchor": "gemserverhost-endmatch",
|
|
23342
23592
|
"group": "host",
|
|
23343
|
-
"signature": "endMatch(): Promise
|
|
23593
|
+
"signature": "endMatch(): Promise<string & object | \"ok\" | \"already_ended\" | \"refused\" | \"failed\">",
|
|
23344
23594
|
"documented": true,
|
|
23345
23595
|
"summary": "Ask the platform half to end this match, with the allocation's own proof.\n\nMAY BE ABSENT on older platform images. `gem.match.end()` is this call —\nthe request carries the allocation nonce, which never enters the bundle\nprocess — and refuses on an image that does not offer it. You rarely call\nthis yourself.\n\nResolves with what the platform half did: `ok`, `already_ended` (this\nmatch was already over, or you asked twice), `refused` (the allocation was\nnot ready yet) or `failed` (the platform could not be reached).",
|
|
23346
23596
|
"deprecated": null,
|
|
@@ -23930,7 +24180,7 @@
|
|
|
23930
24180
|
"name": "match",
|
|
23931
24181
|
"anchor": "gemmatchserver-match",
|
|
23932
24182
|
"group": "start",
|
|
23933
|
-
"signature": "readonly match: { end: null }",
|
|
24183
|
+
"signature": "readonly match: { end: null; setBackfill: null; reportPlayers: null }",
|
|
23934
24184
|
"documented": true,
|
|
23935
24185
|
"summary": "Ending the match.\n\n`end()` is HOW A SERVER-AUTHORITATIVE GAME FINISHES. It tells the platform\nthe match is over; the platform then tells the runtime, and your server\nwinds down through its ordinary shutdown. You do not stop the process\nyourself.\n\nIt always means THIS match — the id is already known, so there is nothing to\npass and no way to end someone else's.\n\nThe platform half makes the request, not your process: it holds the proof\nthat this server is the one allocated to the match, which the platform\nrequires. On a platform image too old to offer that, `end()` throws rather\nthan sending a request without the proof.",
|
|
23936
24186
|
"deprecated": null,
|
|
@@ -23964,6 +24214,64 @@
|
|
|
23964
24214
|
"defaultValue": null,
|
|
23965
24215
|
"guides": []
|
|
23966
24216
|
},
|
|
24217
|
+
{
|
|
24218
|
+
"id": "GemMatchServer.match.setBackfill",
|
|
24219
|
+
"name": "setBackfill",
|
|
24220
|
+
"anchor": "gemmatchserver-match-setbackfill",
|
|
24221
|
+
"group": "start",
|
|
24222
|
+
"signature": "setBackfill(options: { open: boolean; seats?: number }, signal?: AbortSignal): Promise<MatchBackfillState>",
|
|
24223
|
+
"documented": true,
|
|
24224
|
+
"summary": "Stop, or restart, the platform topping this match up from the\nmatchmaking queue.\n\nA match the platform matchmade from a room type with backfill turned on\ntakes waiting players into its open seats from the moment it starts.\nThis is how your server takes control of that: close it once joining\nlate no longer makes sense — the last round, a boss fight — and open it\nagain if it does.\n\nClosing removes nobody, and a player already on the way in still\narrives. The setting lasts for the match.",
|
|
24225
|
+
"deprecated": null,
|
|
24226
|
+
"params": [
|
|
24227
|
+
{
|
|
24228
|
+
"name": "options",
|
|
24229
|
+
"type": "{ open: boolean; seats?: number }",
|
|
24230
|
+
"optional": false,
|
|
24231
|
+
"summary": "`open` — whether to take players. `seats` — with\n `open: true`, the most players still wanted; it counts down as seats\n fill. Omit it for every open seat."
|
|
24232
|
+
},
|
|
24233
|
+
{
|
|
24234
|
+
"name": "signal",
|
|
24235
|
+
"type": "AbortSignal",
|
|
24236
|
+
"optional": true,
|
|
24237
|
+
"summary": "Cancels the request."
|
|
24238
|
+
}
|
|
24239
|
+
],
|
|
24240
|
+
"returns": null,
|
|
24241
|
+
"throws": "A `GemApiError` with code `backfill_not_enabled` when this match\n is not one the platform tops up: its room type has no backfill, or the\n room was not matchmade.",
|
|
24242
|
+
"example": "```js\n// Nobody new once the final round starts.\nawait gem.match.setBackfill({ open: false });\n```",
|
|
24243
|
+
"defaultValue": null,
|
|
24244
|
+
"guides": []
|
|
24245
|
+
},
|
|
24246
|
+
{
|
|
24247
|
+
"id": "GemMatchServer.match.reportPlayers",
|
|
24248
|
+
"name": "reportPlayers",
|
|
24249
|
+
"anchor": "gemmatchserver-match-reportplayers",
|
|
24250
|
+
"group": "start",
|
|
24251
|
+
"signature": "reportPlayers(reports: readonly MatchPlayerReport[], signal?: AbortSignal): Promise<MatchPlayerReportResult[]>",
|
|
24252
|
+
"documented": true,
|
|
24253
|
+
"summary": "Tell the platform which players have connected, never arrived, or left.\n\n`no_show` and `left` REMOVE THE PLAYER FROM THE ROOM, which frees the\nseat — for the queue, when the match is being topped up. Report `left`\nonly for a player who is gone for good: reconnecting does not put a\nremoved player back, they have to join again.\n\nYOU DO NOT REPORT `connected` YOURSELF in a bundle built with\n[defineServer](api-reference-server.md#defineserver): it reports each player as they join. That report is\nwhat gives a seat the platform filled from the queue a time limit — a\nplayer who is seated and never connects is removed, and the seat goes to\nthe next one waiting.",
|
|
24254
|
+
"deprecated": null,
|
|
24255
|
+
"params": [
|
|
24256
|
+
{
|
|
24257
|
+
"name": "reports",
|
|
24258
|
+
"type": "readonly MatchPlayerReport[]",
|
|
24259
|
+
"optional": false,
|
|
24260
|
+
"summary": "1 to 32 players."
|
|
24261
|
+
},
|
|
24262
|
+
{
|
|
24263
|
+
"name": "signal",
|
|
24264
|
+
"type": "AbortSignal",
|
|
24265
|
+
"optional": true,
|
|
24266
|
+
"summary": "Cancels the request."
|
|
24267
|
+
}
|
|
24268
|
+
],
|
|
24269
|
+
"returns": "One result per report, in the same order.",
|
|
24270
|
+
"throws": null,
|
|
24271
|
+
"example": "```js\nleave(playerId) {\n // This game does not let a player back in once they quit.\n void gem.match.reportPlayers([{ playerId, state: 'left' }]);\n}\n```",
|
|
24272
|
+
"defaultValue": null,
|
|
24273
|
+
"guides": []
|
|
24274
|
+
},
|
|
23967
24275
|
{
|
|
23968
24276
|
"id": "GemMatchServer.analytics",
|
|
23969
24277
|
"name": "analytics",
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,22 @@ 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
|
+
## 10.2.0
|
|
20
|
+
|
|
21
|
+
Upgrading from 10.1.1. Impact: required.
|
|
22
|
+
|
|
23
|
+
### Rebuild
|
|
24
|
+
|
|
25
|
+
- The match object on the server types now has two more methods, setBackfill and reportPlayers, besides end. Recompile your server. If you wrote your own implementation or test double of the server or match server types, add the two new methods or it will no longer type-check.
|
|
26
|
+
- The list of frame-to-parent message names, the frame message type and the capability type each gained a new member for the avatar picker. Recompile. If your code switches exhaustively over any of them, or checks that every member is handled, add a case for the new avatar picker member.
|
|
27
|
+
- The order source type gained two new values, one for test account provisioning and one for developer grants, and each progression state entry in a paginated response may now carry an optional completion threshold. Recompile. If you switch exhaustively over order sources, handle the new values.
|
|
28
|
+
|
|
29
|
+
### New capability
|
|
30
|
+
|
|
31
|
+
- Games running inside the Arcade can now read the player's current Arcade avatar from GemPlayer.avatarIconId. It is only set when the value is a valid identifier, and the Arcade sends the player again whenever the worn avatar changes, so listen for player updates rather than reading it once. Games can also ask the Arcade to open its avatar picker. Check Gem.avatarPickerAvailable first, then call Gem.openAvatarPicker. It returns false outside the Arcade, before the connection is ready, when the Arcade does not offer the picker, or when you call it too often, and it never throws or waits. Nothing answers the request directly; the outcome is an updated player.
|
|
32
|
+
- If your code hosts a game frame, you can now pass the avatar identifier when sending player information and handle the avatar picker request through the new host callback and option. Incoming picker requests have a new message type with its own validator. Without a handler, the request is refused.
|
|
33
|
+
- Dedicated game servers can now control matchmaking backfill. Call setBackfill to stop or restart the platform topping up a running match from the matchmaking queue, optionally with a seat count, and it returns the resulting backfill state. Call reportPlayers to tell the platform whether each player has connected, never showed up, or left. The last two remove the player from the room. The server SDK also reports each joining player as connected on its own, which gives a seat filled from the queue a time limit. If the platform does not offer these routes, it stops trying for that match after one refused call. The matching low-level server API operations and request and response types are available too.
|
|
34
|
+
|
|
19
35
|
## 10.1.1
|
|
20
36
|
|
|
21
37
|
Upgrading from 10.1.0. Impact: none.
|
package/docs/getting-started.md
CHANGED
package/docs/manifest.json
CHANGED
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"file": "changelog.md",
|
|
36
|
-
"sha256": "
|
|
37
|
-
"updated": "2026-10-
|
|
36
|
+
"sha256": "d7178e17c79418dc26cb685c667eb998b0d09dca6b1b2760268812c6af466151",
|
|
37
|
+
"updated": "2026-10-11T16:47:39Z"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"file": "cross-promotion.md",
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
"file": "getting-started.md",
|
|
71
|
-
"sha256": "
|
|
72
|
-
"updated": "2026-10-
|
|
71
|
+
"sha256": "5758f3a68307af4650e6af3e74cba48e7110dd9c405aefa33b4eaa1c73b3f85a",
|
|
72
|
+
"updated": "2026-10-11T16:47:39Z"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"file": "inventory-and-catalogs.md",
|
|
@@ -113,8 +113,8 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"file": "rooms-and-matches.md",
|
|
116
|
-
"sha256": "
|
|
117
|
-
"updated": "2026-10-
|
|
116
|
+
"sha256": "be22367f8dc9147e815a14ddb417788fa6614720a0df4f9f01624bf4667877d3",
|
|
117
|
+
"updated": "2026-10-11T10:17:11Z"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
"file": "saving-player-progress.md",
|
|
@@ -123,8 +123,8 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"file": "server-bundles.md",
|
|
126
|
-
"sha256": "
|
|
127
|
-
"updated": "2026-10-
|
|
126
|
+
"sha256": "a0e995c9a4f05515f39c5958918bd38bccb53e1c20bf13959f81d7694aa72ef0",
|
|
127
|
+
"updated": "2026-10-11T16:47:39Z"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
"file": "serverless-functions-js.md",
|
|
@@ -148,8 +148,8 @@
|
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"file": "what-a-game-must-do.md",
|
|
151
|
-
"sha256": "
|
|
152
|
-
"updated": "2026-10-
|
|
151
|
+
"sha256": "2ef43f70b16f0004e6b82178a2b370b84e68b87754e50a649cf78164177244f0",
|
|
152
|
+
"updated": "2026-10-11T16:21:27Z"
|
|
153
153
|
}
|
|
154
154
|
]
|
|
155
155
|
}
|
|
@@ -770,6 +770,13 @@ else backToMenu(outcome.status);
|
|
|
770
770
|
`matchmaking_not_enabled`**. `fill_window_seconds` in that block is how long a queue holds out for
|
|
771
771
|
`max_players` before it accepts `min_players`. The block cannot be combined with `auto_start_host`.
|
|
772
772
|
|
|
773
|
+
**A running match can take late arrivals.** Add `backfill: { enabled: true }` to the template's
|
|
774
|
+
`matchmaking` block (the template must also allow joining in progress) and a match the platform made
|
|
775
|
+
is topped up from the queue: a ticket that arrives after the match started is seated in it when it
|
|
776
|
+
has an open seat, instead of waiting for a whole new lobby. To the queueing player nothing is
|
|
777
|
+
different — the ticket ends `matched`, in a room whose match is already running. The match's server
|
|
778
|
+
decides how long that lasts; see [the server guide](server-bundles.md#topping-a-running-match-up-from-the-queue).
|
|
779
|
+
|
|
773
780
|
**A ticket names every mode the player accepts**, one to eight templates, and is matched under
|
|
774
781
|
whichever fills first. That is one ticket, not several: a player can be on one ticket at a time, across
|
|
775
782
|
every game.
|
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.2.0/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v10.2.0/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -769,6 +769,50 @@ publish is your code plus whatever of the SDK it reached. No API origin is baked
|
|
|
769
769
|
that arrives on `host.apiBaseUrl` at runtime. (`/server` remains as the server-only door for a
|
|
770
770
|
bundle that wants nothing of the browser half in its graph.)
|
|
771
771
|
|
|
772
|
+
### Topping a running match up from the queue
|
|
773
|
+
|
|
774
|
+
A match the platform matchmade from a room template with `matchmaking.backfill` turned on takes
|
|
775
|
+
waiting players into its open seats from the moment it starts, and again whenever a seat frees. Your
|
|
776
|
+
server does nothing to get that. Two calls let it take control:
|
|
777
|
+
|
|
778
|
+
```js
|
|
779
|
+
// Nobody new once the final round starts.
|
|
780
|
+
await gem.match.setBackfill({ open: false });
|
|
781
|
+
|
|
782
|
+
// Open again, for at most two more players.
|
|
783
|
+
await gem.match.setBackfill({ open: true, seats: 2 });
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
- **Closing removes nobody**, and a player already on the way in still arrives.
|
|
787
|
+
- **`seats` counts down** as seats fill and backfill stops at 0. Omit it for every open seat.
|
|
788
|
+
- It rejects with `GemApiError.code === 'backfill_not_enabled'` on a match the platform is not
|
|
789
|
+
topping up: the room template has no backfill, or the room was not matchmade.
|
|
790
|
+
|
|
791
|
+
```js
|
|
792
|
+
leave(playerId) {
|
|
793
|
+
// This game does not let a player back in once they quit.
|
|
794
|
+
void gem.match.reportPlayers([{ playerId, state: 'left' }]);
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
`gem.match.reportPlayers` tells the platform about players, one of three ways:
|
|
799
|
+
|
|
800
|
+
| `state` | Meaning | Effect |
|
|
801
|
+
| --- | --- | --- |
|
|
802
|
+
| `connected` | the player has connected to your server | noted |
|
|
803
|
+
| `no_show` | the player was given a seat and never connected | removed from the room; the seat is free |
|
|
804
|
+
| `left` | the player was in the match and is gone for good | removed from the room; the seat is free |
|
|
805
|
+
|
|
806
|
+
⚠️ **`left` is final.** A removed player is not put back by reconnecting; they have to join again.
|
|
807
|
+
Do not report it from every `leave()` in a game where a dropped connection is expected to return.
|
|
808
|
+
|
|
809
|
+
**You do not report `connected` yourself** in a bundle built with `defineServer`: the SDK reports
|
|
810
|
+
each player as they join. That report is what gives a seat filled from the queue a time limit. Once
|
|
811
|
+
your server has reported anyone connected, a player the platform seats afterwards has a limited
|
|
812
|
+
time to connect too, and is removed if they do not — so a seat is not held by somebody who closed
|
|
813
|
+
the tab on the way in. A bundle that drives the lifecycle by hand reports `connected` from its own
|
|
814
|
+
`join`, or gets no time limit.
|
|
815
|
+
|
|
772
816
|
### What is not on the server surface, and why
|
|
773
817
|
|
|
774
818
|
`gem.p2p`, `gem.dedicatedServer`, `gem.me`, `gem.characters` and `gem.gameConfig` are browser-only.
|