@indiegems/gem-web-sdk 23.2.0 → 24.0.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 +48 -0
- package/dist/conformance.d.ts +5 -2
- package/dist/function.d.ts +22 -1
- package/dist/game.d.ts +1590 -1466
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +3 -3
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +8 -1
- package/dist/sdk.d.ts +1600 -1464
- package/dist/server.d.ts +44 -1
- package/docs/analytics.md +40 -0
- package/docs/api/api-reference-function.md +196 -45
- package/docs/api/api-reference-game.md +778 -33
- package/docs/api/api-reference-server.md +477 -3
- package/docs/api/reference.json +1833 -102
- package/docs/arcade-and-store.md +166 -61
- package/docs/arcade-economy.md +184 -184
- package/docs/changelog.md +48 -0
- package/docs/deprecation-policy.md +2 -0
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +247 -76
- package/docs/manifest.json +24 -24
- package/docs/platform-rules.md +36 -27
- package/docs/player-sign-in.md +60 -3
- package/docs/rewarded-ads.md +32 -1
- package/docs/server-bundles.md +9 -7
- package/docs/what-a-game-must-do.md +7 -4
- package/package.json +1 -1
- package/schema/bridge.v1.json +97 -7
package/dist/server.d.ts
CHANGED
|
@@ -139,9 +139,15 @@ interface GemServerHost extends ServerBundleHost {
|
|
|
139
139
|
broadcast(data: string | Uint8Array, options?: BroadcastOptions): void;
|
|
140
140
|
kick(playerId: string, reason?: string): void;
|
|
141
141
|
endMatch?(): Promise<"ok" | "already_ended" | "refused" | "failed" | (string & {})>;
|
|
142
|
+
passes?(playerId: string): Promise<GemServerHostRead>;
|
|
143
|
+
boost?(playerId: string): Promise<GemServerHostRead>;
|
|
142
144
|
log(message: string): void;
|
|
143
145
|
warn(message: string): void;
|
|
144
146
|
}
|
|
147
|
+
interface GemServerHostRead {
|
|
148
|
+
readonly status: number;
|
|
149
|
+
readonly body: unknown;
|
|
150
|
+
}
|
|
145
151
|
interface HostTokenSourceOptions {
|
|
146
152
|
readonly clock: Clock;
|
|
147
153
|
readonly timeoutMs?: number;
|
|
@@ -1774,6 +1780,40 @@ interface ServerListChallengesOptions {
|
|
|
1774
1780
|
interface ServerChallenges {
|
|
1775
1781
|
list(options: ServerListChallengesOptions): Promise<Challenge[]>;
|
|
1776
1782
|
}
|
|
1783
|
+
interface MatchBoostMultiplier {
|
|
1784
|
+
readonly currencySlug: string;
|
|
1785
|
+
readonly multiplier: number;
|
|
1786
|
+
}
|
|
1787
|
+
interface MatchPlayerBoost {
|
|
1788
|
+
readonly active: boolean;
|
|
1789
|
+
readonly multipliers: readonly MatchBoostMultiplier[];
|
|
1790
|
+
readonly expiresInDays?: number;
|
|
1791
|
+
}
|
|
1792
|
+
type PlatformGrantSource = {
|
|
1793
|
+
readonly source: "pass";
|
|
1794
|
+
readonly pass: string;
|
|
1795
|
+
readonly amount: number;
|
|
1796
|
+
} | {
|
|
1797
|
+
readonly source: "boost";
|
|
1798
|
+
readonly amount: number;
|
|
1799
|
+
};
|
|
1800
|
+
interface PlatformGrantPreview {
|
|
1801
|
+
readonly currencySlug?: string;
|
|
1802
|
+
readonly currencyId?: string;
|
|
1803
|
+
readonly base: number;
|
|
1804
|
+
readonly total: number;
|
|
1805
|
+
readonly breakdown: readonly PlatformGrantSource[];
|
|
1806
|
+
readonly ifComplete?: true;
|
|
1807
|
+
}
|
|
1808
|
+
interface EarningsApi {
|
|
1809
|
+
fromReceipt(change: unknown): Promise<PlatformGrantPreview | null>;
|
|
1810
|
+
}
|
|
1811
|
+
interface GamePass {
|
|
1812
|
+
readonly pass: string;
|
|
1813
|
+
readonly held: boolean;
|
|
1814
|
+
readonly daysLeft: number;
|
|
1815
|
+
readonly endsAt: string | null;
|
|
1816
|
+
}
|
|
1777
1817
|
interface GemServerOptions {
|
|
1778
1818
|
readonly baseUrl?: string;
|
|
1779
1819
|
readonly clock?: Clock;
|
|
@@ -1812,7 +1852,10 @@ interface GemServer {
|
|
|
1812
1852
|
readonly seats?: number;
|
|
1813
1853
|
}, signal?: AbortSignal): Promise<MatchBackfillState>;
|
|
1814
1854
|
reportPlayers(reports: readonly MatchPlayerReport[], signal?: AbortSignal): Promise<MatchPlayerReportResult[]>;
|
|
1855
|
+
playerPasses(playerId: string, signal?: AbortSignal): Promise<readonly GamePass[]>;
|
|
1856
|
+
playerBoost(playerId: string, signal?: AbortSignal): Promise<MatchPlayerBoost>;
|
|
1815
1857
|
};
|
|
1858
|
+
readonly earnings: EarningsApi;
|
|
1816
1859
|
readonly analytics: ServerAnalytics;
|
|
1817
1860
|
readonly functions: FunctionsApi;
|
|
1818
1861
|
readonly challenges: ServerChallenges;
|
|
@@ -1913,4 +1956,4 @@ interface AchievementReceipt {
|
|
|
1913
1956
|
readonly reason?: string;
|
|
1914
1957
|
}
|
|
1915
1958
|
declare function achievementReceipts(order: unknown): AchievementReceipt[];
|
|
1916
|
-
export { type AchievementReceipt, type AnalyticsFlushResult, type AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Base64String, type BroadcastOptions, type BytesResponse, CONDITIONAL_HEADERS, type Challenge, type ChallengeWindow, type ClientOptions, type Clock, type ConditionalRequestHeaders, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameApi, GemApiError, GemClient, type GemErrorReason, GemFunctionError, type GemMatchServer, type GemServer, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerIdentity, type GemServerLifecycle, type GemServerOptions, type GemServerProfile, type HostTokenSource, type HostTokenSourceOptions, type MatchBackfillState, type MatchPlayerReport, type MatchPlayerReportResult, PlayerText, type RequestOptions, type RoomArgValue, type RuntimeShutdownPayload, type SendOptions, type ServerAnalytics, type ServerApi, type ServerBundleHost, type ServerChallenges, type ServerListChallengesOptions, type ServerShutdownInfo, type ServerTokenGrant, type TokenGrant, type Transport, TransportRefusal, type TransportRequest, type TransportResponse, type VersionedGetOptions, type VersionedResponse, achievementReceipts, asPlayerText, createGemServer, createHostTokenSource, createMatchServer, defineServer, systemClock, text, toBase64 };
|
|
1959
|
+
export { type AchievementReceipt, type AnalyticsFlushResult, type AnalyticsRejection, type AnalyticsValue, AuthError, type AuthFailure, type AuthState, type Base64String, type BroadcastOptions, type BytesResponse, CONDITIONAL_HEADERS, type Challenge, type ChallengeWindow, type ClientOptions, type Clock, type ConditionalRequestHeaders, type EarningsApi, type FunctionFailure, type FunctionInvokeOptions, type FunctionResult, FunctionsApi, type GameApi, type GamePass, GemApiError, GemClient, type GemErrorReason, GemFunctionError, type GemMatchServer, type GemServer, type GemServerBundle, type GemServerFactory, type GemServerHost, type GemServerHostProfile, type GemServerHostRead, type GemServerIdentity, type GemServerLifecycle, type GemServerOptions, type GemServerProfile, type HostTokenSource, type HostTokenSourceOptions, type MatchBackfillState, type MatchBoostMultiplier, type MatchPlayerBoost, type MatchPlayerReport, type MatchPlayerReportResult, type PlatformGrantPreview, type PlatformGrantSource, PlayerText, type RequestOptions, type RoomArgValue, type RuntimeShutdownPayload, type SendOptions, type ServerAnalytics, type ServerApi, type ServerBundleHost, type ServerChallenges, type ServerListChallengesOptions, type ServerShutdownInfo, type ServerTokenGrant, type TokenGrant, type Transport, TransportRefusal, type TransportRequest, type TransportResponse, type VersionedGetOptions, type VersionedResponse, achievementReceipts, asPlayerText, createGemServer, createHostTokenSource, createMatchServer, defineServer, systemClock, text, toBase64 };
|
package/docs/analytics.md
CHANGED
|
@@ -295,6 +295,46 @@ It counts against the event's three dimensions and its cardinality cap like any
|
|
|
295
295
|
on the events that answer the question rather than on all of them. Name the dimension after your
|
|
296
296
|
experiment, never `variant`: the platform fills that one in itself, for store-page experiments.
|
|
297
297
|
|
|
298
|
+
**Declare the test too, or the platform's results never show it.** The platform reads your own
|
|
299
|
+
A/B test, every arm side by side, only when your game config names it under `analytics_tests`:
|
|
300
|
+
which funnel it is read through, and which dimension of that funnel's first event holds the arm.
|
|
301
|
+
This config uploads as it stands; its `tutorial_finished` entry is the one above, so declare it
|
|
302
|
+
once:
|
|
303
|
+
|
|
304
|
+
```yaml
|
|
305
|
+
analytics_events:
|
|
306
|
+
- id: tutorial_started # step 0: carries the arm dimension
|
|
307
|
+
dimensions:
|
|
308
|
+
tutorial_length: { values: [short, long] }
|
|
309
|
+
- id: tutorial_finished
|
|
310
|
+
dimensions:
|
|
311
|
+
tutorial_length: { values: [short, long] }
|
|
312
|
+
|
|
313
|
+
analytics_funnels:
|
|
314
|
+
- id: tutorial_test
|
|
315
|
+
steps: [tutorial_started, tutorial_finished]
|
|
316
|
+
|
|
317
|
+
analytics_tests:
|
|
318
|
+
- id: tutorial_length_ab
|
|
319
|
+
name: "Tutorial: short vs long" # optional, at most 60 characters
|
|
320
|
+
funnel: tutorial_test # declared in analytics_funnels
|
|
321
|
+
arm: tutorial_length # a values dimension of the funnel's step-0 event
|
|
322
|
+
arms: [short, long] # 2 to 8 of its declared values; the first is the control
|
|
323
|
+
step_labels: [Started, Finished] # optional, one per step
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
- **The funnel's first step (step 0) must be your own event, and it must carry the arm
|
|
327
|
+
dimension.** Here `tutorial_started` declares `tutorial_length: { values: [short, long] }` and
|
|
328
|
+
your game passes the selection on it. Never start the funnel at `gem_session_start`: that is
|
|
329
|
+
the platform's event, and it can't carry your arm.
|
|
330
|
+
- **The arm is a `values` dimension.** Its `arms` are values that dimension declares, so give
|
|
331
|
+
`experiment` a list of strings, not a count, for a test you want read this way.
|
|
332
|
+
- **The first arm listed is the control.** The others are compared against it.
|
|
333
|
+
|
|
334
|
+
It ships with the rest of your config, uploaded and promoted to the channel. The declaration
|
|
335
|
+
records nothing new about any player: the arm is already in your events, and the declaration only
|
|
336
|
+
names the funnel and the dimension.
|
|
337
|
+
|
|
298
338
|
**The selection can be reproduced anywhere** — a dedicated server, an analytics query — because
|
|
299
339
|
the hash is fixed and will not change: FNV-1a 32-bit over the UTF-8 bytes of
|
|
300
340
|
`"<key>:<playerId>"`, then MurmurHash3's 32-bit finalizer (`fmix32`), as an unsigned integer, mod
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The function surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/function`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/function` v24.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/function` exports exactly the symbols on this page and nothing else.
|
|
6
6
|
|
|
@@ -360,7 +360,8 @@ dimension for it on the events you want to break down, with the arms as its
|
|
|
360
360
|
values, named after the experiment, and pass the selection there yourself:
|
|
361
361
|
`gem.analytics.track('tutorial_finished', { tutorial_length: tutorial })`.
|
|
362
362
|
Never the platform's `variant` field, which belongs to store-page
|
|
363
|
-
experiments.
|
|
363
|
+
experiments. For the platform's results to show the test, also declare it
|
|
364
|
+
under `analytics_tests` in your game config (see docs/analytics.md).
|
|
364
365
|
|
|
365
366
|
WAITS FOR THE PLAYER. The id arrives with the first play token, so a call
|
|
366
367
|
during boot waits for it, and rejects as `Gem.awaitIdentity` does
|
|
@@ -1667,6 +1668,125 @@ only when your studio corrects a running period; clients may hold the old
|
|
|
1667
1668
|
content for up to an hour afterwards, and the hash is how a verifier tells
|
|
1668
1669
|
them apart.
|
|
1669
1670
|
|
|
1671
|
+
<a id="platformgrantsource"></a>
|
|
1672
|
+
|
|
1673
|
+
## PlatformGrantSource
|
|
1674
|
+
|
|
1675
|
+
```ts
|
|
1676
|
+
type PlatformGrantSource = { source: "pass"; pass: string; amount: number } | { source: "boost"; amount: number }
|
|
1677
|
+
```
|
|
1678
|
+
|
|
1679
|
+
Where the part of a payout above its base comes from.
|
|
1680
|
+
|
|
1681
|
+
<a id="platformgrantpreview"></a>
|
|
1682
|
+
|
|
1683
|
+
## PlatformGrantPreview
|
|
1684
|
+
|
|
1685
|
+
```ts
|
|
1686
|
+
interface PlatformGrantPreview
|
|
1687
|
+
```
|
|
1688
|
+
|
|
1689
|
+
One platform payout — Coins, today — as the preview expects it or a receipt
|
|
1690
|
+
reports it, in one shape, so "you will earn" and "you earned" render with one
|
|
1691
|
+
component.
|
|
1692
|
+
|
|
1693
|
+
<a id="platformgrantpreview-currencyslug"></a>
|
|
1694
|
+
|
|
1695
|
+
## PlatformGrantPreview.currencySlug
|
|
1696
|
+
|
|
1697
|
+
```ts
|
|
1698
|
+
readonly currencySlug?: string
|
|
1699
|
+
```
|
|
1700
|
+
|
|
1701
|
+
The currency's slug, e.g. `ARCADE_TICKETS`. Absent when unknown.
|
|
1702
|
+
|
|
1703
|
+
<a id="platformgrantpreview-currencyid"></a>
|
|
1704
|
+
|
|
1705
|
+
## PlatformGrantPreview.currencyId
|
|
1706
|
+
|
|
1707
|
+
```ts
|
|
1708
|
+
readonly currencyId?: string
|
|
1709
|
+
```
|
|
1710
|
+
|
|
1711
|
+
The currency's id, when it is known.
|
|
1712
|
+
|
|
1713
|
+
<a id="platformgrantpreview-base"></a>
|
|
1714
|
+
|
|
1715
|
+
## PlatformGrantPreview.base
|
|
1716
|
+
|
|
1717
|
+
```ts
|
|
1718
|
+
readonly base: number
|
|
1719
|
+
```
|
|
1720
|
+
|
|
1721
|
+
What a player with no pass and no Boost gets.
|
|
1722
|
+
|
|
1723
|
+
<a id="platformgrantpreview-total"></a>
|
|
1724
|
+
|
|
1725
|
+
## PlatformGrantPreview.total
|
|
1726
|
+
|
|
1727
|
+
```ts
|
|
1728
|
+
readonly total: number
|
|
1729
|
+
```
|
|
1730
|
+
|
|
1731
|
+
What this player gets: `base` plus `breakdown`.
|
|
1732
|
+
|
|
1733
|
+
<a id="platformgrantpreview-breakdown"></a>
|
|
1734
|
+
|
|
1735
|
+
## PlatformGrantPreview.breakdown
|
|
1736
|
+
|
|
1737
|
+
```ts
|
|
1738
|
+
readonly breakdown: readonly PlatformGrantSource[]
|
|
1739
|
+
```
|
|
1740
|
+
|
|
1741
|
+
The part above `base`, and what it is from. Empty when `total` is `base`.
|
|
1742
|
+
|
|
1743
|
+
<a id="platformgrantpreview-ifcomplete"></a>
|
|
1744
|
+
|
|
1745
|
+
## PlatformGrantPreview.ifComplete
|
|
1746
|
+
|
|
1747
|
+
```ts
|
|
1748
|
+
readonly ifComplete?: true
|
|
1749
|
+
```
|
|
1750
|
+
|
|
1751
|
+
`true` for an `on_complete` payout in a preview: it pays only if this
|
|
1752
|
+
claim completes the progression. Absent otherwise, and on a receipt.
|
|
1753
|
+
|
|
1754
|
+
<a id="earningsapi"></a>
|
|
1755
|
+
|
|
1756
|
+
## EarningsApi
|
|
1757
|
+
|
|
1758
|
+
```ts
|
|
1759
|
+
interface EarningsApi
|
|
1760
|
+
```
|
|
1761
|
+
|
|
1762
|
+
`gem.earnings`: what a payout was, in the shape `gem.gameConfig.preview`
|
|
1763
|
+
says it would be.
|
|
1764
|
+
|
|
1765
|
+
<a id="earningsapi-fromreceipt"></a>
|
|
1766
|
+
|
|
1767
|
+
## EarningsApi.fromReceipt
|
|
1768
|
+
|
|
1769
|
+
```ts
|
|
1770
|
+
fromReceipt(change: unknown): Promise<PlatformGrantPreview | null>
|
|
1771
|
+
```
|
|
1772
|
+
|
|
1773
|
+
Turn one `platform_grant` change from an order's receipt into a
|
|
1774
|
+
[PlatformGrantPreview](#platformgrantpreview): `base` is what a player with no pass and no
|
|
1775
|
+
Boost would have got under the same limits, `total` what this player got,
|
|
1776
|
+
and `breakdown` the pass's or the Boost's part, even when a limit trimmed
|
|
1777
|
+
the payout. Draw "you earned" with the component that drew "you will
|
|
1778
|
+
earn". Resolves `null` for any other kind of change.
|
|
1779
|
+
|
|
1780
|
+
**This and `preview` are the only way to show a platform payout.** Never
|
|
1781
|
+
multiply by `gem.player.boost`, and never copy a config amount into your
|
|
1782
|
+
game.
|
|
1783
|
+
|
|
1784
|
+
| Parameter | Type |
|
|
1785
|
+
| --- | --- |
|
|
1786
|
+
| `change` | `unknown` |
|
|
1787
|
+
|
|
1788
|
+
**Returns.** Always resolves; never rejects.
|
|
1789
|
+
|
|
1670
1790
|
<a id="currencybalance"></a>
|
|
1671
1791
|
|
|
1672
1792
|
## CurrencyBalance
|
|
@@ -3183,49 +3303,6 @@ was already claimed from it — the progress resets, the rewards stay.
|
|
|
3183
3303
|
| `progressionId` | `string` |
|
|
3184
3304
|
| `signal` | `AbortSignal` |
|
|
3185
3305
|
|
|
3186
|
-
<a id="gamepass"></a>
|
|
3187
|
-
|
|
3188
|
-
## GamePass
|
|
3189
|
-
|
|
3190
|
-
```ts
|
|
3191
|
-
interface GamePass
|
|
3192
|
-
```
|
|
3193
|
-
|
|
3194
|
-
One pass games may see, and whether this player holds it. Listed whether they
|
|
3195
|
-
hold it or not, and this is everything a game learns about it.
|
|
3196
|
-
|
|
3197
|
-
<a id="gamepass-pass"></a>
|
|
3198
|
-
|
|
3199
|
-
## GamePass.pass
|
|
3200
|
-
|
|
3201
|
-
```ts
|
|
3202
|
-
readonly pass: string
|
|
3203
|
-
```
|
|
3204
|
-
|
|
3205
|
-
The pass's offering slug, such as `vip`. Stable and not localized: key on this.
|
|
3206
|
-
|
|
3207
|
-
<a id="gamepass-held"></a>
|
|
3208
|
-
|
|
3209
|
-
## GamePass.held
|
|
3210
|
-
|
|
3211
|
-
```ts
|
|
3212
|
-
readonly held: boolean
|
|
3213
|
-
```
|
|
3214
|
-
|
|
3215
|
-
Whether the player holds it right now.
|
|
3216
|
-
|
|
3217
|
-
<a id="gamepass-endsat"></a>
|
|
3218
|
-
|
|
3219
|
-
## GamePass.endsAt
|
|
3220
|
-
|
|
3221
|
-
```ts
|
|
3222
|
-
readonly endsAt: string | null
|
|
3223
|
-
```
|
|
3224
|
-
|
|
3225
|
-
When the player stops holding it, RFC 3339 in UTC, grace and any queued
|
|
3226
|
-
period included. `null` when it is not held, and when it is held with no
|
|
3227
|
-
end, so read [GamePass.held](#gamepass-held) first: `null` alone never means ended.
|
|
3228
|
-
|
|
3229
3306
|
<a id="passesevent"></a>
|
|
3230
3307
|
|
|
3231
3308
|
## PassesEvent
|
|
@@ -7028,6 +7105,67 @@ const theirs = await gem.leaderboards.character('time-trial', characterId);
|
|
|
7028
7105
|
caller may not act for this character at all — that one is decided from
|
|
7029
7106
|
the caller's own claims, not from which character was asked for.
|
|
7030
7107
|
|
|
7108
|
+
<a id="gamepass"></a>
|
|
7109
|
+
|
|
7110
|
+
## GamePass
|
|
7111
|
+
|
|
7112
|
+
```ts
|
|
7113
|
+
interface GamePass
|
|
7114
|
+
```
|
|
7115
|
+
|
|
7116
|
+
One pass games may see, and whether this player holds it. Listed whether they
|
|
7117
|
+
hold it or not, and this is everything a game learns about it.
|
|
7118
|
+
|
|
7119
|
+
<a id="gamepass-pass"></a>
|
|
7120
|
+
|
|
7121
|
+
## GamePass.pass
|
|
7122
|
+
|
|
7123
|
+
```ts
|
|
7124
|
+
readonly pass: string
|
|
7125
|
+
```
|
|
7126
|
+
|
|
7127
|
+
The pass's offering slug, such as `vip`. Stable and not localized: key on this.
|
|
7128
|
+
|
|
7129
|
+
<a id="gamepass-held"></a>
|
|
7130
|
+
|
|
7131
|
+
## GamePass.held
|
|
7132
|
+
|
|
7133
|
+
```ts
|
|
7134
|
+
readonly held: boolean
|
|
7135
|
+
```
|
|
7136
|
+
|
|
7137
|
+
Whether the player holds it right now.
|
|
7138
|
+
|
|
7139
|
+
<a id="gamepass-daysleft"></a>
|
|
7140
|
+
|
|
7141
|
+
## GamePass.daysLeft
|
|
7142
|
+
|
|
7143
|
+
```ts
|
|
7144
|
+
readonly daysLeft: number
|
|
7145
|
+
```
|
|
7146
|
+
|
|
7147
|
+
Whole days the player keeps it, rounded up so a pass with any time left
|
|
7148
|
+
reads at least `1`, and **capped at 7**: `7` means "7 or more", and is
|
|
7149
|
+
also what a pass held with no end reads. `0` when it is not held. Show
|
|
7150
|
+
`7` as "7+ days left", never as an exact end.
|
|
7151
|
+
|
|
7152
|
+
<a id="gamepass-endsat"></a>
|
|
7153
|
+
|
|
7154
|
+
## GamePass.endsAt
|
|
7155
|
+
|
|
7156
|
+
```ts
|
|
7157
|
+
readonly endsAt: string | null
|
|
7158
|
+
```
|
|
7159
|
+
|
|
7160
|
+
**Deprecated.** Use [GamePass.daysLeft](#gamepass-daysleft). An exact instant is a value two
|
|
7161
|
+
games could match to link one player's identities, so the platform will
|
|
7162
|
+
stop sending it; it reads `null` then. See
|
|
7163
|
+
[the deprecation policy](../deprecation-policy.md).
|
|
7164
|
+
|
|
7165
|
+
When the player stops holding it, RFC 3339 in UTC, grace and any queued
|
|
7166
|
+
period included. `null` when it is not held, and when it is held with no
|
|
7167
|
+
end, so read [GamePass.held](#gamepass-held) first: `null` alone never means ended.
|
|
7168
|
+
|
|
7031
7169
|
<a id="platformcurrencyaccess"></a>
|
|
7032
7170
|
|
|
7033
7171
|
## PlatformCurrencyAccess
|
|
@@ -8714,6 +8852,19 @@ The invoking player's platform-currency balances and earn allowance. The
|
|
|
8714
8852
|
platform refuses both for a game server. `earnAllowance.on()` never fires
|
|
8715
8853
|
here: the hint comes from the arcade, and a function has none.
|
|
8716
8854
|
|
|
8855
|
+
<a id="gemfunction-earnings"></a>
|
|
8856
|
+
|
|
8857
|
+
## GemFunction.earnings
|
|
8858
|
+
|
|
8859
|
+
```ts
|
|
8860
|
+
readonly earnings: EarningsApi
|
|
8861
|
+
```
|
|
8862
|
+
|
|
8863
|
+
What a platform payout was: `earnings.fromReceipt(change)` turns a
|
|
8864
|
+
`platform_grant` change from a receipt into the shape the browser's payout
|
|
8865
|
+
preview gives — `base`, `total`, and the pass's or the Boost's part — to
|
|
8866
|
+
return to the game that draws it.
|
|
8867
|
+
|
|
8717
8868
|
<a id="gemfunction-profiles"></a>
|
|
8718
8869
|
|
|
8719
8870
|
## GemFunction.profiles
|