@indiegems/gem-web-sdk 20.0.0 → 21.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 +31 -0
- package/brand/ticket-hero-128.png +0 -0
- package/brand/ticket-hero-256.png +0 -0
- package/brand/ticket-hero-384.png +0 -0
- package/brand/ticket-hero-512.png +0 -0
- package/brand/ticket-hero-768.png +0 -0
- package/brand/ticket-icon-20-1.5x.png +0 -0
- package/brand/ticket-icon-20-1x.png +0 -0
- package/brand/ticket-icon-20-2x.png +0 -0
- package/brand/ticket-icon-20-3x.png +0 -0
- package/brand/ticket-icon-32-1.5x.png +0 -0
- package/brand/ticket-icon-32-1x.png +0 -0
- package/brand/ticket-icon-32-2x.png +0 -0
- package/brand/ticket-icon-32-3x.png +0 -0
- package/brand/ticket-icon-ingame-20-1.5x.png +0 -0
- package/brand/ticket-icon-ingame-20-1x.png +0 -0
- package/brand/ticket-icon-ingame-20-2x.png +0 -0
- package/brand/ticket-icon-ingame-20-3x.png +0 -0
- package/brand/ticket-icon-ingame-32-1.5x.png +0 -0
- package/brand/ticket-icon-ingame-32-1x.png +0 -0
- package/brand/ticket-icon-ingame-32-2x.png +0 -0
- package/brand/ticket-icon-ingame-32-3x.png +0 -0
- package/brand/ticket-pile-1-128.png +0 -0
- package/brand/ticket-pile-1-256.png +0 -0
- package/brand/ticket-pile-1-384.png +0 -0
- package/brand/ticket-pile-1-512.png +0 -0
- package/brand/ticket-pile-1-768.png +0 -0
- package/brand/ticket-pile-2-128.png +0 -0
- package/brand/ticket-pile-2-256.png +0 -0
- package/brand/ticket-pile-2-384.png +0 -0
- package/brand/ticket-pile-2-512.png +0 -0
- package/brand/ticket-pile-2-768.png +0 -0
- package/dist/function.d.ts +1 -1
- package/dist/game.d.ts +52 -4
- 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 +57 -6
- package/dist/sdk.d.ts +52 -4
- package/docs/analytics.md +11 -0
- package/docs/api/api-reference-function.md +7 -2
- package/docs/api/api-reference-game.md +15 -9
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +8 -8
- package/docs/brand-art.md +65 -16
- package/docs/changelog.md +31 -0
- package/docs/dedicated-server-flow.md +30 -2
- package/docs/getting-started.md +3 -3
- package/docs/manifest.json +18 -18
- package/docs/match-session.md +9 -0
- package/docs/p2p-and-parties.md +79 -6
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +8 -5
- package/package.json +1 -1
- package/schema/bridge.v1.json +67 -2
- package/schema/capabilities.public.json +6 -0
package/docs/manifest.json
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"file": "analytics.md",
|
|
16
|
-
"sha256": "
|
|
17
|
-
"updated": "2026-
|
|
16
|
+
"sha256": "0883e3c53f500f4d0554d7d62ef938bc53676800b11a285e0da508c6fcc4076d",
|
|
17
|
+
"updated": "2026-11-02T22:24:57Z"
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"file": "arcade-and-store.md",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"file": "brand-art.md",
|
|
41
|
-
"sha256": "
|
|
42
|
-
"updated": "2026-
|
|
41
|
+
"sha256": "7d4d5c7226710742efdb958e10387d5e5a3de0622a686734c885720e16a5b128",
|
|
42
|
+
"updated": "2026-11-02T21:18:05Z"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"file": "branding.md",
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"file": "changelog.md",
|
|
51
|
-
"sha256": "
|
|
52
|
-
"updated": "2026-11-
|
|
51
|
+
"sha256": "ed2476deed910a61badb23023267b5a8292467d3812c211adf3baf23c42ae914",
|
|
52
|
+
"updated": "2026-11-03T21:36:35Z"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"file": "daily-challenges.md",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
},
|
|
59
59
|
{
|
|
60
60
|
"file": "dedicated-server-flow.md",
|
|
61
|
-
"sha256": "
|
|
62
|
-
"updated": "2026-10-
|
|
61
|
+
"sha256": "4a51622df43be68b5b9a88ae89907972090e64fd9b793ba74e5daa576489281c",
|
|
62
|
+
"updated": "2026-10-27T02:41:17Z"
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
65
|
"file": "deprecation-policy.md",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"file": "getting-started.md",
|
|
81
|
-
"sha256": "
|
|
82
|
-
"updated": "2026-11-
|
|
81
|
+
"sha256": "dd7328fd2462efd7373904f1c14085fd75b046d76670113cd0762825d0c0f107",
|
|
82
|
+
"updated": "2026-11-03T21:36:35Z"
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"file": "inventory-and-catalogs.md",
|
|
@@ -93,13 +93,13 @@
|
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"file": "match-session.md",
|
|
96
|
-
"sha256": "
|
|
97
|
-
"updated": "2026-
|
|
96
|
+
"sha256": "121eb2bd92da9daf3922097699e0ab450da6a087eec8de552fe573094cf7e2c4",
|
|
97
|
+
"updated": "2026-11-02T16:12:53Z"
|
|
98
98
|
},
|
|
99
99
|
{
|
|
100
100
|
"file": "p2p-and-parties.md",
|
|
101
|
-
"sha256": "
|
|
102
|
-
"updated": "2026-
|
|
101
|
+
"sha256": "1de834a88746a4c7b6de555b3e6e3500fce0b7e75fc3cb87d46795efc7e27940",
|
|
102
|
+
"updated": "2026-11-02T23:33:48Z"
|
|
103
103
|
},
|
|
104
104
|
{
|
|
105
105
|
"file": "platform-moments.md",
|
|
@@ -138,8 +138,8 @@
|
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"file": "server-bundles.md",
|
|
141
|
-
"sha256": "
|
|
142
|
-
"updated": "2026-11-
|
|
141
|
+
"sha256": "736e7b4ceb810944331274f940cb47e7d3d4a57f815340a9ade7ffc5f01cd0fa",
|
|
142
|
+
"updated": "2026-11-03T21:36:35Z"
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"file": "serverless-functions-js.md",
|
|
@@ -163,8 +163,8 @@
|
|
|
163
163
|
},
|
|
164
164
|
{
|
|
165
165
|
"file": "what-a-game-must-do.md",
|
|
166
|
-
"sha256": "
|
|
167
|
-
"updated": "2026-11-
|
|
166
|
+
"sha256": "059f3c8a31c0f6098d2c8e4836b79e866d0c5486443dc89039e160648946bd68",
|
|
167
|
+
"updated": "2026-11-02T23:27:22Z"
|
|
168
168
|
}
|
|
169
169
|
]
|
|
170
170
|
}
|
package/docs/match-session.md
CHANGED
|
@@ -155,6 +155,15 @@ own. When a host migration is already in flight the attempt waits it out instead
|
|
|
155
155
|
the way back, and it re-attaches this connection without a dial (§5.1 of
|
|
156
156
|
[p2p-and-parties.md](p2p-and-parties.md)).
|
|
157
157
|
|
|
158
|
+
**A relay that moves is not a drop.** When the platform takes a TURN relay out of service, or one
|
|
159
|
+
dies under a match, the SDK moves the peer-to-peer connection to another relay with an ICE restart
|
|
160
|
+
on the connection it already has (§4.1 and §5 of [p2p-and-parties.md](p2p-and-parties.md)). The
|
|
161
|
+
data channels stay open, so there is no `matchSuspended`, no `matchReconnected` and nothing to
|
|
162
|
+
resync — at most a pause of a few seconds while the route changes. Only a move that misses the
|
|
163
|
+
platform's deadline, or a restart that never comes back, leaves that path: the first is a drop with
|
|
164
|
+
`cause: 'link_closed'` and the re-dial above; the second is treated as the host being gone, as §5
|
|
165
|
+
there describes.
|
|
166
|
+
|
|
158
167
|
The loop never runs while the page is hidden. It parks, and the next visible edge — the phone
|
|
159
168
|
unlocked, the tab foregrounded — is itself a read, so a match that ended while the player was away
|
|
160
169
|
resolves to `matchEnded` and one that is still running resumes reconnecting. Nothing in your game
|
package/docs/p2p-and-parties.md
CHANGED
|
@@ -327,6 +327,14 @@ peer connection's configuration when it is constructed. Leasing afterwards
|
|
|
327
327
|
produces a connection that works whenever both peers are directly reachable —
|
|
328
328
|
which passes every test on a LAN and fails for a large minority of real players.
|
|
329
329
|
|
|
330
|
+
**A lease has a lifetime, and a match can outlast it.** The SDK keeps the
|
|
331
|
+
lease's expiry and renews it whenever it is about to build a connection on a
|
|
332
|
+
lease within two minutes of running out — a host answering a player's offer,
|
|
333
|
+
or a player rebuilding after a migration — so a host
|
|
334
|
+
deep into a long match never answers with credentials that have already
|
|
335
|
+
lapsed. A renewal also reaches every connection already up, ready for its next
|
|
336
|
+
change of route. Renewals are paced at one per event, never one per candidate.
|
|
337
|
+
|
|
330
338
|
**Which cluster to lease from is normally nobody's decision to make here.** The
|
|
331
339
|
parent measures the regions and hands the close ones across the bridge, and each
|
|
332
340
|
peer leases from its own best one — a relay near that peer, whichever cluster
|
|
@@ -343,6 +351,23 @@ alike — which is a decision to move the room. When nothing resolves one the
|
|
|
343
351
|
connection proceeds without a relay and says so loudly, because the failure it
|
|
344
352
|
causes is the one that looks like success.
|
|
345
353
|
|
|
354
|
+
**A lease refused for its cluster moves to the next one the page sent.** The page
|
|
355
|
+
sends up to three clusters, best first. When the platform refuses a lease because
|
|
356
|
+
of the cluster it named — `cluster_hidden`, an operator took it out of service and
|
|
357
|
+
no other cluster in its region can relay for it, or `invalid_cluster_target`, it
|
|
358
|
+
is not one the platform runs — the SDK leases from the next, before any connection
|
|
359
|
+
is built. Only when every one is refused does the connection proceed without a
|
|
360
|
+
relay, with the same warning. Any other refusal is not walked, because another
|
|
361
|
+
cluster would answer it the same. A cluster you named yourself is the only one
|
|
362
|
+
tried. A renewal walks the same way, starting from the cluster that granted the
|
|
363
|
+
lease it replaces, and a re-dial after a dropped connection leases afresh over the
|
|
364
|
+
whole list, so a cluster hidden mid-match is passed over either way.
|
|
365
|
+
|
|
366
|
+
An earlier SDK version asks only the best cluster. Where the platform can answer
|
|
367
|
+
from another cluster in the same region it does, and that SDK gets its relay
|
|
368
|
+
anyway; where it cannot, that SDK connects without one. `dedicated-server-flow.md`
|
|
369
|
+
§5 says what hiding a cluster does to rooms and matches.
|
|
370
|
+
|
|
346
371
|
**`null` names no cluster, exactly as omitting the option does.** A game picking
|
|
347
372
|
its target out of a list writes `targets[0]?.id ?? null`, so that is the spelling
|
|
348
373
|
ordinary code arrives with — and it defers to the parent's answer the same way an
|
|
@@ -351,6 +376,41 @@ request for a cluster with no id: that costs the caller the no-relay warning and
|
|
|
351
376
|
reports "the relay was refused" for a player whose actual problem is that nobody
|
|
352
377
|
ever resolved a region for them.
|
|
353
378
|
|
|
379
|
+
### 4.1 A relay going away does not end the match
|
|
380
|
+
|
|
381
|
+
Relays are taken out of service for deploys, credential rotation and scaling.
|
|
382
|
+
The platform does it in three ordered steps:
|
|
383
|
+
|
|
384
|
+
1. **Delist.** No new lease names the relay, so no new connection is built on it.
|
|
385
|
+
2. **Move.** Every player in a match that may be using it is told, and the
|
|
386
|
+
connections on it move elsewhere while it still forwards traffic.
|
|
387
|
+
3. **Stop.** The relay stops once nobody is on it, or when the platform's
|
|
388
|
+
deadline for the move passes.
|
|
389
|
+
|
|
390
|
+
**Your game sees none of it.** On the notice every player renews its lease and
|
|
391
|
+
drops the draining relay from the list it holds. A player whose connection to
|
|
392
|
+
the host runs through that relay moves with an **ICE restart on the connection
|
|
393
|
+
it already has**: new candidates on the remaining relays, the same DTLS
|
|
394
|
+
session, the same three data channels. The host answers on its existing
|
|
395
|
+
connection. Nothing closes, `gem.match` raises no `matchSuspended`, and nobody
|
|
396
|
+
reports the host unreachable. A player whose route does not touch the draining
|
|
397
|
+
relay renews and does nothing else, and the host renews and answers but never
|
|
398
|
+
offers a restart of its own.
|
|
399
|
+
|
|
400
|
+
**If the move has not completed by the platform's deadline**, the player drops
|
|
401
|
+
that connection and the ordinary reconnect takes over (§5.1, and §4 of
|
|
402
|
+
[match-session.md](match-session.md)): a fresh session at the same host, over
|
|
403
|
+
the renewed lease, reported as this player's own link closing — `link_closed` —
|
|
404
|
+
because the host is fine. The same happens at once if the host has already let
|
|
405
|
+
go of that player's connection when the restart reaches it: the host can only
|
|
406
|
+
answer from a new connection, which the player's existing one cannot use.
|
|
407
|
+
|
|
408
|
+
**The announced move needs the Arcade page to pass the notice on.** The page is
|
|
409
|
+
what relays it into the game frame, and a page whose side of the bridge
|
|
410
|
+
predates the SDK release that brought this drops it. Until the page is at that
|
|
411
|
+
release or later, a relay stopping under a match is recovered the way an
|
|
412
|
+
unannounced loss is (§5): a few seconds of quiet, then the same restart.
|
|
413
|
+
|
|
354
414
|
---
|
|
355
415
|
|
|
356
416
|
## 5. Host migration is where a split match can happen
|
|
@@ -388,18 +448,31 @@ trying to fix, and the old host is already gone. A host that settled on its own
|
|
|
388
448
|
send could not tell that apart from a healthy match. The active phase is bounded
|
|
389
449
|
at 30 seconds and a stall is reported as `migration_failed`.
|
|
390
450
|
|
|
391
|
-
A non-host
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
451
|
+
A non-host whose connection to the host goes quiet **tries to bring it back
|
|
452
|
+
before it reports anything**. ICE `disconnected` is what a lost relay looks like
|
|
453
|
+
as much as a lost host, so the player first renews its lease and restarts ICE on
|
|
454
|
+
the existing connection — the move §4.1 describes, without the notice — and
|
|
455
|
+
gives it about eight seconds to come back. Only if it does not, or the
|
|
456
|
+
connection fails or closes outright meanwhile, does the player report the host
|
|
457
|
+
unreachable, **once per epoch**. A relay that dies without warning therefore no
|
|
458
|
+
longer ends a two-player match or migrates a larger one away from a healthy
|
|
459
|
+
host; the cost is that a host who really has gone is reported a few seconds
|
|
460
|
+
later.
|
|
461
|
+
|
|
462
|
+
The report is a vote, not a verdict. In a match of three or more, gem-api
|
|
463
|
+
migrates on a quorum of at least two independent reporters, so one player
|
|
464
|
+
cannot unseat a host who is fine for everyone else. In a two-player match there
|
|
465
|
+
is nobody to elect, and the lone peer's report ends the match with `host_left`
|
|
466
|
+
— which is why the report waits for the restart to fail.
|
|
395
467
|
|
|
396
468
|
### 5.1 What `gem.match` does with a migration
|
|
397
469
|
|
|
398
470
|
A `MatchConnection` to a hosting peer survives a migration as a **pause**: its
|
|
399
471
|
channels re-resolve on `hostChanged`, so the game sees a gap rather than a new
|
|
400
472
|
connection to build. What the room reports meanwhile is the ordinary session
|
|
401
|
-
vocabulary — `matchSuspended` with cause `
|
|
402
|
-
link
|
|
473
|
+
vocabulary — `matchSuspended` with cause `link_closed` when this side sees the
|
|
474
|
+
host's link fail or `host_unreachable` when the platform reports the host gone,
|
|
475
|
+
`matchReconnected` with `attempt: 0` when the migration seated a
|
|
403
476
|
new host and the link came back without a re-dial, and `matchEnded` with
|
|
404
477
|
`host_left` or `migration_failed` when it did not. The screens for each are
|
|
405
478
|
[match-session.md](match-session.md) §3 and §4; the listen host's own
|
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/
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v21.0.0/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v21.0.0/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
|
@@ -53,10 +53,10 @@ No `baseUrl`. The Arcade names the backend on every token and the SDK routes the
|
|
|
53
53
|
tokens only if its origin is allowed, and the pages that matter are always allowed without a word
|
|
54
54
|
from you:
|
|
55
55
|
|
|
56
|
-
- **the Gem Arcade website
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
- **the Gem Arcade website and app** — the SDK carries their origins as `ARCADE_EMBEDDERS`, on every
|
|
57
|
+
platform the app runs on. The list also holds `https://developer.gemarcade.com`, which is
|
|
58
|
+
reserved and not live: no page is served there. You play your own dev and test builds with
|
|
59
|
+
`gemctl serve` or the Gem Editor, or on your game's dev or test channel in the Arcade;
|
|
60
60
|
- **a dev host on your own machine** — `gemctl serve` and the Gem Editor frame a locally-served game
|
|
61
61
|
from a loopback origin whose port changes every run, and the SDK admits that shape by rule, only
|
|
62
62
|
while your game itself is served from loopback. Once published, nothing about the rule applies.
|
|
@@ -1710,7 +1710,10 @@ stops your media elements, and restarts neither. The snippet above has stopped l
|
|
|
1710
1710
|
the SDK does this part for you. After the page was hidden, or a context left `running` without your
|
|
1711
1711
|
asking, it listens again for the player's next lift in your frame, resumes the contexts that were
|
|
1712
1712
|
running and replays the elements that were playing when the sound was cut off, then stops
|
|
1713
|
-
listening.
|
|
1713
|
+
listening. A context that reads `running` after the page comes back can still be silent, as when
|
|
1714
|
+
your own `resume()` ran outside a tap, so on that first lift the SDK also suspends and resumes each
|
|
1715
|
+
context that had run, once per return: your clock pauses for a moment, and the sound comes back.
|
|
1716
|
+
You write nothing for it. It stays out of your way:
|
|
1714
1717
|
|
|
1715
1718
|
- **A context you `suspend()` yourself is left alone** until you call `resume()`. If you pause your
|
|
1716
1719
|
audio when the page is hidden and resume it when it comes back, keep doing that: the SDK steps in
|
package/package.json
CHANGED
package/schema/bridge.v1.json
CHANGED
|
@@ -1790,8 +1790,11 @@
|
|
|
1790
1790
|
"deliberately: a frame that took its JWKS endpoint from a relayed message",
|
|
1791
1791
|
"would let the parent choose the keys it verifies peers against.",
|
|
1792
1792
|
"",
|
|
1793
|
-
"The nine events are the whole vocabulary
|
|
1794
|
-
"a drop at the parent, not a passthrough.",
|
|
1793
|
+
"The nine events are the whole vocabulary of this message; an",
|
|
1794
|
+
"unrecognised p2p_* type is a drop at the parent, not a passthrough. The",
|
|
1795
|
+
"relay drain notice is the one further push the parent relays, and it",
|
|
1796
|
+
"crosses as its own type, gem.relayDraining: this enum is closed, so a",
|
|
1797
|
+
"tenth value would be refused by every frame already shipped.",
|
|
1795
1798
|
"",
|
|
1796
1799
|
"PARENT TO FRAME ONLY. gem.signal is the other direction."
|
|
1797
1800
|
],
|
|
@@ -1824,6 +1827,68 @@
|
|
|
1824
1827
|
"rateLimit": { "perMinute": 1800 }
|
|
1825
1828
|
},
|
|
1826
1829
|
|
|
1830
|
+
{
|
|
1831
|
+
"type": "gem.relayDraining",
|
|
1832
|
+
"direction": "parentToFrame",
|
|
1833
|
+
"transport": "port",
|
|
1834
|
+
"$comment": [
|
|
1835
|
+
"A TURN relay this player's P2P match may be using is going away, relayed",
|
|
1836
|
+
"from gem-api's `p2p_relay_draining`. Gated by the `p2pRelayDrain`",
|
|
1837
|
+
"capability; the mapping lives in schema/capabilities.json and nowhere",
|
|
1838
|
+
"else.",
|
|
1839
|
+
"",
|
|
1840
|
+
"A HINT, AND ONE THE FRAME COULD SURVIVE WITHOUT. It tells the frame to",
|
|
1841
|
+
"re-lease and, where its selected candidate pair runs through one of",
|
|
1842
|
+
"these relays, to restart ICE on the connection it already has, so the",
|
|
1843
|
+
"match moves before the relay stops rather than after. The control is",
|
|
1844
|
+
"the relay's own: a draining relay refuses new allocations whatever",
|
|
1845
|
+
"this message says, and a connection on it fails over by the ordinary",
|
|
1846
|
+
"path when it stops. A parent that rewrites this can cost a frame a",
|
|
1847
|
+
"renegotiation; it cannot move a connection anywhere.",
|
|
1848
|
+
"",
|
|
1849
|
+
"ITS OWN TYPE, NOT A TENTH VALUE IN gem.signalEvent'S ENUM: the",
|
|
1850
|
+
"generated validator rejects an unknown enum value, and every frame",
|
|
1851
|
+
"already shipped would answer `invalid_message`. A parent sends this",
|
|
1852
|
+
"only to a frame that advertised the capability and drops it otherwise.",
|
|
1853
|
+
"",
|
|
1854
|
+
"SCOPED BY MATCH, NOT BY GAME. gem-api stamps the push with no game id,",
|
|
1855
|
+
"as it does `p2p_error`, so the parent relays it only for a match this",
|
|
1856
|
+
"player's socket or this frame has already named.",
|
|
1857
|
+
"",
|
|
1858
|
+
"PARENT TO FRAME ONLY. A frame-authored drain notice would let a game",
|
|
1859
|
+
"tell the parent a relay is going away."
|
|
1860
|
+
],
|
|
1861
|
+
"fields": {
|
|
1862
|
+
"matchId": {
|
|
1863
|
+
"kind": "string",
|
|
1864
|
+
"maxLength": 64,
|
|
1865
|
+
"$comment": "The match whose relays are draining. The frame acts only for the live session on this match."
|
|
1866
|
+
},
|
|
1867
|
+
"clusterTargetId": {
|
|
1868
|
+
"kind": "string",
|
|
1869
|
+
"maxLength": 64,
|
|
1870
|
+
"$comment": "The cluster the draining relays belong to. It decides nothing on the frame: the re-lease asks the cluster the session already leases from."
|
|
1871
|
+
},
|
|
1872
|
+
"relayIps": {
|
|
1873
|
+
"kind": "array",
|
|
1874
|
+
"maxItems": 16,
|
|
1875
|
+
"items": { "kind": "string", "maxLength": 64 },
|
|
1876
|
+
"$comment": "The draining relays' public addresses — the hosts in the lease's `turn:` URLs, and what a selected candidate pair names. A lease names at most three relays."
|
|
1877
|
+
},
|
|
1878
|
+
"drainingSince": {
|
|
1879
|
+
"kind": "string",
|
|
1880
|
+
"maxLength": 40,
|
|
1881
|
+
"$comment": "RFC 3339. When the relays began draining."
|
|
1882
|
+
},
|
|
1883
|
+
"deadline": {
|
|
1884
|
+
"kind": "string",
|
|
1885
|
+
"maxLength": 40,
|
|
1886
|
+
"$comment": "RFC 3339. When the relays stop relaying. A link still on one by then is dropped and re-dialled; one this side cannot parse is read as the drain ceiling, thirty minutes."
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
"rateLimit": { "perMinute": 60 }
|
|
1890
|
+
},
|
|
1891
|
+
|
|
1827
1892
|
{
|
|
1828
1893
|
"type": "gem.dedicatedEvent",
|
|
1829
1894
|
"direction": "parentToFrame",
|