@bountyboard/arcade-sdk 1.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/AGENTS.md ADDED
@@ -0,0 +1,75 @@
1
+ # Arcade SDK integration guide for coding agents
2
+
3
+ You are integrating a game with the Bounty Board Arcade SDK (`@bountyboard/arcade-sdk`, or the
4
+ script tag at `https://www.bountyboard.gg/arcade-sdk/v1.js`). This file is the canonical set of
5
+ rules. The API reference lives in the package types and at
6
+ https://www.bountyboard.gg/arcade/sdk/llms.txt — this file is about using it CORRECTLY.
7
+
8
+ ## Golden rules (breaking these ships bugs or gets a game flagged)
9
+
10
+ 1. **The SDK must never be load-bearing.** Every feature has an offline/standalone outcome:
11
+ fire-and-forget calls no-op, `save`/`load` reject with `code: 'unsupported'`, `getPlayer`
12
+ resolves `null`, `getVariant` resolves its alphabetical control, `joinRoom` rejects
13
+ `'unsupported'`. The game must run perfectly with all of those outcomes at once.
14
+ 2. **Rewarded ads**: only ever triggered by an explicit player gesture on YOUR UI ("Watch ad to
15
+ revive"). Grant the reward if and only if `rewardedBreak(...)` resolves exactly `true`.
16
+ Pause gameplay and audio in `onStart`. Never loop or auto-retry a dismissed ad.
17
+ 3. **Scores are integers and `gameOver` fires exactly once per run.** Don't submit
18
+ post-game-over corrections; the server enforces per-game plausibility caps and rejects
19
+ implausible values. Call `submitScore` freely during play — the host throttles.
20
+ 4. **Always handle `getPlayer() === null`** (guests, standalone, off-host). The SDK never
21
+ provides ids, emails, or roles — do not design features that need them.
22
+ 5. **One save blob.** Serialize the whole progress object into a single `save(string)` (~1MB
23
+ cap). Save on checkpoints/game-over, never per frame. Handle all five rejection codes; keep
24
+ localStorage as the standalone fallback (hosted builds have no localStorage — the sandbox is
25
+ opaque-origin — so the SDK save IS primary there).
26
+ 6. **`lockToHost()` before boot** when the studio wants anti-theft, with their own domains in
27
+ `allow`. It's a deterrent, not DRM — never present it as more.
28
+ 7. **Multiplayer: clients send inputs, never state.** The room server simulates and validates;
29
+ design the game so all authority lives server-side. Don't trust or display any value another
30
+ client sent directly.
31
+
32
+ ## Correct lifecycle order
33
+
34
+ ```
35
+ lockToHost() → init() → [getPlayer()] → gameLoadingFinished()
36
+ → per run: gameplayStart() → submitScore()* → gameplayStop() → gameOver()
37
+ → save() on checkpoint/game-over
38
+ ```
39
+
40
+ `gameplayStart`/`gameplayStop` bracket ACTIVE play only (not menus). They drive playtime
41
+ analytics and feed ranking, so missing `gameplayStop` on pause inflates numbers and will look
42
+ anomalous. WebXR games must call `xrSessionStart()`/`xrSessionEnd()` around immersive sessions
43
+ or their playtime flatlines while the headset hides the page.
44
+
45
+ ## Module vs script tag
46
+
47
+ - Build step → `import { BBArcade } from '@bountyboard/arcade-sdk'` (typed; no global installed;
48
+ SSR-safe import).
49
+ - No build step → script tag; global `window.BBArcade`; standalone typings via
50
+ `/// <reference path="./arcade-sdk.d.ts" />` (download from /arcade-sdk.d.ts).
51
+ - Never mix both in one page. Both are built from the same source and behave identically.
52
+
53
+ ## A/B experiments
54
+
55
+ `getVariant(key, variants)` is deterministic per player+game+key, even split. The
56
+ alphabetically-first variant doubles as the control/fallback (standalone play, errors), so make
57
+ the safe/default experience the alphabetically-first name. Don't re-randomize client-side.
58
+
59
+ ## Verifying an integration
60
+
61
+ - Standalone smoke test: open the build from `file:` or a bare local server with NO host page.
62
+ It must boot, play, and never hang on an SDK promise.
63
+ - Mock-host test: embed the game in an iframe and answer its postMessage traffic (`source:
64
+ 'bb-arcade'`) with a scripted parent — reply to `init`/`ready` with a `bb-arcade-host` config
65
+ message, answer `save`/`load` requests by `requestId`. This is how Bounty Board's own
66
+ Playwright harnesses verify games; it exercises the real protocol without the real site.
67
+ - Ads: on localhost the SDK auto-enables Google's test-ads mode; `rewardedBreak` resolving
68
+ `false`/`'unavailable'` locally is normal — verify the grant-only-on-true path both ways.
69
+ - Multiplayer: run the room server locally (`wrangler dev` with `DEV_ALLOW_UNSIGNED=1`) and pass
70
+ `{ roomUrl, ticket }` overrides to `joinRoom` for development.
71
+
72
+ ## Design intent (read docs/game-design-playbook.md before building a NEW game)
73
+
74
+ Leaderboard shape, daily-seed modes, session length, and ad-placement etiquette are design
75
+ decisions, not integration details. The playbook covers what actually performs on Bounty Board.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bounty Board
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # @bountyboard/arcade-sdk
2
+
3
+ Connect your HTML5 game to the [Bounty Board arcade](https://www.bountyboard.gg/arcade):
4
+ global leaderboards, cross-device cloud saves, player identity, A/B variants, rewarded-ad
5
+ revenue (90% studio share), site-lock anti-theft, and authoritative multiplayer rooms.
6
+ Zero dependencies. Every call is optional and safe standalone — off Bounty Board the SDK
7
+ quietly does nothing, so the same build runs anywhere.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npm install @bountyboard/arcade-sdk
13
+ ```
14
+
15
+ ```ts
16
+ import { BBArcade } from '@bountyboard/arcade-sdk';
17
+
18
+ BBArcade.lockToHost(); // optional: refuse to run on scrape-and-reupload sites
19
+ BBArcade.init(); // handshake with the host (fire-and-forget is fine)
20
+ BBArcade.gameLoadingFinished(); // the player can press start
21
+ BBArcade.gameplayStart(); // a run began
22
+ BBArcade.submitScore(score); // as the score changes (host throttles)
23
+ BBArcade.gameOver(finalScore); // once per run — feeds the leaderboards
24
+ ```
25
+
26
+ Full TypeScript definitions are included. Importing the module never installs a
27
+ `window.BBArcade` global and is SSR-safe; if your setup wants the global, use
28
+ `import '@bountyboard/arcade-sdk/global'`.
29
+
30
+ No build step? Use the script tag instead — same code, same protocol:
31
+
32
+ ```html
33
+ <script src="https://www.bountyboard.gg/arcade-sdk/v1.js"></script>
34
+ ```
35
+
36
+ with standalone typings at [`/arcade-sdk.d.ts`](https://www.bountyboard.gg/arcade-sdk.d.ts).
37
+
38
+ ## The surface in one look
39
+
40
+ | Call | What it does |
41
+ | ---------------------------------------- | --------------------------------------------------------------------------- |
42
+ | `lockToHost({ allow? , signed? })` | Block scraped re-hosts of your build (deterrent, not DRM) |
43
+ | `init(options?)` / `configure(options?)` | Configure modules (rewarded ads), announce to the host |
44
+ | `ready()` / `gameLoadingFinished()` | Loading finished, player can start |
45
+ | `gameplayStart()` / `gameplayStop()` | Bracket active play (playtime + feed ranking) |
46
+ | `submitScore(n, { mode?: 'daily' })` | Live integer score; server enforces plausibility caps |
47
+ | `gameOver(n, { mode?: 'daily' })` | Final score, once per run |
48
+ | `save(blob)` / `load()` | 1MB cloud save per player (hosted builds; always catch rejections) |
49
+ | `getPlayer()` | `{ name, avatarUrl }` or `null` — display identity only, always handle null |
50
+ | `getVariant(key, variants)` | Deterministic A/B split; alphabetical first variant = control |
51
+ | `rewardedBreak(opts)` | Rewarded ad; grant only when it resolves `true` |
52
+ | `xrSessionStart()` / `xrSessionEnd()` | Keep WebXR headset time counting as playtime |
53
+ | `multiplayer.joinRoom(...)` | Authoritative multiplayer rooms — see below |
54
+
55
+ Rejections from `save()`/`load()` are real `Error`s carrying a typed machine-readable
56
+ `code`: `'unsupported' | 'unauthenticated' | 'too_large' | 'rejected' | 'error'`.
57
+
58
+ ## Multiplayer
59
+
60
+ ```ts
61
+ import { joinRoom } from '@bountyboard/arcade-sdk/multiplayer';
62
+
63
+ const room = await joinRoom({ create: true }); // or { code: 'ABCD' }
64
+ room.on('snapshot', ({ state }) => render(state)); // server-authoritative state
65
+ room.on('end', ({ results }) => showResults(results));
66
+ room.send({ move: dir }); // inputs only — the server simulates
67
+ ```
68
+
69
+ Rooms run on Bounty Board's authoritative room servers: clients send inputs, the server
70
+ validates and simulates, and each player receives only the state they're allowed to see
71
+ (your hiders stay hidden). Auth is a short-lived signed ticket the SDK obtains from the
72
+ host automatically; off Bounty Board `joinRoom` rejects with `code: 'unsupported'`.
73
+
74
+ Multiplayer is currently a curated integration: Bounty Board must add and deploy a
75
+ server-side game module for your slug before `joinRoom()` can accept players. The
76
+ initial reference module is `hide-and-seek`; arbitrary games do not become
77
+ authoritative multiplayer games merely by importing the client SDK.
78
+
79
+ ## Docs
80
+
81
+ - Human docs: https://www.bountyboard.gg/arcade/sdk
82
+ - Agent-readable reference: https://www.bountyboard.gg/arcade/sdk/llms.txt
83
+ - Integration best practices for coding agents: [`AGENTS.md`](./AGENTS.md)
84
+ - What makes a game perform on Bounty Board: [`docs/game-design-playbook.md`](./docs/game-design-playbook.md)
85
+ - Submit your game: https://www.bountyboard.gg/arcade/submit
86
+
87
+ ## License
88
+
89
+ MIT. See [`LICENSE`](./LICENSE).