This quickstart guide walks you through the entire process for adding matchmaking to your game using the PlayFab Multiplayer SDK.
This tutorial illustrates how to submit a ticket to a specific queue in order to find a game. A queue likely maps to a game mode or multiple game modes (ex.: a capture the flag mode and a king of the hill mode in the same queue).
The matchmaking service handles finding a match among tickets in a queue. When a match is found, your title must handle connecting the players together for gameplay.
Obs!
The PlayFab Multiplayer SDK also provides APIs for PlayFab Lobbies.
The library matches users together who create tickets for queues that are configured in Game Manager. For details on how to set one up, see Configuring matchmaking queues.
Download and set up the PlayFab Multiplayer SDK
Download the C/C++ SDK for your platform and integrate the provider header and library files into your build.
Obs!
This quick start focuses on using the C/C++ SDK. For Unity and Unreal interfaces, see the following articles:
To use the PlayFab Lobby SDK, you need to authenticate your client using PlayFab entity keys and entity tokens. Acquire a PlayFab entity key and token pair by logging in with LoginWithCustomId REST API. This API is also available as a C/C++ projection via the PlayFab REST SDK.
Obs!
LoginWithCustomId is a quick way to get started with PlayFab features but isn't intended to be the login mechanism you ship with. For login guidance, see Login basics and best practices.
Initialize the PlayFab Multiplayer SDK
Initialize the PlayFab Multiplayer SDK by following these basic steps:
Set the entity key and token used by the library on behalf of your players by calling PFMultiplayerSetEntityToken.
C++
static PFMultiplayerHandle g_pfmHandle = nullptr;
...
...
HRESULT hr = S_OK;
// Initialize the PFMultiplayer library.
hr = PFMultiplayerInitialize(titleId, &g_pfmHandle);
if (FAILED(hr))
{
// handle initialize failure
}
// Set an entity token for a local user. The token is used to authenticate PlayFab operations on behalf of this user. // Tokens can expire, and this API token should be called again when this token is refreshed.
hr = PFMultiplayerSetEntityToken(g_pfmHandle, localUserEntity, entityToken);
if (FAILED(hr))
{
// handle set entity token failure
}
Create a matchmaking ticket
You create a matchmaking ticket using PFMultiplayerCreateMatchmakingTicket, in which you specify all local users that should be part of the match and any attributes you want associated with those users.
This function also takes a PFMatchmakingTicketConfiguration where you specify which queue the ticket is for, a timeout for the ticket, and any remote users that you want to match into this ticket.
Matchmaking with a single local user
You can start matchmaking for a single local user with a call to PFMultiplayerCreateMatchmakingTicket.
C++
constchar* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.
PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = yourQueueName;
constchar* attributes = "\"{\"color\":\"blue\", \"role\":\"tank\"}\"";
const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
g_pfmHandle,
1, // number of local users
localUserEntity,
&attributes,
&configuration,
nullptr, // optional asyncContext
&ticket);
RETURN_IF_FAILED(hr);
Matchmaking with a group of remote users
To start group matchmaking with remote users, it's helpful to think of one client as the leader. Have the leader create the ticket using PFMultiplayerCreateMatchmakingTicket, specifying the other users in the group through the configuration parameter. Once the ticket is created, call GetTicketId to get the ticket ID. Send this ID to each other user through an external mechanism, such as a networking mesh or a shared PlayFab Lobby, and have each client call PFMultiplayerJoinMatchmakingTicketFromId with the ticket ID to join the matchmaking ticket. The ticket status will be PFMatchmakingTicketStatus::WaitingForPlayers while waiting for the specified players to join and will change to PFMatchmakingTicketStatus::WaitingForMatch once all players have joined the ticket.
C++
// Creating the ticket on the leader's clientconstchar* remoteMemberEntityId1 = ...;
constchar* remoteMemberEntityId2 = ...;
std::vector<PFEntityKey> remoteMatchMemberEntityKeys;
remoteMatchMemberEntityKeys.push_back({ remoteMemberEntityId1, "title_player_account" });
remoteMatchMemberEntityKeys.push_back({ remoteMemberEntityId2, "title_player_account" });
constchar* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.
PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = yourQueueName;
configuration.membersToMatchWithCount = 2; // number of remote members to match with
configuration.membersToMatchWith = remoteMatchMemberEntityKeys.data();
constchar* attributes = "\"{\"color\":\"blue\", \"role\":\"tank\"}\"";
const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
g_pfmHandle,
1, // number of local users
localUserEntity,
&attributes,
&configuration,
nullptr, // optional asyncContext
&ticket);
RETURN_IF_FAILED(hr);
// Getting the ticket ID
PCSTR ticketId;
hr = PFMatchmakingTicketGetTicketId(ticket, &ticketId);
RETURN_IF_FAILED(hr);
C++
// Joining the ticket on the other players' clientsconstchar* attributes = "\"{\"color\":\"blue\", \"role\":\"healer\"}\"";
constchar* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerJoinMatchmakingTicketFromId(
g_pfmHandle,
1, // number of local users
localUserEntity,
&attributes,
ticketId,
yourQueueName,
nullptr, // optional asyncContext
&ticket);
Matchmaking with multiple local users
When matchmaking with multiple local users, instead of passing in one PFEntityKey to either the PFMultiplayerCreateMatchmakingTicket or the PFMultiplayerJoinMatchmakingTicketFromId functions, you need to pass in a list of keys. Similarly, you'll need to pass in a list of attributes for each user. Each list entry position should correspond with each other. Meaning that the first entry in attributes list should be the attributes for the first player in the PFEntityKey list.
C++
constchar* yourQueueName = ...; // This is the name of the queue you configured in Game Manager.
PFMatchmakingTicketConfiguration configuration{};
configuration.timeoutInSeconds = 120;
configuration.queueName = queueName;
std::vector<PFEntityKey> localMatchMemberEntityKeys{ ... };
std::vector<PCSTR> localMatchMemberAttributes{ ... };
const PFMatchmakingTicket* ticket;
HRESULT hr = PFMultiplayerCreateMatchmakingTicket(
g_pfmHandle,
static_cast<uint32_t>(localMatchMemberEntityKeys.size())
localMatchMemberEntityKeys.data(),
localMatchMemberAttributes.data(),
&configuration,
nullptr, // optional asyncContext
&ticket);
RETURN_IF_FAILED(hr);
The SDK will return a TicketStatusChanged state change any time the status of the ticket changes and a TicketCompleted state change when matchmaking has completed.
Example using the Matchmaking client SDK
C++
HRESULT hrTicketError = S_OK;
uint32_t stateChangeCount;
const PFMatchmakingStateChange * const * stateChanges;
hr = PFMultiplayerStartProcessingMatchmakingStateChanges(g_pfmHandle, &stateChangeCount, &stateChanges);
RETURN_IF_FAILED(hr);
for (uint32_t i = 0; i < stateChangeCount; ++i)
{
const PFMatchmakingStateChange& stateChange = *stateChanges[i];
switch (stateChange.stateChangeType)
{
case PFMatchmakingStateChangeType::TicketStatusChanged:
{
constauto& ticketStatusChanged = static_cast<const PFMatchmakingTicketStatusChangedStateChange&>(stateChange);
PFMatchmakingTicketStatus status;
if (SUCCEEDED(PFMatchmakingTicketGetStatus(ticketStatusChanged.ticket, &status)))
{
printf("Ticket status is now: %i.\n", status);
}
break;
}
case PFMatchmakingStateChangeType::TicketCompleted:
{
constauto& ticketCompleted = static_cast<const PFMatchmakingTicketCompletedStateChange&>(stateChange);
printf("PFMatchmaking completed with Result 0x%08x.\n", ticketCompleted.result);
if (FAILED(ticketCompleted.result))
{
// On failure, we must record the HRESULT so we can return the state change(s) and then bail// out of this function.
hrTicketError = ticketCompleted.result;
}
break;
}
}
}
hr = PFMultiplayerFinishProcessingMatchmakingStateChanges(g_pfmHandle, stateChangeCount, stateChanges);
RETURN_IF_FAILED(hr);
// Now that we've returned the state change(s), bail out if we detected ticket failure.
RETURN_IF_FAILED(hrTicketError);
Get the match
After receiving the PFMatchmakingStateChangeType::TicketCompleted state change, call PFMatchmakingTicketGetMatch to get the details of the match. These details will contain the match ID, the users who have been matched together, and the preferred region for the match, and an arrangement string for lobby associated with the match.
Once you've retrieved any information you need out of the PFMatchmakingMatchDetails struct, the ticket should be destroyed with PFMultiplayerDestroyMatchmakingTicket.
If for some reason your client wants to cancel the matchmaking process prior to the timeout set in the PFMatchmakingTicketConfiguration, call PFMatchmakingTicketCancel with the ticket handle.
Calling this API doesn't guarantee the ticket will be cancelled. The ticket could still complete before the cancellation can be processed, or the cancellation request may fail due to networking or service errors. You can still process matchmaking state changes to get the result of the ticket if you'd like to confirm the ticket cancellation has completed prior to moving on. Otherwise, you can immediately call PFMultiplayerDestroyMatchmakingTicket.
(Optional) Connecting your players together into a Lobby
After your players have matched, they can join up together in a lobby. The PFMatchmakingMatchDetails from the matched ticket contains a lobbyArrangementString field, which can be used to join the users into the same Lobby.
Use C#, Visual Studio, Unity, and the Mixed Reality Toolkit to create an interactive virtual reality experience compatible with the HoloLens. Inspired by *Space Jam: A New Legacy*, you create an experience for coaches and fans to learn more about the Tune Squad.