マッチメイキングエラー処理 – ベストプラクティス
注釈:本資料はAI技術を用いて翻訳されています。
マッチメイキング接続エラーの処理は、スムーズなプレイヤーエクスペリエンスを確保するために重要です。接続の問題が発生した場合、ゲームはプレイヤーに明確なフィードバックを提供し、適切に回復を試みる必要があります。適切なエラー処理を実装することで、フラストレーションを防ぎ、プレイヤーがゲームプレイを中断することなく迅速に再接続できるようになります。マッチメイキング接続の問題を処理する方法:
- 各ステップのデリゲートのエラーに基づいて明確で有益なメッセージを表示し(マッチメイキングの開始、セッションへの参加、DSへの移動など)、プレイヤーがマッチメイキングフローが失敗した理由を理解できるようにします。DSでのプレイフローのリファレンスについては、Byte Warsチュートリアルモジュールを使用できます(Unreal EngineまたはUnity)。
- ロビー接続が失われた場合、マッチメイキング機能を無効にします。
- マッチメイキングの各ステップでポップアップまたはメッセージに手動再試行オプションを表示します。
マッチメイキングエラー処理
- Unreal Engine OSS
- Unreal Engine SDK
- Unity
Unreal Engine OSSには、マッチチケットのステータスを確認するための組み込みのポーリングシステムがあります。マッチチケットがマッチメイキングサービスで正常に作成されると、システムは15秒ごとに更新をポーリングします。ポーリングは、マッチチケットが見つかるか、マッチメイキングサービスで見つからない(エラーコード520303で応答)場合に自動的に停止します。ただし、接続の中断が発生してもプレイヤーがログインしたままの場合、ポーリングは完了するまでバックグラウンドで実行され続けます。
...
auto ABSubsystem = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
auto SessionInterface = ABSubsystem->GetSessionInterface();
SessionInterface->OnMatchmakingCompleteDelegates.AddWeakLambda(this, [this](FName SessionName, bool bWasSuccessful)
{
if (bWasSuccessful)
{
// マッチメイキングリクエストが成功したときに何かを実行します。
}
else
{
// 失敗したリクエストを処理するソリューションを実装します。
}
});
...
現在、マッチメイキングが失敗した場合、AGS OSSは失敗がWebSocketタイムアウトによるものか、別の問題によるものかについての詳細を提供しません。
Unreal Engine AGS SDKを使用する場合にマッチメイキングステータスを取得するには、ゲームはマッチステータスの詳細を取得するためのポーリングを実装する必要があります。Unreal Engine AGS OSSにはこのポーリングメカニズムがあり、ポーリング間隔は15秒です。マッチステータスの取得が失敗したか、マッチチケットがアクティブでも提案されてもいない場合、ゲームはマッチメイキングプロセスが失敗したことを識別する必要があります。
...
bool isMatchFound{true};
...
...
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->MatchmakingV2.GetMatchTicketDetails("<match-ticket-id>",
THandler<FAccelByteModelsV2MatchmakingCreateTicketResponse>::CreateLambda([&isMatchFound](const FAccelByteModelsV2MatchmakingGetTicketDetailsResponse& Response)
{
// マッチメイキングリクエストが成功したときに何かを実行します。
// チケットが現在アクティブでなく、提案されていない場合、チケットはキャンセルされたことを意味します
if(!Response.IsActive && Response.ProposedProposal.Status.IsEmpty())
{
isMatchFound = false;
}
}), FCreateMatchmakingTicketErrorHandler::CreateLambda([&isMatchFound](int32 ErrorCode, const FString& ErrorMessage, const FErrorCreateMatchmakingTicketV2& CreateTicketErrorInfo)
{
UE_LOG(LogTemp, Warning, TEXT("Error matchmaking request. Code: %d, Message: %s"), ErrorCode, *ErrorMessage);
isMatchFound = false;
})
);
...
マッチチケットのステータスを取得するための組み込みのポーリングがあります。マッチチケットがマッチメイキングサービスで正常に作成されると、Unity SDKは5秒ごとにマッチチケットのステータスをポーリングします。ポーリングは、マッチチケットが見つかった場合、マッチメイキングサービスがダウンしている場合(HTTPエラーコード404)、またはマッチメイキングサービスが指定されたマッチメイキングチケットを見つけられなかった場合(HTTPエラーコード520303)に停止します。サービスがダウンしているか、指定されたチケットが見つからない場合、ポーリングは停止し、MatchmakingV2TicketExpiredコールバックをトリガーします。
...
public bool isMatchFound = true;
...
...
AccelByteSDK.AccelByteSDK.GetClientRegistry().GetApi().GetLobby().MatchmakingV2TicketExpired += notificationPayload =>
{
// マッチチケットの有効期限を処理するソリューションを実装します。
isMatchFound = false;
};
...
セッション参加エラー処理
- Unreal Engine OSS
- Unreal Engine SDK
- Unity
...
auto ABSubsystem = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
auto SessionInterface = ABSubsystem->GetSessionInterface();
SessionInterface->OnJoinSessionCompleteDelegates.AddWeakLambda(this, [this](Name SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if(Result == EOnJoinSessionCompleteResult::Success)
{
// ゲームセッションへの参加が成功したときに何かを実行します。
}
else
{
// 失敗したゲームセッション参加リクエストを処理するソリューションを実装します。
}
});
...
現在、マッチメイキングが失敗した場合、AGS OSSは失敗がWebSocketタイムアウトによるものか、別の問題によるものかについての詳細を提供しません。
...
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Session.JoinGameSession("<SessionID>",
THandler<FAccelByteModelsV2GameSession>::CreateLambda([](const FAccelByteModelsV2GameSession& Result)
{
// ゲームセッションへの参加が成功したときに何かを実行します。
}), FErrorHandler::CreateLambda([](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Warning, TEXT("Error joining game session. Code: %d, Message: %s"), ErrorCode, *ErrorMessage);
if(ErrorCode == static_cast<int32>(ErrorCodes::NetworkError))
{
// HTTP再試行タイムアウトを処理するソリューションを実装します。
}
})
);
...
...
AccelByteSDK.GetClientRegistry().GetApi().GetSession().JoinGameSession("<sessionId>", result =>
{
if (!result.IsError)
{
// ゲームセッションへの参加が成功したときに何かを実行します。
}
else
{
// 失敗したリクエストを処理するソリューションを実装します。
Debug.LogWarning($"Unable to login. Code: {result.Error.Code}, Message: {result.Error.Message}");
if(result.Error.Code.Equals(ErrorCode.NetworkError))
{
// HTTP再試行タイムアウトを処理するソリューションを実装します。
}
}
});
...
セッションタイムアウト処理
プレイヤーが何らかの理由でセッションサービスから切断された場合、セッションサービスはプレイヤーをセッションの非アクティブメンバーとして識別します。非アクティブメンバーにはInactive Timeout制限があり、ここで設定できます。WebSocket接続タイムアウトよりも大きいInactive Timeoutの値を設定して、WebSocketが正常に再接続されたときにプレイヤーがまだゲームセッションにいることを確認できます。
ゲームセッションの回復
プレイヤーがWebSocket接続から切断されたが、セッションサービスによってゲームセッションから削除されていない場合、ゲームクライアントはセッションを復元できます。これは、WebSocket接続が正常に再確立されると、プレイヤーがシームレスにセッションに戻ることができることを意味します。
- Unreal Engine OSS
- Unreal Engine SDK
- Unity
- ゲームセッションを復元します。
...
auto ABSubsystem = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
auto SessionInterface = ABSubsystem->GetSessionInterface();
SessionInterface->RestoreActiveSessions("<UserId>",
FOnRestoreActiveSessionsComplete::CreateWeakLambda(this, [this](const FUniqueNetId& LocalUserId, const FOnlineError& ResultState)
{
if (ResultState.bSucceeded)
{
// ゲームセッションの復元が成功したときに何かを実行します。
}
else
{
// 失敗したリクエストを処理するソリューションを実装します。
}
})
);
...
- ゲームセッションの復元に成功した後、DSに再参加します。
...
auto ABSubsystem = IOnlineSubsystem::Get(ACCELBYTE_SUBSYSTEM);
auto SessionInterface = ABSubsystem->GetSessionInterface();
auto ABSessionInterface = StaticCastSharedPtr<FOnlineSessionV2AccelByte>(SessionInterface);
FString ServerAddress = "";
ABSessionInterface->GetResolvedConnectString(Name_GameSession, ServerAddress);
APlayerController::ClientTravel(ServerAddress, TRAVEL_Absolute);
...
- ゲームセッションを復元します。
...
FAccelByteModelsV2GameSession CurrentGameSession{};
...
...
FApiClientPtr ApiClient = AccelByteOnlineSubsystemPtr->GetApiClient();
ApiClient->Lobby.SetConnectSuccessDelegate(Lobby::FConnectSuccess::CreateLambda([this]()
{
...
ApiClient->Session.GetMyGameSessions(THandler<FAccelByteModelsV2PaginatedGameSessionQueryResult>::CreateLambda([&CurrentGameSession](const FAccelByteModelsV2PaginatedGameSessionQueryResult& Result)
{
// ゲームセッションの取得が成功したときに何かを実行します。
if(Result.Num())
{
// 通常、プレイヤーは1つのアクティブセッションのみを持ちます。
CurrentGameSession = Result[0];
}
}), FErrorHandler::CreateLambda([](int32 ErrorCode, const FString& ErrorMessage)
{
UE_LOG(LogTemp, Warning, TEXT("Error retrieving game session. Code: %d, Message: %s"), ErrorCode, *ErrorMessage);
// 失敗したリクエストを処理するソリューションを実装します。
})
);
...
}));
ApiClient->Lobby.Connect();
...
- ゲームセッションの復元に成功した後、DSに再参加します。
...
FString Url = FString::Printf(TEXT("%s:%d"), *CurrentGameSession.DSInformation.Server.Ip, DSInformation.Server.Port);
APlayerController::ClientTravel(Url, TRAVEL_Absolute);
...
- ゲームセッションを復元します。
...
SessionV2GameSession currentGameSession;
...
...
AccelByteSDK.GetClientRegistry().GetApi().GetSession().GetUserGameSessions(null, null, null, result =>
{
if (!result.IsError)
{
// ゲームセッションの取得が成功したときに何かを実行します。
if(result.Value.data.Length > 0)
{
CurrentGameSession = result.Value.data[0];
}
}
else
{
// 失敗したリクエストを処理するソリューションを実装します。
}
});
...
- ゲームセッションの復元に成功した後、DSに再参加します。
...
ushort port = currentGameSession.dsInformation.server.port;
string ip = currentGameSession.dsInformation.server.ip;
GameManager.Instance.StartAsClient(ip, port, null);
...