Skip to main content
Version: 5.x

Open Match

The open-match provider is a Node.js integration of Open Match, an open-source, flexible, and scalable matchmaker created by Google. Open Match recommends the Match Function (the logic of how players get matched) be written in Go, but since Redwood aims to only introduce one language (JavaScript/TypeScript), we implemented a Node.js Match Function that scales just as easily as one made in Go.

Open Match only runs in Kubernetes. Redwood deploys it for you as one of the cluster dependencies (the deployment.dependencies.open-match Helm chart, version 1.8.1), controlled by deployment.kubernetes.instances.<id>.dependencies.openMatch.enabled (on by default), and deploys Redwood's Match Function service with each realm. The kubernetes config environment already sets ticketing.matchmaking.provider to open-match for the default realm. With the Dev Initiator, use Simple Match instead.

By default, the Match Function provided by Redwood creates matches that consider optimal ping, low wait time (aka age), and more players. You can see this implementation in packages/match-function/src/match-functions/default.ts in the computeMatchScore() function. You can change these weights via configuration, but there's a good chance that you're going to want to customize the Match Function to your needs.

Backfills​

Redwood currently doesn't support backfills, but it's on the roadmap!

Configuration​

Common​

Both Simple Match and Open Match providers share some common matchmaking config variables. They're in each realm's Realm Instance Config under ticketing.matchmaking (the defaults are in config/node/default/realm/instances/redwoodBase.yaml). Here's a brief overview of each of them:

NameDefaultDescription
ticketing.matchmaking.enabledtrueWhether matchmaking runs for this realm.
ticketing.matchmaking.providersimple-matchsimple-match or open-match.
ticketing.matchmaking.ticket-stale-time-ms60000The amount of time in milliseconds to wait before considering a ticket stale if it can't find a match beforehand. The client is told that the ticket went stale (TicketError) and they should try again later. Uncooked servers in development can take longer than a minute to start, so we raise this in Installing.
ticketing.matchmaking.max-match-age-ms30000The amount of time in milliseconds to wait before considering a found match to be stale if a server can't be allocated/initialized beforehand. This rarely should happen, but if there are issues with the hosting provider a server may not come up. This is a catch-all. Clients are notified when this happens and tickets are re-entered into the ticket pool. Keep this lower than Open Match's pendingReleaseTimeout (1m by default).
ticketing.matchmaking.max-wait-until-make-shallow-match-ms45000The amount of time in milliseconds to wait before making a shallow (aka not full) match. The time is based on the oldest ticket in the viable match.

The game profile fields matchmaking-zone-name, min-players-for-matchmaking, and max-players-per-shard (see Game Modes and Maps) decide which profiles can be matchmade and how big matches are.

Open Match Specific​

You can configure the endpoints for the various Open Match services (ticketing.matchmaking.open-match.frontend, .backend, and .query) and Redwood's Match Function service (ticketing.matchmaking.open-match.match-function), but you should generally let Redwood configure these for you. See the realm config reference for every variable. Below we go into more detail about configuring the match function that is used for Open Match.

Match Function​

Open Match requires a separate Match Function service that is used to evaluate the pending tickets for potential matches and provide a score to prevent overlapping matches. You can configure which match function is used with the ticketing.matchmaking.open-match.match-function.name configuration variable. This must match the name of the match function file (i.e. a name of default matches the default.ts match function).

Match functions are stored in packages/match-function/src/match-functions/ and are written in TypeScript. You can create your own match function by creating a new file in this directory and exporting a class that implements the IMatchFunction TypeScript interface (from match-function.ts in the same folder).

The default match function​

Redwood comes with a default match function, which is a simple scoring function that balances ticket age, ping to the region, and number of players:

ticketing:
matchmaking:
open-match:
match-function:
# Equal numbers means equal preference, increase a number to favor it more
scoring-weights:
age: 1
ping: 1
players: 1

These are unitless weights when computing a potential match's score. Redwood uses the Default Evaluator by default, which creates non-conflicting matches by favoring the ones with a higher score determined by the computeMatchScore() function mentioned above. These weights can be increased/decreased to favor optimal ticket age, ping, or player count respectively.

note

The default match function's absolute ping score treats ping as milliseconds (it starts penalizing above 80), but the Unreal plugin reports ping in seconds in 5.0, so every region currently gets a near-perfect absolute ping score. The relative part of the ping score (preferring each ticket's better regions) still works. If absolute ping matters for your game, convert it to milliseconds in your own match function.

Creating a custom match function​

While you can directly modify the default.ts match function, you can also implement a completely different one to prevent your changes from being overwritten during backend updates. To do this, create a new TypeScript file in packages/match-function/src/match-functions/ (e.g. my-match-function.ts), and add a default export of a class that implements the IMatchFunction interface:

import type { openmatchMatchProfile, openmatchTicket } from "@open-match/api";
import { IRealmInstanceConfig } from "@redwoodmmo/common";
import { Writable } from "stream";
import { IMatchFunction } from "./match-function";

export default class MyMatchFunction implements IMatchFunction {
public constructor() {
//
}

public makeAndStreamMatches(
omProfile: openmatchMatchProfile,
poolTickets: Map<string, openmatchTicket[]>, // by default, Redwood will only have one pool for all tickets
output: Writable,
ticketingConfig: IRealmInstanceConfig["ticketing"]
): void {
//
}
}
note

The name of your exported class doesn't matter, only the name of the file matters.

The makeAndStreamMatches function is automatically called for you. To add potential matches, you write them using output.write(...). Every match proposal should have at least the region and evaluation_input extensions.

Match functions have a lot of details and are hard to properly document without confusing you; the best way to understand how to write one is to heavily reference/copy the default.ts match function included with Redwood.

Configuring your custom match function​

To use your custom match function, update the ticketing.matchmaking.open-match.match-function.name configuration variable to match the name of your match function file (without the .ts extension). For example, if you created a my-match-function.ts file, you would set the configuration variable to my-match-function.

Packaging your custom match function​

The match function ships with the backend source for both Standard and Custom licenses, so either can build a custom one. To create a binary for your custom match function, run the following commands in the RedwoodBackend folder:

yarn build
yarn pkg:match-function