This tutorial assumes you finished tic-tac-toe with gamekit and have a workingDocumentation Index
Fetch the complete documentation index at: https://openturn.io/docs/llms.txt
Use this file to discover all available pages before exploring further.
@my/tic-tac-toe-game package. Here we host the same game with @openturn/server, connect to it from a React app with <OpenturnProvider cloud> + useRoom, and deploy it to openturn cloud with openturn deploy.
Reference code: examples/hosted-multiplayer/tic-tac-toe-multiplayer.
1. Switch the app metadata to multiplayer
Take theapp/app/openturn.ts from the previous tutorial and update it:
runtime: "multiplayer" tells openturn build to emit a Cloudflare Worker bundle alongside the client. gameKey is the stable identifier used when deploying; schemaVersion lets you force-reset rooms when you break save compatibility.
app/app/game.ts stays the same. The same game and match values drive both local and hosted play.
2. Wrap the page in <OpenturnProvider> and read useRoom
Replace the local-only experience with a lobby-aware one:
useRoom() returns a phase-aware state object:
phaseis one ofidle | missing_backend | connecting | lobby | transitioning | game | closed | error.lobbyis aLobbyViewwhile players are still joining.gameis aHostedMatchStateonce the lobby hands off to the match.inviteURLis a sharable link that puts a new player in the same room.
playerID to dispatch: the server already knows who you are from your room token.
3. Run the local hosted stack
The CLI can run your game with a local Bun-backed server, no cloud needed:tic-tac-toe-multiplayer package root. app is the project directory containing app/game.ts, app/page.tsx, app/openturn.ts.)
The CLI prints a play URL. Open it in two browser windows. Each window is assigned an anonymous user; the first window creates a room, shares the invite URL, the second window joins the lobby, and both are routed into the hosted match. Moves are dispatched to the server over WebSocket; the server validates them with the same authoritative ticTacToe reducer, then broadcasts batches back to each client’s player view.
Under the hood: @openturn/cli runs a Bun HTTP server backed by SQLite. It uses @openturn/server’s createRoomRuntime plus LobbyRuntime to manage rooms, and @better-auth to issue anonymous session tokens. The dev shell injects the same #openturn-bridge=… URL fragment that openturn-cloud injects in production, so the React app’s <OpenturnProvider> connects through @openturn/bridge without any code change between dev and prod.
4. Deploy to openturn cloud
When you are happy locally, build and ship:openturn build writes a bundle to .openturn/deploy/ containing:
index.html+ client JS and CSS.- A server script with a per-room namespace (
GameRoom). - An
openturn.manifest.jsonwithgameKey,schemaVersion, a SHA256 digest, and runtime bindings.
openturn deploy uploads the bundle to the openturn cloud control plane, which provisions the per-room backends and returns a play URL. That play URL embeds a backend fragment that @openturn/bridge decodes to find the WebSocket endpoint.
See how-to: deploy to openturn cloud for flags and troubleshooting.
What you learned
- A single game definition drives local and hosted play unchanged. Only the React hook and the metadata differ.
- The hosted stack is lobby → room.
useRoom()exposes both phases. openturn devgives you production-shaped hosted play on localhost with no configuration.openturn build+openturn deployship to Cloudflare Durable Objects.
What to do next
- Tutorial: tic-tac-toe replay captures matches and scrubs them back.
- How-to: build a lobby goes deeper on lobby state and seat assignment.
- Reference: server is the API for the authoritative room runtime.