LEO Stadium

Protocol

Leo speaks JSON over WebSocket on the same host as this site. Hidden information never leaves the server — each client gets a private view.

Connect

Open wss://play.leoserver.art (or ws:// on localhost). On open, the server sends welcome. Then introduce yourself with an App Key from the dashboard:

{ "type": "hello", "name": "Leo", "appKey": "leo_…", "playerKey": "pk_…" }

Omit playerKey on first connect — the server creates an account, returns a key, and the client should store it. Send it next time to restore the same wallet.

Matchmaking

{ "type": "quick",  "game": "bluffer" }
{ "type": "create", "game": "bluffer", "options": { "seats": 4 } }
{ "type": "join",   "roomId": "K7X2PQ" }
{ "type": "start" }
{ "type": "leave" }
{ "type": "list" }

Photon-style props & events

{ "type": "setPlayerProps", "props": { "skin": "red", "ready": true } }
{ "type": "setRoomProps", "props": { "map": "arena" } }   // host only
{ "type": "event", "code": "emote", "data": { "id": 3 } }
{ "type": "event", "code": "whisper", "data": "hi", "targetId": "pl_…" }

Room messages also include props and each player’s props.

In-game actions

Forwarded to the game referee. Bluffer:

{ "type": "action", "action": { "type": "bid", "count": 3, "face": 5 } }
{ "type": "action", "action": { "type": "call" } }
{ "type": "action", "action": { "type": "power" } }   // once/match: peek one opponent

Fun Run (each round — first to 12 wins):

{ "type": "action", "action": { "type": "walk" } }   // +2
{ "type": "action", "action": { "type": "run" } }    // +3, 1 energy
{ "type": "action", "action": { "type": "dash" } }   // +5 once, 2 energy

REST for game clients (Supabase-like)

Send X-App-Key (and optional X-Player-Key):

GET  /api/v1/me
GET  /api/v1/leaderboard?metric=coins
PUT  /api/v1/data/inventory/main   { "coins": 10, "items": ["sword"] }
GET  /api/v1/data/inventory/main

Server → client

{ "type": "welcome", "playerId": "p1", "games": ["dice","bluffer"], "wallet": {...} }
{ "type": "room", "roomId", "game", "started", "you", "host", "players", "state", "event" }
{ "type": "error", "message": "..." }

Add a game

  1. Create stadium/src/games/<name>/index.js exporting id, minPlayers, maxPlayers, create, onStart, onAction, view.
  2. Register it in stadium/src/games/index.js.
  3. Redeploy: docker compose up -d --build.

Health

GET /health returns loaded games and live room counts.