ball2d 0.1.1 → 0.2.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.
Files changed (59) hide show
  1. package/LICENSE +42 -0
  2. package/README.md +184 -138
  3. package/dist/browser.js +1790 -1251
  4. package/dist/core.wasm +0 -0
  5. package/dist/node.js +104 -43358
  6. package/dist/stadiums/big.hbs +515 -85
  7. package/dist/stadiums/big_easy.hbs +515 -85
  8. package/dist/stadiums/big_hockey.hbs +612 -87
  9. package/dist/stadiums/big_rounded.hbs +611 -100
  10. package/dist/stadiums/classic.hbs +515 -85
  11. package/dist/stadiums/easy.hbs +515 -85
  12. package/dist/stadiums/hockey.hbs +612 -87
  13. package/dist/stadiums/huge.hbs +515 -85
  14. package/dist/stadiums/provenance.json +24 -22
  15. package/dist/stadiums/rounded.hbs +611 -100
  16. package/dist/stadiums/small.hbs +515 -85
  17. package/dist/types/announcement.d.ts +1 -0
  18. package/dist/types/browser.d.ts +10 -0
  19. package/dist/types/config.d.ts +9 -0
  20. package/dist/types/disc.d.ts +4 -0
  21. package/dist/types/{core/match-state.d.ts → match-state.d.ts} +2 -0
  22. package/dist/types/node.d.ts +15 -0
  23. package/dist/types/{core/host-types.d.ts → player.d.ts} +1 -1
  24. package/dist/types/{core/replay-types.d.ts → replay.d.ts} +3 -3
  25. package/dist/types/{sdk/types.d.ts → room.d.ts} +6 -4
  26. package/dist/types/stadium.d.ts +5 -0
  27. package/dist/types/team.d.ts +6 -0
  28. package/package.json +28 -6
  29. package/dist/types/client/announcement.d.ts +0 -10
  30. package/dist/types/client/connection-stats.d.ts +0 -11
  31. package/dist/types/client/headless.d.ts +0 -130
  32. package/dist/types/client/host-config.d.ts +0 -11
  33. package/dist/types/client/host-runtime.d.ts +0 -10
  34. package/dist/types/client/network-runtime.d.ts +0 -8
  35. package/dist/types/client/network.d.ts +0 -91
  36. package/dist/types/client/stadium-library.d.ts +0 -4
  37. package/dist/types/client/startup-scope.d.ts +0 -6
  38. package/dist/types/client/traffic.d.ts +0 -9
  39. package/dist/types/core/avatar.d.ts +0 -2
  40. package/dist/types/core/build-id.d.ts +0 -2
  41. package/dist/types/core/control.d.ts +0 -5
  42. package/dist/types/core/disc-properties.d.ts +0 -7
  43. package/dist/types/core/engine.d.ts +0 -68
  44. package/dist/types/core/protocol.d.ts +0 -24
  45. package/dist/types/core/replay-codec.d.ts +0 -6
  46. package/dist/types/core/replay-wire.d.ts +0 -5
  47. package/dist/types/core/replay.d.ts +0 -48
  48. package/dist/types/core/stadium.d.ts +0 -91
  49. package/dist/types/core/team-colors.d.ts +0 -8
  50. package/dist/types/sdk/browser.d.ts +0 -11
  51. package/dist/types/sdk/command-queue.d.ts +0 -11
  52. package/dist/types/sdk/config.d.ts +0 -6
  53. package/dist/types/sdk/node-transport.d.ts +0 -4
  54. package/dist/types/sdk/node.d.ts +0 -18
  55. package/dist/types/sdk/replay.d.ts +0 -4
  56. package/dist/types/sdk/room.d.ts +0 -7
  57. package/dist/types/sdk/stadium.d.ts +0 -9
  58. package/dist/types/shared/room-link.d.ts +0 -6
  59. /package/dist/types/{sdk/create-options.d.ts → creation.d.ts} +0 -0
