Skip to main content

SDK Integration

Beginning a Session

Once the SDK is authenticated, a Matchmaking session can be created by calling the BeginSession() function, as demonstrated in the example below.

#include "FACEITGameClientSDK/Matchmaking/Matchmaking.h"
#include "FACEITGameClientSDK/Utility/StringHelpers.h"

void MyGameClientInstance::BeginMatchmakingSession()
{
// Create some session settings.
FGCSDK_MM_BeginSessionSettingsData settings {};

// The session's user data can be whatever is convenient for the use case.
// User data is passed into any callbacks that are called by the
// Matchmaking implementation. For a persistent game instance class,
// passing "this" allows you to easily call functions on the instance
// when responding to a callback.
settings.userData = this;

// For the purposes of this example, we set auto check-in to
// be false, to demonstrate how to use CheckIntoCurrentMatch().
settings.autoCheckIn = false;

// Set up some callbacks to handle events.
// The implementation for these callbacks is explained
// in detail later.
settings.onUserJoinedQueueCallback = &Callback_MM_OnUserJoinedQueue;
settings.onUserLeftQueueCallback = &Callback_MM_OnUserLeftQueue;
settings.onUserStatusChangedCallback = &Callback_MM_OnUserStatusChanged;
settings.onMatchFoundCallback = &Callback_MM_OnMatchFound;
settings.onMatchReadyCallback = &Callback_MM_OnMatchReady;
settings.onMatchFinishedCallback = &Callback_MM_OnMatchFinished;
settings.onQueueUpdatedCallback = &Callback_MM_OnQueueUpdated;

// Make a request to begin the session.
FGCSDK_MM_ResultCode result =
FGCSDK_MM_BeginSession(&settings, &Callback_MM_OnBeginSession);

// If the result is not OK, the session could not be started.
// This is usually due to some user error such as not having
// completed the authentication process yet.
if ( result != FGCSDK_MM_OK )
{
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Matchmaking session failed to start: %s",
FGCSDK_ToString(&FGCSDK_MM_ResultCode_GetDescription, result).c_str());

return;
}
}

When the callback passed to BeginSession() is called and returns a successful result, the Matchmaking session is prepared and ready for use.

// Static callback function:
void Callback_MM_OnBeginSession(const FGCSDK_MM_BeginSessionReadyInfo* data)
{
// Call into game instance - see below.
static_cast<MyGameClientInstance*>(data->userData)
->MM_OnBeginSession(data);
}

void MyGameClientInstance::MM_OnBeginSession(const FGCSDK_MM_BeginSessionReadyInfo* data)
{
if ( data->result == FGCSDK_MM_OK )
{
// The matchmaking session has been started successfully.
// We can now perform the rest of the required setup.
// See the "Discovering Queues" section
// for the definition of this function.
PopulateDataFromAvailableMMQueues();
}
else
{
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Matchmaking session failed to start: %s",
FGCSDK_ToString(&FGCSDK_MM_ResultCode_GetDescription, result).c_str());

return;
}
}

Discovering Queues

At this stage, the list of queues available for the current game can be fetched by calling GetQueues(). The number of queues that may be interacted with via the Matchmaking interface will not change during the course of a session, so the game may safely keep its own list of data corresponding to each of these queues. However, some properties that are part of each queue may change during the session: specifically, whether the user is known to currently be in the queue, and whether the queue itself is open or closed.

The example below demonstrates how to obtain a list of queues once a Matchmaking session has been started.

// This function is called from the previous example, in response
// to the successful initialisation of a matchmaking session.
void MyGameClientInstance::PopulateDataFromAvailableMMQueues()
{
// The Matchmaking interface provides access to information about queues
// by copying the data into a user-specified array. In this example,
// we use a vector that is kept as a member of the game instance,
// which stores a list of FGCSDK_MM_Queue structs directly.
// Alternatively, a game could easily define its own data structure,
// obtain the Matchmaking queue list using a local vector of
// FGCSDK_MM_Queue structs, and then convert this local data into
// the appropriate format to be stored persistently.
std::vector<FGCSDK_MM_Queue>& queues = m_Queues;

// First, resize the vector to accommodate the number of queues.
// If nullptr and 0 are supplied to GetQueues(), no data is
// copied out, but the total number of queues is still returned.
queues.resize(FGCSDK_MM_GetQueues(nullptr, 0));

// Now that the vector is of the correct size, pass in its base
// pointer and length to receive the queue data.
FGCSDK_MM_GetQueues(queues.data(), queues.size());
}

Joining a Queue

