@indiegems/gem-web-sdk 7.6.0 → 7.8.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/dist/function.d.ts +20 -4
- package/dist/game.d.ts +25 -5
- 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 +4 -0
- package/dist/sdk.d.ts +61 -11
- package/dist/server-api.d.ts +33 -6
- package/dist/server.d.ts +36 -6
- package/docs/api/api-reference-game.md +370 -9
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +383 -9
- package/docs/daily-challenges.md +14 -2
- package/docs/dedicated-server-flow.md +23 -14
- package/docs/getting-started.md +1 -1
- package/docs/leaderboards.md +140 -0
- package/docs/manifest.json +14 -14
- package/docs/server-bundles.md +2 -2
- package/docs/serverless-functions.md +35 -5
- package/docs/what-a-game-must-do.md +44 -21
- package/package.json +1 -1
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.8.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -7572,6 +7572,23 @@
|
|
|
7572
7572
|
"defaultValue": null,
|
|
7573
7573
|
"guides": []
|
|
7574
7574
|
},
|
|
7575
|
+
{
|
|
7576
|
+
"id": "GemOptions.selectableText",
|
|
7577
|
+
"name": "selectableText",
|
|
7578
|
+
"anchor": "gemoptions-selectabletext",
|
|
7579
|
+
"group": "start",
|
|
7580
|
+
"url": "https://gemarcade.com/developer/api/game#gemoptions-selectabletext",
|
|
7581
|
+
"signature": "readonly selectableText?: boolean",
|
|
7582
|
+
"documented": true,
|
|
7583
|
+
"summary": "Leave your page's text selectable. Default `false`: the SDK stops\naccidental text selection, the long-press copy menu and the tap highlight\nacross your document, because in a game every one of them is a press the\nplayer did not mean.\n\nYour own CSS always wins — the SDK's rule has zero specificity — so to make\none element copyable, give it `user-select: text` (and\n`-webkit-user-select: text`). Inputs, text areas and editable content stay\ntypeable and selectable without anything from you. Set this to `true` only\nif your game is mostly text a player should be able to copy.",
|
|
7584
|
+
"deprecated": null,
|
|
7585
|
+
"params": [],
|
|
7586
|
+
"returns": null,
|
|
7587
|
+
"throws": null,
|
|
7588
|
+
"example": null,
|
|
7589
|
+
"defaultValue": null,
|
|
7590
|
+
"guides": []
|
|
7591
|
+
},
|
|
7575
7592
|
{
|
|
7576
7593
|
"id": "GemOptions.onPause",
|
|
7577
7594
|
"name": "onPause",
|
|
@@ -9399,6 +9416,40 @@
|
|
|
9399
9416
|
"defaultValue": null,
|
|
9400
9417
|
"guides": []
|
|
9401
9418
|
},
|
|
9419
|
+
{
|
|
9420
|
+
"id": "LeaderboardPeriod",
|
|
9421
|
+
"name": "LeaderboardPeriod",
|
|
9422
|
+
"anchor": "leaderboardperiod",
|
|
9423
|
+
"group": "leaderboards",
|
|
9424
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod",
|
|
9425
|
+
"signature": "interface LeaderboardPeriod",
|
|
9426
|
+
"documented": true,
|
|
9427
|
+
"summary": "Which period a `per_period` board's result is for.\n\nA board whose config sets `per_period: true` exists once per period of the\nchallenge its run type names: one board for each day of a daily challenge,\neach week of a weekly one. Present on a result exactly when the board is one\nof those; absent — not `null` — on an ordinary board.\n\n⚠️ **Players on the same date share a board, whatever their time zone.** On\na challenge that follows each player's own time zone (the default), a date\nbegins for each player at their own midnight, so one day's board is open for\nabout 50 hours worldwide and the standings can still change after YOUR day\nhas ended. [LeaderboardPeriod.final](https://gemarcade.com/developer/api/game#leaderboardperiod-final) says when they no longer can.",
|
|
9428
|
+
"deprecated": null,
|
|
9429
|
+
"params": [],
|
|
9430
|
+
"returns": null,
|
|
9431
|
+
"throws": null,
|
|
9432
|
+
"example": null,
|
|
9433
|
+
"defaultValue": null,
|
|
9434
|
+
"guides": []
|
|
9435
|
+
},
|
|
9436
|
+
{
|
|
9437
|
+
"id": "PeriodOptions",
|
|
9438
|
+
"name": "PeriodOptions",
|
|
9439
|
+
"anchor": "periodoptions",
|
|
9440
|
+
"group": "leaderboards",
|
|
9441
|
+
"url": "https://gemarcade.com/developer/api/game#periodoptions",
|
|
9442
|
+
"signature": "interface PeriodOptions",
|
|
9443
|
+
"documented": true,
|
|
9444
|
+
"summary": "Which period of a `per_period` board to read. Shared by every read.\n\nName at most one of [PeriodOptions.periodsBack](https://gemarcade.com/developer/api/game#periodoptions-periodsback) and\n[PeriodOptions.period](https://gemarcade.com/developer/api/game#periodoptions-period); with neither, the read answers for the current\nperiod. On a board that is not `per_period` both are refused with `invalid`\nrather than ignored — asking an all-time board for yesterday and being\nhanded all time would be a different answer with nothing saying so.",
|
|
9445
|
+
"deprecated": null,
|
|
9446
|
+
"params": [],
|
|
9447
|
+
"returns": null,
|
|
9448
|
+
"throws": null,
|
|
9449
|
+
"example": null,
|
|
9450
|
+
"defaultValue": null,
|
|
9451
|
+
"guides": []
|
|
9452
|
+
},
|
|
9402
9453
|
{
|
|
9403
9454
|
"id": "ShowcaseApi",
|
|
9404
9455
|
"name": "ShowcaseApi",
|
|
@@ -12997,6 +13048,125 @@
|
|
|
12997
13048
|
"defaultValue": null,
|
|
12998
13049
|
"guides": []
|
|
12999
13050
|
},
|
|
13051
|
+
{
|
|
13052
|
+
"id": "LeaderboardPeriod.id",
|
|
13053
|
+
"name": "id",
|
|
13054
|
+
"anchor": "leaderboardperiod-id",
|
|
13055
|
+
"group": "leaderboards",
|
|
13056
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod-id",
|
|
13057
|
+
"signature": "readonly id: string",
|
|
13058
|
+
"documented": true,
|
|
13059
|
+
"summary": "The period this board is for: the date for a daily challenge, the Monday\nfor a weekly, the 1st for a monthly (`2026-10-02`), or a cron challenge's\nstart as `2026-10-02T06:00:00Z`.\n\n**This is the board's identity.** Pass it back as\n[GetLeaderboardOptions.period](https://gemarcade.com/developer/api/game#getleaderboardoptions-period) to read this same board again later —\nfrom a shared link, or a history screen — and key any cache on it.",
|
|
13060
|
+
"deprecated": null,
|
|
13061
|
+
"params": [],
|
|
13062
|
+
"returns": null,
|
|
13063
|
+
"throws": null,
|
|
13064
|
+
"example": null,
|
|
13065
|
+
"defaultValue": null,
|
|
13066
|
+
"guides": []
|
|
13067
|
+
},
|
|
13068
|
+
{
|
|
13069
|
+
"id": "LeaderboardPeriod.localDate",
|
|
13070
|
+
"name": "localDate",
|
|
13071
|
+
"anchor": "leaderboardperiod-localdate",
|
|
13072
|
+
"group": "leaderboards",
|
|
13073
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod-localdate",
|
|
13074
|
+
"signature": "readonly localDate?: string",
|
|
13075
|
+
"documented": true,
|
|
13076
|
+
"summary": "The period's calendar date, `YYYY-MM-DD` — the same value\n`gem.challenges.list()` gives as `periodLocalDate` for the challenge, so\nthe two can be compared. Absent for a cron challenge, which has no date.",
|
|
13077
|
+
"deprecated": null,
|
|
13078
|
+
"params": [],
|
|
13079
|
+
"returns": null,
|
|
13080
|
+
"throws": null,
|
|
13081
|
+
"example": null,
|
|
13082
|
+
"defaultValue": null,
|
|
13083
|
+
"guides": []
|
|
13084
|
+
},
|
|
13085
|
+
{
|
|
13086
|
+
"id": "LeaderboardPeriod.startAt",
|
|
13087
|
+
"name": "startAt",
|
|
13088
|
+
"anchor": "leaderboardperiod-startat",
|
|
13089
|
+
"group": "leaderboards",
|
|
13090
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod-startat",
|
|
13091
|
+
"signature": "readonly startAt?: number",
|
|
13092
|
+
"documented": true,
|
|
13093
|
+
"summary": "When this period began for the player the read answered for, in Unix\nseconds. Absent when the read answered for no player — a server or a\nfunction that named none — on a challenge that follows each player's own\ntime zone.",
|
|
13094
|
+
"deprecated": null,
|
|
13095
|
+
"params": [],
|
|
13096
|
+
"returns": null,
|
|
13097
|
+
"throws": null,
|
|
13098
|
+
"example": null,
|
|
13099
|
+
"defaultValue": null,
|
|
13100
|
+
"guides": []
|
|
13101
|
+
},
|
|
13102
|
+
{
|
|
13103
|
+
"id": "LeaderboardPeriod.endsAt",
|
|
13104
|
+
"name": "endsAt",
|
|
13105
|
+
"anchor": "leaderboardperiod-endsat",
|
|
13106
|
+
"group": "leaderboards",
|
|
13107
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod-endsat",
|
|
13108
|
+
"signature": "readonly endsAt?: number",
|
|
13109
|
+
"documented": true,
|
|
13110
|
+
"summary": "When this period ends for that player, in Unix seconds: their own runs stop\ncounting towards this board then.\n\n⚠️ **Not when the board stops changing.** Players in later time zones are\nstill on this date after it; see [LeaderboardPeriod.closesAt](https://gemarcade.com/developer/api/game#leaderboardperiod-closesat) and\n[LeaderboardPeriod.final](https://gemarcade.com/developer/api/game#leaderboardperiod-final). Absent exactly when\n[LeaderboardPeriod.startAt](https://gemarcade.com/developer/api/game#leaderboardperiod-startat) is.",
|
|
13111
|
+
"deprecated": null,
|
|
13112
|
+
"params": [],
|
|
13113
|
+
"returns": null,
|
|
13114
|
+
"throws": null,
|
|
13115
|
+
"example": null,
|
|
13116
|
+
"defaultValue": null,
|
|
13117
|
+
"guides": []
|
|
13118
|
+
},
|
|
13119
|
+
{
|
|
13120
|
+
"id": "LeaderboardPeriod.opensAt",
|
|
13121
|
+
"name": "opensAt",
|
|
13122
|
+
"anchor": "leaderboardperiod-opensat",
|
|
13123
|
+
"group": "leaderboards",
|
|
13124
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod-opensat",
|
|
13125
|
+
"signature": "readonly opensAt: number",
|
|
13126
|
+
"documented": true,
|
|
13127
|
+
"summary": "When this board started accepting runs from anyone, in Unix seconds. `0`\nwhen the platform sent something this SDK could not read.",
|
|
13128
|
+
"deprecated": null,
|
|
13129
|
+
"params": [],
|
|
13130
|
+
"returns": null,
|
|
13131
|
+
"throws": null,
|
|
13132
|
+
"example": null,
|
|
13133
|
+
"defaultValue": null,
|
|
13134
|
+
"guides": []
|
|
13135
|
+
},
|
|
13136
|
+
{
|
|
13137
|
+
"id": "LeaderboardPeriod.closesAt",
|
|
13138
|
+
"name": "closesAt",
|
|
13139
|
+
"anchor": "leaderboardperiod-closesat",
|
|
13140
|
+
"group": "leaderboards",
|
|
13141
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod-closesat",
|
|
13142
|
+
"signature": "readonly closesAt: number",
|
|
13143
|
+
"documented": true,
|
|
13144
|
+
"summary": "When this board stops accepting runs from anyone, in Unix seconds. `0`\nwhen the platform sent something this SDK could not read.",
|
|
13145
|
+
"deprecated": null,
|
|
13146
|
+
"params": [],
|
|
13147
|
+
"returns": null,
|
|
13148
|
+
"throws": null,
|
|
13149
|
+
"example": null,
|
|
13150
|
+
"defaultValue": null,
|
|
13151
|
+
"guides": []
|
|
13152
|
+
},
|
|
13153
|
+
{
|
|
13154
|
+
"id": "LeaderboardPeriod.final",
|
|
13155
|
+
"name": "final",
|
|
13156
|
+
"anchor": "leaderboardperiod-final",
|
|
13157
|
+
"group": "leaderboards",
|
|
13158
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboardperiod-final",
|
|
13159
|
+
"signature": "readonly final: boolean",
|
|
13160
|
+
"documented": true,
|
|
13161
|
+
"summary": "`true` once no run can still arrive, so the standings will not change\nagain — stop polling, and say \"final\" if your screen says anything.\n\nWhile it is `false` the standings may change, **including after the\nviewer's own period has ended**: a \"you finished 3rd today\" shown at the\nplayer's midnight can be wrong an hour later. Say \"so far\" until this is\n`true`.",
|
|
13162
|
+
"deprecated": null,
|
|
13163
|
+
"params": [],
|
|
13164
|
+
"returns": null,
|
|
13165
|
+
"throws": null,
|
|
13166
|
+
"example": null,
|
|
13167
|
+
"defaultValue": null,
|
|
13168
|
+
"guides": []
|
|
13169
|
+
},
|
|
13000
13170
|
{
|
|
13001
13171
|
"id": "LeaderboardEntry.playerId",
|
|
13002
13172
|
"name": "playerId",
|
|
@@ -13141,7 +13311,7 @@
|
|
|
13141
13311
|
"url": "https://gemarcade.com/developer/api/game#leaderboardentry-occurredat",
|
|
13142
13312
|
"signature": "readonly occurredAt: number",
|
|
13143
13313
|
"documented": true,
|
|
13144
|
-
"summary": "When the ranked run occurred — Unix seconds.",
|
|
13314
|
+
"summary": "When the ranked run occurred — Unix seconds.\n\n⚠️ **`0` on a `per_period` board whose challenge follows each player's own\ntime zone** (the default `local` boundary). The platform serves no time\nthere: beside the board's date, the time of a run would show which time\nzones its player could be in. Render no time for `0` rather than 1970.\nRanks are unaffected — ties are still broken by the real time.",
|
|
13145
13315
|
"deprecated": null,
|
|
13146
13316
|
"params": [],
|
|
13147
13317
|
"returns": null,
|
|
@@ -13354,6 +13524,23 @@
|
|
|
13354
13524
|
"defaultValue": null,
|
|
13355
13525
|
"guides": []
|
|
13356
13526
|
},
|
|
13527
|
+
{
|
|
13528
|
+
"id": "Leaderboard.period",
|
|
13529
|
+
"name": "period",
|
|
13530
|
+
"anchor": "leaderboard-period",
|
|
13531
|
+
"group": "leaderboards",
|
|
13532
|
+
"url": "https://gemarcade.com/developer/api/game#leaderboard-period",
|
|
13533
|
+
"signature": "readonly period?: LeaderboardPeriod",
|
|
13534
|
+
"documented": true,
|
|
13535
|
+
"summary": "Which period this result is for, on a board whose config sets\n`per_period`. Absent on an ordinary board, which ranks every run.\n\nOnce `period.final` is `true` the board no longer changes and need not be\npolled.",
|
|
13536
|
+
"deprecated": null,
|
|
13537
|
+
"params": [],
|
|
13538
|
+
"returns": null,
|
|
13539
|
+
"throws": null,
|
|
13540
|
+
"example": null,
|
|
13541
|
+
"defaultValue": null,
|
|
13542
|
+
"guides": []
|
|
13543
|
+
},
|
|
13357
13544
|
{
|
|
13358
13545
|
"id": "RankedRow.rank",
|
|
13359
13546
|
"name": "rank",
|
|
@@ -13379,7 +13566,7 @@
|
|
|
13379
13566
|
"url": "https://gemarcade.com/developer/api/game#rankedrow-occurredat",
|
|
13380
13567
|
"signature": "readonly occurredAt: number",
|
|
13381
13568
|
"documented": true,
|
|
13382
|
-
"summary": "When the ranked run occurred — Unix seconds.",
|
|
13569
|
+
"summary": "When the ranked run occurred — Unix seconds. `0` on a `per_period` board\nwhose challenge follows each player's own time zone, where the platform\nserves no time; render nothing for it.",
|
|
13383
13570
|
"deprecated": null,
|
|
13384
13571
|
"params": [],
|
|
13385
13572
|
"returns": null,
|
|
@@ -13430,7 +13617,7 @@
|
|
|
13430
13617
|
"url": "https://gemarcade.com/developer/api/game#entityleaderboardentry-occurredat",
|
|
13431
13618
|
"signature": "readonly occurredAt: number",
|
|
13432
13619
|
"documented": true,
|
|
13433
|
-
"summary": "When the ranked run occurred — Unix seconds.",
|
|
13620
|
+
"summary": "When the ranked run occurred — Unix seconds. `0` on a `per_period` board\nwhose challenge follows each player's own time zone, where the platform\nserves no time; render nothing for it.",
|
|
13434
13621
|
"deprecated": null,
|
|
13435
13622
|
"params": [],
|
|
13436
13623
|
"returns": null,
|
|
@@ -13677,6 +13864,125 @@
|
|
|
13677
13864
|
"defaultValue": null,
|
|
13678
13865
|
"guides": []
|
|
13679
13866
|
},
|
|
13867
|
+
{
|
|
13868
|
+
"id": "EntityLeaderboard.period",
|
|
13869
|
+
"name": "period",
|
|
13870
|
+
"anchor": "entityleaderboard-period",
|
|
13871
|
+
"group": "leaderboards",
|
|
13872
|
+
"url": "https://gemarcade.com/developer/api/game#entityleaderboard-period",
|
|
13873
|
+
"signature": "readonly period?: LeaderboardPeriod",
|
|
13874
|
+
"documented": true,
|
|
13875
|
+
"summary": "Which period these runs are from, on a board whose config sets\n`per_period`: `entries`, `personalBest` and `totalRuns`\nare then that ONE period's, not the entity's whole history. Absent on an\nordinary board.",
|
|
13876
|
+
"deprecated": null,
|
|
13877
|
+
"params": [],
|
|
13878
|
+
"returns": null,
|
|
13879
|
+
"throws": null,
|
|
13880
|
+
"example": null,
|
|
13881
|
+
"defaultValue": null,
|
|
13882
|
+
"guides": []
|
|
13883
|
+
},
|
|
13884
|
+
{
|
|
13885
|
+
"id": "PeriodOptions.periodsBack",
|
|
13886
|
+
"name": "periodsBack",
|
|
13887
|
+
"anchor": "periodoptions-periodsback",
|
|
13888
|
+
"group": "leaderboards",
|
|
13889
|
+
"url": "https://gemarcade.com/developer/api/game#periodoptions-periodsback",
|
|
13890
|
+
"signature": "readonly periodsBack?: number",
|
|
13891
|
+
"documented": true,
|
|
13892
|
+
"summary": "How many periods back to read. `0` — the default — is the current period;\n`1` is yesterday's board for a daily challenge, last week's for a weekly.\n\nAn integer from 0 to 400. Anything else is a `TypeError` before the\nrequest is made.",
|
|
13893
|
+
"deprecated": null,
|
|
13894
|
+
"params": [],
|
|
13895
|
+
"returns": null,
|
|
13896
|
+
"throws": null,
|
|
13897
|
+
"example": null,
|
|
13898
|
+
"defaultValue": null,
|
|
13899
|
+
"guides": []
|
|
13900
|
+
},
|
|
13901
|
+
{
|
|
13902
|
+
"id": "PeriodOptions.period",
|
|
13903
|
+
"name": "period",
|
|
13904
|
+
"anchor": "periodoptions-period",
|
|
13905
|
+
"group": "leaderboards",
|
|
13906
|
+
"url": "https://gemarcade.com/developer/api/game#periodoptions-period",
|
|
13907
|
+
"signature": "readonly period?: string",
|
|
13908
|
+
"documented": true,
|
|
13909
|
+
"summary": "The period to read, as an earlier result's [LeaderboardPeriod.id](https://gemarcade.com/developer/api/game#leaderboardperiod-id) —\nfor a board reached from a shared link or a history screen. At most 400\nperiods back.\n\n⚠️ A period that has not begun for the player is refused, even when it has\nalready begun for players in time zones ahead of theirs.",
|
|
13910
|
+
"deprecated": null,
|
|
13911
|
+
"params": [],
|
|
13912
|
+
"returns": null,
|
|
13913
|
+
"throws": null,
|
|
13914
|
+
"example": null,
|
|
13915
|
+
"defaultValue": null,
|
|
13916
|
+
"guides": []
|
|
13917
|
+
},
|
|
13918
|
+
{
|
|
13919
|
+
"id": "PeriodOptions.playerId",
|
|
13920
|
+
"name": "playerId",
|
|
13921
|
+
"anchor": "periodoptions-playerid",
|
|
13922
|
+
"group": "leaderboards",
|
|
13923
|
+
"url": "https://gemarcade.com/developer/api/game#periodoptions-playerid",
|
|
13924
|
+
"signature": "readonly playerId?: string",
|
|
13925
|
+
"documented": true,
|
|
13926
|
+
"summary": "For a serverless function or a dedicated server: the player whose period\nto answer for. Their credential names no player, so without this a\n`per_period` board on each player's own time zone is answered in UTC,\nwhich is not that player's day.\n\nIgnored in the browser, where the read is always for the signed-in player.",
|
|
13927
|
+
"deprecated": null,
|
|
13928
|
+
"params": [],
|
|
13929
|
+
"returns": null,
|
|
13930
|
+
"throws": null,
|
|
13931
|
+
"example": null,
|
|
13932
|
+
"defaultValue": null,
|
|
13933
|
+
"guides": []
|
|
13934
|
+
},
|
|
13935
|
+
{
|
|
13936
|
+
"id": "EntityLeaderboardOptions.periodsBack",
|
|
13937
|
+
"name": "periodsBack",
|
|
13938
|
+
"anchor": "entityleaderboardoptions-periodsback",
|
|
13939
|
+
"group": "leaderboards",
|
|
13940
|
+
"url": "https://gemarcade.com/developer/api/game#entityleaderboardoptions-periodsback",
|
|
13941
|
+
"signature": "readonly periodsBack?: number",
|
|
13942
|
+
"documented": true,
|
|
13943
|
+
"summary": "How many periods back to read. `0` — the default — is the current period;\n`1` is yesterday's board for a daily challenge, last week's for a weekly.\n\nAn integer from 0 to 400. Anything else is a `TypeError` before the\nrequest is made.",
|
|
13944
|
+
"deprecated": null,
|
|
13945
|
+
"params": [],
|
|
13946
|
+
"returns": null,
|
|
13947
|
+
"throws": null,
|
|
13948
|
+
"example": null,
|
|
13949
|
+
"defaultValue": null,
|
|
13950
|
+
"guides": []
|
|
13951
|
+
},
|
|
13952
|
+
{
|
|
13953
|
+
"id": "EntityLeaderboardOptions.period",
|
|
13954
|
+
"name": "period",
|
|
13955
|
+
"anchor": "entityleaderboardoptions-period",
|
|
13956
|
+
"group": "leaderboards",
|
|
13957
|
+
"url": "https://gemarcade.com/developer/api/game#entityleaderboardoptions-period",
|
|
13958
|
+
"signature": "readonly period?: string",
|
|
13959
|
+
"documented": true,
|
|
13960
|
+
"summary": "The period to read, as an earlier result's [LeaderboardPeriod.id](https://gemarcade.com/developer/api/game#leaderboardperiod-id) —\nfor a board reached from a shared link or a history screen. At most 400\nperiods back.\n\n⚠️ A period that has not begun for the player is refused, even when it has\nalready begun for players in time zones ahead of theirs.",
|
|
13961
|
+
"deprecated": null,
|
|
13962
|
+
"params": [],
|
|
13963
|
+
"returns": null,
|
|
13964
|
+
"throws": null,
|
|
13965
|
+
"example": null,
|
|
13966
|
+
"defaultValue": null,
|
|
13967
|
+
"guides": []
|
|
13968
|
+
},
|
|
13969
|
+
{
|
|
13970
|
+
"id": "EntityLeaderboardOptions.playerId",
|
|
13971
|
+
"name": "playerId",
|
|
13972
|
+
"anchor": "entityleaderboardoptions-playerid",
|
|
13973
|
+
"group": "leaderboards",
|
|
13974
|
+
"url": "https://gemarcade.com/developer/api/game#entityleaderboardoptions-playerid",
|
|
13975
|
+
"signature": "readonly playerId?: string",
|
|
13976
|
+
"documented": true,
|
|
13977
|
+
"summary": "For a serverless function or a dedicated server: the player whose period\nto answer for. Their credential names no player, so without this a\n`per_period` board on each player's own time zone is answered in UTC,\nwhich is not that player's day.\n\nIgnored in the browser, where the read is always for the signed-in player.",
|
|
13978
|
+
"deprecated": null,
|
|
13979
|
+
"params": [],
|
|
13980
|
+
"returns": null,
|
|
13981
|
+
"throws": null,
|
|
13982
|
+
"example": null,
|
|
13983
|
+
"defaultValue": null,
|
|
13984
|
+
"guides": []
|
|
13985
|
+
},
|
|
13680
13986
|
{
|
|
13681
13987
|
"id": "EntityLeaderboardOptions.bucket1",
|
|
13682
13988
|
"name": "bucket1",
|
|
@@ -13745,6 +14051,57 @@
|
|
|
13745
14051
|
"defaultValue": null,
|
|
13746
14052
|
"guides": []
|
|
13747
14053
|
},
|
|
14054
|
+
{
|
|
14055
|
+
"id": "GetLeaderboardOptions.periodsBack",
|
|
14056
|
+
"name": "periodsBack",
|
|
14057
|
+
"anchor": "getleaderboardoptions-periodsback",
|
|
14058
|
+
"group": "leaderboards",
|
|
14059
|
+
"url": "https://gemarcade.com/developer/api/game#getleaderboardoptions-periodsback",
|
|
14060
|
+
"signature": "readonly periodsBack?: number",
|
|
14061
|
+
"documented": true,
|
|
14062
|
+
"summary": "How many periods back to read. `0` — the default — is the current period;\n`1` is yesterday's board for a daily challenge, last week's for a weekly.\n\nAn integer from 0 to 400. Anything else is a `TypeError` before the\nrequest is made.",
|
|
14063
|
+
"deprecated": null,
|
|
14064
|
+
"params": [],
|
|
14065
|
+
"returns": null,
|
|
14066
|
+
"throws": null,
|
|
14067
|
+
"example": null,
|
|
14068
|
+
"defaultValue": null,
|
|
14069
|
+
"guides": []
|
|
14070
|
+
},
|
|
14071
|
+
{
|
|
14072
|
+
"id": "GetLeaderboardOptions.period",
|
|
14073
|
+
"name": "period",
|
|
14074
|
+
"anchor": "getleaderboardoptions-period",
|
|
14075
|
+
"group": "leaderboards",
|
|
14076
|
+
"url": "https://gemarcade.com/developer/api/game#getleaderboardoptions-period",
|
|
14077
|
+
"signature": "readonly period?: string",
|
|
14078
|
+
"documented": true,
|
|
14079
|
+
"summary": "The period to read, as an earlier result's [LeaderboardPeriod.id](https://gemarcade.com/developer/api/game#leaderboardperiod-id) —\nfor a board reached from a shared link or a history screen. At most 400\nperiods back.\n\n⚠️ A period that has not begun for the player is refused, even when it has\nalready begun for players in time zones ahead of theirs.",
|
|
14080
|
+
"deprecated": null,
|
|
14081
|
+
"params": [],
|
|
14082
|
+
"returns": null,
|
|
14083
|
+
"throws": null,
|
|
14084
|
+
"example": null,
|
|
14085
|
+
"defaultValue": null,
|
|
14086
|
+
"guides": []
|
|
14087
|
+
},
|
|
14088
|
+
{
|
|
14089
|
+
"id": "GetLeaderboardOptions.playerId",
|
|
14090
|
+
"name": "playerId",
|
|
14091
|
+
"anchor": "getleaderboardoptions-playerid",
|
|
14092
|
+
"group": "leaderboards",
|
|
14093
|
+
"url": "https://gemarcade.com/developer/api/game#getleaderboardoptions-playerid",
|
|
14094
|
+
"signature": "readonly playerId?: string",
|
|
14095
|
+
"documented": true,
|
|
14096
|
+
"summary": "For a serverless function or a dedicated server: the player whose period\nto answer for. Their credential names no player, so without this a\n`per_period` board on each player's own time zone is answered in UTC,\nwhich is not that player's day.\n\nIgnored in the browser, where the read is always for the signed-in player.",
|
|
14097
|
+
"deprecated": null,
|
|
14098
|
+
"params": [],
|
|
14099
|
+
"returns": null,
|
|
14100
|
+
"throws": null,
|
|
14101
|
+
"example": null,
|
|
14102
|
+
"defaultValue": null,
|
|
14103
|
+
"guides": []
|
|
14104
|
+
},
|
|
13748
14105
|
{
|
|
13749
14106
|
"id": "GetLeaderboardOptions.bucket1",
|
|
13750
14107
|
"name": "bucket1",
|
|
@@ -13804,7 +14161,7 @@
|
|
|
13804
14161
|
"url": "https://gemarcade.com/developer/api/game#leaderboardsapi-get",
|
|
13805
14162
|
"signature": "get(leaderboardType: string, options?: GetLeaderboardOptions): Promise<Leaderboard>",
|
|
13806
14163
|
"documented": true,
|
|
13807
|
-
"summary": "Read one board, whole.\n\nA bucketed board (config `bucket_by`) needs exactly its bucket values —\n`get('weekly', { bucket1: 'na' })` — and refuses a mismatch. Poll no\nfaster than the result's `updateFrequencySeconds`; the server regenerates\nat that cadence and faster polling re-reads the same rendering.",
|
|
14164
|
+
"summary": "Read one board, whole.\n\nA bucketed board (config `bucket_by`) needs exactly its bucket values —\n`get('weekly', { bucket1: 'na' })` — and refuses a mismatch. Poll no\nfaster than the result's `updateFrequencySeconds`; the server regenerates\nat that cadence and faster polling re-reads the same rendering.\n\nA `per_period` board answers for one period — the current one by default:\n\n```js\nconst today = await gem.leaderboards.get('daily');\nconst yesterday = await gem.leaderboards.get('daily', { periodsBack: 1 });\nlabel(today.period.final ? 'Final standings' : 'Standings so far');\n```",
|
|
13808
14165
|
"deprecated": null,
|
|
13809
14166
|
"params": [
|
|
13810
14167
|
{
|
|
@@ -13821,7 +14178,7 @@
|
|
|
13821
14178
|
}
|
|
13822
14179
|
],
|
|
13823
14180
|
"returns": null,
|
|
13824
|
-
"throws": "`not_found` when no board with this id is in the channel's active\n config, `invalid` when bucket values do not match the board's\n `bucket_by
|
|
14181
|
+
"throws": "`TypeError` when `periodsBack` is not an integer from 0 to 400,\n `period` is not a period id, or both are passed — refused before the\n request.\n\n`not_found` when no board with this id is in the channel's active\n config, `invalid` when bucket values do not match the board's\n `bucket_by`, when a period is asked of a board that is not `per_period`\n (`code` `invalid_period`), or when the period has not begun for the\n player (`code` `period_not_begun`).",
|
|
13825
14182
|
"example": null,
|
|
13826
14183
|
"defaultValue": null,
|
|
13827
14184
|
"guides": []
|
|
@@ -13834,7 +14191,7 @@
|
|
|
13834
14191
|
"url": "https://gemarcade.com/developer/api/game#leaderboardsapi-friends",
|
|
13835
14192
|
"signature": "friends(leaderboardType: string, options?: GetLeaderboardOptions): Promise<Leaderboard>",
|
|
13836
14193
|
"documented": true,
|
|
13837
|
-
"summary": "The same board, ranked over you and your accepted friends only.\n\nSame run type, ordering, buckets and `maxSize` as `get`, and the\nsame cached cadence — but ranks are positions **within this restricted\nboard**, not global ones. Your own rows carry\n[LeaderboardEntry.isViewer](https://gemarcade.com/developer/api/game#leaderboardentry-isviewer), which is what lets a standings screen\nmark \"you\"; on `get` that field is absent rather than false, because\na global board does not answer the question at all.\n\nA player with no friends gets a board of one — their own rows — not an\nerror. Character-owned runs are never on a friend board: friendship is a\nplayer-to-player relation.\n\n```js\nconst board = await gem.leaderboards.friends('time-trial');\nfor (const row of board.entries) {\n const name = row.displayTag ? text.toPlain(row.displayTag) : 'Anonymous player';\n render(row.rank, name, { you: row.isViewer });\n}\n
|
|
14194
|
+
"summary": "The same board, ranked over you and your accepted friends only.\n\nSame run type, ordering, buckets and `maxSize` as `get`, and the\nsame cached cadence — but ranks are positions **within this restricted\nboard**, not global ones. Your own rows carry\n[LeaderboardEntry.isViewer](https://gemarcade.com/developer/api/game#leaderboardentry-isviewer), which is what lets a standings screen\nmark \"you\"; on `get` that field is absent rather than false, because\na global board does not answer the question at all.\n\nA player with no friends gets a board of one — their own rows — not an\nerror. Character-owned runs are never on a friend board: friendship is a\nplayer-to-player relation.\n\n```js\nconst board = await gem.leaderboards.friends('time-trial');\nfor (const row of board.entries) {\n const name = row.displayTag ? text.toPlain(row.displayTag) : 'Anonymous player';\n render(row.rank, name, { you: row.isViewer });\n}\n```\n\nOn a `per_period` board this takes the same `periodsBack` and `period` as\n`get`. A friend in another time zone who is on the same date is on\nthe same board.",
|
|
13838
14195
|
"deprecated": null,
|
|
13839
14196
|
"params": [
|
|
13840
14197
|
{
|
|
@@ -13851,7 +14208,7 @@
|
|
|
13851
14208
|
}
|
|
13852
14209
|
],
|
|
13853
14210
|
"returns": null,
|
|
13854
|
-
"throws": "`not_found` when no board with this id is in the channel's active\n config, `invalid` when bucket values do not match the board
|
|
14211
|
+
"throws": "`not_found` when no board with this id is in the channel's active\n config, `invalid` when bucket values or a period do not match the board\n (as on `get`), and `forbidden` in a context with no player — the\n friend view is a player surface, and a game server has no friends. (The\n global `get` declares no such refusal: it needs no viewer.)",
|
|
13855
14212
|
"example": null,
|
|
13856
14213
|
"defaultValue": null,
|
|
13857
14214
|
"guides": []
|
|
@@ -13864,7 +14221,7 @@
|
|
|
13864
14221
|
"url": "https://gemarcade.com/developer/api/game#leaderboardsapi-mine",
|
|
13865
14222
|
"signature": "mine(leaderboardType: string, options?: EntityLeaderboardOptions): Promise<EntityLeaderboard>",
|
|
13866
14223
|
"documented": true,
|
|
13867
|
-
"summary": "Your own runs on a board, best first, with your personal best.\n\nThis is the read for \"New personal best!\" and for a run-history panel. It\nis **not** a slice of the global board: `rank` is your position among your\nown runs, so `rank: 1` is your best — which is the number most games\nactually want and the one the global board cannot give you unless you\nhappen to be on it.\n\n```js\nconst mine = await gem.leaderboards.mine('time-trial');\nif (mine.personalBest) show(mine.personalBest.stats?.finish_ms);\n```\n\n**When to call it.** Not on a timer — there is no cadence to poll at, and\ndeliberately no `updateFrequencySeconds` on the result. Call it once a run\nhas been reported. ⚠️ Reporting a run is your dedicated server's job, or\nyour serverless function's, and your client has no happens-after edge with\na server: a read fired the instant a match ends can legitimately miss the\nrun just played. Read it when your server has told your client the run\nlanded, or once the function that recorded it has answered.\n\nAn empty result is the ordinary state of a player who has not played yet —\na `200` with no entries, not an error. If it is *always* empty, see\n[LeaderboardsApi](https://gemarcade.com/developer/api/game#leaderboardsapi) on where runs come from: a browser cannot submit\none.",
|
|
14224
|
+
"summary": "Your own runs on a board, best first, with your personal best.\n\nThis is the read for \"New personal best!\" and for a run-history panel. It\nis **not** a slice of the global board: `rank` is your position among your\nown runs, so `rank: 1` is your best — which is the number most games\nactually want and the one the global board cannot give you unless you\nhappen to be on it.\n\n```js\nconst mine = await gem.leaderboards.mine('time-trial');\nif (mine.personalBest) show(mine.personalBest.stats?.finish_ms);\n```\n\n**When to call it.** Not on a timer — there is no cadence to poll at, and\ndeliberately no `updateFrequencySeconds` on the result. Call it once a run\nhas been reported. ⚠️ Reporting a run is your dedicated server's job, or\nyour serverless function's, and your client has no happens-after edge with\na server: a read fired the instant a match ends can legitimately miss the\nrun just played. Read it when your server has told your client the run\nlanded, or once the function that recorded it has answered.\n\nAn empty result is the ordinary state of a player who has not played yet —\na `200` with no entries, not an error. If it is *always* empty, see\n[LeaderboardsApi](https://gemarcade.com/developer/api/game#leaderboardsapi) on where runs come from: a browser cannot submit\none.\n\nOn a `per_period` board the runs, the personal best and the total are ONE\nperiod's — the current one unless `periodsBack` or `period` names another\n— so `personalBest` is \"your best today\", and an empty result at the start\nof a day is the ordinary state.",
|
|
13868
14225
|
"deprecated": null,
|
|
13869
14226
|
"params": [
|
|
13870
14227
|
{
|
|
@@ -17900,6 +18257,23 @@
|
|
|
17900
18257
|
"defaultValue": null,
|
|
17901
18258
|
"guides": []
|
|
17902
18259
|
},
|
|
18260
|
+
{
|
|
18261
|
+
"id": "Room.selfHosted",
|
|
18262
|
+
"name": "selfHosted",
|
|
18263
|
+
"anchor": "room-selfhosted",
|
|
18264
|
+
"group": "rooms",
|
|
18265
|
+
"url": "https://gemarcade.com/developer/api/game#room-selfhosted",
|
|
18266
|
+
"signature": "readonly selfHosted: boolean",
|
|
18267
|
+
"documented": true,
|
|
18268
|
+
"summary": "Whether this room's matches are served from the developer's own machine\nrather than by the platform.\n\nTrue only inside a development tool that runs your game's server locally\n(the Gem Editor's play surface). The SDK asks for such a room on your\nbehalf — there is no option for a game to set — and the platform keeps it\nto its host: no other player can join it, quick-join never matches into\nit, and the server browser never lists it. False on every room a player in\nthe Arcade is in.",
|
|
18269
|
+
"deprecated": null,
|
|
18270
|
+
"params": [],
|
|
18271
|
+
"returns": null,
|
|
18272
|
+
"throws": null,
|
|
18273
|
+
"example": null,
|
|
18274
|
+
"defaultValue": null,
|
|
18275
|
+
"guides": []
|
|
18276
|
+
},
|
|
17903
18277
|
{
|
|
17904
18278
|
"id": "Room.matchId",
|
|
17905
18279
|
"name": "matchId",
|
package/docs/daily-challenges.md
CHANGED
|
@@ -64,8 +64,10 @@ reset_schedule: { type: daily, boundary: UTC, reset_time: '04:00' }
|
|
|
64
64
|
|
|
65
65
|
- **`boundary`** — `local`, the default, is each player's own time zone. An IANA zone name such as
|
|
66
66
|
`UTC` or `Asia/Tokyo` puts every player on that one calendar instead, so everybody's day turns
|
|
67
|
-
over at the same instant: what a daily leaderboard, or a server that works out
|
|
68
|
-
itself, needs.
|
|
67
|
+
over at the same instant: what a strictly fair daily leaderboard, or a server that works out
|
|
68
|
+
"today" for itself, needs. A daily leaderboard works on `local` too, with players on the same
|
|
69
|
+
date sharing a board — [section 10](#10-what-your-game-can-read-back) has the read, and
|
|
70
|
+
[leaderboards.md](leaderboards.md#one-day-many-time-zones) what the choice costs.
|
|
69
71
|
- **`reset_time`** — the clock time, `HH:MM` in that zone, at which a period begins. The default is
|
|
70
72
|
`00:00`; `'04:00'` lets a late-night player finish the day they started. Quote it: some YAML
|
|
71
73
|
parsers read an unquoted `04:00` as a number.
|
|
@@ -563,6 +565,16 @@ server reads the same list for one player with `gem.challenges.list({ playerId }
|
|
|
563
565
|
**How the player is doing in THIS period — yes**, through the ordinary progression read
|
|
564
566
|
(section 3): `currentValue` and `isCompleted` are this period's, and reset with it.
|
|
565
567
|
|
|
568
|
+
**How everybody did in THIS period — yes, if you declare a board for it.** A leaderboard can be
|
|
569
|
+
scoped to this challenge's periods: today's standings, which start empty each day and close when
|
|
570
|
+
the day does. Name the challenge on the board's run type and set `per_period` on the board; the
|
|
571
|
+
board then takes this challenge's schedule, boundary and reset time, and
|
|
572
|
+
`gem.leaderboards.get('daily')` answers for the player's current period. It needs no
|
|
573
|
+
`platform_display` and no stored content. When your function or server records the run, send the
|
|
574
|
+
`periodLocalDate` you read here as the run's `period_id`, so a run finished just before midnight
|
|
575
|
+
counts for the day it was played. [A board for each day](leaderboards.md#a-board-for-each-day-period-boards)
|
|
576
|
+
is the page.
|
|
577
|
+
|
|
566
578
|
**Their per-period history and their streak — no.** There is no history read and no streak read in
|
|
567
579
|
the SDK. The arcade is what displays those — the marker on your game's card, and the player's own
|
|
568
580
|
progress, history and streak on platform surfaces. Your game does not have to render any of it.
|
|
@@ -849,20 +849,29 @@ the same thing and a regression on either would be silent.
|
|
|
849
849
|
Inside a development tool that can host — it advertises the `localHost` capability, which no
|
|
850
850
|
player-facing embedder carries — the platform can dispatch a dedicated match to a server running
|
|
851
851
|
on the developer's own machine instead of allocating one from the fleet. **Your game's source does
|
|
852
|
-
not participate.**
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
852
|
+
not participate.** Every room the SDK creates there — by `create()` or `quickJoin()` — is a
|
|
853
|
+
*self-hosted* room (`room.selfHosted` is `true`), and the moment the SDK holds one it asks the
|
|
854
|
+
embedder for a server for it; the tool spawns your game's own server bundle and registers it with
|
|
855
|
+
the platform. A self-hosted room's match is dispatched to that server and never to the fleet: a
|
|
856
|
+
template with `auto_start_host` waits for the registration and then starts by itself, and a start
|
|
857
|
+
your game makes waits for the server first. A game published to players never runs inside such
|
|
858
|
+
an embedder, so there is nothing to gate and nothing to remove before shipping.
|
|
859
|
+
|
|
860
|
+
Four things are worth knowing while you develop this way:
|
|
861
|
+
|
|
862
|
+
- **A self-hosted room is yours alone.** Nobody else can join it, `quickJoin()` makes a fresh one
|
|
863
|
+
rather than joining another player's room, and the server browser never lists it. The build you
|
|
864
|
+
are running does not need to be published on the channel: the platform skips its published-build
|
|
865
|
+
check for these rooms, though a blacklisted build is still refused. A room created with a party
|
|
866
|
+
is an ordinary room.
|
|
867
|
+
|
|
868
|
+
- **A refusal arrives as `startMatch()` rejecting — never as a hang.** The two failure shapes are
|
|
869
|
+
deliberately different. A platform refusal (you are not signed in as a developer on this game,
|
|
870
|
+
or the room is not on a development channel) is terminal, and retrying sends the same request
|
|
871
|
+
to the same refusal. A machine that merely could not start the process is retryable, and your
|
|
872
|
+
tool has the log. Silence is bounded too: an embedder that never answers is given thirty
|
|
873
|
+
seconds, then the rejection says so. However many times a racing Start button asks, one server
|
|
874
|
+
is started per room.
|
|
866
875
|
|
|
867
876
|
- **The winning transport is usually WebSocket, so take the negotiated path.** A locally run server
|
|
868
877
|
typically offers no WebTransport certificate chain and no WebRTC signaling; the ladder works
|