Queue Room

The built-in QueueRoom provides a matchmaking queue that groups players over time and spawns a match room when a group is ready.

Features:

  • Rank-based grouping with customizable compatibility (compare).
  • Team-aware grouping (maxTeamSize, teamId).
  • Timeout-based priority escalation (maxWaitingCyclesForPriority).
  • Optional incomplete matches for bots (allowIncompleteGroups).
  • Custom room creation hook (onGroupReady).

The source-code for QueueRoom is available here. You can extend it or override compare/onGroupReady for custom matchmaking.

Server

Expose the queue room

server.ts
import { defineServer, defineRoom, QueueRoom } from "colyseus";
 
const server = defineServer({
	rooms: {
		queue: defineRoom(QueueRoom, {
			matchRoomName: "battle",
			maxPlayers: 4,
			maxWaitingCycles: 15,
			maxWaitingCyclesForPriority: 10,
		}),
	}
});

Expose your match room

server.ts
import { defineServer, defineRoom, Room } from "colyseus";
 
class BattleRoom extends Room {}
 
const server = defineServer({
	rooms: {
		battle: defineRoom(BattleRoom),
		queue: defineRoom(QueueRoom, { matchRoomName: "battle" })
	}
});

Options

QueueRoom receives the following options on defineRoom(QueueRoom, options):

OptionTypeDefaultDescription
matchRoomNamestringrequiredName of the room to create when a group is ready.
maxPlayersnumber4Number of players per match group.
maxWaitingCyclesnumber15Cycles after which a group can be marked ready if allowIncompleteGroups is true.
maxWaitingCyclesForPrioritynumber10Cycles after which a client becomes high-priority and can bypass compare.
maxTeamSizenumberEnables team-based grouping and limits team size per match.
allowIncompleteGroupsbooleanfalseAllows groups to start before reaching maxPlayers (expect bots).
compare(client, group) => booleanbuilt-inCompatibility check for assigning a client to a group.
onGroupReady(group) => Promise<IRoomCache>built-inCustom hook to create/choose the room to join.

Client

Joining the queue

client.ts
import { Client } from "@colyseus/sdk";
 
const client = new Client("http://localhost:2567");
 
const queue = await client.joinOrCreate("queue", {
	rank: 1200,
	teamId: "party-42",
	mode: "ranked",
});

Queue updates

The server periodically notifies clients with the number of players currently in their group.

client.ts
queue.onMessage("clients", (count) => {
	console.log("Players in your group:", count);
});

Receiving a seat reservation

When a group is ready, the queue sends a seat reservation for the target match room:

client.ts
queue.onMessage("seat", async (reservation) => {
	// Optionally confirm the reservation to the queue
	queue.send("confirm");
 
	// Join the match room with the reservation
	const match = await client.consumeSeatReservation(reservation);
	console.log("Joined match", match.roomId);
});

Matching behavior

Rank-based grouping

By default, clients are grouped by rank and split into multiple groups if the rank difference becomes too high. You can customize this logic with compare:

server.ts
import { defineRoom, QueueRoom } from "colyseus";
 
defineRoom(QueueRoom, {
	matchRoomName: "battle",
	compare: (client, group) => {
		const diff = Math.abs(client.rank - group.averageRank);
		return diff <= 50; // strict rank matching
	}
});

Team-based grouping

Provide maxTeamSize and pass teamId from the client to enforce team cohesion:

server.ts
defineRoom(QueueRoom, {
	matchRoomName: "battle",
	maxPlayers: 6,
	maxTeamSize: 3,
});
client.ts
await client.joinOrCreate("queue", {
	rank: 980,
	teamId: "party-7",
});

Incomplete groups and bots

If you want matches to start even when groups are not full, enable allowIncompleteGroups. Your match room should fill the remaining slots with bots.

server.ts
defineRoom(QueueRoom, {
	matchRoomName: "battle",
	maxPlayers: 4,
	allowIncompleteGroups: true,
	maxWaitingCycles: 8,
});

Custom room creation

Use onGroupReady to decide how and where a match room is created:

server.ts
import { defineRoom, QueueRoom, matchMaker } from "colyseus";
 
defineRoom(QueueRoom, {
	matchRoomName: "battle",
	onGroupReady: async function (group) {
		// Create a new room with match options
		return await matchMaker.createRoom("battle", {
			averageRank: group.averageRank,
			players: group.clients.length,
		});
	}
});
Last updated on