The Matchmaking interface allows users to join or leave queues that are available for the current game. When a user is in a queue, the FACEIT platform will try to match them with other players waiting in that queue. Players are automatically removed from their queue when they are matched together.

The Matchmaking interface places some restrictions on the queues a player may join:

  • The queue must belong to a FACEIT matchmaking entity for the current game.
  • The queue must currently be open.
  • The player must not already be present in the queue.
  • The partySize set on the queue must allow the player's current party size.
    • If the player is not in a party, they may only join solo queues (queues with a partySize of 1).

To join a queue, call JoinQueue() on the Matchmaking interface as in the following example, providing the ID of the queue you wish to join.

void MyGameClientInstance::OnUserClickedJoinQueue(const std::string& queueID)
{
// Make the request to join the queue.
FGCSDK_MM_ResultCode result = FGCSDK_MM_JoinQueue(queueID.c_str(),
[](const FGCSDK_BasicWebRequestResponseInfo* response)
{
if ( response->errorType == FGCSDK_REQ_NO_ERROR )
{
// The user successfully joined the queue.
}
else
{
// The FACEIT platform returned an error.
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Failed to join queue. Error: %s", response->summary);
}
}
);

// If the result is not OK, the request could not be initiated.
// This is usually due to some pre-validation error, such as
// the Matchmaking interface knowing that the user is already
// present in the queue.
if ( result != FGCSDK_MM_OK )
{
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Failed to make request to join queue. Error: %s",
FGCSDK_ToString(&FGCSDK_MM_ResultCode_GetDescription, result).c_str());
}
}

If the JoinQueue() request is successful, this will trigger a call to the onUserJoinedQueueCallback function once the FACEIT platform propagates the event, in addition to calling the JoinQueue() callback with the request response.

info

FACEIT recommends that the onUserJoinedQueueCallback function always be treated as the source of truth for whether a player is present in a queue, rather than simply relying on the JoinQueue() result callback. This is because users can be placed into queues for reasons other than having made a JoinQueue() call: for example, they could have joined a queue via the FACEIT web frontend.

A successful call to JoinQueue() will always trigger the onUserJoinedQueueCallback.

Leaving a Queue

Equivalently, you can leave a queue by calling LeaveQueue(), providing the ID of the queue you wish to leave.

void MyGameClientInstance::OnUserClickedLeaveQueue(const std::string& queueID)
{
// Make the request to leave the queue.
FGCSDK_MM_ResultCode result = FGCSDK_MM_LeaveQueue(queueID.c_str(),
[](const FGCSDK_BasicWebRequestResponseInfo* response)
{
if ( response->errorType == FGCSDK_REQ_NO_ERROR )
{
// The user successfully left the queue.
}
else
{
// The FACEIT platform returned an error.
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Failed to leave queue. Error: %s", response->summary);
}
}
);

// If the result is not OK, the request could not be initiated.
// This is usually due to some pre-validation error, such as
// the Matchmaking interface knowing that the user is not present
// in the queue.
if ( result != FGCSDK_MM_OK )
{
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Failed to make request to leave queue. Error: %s",
FGCSDK_ToString(&FGCSDK_MM_ResultCode_GetDescription, result).c_str());
}
}

If the LeaveQueue() request is successful, this will trigger a call to the onUserLeftQueueCallback function once the FACEIT platform propagates the event, in addition to calling the LeaveQueue() callback with the request response.

info

FACEIT recommends that the onUserLeftQueueCallback function always be treated as the source of truth for whether a player has left a queue, rather than simply relying on the LeaveQueue() result callback. This is because users can be removed from queues for reasons other than having made a LeaveQueue() call: for example, they could have left a queue via the FACEIT web frontend.

A successful call to LeaveQueue() will always trigger the onUserLeftQueueCallback.

Queue Events

When the user joins or leaves a queue, the Matchmaking interface calls the onUserJoinedQueueCallback and onUserLeftQueueCallback functions provided in the session settings. The game may handle these events by updating the data it stores locally about the queue.

Note that the GetQueuesContainingUser() function may be called at any time to obtain a list of queues the user is currently part of. This does not require an asynchronous call.

// Static callback function:
void Callback_MM_OnUserJoinedQueue(const FGCSDK_MM_OnUserJoinedQueueInfo* data)
{
// Call into game instance - see below.
static_cast<MyGameClientInstance*>(data->userData)
->MM_OnUserJoinedQueue(data);
}

