Skip to content

Abstract Class: Matchmaker<Env> ​

Defined in: server/matchmaker.ts:61

Puts waiting players together into matches. One Durable Object per queue name.

A player waits by holding a WebSocket open. As soon as matchSize players are waiting, the matchmaker picks a new room id, reserves the room for them, tells each of them where to go and closes their sockets. The queue is the set of open sockets, so the object hibernates while people wait.

Who is matched with whom is first come, first served, unless you say otherwise: ratingOf and ratingGap keep players of different skill apart, and regionWaitMs keeps players of different regions apart, each for as long as you think a player should wait for a better match.

Friends who queue together are a party: their join tokens say so, or partyOf does. A party is matched as one, never split, and with teams set it plays on one side.

Extends ​

  • DurableObject<Env>

Type Parameters ​

Type ParameterDefault type
EnvCloudflare.Env

Constructors ​

Constructor ​

ts
new Matchmaker<Env>(ctx, env): Matchmaker<Env>;

Defined in: server/matchmaker.ts:129

Parameters ​

ParameterType
ctxDurableObjectState
envEnv

Returns ​

Matchmaker<Env>

Overrides ​

DurableObject<Env>.constructor

Properties ​

PropertyModifierTypeDefault valueDescriptionDefined in
matchSizeprotectednumber2Players per match.server/matchmaker.ts:63
minPlayersprotectednumber | undefinedundefinedThe fewest players a match may start with once the longest-waiting player has waited maxWaitMs. Defaults to matchSize: full matches only.server/matchmaker.ts:69
maxWaitMsprotectednumber0How long the longest-waiting player waits for a full match before one starts with whoever is there, provided that is at least minPlayers. 0, the default, waits for a full match however long it takes.server/matchmaker.ts:76
ratingGapprotectednumberInfinityHow far apart two players' ratings may be for them to be matched. Infinity, the default, matches players whatever ratingOf says. A match is built around whoever has waited longest: the players nearest to them in rating, none further away than this.server/matchmaker.ts:84
ratingGapPerSecondprotectednumber0How much ratingGap grows for every second a player has waited, so that a player nobody is close to still gets a match. 0, the default, never widens it: such a player waits until someone close enough arrives.server/matchmaker.ts:91
regionWaitMsprotectednumber0For how long a player is matched only with players of their own region. After that they are matched with anyone, players of their region first. 0, the default, matches across regions from the start.server/matchmaker.ts:98
teamsprotectednumber1How many sides a match is split into. 1, the default, says nothing about sides. With more, the matchmaker shares each match's players out among that many teams of as equal a size as it can, keeps every party on one team, and evens out the teams' ratings. The teams go in the match message and to the room, as this.teams. A party larger than a team is turned away.server/matchmaker.ts:108
partyWaitMsprotectednumber60_000How long the first member of a party to arrive waits for the others. After that the members who are there are matched without the rest.server/matchmaker.ts:114
roomsprotectedRoomNamespace | undefinedundefinedWhere the rooms for matches are: the Durable Object namespace of your Room class. protected override rooms = this.env.ROOMS;server/matchmaker.ts:120

Methods ​

reserveRoom() ​

ts
protected reserveRoom(
   roomId, 
   players, 
   locationHint, 
   teams?
): Promise<void>;

Defined in: server/matchmaker.ts:141

Set the room up for these players. The default creates it in rooms, near locationHint (where most of the players are), and closes it to everyone else. Override it to do more first, such as writing down the match, and call super.reserveRoom from yours. If it throws, the players keep waiting and the match is tried again shortly.

Parameters ​

ParameterType
roomIdstring
playersstring[]
locationHintDurableObjectLocationHint | undefined
teams?string[][]

Returns ​

Promise<void>


ratingOf() ​

ts
protected ratingOf(player, request): number | Promise<number | undefined> | undefined;

Defined in: server/matchmaker.ts:168

How good this player is, as a number in which players who are close are a fair match: an Elo rating, a rank, a level. It is asked once, when the player starts waiting, and only matters once ratingGap is set. A player without a rating, which is what the default gives everyone, can be matched with anyone.

protected override ratingGap = 150;
protected override async ratingOf(player: string) {
  const stats = await new Accounts(this.env.DB, options).stats(player);
  return stats.rating ?? 1000;
}

Parameters ​

ParameterType
playerstring
requestRequest

Returns ​

number | Promise<number | undefined> | undefined


regionOf() ​

ts
protected regionOf(player, request): string | Promise<string | undefined> | undefined;

Defined in: server/matchmaker.ts:181

Which region this player is in, for regionWaitMs. The default is the part of the world the gateway saw the request come from, one of Cloudflare's location hints such as "weur" or "enam", and undefined when that is not known, as in local development. A player without a region can be matched with anyone.

Parameters ​

ParameterType
playerstring
requestRequest

Returns ​

string | Promise<string | undefined> | undefined


partyOf() ​

ts
protected partyOf(player, request): 
  | Party
  | Promise<Party | undefined>
  | undefined;

Defined in: server/matchmaker.ts:194

The party this player queues with, if any: friends who are to be matched together. The default is what their join token says, which only your server can write (see JoinTokenClaims.party). Override it to look the party up yourself, in a table of your own for instance. Never take it from the request's query: a player who could name any party could join a stranger's.

Parameters ​

ParameterType
playerstring
requestRequest

Returns ​

| Party | Promise<Party | undefined> | undefined


fetch() ​

ts
fetch(request): Promise<Response>;

Defined in: server/matchmaker.ts:208

Durable Object entry point for players joining the queue. Tickflare's: don't override it.

Parameters ​

ParameterType
requestRequest

Returns ​

Promise<Response>

Overrides ​

DurableObject.fetch


webSocketClose() ​

ts
webSocketClose(
   ws, 
   code, 
   reason
): void;

Defined in: server/matchmaker.ts:268

Durable Object entry point for players leaving the queue. Tickflare's: don't override it.

Parameters ​

ParameterType
wsWebSocket
codenumber
reasonstring

Returns ​

void

Overrides ​

DurableObject.webSocketClose


alarm() ​

ts
alarm(): Promise<void>;

Defined in: server/matchmaker.ts:277

The wait is over for whoever is in the queue.

Returns ​

Promise<void>

Overrides ​

DurableObject.alarm

MIT licence. Tickflare is an independent project and is not affiliated with Cloudflare.