Integrate using C++
The following section shows how to integrate with the Matchmaker SDK using the Unreal Engine Subsystems.
The Unity Gaming Services SDK offers two matchmaker interfaces:
Add the Matchmaker SDK as a dependency
Before continuing, add the MatchmakerSDK
as a public dependency of your module, then include the plugin header files in your classes as shown below.
Add MatchmakerServer
and MatchmakerClient
to your module's dependencies to your Unreal project build file (YourProjectName.Build.cs
):
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
PublicDependencyModuleNames.AddRange(new string[] { "MatchmakerClient", "MatchmakerServer" });
PublicDependencyModuleNames.AddRange(new string[] { "Json", "JsonUtilities" });
Include the plugin header files you want to access in your classes:
#include "MatchmakerClientSubsystem.h"
#include "MatchmakerServerSubsystem.h"
Matchmaker Client Subsystem
Note: Before accessing the Matchmaking Client Subsystem, ensure that you set up Authentication.
The Matchmaker Client Subsystem controls the client portion of matchmaking and finding matches. This includes creating, deleting, and polling matchmaking tickets.
You can access the Matchmaker Client Subsystem by getting a reference to the UMatchmakerClientSubsystem
subsystem. UMatchmakerClientSubsystem
is a UGameInstanceSubsystem
you can retrieve from UGameInstance
.
UWorld* GameWorld = GetWorld();
UGameInstance* GameInstance = GameWorld->GetGameInstance();
UMatchmakerClientSubsystem* MatchmakerClientSubsystem = GameInstance->GetSubsystem<UMatchmakerClientSubsystem>();
CreateTicket
Use the CreateTicket()
method to start the matchmaking process by creating a matchmaking ticket for the clients.
Calling this method adds all valid players in the Players
array to the matchmaking queue as a group and joins a match as part of the same team.
There are multiple parameters you can pass in, but the only required parameter is the Players list (with a minimum of one player using a valid ID). Other optional parameters include Queue Name, Qos Results, and custom data for players.
// Create Players array
TArray<FMatchmakerPlayer> Players;
// Create player definition
FMatchmakerPlayer SamplePlayer;
SamplePlayer.Id = TEXT("SamplePlayer");
// Adding custom player data
TSharedPtr<FJsonObject> CustomData = MakeShared<FJsonObject>();
CustomData->SetNumberField("Skill", 100);
CustomData->SetStringField("PreferredMap", "Dune");
SamplePlayer.CustomData = CustomData;
Players.Add(SamplePlayer);
// Setup Ticket Options
FCreateTicketOptions Options;
Options.QueueName = TEXT("default-queue");
MatchmakerClientSubsystem->CreateTicket(Players, options, Unity::Services::Core::THandler<FCreateTicketResponse>::CreateLambda([this](FCreateTicketResponse Response)
{
// Your response logic here (response includes Ticket Id)
}));
You can handle the response from the SDK using THandler which accepts a FCreateTicketResponse
.
GetTicketStatus
Poll for a client's ticket status against the matchmaker service by using the GetTicketStatus()
method.
You should poll continuously in a loop until you retrieve a response indicating either a success or failure of the matchmaking process.
To achieve this, use FTimerDelegate
to start a timer as in the following example:
// Create the Timer Delegate and set the function to call
FTimerDelegate PollTicketDelegate = FTimerDelegate::CreateUObject(this, &UMyCustomClass::PollTicket, Response.TicketId);
// Start the Timer that runs every 5 seconds, cache PollTicketTimerHandler to use for stopping the timer later GEngine->GameViewport->GetWorld()->GetTimerManager().SetTimer(PollTicketTimerHandle, PollTicketDelegate, 5, true);
Then your poll match function would look similar to the following example:
void UMyCustomClass::PollTicket(FGuid TicketId)
{
MatchmakerClientSubsystem->GetTicketStatus(TicketId, THandler<FGetTicketStatusResponse>::CreateLambda([this, TicketId](FGetTicketStatusResponse Response)
{
if (Response.bWasSuccessful)
{
FString TicketIdStr = *TicketId.ToString(EGuidFormats::DigitsWithHyphens).ToLower();
switch (Response.Status)
{
case StatusEnum::Failed:
UE_LOG(LogTemp, Log, TEXT("polling for ticket has failed, response was: %s"), *Response.ErrorMessage);
break;
case StatusEnum::Timeout:
UE_LOG(LogTemp, Log, TEXT("polling for ticket has timed out, response was: %s"), *Response.ErrorMessage);
break;
case StatusEnum::InProgress:
UE_LOG(LogTemp, Log, TEXT("polling for ticket %s is still in progress."), *TicketId.ToString(EGuidFormats::DigitsWithHyphens).ToLower());
return;
case StatusEnum::Found:
UE_LOG(LogTemp, Log, TEXT("polling for ticket has completed, match found with ip: %s"), *Response.Ip);
break;
default:
UE_LOG(LogTemp, Log, TEXT("unknown status received for ticket: %s), *TicketId.ToString(EGuidFormats::DigitsWithHyphens).ToLower());
break;
}
// If we made it this far, we got a status other than InProgress so we can end the polling timer.
GEngine->GameViewport->GetWorld()->GetTimerManager().ClearTimer(PollTicketTimerHandle);
// Delete ticket here
}
else
{
UE_LOG(LogTemp, Log, TEXT("Failed to poll Ticket status with ticket ID: %s"), *TicketId.ToString(EGuidFormats::DigitsWithHyphens).ToLower());
}
}));
}
You can handle the response from the SDK using a THandler which accepts a FGetTicketStatusResponse
.
DeleteTicket
Use the DeleteTicket()
method to delete a matchmaking ticket and cancel matchmaking. This is typically done after a match is found (successfully or not) or when a client no longer wants to be considered for the matchmaking process.
You can handle the response from the SDK in a response handler which accepts a FDeleteTicketResponse
.
MatchmakerClientSubsystem->DeleteTicket(TicketId, Unity::Services::Core::THandler<FDeleteTicketResponse>::CreateLambda([](FDeleteTicketResponse Response)
{
// Your response logic here
}));
You can handle the response from the SDK using a THandler which accepts a FDeleteTicketResponse
.
Matchmaker Server Subsystem
The Matchmaker Server Subsystem controls the server portion of matchmaking. This includes creating, approving, deleting, and updating backfill tickets.
Before you can use the UMatchmakerServerSubsystem
, you must retrieve it as shown in the following code snippet:
UWorld* GameWorld = GetWorld();
UGameInstance* GameInstance = GameWorld->GetGameInstance();
UMatchmakerServerSubsystem* MatchmakerServerSubsystem = GameInstance->GetSubsystem<UMatchmakerServerSubsystem>();
Note: Matchmaker is at the time of writing expected to be used solely with Multiplay Servers and as such it is important to note that the following server functions are expected to be used with some of the Multiplay SDK functionality.
See Allocation Payload for more details (access with GetPayloadAllocation()
with Multiplay’s Subsystem). This is used to initially fill MatchProperties.
CreateBackfillTicket
You need to create a new backfill ticket when a player or players leave a full match, and the server needs to fill in the empty slots. To create a new backfill ticket for the server, use the CreateBackfillTicket()
method.
The following code snippet shows how to create a new backfill ticket:
// Setup Backfill Ticket Options
FCreateBackfillTicketOptions CreateBackfillTicketOptions;
CreateBackfillTicketOptions.Connection = TEXT("35.245.90.171:9000");
CreateBackfillTicketOptions.QueueName = TEXT("default-queue");
// Create Match Properties
FMatchProperties MatchProperties;
FGuid BackfillTicketId = FGuid::NewGuid();
MatchProperties.BackfillTicketId = BackfillTicket.Id;
MatchProperties.Region = TEXT("aaaaaaaa-1111-bbbb-2222-cccccccccccc");
// Create Player
FMatchmakerPlayer Player;
Player.Id = FGuid::NewGuid().ToString();
FQosResult QosResult;
QosResult.Latency = 20;
QosResult.PacketLoss = 5;
QosResult.Region = TEXT("aaaaaaaa-1111-bbbb-2222-cccccccccccc");
Player.QoSResults.Add(QosResult);
MatchProperties.Players.Add(Player);
// Create Team
FMatchmakerTeam Team;
Team.TeamName = TEXT("Red");
FGuid TeamId = FGuid::NewGuid();
Team.TeamId = TeamId.ToString(EGuidFormats::DigitsWithHyphens);
Team.PlayerIds.Add(Player.Id);
MatchProperties.Teams.Add(Team);
CreateBackfillTicketOptions.Properties.MatchProperties = MatchProperties;
MatchmakerServerSubsystem->CreateBackfillTicket(CreateBackfillTicketOptions, Unity::Services::Core::THandler<FCreateBackfillTicketResponse>::CreateLambda([this, Player, Team](FCreateBackfillTicketResponse CreateResponse)
{
// Your response logic here
}));
You can handle the response from the SDK using a THandler which accepts a FCreateBackfillTicketResponse
.
ApproveBackfillTicket
To approve a backfill ticket after creation, use the ApproveBackfillTicket()
method. Approving a backfill ticket allows new players into the server.
It's recommended to approve backfill tickets no faster than once a second. The Unity Matchmaker deletes tickets if they haven’t been approved in 20 seconds.
The following code snippet shows how to approve a backfill ticket:
MatchmakerServerSubsystem->ApproveBackfillTicket(BackfillTicketId, Unity::Services::Core::THandler<FApproveBackfillTicketResponse>::CreateLambda([this](FApproveBackfillTicketResponse ApproveResponse)
{
// Your response logic here
}));
You can handle the response from the SDK using a THandler which accepts a FApproveBackfillTicketResponse
.
DeleteBackfillTicket
Delete a backfill ticket when a match becomes full, and you no longer need the server to accept new players. You should also do this after a match concludes and you no longer want the server to receive new players. To stop backfilling on a server, use the DeleteBackfillTicket()
method.
The following code snippet shows how to delete a backfill ticket:
MatchmakerServerSubsystem->DeleteBackfillTicket(BackfillTicketId, Unity::Services::Core::THandler<FDeleteBackfillTicketResponse>::CreateLambda([this](FDeleteBackfillTicketResponse DeleteResponse)
{
// Handle response here
}));
You can handle the response from the SDK using a THandler which accepts a FDeleteBackfillTicketResponse
.
UpdateBackfillTicket
To update a server's current backfill ticket, use the UpdateBackfillTicket()
method.
Update a backfill ticket anytime a player leaves the server or anytime a player joins the server from outside of matchmaking logic. This can include (but isn't limited to) party invites, direct connections, and friend invites.
You should update backfill tickets no more than once every three seconds or after an approved backfill ticket sees a change in the backfill ticket to ensure that a matchmaking cycle has passed. Updating a backfill ticket too often can cause players to never backfill into the match. See Matchmaking Logic Sample to learn more.
// From Approve
FBackfillTicket BackfillTicket;
FGuid BackfillTicketId = FGuid::Parse(ApproveResponse.BackfillTicket.Id, BackfillTicketId);
FBackfillTicket BackfillTicket = ApproveResponse.BackfillTicket;
// Setup MatchProperties
FMatchProperties MatchProperties;
MatchProperties.BackfillTicketId = BackfillTicket.Id; // Could be retrieved in Payload allocation's MatchProperties
MatchProperties.Region = TEXT("aaaaaaaa-1111-bbbb-2222-cccccccccccc"); // Could be retrieved in Payload allocation's MatchProperties
// Add a new player to the backfill ticket
FMatchmakerPlayer NewPlayer;
NewPlayer.Id = TEXT("NewPlayer");
TSharedPtr<FJsonObject> CustomData = MakeShared<FJsonObject>();
CustomData->SetNumberField("Skill", 100);
CustomData->SetStringField("PreferredMap", "Dune");
NewPlayer.CustomData = CustomData;
BackfillTicket.Properties.Players.Add(NewPlayer);
BackfillTicket.Properties.[0].PlayerIds.Add(NewPlayer.Id);
// Remove a Player from the backfill ticket
FString PlayerIdToRemove = TEXT("SamplePlayer");
for (FMatchmakerPlayer Player : BackfillTicket.Properties.Players)
{
if (Player.Id == PlayerIdToRemove)
{
BackfillTicket.Properties.Players.Remove(Player);
break;
}
}
if (BackfillTicket.Properties.Teams[0].PlayerIds.Contains(PlayerIdToRemove))
{
BackfillTicket.Properties.Teams[0].PlayerIds.Remove(PlayerIdToRemove);
}
// Call to update backfill ticket
MatchmakerServerSubsystem->UpdateBackfillTicket(BackfillTicketId, BackfillTicket, Unity::Services::Core::THandler<FUpdateBackfillTicketResponse>::CreateLambda([this, BackfillTicketId](FUpdateBackfillTicketResponse UpdateResponse)
{
// Handle Response here
}));
Unity recommends calling ApproveBackfillTicket
first and using the BackfillTicket
returned from ApproveBackfillTicket
to modify and pass into UpdateBackfillTicket
.
You can handle the response from the SDK using a THandler which accepts a FUpdateBackfillTicketResponse
.
Using THandler
The SDK includes custom blueprint-compatible delegates. Using the THandler is similar to a regular Unreal Engine delegate.
The following code snippet shows how to make a call with an in-line response:
Matchmaker::THandler<FCreateTicketResponse>::CreateLambda([ResponseDelegate](FCreateTicketResponse Response)
{
if (Response.bWasSuccessful)
{
UE_LOG(LogMatchmakerSDK, Log, TEXT("Successfully retrieved Ticket ID: %s"), *Response.TicketId);
}
else
{
UE_LOG(LogMatchmakerSDK, Error, TEXT("Failed to retrieve Ticket ID: %s"), *Response.ErrorMessage);
}
ResponseDelegate.ExecuteIfBound(Response);
}));