# @bountyboard/arcade-sdk ## 1.4.0 ### Minor Changes - publish workflow and docs cleanup ## 1.3.1 ### Patch Changes - 7273667: Stop prompting flexible-orientation Arcade games to rotate on mobile. ## 1.3.0 ### Minor Changes - 0f53ae3: Add `BBArcade.storage`, a localStorage compatibility shim for engine exports. Bounty-Board-hosted builds run on an opaque origin where `localStorage` throws rather than returning empty. Games that own their source can just call `save()`/`load()`, but engine runtimes bake synchronous storage in: GameMaker HTML5 routes `ini_open`/`ini_write_*`/`game_save` through it, as do Godot, Unity, and Construct. `storage.install()` swaps the throwing `localStorage` for a Storage-shaped object backed by the SDK's cloud save, so those builds persist per player and across devices with no engine changes. It is idempotent and a no-op wherever a real Storage works, so standalone play and URL embeds are untouched. The script-tag build installs it at load; module consumers keep a side-effect-free import and call it themselves before boot. `ready()` resolves once the cloud read lands (`getItem` is synchronous, the read is not), `flush()` forces the debounced write out, and `mode` reports `'native' | 'cloud' | 'memory'`. `sessionStorage` is shimmed memory-only. With the shim active `save()`/`load()` share the player's one save slot through a tagged envelope that `load()` unwraps; a raw blob written before the shim existed reads back untouched. The shim never writes over a save it has not successfully read. A read that FAILS settles just like one that succeeds, so persisting on that basis would serialize only the current session's keys and wipe the rest; instead a failed read is retried on the next write, and a write that still cannot confirm what the player had is refused rather than allowed to overwrite it blind. `save()` is size-checked against the whole envelope before it commits, so a rejected oversized blob cannot leave the session unable to write at all. Keys named `__proto__` are stored and restored like any other, matching a real `Storage`. ## 1.2.0 ### Minor Changes - c2854a7: Support curated external authoritative multiplayer servers with per-game ticket keys while preserving the existing `joinRoom()` API and Bounty-hosted room modules. - 81fdada: Multiplayer: public quick match. `joinRoom({ match: true })` joins the game's open public room (or starts a fresh one when no seat is free) instead of requiring a share code. Rooms fill to the game's player cap (at most 64), a matched room still exposes `room.code` for inviting friends, and the SDK automatically re-matchmakes up to twice when it loses a last-seat race (`error.detail` now carries the raw server reason, e.g. `room_full`). - 387a347: Player-identity change notifications, multiplayer tuning hooks, and tighter failure semantics. - Add `BBArcade.onPlayerChange(handler)`: fires when the host delivers a changed display identity (mid-session login/logout); returns an unsubscribe function. - Add `joinRoom({ timeoutMs })` to override the 10-second welcome timeout (clamped to 1000–60000 ms, applies to reconnects too), and `room.latencyMs`, a join-handshake latency estimate refreshed on every (re)connect. - `save()` now rejects oversized blobs with `too_large` immediately via a client-side precheck against the same 1 MiB cap the server enforces. - A rewarded placement whose break ends before a prepared ad is ever shown now resolves `dismissed` instead of the non-terminal `ready` (the shown-but-unreported legacy edge keeps `ready`). - `lockToHost()`'s opaque-origin host handshake times out after ~5 seconds instead of 15, so scraped copies are blocked sooner. - `leave()` during an in-flight join/reconnect settles immediately, and a welcome arriving after `leave()` is ignored instead of resurrecting the room. - Fractional scores are truncated toward zero (`Math.trunc`), matching the documented contract (previously floored, which differed for negative scores). ### Patch Changes - fb26238: Use the declared multiplayer SDK integration when authorizing room tickets and server-authoritative result reports. - 8289ae5: Replace private-repository documentation links with public Bounty Board URLs and rewrite the external authoritative server guide as a vendor-neutral integration contract. - 145d951: Document the built-in relay room tier: every multiplayer-approved game gets hosted casual lobbies (public quick match, invite codes, host succession, rate-guarded public message fan-out) with zero server code, via the existing `joinRoom()` API. New `docs/relay-rooms.md` covers the wire contract: the `joinData.roomSize` founding rule (2–64, default 8), `relay`/`relay_host` events, the 1 KiB / 15 msg/s / 120 msg/room/s guardrails, and the client-trusted outcome model (relay rooms never emit `end` or report results). No runtime code changes. - f55f6e6: Multiplayer: treat the server's 4005 seat-full close as terminal. A seat already holding its maximum concurrent sockets now rejects newcomers, and the client stops retrying instead of looping ticket requests against a full seat. ## 1.1.0 ### Minor Changes - 10a4997: Support production real-time game modules with bounded join data, observable reconnect state, and explicit `trySend` backpressure. ### Patch Changes - f701c2c: Require Bounty Board-approved, host-session-bound tickets for multiplayer rooms and harden the authoritative room server's edge authorization. - 732b929: Keep the Bounty Board host fullscreen overlay from covering provider iframe ad controls on touch devices. - 72daa42: Ship the package changelog and add reproducible release validation for npm, browser-script, ESM, CommonJS, TypeScript, global, and multiplayer consumers. ## 1.0.1 ### Patch Changes - Added a two-stage rewarded-ad preparation flow that preserves the browser's direct user activation requirement, with expanded integration guidance and regression coverage. ## 1.0.0 ### Major Changes - Initial public release with leaderboard, cloud-save, rewarded-ad, experimentation, player, sitelock, and multiplayer APIs.