Matchmaking Tasks #
Matchmaking features are built and customized using a combination of calls and plugin methods. This page describes common matchmaking operations, along with descriptions of SDK actions and related Matchmaking plugin methods.
Common matchmaking operations include:
- Match parties
- Add more parties to a current game instance
- Add custom behavior after a matchmaking loop
- Continue matchmaking after creating or updating a game
- Leave matchmaking
- Generate custom matchmaking queue names
For a full list of all available Matchmaking SDK and plugin function, see Matchmaking Reference.
Initialize the matchmaking process #
Perform any validation logic or party/player transformations before adding the party to a matchmaking queue
The matchmaking process starts when parties enter the Matchmaking service from the Party service. The MatchmakingService.enterMatchmaking
call, which is called as part of PartyService.enterMatchmaking
, places the party into a Matchable object and invokes the Matchmaking Plugin’s initialize()
method.
interface MatchmakingPlugin {
fun initialize(
queueKey: MatchmakingQueueKey,
matchable: Matchmaking.Matchable
): NewGameInstance? {
return null
}
}
The Matchable passed to the initialize()
method will include an empty ExtMatchable
payload. Use initialize()
to set data on the ExtMatchable
before the Matchable enters the queue.
This method will have one of the following outcomes:
null
(default): A Matchable is added to the matchmaking queueNewGameInstance
: A party in the Matchable is fast-tracked to the game instance. For example, custom games for events like in-house tournaments may want to skip matchmaking and place players directly in a game instance regardless of skill level or other standard matchmaking data.
Related events:
- OnEnteredMatchmaking
- OnAddedToGameInstance (if fast-tracking to game instance)
Related errors:
Match parties in the matchmaking queue #
Design logic to compare parties to send to a game instance
You can customize the matchParties()
method to compare two Matchables in the matchmaking queue with the goal of creating a set of parties that should be sent to a game instance together. matchParties()
compares a specific anchor
Matchable with all other Matchables in the queue before changing anchor
to the next-oldest queue member.
interface MatchmakingPlugin {
fun matchParties(
queueKey: MatchmakingQueueKey,
anchor: Matchable,
other: Matchable
): NewGameInstance? {
return null
}
}
Within matchParties()
, do the following:
Compare parties in the
anchor
Matchable to parties in theother
Matchable using a set of developer-defined matchmaking data, such as skill level or preferred game server zones.Move parties between the two Matchables to work toward creating a set of parties to send to a new game instance. Both
anchor
andother
can take parties from one another usingtakePartiesFrom()
. When parties move between Matchables, that change persists for the life of the party unless the party is deliberately moved elsewhere.If a complete match is not found using parties in the anchor and other Matchable, return
null
to continue the matching parties with a new anchor.If a complete match is found, send appropriate parties to a new game instance together:
- Create a
NewGameInstance
, providingExtBackendCreateRequest
andgameServerZone
in the constructor. - Add parties to the game instance using
addParties()
. Added players are removed from matchmaking by exchanging the MATCHMAKING_ID on their sessions with a GAME_INSTANCE_ID. - Optionally, use
setTeamByPlayers()
andsetExtForPlayer()
to set teams and player data for the parties. - If you decide your new game instance should continue looking for parties after creation, call
newGameInstance.continueMatchmaking()
with aMatchmakingQueueKey
. In this case, the new game instance will be spun up and then will re-enter matchmaking as aMatchmaking.GameInstance
. See match parties with an active game instance - Return the
NewGameInstance
.
- Create a
For example:
override fun matchParties(
queueKey: MatchmakingQueueKey,
anchor: Matchable,
other: Matchable
): NewGameInstance? {
// Custom matchmaking logic
if (isCompleteMatch()) {
val newGameInstance = NewGameInstance(
requestExt = ExtBackendCreateRequest.getDefaultInstance(),
gameServerZone = "North America")
newGameInstance.addParties(allCompatibleParties)
return newGameInstance
} else {
anchor.takePartiesFrom(matchable, compatiblePartiesFromMatchable)
return null
}
}
If thematchParties()
method throws an exception, the Matchmaking service logs an error and removes both theanchor
Matchable and theother
Matchable from the queue to allow for debugging. Players are notified via aSessionChangedV1Notification
. The anchor position is then reset to the oldest Matchable in the queue.
See also: Add custom behavior after matchmaking loop.
Match parties with an active game instance #
Design logic to add parties to an existing game instances
When a game instance enters matchmaking to receive more parties, the Game Instance Matchmaking Plugin builds customized data about the game instance (ExtMatchmakingGameInstance
) and the players within it (ExtMatchmakingGamePlayer
). The data in these ext
payloads will be made available to the Matchmaking Plugin’s matchPartiesWithGame()
function via the Matchmaking.GameInstance
object when comparing game instances.
If game instance data (player list, ext
payloads, etc.) is updated while the game instance is in matchmaking, the changes are automatically sent matchmaking to ensure the matchmaking representation of the game instance is up to date.
suspend fun buildExtMatchmakingGameInstance(
gameInstance: GameInstance.GameInstance
): ExtMatchmakingGameInstance
suspend fun buildExtMatchmakingGamePlayer(
gameInstance: GameInstance.GameInstance,
player: GameInstance.GamePlayer
): ExtMatchmakingGamePlayer
Use matchPartiesWithGame()
to determine whether parties in a Matchable should be added to the existing game instance. The Matchmaking Plugin’s matchPartiesWithGame()
method is called for each anchor
Matchable in the specified matchmaking queue until stopMatchmaking()
is called.
interface MatchmakingPlugin {
fun matchPartiesWithGame(
queueKey: MatchmakingQueueKey,
anchor: Matchable,
game: Matchmaking.GameInstance
): GameInstanceUpdate? {
return null
}
}
Within matchPartiesWithGame()
, do the following:
Use developer-defined matchmaking data, such as skill level or preferred game server zones, to determine if a party or parties in the
anchor
should be added to the current game instance.If parties in the anchor Matchable should not added to the game instance:
- Return
null
to advance the anchor Matchable.
- Return
If parties in the Matchable should be added to the game instance:
- Create a game instance update object.
- Add appropriate parties to the game instance update. Any parties moved into the
GameInstanceUpdate
will be connected to the game server after this method completes: - Optionally, use
setTeamByPlayers()
andsetExtForPlayer()
to set teams and player data for the new parties.
Because
GameInstanceUpdate
objects are created with the express purpose of entering matchmaking, the matchmaking loop will continue until you explicitly callGameInstanceUpdate.stopMatchmaking()
.
For example:
override fun matchPartiesWithGame(
queueKey: MatchmakingQueueKey,
anchor: Matchmaking.Matchable,
game: Matchmaking.GameInstance
): GameInstanceUpdate? {
// Custom matchmaking logic
if (isCompleteMatch()) {
val gameInstanceUpdate = GameInstanceUpdate(
ExtBackendAddPlayerRequest.getDefaultInstance())
gameInstanceUpdate.addParties(allPartiesToAdd)
gameInstanceUpdate.stopMatchmaking()
return gameInstanceUpdate
} else {
game.takePartiesFrom(anchor, compatiblePartiesFromAnchor)
return null
}
}
Moving parties between Matchables does not automatically update team numbers. For existing game instances you can use theteamNumber
parameter in theaddParties()
method. Otherwise, use the functions in the Assign Teams section to change team numbers as necessary.
Comparison data #
When comparing Matchables, the following information is available to the Matchmaking Plugin for consideration in your matchmaking logic.
parties
: list of parties in the MatchablepartyId
: unique party IDext
(ExtMatchmakingParty
): custom party data to inform the matchmaking processpreferredGameServerZones
: list of the party’s preferred game server zonesplayers
: list of player objects within the partyplayerCount
: number of players in the partysecondsInQueue()
: number of seconds the party has been in the matchmaking queue
players
: list of all the players in the MatchableplayerId
: unique player IDteamNumber
: player’s team numberpartyId
: unique party ID for the party the player is ingameServerZoneToPing
: map of the player’s client-supplied ping map, keyed by gameServerZone IDext
(ExtMatchmakingPlayer
): custom player data to inform the matchmaking process
extMatchable
(ExtMatchable
): use this ext to store data calculated within the Matchmaking loop so you don’t have to recompute it on everymatchParties
callmatchmakingKey
(ExtMatchmakingKey
): custom data about the Matchable’s matchmaking queuegameServerVersion
: version of the game server the match would be played on
Related events:
Related errors:
Add custom behavior after matchmaking loop #
Define what happens after an anchor Matchable exhausts the matchmaking queue
If an anchor Matchable makes it through the entire queue without forming a complete match, the default behavior is that the anchor position is moved to the next oldest Matchable in the queue. Custom behavior can be added before the anchor position is moved by defining logic in the endOfLoop()
method. This method gives the plugin a chance to start a new game instance even if ideal matches were not made during the iteration. For example, when building matches with a limited matchmaking pool, such as during testing.
interface MatchmakingPlugin {
fun endOfLoop(
queueKey: MatchmakingQueueKey,
anchor: Matchable
): NewGameInstance?
return null
}
The endOfLoop()
method is also called if there is only one item in the matchmaking queue.
Leave matchmaking with the API #
Exit the matchmaking process using the SDK for Unreal or Unity
Players can request that their party leave matchmaking by invoking the PartyApi.LeaveMatchmaking()
SDK method. This action removes the player’s whole party from the matchmaking service. After the player and their party are removed from matchmaking, players receive a OnLeftMatchmaking
event. The PartyApi.LeaveMatchmaking()
function also triggers the Party Plugin’s returnFromMatchmaking()
method, which provides a way to handle players and parties leaving matchmaking. See Party Service Tasks: Return from Matchmaking for more information.
Player->PartyApi()->LeaveMatchmaking(
const FOnCompleteDelegate& OnComplete
)
player.PartyApi.LeaveMatchmaking(
CompleteDelegate onComplete
)
{
"requestId": 15,
"type": "matchmakingRpc.LeaveMatchmakingV2Request",
"payload": {
}
}
Game servers can request that an active game instance leave matchmaking by invoking MatchApi.LeaveMatchmaking()
.
Player->MatchApi()->LeaveMatchmaking(
const FString& GameInstanceId,
const FOnCompleteDelegate& OnComplete
)
player.MatchApi.LeaveMatchmaking(
PragmaId gameInstanceId,
CompleteDelegate onComplete
)
{
"requestId": 16,
"type": "matchmakingRpc.LeaveMatchmakingV2Request",
"payload": {
}
}
Customize queue names for reporting #
Customize queue names for metrics/logging
For purposes of viewing metrics and logs, you might want to group various matchmaking queues into one metric/log entry. You can generate a queue name using the Matchmaking Plugin’s getQueueName()
method, which is called during instantiation of a new matchmaking queue or when a metric/log requires a queue name and doesn’t already have access to the queue.
For example, say your ExtMatchmakingKey
contains a game mode, along with various other parameters needed for matchmaking purposes (region, difficulty, etc.). For reporting purposes, you might want matchmaking queues with the same game mode to appear as one metric. To do so, customize the getQueueName()
function in your implementation of the Matchmaking Plugin:
override fun getQueueName(extMatchmakingKey: ExtMatchmakingKey): String {
return extMatchmakingKey.gameMode;
}
If you don’t customize getQueueName()
, the function returns “undefinedqueueName” by default.