Game and matchmaking example

A practical pattern for multiplayer games on Xshathra's data plane: one realtime channel per match, an authoritative game server that publishes state, and a matchmaking flow that moves players from queue → lobby → match. Pairs with Client integration and Chat example.

What you will have at the end

  • Match channel — subscribers receive ticks or snapshots from your simulation.
  • Lobby / queue channels — lightweight realtime for ready checks and “match found” without spamming the match channel.
  • Clear handoff — matchmaking service tells clients which channel to join and issues the right subscription tokens.

Before you start

  1. Same baseline as other examples: a console project with server_api_key / client_api_key, clients using your app's /realtime/connection/websocket URL, and a backend that mints connection and (optionally) subscription JWTs.
  2. A game server process (or fleet) that owns simulation: physics, hit validation, win conditions. Clients are never the source of truth for outcomes.

Part A — In-match realtime (authoritative server)

Step 1 — Name the match channel

Use a unique id per match session. Include project (or environment) id to avoid collisions.

# Pattern
match:{projectId}:{matchId}

# Example
match:proj_01HZZZZZZZZZZZZZZZZZZZZZZ:01HMATCHZZZZZZZZZZZZZZZZZ

Step 2 — Who may publish?

Recommended: only your game server publishes to match:… using Xshathra publish API on your app origin, e.g. POST https://your-app.example/api/realtime/publish with project server_api_key. Clients subscribe read-only; they send inputs to your game server over your existing transport (UDP/TCP, another WebSocket, or HTTP).

Alternative (small games): clients publish intent messages (e.g. “move request”) to a channel the server also reads — still validate every frame server-side before updating authoritative state.

Step 3 — Message shapes

Keep a small set of event types. Serialize compactly for high tick rates if needed (binary on the wire is optional; JSON is fine to start).

// Snapshot (low frequency or on join)
{
  "type": "match.snapshot",
  "matchId": "01HMATCH...",
  "tick": 18402,
  "state": { /* your compact world state */ }
}

// Delta / event (between snapshots)
{
  "type": "match.event",
  "matchId": "01HMATCH...",
  "tick": 18403,
  "name": "player.eliminated",
  "payload": { "playerId": "p1" }
}

Part B — Matchmaking flow (step by step)

Step 1 — Player enters queue

Client calls your REST API (not the realtime gateway directly): e.g. POST /matchmaking/queue with playlist id, party id, skill bucket. Your service records the ticket in Redis or a database.

Step 2 — Optional: queue / party channel

For party games, a channel like party:{projectId}:{partyId} lets members see invites and ready state. Subscription tokens should only include party members.

party:proj_01HZ...:party_7xk2

Step 3 — Matcher forms a match

A worker pulls compatible tickets, assigns a matchId, starts (or reserves) a game server instance, and persists the roster.

Step 4 — Tell players “match found”

Two common patterns (you can combine):

  • HTTP response — client polls or uses long-polling / SSE until the ticket resolves; response body includes matchId, server address, and any join secret.
  • Per-user notify channel — e.g. user:{projectId}:{userId}:notify — server publishes match.ready with match id and connection hints. Client stays subscribed while in menus.

Step 5 — Subscribe to the match channel

Client requests a subscription token for match:{projectId}:{matchId} only after the ticket is accepted. Then connect to Xshathra Realtime (if not already) and subscribe. Game server begins publishing snapshots/events when the session starts.

Step 6 — End of match

Publish a final match.ended event, stop the simulation, and let subscription tokens expire. Clients unsubscribe; you may archive history via realtime stream history if enabled.

Anti-patterns to avoid

  • Trusting client-published damage or scores on a shared channel without server validation.
  • Guessable match ids — use opaque ids and private channels + subscription JWTs.
  • Publishing high-rate physics to every viewer worldwide— scope channels per match; consider interest management in your sim before fan-out.

Related