void MyGameClientInstance::MM_OnUserJoinedQueue(const FGCSDK_MM_OnUserJoinedQueueInfo* data)
{
// The ID of the queue that the user joined is provided in the event data.
// We update the data in the game instance's queue list to reflect the change.
// Note that matchmaking->GetQueuesContainingUser() may also be used to
// obtain a list of queues that the user is currently in.
for ( FGCSDK_MM_Queue& queue : m_Queues )
{
if ( strcmp(queue.id, data->queueID) == 0 )
{
// Record that this queue now contains the user.
// This data update could be used to display an
// indicator in the game's UI, for example.
queue.containsUser = true;
}
}
}
// Static callback function:
void Callback_MM_OnUserLeftQueue(const FGCSDK_MM_OnUserLeftQueueInfo* data)
{
// Call into game instance - see below.
static_cast<MyGameClientInstance*>(data->userData)
->MM_OnUserLeftQueue(data);
}

void MyGameClientInstance::MM_OnUserLeftQueue(const FGCSDK_MM_OnUserLeftQueueInfo* data)
{
// The ID of the queue that the user left is provided in the event data.
// We update the data in the game instance's queue list to reflect the change.
for ( FGCSDK_MM_Queue& queue : m_Queues )
{
if ( strcmp(queue.id, data->queueID) == 0 )
{
// Record that this queue no longer contains the user.
// This data update could be used to remove an
// indicator from the game's UI, for example.
queue.containsUser = false;
}
}
}

In addition to users joining or leaving queues, queues may also be opened or closed by administrators under exceptional circumstances. When this happens, the onQueueUpdatedCallback is called to report the new state. The game may handle this event by updating the data it stores locally about the queue. However, note that the Matchmaking interface also tracks this state internally, and will automatically disallow requests to join queues which are closed.

// Static callback function:
void Callback_MM_OnQueueUpdated(const FGCSDK_MM_OnQueueUpdatedInfo* data)
{
// Call into game instance - see below.
static_cast<MyGameClientInstance*>(data->userData)
->MM_OnQueueUpdated(data);
}

void MyGameClientInstance::MM_OnQueueUpdated(const FGCSDK_MM_OnQueueUpdatedInfo* data)
{
// The ID of the queue that the user left is provided in the event data.
// We update the data in the game instance's queue list to reflect the change.
for ( FGCSDK_MM_Queue& queue : instance->m_Queues )
{
if ( strcmp(queue.id, data.queueID) == 0 )
{
// Record the queue's new state.
queue.isOpen = data.isOpen
}
}
}

User Status

The Matchmaking interface exposes a "user status" value, which describes the state of the current user with respect to the matchmaking session. The user status can take one of the following values:

  • Available: The user is not performing any specific action with respect to the current game's matchmaking, and is able to join queues and potentially be matched with other players.
  • In Queue: The user has joined at least one queue for the current game. Note that this state is not set if the user is part of matchmaking queues for other games, as this has no impact on the current matchmaking session.
  • In Match: The user is part of an ongoing match for the current game. Note that this state is not set if the user is part of an ongoing match for a different game: this results in the user being classified as unavailable instead.
  • Unavailable: The user is not available to participate in the current matchmaking session. For example, if the user is currently in a match for a different game, or if no matchmaking session has been started for the current game, the user will be classified as unavailable.

When the user status changes, the onUserStatusChangedCallback function is called. The current user status can also be obtained by calling GetUserStatus() on the Matchmaking interface.

The Matchmaking interface will automatically refuse requests for actions that do not apply to the current user status, eg. attempting to join a queue while the user is in a match.

Match Callbacks

There are three callbacks provided to the Matchmaking session that relate to finding and playing matches:

  • The onMatchFoundCallback function is called when a potential match is found for the user. All players involved in this match must "check in" within the required time window.
  • If this match proceeds to be assigned to a game server and become ready for play, the onMatchReadyCallback function is called.
  • Whenever a match that has been found finishes for any reason, the onMatchFinishedCallback function is called.

In addition to these callbacks, the Matchmaking interface exposes the HasCurrentMatch() function. The session is considered to have a current match if:

  • A match has been found (ie. the onMatchFoundCallback function has been called), and
  • The match has not yet finished (ie. the onMatchFinishedCallback function has not been called).

The return value of HasCurrentMatch() corresponds one-to-one with the In Match user status.

Checking Into a Match

When a match is found, the onMatchFoundCallback function is called, and the user must accept the match by invoking "check-in". There are two possible ways that this can happen:

  • If autoCheckIn is set to true when beginning a session, check-in is performed automatically by the Matchmaking interface as soon as a match is found.
  • If autoCheckIn is set to false when beginning a session, check-in must be performed manually by calling CheckIntoCurrentMatch(). This is to allow the user to consent to joining the match.

