Matchmaking and Game Rooms
The matchmaking and room systems work together but serve different purposes. Matchmaking is about player selection, finding compatible players and grouping them. Room management is about session execution, creating game instances, managing their lifecycle, and cleaning them up when finished. In small games, both responsibilities can live in a single server process. As your game scales, they typically separate into distinct services.
Design the Room Architecture
A room is an independent game instance that hosts a specific group of players. Each room has its own game state, tick loop, player list, and network connections. Rooms run concurrently within a server process, sharing CPU time but maintaining complete state isolation from one another.
Define room types for each game mode your game supports. A battle royale mode might allow 50 players, run on a large map, and use a shrinking zone mechanic. A deathmatch mode might allow 8 players on a small map with respawning. Each room type has its own configuration: maximum players, minimum players to start, time limit, map selection, game rules, and tick rate. Register these room types on the server so the matchmaking system can create instances as needed.
Each room should be addressable by a unique ID. When matchmaking assigns a player to a room, it provides the room ID and the server endpoint where that room is running. The client connects directly to that server and sends the room ID during the WebSocket handshake. The server routes the connection to the correct room instance. This addressing scheme works whether you have one server or a hundred, as long as the matchmaking service knows which server hosts each room.
Build a Room Lifecycle Manager
Rooms progress through distinct lifecycle states, and managing these transitions correctly prevents resource leaks and stale sessions.
The waiting state begins when the room is created. Players join during this phase. A countdown timer starts when the minimum player count is reached (for example, 4 out of a maximum 8). If more players join during the countdown, they are added. If players leave and the count drops below minimum, the countdown resets. The countdown duration is typically 10 to 30 seconds, long enough to fill the room but short enough to avoid frustrating waiting times.
The playing state begins when the countdown expires. The room locks (no new players join mid-game unless your game supports mid-match joining), initializes the game state (spawning players, loading the map), and starts the simulation tick loop. During gameplay, the room processes inputs, updates state, and handles player disconnections with the grace period and reconnection logic described in the multiplayer basics guide.
The finished state begins when the game reaches a conclusion (win condition met, time expired, all players disconnected). The room calculates final scores, updates player statistics in the database, and sends results to all connected clients. After a brief period for players to view results (5 to 10 seconds), the room enters cleanup. All connections are closed, resources are freed, and the room is removed from the server's active room list.
Implement a timeout for rooms stuck in the waiting state. If a room sits in waiting for more than 2 to 5 minutes without reaching the minimum player count, close it and return waiting players to the matchmaking queue. This prevents ghost rooms from consuming server resources indefinitely.
Implement Basic Matchmaking
The simplest matchmaking system is a queue. Players request a game by specifying their desired game mode. The matchmaking service checks for existing rooms of that type with available slots. If one exists, the player joins it. If none are available, a new room is created on a server with available capacity, and the player joins it as the first participant.
This "first available" approach is what Colyseus provides with its joinOrCreate method, and it works well for casual games where skill balance does not matter. Players get into games quickly because there is no waiting for matched opponents, they simply join the next available room.
To improve the basic queue, add filters. Let players specify map preferences, game length preferences, or region preferences. The matchmaking service filters available rooms by these criteria before joining. If no matching room exists, create one with the requested settings. This gives players some control over their experience without the complexity of skill-based systems.
For team-based modes, the matchmaking service must handle team composition. Maintain separate queues for players requesting specific roles (if applicable) and build teams that satisfy composition requirements before assigning them to a room. Alternatively, let players form parties before queuing and treat the party as a single unit in the matchmaking process.
Add Skill-Based Matchmaking
For competitive games, matching players by skill is essential for both player retention and game quality. Lopsided matches frustrate both sides: weaker players feel helpless and stronger players feel unchallenged.
The Elo rating system (originally designed for chess) is the simplest skill-based approach. Each player has a numeric rating (starting at 1200 by convention). After each match, the winner gains points and the loser loses points. The amount of change depends on the expected outcome: beating a much higher-rated player earns more points than beating someone rated below you. The matchmaking service then groups players with similar Elo ratings.
Glicko-2 improves on Elo by tracking a rating deviation (how confident the system is in the rating) and a volatility value (how consistent the player's performance is). New players start with high deviation, so their rating changes quickly in early matches and stabilizes over time. The matchmaking system can prioritize matching low-deviation players together for more predictable match quality, while allowing high-deviation players (new or inconsistent) to match more broadly to establish their true rating faster.
Match quality metrics help you tune the system. Track the win rate of the favored player in each match. Ideal matchmaking produces roughly 50/50 win rates. If your data shows that the higher-rated player wins 70% of matches, your skill brackets are too wide and you need tighter matching. If queue times are too long, your brackets are too narrow and you need to widen them. The balance between match quality and queue time is the central tension of skill-based matchmaking.
Handle Geographic Routing
For real-time games, geographic proximity to the server directly determines latency. A player in Tokyo connecting to a server in Virginia will always experience at least 150 milliseconds of round-trip time due to the speed of light through fiber. No amount of server optimization can fix physics.
Deploy game servers in multiple geographic regions (North America, Europe, Asia at minimum for a global game). The matchmaking service detects the player's region from their IP address or, more accurately, by having the client ping multiple regional endpoints and reporting which one has the lowest latency.
Regional matchmaking creates a tradeoff between latency and queue time. If your game has many players, matching within regions keeps latency low with short queues. If your player base is small, strict regional matching can cause long wait times in low-population regions. Implement a fallback: after waiting a configurable time (30 to 60 seconds), expand the search to adjacent regions. An Australian player with no local matches might accept 100 milliseconds of latency to a Singapore server rather than waiting 5 minutes for a local match.
Add Private Rooms and Invites
Not all multiplayer sessions go through public matchmaking. Players want to create private games for friends, practice sessions, and organized tournaments. Implement a private room system alongside public matchmaking.
When a player creates a private room, generate a short, human-readable room code (6 alphanumeric characters is standard). The creator shares this code with friends through external channels (messaging apps, Discord, social media). Friends enter the code in the game client, and the matchmaking service routes them to the specific room identified by that code.
Private rooms should follow the same lifecycle as public rooms but with different rules for starting. Instead of a minimum player count countdown, the room creator controls when the game starts with a "start game" button. The maximum player count should still be enforced. Room codes should expire after a reasonable time (1 hour) or when the game finishes, whichever comes first.
For games with party systems, let players form groups in a lobby before entering matchmaking. The party joins the queue as a unit and is placed into the same team. Party-based matchmaking should account for the average skill of the party when using skill-based matching, and it should try to match premade parties against other premade parties rather than against solo players for competitive fairness.
Start with simple first-available matchmaking and add complexity as your player base grows. Room lifecycle management prevents resource leaks. Skill-based matchmaking improves competitive games but requires enough players to maintain reasonable queue times. Geographic routing is essential for any real-time game with a global audience.