Multiplayer Game
IMPORTANT: Before doing anything, you MUST read BASE_SKILL.md in this skill's directory. It contains essential guidance on debugging, error handling, state management, deployment, and project setup. Those rules and patterns apply to all RivetKit work. Everything below assumes you have already read and understood it.
Working Examples
If you need a reference implementation, read the raw working example code in these templates:
Patterns for building multiplayer games with RivetKit, intended as a practical checklist you can adapt per genre.
Starter Code
Start with one of the working examples on GitHub and adapt it to your game. Do not start from scratch for matchmaking and lifecycle flows.
| Game Classification |
Starter Code |
Common Examples |
| Battle Royale |
GitHub |
Fortnite, Apex Legends, PUBG, Warzone |
| Arena |
GitHub |
Call of Duty TDM/FFA, Halo Slayer, Counter-Strike casual, VALORANT unrated, Overwatch Quick Play, Rocket League |
| IO Style |
GitHub |
Agar.io, Slither.io, surviv.io |
| Open World |
GitHub |
Minecraft survival servers, Rust-like worlds, MMO zone/chunk worlds |
| Party |
GitHub |
Fall Guys private lobbies, custom game rooms, social party sessions |
| Physics 2D |
GitHub |
Top-down physics brawlers, 2D arena games, platform fighters |
| Physics 3D |
GitHub |
Physics sandbox sessions, 3D arena games, movement playgrounds |
| Ranked |
GitHub |
Chess ladders, competitive card games, duel arena ranked queues |
| Turn-Based |
GitHub |
Chess correspondence, Words With Friends, async board games |
| Idle |
GitHub |
Cookie Clicker, Idle Miner Tycoon, Adventure Capitalist |
Server Simulation
Game Loop And Tick Rates
| Pattern |
Use When |
Implementation Guidance |
| Fixed realtime loop |
Battle Royale, Arena, IO Style, Open World, Ranked |
Run in run with sleep(tickMs) and exit on c.aborted. |
| Action-driven updates |
Party, Turn-Based |
Mutate and broadcast only on actions/events rather than scheduled ticks. |
| Coarse offline progression |
Any mode with idle progression |
Use c.schedule.after(...) with coarse windows (for example 5 to 15 minutes) and apply catch-up from elapsed wall clock time. |
Physics
Start with custom kinematic logic for simple games. Switch to a full physics engine when you need joints, stacked bodies, high collision density, or complex shapes (rotated polygons, capsules, convex hulls, triangle meshes).
Pick one engine per simulation. Keep frontend-only libs out of backend simulation paths and treat server state as authoritative.
| Dimension |
Primary Engine |
Fallback Engines |
Example Code |
| 2D |
@dimforge/rapier2d |
planck-js, matter-js |
GitHub |
| 3D |
@dimforge/rapier3d |
cannon-es, ammo.js |
GitHub |
Spatial Indexing
For non-physics spatial queries, use a dedicated index instead of naive O(n^2) checks:
| Index Type |
Recommendation |
| AABB index |
For AOI, visibility, and non-collider entities, use rbush for dynamic sets or flatbush for static-ish sets. |
| Point index |
For nearest-neighbor or within-radius queries, use d3-quadtree. |
Networking & State Sync
Netcode
| Model |
When To Use |
Implementation |
| Hybrid (client movement, server combat) |
Shooters, action sports, ranked duels |
Client owns movement and sends capped-rate position updates. Server validates for anti-cheat. Combat (projectiles, hits, damage) is fully server-authoritative. |
| Server-authoritative with interpolation |
IO Style, persistent worlds |
Client sends input commands. Server simulates on fixed ticks and publishes authoritative snapshots. Client interpolates between snapshots. |
| Server-authoritative (basic logic) |
Turn-based, event-driven |
Server validates and applies discrete actions (turns, phase transitions, votes). Client displays confirmed state. |
Realtime Data Model
- Snapshots and diffs: Publish state as events. Send a full snapshot on join/resync, then per-tick diffs for regular updates.
- Batch per tick: Keep events small and typed. Batch high-frequency updates per tick.
- Avoid UI framework state for game updates: Use
requestAnimationFrame or a Canvas/Three.js loop for simulation, not React state. Reserve UI framework state for menus, HUD, and forms.
- Broadcast vs per-connection: Use
c.broadcast(...) for shared updates and conn.send(...) for private/per-player data.
Shared Simulation Logic
Shared simulation logic runs on both the client and the server. For example, an applyInput(state, input, dt) function that integrates velocity and clamps to world bounds can run on the client for prediction and on the server for validation.
- Hybrid modes: Client runs shared movement as primary authority, server runs it for anti-cheat validation.
- Server-authoritative modes: Client uses shared logic for interpolation and prediction only.
- Keep it pure: Movement integration, input transforms, collision helpers, and constants only.
- Put shared code in
src/shared/: Keep deterministic helpers in src/shared/sim/* with no side effects.
Interest Management
Control what each client receives to reduce bandwidth and prevent information leaks.
Per-Player Replication Filters
- Filter by relevance: Send each client only state relevant to that player (proximity, line-of-sight, team, or game phase).
- Shooters and action games: Limit replication by proximity and optional field-of-view checks.
- Server-side only: Clients should never receive data they should not see.
Sharded Worlds
- Partition large worlds: Use chunk actors keyed by
worldId:chunkX:chunkY.
- Subscribe to nearby chunks: Clients connect only to nearby partitions (for example a 3x3 chunk window).
- Use sparingly: Only when the world is large and state-heavy (sandbox builders, MMOs), not as a default for small matches.
Backend Infrastructure
Persistence
- In-memory state: Best for realtime game state that changes every tick (player positions, inputs, match phase, scores).
- SQLite (
rivetkit/db): Better for large or table-like state that needs queries, indexes, or long-term persistence (tiles, inventory, matchmaking pools). Serialize DB work through a queue since multiple actions can hit the same actor concurrently.
Matchmaking Patterns
Common building blocks used across the architecture patterns below.
Actor Topology
| Primitive |
Use When |
Typical Ownership |
matchmaker["main"] + match[matchId] |
Session-based multiplayer (battle royale, arena, ranked, party, turn-based) |
Matchmaker owns discovery/assignment. Match owns lifecycle and gameplay state. |
chunk[worldId,chunkX,chunkY] |
Large continuous worlds that need sharding |
Each chunk owns local players, chunk state, and local simulation. |
world[playerId] |
Per-player progression loops (idle/solo world state) |
Per-player resources, buildings, timers, and progression. |
player[username] |
Canonical profile/rating reused across matches |
Durable player stats (for example rating and win/loss). |
leaderboard["main"] |
Shared rankings across many matches/players |
Global ordered score rows and top lists. |
Queueing Strategy
- Multiple players can hit the matchmaker at the same time, so actions like find/create, queue/unqueue, and close need to be serialized through actor queues to avoid races.
- Match-local actions (gameplay, scoring) do not need queueing unless they write back to the matchmaker.
Security And Anti-Cheat
Start with this baseline, then harden further for competitive or high-risk environments.
Baseline Checklist
- Identity: Use
c.conn.id as the authoritative transport identity. Treat playerId/username in params as untrusted input and bind through server-issued assignment/join tickets.
- Authorization: Validate the caller is allowed to mutate the target entity (room membership, turn ownership, host-only actions).
- Input validation: Clamp sizes/lengths, validate enums, and validate usernames (length, allowed chars, avoid unbounded Unicode).
- Rate limiting: Per-connection rate limits for spammy actions (chat, join/leave, fire, movement updates).
- State integrity: Server recomputes derived state (scores, win conditions, placements). Never allow client-authoritative changes to inventory/currency/leaderboard totals.
Movement Validation
For any mode with client-authoritative movement (hybrid flows), clients may send position/rotation updates for smoothness, but the server must:
- Enforce max delta per update (speed cap) based on elapsed time.
- Reject or clamp teleports.
- Enforce world bounds (and basic collision if applicable).
- Rate limit update frequency (for example 20Hz max).
Architecture Patterns
Each game type below starts with a quick summary table, then details actors and lifecycle.
Battle Royale
| Topic |
Summary |
| Matchmaking |
Immediate routing to the fullest non-started lobby (oldest tie-break); players wait in lobby until capacity, then the match starts. |
| Netcode |
Hybrid. Client owns movement, camera, and local prediction. Server owns zone state, projectiles, hit resolution, eliminations, loot, and final placement. |
| Tick Rate |
10 ticks/sec (100ms) with a fixed loop for zone progression and lifecycle checks. |
| Physics |
Client owns movement with server anti-cheat validation; projectiles, hits, and damage are server-authoritative. Use @dimforge/rapier3d for 3D or @dimforge/rapier2d for top-down 2D. |
Actors
Key: matchmaker["main"]
Responsibility: Finds or creates lobbies, tracks pending reservations, and maintains occupancy.
Actions
findMatch
pendingPlayerConnected
updateMatch
closeMatch
Queues
findMatch
pendingPlayerConnected
updateMatch
closeMatch
State
- SQLite
matches
pending_players
player_count includes connected and pending players
Key: match[matchId]
Responsibility: Runs lobby/live/finished phases, owns player state, zone progression, and eliminations.
Actions
connect
- Movement and combat actions
Queues
State
- JSON
phase
players
zone
eliminations
snapshot data
Lifecycle
sequenceDiagram
participant C as Client
participant MM as matchmaker
participant M as match
C->>MM: findMatch()
alt no open lobby
MM->>M: create(matchId)
end
MM-->>C: {matchId, playerId}
C->>M: connect(playerId)
M->>MM: pendingPlayerConnected(matchId, playerId)
MM-->>M: accepted
Note over M: lobby countdown -> live
M-->>C: snapshot + shoot events
M->>MM: closeMatch(matchId)
Arena
| Topic |
Summary |
| Matchmaking |
Mode-based fixed-capacity queues (duo, squad, ffa) that build only full matches and pre-assign teams (except FFA). |
| Netcode |
Hybrid. Client owns movement plus prediction and smoothing. Server owns team or FFA assignment, projectiles, hit resolution, phase transitions, and scoring. |
| Tick Rate |
20 ticks/sec (50ms) with a tighter loop for live team and FFA snapshots. |
| Physics |
Medium to high intensity; client movement with server validation and server-authoritative combat/entities. |
Actors
Key: matchmaker["main"]
Responsibility: Runs mode queues, builds full matches, assigns teams, and publishes assignments.
Actions
queueForMatch
unqueueForMatch
matchCompleted
Queues
queueForMatch
unqueueForMatch
matchCompleted
State
- SQLite
player_pool
matches
assignments keyed by connection and player
Key: match[matchId]
Responsibility: Runs match phases and in-match player/team state for score and win conditions.
Actions
Queues
State
- JSON
phase
players
team assignments
score and win state
Lifecycle
sequenceDiagram
participant C as Client
participant MM as matchmaker
participant M as match
C->>MM: queueForMatch(mode)
Note over MM: enqueue in player_pool
Note over MM: fill when capacity reached
MM->>M: create(matchId, assignments)
Note over MM: persist assignments
MM-->>C: assignmentReady
C->>M: connect(playerId)
Note over M: waiting -> live when all players connect
M->>MM: matchCompleted(matchId)
IO Style
| Topic |
Summary |
| Matchmaking |
Open-lobby routing to the fullest room below capacity; room counts are heartbeated and new lobbies are auto-created when needed. |
| Netcode |
Server-authoritative with interpolation. Client sends input intents and interpolates. Server owns movement, bounds, room membership, and canonical snapshots. |
| Tick Rate |
10 ticks/sec (100ms) with lightweight periodic room snapshots. |
| Physics |
Low to medium intensity; server-authoritative kinematic movement, escalating to a physics engine only when collisions get complex. |
Actors
Lifecycle
sequenceDiagram
participant C as Client
participant MM as matchmaker
participant M as match
C->>MM: findLobby()
alt no open lobby
MM->>M: create(matchId)
end
MM-->>C: {matchId, playerId}
C->>M: connect(playerId)
M->>MM: pendingPlayerConnected(matchId, playerId)
MM-->>M: accepted
Note over M: fixed tick simulation
M-->>C: snapshot events
M->>MM: closeMatch(matchId)
Open World
| Topic |
Summary |
| Matchmaking |
Client-driven chunk routing from world coordinates, with nearby chunk windows preloaded via adjacent chunk connections. |
| Netcode |
Hybrid for sandbox (client movement with validation) or server-authoritative for MMO-like flows. Server owns chunk routing, persistence, and canonical world state. |
| Tick Rate |
10 ticks/sec per chunk actor (100ms), so load scales with active chunks. |
| Physics |
Medium to high at scale; chunk-local simulation can be server-authoritative (MMO-like) or client movement with server validation (sandbox-like). |
Actors
- Key:
chunk[worldId,chunkX,chunkY]
- Responsibility: Owns chunk-local players, blocks, movement tick, and chunk membership.
- Actions
connect
enterChunk
addPlayer
setInput
leaveChunk
removePlayer
- Queues
- State
- JSON
connections
players
blocks scoped to one chunk key
Lifecycle
sequenceDiagram
participant C as Client
participant CH as chunk
Note over C: resolve chunk keys from world position
loop each visible chunk
C->>CH: connect(worldId, chunkX, chunkY, playerId)
Note over CH: store connection metadata
end
C->>CH: enterChunk/addPlayer
loop movement updates
C->>CH: setInput(...)
CH-->>C: snapshot
end
C->>CH: leaveChunk/removePlayer or disconnect
Note over CH: remove membership and metadata
Party
| Topic |
Summary |
| Matchmaking |
Host-created private party flow using party codes and explicit joins. |
| Netcode |
Server-authoritative (basic logic). Server owns membership, host permissions, and phase transitions. |
| Tick Rate |
No continuous tick; updates are event-driven (join, start, finish). |
| Physics |
Low intensity for lobby-first flows; usually no dedicated physics or indexing unless you add realtime mini-games. |
Actors
Key: matchmaker["main"]
Responsibility: Handles party create/join flow, validates join tickets, and tracks party size.
Actions
createParty
joinParty
verifyJoin
updatePartySize
closeParty
Queues
createParty
joinParty
verifyJoin
updatePartySize
closeParty
State
- SQLite
parties
join_tickets for party lookup and join validation
Key: match[matchId]
Responsibility: Owns party members, host role, ready flags, and phase transitions.
Actions
connect
startGame
finishGame
Queues
State
- JSON
members
host
ready state
phase
party events
Lifecycle
Host Flow
sequenceDiagram
participant H as Host Client
participant MM as matchmaker
participant M as match
H->>MM: createParty()
MM-->>H: {matchId, partyCode, playerId, joinToken}
H->>M: connect(playerId, joinToken)
M->>MM: verifyJoin(...)
MM-->>M: allowed
M->>MM: updatePartySize(playerCount)
H->>M: startGame() / finishGame()
M->>MM: closeParty(matchId)
Joiner Flow
sequenceDiagram
participant J as Joiner Client
participant MM as matchmaker
participant M as match
J->>MM: joinParty(partyCode)
MM-->>J: {matchId, playerId, joinToken}
J->>M: connect(playerId, joinToken)
M->>MM: verifyJoin(...)
MM-->>M: allowed / denied
M->>MM: updatePartySize(playerCount)
Ranked
| Topic |
Summary |
| Matchmaking |
ELO-based queue pairing with a widening search window as wait time increases. |
| Netcode |
Hybrid. Client owns movement with local prediction and interpolation. Server owns projectiles, hit resolution, match results, and rating updates. |
| Tick Rate |
20 ticks/sec (50ms) with fixed live ticks for deterministic pacing and broadcast cadence. |
| Physics |
Medium to high intensity; client movement with server validation and server-authoritative combat/hit resolution. |
Actors
Key: matchmaker["main"]
Responsibility: Runs rating-based queueing, pairing, assignment persistence, and completion fanout.
Actions
queueForMatch
unqueueForMatch
matchCompleted
Queues
queueForMatch
unqueueForMatch
matchCompleted
State
- SQLite
player_pool
matches
assignments with rating window and connection scoping
Key: match[matchId]
Responsibility: Runs ranked match phase, score, and winner reporting.
Actions
Queues
State
- JSON
phase
players
score
winner
completion payload
Key: player[username]
Responsibility: Stores canonical player MMR and win/loss profile.
Actions
initialize
getRating
applyMatchResult
Queues
State
- JSON
rating
wins
losses
match counters
Key: leaderboard["main"]
Responsibility: Stores and serves top-ranked players.
Actions
Queues
State
- SQLite
- Leaderboard score rows
- Top-list ordering
Lifecycle
sequenceDiagram
participant C as Client
participant MM as matchmaker
participant P as player
participant M as match
participant LB as leaderboard
C->>MM: queueForMatch(username)
MM->>P: initialize/getRating
P-->>MM: rating
Note over MM: store queue row + retry pairing
MM->>M: create(matchId, assigned players)
MM-->>C: assignmentReady
C->>M: connect(username)
M->>MM: matchCompleted(...)
MM->>P: applyMatchResult(...)
MM->>LB: updatePlayer(...)
Note over MM: remove matches + assignments rows
Turn-Based
| Topic |
Summary |
| Matchmaking |
Async private-invite and public-queue pairing in the same pattern. |
| Netcode |
Server-authoritative (basic logic). Client can draft moves before submit. Server owns turn ownership, committed move log, turn order, and completion state. |
| Tick Rate |
No continuous tick; move submission and turn transitions drive updates. |
| Physics |
Very low intensity; no realtime physics loop, just discrete rules validation. Indexing is optional and mostly for board or query convenience at scale. |
Actors
Key: matchmaker["main"]
Responsibility: Handles private invite and public queue pairing for async matches.
Actions
createGame
joinByCode
queueForMatch
unqueueForMatch
closeMatch
Queues
createGame
joinByCode
queueForMatch
unqueueForMatch
closeMatch
State
- SQLite
matches
player_pool
assignments for invite and queue mapping
Key: match[matchId]
Responsibility: Owns board state, turn order, move validation, and final result.
Actions
Queues
State
- JSON
board
turns
players
connection presence
result
Lifecycle
Public Queue
sequenceDiagram
participant A as Client A
participant B as Client B
participant MM as matchmaker
participant M as match
A->>MM: queueForMatch()
B->>MM: queueForMatch()
Note over MM: pair first two queued players
MM->>M: create(matchId) + seed X/O players
MM-->>A: assignment/match info
MM-->>B: assignment/match info
A->>M: connect(playerId)
B->>M: connect(playerId)
A->>M: makeMove()
B->>M: makeMove()
opt all players disconnected for timeout
Note over M: destroy after idle timeout
end
M->>MM: closeMatch(matchId)
Private Invite
sequenceDiagram
participant A as Client A
participant B as Client B
participant MM as matchmaker
participant M as match
A->>MM: createGame()
MM-->>A: {matchId, playerId, inviteCode}
B->>MM: joinByCode(inviteCode)
MM->>M: create(matchId) + seed X/O players
MM-->>A: assignment/match info
MM-->>B: assignment/match info
A->>M: connect(playerId)
B->>M: connect(playerId)
A->>M: makeMove()
B->>M: makeMove()
M->>MM: closeMatch(matchId)
Idle
| Topic |
Summary |
| Matchmaking |
No matchmaker; each player uses a direct per-player actor and a shared leaderboard actor. |
| Netcode |
Server-authoritative (basic logic). Client owns UI and build intent. Server owns resources, production rates, building validation, and leaderboard totals. |
| Tick Rate |
No continuous tick; use c.schedule.after(...) for coarse intervals and compute offline catch-up from elapsed wall time. |
| Physics |
None for standard idle loops; transitions are discrete (build, collect, upgrade) and do not need spatial indexing. |
Actors
Key: world[playerId]
Responsibility: Owns one player's progression, buildings, production scheduling, and state updates.
Actions
initialize
build
collectProduction
Queues
State
- JSON
- Per-player buildings
resources
timers
progression state
Key: leaderboard["main"]
Responsibility: Stores global scores and serves leaderboard updates.
Actions
Queues
State
- SQLite
scores table keyed by player
- Current leaderboard totals
Lifecycle
sequenceDiagram
participant C as Client
participant W as world
participant LB as leaderboard
C->>W: getOrCreate(playerId) + initialize()
Note over W: seed state + schedule collection
W-->>C: stateUpdate
loop gameplay loop
C->>W: build() / collectProduction()
W->>LB: updateScore(...)
Note over LB: upsert scores
LB-->>C: leaderboardUpdate
W-->>C: stateUpdate
end
Reference Map
Actors
- Access Control
- Actions
- Actor Keys
- Actor Scheduling
- Actor Statuses
- Authentication
- Cloudflare Workers Quickstart
- Communicating Between Actors
- Connections
- Custom Inspector Tabs
- Debugging
- Design Patterns
- Destroying Actors
- Effect.ts Quickstart (Beta)
- Errors
- Fetch and WebSocket Handler
- Helper Types
- Icons & Names
- In-Memory State
- Input Parameters
- Lifecycle
- Limits
- Low-Level HTTP Request Handler
- Low-Level KV Storage
- Low-Level WebSocket Handler
- Metadata
- Next.js Quickstart
- Node.js & Bun Quickstart
- Queues & Run Loops
- React Quickstart
- Realtime
- Rust Quickstart (Beta)
- Scaling & Concurrency
- Sharing and Joining State
- SQLite
- SQLite + Drizzle
- Supabase Functions Quickstart
- Testing
- Troubleshooting
- Types
- Vanilla HTTP API
- Versions & Upgrades
- Workflows
Cli
Clients
- Node.js & Bun
- React
- Rust (Beta)
- Swift
- SwiftUI
Cookbook
- AI Agent
- Chat Room
- Collaborative Text Editor
- Cron Jobs and Scheduled Tasks
- Database per Tenant
- Deploying Rivet in a VPC or Air-Gapped Network
- Live Cursors and Presence
- Multiplayer Game
Deploy
- Container Runner
- Deploy To Amazon Web Services Lambda
- Deploying to AWS ECS
- Deploying to Cloudflare Workers
- Deploying to Freestyle
- Deploying to Google Cloud Run
- Deploying to Hetzner
- Deploying to Kubernetes
- Deploying to Railway
- Deploying to Rivet Compute
- Deploying to Supabase Functions
- Deploying to Vercel
- Deploying to VMs & Bare Metal
General
- Actor Configuration
- Architecture
- Cross-Origin Resource Sharing
- Documentation for LLMs & AI
- Edge Networking
- Endpoints
- Environment Variables
- HTTP Server
- Logging
- Pool Configuration
- Production Checklist
- Registry Configuration
- Runtime Modes
- WASM vs Native SDK
Self Hosting
- Configuration
- Docker Compose
- Docker Container
- File System
- FoundationDB (Enterprise)
- Installing Rivet Engine
- Kubernetes
- Multi-Region
- PostgreSQL
- Production Checklist
- Railway Deployment
- Render Deployment
- TLS & Certificates
1---2name: multiplayer-game3description: Pragmatic patterns for building multiplayer games: matchmaking, tick loops, realtime state, interest management, and validation.4---5
6# Multiplayer Game
7
8**IMPORTANT: Before doing anything, you MUST read `BASE_SKILL.md` in this skill's directory. It contains essential guidance on debugging, error handling, state management, deployment, and project setup. Those rules and patterns apply to all RivetKit work. Everything below assumes you have already read and understood it.**
9
10## Working Examples
11
12If you need a reference implementation, read the raw working example code in these templates:
13
14- [multiplayer-game-patterns](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns)
15
16
17Patterns for building multiplayer games with RivetKit, intended as a practical checklist you can adapt per genre.
18
19## Starter Code
20
21Start with one of the working examples on [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/) and adapt it to your game. Do not start from scratch for matchmaking and lifecycle flows.
22
23| Game Classification | Starter Code | Common Examples |
24| --- | --- | --- |
25| Battle Royale | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/battle-royale/) | Fortnite, Apex Legends, PUBG, Warzone |
26| Arena | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/arena/) | Call of Duty TDM/FFA, Halo Slayer, Counter-Strike casual, VALORANT unrated, Overwatch Quick Play, Rocket League |
27| IO Style | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/io-style/) | Agar.io, Slither.io, surviv.io |
28| Open World | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/open-world/) | Minecraft survival servers, Rust-like worlds, MMO zone/chunk worlds |
29| Party | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/party/) | Fall Guys private lobbies, custom game rooms, social party sessions |
30| Physics 2D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) | Top-down physics brawlers, 2D arena games, platform fighters |
31| Physics 3D | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) | Physics sandbox sessions, 3D arena games, movement playgrounds |
32| Ranked | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/ranked/) | Chess ladders, competitive card games, duel arena ranked queues |
33| Turn-Based | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/turn-based/) | Chess correspondence, Words With Friends, async board games |
34| Idle | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/idle/) | Cookie Clicker, Idle Miner Tycoon, Adventure Capitalist |
35
36## Server Simulation
37
38### Game Loop And Tick Rates
39
40| Pattern | Use When | Implementation Guidance |
41| --- | --- | --- |
42| Fixed realtime loop | Battle Royale, Arena, IO Style, Open World, Ranked | Run in `run` with `sleep(tickMs)` and exit on `c.aborted`. |
43| Action-driven updates | Party, Turn-Based | Mutate and broadcast only on actions/events rather than scheduled ticks. |
44| Coarse offline progression | Any mode with idle progression | Use `c.schedule.after(...)` with coarse windows (for example 5 to 15 minutes) and apply catch-up from elapsed wall clock time. |
45
46### Physics
47
48Start with custom kinematic logic for simple games. Switch to a full physics engine when you need joints, stacked bodies, high collision density, or complex shapes (rotated polygons, capsules, convex hulls, triangle meshes).
49
50Pick one engine per simulation. Keep frontend-only libs out of backend simulation paths and treat server state as authoritative.
51
52| Dimension | Primary Engine | Fallback Engines | Example Code |
53| --- | --- | --- | --- |
54| 2D | `@dimforge/rapier2d` | `planck-js`, `matter-js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-2d/) |
55| 3D | `@dimforge/rapier3d` | `cannon-es`, `ammo.js` | [GitHub](https://github.com/rivet-dev/rivet/tree/main/examples/multiplayer-game-patterns/src/actors/physics-3d/) |
56
57### Spatial Indexing
58
59For non-physics spatial queries, use a dedicated index instead of naive `O(n^2)` checks:
60
61| Index Type | Recommendation |
62| --- | --- |
63| AABB index | For AOI, visibility, and non-collider entities, use `rbush` for dynamic sets or `flatbush` for static-ish sets. |
64| Point index | For nearest-neighbor or within-radius queries, use `d3-quadtree`. |
65
66## Networking & State Sync
67
68### Netcode
69
70| Model | When To Use | Implementation |
71| --- | --- | --- |
72| Hybrid (client movement, server combat) | Shooters, action sports, ranked duels | Client owns movement and sends capped-rate position updates. Server validates for anti-cheat. Combat (projectiles, hits, damage) is fully server-authoritative. |
73| Server-authoritative with interpolation | IO Style, persistent worlds | Client sends input commands. Server simulates on fixed ticks and publishes authoritative snapshots. Client interpolates between snapshots. |
74| Server-authoritative (basic logic) | Turn-based, event-driven | Server validates and applies discrete actions (turns, phase transitions, votes). Client displays confirmed state. |
75
76### Realtime Data Model
77
78- **Snapshots and diffs**: Publish state as events. Send a full snapshot on join/resync, then per-tick diffs for regular updates.
79- **Batch per tick**: Keep events small and typed. Batch high-frequency updates per tick.
80- **Avoid UI framework state for game updates**: Use `requestAnimationFrame` or a Canvas/Three.js loop for simulation, not React state. Reserve UI framework state for menus, HUD, and forms.
81- **Broadcast vs per-connection**: Use `c.broadcast(...)` for shared updates and `conn.send(...)` for private/per-player data.
82
83### Shared Simulation Logic
84
85Shared simulation logic runs on both the client and the server. For example, an `applyInput(state, input, dt)` function that integrates velocity and clamps to world bounds can run on the client for prediction and on the server for validation.
86
87- **Hybrid modes**: Client runs shared movement as primary authority, server runs it for anti-cheat validation.
88- **Server-authoritative modes**: Client uses shared logic for interpolation and prediction only.
89- **Keep it pure**: Movement integration, input transforms, collision helpers, and constants only.
90- **Put shared code in `src/shared/`**: Keep deterministic helpers in `src/shared/sim/*` with no side effects.
91
92### Interest Management
93
94Control what each client receives to reduce bandwidth and prevent information leaks.
95
96#### Per-Player Replication Filters
97
98- **Filter by relevance**: Send each client only state relevant to that player (proximity, line-of-sight, team, or game phase).
99- **Shooters and action games**: Limit replication by proximity and optional field-of-view checks.
100- **Server-side only**: Clients should never receive data they should not see.
101
102#### Sharded Worlds
103
104- **Partition large worlds**: Use chunk actors keyed by `worldId:chunkX:chunkY`.
105- **Subscribe to nearby chunks**: Clients connect only to nearby partitions (for example a 3x3 chunk window).
106- **Use sparingly**: Only when the world is large and state-heavy (sandbox builders, MMOs), not as a default for small matches.
107
108## Backend Infrastructure
109
110### Persistence
111
112- **In-memory state**: Best for realtime game state that changes every tick (player positions, inputs, match phase, scores).
113- **SQLite (`rivetkit/db`)**: Better for large or table-like state that needs queries, indexes, or long-term persistence (tiles, inventory, matchmaking pools). Serialize DB work through a queue since multiple actions can hit the same actor concurrently.
114
115### Matchmaking Patterns
116
117Common building blocks used across the architecture patterns below.
118
119#### Actor Topology
120
121| Primitive | Use When | Typical Ownership |
122| --- | --- | --- |
123| `matchmaker["main"]` + `match[matchId]` | Session-based multiplayer (battle royale, arena, ranked, party, turn-based) | Matchmaker owns discovery/assignment. Match owns lifecycle and gameplay state. |
124| `chunk[worldId,chunkX,chunkY]` | Large continuous worlds that need sharding | Each chunk owns local players, chunk state, and local simulation. |
125| `world[playerId]` | Per-player progression loops (idle/solo world state) | Per-player resources, buildings, timers, and progression. |
126| `player[username]` | Canonical profile/rating reused across matches | Durable player stats (for example rating and win/loss). |
127| `leaderboard["main"]` | Shared rankings across many matches/players | Global ordered score rows and top lists. |
128
129#### Queueing Strategy
130
131- Multiple players can hit the matchmaker at the same time, so actions like find/create, queue/unqueue, and close need to be serialized through actor queues to avoid races.
132- Match-local actions (gameplay, scoring) do not need queueing unless they write back to the matchmaker.
133
134## Security And Anti-Cheat
135
136Start with this baseline, then harden further for competitive or high-risk environments.
137
138### Baseline Checklist
139
140- **Identity**: Use `c.conn.id` as the authoritative transport identity. Treat `playerId`/`username` in params as untrusted input and bind through server-issued assignment/join tickets.
141- **Authorization**: Validate the caller is allowed to mutate the target entity (room membership, turn ownership, host-only actions).
142- **Input validation**: Clamp sizes/lengths, validate enums, and validate usernames (length, allowed chars, avoid unbounded Unicode).
143- **Rate limiting**: Per-connection rate limits for spammy actions (chat, join/leave, fire, movement updates).
144- **State integrity**: Server recomputes derived state (scores, win conditions, placements). Never allow client-authoritative changes to inventory/currency/leaderboard totals.
145
146### Movement Validation
147
148For any mode with client-authoritative movement (hybrid flows), clients may send position/rotation updates for smoothness, but the server must:
149
150- Enforce max delta per update (speed cap) based on elapsed time.
151- Reject or clamp teleports.
152- Enforce world bounds (and basic collision if applicable).
153- Rate limit update frequency (for example 20Hz max).
154
155## Architecture Patterns
156
157Each game type below starts with a quick summary table, then details actors and lifecycle.
158
159### Battle Royale
160
161| Topic | Summary |
162| --- | --- |
163| Matchmaking | Immediate routing to the fullest non-started lobby (oldest tie-break); players wait in lobby until capacity, then the match starts. |
164| Netcode | Hybrid. Client owns movement, camera, and local prediction. Server owns zone state, projectiles, hit resolution, eliminations, loot, and final placement. |
165| Tick Rate | 10 ticks/sec (`100ms`) with a fixed loop for zone progression and lifecycle checks. |
166| Physics | Client owns movement with server anti-cheat validation; projectiles, hits, and damage are server-authoritative. Use `@dimforge/rapier3d` for 3D or `@dimforge/rapier2d` for top-down 2D. |
167
168**Actors**
169
170- **Key**: `matchmaker["main"]`
171- **Responsibility**: Finds or creates lobbies, tracks pending reservations, and maintains occupancy.
172- **Actions**
173 - `findMatch`
174 - `pendingPlayerConnected`
175 - `updateMatch`
176 - `closeMatch`
177- **Queues**
178 - `findMatch`
179 - `pendingPlayerConnected`
180 - `updateMatch`
181 - `closeMatch`
182- **State**
183 - SQLite
184 - `matches`
185 - `pending_players`
186 - `player_count` includes connected and pending players
187
188- **Key**: `match[matchId]`
189- **Responsibility**: Runs lobby/live/finished phases, owns player state, zone progression, and eliminations.
190- **Actions**
191 - `connect`
192 - Movement and combat actions
193- **Queues**
194 - None
195- **State**
196 - JSON
197 - `phase`
198 - `players`
199 - `zone`
200 - `eliminations`
201 - `snapshot data`
202
203**Lifecycle**
204
205```mermaid
206sequenceDiagram
207 participant C as Client
208 participant MM as matchmaker
209 participant M as match
210
211 C->>MM: findMatch()
212 alt no open lobby
213 MM->>M: create(matchId)
214 end
215 MM-->>C: {matchId, playerId}
216 C->>M: connect(playerId)
217 M->>MM: pendingPlayerConnected(matchId, playerId)
218 MM-->>M: accepted
219 Note over M: lobby countdown -> live
220 M-->>C: snapshot + shoot events
221 M->>MM: closeMatch(matchId)
222```
223
224### Arena
225
226| Topic | Summary |
227| --- | --- |
228| Matchmaking | Mode-based fixed-capacity queues (`duo`, `squad`, `ffa`) that build only full matches and pre-assign teams (except FFA). |
229| Netcode | Hybrid. Client owns movement plus prediction and smoothing. Server owns team or FFA assignment, projectiles, hit resolution, phase transitions, and scoring. |
230| Tick Rate | 20 ticks/sec (`50ms`) with a tighter loop for live team and FFA snapshots. |
231| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/entities. |
232
233**Actors**
234
235- **Key**: `matchmaker["main"]`
236- **Responsibility**: Runs mode queues, builds full matches, assigns teams, and publishes assignments.
237- **Actions**
238 - `queueForMatch`
239 - `unqueueForMatch`
240 - `matchCompleted`
241- **Queues**
242 - `queueForMatch`
243 - `unqueueForMatch`
244 - `matchCompleted`
245- **State**
246 - SQLite
247 - `player_pool`
248 - `matches`
249 - `assignments` keyed by connection and player
250
251- **Key**: `match[matchId]`
252- **Responsibility**: Runs match phases and in-match player/team state for score and win conditions.
253- **Actions**
254 - `connect`
255 - Gameplay actions
256- **Queues**
257 - None
258- **State**
259 - JSON
260 - `phase`
261 - `players`
262 - `team assignments`
263 - `score and win state`
264
265**Lifecycle**
266
267```mermaid
268sequenceDiagram
269 participant C as Client
270 participant MM as matchmaker
271 participant M as match
272
273 C->>MM: queueForMatch(mode)
274 Note over MM: enqueue in player_pool
275 Note over MM: fill when capacity reached
276 MM->>M: create(matchId, assignments)
277 Note over MM: persist assignments
278 MM-->>C: assignmentReady
279 C->>M: connect(playerId)
280 Note over M: waiting -> live when all players connect
281 M->>MM: matchCompleted(matchId)
282```
283
284### IO Style
285
286| Topic | Summary |
287| --- | --- |
288| Matchmaking | Open-lobby routing to the fullest room below capacity; room counts are heartbeated and new lobbies are auto-created when needed. |
289| Netcode | Server-authoritative with interpolation. Client sends input intents and interpolates. Server owns movement, bounds, room membership, and canonical snapshots. |
290| Tick Rate | 10 ticks/sec (`100ms`) with lightweight periodic room snapshots. |
291| Physics | Low to medium intensity; server-authoritative kinematic movement, escalating to a physics engine only when collisions get complex. |
292
293**Actors**
294
295- **Key**: `matchmaker["main"]`
296- **Responsibility**: Routes players into the fullest open lobby and tracks reservations and occupancy.
297- **Actions**
298 - `findLobby`
299 - `pendingPlayerConnected`
300 - `updateMatch`
301 - `closeMatch`
302- **Queues**
303 - `findLobby`
304 - `pendingPlayerConnected`
305 - `updateMatch`
306 - `closeMatch`
307- **State**
308 - SQLite
309 - `matches`
310 - `pending_players`
311 - Occupancy includes pending reservations
312
313- **Key**: `match[matchId]`
314- **Responsibility**: Runs per-match movement simulation and broadcasts snapshots.
315- **Actions**
316 - `connect`
317 - `setInput`
318- **Queues**
319 - None
320- **State**
321 - JSON
322 - `players`
323 - `inputs`
324 - `movement state`
325 - `snapshot cache`
326
327**Lifecycle**
328
329```mermaid
330sequenceDiagram
331 participant C as Client
332 participant MM as matchmaker
333 participant M as match
334
335 C->>MM: findLobby()
336 alt no open lobby
337 MM->>M: create(matchId)
338 end
339 MM-->>C: {matchId, playerId}
340 C->>M: connect(playerId)
341 M->>MM: pendingPlayerConnected(matchId, playerId)
342 MM-->>M: accepted
343 Note over M: fixed tick simulation
344 M-->>C: snapshot events
345 M->>MM: closeMatch(matchId)
346```
347
348### Open World
349
350| Topic | Summary |
351| --- | --- |
352| Matchmaking | Client-driven chunk routing from world coordinates, with nearby chunk windows preloaded via adjacent chunk connections. |
353| Netcode | Hybrid for sandbox (client movement with validation) or server-authoritative for MMO-like flows. Server owns chunk routing, persistence, and canonical world state. |
354| Tick Rate | 10 ticks/sec per chunk actor (`100ms`), so load scales with active chunks. |
355| Physics | Medium to high at scale; chunk-local simulation can be server-authoritative (MMO-like) or client movement with server validation (sandbox-like). |
356
357**Actors**
358
359- **Key**: `chunk[worldId,chunkX,chunkY]`
360- **Responsibility**: Owns chunk-local players, blocks, movement tick, and chunk membership.
361- **Actions**
362 - `connect`
363 - `enterChunk`
364 - `addPlayer`
365 - `setInput`
366 - `leaveChunk`
367 - `removePlayer`
368- **Queues**
369 - None
370- **State**
371 - JSON
372 - `connections`
373 - `players`
374 - `blocks` scoped to one chunk key
375
376**Lifecycle**
377
378```mermaid
379sequenceDiagram
380 participant C as Client
381 participant CH as chunk
382
383 Note over C: resolve chunk keys from world position
384 loop each visible chunk
385 C->>CH: connect(worldId, chunkX, chunkY, playerId)
386 Note over CH: store connection metadata
387 end
388 C->>CH: enterChunk/addPlayer
389 loop movement updates
390 C->>CH: setInput(...)
391 CH-->>C: snapshot
392 end
393 C->>CH: leaveChunk/removePlayer or disconnect
394 Note over CH: remove membership and metadata
395```
396
397### Party
398
399| Topic | Summary |
400| --- | --- |
401| Matchmaking | Host-created private party flow using party codes and explicit joins. |
402| Netcode | Server-authoritative (basic logic). Server owns membership, host permissions, and phase transitions. |
403| Tick Rate | No continuous tick; updates are event-driven (`join`, `start`, `finish`). |
404| Physics | Low intensity for lobby-first flows; usually no dedicated physics or indexing unless you add realtime mini-games. |
405
406**Actors**
407
408- **Key**: `matchmaker["main"]`
409- **Responsibility**: Handles party create/join flow, validates join tickets, and tracks party size.
410- **Actions**
411 - `createParty`
412 - `joinParty`
413 - `verifyJoin`
414 - `updatePartySize`
415 - `closeParty`
416- **Queues**
417 - `createParty`
418 - `joinParty`
419 - `verifyJoin`
420 - `updatePartySize`
421 - `closeParty`
422- **State**
423 - SQLite
424 - `parties`
425 - `join_tickets` for party lookup and join validation
426
427- **Key**: `match[matchId]`
428- **Responsibility**: Owns party members, host role, ready flags, and phase transitions.
429- **Actions**
430 - `connect`
431 - `startGame`
432 - `finishGame`
433- **Queues**
434 - None
435- **State**
436 - JSON
437 - `members`
438 - `host`
439 - `ready state`
440 - `phase`
441 - `party events`
442
443**Lifecycle**
444
445### Host Flow
446
447```mermaid
448sequenceDiagram
449 participant H as Host Client
450 participant MM as matchmaker
451 participant M as match
452
453 H->>MM: createParty()
454 MM-->>H: {matchId, partyCode, playerId, joinToken}
455 H->>M: connect(playerId, joinToken)
456 M->>MM: verifyJoin(...)
457 MM-->>M: allowed
458 M->>MM: updatePartySize(playerCount)
459 H->>M: startGame() / finishGame()
460 M->>MM: closeParty(matchId)
461```
462
463### Joiner Flow
464
465```mermaid
466sequenceDiagram
467 participant J as Joiner Client
468 participant MM as matchmaker
469 participant M as match
470
471 J->>MM: joinParty(partyCode)
472 MM-->>J: {matchId, playerId, joinToken}
473 J->>M: connect(playerId, joinToken)
474 M->>MM: verifyJoin(...)
475 MM-->>M: allowed / denied
476 M->>MM: updatePartySize(playerCount)
477```
478
479### Ranked
480
481| Topic | Summary |
482| --- | --- |
483| Matchmaking | ELO-based queue pairing with a widening search window as wait time increases. |
484| Netcode | Hybrid. Client owns movement with local prediction and interpolation. Server owns projectiles, hit resolution, match results, and rating updates. |
485| Tick Rate | 20 ticks/sec (`50ms`) with fixed live ticks for deterministic pacing and broadcast cadence. |
486| Physics | Medium to high intensity; client movement with server validation and server-authoritative combat/hit resolution. |
487
488**Actors**
489
490- **Key**: `matchmaker["main"]`
491- **Responsibility**: Runs rating-based queueing, pairing, assignment persistence, and completion fanout.
492- **Actions**
493 - `queueForMatch`
494 - `unqueueForMatch`
495 - `matchCompleted`
496- **Queues**
497 - `queueForMatch`
498 - `unqueueForMatch`
499 - `matchCompleted`
500- **State**
501 - SQLite
502 - `player_pool`
503 - `matches`
504 - `assignments` with rating window and connection scoping
505
506- **Key**: `match[matchId]`
507- **Responsibility**: Runs ranked match phase, score, and winner reporting.
508- **Actions**
509 - `connect`
510 - Gameplay actions
511- **Queues**
512 - None
513- **State**
514 - JSON
515 - `phase`
516 - `players`
517 - `score`
518 - `winner`
519 - `completion payload`
520
521- **Key**: `player[username]`
522- **Responsibility**: Stores canonical player MMR and win/loss profile.
523- **Actions**
524 - `initialize`
525 - `getRating`
526 - `applyMatchResult`
527- **Queues**
528 - None
529- **State**
530 - JSON
531 - `rating`
532 - `wins`
533 - `losses`
534 - `match counters`
535
536- **Key**: `leaderboard["main"]`
537- **Responsibility**: Stores and serves top-ranked players.
538- **Actions**
539 - `updatePlayer`
540- **Queues**
541 - None
542- **State**
543 - SQLite
544 - Leaderboard score rows
545 - Top-list ordering
546
547**Lifecycle**
548
549```mermaid
550sequenceDiagram
551 participant C as Client
552 participant MM as matchmaker
553 participant P as player
554 participant M as match
555 participant LB as leaderboard
556
557 C->>MM: queueForMatch(username)
558 MM->>P: initialize/getRating
559 P-->>MM: rating
560 Note over MM: store queue row + retry pairing
561 MM->>M: create(matchId, assigned players)
562 MM-->>C: assignmentReady
563 C->>M: connect(username)
564 M->>MM: matchCompleted(...)
565 MM->>P: applyMatchResult(...)
566 MM->>LB: updatePlayer(...)
567 Note over MM: remove matches + assignments rows
568```
569
570### Turn-Based
571
572| Topic | Summary |
573| --- | --- |
574| Matchmaking | Async private-invite and public-queue pairing in the same pattern. |
575| Netcode | Server-authoritative (basic logic). Client can draft moves before submit. Server owns turn ownership, committed move log, turn order, and completion state. |
576| Tick Rate | No continuous tick; move submission and turn transitions drive updates. |
577| Physics | Very low intensity; no realtime physics loop, just discrete rules validation. Indexing is optional and mostly for board or query convenience at scale. |
578
579**Actors**
580
581- **Key**: `matchmaker["main"]`
582- **Responsibility**: Handles private invite and public queue pairing for async matches.
583- **Actions**
584 - `createGame`
585 - `joinByCode`
586 - `queueForMatch`
587 - `unqueueForMatch`
588 - `closeMatch`
589- **Queues**
590 - `createGame`
591 - `joinByCode`
592 - `queueForMatch`
593 - `unqueueForMatch`
594 - `closeMatch`
595- **State**
596 - SQLite
597 - `matches`
598 - `player_pool`
599 - `assignments` for invite and queue mapping
600
601- **Key**: `match[matchId]`
602- **Responsibility**: Owns board state, turn order, move validation, and final result.
603- **Actions**
604 - `connect`
605 - `makeMove`
606- **Queues**
607 - None
608- **State**
609 - JSON
610 - `board`
611 - `turns`
612 - `players`
613 - `connection presence`
614 - `result`
615
616**Lifecycle**
617
618### Public Queue
619
620```mermaid
621sequenceDiagram
622 participant A as Client A
623 participant B as Client B
624 participant MM as matchmaker
625 participant M as match
626
627 A->>MM: queueForMatch()
628 B->>MM: queueForMatch()
629 Note over MM: pair first two queued players
630 MM->>M: create(matchId) + seed X/O players
631 MM-->>A: assignment/match info
632 MM-->>B: assignment/match info
633 A->>M: connect(playerId)
634 B->>M: connect(playerId)
635 A->>M: makeMove()
636 B->>M: makeMove()
637 opt all players disconnected for timeout
638 Note over M: destroy after idle timeout
639 end
640 M->>MM: closeMatch(matchId)
641```
642
643### Private Invite
644
645```mermaid
646sequenceDiagram
647 participant A as Client A
648 participant B as Client B
649 participant MM as matchmaker
650 participant M as match
651
652 A->>MM: createGame()
653 MM-->>A: {matchId, playerId, inviteCode}
654 B->>MM: joinByCode(inviteCode)
655 MM->>M: create(matchId) + seed X/O players
656 MM-->>A: assignment/match info
657 MM-->>B: assignment/match info
658 A->>M: connect(playerId)
659 B->>M: connect(playerId)
660 A->>M: makeMove()
661 B->>M: makeMove()
662 M->>MM: closeMatch(matchId)
663```
664
665### Idle
666
667| Topic | Summary |
668| --- | --- |
669| Matchmaking | No matchmaker; each player uses a direct per-player actor and a shared leaderboard actor. |
670| Netcode | Server-authoritative (basic logic). Client owns UI and build intent. Server owns resources, production rates, building validation, and leaderboard totals. |
671| Tick Rate | No continuous tick; use `c.schedule.after(...)` for coarse intervals and compute offline catch-up from elapsed wall time. |
672| Physics | None for standard idle loops; transitions are discrete (`build`, `collect`, `upgrade`) and do not need spatial indexing. |
673
674**Actors**
675
676- **Key**: `world[playerId]`
677- **Responsibility**: Owns one player's progression, buildings, production scheduling, and state updates.
678- **Actions**
679 - `initialize`
680 - `build`
681 - `collectProduction`
682- **Queues**
683 - None
684- **State**
685 - JSON
686 - Per-player buildings
687 - `resources`
688 - `timers`
689 - `progression state`
690
691- **Key**: `leaderboard["main"]`
692- **Responsibility**: Stores global scores and serves leaderboard updates.
693- **Actions**
694 - `updateScore`
695- **Queues**
696 - `updateScore`
697- **State**
698 - SQLite
699 - `scores` table keyed by player
700 - Current leaderboard totals
701
702**Lifecycle**
703
704```mermaid
705sequenceDiagram
706 participant C as Client
707 participant W as world
708 participant LB as leaderboard
709
710 C->>W: getOrCreate(playerId) + initialize()
711 Note over W: seed state + schedule collection
712 W-->>C: stateUpdate
713 loop gameplay loop
714 C->>W: build() / collectProduction()
715 W->>LB: updateScore(...)
716 Note over LB: upsert scores
717 LB-->>C: leaderboardUpdate
718 W-->>C: stateUpdate
719 end
720```
721
722## Reference Map
723
724### Actors
725
726- [Access Control](reference/actors/access-control.md)
727- [Actions](reference/actors/actions.md)
728- [Actor Keys](reference/actors/keys.md)
729- [Actor Scheduling](reference/actors/schedule.md)
730- [Actor Statuses](reference/actors/statuses.md)
731- [Authentication](reference/actors/authentication.md)
732- [Cloudflare Workers Quickstart](reference/actors/quickstart/cloudflare.md)
733- [Communicating Between Actors](reference/actors/communicating-between-actors.md)
734- [Connections](reference/actors/connections.md)
735- [Custom Inspector Tabs](reference/actors/inspector-tabs.md)
736- [Debugging](reference/actors/debugging.md)
737- [Design Patterns](reference/actors/design-patterns.md)
738- [Destroying Actors](reference/actors/destroy.md)
739- [Effect.ts Quickstart (Beta)](reference/actors/quickstart/effect.md)
740- [Errors](reference/actors/errors.md)
741- [Fetch and WebSocket Handler](reference/actors/fetch-and-websocket-handler.md)
742- [Helper Types](reference/actors/helper-types.md)
743- [Icons & Names](reference/actors/appearance.md)
744- [In-Memory State](reference/actors/state.md)
745- [Input Parameters](reference/actors/input.md)
746- [Lifecycle](reference/actors/lifecycle.md)
747- [Limits](reference/actors/limits.md)
748- [Low-Level HTTP Request Handler](reference/actors/request-handler.md)
749- [Low-Level KV Storage](reference/actors/kv.md)
750- [Low-Level WebSocket Handler](reference/actors/websocket-handler.md)
751- [Metadata](reference/actors/metadata.md)
752- [Next.js Quickstart](reference/actors/quickstart/next-js.md)
753- [Node.js & Bun Quickstart](reference/actors/quickstart/backend.md)
754- [Queues & Run Loops](reference/actors/queues.md)
755- [React Quickstart](reference/actors/quickstart/react.md)
756- [Realtime](reference/actors/events.md)
757- [Rust Quickstart (Beta)](reference/actors/quickstart/rust.md)
758- [Scaling & Concurrency](reference/actors/scaling.md)
759- [Sharing and Joining State](reference/actors/sharing-and-joining-state.md)
760- [SQLite](reference/actors/sqlite.md)
761- [SQLite + Drizzle](reference/actors/sqlite-drizzle.md)
762- [Supabase Functions Quickstart](reference/actors/quickstart/supabase.md)
763- [Testing](reference/actors/testing.md)
764- [Troubleshooting](reference/actors/troubleshooting.md)
765- [Types](reference/actors/types.md)
766- [Vanilla HTTP API](reference/actors/http-api.md)
767- [Versions & Upgrades](reference/actors/versions.md)
768- [Workflows](reference/actors/workflows.md)
769
770### Cli
771
772- [CLI](reference/cli.md)
773
774### Clients
775
776- [Node.js & Bun](reference/clients/javascript.md)
777- [React](reference/clients/react.md)
778- [Rust (Beta)](reference/clients/rust.md)
779- [Swift](reference/clients/swift.md)
780- [SwiftUI](reference/clients/swiftui.md)
781
782### Cookbook
783
784- [AI Agent](reference/cookbook/ai-agent.md)
785- [Chat Room](reference/cookbook/chat-room.md)
786- [Collaborative Text Editor](reference/cookbook/collaborative-text-editor.md)
787- [Cron Jobs and Scheduled Tasks](reference/cookbook/cron-jobs.md)
788- [Database per Tenant](reference/cookbook/per-tenant-database.md)
789- [Deploying Rivet in a VPC or Air-Gapped Network](reference/cookbook/vpc-air-gapped.md)
790- [Live Cursors and Presence](reference/cookbook/live-cursors.md)
791- [Multiplayer Game](reference/cookbook/multiplayer-game.md)
792
793### Deploy
794
795- [Container Runner](reference/deploy/container-runner.md)
796- [Deploy To Amazon Web Services Lambda](reference/deploy/aws-lambda.md)
797- [Deploying to AWS ECS](reference/deploy/aws-ecs.md)
798- [Deploying to Cloudflare Workers](reference/deploy/cloudflare.md)
799- [Deploying to Freestyle](reference/deploy/freestyle.md)
800- [Deploying to Google Cloud Run](reference/deploy/gcp-cloud-run.md)
801- [Deploying to Hetzner](reference/deploy/hetzner.md)
802- [Deploying to Kubernetes](reference/deploy/kubernetes.md)
803- [Deploying to Railway](reference/deploy/railway.md)
804- [Deploying to Rivet Compute](reference/deploy/rivet-compute.md)
805- [Deploying to Supabase Functions](reference/deploy/supabase.md)
806- [Deploying to Vercel](reference/deploy/vercel.md)
807- [Deploying to VMs & Bare Metal](reference/deploy/vm-and-bare-metal.md)
808
809### General
810
811- [Actor Configuration](reference/general/actor-configuration.md)
812- [Architecture](reference/general/architecture.md)
813- [Cross-Origin Resource Sharing](reference/general/cors.md)
814- [Documentation for LLMs & AI](reference/general/docs-for-llms.md)
815- [Edge Networking](reference/general/edge.md)
816- [Endpoints](reference/general/endpoints.md)
817- [Environment Variables](reference/general/environment-variables.md)
818- [HTTP Server](reference/general/http-server.md)
819- [Logging](reference/general/logging.md)
820- [Pool Configuration](reference/general/pool-configuration.md)
821- [Production Checklist](reference/general/production-checklist.md)
822- [Registry Configuration](reference/general/registry-configuration.md)
823- [Runtime Modes](reference/general/runtime-modes.md)
824- [WASM vs Native SDK](reference/general/wasm-vs-native-sdk.md)
825
826### Self Hosting
827
828- [Configuration](reference/self-hosting/configuration.md)
829- [Docker Compose](reference/self-hosting/docker-compose.md)
830- [Docker Container](reference/self-hosting/docker-container.md)
831- [File System](reference/self-hosting/filesystem.md)
832- [FoundationDB (Enterprise)](reference/self-hosting/foundationdb.md)
833- [Installing Rivet Engine](reference/self-hosting/install.md)
834- [Kubernetes](reference/self-hosting/kubernetes.md)
835- [Multi-Region](reference/self-hosting/multi-region.md)
836- [PostgreSQL](reference/self-hosting/postgres.md)
837- [Production Checklist](reference/self-hosting/production-checklist.md)
838- [Railway Deployment](reference/self-hosting/railway.md)
839- [Render Deployment](reference/self-hosting/render.md)
840- [TLS & Certificates](reference/self-hosting/tls.md)
841