@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 +75 -0
- package/LICENSE +21 -0
- package/README.md +89 -0
- package/dist/chunk-WCGBR3MI.js +805 -0
- package/dist/chunk-WCGBR3MI.js.map +1 -0
- package/dist/global-sdk.d.ts +342 -0
- package/dist/global.js +1044 -0
- package/dist/index.cjs +832 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/multiplayer.cjs +1070 -0
- package/dist/multiplayer.cjs.map +1 -0
- package/dist/multiplayer.d.cts +7 -0
- package/dist/multiplayer.d.ts +7 -0
- package/dist/multiplayer.js +248 -0
- package/dist/multiplayer.js.map +1 -0
- package/dist/types-B6xH-lDi.d.cts +328 -0
- package/dist/types-B6xH-lDi.d.ts +328 -0
- package/docs/game-design-playbook.md +73 -0
- package/package.json +59 -0
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).
|