@bountyboard/arcade-sdk 1.2.0 → 1.3.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/AGENTS.md +12 -3
- package/CHANGELOG.md +35 -0
- package/README.md +50 -3
- package/dist/{chunk-KDBBR532.js → chunk-PYW5F45R.js} +334 -2
- package/dist/chunk-PYW5F45R.js.map +1 -0
- package/dist/global-sdk.d.ts +47 -0
- package/dist/global.js +343 -10
- package/dist/index.cjs +333 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/multiplayer.cjs +342 -10
- package/dist/multiplayer.cjs.map +1 -1
- package/dist/multiplayer.d.cts +2 -2
- package/dist/multiplayer.d.ts +2 -2
- package/dist/multiplayer.js +1 -1
- package/dist/{types-dtsNXYZW.d.cts → types-B5bqWnHH.d.cts} +46 -1
- package/dist/{types-dtsNXYZW.d.ts → types-B5bqWnHH.d.ts} +46 -1
- package/docs/llms.txt +137 -52
- package/package.json +1 -1
- package/dist/chunk-KDBBR532.js.map +0 -1
package/dist/multiplayer.js
CHANGED
|
@@ -42,6 +42,46 @@ interface BBArcadeRewardedAdsConfig {
|
|
|
42
42
|
interface BBArcadeConfig {
|
|
43
43
|
rewardedAds?: BBArcadeRewardedAdsConfig;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* What is backing `window.localStorage` right now.
|
|
47
|
+
* - `native` — a real Storage works here; the SDK changed nothing.
|
|
48
|
+
* - `cloud` — the shim is installed and syncs to the player's Bounty Board save.
|
|
49
|
+
* - `memory` — the shim is installed but nothing can persist (guest, standalone,
|
|
50
|
+
* off-host); reads and writes work for the session and are then discarded.
|
|
51
|
+
*/
|
|
52
|
+
type BBArcadeStorageMode = 'native' | 'cloud' | 'memory';
|
|
53
|
+
/**
|
|
54
|
+
* localStorage compatibility for hosted builds. Bounty-Board-hosted uploads run
|
|
55
|
+
* in an opaque-origin sandbox where `localStorage` THROWS rather than returning
|
|
56
|
+
* empty, which breaks engine exports (GameMaker `ini_*`, Godot, Unity) that
|
|
57
|
+
* assume synchronous storage. The shim replaces it with a Storage-shaped object
|
|
58
|
+
* backed by the SDK's own cloud save, so those builds persist per player and
|
|
59
|
+
* across devices with no engine changes.
|
|
60
|
+
*
|
|
61
|
+
* The script-tag build installs it automatically at load. Module consumers keep
|
|
62
|
+
* a side-effect-free import and call `install()` themselves before boot.
|
|
63
|
+
*/
|
|
64
|
+
interface BBArcadeStorage {
|
|
65
|
+
/**
|
|
66
|
+
* Install the shim if this origin has no working localStorage, and return the
|
|
67
|
+
* resulting mode. Idempotent, and a no-op when native storage works. Call it
|
|
68
|
+
* BEFORE the engine/game script runs.
|
|
69
|
+
*/
|
|
70
|
+
install(): BBArcadeStorageMode;
|
|
71
|
+
/**
|
|
72
|
+
* Resolves once the cloud read has landed (or settled as unpersistable).
|
|
73
|
+
* `getItem` is synchronous but the cloud read is not, so a game that reads
|
|
74
|
+
* saved progress during boot should await this first.
|
|
75
|
+
*/
|
|
76
|
+
ready(): Promise<BBArcadeStorageMode>;
|
|
77
|
+
/**
|
|
78
|
+
* Force pending writes out now instead of waiting for the debounce. Rejects
|
|
79
|
+
* with the same BBArcadeError codes as save().
|
|
80
|
+
*/
|
|
81
|
+
flush(): Promise<void>;
|
|
82
|
+
/** The current backing mode. */
|
|
83
|
+
readonly mode: BBArcadeStorageMode;
|
|
84
|
+
}
|
|
45
85
|
/**
|
|
46
86
|
* The logged-in player's public display identity, as delivered by the Bounty
|
|
47
87
|
* Board host. Display name + avatar ONLY — the host never sends ids, emails,
|
|
@@ -402,6 +442,11 @@ interface BBArcadeSDK {
|
|
|
402
442
|
* 'error' after the same 15s request timeout when no host answers).
|
|
403
443
|
*/
|
|
404
444
|
load(): Promise<string | null>;
|
|
445
|
+
/**
|
|
446
|
+
* localStorage compatibility for hosted builds — the escape hatch for engine
|
|
447
|
+
* exports that can't be rewired to call save()/load(). See BBArcadeStorage.
|
|
448
|
+
*/
|
|
449
|
+
storage: BBArcadeStorage;
|
|
405
450
|
/**
|
|
406
451
|
* The logged-in player's display identity for in-game UI. Resolves
|
|
407
452
|
* { name, avatarUrl } once the host's config handshake completes (or after
|
|
@@ -436,4 +481,4 @@ interface BBArcadeSDK {
|
|
|
436
481
|
version: number;
|
|
437
482
|
}
|
|
438
483
|
|
|
439
|
-
export type { BBArcadeSDK as B, BBArcadeConfig as a, BBArcadeError as b, BBArcadeErrorCode as c, BBArcadeLockToHostOptions as d, BBArcadePlayer as e, BBArcadePrepareRewardedAdOptions as f, BBArcadePreparedRewardedAd as g, BBArcadeRewardedAdOptions as h, BBArcadeRewardedAdPreparation as i, BBArcadeRewardedAdPrepareFailure as j, BBArcadeRewardedAdResult as k, BBArcadeRewardedAdStatus as l, BBArcadeRewardedAdsConfig as m, BBArcadeRewardedBreakOptions as n,
|
|
484
|
+
export type { BBArcadeSDK as B, BBArcadeConfig as a, BBArcadeError as b, BBArcadeErrorCode as c, BBArcadeLockToHostOptions as d, BBArcadePlayer as e, BBArcadePrepareRewardedAdOptions as f, BBArcadePreparedRewardedAd as g, BBArcadeRewardedAdOptions as h, BBArcadeRewardedAdPreparation as i, BBArcadeRewardedAdPrepareFailure as j, BBArcadeRewardedAdResult as k, BBArcadeRewardedAdStatus as l, BBArcadeRewardedAdsConfig as m, BBArcadeRewardedBreakOptions as n, BBArcadeStorage as o, BBArcadeStorageMode as p, BBArcadeMpJoinOptions as q, BBArcadeMpRoom as r, BBArcadeMultiplayer as s, BBArcadeMpPlayer as t, BBArcadeMpResult as u, BBArcadeMpRoomEvents as v };
|
|
@@ -42,6 +42,46 @@ interface BBArcadeRewardedAdsConfig {
|
|
|
42
42
|
interface BBArcadeConfig {
|
|
43
43
|
rewardedAds?: BBArcadeRewardedAdsConfig;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* What is backing `window.localStorage` right now.
|
|
47
|
+
* - `native` — a real Storage works here; the SDK changed nothing.
|
|
48
|
+
* - `cloud` — the shim is installed and syncs to the player's Bounty Board save.
|
|
49
|
+
* - `memory` — the shim is installed but nothing can persist (guest, standalone,
|
|
50
|
+
* off-host); reads and writes work for the session and are then discarded.
|
|
51
|
+
*/
|
|
52
|
+
type BBArcadeStorageMode = 'native' | 'cloud' | 'memory';
|
|
53
|
+
/**
|
|
54
|
+
* localStorage compatibility for hosted builds. Bounty-Board-hosted uploads run
|
|
55
|
+
* in an opaque-origin sandbox where `localStorage` THROWS rather than returning
|
|
56
|
+
* empty, which breaks engine exports (GameMaker `ini_*`, Godot, Unity) that
|
|
57
|
+
* assume synchronous storage. The shim replaces it with a Storage-shaped object
|
|
58
|
+
* backed by the SDK's own cloud save, so those builds persist per player and
|
|
59
|
+
* across devices with no engine changes.
|
|
60
|
+
*
|
|
61
|
+
* The script-tag build installs it automatically at load. Module consumers keep
|
|
62
|
+
* a side-effect-free import and call `install()` themselves before boot.
|
|
63
|
+
*/
|
|
64
|
+
interface BBArcadeStorage {
|
|
65
|
+
/**
|
|
66
|
+
* Install the shim if this origin has no working localStorage, and return the
|
|
67
|
+
* resulting mode. Idempotent, and a no-op when native storage works. Call it
|
|
68
|
+
* BEFORE the engine/game script runs.
|
|
69
|
+
*/
|
|
70
|
+
install(): BBArcadeStorageMode;
|
|
71
|
+
/**
|
|
72
|
+
* Resolves once the cloud read has landed (or settled as unpersistable).
|
|
73
|
+
* `getItem` is synchronous but the cloud read is not, so a game that reads
|
|
74
|
+
* saved progress during boot should await this first.
|
|
75
|
+
*/
|
|
76
|
+
ready(): Promise<BBArcadeStorageMode>;
|
|
77
|
+
/**
|
|
78
|
+
* Force pending writes out now instead of waiting for the debounce. Rejects
|
|
79
|
+
* with the same BBArcadeError codes as save().
|
|
80
|
+
*/
|
|
81
|
+
flush(): Promise<void>;
|
|
82
|
+
/** The current backing mode. */
|
|
83
|
+
readonly mode: BBArcadeStorageMode;
|
|
84
|
+
}
|
|
45
85
|
/**
|
|
46
86
|
* The logged-in player's public display identity, as delivered by the Bounty
|
|
47
87
|
* Board host. Display name + avatar ONLY — the host never sends ids, emails,
|
|
@@ -402,6 +442,11 @@ interface BBArcadeSDK {
|
|
|
402
442
|
* 'error' after the same 15s request timeout when no host answers).
|
|
403
443
|
*/
|
|
404
444
|
load(): Promise<string | null>;
|
|
445
|
+
/**
|
|
446
|
+
* localStorage compatibility for hosted builds — the escape hatch for engine
|
|
447
|
+
* exports that can't be rewired to call save()/load(). See BBArcadeStorage.
|
|
448
|
+
*/
|
|
449
|
+
storage: BBArcadeStorage;
|
|
405
450
|
/**
|
|
406
451
|
* The logged-in player's display identity for in-game UI. Resolves
|
|
407
452
|
* { name, avatarUrl } once the host's config handshake completes (or after
|
|
@@ -436,4 +481,4 @@ interface BBArcadeSDK {
|
|
|
436
481
|
version: number;
|
|
437
482
|
}
|
|
438
483
|
|
|
439
|
-
export type { BBArcadeSDK as B, BBArcadeConfig as a, BBArcadeError as b, BBArcadeErrorCode as c, BBArcadeLockToHostOptions as d, BBArcadePlayer as e, BBArcadePrepareRewardedAdOptions as f, BBArcadePreparedRewardedAd as g, BBArcadeRewardedAdOptions as h, BBArcadeRewardedAdPreparation as i, BBArcadeRewardedAdPrepareFailure as j, BBArcadeRewardedAdResult as k, BBArcadeRewardedAdStatus as l, BBArcadeRewardedAdsConfig as m, BBArcadeRewardedBreakOptions as n,
|
|
484
|
+
export type { BBArcadeSDK as B, BBArcadeConfig as a, BBArcadeError as b, BBArcadeErrorCode as c, BBArcadeLockToHostOptions as d, BBArcadePlayer as e, BBArcadePrepareRewardedAdOptions as f, BBArcadePreparedRewardedAd as g, BBArcadeRewardedAdOptions as h, BBArcadeRewardedAdPreparation as i, BBArcadeRewardedAdPrepareFailure as j, BBArcadeRewardedAdResult as k, BBArcadeRewardedAdStatus as l, BBArcadeRewardedAdsConfig as m, BBArcadeRewardedBreakOptions as n, BBArcadeStorage as o, BBArcadeStorageMode as p, BBArcadeMpJoinOptions as q, BBArcadeMpRoom as r, BBArcadeMultiplayer as s, BBArcadeMpPlayer as t, BBArcadeMpResult as u, BBArcadeMpRoomEvents as v };
|
package/docs/llms.txt
CHANGED
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Human guide: https://www.bountyboard.gg/arcade/sdk
|
|
4
4
|
Package: @bountyboard/arcade-sdk
|
|
5
|
-
Stable npm release: 1.
|
|
6
|
-
Current browser v1/source addition: public quick match (queued for npm 1.2.0)
|
|
5
|
+
Stable npm release: 1.2.0
|
|
7
6
|
Wire protocol: 1
|
|
8
7
|
|
|
9
8
|
This file is the complete integration contract for coding agents integrating an
|
|
10
9
|
HTML5 game. The package TypeScript declarations remain the exact public type
|
|
11
|
-
reference.
|
|
12
|
-
on all three approved rails:
|
|
13
|
-
shared-service
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
external-authority adapter guide is public now and will also join the packaged
|
|
18
|
-
docs in 1.2.0.
|
|
10
|
+
reference. npm 1.2.0, the /arcade-sdk/v1.js browser artifact, and repository
|
|
11
|
+
source all expose the same surface. Rooms open on all three approved rails:
|
|
12
|
+
Bounty shared-service referee modules, Bounty shared-service relay rooms, and
|
|
13
|
+
host-routed registered external authorities. code/create works on every rail;
|
|
14
|
+
match: true works on both Bounty shared-service tiers. The external-authority
|
|
15
|
+
adapter guide ships in the package and is public at the URL above.
|
|
19
16
|
|
|
20
17
|
## Non-negotiable integration rules
|
|
21
18
|
|
|
@@ -29,7 +26,9 @@ docs in 1.2.0.
|
|
|
29
26
|
4. getPlayer() can be null and avatarUrl can be null. The SDK never exposes
|
|
30
27
|
account ids, emails, or roles.
|
|
31
28
|
5. Store all progress in one save(string) blob (about 1 MB). Save at
|
|
32
|
-
checkpoints/game over, not in a frame loop, and catch every rejection.
|
|
29
|
+
checkpoints/game over, not in a frame loop, and catch every rejection. When
|
|
30
|
+
the game is an engine export whose storage layer you cannot rewire, use
|
|
31
|
+
storage.install() instead and let the shim carry localStorage to the cloud.
|
|
33
32
|
6. For new rewarded-ad work, prepare first, enable the game's button only when
|
|
34
33
|
status is ready, call prepared.show() directly from the click/tap handler,
|
|
35
34
|
and grant only when the final status is viewed.
|
|
@@ -38,7 +37,10 @@ docs in 1.2.0.
|
|
|
38
37
|
admission, roster, capacity, host succession, reconnect grace, and public
|
|
39
38
|
message fan-out, but it does not referee game state or outcomes. Catch every
|
|
40
39
|
joinRoom() rejection and keep solo/standalone play available.
|
|
41
|
-
8. lockToHost() is an optional anti-rehosting deterrent, not DRM. Call it before
|
|
40
|
+
8. lockToHost() is an optional anti-rehosting deterrent, not DRM. Call it before
|
|
41
|
+
boot. allow lists the domains the STUDIO hosts the build on, so a build that
|
|
42
|
+
only runs on Bounty Board passes no allow entries (or skips the call);
|
|
43
|
+
play.yourgame.com in every example is a placeholder, never a real value.
|
|
42
44
|
|
|
43
45
|
## Install and distribution
|
|
44
46
|
|
|
@@ -81,9 +83,11 @@ Distribution format does not determine capabilities; the embedding host does.
|
|
|
81
83
|
|
|
82
84
|
- Bounty-hosted upload:
|
|
83
85
|
lifecycle/scores supported; player identity/variants supported; cloud
|
|
84
|
-
save/load available to logged-in players;
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
save/load available to logged-in players; native localStorage/sessionStorage/
|
|
87
|
+
IndexedDB/cookies all THROW (opaque origin), so engine exports need
|
|
88
|
+
storage.install(); ads available to approved games; multiplayer available
|
|
89
|
+
after per-game approval, with relay as the default Bounty shared-service tier
|
|
90
|
+
when no bespoke referee module is registered.
|
|
87
91
|
- Approved external URL embed inside the Bounty Board player:
|
|
88
92
|
lifecycle/scores, identity, variants, and approved multiplayer (including the
|
|
89
93
|
Bounty relay tier) are supported; rewarded ads are unavailable (no payable
|
|
@@ -97,11 +101,19 @@ Distribution format does not determine capabilities; the embedding host does.
|
|
|
97
101
|
Guests are normal. getPlayer resolves null and account-backed calls may reject
|
|
98
102
|
with code unauthenticated.
|
|
99
103
|
|
|
104
|
+
Leaderboards need one thing outside the code: the studio must declare
|
|
105
|
+
Leaderboards for the game in its Arcade submission (or later by editing it).
|
|
106
|
+
Until then the host rejects every posted score and the game sees nothing,
|
|
107
|
+
because submitScore()/gameOver() are fire-and-forget. Optional per-game score
|
|
108
|
+
ceilings live beside that declaration and reject implausible values.
|
|
109
|
+
|
|
100
110
|
## Minimal correct lifecycle
|
|
101
111
|
|
|
102
112
|
import { BBArcade } from '@bountyboard/arcade-sdk';
|
|
103
113
|
|
|
104
|
-
|
|
114
|
+
// Optional, before boot. Bounty Board and localhost are always allowed, so
|
|
115
|
+
// allow lists only domains YOU host on; omit it when Bounty Board hosts the build.
|
|
116
|
+
BBArcade.lockToHost({ allow: ['play.yourgame.com'] }); // placeholder domain
|
|
105
117
|
void BBArcade.init(); // never gate boot on it
|
|
106
118
|
BBArcade.gameLoadingFinished(); // first playable scene ready
|
|
107
119
|
|
|
@@ -114,7 +126,7 @@ For a shared-seed Daily, pass { mode: 'daily' } to submitScore and gameOver.
|
|
|
114
126
|
|
|
115
127
|
## Exact shared types and configuration
|
|
116
128
|
|
|
117
|
-
All rejected SDK promises use a real Error
|
|
129
|
+
All rejected SDK promises use a real Error:
|
|
118
130
|
|
|
119
131
|
type BBArcadeErrorCode =
|
|
120
132
|
| 'unsupported' // no compatible host/capability
|
|
@@ -123,12 +135,6 @@ All rejected SDK promises use a real Error. Stable npm 1.1.0 has this shape:
|
|
|
123
135
|
| 'rejected' // host/authority refused the payload or room
|
|
124
136
|
| 'error'; // timeout, transport, or server failure
|
|
125
137
|
|
|
126
|
-
interface BBArcadeError extends Error {
|
|
127
|
-
code: BBArcadeErrorCode;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
The current browser v1/source build, and npm 1.2.0 when released, add:
|
|
131
|
-
|
|
132
138
|
interface BBArcadeError extends Error {
|
|
133
139
|
code: BBArcadeErrorCode;
|
|
134
140
|
detail?: string; // raw authority reason when available
|
|
@@ -167,6 +173,20 @@ Player and score shapes:
|
|
|
167
173
|
interface BBArcadePlayer { name: string; avatarUrl: string | null }
|
|
168
174
|
// submitScore/gameOver use the inline option shape { mode?: 'daily' }.
|
|
169
175
|
|
|
176
|
+
Storage compatibility shapes:
|
|
177
|
+
|
|
178
|
+
type BBArcadeStorageMode =
|
|
179
|
+
| 'native' // a real Storage works here; the SDK changed nothing
|
|
180
|
+
| 'cloud' // shim installed, syncing to the player's Bounty Board save
|
|
181
|
+
| 'memory'; // shim installed but nothing can persist (guest/standalone)
|
|
182
|
+
|
|
183
|
+
interface BBArcadeStorage {
|
|
184
|
+
install(): BBArcadeStorageMode; // idempotent; no-op when native works
|
|
185
|
+
ready(): Promise<BBArcadeStorageMode>;
|
|
186
|
+
flush(): Promise<void>; // rejects with the save() error codes
|
|
187
|
+
readonly mode: BBArcadeStorageMode;
|
|
188
|
+
}
|
|
189
|
+
|
|
170
190
|
Rewarded-ad options and results:
|
|
171
191
|
|
|
172
192
|
interface BBArcadeRewardedAdOptions {
|
|
@@ -264,9 +284,18 @@ Player data and experiments:
|
|
|
264
284
|
against the same 1 MiB cap the server enforces.
|
|
265
285
|
- load(): Promise<string | null>
|
|
266
286
|
Returns null when no save exists. Rejects like save().
|
|
287
|
+
- storage: BBArcadeStorage
|
|
288
|
+
localStorage compatibility for hosted builds. install(): BBArcadeStorageMode
|
|
289
|
+
replaces a throwing window.localStorage with a Storage-shaped object backed
|
|
290
|
+
by the cloud save, and is a no-op where a real Storage works. ready():
|
|
291
|
+
Promise<BBArcadeStorageMode> resolves once the cloud read has landed.
|
|
292
|
+
flush(): Promise<void> forces the debounced write out now. mode is the
|
|
293
|
+
current BBArcadeStorageMode ('native' | 'cloud' | 'memory'). The script-tag
|
|
294
|
+
build calls install() for you at load; module consumers call it themselves
|
|
295
|
+
before boot.
|
|
267
296
|
- getPlayer(): Promise<{ name: string, avatarUrl: string | null } | null>
|
|
268
297
|
Display identity only; always handle null.
|
|
269
|
-
- onPlayerChange(handler): () => void
|
|
298
|
+
- onPlayerChange(handler): () => void
|
|
270
299
|
Subscribes to CHANGES in the display identity (mid-session login/logout).
|
|
271
300
|
The handler receives the same { name, avatarUrl } | null shape as
|
|
272
301
|
getPlayer() and runs only when the identity actually changes; subscribing
|
|
@@ -307,16 +336,16 @@ ad_break_unavailable, ad_break_timeout, no_rewarded_ad, ad_in_progress,
|
|
|
307
336
|
show_ad_error, direct_user_action_required, before_reward_error, and
|
|
308
337
|
ad_break_error.
|
|
309
338
|
|
|
310
|
-
Multiplayer
|
|
339
|
+
Multiplayer:
|
|
311
340
|
|
|
312
341
|
type BBArcadeMpJoinOptions = {
|
|
313
342
|
code?: string;
|
|
314
343
|
create?: boolean;
|
|
315
|
-
match?: boolean;
|
|
344
|
+
match?: boolean; // public quick match; Bounty shared-service tiers only
|
|
316
345
|
joinData?: Readonly<Record<string, unknown>>;
|
|
317
346
|
roomUrl?: string; // local development override; provide with ticket
|
|
318
347
|
ticket?: string; // local development override; provide with roomUrl
|
|
319
|
-
timeoutMs?: number; //
|
|
348
|
+
timeoutMs?: number; // welcome timeout override, clamped 1000-60000 ms
|
|
320
349
|
};
|
|
321
350
|
|
|
322
351
|
interface BBArcadeMpPlayer {
|
|
@@ -350,7 +379,7 @@ Multiplayer (current browser v1/source shape; stable npm 1.1.0 omits match):
|
|
|
350
379
|
players: BBArcadeMpPlayer[];
|
|
351
380
|
state: unknown;
|
|
352
381
|
connected: boolean;
|
|
353
|
-
latencyMs: number | null; //
|
|
382
|
+
latencyMs: number | null; // join-handshake estimate, refreshed on reconnect
|
|
354
383
|
send(input: unknown): void;
|
|
355
384
|
trySend(input: unknown): boolean;
|
|
356
385
|
on<K extends keyof BBArcadeMpRoomEvents>(
|
|
@@ -416,10 +445,9 @@ as unknown.
|
|
|
416
445
|
- leave intentionally closes the Room and permanently disables reconnect for it.
|
|
417
446
|
A welcome that arrives after leave() (e.g. racing a reconnect) is ignored and
|
|
418
447
|
never flips the room back to connected.
|
|
419
|
-
- A pre-welcome room error rejects joinRoom with BBArcadeError.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
error.code. An error after welcome emits room.on('error', { code }).
|
|
448
|
+
- A pre-welcome room error rejects joinRoom with BBArcadeError. Raw reasons such
|
|
449
|
+
as room_full/room_over are carried in error.detail when available.
|
|
450
|
+
An error after welcome emits room.on('error', { code }).
|
|
423
451
|
Connection loss emits connection/close events and may start automatic
|
|
424
452
|
reconnect.
|
|
425
453
|
|
|
@@ -429,9 +457,10 @@ version field on multiplayer WebSocket frames.
|
|
|
429
457
|
|
|
430
458
|
## Cloud-save recipe
|
|
431
459
|
|
|
432
|
-
Hosted uploads have an opaque origin
|
|
433
|
-
|
|
434
|
-
|
|
460
|
+
Hosted uploads have an opaque origin where a real localStorage is not merely
|
|
461
|
+
empty — reading window.localStorage THROWS a SecurityError, as do
|
|
462
|
+
sessionStorage, IndexedDB, and document.cookie. SDK save is the primary store
|
|
463
|
+
there. URL embeds and standalone builds need their own same-origin fallback.
|
|
435
464
|
|
|
436
465
|
const blob = JSON.stringify(progress);
|
|
437
466
|
try {
|
|
@@ -449,6 +478,59 @@ fallback.
|
|
|
449
478
|
|
|
450
479
|
Do not assume load() resolves null for a guest; it can reject unauthenticated.
|
|
451
480
|
|
|
481
|
+
## localStorage shim for engine exports
|
|
482
|
+
|
|
483
|
+
Prefer save()/load() when you control the source. The shim exists for engine
|
|
484
|
+
runtimes whose storage layer cannot be rewired without patching engine
|
|
485
|
+
internals: GameMaker HTML5 (ini_open/ini_write_*/game_save all sit on
|
|
486
|
+
localStorage), Godot, Unity, and Construct.
|
|
487
|
+
|
|
488
|
+
With the script tag, nothing is required beyond load order — the SDK installs
|
|
489
|
+
the shim at load and window.localStorage starts working:
|
|
490
|
+
|
|
491
|
+
<script src="https://www.bountyboard.gg/arcade-sdk/v1.js"></script>
|
|
492
|
+
<script src="html5game/YourGame.js"></script>
|
|
493
|
+
|
|
494
|
+
Module consumers keep a side-effect-free import and install it themselves,
|
|
495
|
+
before any engine code runs:
|
|
496
|
+
|
|
497
|
+
import { BBArcade } from '@bountyboard/arcade-sdk';
|
|
498
|
+
BBArcade.storage.install();
|
|
499
|
+
|
|
500
|
+
install() is idempotent and a no-op wherever a real Storage works, so standalone
|
|
501
|
+
play and URL embeds are untouched. Behavior once installed:
|
|
502
|
+
|
|
503
|
+
- Reads and writes are synchronous and in-memory; the map is persisted to the
|
|
504
|
+
player's cloud save on a short debounce, on pagehide, and on flush().
|
|
505
|
+
- Progress becomes per-player and cross-device, not per-browser.
|
|
506
|
+
- setItem throws QuotaExceededError when the write would exceed the ~1 MB save
|
|
507
|
+
cap, matching a real Storage.
|
|
508
|
+
- sessionStorage is shimmed too, but memory-only — it is per-session by
|
|
509
|
+
definition and is never synced.
|
|
510
|
+
- Guests and standalone play settle in 'memory' mode: storage still works for
|
|
511
|
+
the session, it is just never persisted. Nothing rejects; nothing hangs.
|
|
512
|
+
|
|
513
|
+
The one real limitation is that getItem is synchronous while the cloud read is
|
|
514
|
+
not. Gate boot-time reads on ready():
|
|
515
|
+
|
|
516
|
+
await BBArcade.storage.ready(); // 'cloud' | 'memory' | 'native'
|
|
517
|
+
startGame();
|
|
518
|
+
|
|
519
|
+
Writes made before hydration are kept and merged with the cloud read (the
|
|
520
|
+
game's own write wins, and a removeItem is not resurrected), and nothing is
|
|
521
|
+
pushed to the server until that read has SUCCEEDED. A failed read is retried on
|
|
522
|
+
the next write, and a write that still cannot confirm what the player had is
|
|
523
|
+
refused (rejecting with code error) rather than allowed to overwrite it blind —
|
|
524
|
+
so neither a fresh-start write at boot nor a transport blip can wipe an existing
|
|
525
|
+
save. A game that reads
|
|
526
|
+
at boot WITHOUT awaiting ready() may still see an empty map on the first frame.
|
|
527
|
+
|
|
528
|
+
save()/load() keep working alongside the shim: the player has one save slot, so
|
|
529
|
+
with the shim active both halves share it inside a tagged envelope. load()
|
|
530
|
+
unwraps it and returns only what save() wrote. A raw blob written before the
|
|
531
|
+
shim existed is read back untouched, so turning the shim on never orphans a
|
|
532
|
+
save.
|
|
533
|
+
|
|
452
534
|
## Safe rewarded-ad recipe
|
|
453
535
|
|
|
454
536
|
Prepare at the natural break. Keep the game-owned button disabled until ready.
|
|
@@ -484,7 +566,7 @@ is normal even though test mode is enabled automatically.
|
|
|
484
566
|
|
|
485
567
|
try {
|
|
486
568
|
const room = await joinRoom({
|
|
487
|
-
create: true, // or code: 'ABCD'
|
|
569
|
+
create: true, // or code: 'ABCD', or match: true for public quick match
|
|
488
570
|
// Only a relay founder uses roomSize; every joinData field is untrusted.
|
|
489
571
|
joinData: { roomSize: 4, avatar: 'golem' },
|
|
490
572
|
});
|
|
@@ -603,9 +685,8 @@ described above; its 1 Hz snapshot contains metadata only.
|
|
|
603
685
|
can roll before reaching the room's capacity. A matched room.code is also an
|
|
604
686
|
invite code.
|
|
605
687
|
- A lost last-seat/ended-room race triggers at most 2 re-matchmaking retries
|
|
606
|
-
(3 total attempts).
|
|
607
|
-
|
|
608
|
-
last attempt returned.
|
|
688
|
+
(3 total attempts). Final rejection has code rejected and detail room_full or
|
|
689
|
+
room_over, whichever the last attempt returned.
|
|
609
690
|
- When a finite Bounty referee module ends, it broadcasts match_end once, closes
|
|
610
691
|
its sockets, and subsequent joins to that room reject with room_over. This
|
|
611
692
|
finite-close rule does not apply to the endless relay tier.
|
|
@@ -618,11 +699,10 @@ described above; its 1 Hz snapshot contains metadata only.
|
|
|
618
699
|
terminal generic admission rejection; the public SDK does not expose the
|
|
619
700
|
WebSocket close code/reason, so do not label every rejected error as this case.
|
|
620
701
|
|
|
621
|
-
Public quick match is
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
the Bounty shared-service defaults, and an external authority may be endless.
|
|
702
|
+
Public quick match is unavailable on registered external authorities, which
|
|
703
|
+
own their capacity, lobby lifecycle, and any matchmaking. Their reviewed
|
|
704
|
+
limits can differ from the Bounty shared-service defaults, and an external
|
|
705
|
+
authority may be endless.
|
|
626
706
|
|
|
627
707
|
joinRoom rejection map:
|
|
628
708
|
|
|
@@ -634,10 +714,9 @@ joinRoom rejection map:
|
|
|
634
714
|
- error: malformed host grant, postMessage/socket transport failure, server
|
|
635
715
|
failure, or the 10-second welcome timeout.
|
|
636
716
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
retry or show “already playing elsewhere” without a distinct detail.
|
|
717
|
+
Use error.detail only when it is a specific documented value such as room_full
|
|
718
|
+
or room_over. Generic rejected has multiple causes; do not blind retry or show
|
|
719
|
+
“already playing elsewhere” without a distinct detail.
|
|
641
720
|
|
|
642
721
|
Multiplayer is curated per game slug. After approval, Bounty Board assigns one
|
|
643
722
|
of three rails: (a) the built-in casual relay, (b) a bespoke Bounty referee
|
|
@@ -677,7 +756,7 @@ https://www.bountyboard.gg/arcade/sdk/external-authoritative-servers.txt
|
|
|
677
756
|
- Test initial room.state/players rendering before the first later event.
|
|
678
757
|
- Test multiplayer reconnect/grace, trySend false, tier-appropriate events/state,
|
|
679
758
|
and leave.
|
|
680
|
-
- Test create/code, and match
|
|
759
|
+
- Test create/code, and match on the Bounty shared-service tiers.
|
|
681
760
|
- For relay, test roomSize default/range, public fan-out (including sender), host
|
|
682
761
|
succession after grace, byte/rate drops, state.dropped, and absence of end.
|
|
683
762
|
- For referee/external rooms, test viewer-safe snapshots, authoritative results,
|
|
@@ -690,6 +769,13 @@ https://www.bountyboard.gg/arcade/sdk/external-authoritative-servers.txt
|
|
|
690
769
|
fire-and-forget and give every promise feature a standalone outcome.
|
|
691
770
|
- save/load unsupported -> not a Bounty-hosted upload -> use own same-origin store.
|
|
692
771
|
- save/load unauthenticated -> guest player -> continue with defaults/fallback.
|
|
772
|
+
- SecurityError touching localStorage/sessionStorage/document.cookie -> hosted
|
|
773
|
+
builds run on an opaque origin -> use save()/load(), or storage.install() for
|
|
774
|
+
an engine export that cannot be rewired.
|
|
775
|
+
- Shimmed storage reads empty at boot -> the cloud read had not landed yet ->
|
|
776
|
+
await BBArcade.storage.ready() before restoring progress.
|
|
777
|
+
- storage.mode is 'memory' -> guest, standalone, or off-host -> storage works
|
|
778
|
+
for the session but is never persisted; keep first-run defaults sane.
|
|
693
779
|
- direct_user_action_required -> show() lost browser activation -> make show()
|
|
694
780
|
the first line of the click/tap handler.
|
|
695
781
|
- host_disabled/unavailable -> host or inventory is not ready -> keep the normal
|
|
@@ -710,6 +796,5 @@ https://www.bountyboard.gg/arcade/sdk/external-authoritative-servers.txt
|
|
|
710
796
|
- Relay room guide: https://www.bountyboard.gg/arcade/sdk/relay-rooms.txt
|
|
711
797
|
- Standalone types: https://www.bountyboard.gg/arcade-sdk.d.ts
|
|
712
798
|
- Submit a game: https://www.bountyboard.gg/arcade/submit
|
|
713
|
-
- Package README, changelog, AGENTS.md,
|
|
714
|
-
|
|
715
|
-
in 1.2.0.
|
|
799
|
+
- Package README, changelog, AGENTS.md, design playbook, relay-room guide, and
|
|
800
|
+
external-authority guide all ship in the npm package.
|
package/package.json
CHANGED