If not all players check into the match in time, the match will finish with a "NO_CHECKIN" state. Otherwise, match configuration will proceed on the FACEIT platform as normal.

The following example demonstrates performing manual check-in for a match.

// Static callback function:
void Callback_Callback_MM_OnMatchFound(const FGCSDK_MM_OnMatchFoundInfo* data)
{
// Call into game instance - see below.
static_cast<MyGameClientInstance*>(data->userData)
->MM_OnMatchFound(data);
}

void MyGameClientInstance::MM_OnMatchFound(const FGCSDK_MM_OnMatchFoundInfo* data)
{
// Take action now that a match has been found.
// One example could be to show a notification
// to the user, where they must click a button
// to accept.
ShowMatchFoundNotification(data);
}

// An example function that is called when the user accepts joining the match.
void MyGameClientInstance::OnUserClickedAcceptMatch()
{
// Check into the current match.
FGCSDK_MM_ResultCode result = FGCSDK_MM_CheckIntoCurrentMatch(
[](const FGCSDK_BasicWebRequestResponse* response)
{
if ( response->errorType == FGCSDK_REQ_NO_ERROR )
{
// Check-in succeeded.
}
else
{
// The FACEIT platform returned an error.
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Failed to check into current match. Error: %s", response->summary);
}
}
);

// If the result is not OK, check-in could not be performed.
if ( result != FGCSDK_MM_OK )
{
// Handle the error in an appropriate way
// (out of scope of this documentation).
Log("Failed to initiate check-in for current match. Error code: %s",
FGCSDK_ToString(&FGCSDK_MM_ResultCode_GetDescription, result).c_str());
}
}

Joining a Ready Match

When a match is completely configured and ready for players to join a server, the onMatchReadyCallback function is called. The connect information required to connect to the match is passed as a parameter. This is provided as a JSON string, in the format configured for the game on the FACEIT Game Studio.

The game should respond to this event by connecting the player to the provided server.

// Static callback function:
void Callback_MM_OnMatchReady(const FGCSDK_MM_OnMatchReadyInfo* data)
{
// Call into game instance - see below.
static_cast<MyGameClientInstance*>(data->userData)
->MM_OnMatchReady(data);
}

void MyGameServerInstance::MM_OnMatchReady(const FGCSDK_MM_OnMatchReadyInfo* data)
{
// Pass on the connect information. It is up to the game itself
// to parse the JSON appropriately and connect to the server.
ConnectToServer(data->connectInformation);
}

Acknowledging a Finished Match

When a match finishes, the onMatchFinishedCallback function is called. The state that the match finished in is passed as a string parameter. This may be one of:

  • "FINISHED": The match ran to completion as normal.
  • "ABORTED": The match was set up on a game server, but it never started properly because not all the required players connected in time.
  • "CANCELLED": The match was cancelled by an administrator on FACEIT. It may or may not have been assigned to a game server before the cancellation occurred.
  • "NO_CHECKIN": The match was found, but not all the required players checked into the match, so it never passed the check-in phase.

The game may respond to this event by, for example, returning the player to the main menu.

// Static callback function:
void Callback_MM_OnMatchFinished(const FGCSDK_MM_OnMatchFinishedInfo* data)
{
// Call into game instance - see below.
static_cast<MyGameClientInstance*>(data->userData)
->MM_OnMatchFinished(data);
}

void MyGameClientInstance::MM_OnMatchFinished(const FGCSDK_MM_OnMatchFinishedInfo* data)
{
// In this example, we return the game to the main menu,
// passing a localised string to be displayed as the reason.
// In a real use case, the most appropriate course of action
// once a match has finished will depend on the game itself.
ShowMainMenu(LocalisedMatchFinishReason(data->state));
}

Getting the Current Party ID

The ID of the user's current party can be acquired by calling GetPartyID(). A matchmaking session must be active when this function is called.

If the party ID string is not empty, this means that the player is part of a party. If it is empty, this means that the player is not part of any party.

Players in a party may only join party queues, and players who are not in a party may only join solo queues. Note that queues may have additional restrictions, eg. that the party must be of a certain size.

#include "FACEITGameClientSDK/Matchmaking/Matchmaking.h"
#include "FACEITGameClientSDK/Utilities/StringHelpers.h"

std::string MyGameServerInstance::GetCurrentPartyID()
{
return FGSSDK_ToString(&FGCSDK_MM_GetPartyID);
}