Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 48 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,18 @@

TypeScript client SDK for the [Asobi](https://github.com/widgrensit/asobi) game backend. Universal — runs in modern browsers and Node 22+.

## Scope

asobi-js is a thin transport client for the asobi protocol. It connects, authenticates, encodes and decodes message frames, manages reconnection and session resume, and dispatches RPC calls and pubsub subscriptions. That's the whole library.

asobi-js does not provide world or match abstractions, voting, terrain, economy, or any other game-shape helper — those are decisions your game makes, not your transport. Phaser, Three.js, and Pixi integrations live as opt-in adapters under [`examples/`](./examples/), not as a bundled API.

Keeping the core small is deliberate: one library, one job, no surprises in your bundle, no opinions about how your game models itself. If you want game-shape primitives, write them on top of asobi-js — they are 50 lines, not a dependency.

## Status

**Pre-1.0.** The current v0.x publishes additional typed REST helpers (matchmaker, leaderboards, economy, social, etc.) inherited from earlier scaffolding. These are scheduled for removal as the SDK narrows to its protocol-only scope. Build new code against the WebSocket transport described below; treat the typed REST modules as deprecated.

## Installation

```bash
Expand All @@ -21,59 +33,59 @@ cd sdk_demo_backend && docker compose up -d

That serves at `http://localhost:8084` (HTTP + WebSocket on `/ws`) with a 2-player `demo` mode. For the full reference game (arena shooter, boons, modifiers, bots) see [`asobi_arena_lua`](https://github.com/widgrensit/asobi_arena_lua).

## Quick Start
## Quick start

```ts
import { Asobi } from "@widgrensit/asobi";
import { AsobiWebSocket } from "@widgrensit/asobi";

const asobi = new Asobi({ baseUrl: "http://localhost:8084" });

// Register a player (or login if the username already exists).
const { session_token, player_id } = await asobi.auth.register({
username: "player1",
password: "secret",
display_name: "Player One",
const ws = new AsobiWebSocket({
url: "ws://localhost:8084/ws",
token: "your-session-token",
});
console.log("registered as", player_id);

const ws = asobi.websocket();
await ws.connect();

// Subscribe BEFORE queueing — the server may push immediately.
// match.matched (matchmaker push) and match.joined (reply to a client-
// initiated match.join) both signal "in a match — match.state will follow."
const off = ws.on("match.matched", (payload) => {
console.log("matched", payload.match_id);
});
// Subscribe to server-pushed events on any topic
ws.on("match.state", (payload) => {
console.log("tick", payload.tick, "players", Object.keys(payload.players ?? {}).length);
console.log("tick", payload.tick);
});

ws.sendFire("matchmaker.add", { mode: "demo" });
// Fire-and-forget pubsub publish
ws.send("match.input", { move_x: 1, move_y: 0 });

// RPC: send and await a typed reply
const reply = await ws.send("match.join", { match_id: "abc" });
console.log("joined", reply);

// Disconnect cleanly
ws.close();
```

`ws.on(event, handler)` returns an unsubscribe function — call it to detach the listener.
Topics (`match.state`, `world.entity_added`, etc.) are opaque to this SDK — you publish and subscribe to whatever your server emits. See the [WebSocket protocol guide](https://github.com/widgrensit/asobi/blob/main/guides/websocket-protocol.md) for the full event surface.

## API

```ts
new AsobiWebSocket({ url, token, reconnect?, reconnectInterval?, maxReconnectAttempts?, heartbeatInterval? })

ws.connect(): Promise<Record<string, unknown>>
ws.close(): void
ws.send(type: string, payload?: object): Promise<Record<string, unknown>> // RPC
ws.on(event: string, handler: (payload) => void): void
ws.off(event: string, handler): void
```

`sendFire(type, payload)` is fire-and-forget. `send(type, payload)` is RPC-style: it allocates a correlation id and resolves with the matching server reply (10 s timeout).
The `"*"` event receives every frame, useful for debugging or building a custom dispatcher.

See the [WebSocket protocol guide](https://github.com/widgrensit/asobi/blob/main/guides/websocket-protocol.md) for the full event surface.
## Engine and framework adapters

## Features
Game-engine and framework integrations live as opt-in examples, not as bundled exports:

| Subsystem | REST | WebSocket |
|---|---|---|
| Auth, players, IAP | ✓ | — |
| Matches & matchmaker | ✓ | ✓ |
| Worlds (MMO-scale, terrain) | ✓ | ✓ |
| Chat & direct messages | ✓ | ✓ |
| Social (friends, groups) | ✓ | — |
| Economy (wallets, store, inventory) | ✓ | — |
| Leaderboards & tournaments | ✓ | — |
| Cloud saves & storage | ✓ | — |
| Presence & notifications | ✓ | ✓ |
| Voting | ✓ | ✓ |
- `examples/phaser/` — Phaser 3 helper for driving the SDK from a Scene
- `examples/three/` — Three.js loop integration
- `examples/pixi/` — Pixi.js loop integration

The `AsobiWebSocket` class exposes a generic `on(event, cb)` event emitter plus typed `send()` / `sendFire()` helpers. The `"*"` event receives all messages, useful for debugging or a custom dispatcher.
(Examples are added as community contributions land.)

The REST modules under `Asobi` are the v0.x compatibility surface — the long-term direction is protocol-only (everything over `AsobiWebSocket`).

Expand Down
Loading