This documentation is now deprecated. If you are using Matchmaker Self Serve though UDash, please use the documentation here.
Many multiplayer games implement methods for groups of players to enter matchmaking together. For example, a method for players to enter the same activity with each other, or a method for players to get grouped onto the same team in team-based games.
With the Unity Matchmaker, you can support groups of players who are searching for a match together by configuring your
TeamRules in the function config file.
At a high-level, an example implementation might look like the following process:
- You configure your matchmaking logic with the Team structure in the
TeamRulessection to reflect your game.
- You implement a method for grouping players together (for example, a party service).
- You submit one ticket for the group.
- This is typically done through a service or a "leader" client.
- The ticket contains information about the group, which is stored in the ticket's attributes and properties.
- The match function uses information in the group tickets to match groups with other groups and players.
- When the match function creates a match proposal, it writes any group-specific data to the proposal properties.
- When the ticket is assigned the match, the service or client that submitted the ticket consumes the match data.
- You communicate to all clients in the group the necessary information for connecting to the resulting match.
Note that during this process you define what a group is, and you create and manage groups outside of the matchmaker. Integrating your group solution with the matchmaker consists of you submitting whatever group data you want as part of your ticket, and make sure it matches the
TeamRules section in the configuration file.
Providing an optional crossplay experience is a common requirement for platform holders. Although the matchmaking system enables several approaches for enabling crossplay, a recommended approach is to use the built-in matchmaker segmentation functionality to help with processing multiple combinations of platforms. This requires adding some
property information to your matchmaking tickets.
To add platform information to your ticket, consider adding a
platform enum to your ticket attributes. This makes tickets searchable by platform during segmentation. In addition, a Boolean (currently represented by a double) for
crossplay enables an easier way to automatically isolate non-crossplay players during matchmaking with their respective pools.
The following example ticket has
The goal is to create the following scenario during distinct match function runs:
- Match functions that run for crossplay-enabled tickets query regardless of platform, but only for tickets with crossplay enabled.
- Match functions that run for crossplay-disabled tickets exclusively query for tickets per platform, which generates several platform-specific match function runs, but includes tickets regardless of whether they have opted in to crossplay. This means that a Platform A ticket with crossplay enabled could still be matched into an exclusive Platform A game, but ensures that you are not splitting the player population into two separate groups.
The following section details some example configs that result in user-optional crossplay for multi-platform games. The examples include
teamSkill to show how other attributes combine with platform attributes.
The crossplay filter guarantees that only tickets that are opted in to crossplay are matched together. Note that the
platform attribute is ignored.
This config generates 18 concurrent function runs:
The platform segment filter guarantees that only tickets on the same platform are matched together. However, the lack of a crossplay filter means that this function's queries also include tickets that are technically opted in to crossplay. In this scenario, players without crossplay can still match with players that are able to use crossplay, which results in more possible matches for players who want to stay on their own platform.
"Time-to-match" is a metric that measures how quickly your players go from submitting a matchmaking ticket to getting a response back with a match assignment. It is a great measure of player experience, and can have an impact on player retention.
An ideal matchmaking system operating in ideal conditions can quickly create high-quality matches. However, under real-world conditions, there are many factors that can affect time-to-match and match quality. You must determine strategic ways to balance match quality and time-to-match.
The following sections detail factors that influence time-to-match.
The following process details a simplified view of the parts of the matchmaking flow that impact time-to-match:
- A client submits a ticket (POST to tickets endpoint).
- A match function runs and creates a proposal with that ticket.
- The proposal is approved and a new server is allocated (or a backfilling server is selected).
- The client gets the completed match assignment (GET to tickets endpoint).
The real-world absolute minimum time-to-match for a ticket is generally around 1 second. Achieving this match time requires the following conditions to be met:
- The player has low latency to the datacenter that is hosting the matchmaker.
- A match can immediately be created for the player.
- The player is assigned to a backfilled match.
- The player polls the ticket's GET endpoint to pick up the match right after it is assigned.
Note that actual real-world match times can vary widely from this ideal, and generally take several seconds due to a number of variables, such as those detailed in the following list:
- The amount of time that it takes for your client to POST a ticket
- This is calulated as client latency to the datacenter + the system time to ingest the ticket
- The amount of time that it takes for a match function to run
- This is calulated as the function run time + ticket query time + system overhead
- Function run window time
- A variable time window where the system waits for all scheduled functions to run; the minimum time is currently ~300ms and the maximum is ~3000ms
- Tickets are cached between windows to ensure deterministic queries across functions, and new tickets must wait for the next window until they are included
- Whether there are enough tickets available to meet the function's requirements to immediately create matches
- For example, if you need 8 players, but only have 5, you have to wait until you have enough players to make a match
- The amount of time that it takes to assign a connection or server to the match after it is created
- Backfill takes ~0 seconds, and matches that are assigned to new servers take ~1-2 additional seconds to allocate a server for the match
- The amount of time that it takes for the client to get their match assignment after it is created
- This is also referred to as the delay between when the match is ready and when the client checks to see if the match is ready
You can control some of these variables, such as those detailed in the following list:
- The match function's run time
- Your minimum requirements to create a match
- Your client's GET polling rate
Remember that you are generally not making matches in an ideal scenario, so you must consider how to balance your requirements versus your time-to-match. In certain scenarios, you might want to intentionally increase your minimum time-to-match.
A match function can take variable amount of time to run, depending on the complexity of the match function logic and how intensive the operations are that are performed within a match function. Match functions that take longer than ~250ms to run could impact on your time-to-match. Match functions that take longer than ~3000ms to run are assumed to be dead, are force stopped, and their results are ignored.
In general, match functions tend to have Big O performance between O(n) and O(n^2), where n is the number of tickets in the system. Keeping the match functions efficient under load is an important factor in keeping your match times low.
For most developers, the minimum requirements for creating a match is where the vast majority of your time-to-match is determined. The time impact of this part of matchmaking is defined by how easy it is to find the minimum number of compatible players to form a match.
In practice, this means that as the number of compatible players you can choose from (the player pool) increases, and the number of players that you need to form a match (the game size) decreases, the faster your matchmaking can be with real-world traffic.
If you have very narrow and specific criteria for how players can match with each other, it might take a long time to find a quality match. If the time-to-match is too long, your players might give up or they could time out. However, if you only have a few requirements for matching players together, your players might get frustrated at being placed into "poor quality" matches.
To consider a real-world example, say that you have a very simple setup where you want to match players into games based on Quality of service (QoS) region and player skill, and you want to create 4v4 matches with "fair" teams. In this scenario, you could wait to create a perfect 4v4 game where all players are in their best QoS region and have skill ratings that will create a game with a roughly 50/50 chance of either team winning.
However, what if there aren't that many compatible players available? How long will you wait to create that match before giving up (and timing out)? How long before your players give up and quit? Should you put players in less optimal games if it means that they can at least play more quickly? How do you balance time-to-match versus match quality? These questions are some of the most important design questions when configuring matchmaking. How you design solutions to these issues has a large impact on match quality and time-to-match.
A common approach for handling these scenarios is to change your requirements based on the time a ticket has spent in matchmaking. This could include switching from an "optimal" matchmaking strategy to a "non-optimal" strategy, depending on the age of a ticket. In the 4v4 example, consider the following factors:
- An "optimal" match might have criteria such as being in a ticket's preferred QoS region, being a full game, or having an excellent skill match.
- A "non-optimal" match might be outside of the ticket's preferred QoS region, could be partially full, or could be a bad skill match. You define what criteria are important.
This is a scenario where it is recommended that you use multiple function configs to simultaneously run your optimal and non-optimal matchmaking algorithms. You can configure your "optimal" match function to build the best possible matches at all times, and configure your "non-optimal" function to query for tickets older than N seconds and then make non-optimal (but still acceptable) matches. When tickets overlap, you can use the Score field for your match proposals to ensure that the best proposal wins.
It is important to keep the rate at which a client polls for a match assignment balanced. If you poll too quickly, you can overwhelm the system at scale or get rate-limited. If you poll too slowly, your time-to-match rises.
It is recommended that you consider the following poll rates:
- Wait 1-2 seconds after POSTing a ticket before you being polling (this is because it generally takes a minimum of one second for a match to be made after a ticket is submitted)
- Wait 1+ seconds between GET polls
In a worst-case scenario, in which the last poll missed the match assignment, time-to-match is increased by your approximate wait time between GET polls, and the average scenario should be approximately half the worst-case scenario. For example, if you poll once every 2 seconds, your worst-case scenario increases your time-to-match by ~2s, and your average scenario increases your time-to-match by ~2s.
In some scenarios, is can be beneficial to intentionally increase your minimum time-to-match.
For example, you might want to increase your minimum time-to-match to create the following scenarios:
- Drive more tickets to backfill instead of new matches
- Wait for enough players to create a full match
- Wait for enough compatible players to make a high-quality match
The current backfill system (v1) has some inherent race conditions in which a backfill request might not be present in the system during a function registration window at a time when backfill players are needed. This can result in the backfill request not getting fulfilled, or in a worst-case scenario, a new server being allocated when it is not required.
You can use various methods to mitigate these race conditions, including creating a minimum time for new tickets to be considered only for backfill before they are considered for a "new server" game. Adding this wait time can increase time-to-match in a worst-case scenario, but for scenarios where servers are frequently requesting backfill, many of those tickets will be backfilled and not have to wait.
This is generally implemented by adding a "minimum time to wait before creating a new match" variable to the function config. You can use this value when querying for tickets from the ticket database by adding a filter on the ticket's
created time to ensure that only tickets older than
now - timeToWait are matched by the function. Servers that perform backfill ignore this value or set it to 0 so they can process all tickets.
In scenarios where a full match is preferred, but it could take a long time to create one (such as when playing in low-popularity game modes or in developer or QA tests), you might want to implement ways to intentionally slow down matchmaking to allow these full games to form. Having configurable values for criteria such as
minimum players required,
time before creating a minimum player match, and
time before ticket timeout allows you to create matchmaking configurations that can wait much longer than normal for a full game to be ready.
In some scenarios, a developer might value speed over quality, and in other scenarios, they might value quality over speed. Both of these strategies are valid. Sometimes, intentionally waiting a long time for a "good match" might be preferred or required.
Consider a game that supports both "unranked" PvP and "ranked" PvP modes. A developer might want to configure their unranked PvP matchmaking to be fast, and then start reducing match quality based on how long players are waiting in matchmaking to get players into a game faster. However, in their ranked PvP mode, the developer has decided that playing a fair match is the most important consideration, and they refuse to make matches that would be unfair. In this scenario, the ranked matches will probably take much longer to make than the unranked ones.
In addition, consider an official online tournament example, with prizes that have real-world value. In these types of events, cheating can be rampant, so spending extra time to ensure a good match can help to avoid player dissatisfaction.
Some users of the Unity Quality of service (QoS) package in combination with Visual Studio Tools for Unity and Visual Studio 2019 might get compile errors in Visual Studio 2019 that do not occur in the Unity editor. The specific error that might be seen is:
CS8385 "The given expression cannot be used in a fixed statement".
This issue has been confirmed as a bug in the VS/C# compiler integration. The project successfully builds in the editor, but has this error in Visual Studio 2019. If you are seeing this issue, it is recommended that you use either of the following workarounds until the bug has been resolved:
Debug / Attach Unity Debuggerfrom Visual Studio 2019 instead of using the
Playbutton. This does not require that the project builds successfully in Visual Studio 2019 before attaching to the debugger.
- In Visual Studio 2019, set
Tools / Options / Tools for Unity / Disable the full build of projectto
false. In this scenario, the user still sees the error in the code, but the project should build successfully.