Manage backfill tickets using AGS SDK
Armada is not available on AGS Starter tier yet. It's coming soon!
Overview
This guide covers the basics of managing backfill ticket using AccelByte Gaming Services (AGS) Game SDK. The AGS Game SDK is used to interact with V2 sessions and matchmaking backfill.
Goals
- Fetch session information from a dedicated server.
- Accept or reject backfill proposals from a dedicated server.
- Enable or disable backfill from a dedicated server.
Prerequisites
You are familiar with our Lobby, Session and Matchmaking services.
You have knowledge of Unreal Engine, including use of the OnlineSubsystem (OSS).
You have access to the Admin Portal and the Namespace for your game.
You have configured a session template with type set to DS, and set the appropriate deployment.
You have configured a match pool and match ruleset, and associated them with the session template.
Your game client has been integrated with AGS Matchmaking.
You have V2 sessions enabled in your DefaultEngine.ini:
[OnlineSubsystemAccelByte]
bEnableV2Sessions=trueGame servers must have the followings permissions:
Permissions Action Usage NAMESPACE:{namespace}:MATCHMAKING:BACKFILL
CREATE, READ, UPDATE, DELETE
To manage backfill ticket
Backfill Proposal Flow
In this section, you will get an understanding of the backfill proposal flow, that is executed between the Matchmaking service and a dedicated server.
After a game session has been created and invites sent to the matched players, the Session service will request a dedicated server be provisioned through the DS Hub.
Once a dedicated server is ready, the connection information will be returned to the game session, and it will attempt to connect.
After the game session is successfully connected to the dedicated server, the server can query the session data.
If the game session is not full, it can match with additional players, if either auto-backfill has been enabled or by submitting a backfill ticket directly to the Matchmaking service.
If a backfill request has been submitted, either automatically or manually, the Matchmaking service will return proposals to the dedicated server.
The dedicated server will then process the proposal, and evaluate if it is viable. It is important to check if there is still room in the game session before accepting the proposal, as another player may have already taken the open spot due to a race condition.
The dedicated server then accepts or rejects the proposal, informing the Matchmaking service. If accepted, the new player will receive an invite to join the game session.
Fetch Session Information
Once a game session is connected to a dedicated server, players will be able to use the connection details stored in the session. This will enable them to connect with the dedicated server. At the same time, the server can query information about the sessions and connected players.
- OSS
- Unity
To begin with, you need to register our game server with Armada, by retrieving the V2 session interface:
FOnlineSessionV2AccelBytePtr SessionInterface;
if (!ensure(FOnlineSessionV2AccelByte::GetFromWorld(GetWorld(), SessionInterface)))
{
return;
}Listen for an event which marks session assignment to the server,
OnServerReceivedSession
:const FOnServerReceivedSessionDelegate OnServerReceivedSessionDelegate =
FOnServerReceivedSessionDelegate::CreateUObject(
this, &MyClass::OnServerReceivedSession);
FDelegateHandle OnServerReceivedSessionDelegateHandle =
SessionInterface->AddOnServerReceivedSessionDelegate_Handle(
OnServerReceivedSessionDelegate);Invoke the
RegisterServer
method:const FOnRegisterServerComplete OnRegisterServerCompleteDelegate =
FOnRegisterServerComplete::CreateUObject(
this, &MyClass::OnRegisterServerComplete);
SessionInterface->RegisterServer(SessionName, OnRegisterServerCompleteDelegate);Inside the delegate handler for
OnServerReceivedSession
, you can fetch the session the same way as a game client would. First, retrieve the session interface again, then the session:// Here `SessionName` is a param to the session received delegate
FNamedOnlineSession* Session = SessionInterface->GetNamedSession(SessionName);
if (!ensure(Session != nullptr))
{
return;
}The server can now interact with the session settings and data.
It's generally good practice to clear the
OnServerReceivedSession
delegate, using theFDelegateHandle
used earlier.
To begin with, you need to listen for an event which marks session assignment to the server.
var dsHub = AccelByteSDK.GetServerRegistry().GetApi().GetDsHub();
dsHub.MatchmakingV2ServerClaimed += result =>
{
if (result.IsError)
{
// Do something if MatchmakingV2ServerClaimed fails
Debug.Log($"Error MatchmakingV2ServerClaimed, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if MatchmakingV2ServerClaimed is successfully received
};Invoke the
RegisterServer
method.var dsm = AccelByteSDK.GetServerRegistry().GetApi().GetDedicatedServerManager();
int port = 7777;
dsm.RegisterServer(port, result =>
{
if (result.IsError)
{
// Do something if RegisterServer fails
Debug.Log($"Error RegisterServer, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if RegisterServer succeeds
});Inside
MatchmakingV2ServerClaimed
event, you can fetch the session id and interact with that to get the session detail.var session = AccelByteSDK.GetServerRegistry().GetApi().GetSession();
var sessionId = result.Value.sessionId;
var gameMode = result.Value.gameMode;
var matchingAllies = result.Value.matchingAllies;
session.GetGameSessionDetails(sessionId, sessionResult =>
{
if (result.IsError)
{
// Do something if GetGameSessionDetails fails
Debug.Log($"Error GetGameSessionDetails, Error Code: {sessionResult.Error.Code} Error Message: {sessionResult.Error.Message}");
return;
}
// Do something if GetGameSessionDetails is successfully received
});
Enable and Disable Backfill
- OSS
- Unity
Match backfill can be enabled in one of two ways. The first is as a configuration setting from within a match ruleset if it's applicable for your game mode. Or you can create and submit a backfill ticket directly from the dedicated server.
To enable backfill, retrieve the session interface, then create a backfill ticket:
const FOnCreateBackfillTicketComplete OnCreateBackfillTicketCompleteDelegate =
FOnCreateBackfillTicketComplete::CreateUObject(
this, &MyClass::OnCreateBackfillTicketComplete);
SessionInterface->CreateBackfillTicket(
NAME_GameSession, OnCreateBackfillTicketComplete);
You can also supply a match pool name to this method if you want a particular match pool other than the original.
To disable backfill, delete the backfill ticket:
const FOnDeleteBackfillTicketComplete OnDeleteBackfillTicketCompleteDelegate =
FOnDeleteBackfillTicketComplete::CreateUObject(
this, &MyClass::OnDeleteBackfillTicketComplete);
SessionInterface->DeleteBackfillTicket(
NAME_GameSession, OnDeleteBackfillTicketCompleteDelegate);
This feature is not yet supported in AGS Unity SDK. This snippet will be updated once the feature is already supported.
Handle backfill proposals
We recommend that the dedicated server periodically checks if the session is not at full capacity. This allows the server to recreate backfill tickets, as the team member of the session may be out of sync with the backfill ticket. By doing so, the existing game session has an opportunity to reach full capacity through the matchmaking process.
If session backfill has been requested, either by having auto-backfill configured in the match ruleset, or requested directly from the server, the server will begin to receive proposals if the game session has open spots for more players.
- OSS
- Unity
Matchmaking backfill proposals are sent to the dedicated server, where they can be accepted or rejected. Before registering the server, add a delegate handler for receiving backfill proposals:
FOnBackfillProposalReceivedDelegate OnBackfillProposalReceivedDelegate =
FOnBackfillProposalReceivedDelegate::CreateUObject(
this, &MyClass::OnBackfillProposalReceived);
FDelegateHandle OnBackfillProposalReceivedDelegateHandle =
SessionInterface->AddOnBackfillProposalReceivedDelegate_Handle(
OnBackfillProposalReceivedDelegate);
Inside that handler, you can decide whether to accept or reject the proposal. First, retrieve the session interface. Then, after some decision logic, accept or reject the proposal. To accept:
FOnAcceptBackfillProposalComplete OnAcceptBackfillProposalCompleteDelegate =
FOnAcceptBackfillProposalComplete::CreateUObject(
this, &MyClass::OnAcceptBackfillProposalComplete);
SessionInterface->AcceptBackfillProposal(
NAME_GameSession, Proposal, false, OnAcceptBackfillProposalCompleteDelegate)
Or reject:
FOnRejectBackfillProposalComplete OnRejectBackfillProposalCompleteDelegate =
FOnRejectBackfillProposalComplete::CreateUObject(
this, &MyClass::OnRejectBackfillProposalComplete);
SessionInterface->RejectBackfillProposal(
NAME_GameSession, Proposal, false, OnRejectBackfillProposalCompleteDelegate);
The third argument to both of the accept and reject methods is a boolean that tells the session interface whether you want to stop backfilling. In the examples, you will still receive backfill proposals after rejecting or accepting due to the false
argument passed to the respective methods.
Matchmaking backfill proposals are sent to the dedicated server, where they can be accepted or rejected. Before registering the server, add an event listener for receiving backfill proposals.
var dsHub = AccelByteSDK.GetServerRegistry().GetApi().GetDsHub();
dsHub.MatchmakingV2BackfillProposalReceived += result =>
{
if (result.IsError)
{
// Do something if MatchmakingV2BackfillProposalReceived fails
Debug.Log($"Error MatchmakingV2BackfillProposalReceived, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if MatchmakingV2BackfillProposalReceived is successfully received
// Implement accept or reject the backfill proposal function here
};
Inside that handler, you can decide whether to accept or reject the proposal. To accept the proposal, you can use the result value of MatchmakingV2BackfillProposalReceived
as a parameter.
var matchmaking = AccelByteSDK.GetServerRegistry().GetApi().GetMatchmakingV2();
// Proposal from `MatchmakingV2BackfillProposalReceived` result
var backfillProposal = result.Value;
// Set this to true if you want to stop the backfilling
bool stopBackfilling = false;
matchmaking.AcceptBackfillProposal(backfillProposal, stopBackfilling, acceptResult =>
{
if (result.IsError)
{
// Do something if AcceptBackfillProposal fails
Debug.Log($"Error AcceptBackfillProposal, Error Code: {acceptResult.Error.Code} Error Message: {acceptResult.Error.Message}");
return;
}
// Do something if AcceptBackfillProposal succeeds
});
If you want to reject the proposal, use this.
var matchmaking = AccelByteSDK.GetServerRegistry().GetApi().GetMatchmakingV2();
// Proposal from `MatchmakingV2BackfillProposalReceived` result
var backfillProposal = result.Value;
// Set this to true if you want to stop the backfilling
bool stopBackfilling = false;
matchmaking.RejectBackfillProposal(backfillProposal, stopBackfilling, rejectResult =>
{
if (result.IsError)
{
// Do something if RejectBackfillProposal fails
Debug.Log($"Error RejectBackfillProposal, Error Code: {rejectResult.Error.Code} Error Message: {rejectResult.Error.Message}");
return;
}
// Do something if RejectBackfillProposal succeeds
});
If, in case, you still receive the backfill proposals after rejecting or accepting them, it may be due to the stopBackfilling
parameter's value being set to false
.
Example: Get a Backfill Ticket ID
When the auto_backfill match ruleset setting is enabled and the matchmaking process produces a non-full game, a backfill ticket will be generated before the session is created. The resulting effect is that when a DS receives its session, if desired, the DS can check for the presence of a backfill ticket id to indicate whether the incoming match will have automatic backfill.
- OSS
- Unity
const FNamedOnlineSession* Session =
SessionInterface->GetNamedSession(NAME_GameSession);
if (!ensure(Session != nullptr))
{
return;
}
const TSharedPtr<FOnlineSessionInfoAccelByteV2> SessionInfo =
StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(Session->SessionInfo);
if (!ensure(SessionInfo.IsValid()))
{
return;
}
const TSharedPtr<FAccelByteModelsV2GameSession> SessionData =
SessionInfo->GetBackendSessionDataAsGameSession();
if (!ensure(SessionData.IsValid()))
{
return;
}
const bool bIsBackfillEnabled = !SessionData->BackfillTicketID.IsEmpty();
var session = AccelByteSDK.GetServerRegistry().GetApi().GetSession();
string sessionId = "current-session-id";
session.GetGameSessionDetails(sessionId, result =>
{
if (result.IsError)
{
// Do something if GetGameSessionDetails fails
Debug.Log($"Error GetGameSessionDetails, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// Do something if GetGameSessionDetails succeeds
bool isBackfillEnabled = !string.IsNullOrEmpty(result.Value.backfillTicketId);
});
Troubleshooting
In this section, you can find common errors and issues that may occur when using the service, along with recommendations on how to resolve them.
Session Update Fails due to a Version Conflict
Currently, a DS will not automatically receive session updates, so it's possible that the server can run into issues trying to update a session. When the session data on the DS side becomes stale, updating the session can result in a version conflict error. While it's possible to detect this kind of error from the error message logged when invoking UpdateSession
, you can also make use of an error handler for this specific case:
- OSS
- Unity
FOnSessionUpdateConflictErrorDelegate OnSessionUpdateConflictErrorDelegate =
FOnSessionUpdateConflictErrorDelegate::CreateUObject(
this, &MyClass::OnSessionUpdateConflictError);
SessionInterface->AddOnSessionUpdateConflictErrorDelegate_Handle(
OnSessionUpdateConflictErrorDelegate);
Because there is no real generic solution for the session service, or AccelByte OSS, to decide which session settings to update in the case of a conflict, the above delegate can be used to retry the update and/or to house logic for resolving conflicts.
When an update fails, the session data will automatically be refreshed from the backend, and the conflict error handler will receive a copy of the session settings passed to the failed update.
In Unity, if an update request failed because of old data, it will respond with error code ErrorCode.SessionVersionMismatch
.
If you received this error code, you must query the backend first to get the latest session data, then retry the update with the latest session version.