package/LICENSE ADDED
@@ -0,0 +1,42 @@
1
+ Ball2D SDK License, version 1.0
2
+ Copyright (c) 2026 Fillbyte. All rights reserved.
3
+
4
+ 1. Permission
5
+ Fillbyte grants you a non-exclusive, worldwide, royalty-free license to download,
6
+ install and run the unmodified Ball2D SDK to develop and operate integrations
7
+ with the official Ball2D service, including commercial integrations. You may
8
+ copy the SDK into your deployment artifacts for that purpose. You may use, copy
9
+ and adapt the public type declarations, documentation and examples to build your
10
+ integration. Retain this license and the applicable third-party notices in copies
11
+ of the SDK that you distribute with your integration.
12
+
13
+ 2. Service access
14
+ Native room hosting requires a valid Ball2D API key and compliance with the
15
+ service's authorization, scope, rate and concurrent-session limits. This license
16
+ does not grant unlimited service access or permission to bypass those controls.
17
+ You are responsible for protecting your credentials and your integration's data.
18
+
19
+ 3. Reserved rights
20
+ The game implementation remains proprietary. No right to its private source code,
21
+ trademarks or branding is granted. Except for the permission above, you may not
22
+ redistribute a modified Ball2D runtime or offer the SDK as an independent competing
23
+ service without Fillbyte's written permission. This is a proprietary SDK license,
24
+ not an open-source license. Rights that cannot lawfully be restricted remain
25
+ unaffected.
26
+
27
+ 4. Third-party components
28
+ Components identified in dist/licenses retain their own licenses, which govern
29
+ those components and take precedence over conflicting restrictions in this
30
+ license. No ownership of those components is claimed by Fillbyte.
31
+
32
+ 5. Warranty and liability
33
+ To the extent permitted by applicable law, the SDK is provided "AS IS", without
34
+ warranty of any kind. Fillbyte is not liable for damages arising from use of or
35
+ inability to use the SDK. Nothing excludes liability that cannot lawfully be
36
+ excluded. No uptime, compatibility or fitness guarantee is implied.
37
+
38
+ 6. Termination
39
+ If you materially violate this license, the permissions granted by this license
40
+ terminate. Stop using and distributing the SDK under those permissions. The
41
+ third-party licenses remain independent. Separately agreed written terms with
42
+ Fillbyte take precedence where they expressly address the same subject.
package/README.md CHANGED
@@ -1,155 +1,201 @@
1
1
  # Ball2D SDK
2
2
 
3
- ```ts
4
- import { createRoom, type RoomConfig } from 'ball2d';
3
+ Host a Ball2D football room on your own server and control it through a typed
4
+ JavaScript API. The host runs the simulation; players connect directly over
5
+ WebRTC. Ball2D provides authorization, room discovery and signaling.
5
6
 
6
- const config: RoomConfig = { roomName: 'My room', noPlayer: true };
7
- const room = await createRoom(config);
8
- room.onPlayerJoin = (player) => room.setPlayerTeam(player.id, 1);
9
- await room.startGame();
10
- // When the host is finished:
11
- // room.close();
12
- ```
7
+ **Release: 0.2.0.** The matching production service supports API-key-authorized
8
+ Node.js hosting, account quotas and key revocation. This release has passed an
9
+ authenticated production acceptance run with a real browser player.
13
10
 
