AGS SDKを使用してバックフィルチケットを管理する
注釈:本資料はAI技術を用いて翻訳されています。
概要
このガイドでは、AccelByte Gaming Services (AGS) Game SDK を使用してバックフィルチケットを管理する基本について説明します。AGS Game SDK は、AGS Matchmaking セッションとマッチメイキングバックフィルとやり取りするために使用されます。
目標
この記事では、以下の理解を提供することを目的としています。
- 専用サーバーからセッション情報を取得する。
- 専用サーバーからバックフィル提案を承認または拒否する。
- 専用サーバーからバックフィルを有効または無効にする。
前提条件
この記事のすべての手順を完了するには、以下が必要です。
-
AGS Lobby、Matchmaking、および Session に精通していること。
-
AGS Admin Portal へのアクセス。
-
専用サーバータイプの セッションテンプレート、マッチルールセット、および マッチプール を構成していること。
-
ゲームクライアントが AGS Matchmaking と統合されていること。
-
DefaultEngine.ini (Unreal Engine の場合) で V2 セッションが有効になっていること。
[OnlineSubsystemAccelByte]
bEnableV2Sessions=true -
ゲームサーバーが以下の権限を持っていること。
権限 アクション 用途 NAMESPACE:{namespace}:MATCHMAKING:BACKFILLCREATE, READ, UPDATE, DELETEバックフィルチケットを管理する
バックフィル提案フロー
このセクションでは、AGS Matchmaking と専用サーバー (DS) の間で実行されるバックフィル提案フローの概要を説明します。
-
ゲームセッションが作成され、マッチングされたプレイヤーに招待が送信された後、AGS Session は DS Hub を通じて専用サーバーのプロビジョニングを要求します。
-
DS の準備が整うと、接続情報がゲームセッションに返され、接続を試みます。
-
ゲームセッションが専用サーバーに正常に接続された後、サーバーはセッションデータをクエリできます。
-
ゲームセッションが満員でない場合、自動バックフィルが有効になっているか、バックフィルチケットを AGS Matchmaking に直接送信することで、追加のプレイヤーとマッチングできます。
-
バックフィルリクエストが送信された場合、自動または手動にかかわらず、AGS Matchmaking は専用サーバーに提案を返します。
-
専用サーバーは提案を処理し、実行可能かどうかを評価します。競合状態により、別のプレイヤーがすでに空いているスポットを取っている可能性があるため、提案を受け入れる前にゲームセッションにまだスペースがあるかどうかを確認することが重要です。
-
専用サーバーは提案を承認または拒否し、AGS Matchmaking に通知します。承認された場合、新しいプレイヤーはゲームセッションに参加するための招待を受け取ります。
セッション情報を取得する
ゲームセッションが専用サーバーに接続されると、プレイヤーはセッションに保存されている接続の詳細を使用できるようになります。これにより、専用サーバーに接続できるようになります。同時に、サーバーはセッションと接続されたプレイヤーに関する情報をクエリできます。
- OSS
- Unity
-
まず、V2 セッションインターフェースを取得してゲームサーバーを登録する必要があります。
FOnlineSessionV2AccelBytePtr SessionInterface;
if (!ensure(FOnlineSessionV2AccelByte::GetFromWorld(GetWorld(), SessionInterface)))
{
return;
} -
サーバーへのセッション割り当てをマークするイベント
OnServerReceivedSessionをリッスンします。const FOnServerReceivedSessionDelegate OnServerReceivedSessionDelegate =
FOnServerReceivedSessionDelegate::CreateUObject(
this, &MyClass::OnServerReceivedSession);
FDelegateHandle OnServerReceivedSessionDelegateHandle =
SessionInterface->AddOnServerReceivedSessionDelegate_Handle(
OnServerReceivedSessionDelegate); -
RegisterServerメソッドを呼び出します。const FOnRegisterServerComplete OnRegisterServerCompleteDelegate =
FOnRegisterServerComplete::CreateUObject(
this, &MyClass::OnRegisterServerComplete);
SessionInterface->RegisterServer(SessionName, OnRegisterServerCompleteDelegate); -
OnServerReceivedSessionのデリゲートハンドラー内で、ゲームクライアントと同じ方法でセッションを取得できます。セッションインターフェースを再度取得してから、セッションを取得します。// ここで `SessionName` はセッション受信デリゲートのパラメータです
FNamedOnlineSession* Session = SessionInterface->GetNamedSession(SessionName);
if (!ensure(Session != nullptr))
{
return;
}
サーバーはセッション設定とデータとやり取りできるようになりました。
一般的に、以前に使用した FDelegateHandle を使用して OnServerReceivedSession デリゲートをクリアすることは良い慣行です。
-
まず、サーバーへのセッション割り当てをマークするイベントをリッスンする必要があります。
var dsHub = AccelByteSDK.GetServerRegistry().GetApi().GetDsHub();
dsHub.MatchmakingV2ServerClaimed += result =>
{
if (result.IsError)
{
// MatchmakingV2ServerClaimed が失敗した場合に何かを実行する
Debug.Log($"Error MatchmakingV2ServerClaimed, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// MatchmakingV2ServerClaimed が正常に受信された場合に何かを実行する
}; -
SendReadyMessageメソッドを呼び出して、サーバーを準備完了としてマークします。備考AccelByte Multiplayer Servers のセットアップ方法については、[AMS ガイドライン]*../03-multiplayer/multiplayer-servers/index.mdx) に従ってください。
var ams = AccelByteSDK.GetServerRegistry().GetAMS();
ams.SendReadyMessage(); -
MatchmakingV2ServerClaimedイベント内で、セッション ID を取得し、それとやり取りしてセッションの詳細を取得できます。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)
{
// GetGameSessionDetails が失敗した場合に何かを実行する
Debug.Log($"Error GetGameSessionDetails, Error Code: {sessionResult.Error.Code} Error Message: {sessionResult.Error.Message}");
return;
}
// GetGameSessionDetails が正常に受信された場合に何かを実行する
});
バックフィルを有効および無効にする
- OSS
- Unity
マッチバックフィルは 2 つの方法のいずれかで有効にできます。1 つ目は、ゲームモードに適用可能な場合、マッチルールセット内の構成設定として有効にする方法、2 つ目は、専用サーバーから直接バックフィルチケットを作成して送信する方法です。
バックフィルを有効にするには、セッションインターフェースを取得してから、バックフィルチケットを作成します。
const FOnCreateBackfillTicketComplete OnCreateBackfillTicketCompleteDelegate =
FOnCreateBackfillTicketComplete::CreateUObject(
this, &MyClass::OnCreateBackfillTicketComplete);
SessionInterface->CreateBackfillTicket(
NAME_GameSession, OnCreateBackfillTicketComplete);
元のマッチプールとは異なる特定のマッチプールが必要な場合は、このメソッドにマッチプール名を指定することもできます。
バックフィルを無効にするには、バックフィルチケットを削除します。
const FOnDeleteBackfillTicketComplete OnDeleteBackfillTicketCompleteDelegate =
FOnDeleteBackfillTicketComplete::CreateUObject(
this, &MyClass::OnDeleteBackfillTicketComplete);
SessionInterface->DeleteBackfillTicket(
NAME_GameSession, OnDeleteBackfillTicketCompleteDelegate);
この機能は、Unity 用の AGS Game SDK ではまだサポートされていません。
バックフィル提案を処理する
専用サーバーがセッションが満員でないかどうかを定期的にチェックすることをお勧めします。これにより、サーバーはバックフィルチケットを再作成できます。セッションのチームメンバーがバックフィルチケットと同期していない可能性があるためです。これにより、既存のゲームセッションはマッチメイキングプロセスを通じて満員に達する機会を得ることができます。
セッションバックフィルが要求された場合、マッチルールセットで自動バックフィルが構成されているか、サーバーから直接要求されたかにかかわらず、ゲームセッションにより多くのプレイヤーのための空きスポットがある場合、サーバーは提案を受け取り始めます。
- OSS
- Unity
マッチメイキングバックフィル提案は専用サーバーに送信され、承認または拒否できます。サーバーを登録する前に、バックフィル提案を受信するためのデリゲートハンドラーを追加します。
FOnBackfillProposalReceivedDelegate OnBackfillProposalReceivedDelegate =
FOnBackfillProposalReceivedDelegate::CreateUObject(
this, &MyClass::OnBackfillProposalReceived);
FDelegateHandle OnBackfillProposalReceivedDelegateHandle =
SessionInterface->AddOnBackfillProposalReceivedDelegate_Handle(
OnBackfillProposalReceivedDelegate);
そのハンドラー内で、提案を承認するか拒否するかを決定できます。まず、セッションインターフェースを取得します。次に、いくつかの決定ロジックの後、提案を承認または拒否します。
バックフィル提案を受信するためのイベントリスナーを追加します。
var dsHub = AccelByteSDK.GetServerRegistry().GetApi().GetDsHub();
MatchmakingV2BackfillProposalNotification backfillProposal;
dsHub.MatchmakingV2BackfillProposalReceived += result =>
{
if (result.IsError)
{
// MatchmakingV2BackfillProposalReceived が失敗した場合に何かを実行する
Debug.Log($"Error MatchmakingV2BackfillProposalReceived, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// MatchmakingV2BackfillProposalReceived が正常に受信された場合に何かを実行する
backfillProposal = result.Value;
// バックフィル提案を承認または拒否する関数をここに実装する
};
バックフィル提案全体を承認する
- OSS
- Unity
FOnAcceptBackfillProposalComplete OnAcceptBackfillProposalCompleteDelegate =
FOnAcceptBackfillProposalComplete::CreateUObject(
this, &MyClass::OnAcceptBackfillProposalComplete);
SessionInterface->AcceptBackfillProposal(
NAME_GameSession, Proposal, false, OnAcceptBackfillProposalCompleteDelegate);
var matchmaking = AccelByteSDK.GetServerRegistry().GetApi().GetMatchmakingV2();
var optionalParams = new AcceptBackfillProposalOptionalParams()
{
// バックフィルを停止したい場合は、これを true に設定する
StopBackfilling = false
};
matchmaking.AcceptBackfillProposal(backfillProposal, optionalParams, result =>
{
if (result.IsError)
{
// AcceptBackfillProposal が失敗した場合に何かを実行する
Debug.Log($"Error AcceptBackfillProposal, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// AcceptBackfillProposal が成功した場合に何かを実行する
});
バックフィル提案を拒否する
- OSS
- Unity
FOnRejectBackfillProposalComplete OnRejectBackfillProposalCompleteDelegate =
FOnRejectBackfillProposalComplete::CreateUObject(
this, &MyClass::OnRejectBackfillProposalComplete);
SessionInterface->RejectBackfillProposal(
NAME_GameSession, Proposal, false, OnRejectBackfillProposalCompleteDelegate);
承認メソッドと拒否メソッドの両方への 3 番目の引数は、バックフィルを停止するかどうかをセッションインターフェースに伝えるブール値です。例では、それぞれのメソッドに渡された false 引数により、拒否または承認後もバックフィル提案を受け取り続けます。
var matchmaking = AccelByteSDK.GetServerRegistry().GetApi().GetMatchmakingV2();
// バックフィルを停止したい場合は、これを true に設定する
bool stopBackfilling = false;
matchmaking.RejectBackfillProposal(backfillProposal, stopBackfilling, result =>
{
if (result.IsError)
{
// RejectBackfillProposal が失敗した場合に何かを実行する
Debug.Log($"Error RejectBackfillProposal, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// RejectBackfillProposal が成功した場合に何かを実行する
});
拒否または承認した後もバックフィル提案を受け取り続ける場合、stopBackfilling パラメータの値が false に設定されていることが原因である可能性があります。
提案から部分的にバックフィルする
- OSS
- Unity
受信したバックフィル提案をリッスンし、チケット ID に基づいて参加者を選択して処理を試みます。
正当なプレイヤーが FString の配列に蓄積された後、その配列を構造体 FAccelByteModelsV2MatchmakingBackfillAcceptanceOptionalParam に渡します。
構造体を AccelByte Session インターフェース関数に渡して承認します (FOnlineSessionV2AccelByte::AcceptBackfillProposal)
FOnBackfillProposalReceivedDelegate OnBackfillProposalReceivedDelegate =
FOnBackfillProposalReceivedDelegate::CreateUObject(
this, &MyClass::OnBackfillProposalReceived);
FDelegateHandle OnBackfillProposalReceivedDelegateHandle =
SessionInterface->AddOnBackfillProposalReceivedDelegate_Handle(
OnBackfillProposalReceivedDelegate);
void MyClass::OnBackfillProposalReceived(const FAccelByteModelsV2MatchmakingBackfillProposalNotif& BackfillProposalNotif)
{
TArray<FString> AcceptedTicketIDs{};
for (int i = 0; i < BackfillProposalNotif.AddedTickets.Num(); i++)
{
// 特定のプレイヤーを承認する例:
// 提案チケットとチケット内のプレイヤー ID を反復処理します。
if (BackfillProposalNotif.AddedTickets[i].Players.FindByPredicate([&](const FAccelByteModelsV2MatchmakingTicketPlayerData& PlayerData)
{
return (PlayerData.PlayerID.Equals(/*承認するターゲットプレイヤーの ID*/);
}) != nullptr)
{
// 反復処理でターゲットプレイヤーが見つかった場合、プレイヤーのチケットを承認されたチケット配列のリストに追加します。
AcceptedTicketIDs.Add(BackfillProposalNotif.AddedTickets[i].TicketID);
}
}
FAccelByteModelsV2MatchmakingBackfillAcceptanceOptionalParam AcceptanceOptionalParameter{};
AcceptanceOptionalParameter.AcceptedTicketIDs = AcceptedTicketIDs;
SessionInterface->AcceptBackfillProposal(
NAME_GameSession,
BackfillProposalNotif,
false,
// 必要に応じて適切に処理する
FOnAcceptBackfillProposalComplete::CreateLambda([&](bool bSuccess) { }),
AcceptanceOptionalParameter
);
}
受信したバックフィル提案をリッスンし、チケット ID に基づいて参加者を選択して処理を試みます。
正当なプレイヤーが string の配列に蓄積された後、その配列を AcceptBackfillProposal オプションパラメータに渡します。
var userIdToAccept = new string[]
{
"accepted-user-id-1",
"accepted-user-id-2",
};
var dsHub = AccelByteSDK.GetServerRegistry().GetApi().GetDsHub();
var matchmaking = AccelByteSDK.GetServerRegistry().GetApi().GetMatchmakingV2();
dsHub.MatchmakingV2BackfillProposalReceived += result =>
{
if (result.IsError)
{
// 通知にエラーがある場合に何かを実行する
Debug.Log($"Error MatchmakingV2BackfillProposalReceived, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// MatchmakingV2BackfillProposalReceived が正常に受信された場合に何かを実行する
var backfillNotification = result.Value;
System.Collections.Generic.List<string> acceptedIds = new System.Collections.Generic.List<string>();
// 特定のプレイヤーを承認する例:
// 提案チケットとチケット内のプレイヤー ID を反復処理します。
foreach (var backfillTicket in backfillNotification.BackfillProposalTickets)
{
var ticketWithAcceptedUserId = backfillTicket.players.Where(player =>
{
return userIdToAccept.Contains(player.playerId);
});
if (ticketWithAcceptedUserId.Any())
{
acceptedIds.Add(backfillTicket.ticketId);
}
}
var optionalParam = new AcceptBackfillProposalOptionalParams()
{
AcceptedTicketIds = acceptedIds.ToArray(),
StopBackfilling = false
};
matchmaking.AcceptBackfillProposal(backfillNotification, optionalParam, acceptResult =>
{
if (acceptResult.IsError)
{
// MatchmakingV2BackfillProposalReceived が失敗した場合に何かを実行する
Debug.Log($"Error AcceptBackfillProposal, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// AcceptBackfillProposal が成功した場合に何かを実行する
});
};
バックフィルチケット ID を取得する
auto_backfill マッチルールセット設定が有効になっていて、マッチメイキングプロセスが満員でないゲームを生成する場合、セッションが作成される前にバックフィルチケットが生成されます。その結果、DS がセッションを受け取ったときに、必要に応じて、DS はバックフィルチケット ID の存在を確認して、受信した試合に自動バックフィルがあるかどうかを示すことができます。
- 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)
{
// GetGameSessionDetails が失敗した場合に何かを実行する
Debug.Log($"Error GetGameSessionDetails, Error Code: {result.Error.Code} Error Message: {result.Error.Message}");
return;
}
// GetGameSessionDetails が成功した場合に何かを実行する
bool isBackfillEnabled = !string.IsNullOrEmpty(result.Value.backfillTicketId);
});
トラブルシューティング
このセクションでは、サービスを使用する際に発生する可能性のある一般的なエラーと問題、およびそれらを解決する方法に関する推奨事項を見つけることができます。
バージョンの競合によりセッション更新が失敗する
現在、DS はセッションの更新を自動的に受信しないため、サーバーがセッションを更新しようとするときに問題が発生する可能性があります。DS 側のセッションデータが古くなると、セッションを更新するとバージョンの競合エラーが発生する可能性があります。UpdateSession を呼び出したときにログに記録されたエラーメッセージからこの種のエラーを検出することは可能ですが、この特定のケースに対してエラーハンドラーを使用することもできます。
- OSS
- Unity
FOnSessionUpdateConflictErrorDelegate OnSessionUpdateConflictErrorDelegate =
FOnSessionUpdateConflictErrorDelegate::CreateUObject(
this, &MyClass::OnSessionUpdateConflictError);
SessionInterface->AddOnSessionUpdateConflictErrorDelegate_Handle(
OnSessionUpdateConflictErrorDelegate);
セッションサービスまたは AGS OSS が競合の場合にどのセッション設定を更新するかを決定する本当の一般的な解決策はないため、上記のデリゲートを使用して更新を再試行したり、競合を解決するためのロジックを格納したりできます。
更新が失敗すると、セッションデータはバックエンドから自動的に更新され、競合エラーハンドラーは失敗した更新に渡されたセッション設定のコピーを受け取ります。
Unity では、古いデータが原因で更新リクエストが失敗した場合、エラーコード ErrorCode.SessionVersionMismatch で応答します。このエラーコードを受け取った場合、最初にバックエンドをクエリして最新のセッションデータを取得し、最新のセッションバージョンで更新を再試行する必要があります。