14
- This initial entry runs in a browser on an origin serving the matching Ball2D
15
- Worker API, WASM and stadium assets. The separate experimental `ball2d/node` entry provides native Node hosting. The
16
- browser entry does not provide remote-origin hosting or an isolated plugin runtime.
17
- Importing it does not register a window global or allocate a room. Creating a
18
- room explicitly starts the existing browser host and its network lifecycle.
19
-
20
- `RoomConfig`, `Room`, `HostPlayer`, `HostScores` and `HostDiscProperties` are exported
21
- types. `readReplay` reads Ball2D recordings.
22
-
23
- State-changing SDK commands return Promises and run in call order. A synchronous
24
- getter immediately after a command still observes the earlier state; await the
25
- command before reading its result. Inputs are copied at call time. Up to 256
26
- commands may wait; overflow rejects without dropping accepted work. Live command
27
- errors also reach `onError`; cancellation and calls after closure only reject their
28
- Promises. `close()` immediately rejects pending SDK work and closes the
29
- host; it does not undo operations already applied remotely. Getters and recording
30
- start/stop remain synchronous. Chat filters must synchronously return false.
31
- Callbacks receive the SDK facade as `this`; engine/network internals are not
32
- part of that facade. This is an API boundary, not a sandbox for untrusted code.
33
-
34
- `createRoom()` accepts omitted options. Defaults are a private room named
35
- `Headless Room`, capacity 12, and an admin host player named `Host`. Set
36
- `noPlayer: true` for an unattended playerless room and `public: true` to list it.
37
- Integer capacity is clamped to 2–30; malformed numeric or privacy values reject
38
- before engine/network allocation. Omitted/null optional default fields resolve
39
- to defaults. The standalone `/headless` and `/headless.html` pages have been
40
- retired; import the SDK instead. No `window.Ball2D` global is registered.
41
-
42
- Version 0.1.0 is the first functional SDK release. For native hosting use Node.js
43
- 24 and the `ball2d/node` entry below. This is an early API; the native transport
44
- remains experimental and the acceptance limits below still apply.
45
-
46
- Validate a stadium before allocating a room:
47
-
48
- ```ts
49
- import { validateStadium } from 'ball2d';
11
+ Repository maintainers: [local setup](https://github.com/fillbyte/ball2d/blob/main/docs/DEVELOPMENT.md) · [release procedure](https://github.com/fillbyte/ball2d/blob/main/docs/RELEASING.md) · [changelog](https://github.com/fillbyte/ball2d/blob/main/CHANGELOG.md).
50
12
 
51
- const report = validateStadium('{name:"Training", canBeStored:false}');
52
- console.log(report.name, report.canBeStored, report.warnings);
13
+ ## Node.js quick start
14
+
15
+ Use Node.js 24 or newer. The verified native runtime is Node.js 24.19.0 on macOS
16
+ arm64; other systems need independent acceptance. Native transport is experimental.
17
+
18
+ ```sh
19
+ npm install ball2d@0.2.0
53
20
  ```
54
21
 
55
- `validateStadium(source)` is synchronous and works without browser globals,
56
- network requests or a WASM instance. It uses the same parser and limits as room
57
- creation. Malformed/unsupported input throws; ignored field names become bounded
58
- warnings. The returned `StadiumValidation` and warning array are readonly and
59
- frozen. No geometry or internal engine objects are exposed. Passing validation
60
- means Ball2D can parse the source; it does not guarantee every gameplay outcome.
61
- Use the separate `ball2d/node` entry for native room hosting.
62
-
63
- `room.signal` is a readonly `AbortSignal` that aborts once when host closure
64
- begins, including a fatal transport closure. Use it to cancel your own room-bound
65
- work. It signals cancellation, not completion of every cleanup operation. Pending
66
- SDK commands are cancelled from the same signal; cancellation never reenters the
67
- room's error callback. A terminal transport error can still be reported once.
68
-
69
- SDK `setScoreLimit` and `setTimeLimit` clamp integer arguments to 0–99 (goals
70
- and minutes respectively); zero disables that limit. Await them before reading
71
- updated state. Active-match calls remain no-ops. Non-integer or non-numeric
72
- values reject when settings are editable. These commands use the same queued
73
- contract in browser and native SDK hosts.
74
-
75
- ## Experimental native Node host
76
-
77
- ```ts
78
- import { createRoom, type NodeRoom } from 'ball2d/node';
79
-
80
- const room: NodeRoom = await createRoom({
81
- serviceOrigin: 'http://127.0.0.1:8787', // A matching Ball2D deployment
82
- roomName: 'Native room',
22
+ Create an API key through your Ball2D account.
23
+ Supply it as `BALL2D_API_KEY` through your server's secret manager or environment;
24
+ never commit it or include it in a browser bundle.
25
+
26
+ ```js
27
+ import { createRoom } from 'ball2d/node';
28
+
29
+ const apiKey = process.env.BALL2D_API_KEY;
30
+ if (!apiKey) throw new Error('Set BALL2D_API_KEY');
31
+
32
+ const room = await createRoom({
33
+ apiKey,
34
+ roomName: 'Evening football',
83
35
  noPlayer: true,
36
+ maxPlayers: 16,
37
+ public: true,
84
38
  });
39
+
40
+ room.onError = (error) => console.error('Room error:', error.message);
85
41
  room.onPlayerJoin = (player) => room.setPlayerTeam(player.id, 1);
86
42
  await room.setDefaultStadium('Classic');
43
+ await room.setScoreLimit(3);
87
44
  await room.startGame();
88
- // Later:
89
- room.close();
90
- await room.closed; // Native peer cleanup finished; rejects on cleanup failure.
45
+ console.log(room.roomLink);
46
+
47
+ async function shutdown() {
48
+ room.close();
49
+ await room.closed;
50
+ }
51
+ process.once('SIGINT', () => void shutdown().catch(console.error));
52
+ process.once('SIGTERM', () => void shutdown().catch(console.error));
91
53
  ```
92
54
 
93
- Verified on Node 24.19.0 and Bun 1.4.2 on macOS arm64 with local Worker signaling
94
- and real Chrome guests. Both use the same `ball2d/node` entry, runtime WebSocket
95
- and bundled, patched werift 0.24.4; no
96
- browser host, postinstall patching or external transport dependency is required.
97
- WASM and default stadium files are included and the engine identity is checked.
98
- `serviceOrigin` defaults to `https://ball2d.com`; the deployment's client/protocol
99
- and engine must match this SDK version. Importing the package does not
100
- open a room. Browser consumers continue to import from `ball2d`.
101
-
102
- On a deployment with accounts and SDK keys configured, supply an account-owned
103
- key from an environment variable:
55
+ `serviceOrigin` defaults to `https://ball2d.com`. A custom origin must serve a
56
+ matching Ball2D deployment, authorization API and engine/protocol. The native
57
+ package includes its compiled runtime, WebAssembly engine, transport and stadiums;
58
+ it needs no browser process, postinstall patch or sibling source checkout.
59
+
60
+ ## Authorization and limits
61
+
62
+ The native `apiKey` is required. Missing or malformed credentials fail before
63
+ engine and transport allocation. There is no anonymous fallback. The key is sent
64
+ only to the configured service in Authorization headers, never in peer messages
65
+ or room URLs. Cross-origin redirects cannot forward it.
66
+
67
+ | Policy | Initial limit |
68
+ | ----------------------------- | --------------------------------------- |
69
+ | Active API keys per account | 2 |
70
+ | Active rooms per key | 1 |
71
+ | Active rooms per account | 2 |
72
+ | New room attempts per key | 3 per rolling minute |
73
+ | New room attempts per account | 5 per rolling minute |
74
+ | Startup reservation | 30 seconds |
75
+ | Active lease | Up to 90 seconds, bounded by key expiry |
76
+ | Lease renewal | Every 30 seconds |
77
+
78
+ Capacity is enforced by the service, not by a local process counter. Repeated
79
+ idempotent admissions do not consume another room; failed capacity attempts count
80
+ against admission rate limits. Admission and renewal recheck key expiry and
81
+ revocation. The official runtime closes on terminal rejection or lease expiry;
82
+ network failures receive bounded retries. Account revocation is not an instant
83
+ remote kill guarantee. Obey `Retry-After` and avoid tight retry loops.
84
+
85
+ Anonymous browser play is a separate service policy. API keys govern official
86
+ native admission; they cannot make downloaded executable code confidential or
87
+ prevent modified participants from running an unconnected private simulation.
88
+
89
+ ## Room API
90
+
91
+ The public types are included in `dist/types`. The `ball2d/node` entry exports
92
+ `createRoom`, `validateStadium`, `readReplay` and the native/public room types.
93
+ TypeScript projects can use `module: "NodeNext"`, `lib: ["ES2022"]` and Node types;
94
+ native declarations do not require DOM/WebRTC globals or `skipLibCheck`.
95
+
96
+ - **Players and lobby:** inspect players, assign teams/admins, lock teams, kick/ban,
97
+ manage admission, send chat and announcements, and configure the room.
98
+ - **Matches:** start/stop, pause/resume, score/time limits, kick-rate limits and
99
+ `await room.setSurfaceEnabled(true)` for wet grass and ground wear. Pass `false`
100
+ to restore dry ground. Stop the match before changing the surface; loading a
101
+ stadium resets it. Surface changes are retained in replays.
102
+ - **Physics and stadiums:** load custom stadium text, select ten bundled defaults,
103
+ query and modify supported player/disc properties, and use `CollisionFlags`.
104
+ - **Events:** player join/leave/chat, team/admin changes, ball kicks, goals,
105
+ match ticks, position resets, victory, stadium changes and recording completion.
106
+ - **Replay:** record and decode Ball2D recordings with their embedded stadium and
107
+ matching engine identity.
108
+
109
+ State-changing commands return Promises and run in call order. Await a command
110
+ before reading the resulting state. Getters and recording start/stop are
111
+ synchronous. Inputs and returned public data are copied; mutable engine and
112
+ transport internals are not exposed through the API. Up to 256 commands may wait;
113
+ overflow rejects without dropping accepted commands.
114
+
115
+ Callbacks receive the room facade as `this`. Callback failures are reported to
116
+ `onError`; that handler's own failures are contained. Chat filtering must return
117
+ `false` synchronously to suppress a message. Player IDs are stable public IDs,
118
+ not physics slots; departed player IDs are not reused within a room.
119
+
120
+ Room creation resolves only after signaling confirms host authority. An optional
121
+ second argument `{ signal }` cancels startup. Startup has a 15-second overall
122
+ deadline; after creation, call `close()` to stop the room. `signal` aborts when
123
+ closure begins and `closed` settles after native cleanup. `close()` is idempotent,
124
+ cancels pending commands and finalizes an active recording once. It does not
125
+ confirm that every remote player has received a shutdown message.
126
+
127
+ ## Signaling recovery
128
+
129
+ Browser and native runtimes request resumable signaling by default. If the
130
+ signaling connection briefly drops, the same admitted host or player can recover
131
+ within the service membership lifetime while retaining healthy direct peer
132
+ connections. Custom services must support the matching signaling protocol.
133
+
134
+ This does not automatically transfer ownership to another player when the host
135
+ leaves, guarantee uninterrupted delivery, or provide a relay for incompatible
136
+ networks. Native recovery remains subject to the API key and room lease; it does
137
+ not bypass revocation or quota enforcement.
138
+
139
+ ## Stadiums and replays
140
+
141
+ ```js
142
+ import { validateStadium, readReplay } from 'ball2d/node';
104
143
 
105
- ```ts
106
- const apiKey = process.env.BALL2D_API_KEY;
107
- if (!apiKey) throw new Error('Set BALL2D_API_KEY from your Ball2D account.');
108
- const room = await createRoom({ roomName: 'My room host', noPlayer: true, apiKey });
109
- ```
110
-
111
- `apiKey` is optional for existing anonymous hosting. When supplied, it is sent
112
- only in the authenticated room-creation request's Authorization header, never
113
- in a URL or peer message. Keys currently authorize only `rooms:create`. New keys
114
- expire after 90 days by default; the account page offers 7, 30, 90 or 365 days.
115
- Expiry and revocation prevent new room creation without stopping an existing match. Never include this key in browser bundles or version control.
116
- Account access depends on configured social providers and Worker bindings.
117
- Anonymous room hosting is available without an API key.
118
-
119
- The matching Worker limits room creation to 10 attempts per minute per
120
- API key and 20 per minute across an account's keys, in addition to the IP gate.
121
- HTTP rate-limit responses include `Retry-After: 60`; avoid immediate retry loops.
122
- These are approximate, location-local abuse limits, not globally exact quotas.
123
- Exhausting a creation budget does not interrupt an established match.
124
- Node.js 24 is the primary supported development target; Bun uses the same entry
125
- without a separate implementation.
126
-
127
- Native room callbacks and commands use the same SDK facade. `signal` aborts when
128
- close begins; `closed` settles after native cleanup. Neither is a confirmation
129
- that every remote player has received a shutdown message. This is not yet WAN,
130
- Linux, other Bun versions, automatic outage recovery, sustained churn or capacity acceptance.
131
- Bundled dependency licenses are in
132
- `dist/licenses`; the transport includes DCEP unordered-bit and ICE restart fixes.
133
-
134
- TypeScript Node consumers can use `module: "NodeNext"`, `lib: ["ES2022"]` and
135
- `types: ["node"]` with their Node type package. Public declarations do not require
136
- DOM/WebRTC globals or `skipLibCheck`; browser runtime classes remain internal.
137
- `Replay` is exported as a data type from both SDK entries.
138
-
139
- Both SDK entries accept `createRoom(config, { signal })`. The optional
140
- `AbortSignal` cancels startup only; aborting it after creation does not close the
141
- room. Use `room.close()` for an active room. Startup has a 15-second overall
142
- deadline for engine assets, HTTP admission/body and signaling, while the existing
143
- 12-second signaling timeout can fail earlier. Rejection preserves the caller's
144
- abort reason; the overall deadline rejects with `TimeoutError`. In-flight asset
145
- and admission requests receive the startup signal, and late results cannot
146
- continue admission after cancellation.
147
-
148
- ## Install
149
-
150
- ```sh
151
- npm install ball2d@0.1.1
144
+ const report = validateStadium('{name:"Training", canBeStored:false}');
145
+ console.log(report.name, report.warnings);
146
+
147
+ room.startRecording();
148
+ // Play, then stop recording:
149
+ const recording = room.stopRecording();
150
+ if (recording) {
151
+ const replay = await readReplay(recording);
152
+ console.log(replay);
153
+ }
152
154
  ```
153
155
 
154
- The application source remains private. The package retains its existing
155
- UNLICENSED designation; bundled third-party notices are included separately.
156
+ Validation is synchronous and does not allocate a room. Passing validation means
157
+ the stadium can be parsed, not that all gameplay outcomes have been certified.
158
+ Bundled defaults are Classic, Easy, Small, Big, Rounded, Hockey, Big Easy,
159
+ Big Rounded, Big Hockey and Huge. They are Ball2D-authored procedural designs;
160
+ provenance and hashes are included. Custom stadiums and embedded-stadium replays
161
+ remain supported. Geometry and physics can differ from earlier SDK assets.
162
+ Recordings are binary containers: use `readReplay`, not JSON parsing.
163
+
164
+ ## Browser integration
165
+
166
+ `import { createRoom } from 'ball2d'` is the browser entry for an origin serving
167
+ matching Ball2D APIs, WASM and stadium assets. It is not a remote-origin native
168
+ host or an untrusted plugin sandbox. It follows the browser admission policy;
169
+ never put a native API key into its bundle. Importing either entry does not create
170
+ a room or register a window global. Browser and native hosts share public room
171
+ commands while retaining their distinct startup and cleanup contracts.
172
+
173
+ ## Distribution, license and support
174
+
175
+ This public repository contains reviewed executable artifacts, public declarations
176
+ and integration documentation. The game and infrastructure source remain in a
177
+ separate private repository. Source maps, internal declarations and platform
178
+ credentials are excluded. Distributed JavaScript and WASM can still be inspected
179
+ and modified; the package is not a reverse-engineering prevention mechanism.
180
+
181
+ The proprietary [SDK license](LICENSE) permits integrations with the official
182
+ Ball2D service, including commercial use. Public visibility does not make the
183
+ runtime open source. Third-party components retain their licenses in `dist/licenses`.
184
+
185
+ Run `npm run check` in this repository to verify pinned runtime hashes, the
186
+ package boundary and native missing-key behavior. `npm pack --dry-run --ignore-scripts`
187
+ shows the publish contents. GitHub Actions is permanently disabled.
188
+
189
+ Report reproducible defects through [issues](https://github.com/fillbyte/ball2d/issues).
190
+ Remove secrets, personal data and private room links. Use
191
+ [private vulnerability reporting](https://github.com/fillbyte/ball2d/security/advisories/new)
192
+ for sensitive findings. See [contribution guidance](https://github.com/fillbyte/ball2d/blob/main/CONTRIBUTING.md).
193
+
194
+ Production acceptance on 12 September 2026 verified a confirmed-email account
195
+ creating an API key, Node.js room creation, wet-ground simulation with a Chrome
196
+ player, a second room rejected by the key quota, and active-key revocation closing
197
+ the official host and rejecting new admission. Local verification also includes
198
+ installed-package hosting, gameplay, replay and cleanup. This evidence does not
199
+ establish broad WAN/NAT reachability,
200
+ Linux compatibility, sustained capacity or an uptime guarantee. Direct WebRTC
201
+ requires peer reachability; no TURN relay is provided.