ロビーWebSocketリカバリー:中断と再接続の処理
注釈:本資料はAI技術を用いて翻訳されています。
リアルタイムアプリケーションでは、安定した WebSocket 接続を維持することがスムーズなユーザー体験に不可欠です。しかし、サービスメンテナンス時のネットワーク中断やサーバー問題により、接続障害が発生する可能性があります。リトライ試行がタイムアウト制限に達し、再接続が失敗した場合、主要機能を継続して動作させるためにフォールバックメカニズムの実装が重要になります。この記事では、ロビー WebSocket リカバリーのベストプラクティスを概説し、開発者がより堅牢でユーザーフレンドリーなシステムを構築できるよう支援します。プレイヤーの体験を損なわないために、開発者は以下を行う必要があります:
-
接続が失われた際に明確な UI 通知を表示する。
-
自動再接続が失敗した場合に手動再接続オプションを提供する。
WebSocket 再接続を適切に処理することで、開発者はプレイヤーにとってスムーズで中断のない体験を確保できます。
WebSocket 再接続戦略
- Unreal Engine
- Unity
デフォルトの WebSocket 再接続戦略は Balanced Reconnect Strategy を使用しています。一般的な再接続戦略の動作は以下の通りです:
- 最初の再接続は初期バックオフ遅延で開始されます。値は再接続戦略によって異なります。
- 各リトライのバックオフ遅延は、再接続戦略のベースファクターで乗算されます。
- バックオフ遅延は最小1秒、最大リトライ間隔の範囲内に制限されます。
- 現在のバックオフ遅延に対して、-バックオフ遅延から +バックオフ遅延の間の値を4で割った小さなランダム変動が追加されます。これにより、リトライが完全に予測可能な間隔で発生することを防ぎます。
- リトライに費やされた総時間が再接続戦略の総タイムアウトを超えた場合、システムは再接続を停止し、エラーステータスコード
EWebsocketErrorTypes::DisconnectFromExternalReconnectで OnConnectionClosed デリゲートをトリガーします。
WebSocket 再接続戦略サポートはデフォルトで実装されており、以下のバージョンでリリースされました:
作業中 – 近日更新予定!
リクエストが接続エラーに遭遇した場合、システムがリクエストのリトライを開始する際にリクエストデリゲートはトリガーされません。デリゲートは、リトライプロセスが終了した後(リトライ制限に達してタイムアウトした場合、または接続が正常に復元された場合)にのみトリガーされます。
再接続通知
- Unreal Engine
- Unity
ロビー接続が失われた場合、AccelByte SDK にはロビーを再接続するリトライメカニズムがあります。AccelByte SDK がリトライ状態にある間、Byte Wars は再接続通知をポップアップ表示します。
Byte Wars Unreal Engine プロジェクトのファイル参照:
- ヘッダーファイル:
/Source/AccelByteWars/TutorialModules/Play/OnlineSessionUtilsAccelByteWarsOnlineSession.h - CPP ファイル:
/Source/AccelByteWars/TutorialModules/Play/OnlineSessionUtils/AccelByteWarsOnlineSession.cpp
-
ロビー再接続バインディング。
ロビー接続が正常に接続された後、AccelByte OSS
FOnlineIdentityAccelByteのデリゲートAccelByteOnLobbyReconnectedDelegatesにロビー再接続処理をバインドします。void UAccelByteWarsOnlineSession::OnConnectLobbyComplete(int32 LocalUserNum, bool bSucceeded, const FUniqueNetId& UserId, const FString& Error)
{
// ...
FOnlineIdentityAccelBytePtr ABIdentityInt = GetABIdentityInt();
if (!ensureMsgf(ABIdentityInt, TEXT("AB OnlineIdentity interface is nullptr.")))
{
return;
}
// ...
ABIdentityInt->AccelByteOnLobbyReconnectingDelegates->RemoveAll(this);
// ...
ABIdentityInt->AccelByteOnLobbyReconnectingDelegates->AddUObject(this, &ThisClass::OnLobbyReconnecting);
// ...
} -
再接続メッセージをポップアップ表示。
// ...
#define LOBBY_RECONNECTING_MESSAGE NSLOCTEXT(BYTEWARS_LOCTEXT_NAMESPACE, "Lobby Reconnecting", "Reconnecting to AGS.")void UAccelByteWarsOnlineSession::OnLobbyReconnecting(int32 LocalUserNum, const FUniqueNetId& UserId, int32 StatusCode, const FString& Reason, bool bWasClean)
{
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
if (!ensureMsgf(GameInstance, TEXT("Game Instance is null")))
{
return;
}
GameInstance->bIsReconnecting = true;
GetPromptSubystem()->ShowLoading(LOBBY_RECONNECTING_MESSAGE);
}
現在、AGS Unity SDK では再接続デリゲートのサポートはまだ利用できません。
ロビー再接続タイムアウト
ロビー WebSocket 再接続がタイムアウトに達した場合、プレイヤーに良い体験を提供するためにゲームは適切な処理を行う必要があります。処理には通知ポップアップ、ゲームレベルでの自動または手動接続、オンライン機能の無効化などが含まれます。WebSocket 再接続はこれらの手順に従って設定できます。
- Unreal Engine
- Unity
Byte Wars Unreal Engine プロジェクトでは、ロビー再接続タイムアウトを処理するための2つのステップがあります。
ゲームレベルロビー自動接続
ロビーの接続に失敗するか、ロビー再接続メカニズムが失敗した場合、AccelByte OSS FOnlineIdentityAccelByte はデリゲート AccelByteOnLobbyConnectionClosedDelegates をトリガーします。したがって、そのデリゲートに接続失敗処理コードをバインドする必要があります。
-
ロビー接続クローズバインディング。
ロビー接続が正常に接続された後、AccelByte OSS
FOnlineIdentityAccelByteのデリゲートAccelByteOnLobbyConnectionClosedDelegatesにロビー再接続処理をバインドします。void UAccelByteWarsOnlineSession::OnConnectLobbyComplete(int32 LocalUserNum, bool bSucceeded, const FUniqueNetId& UserId, const FString& Error)
{
// ...
FOnlineIdentityAccelBytePtr ABIdentityInt = GetABIdentityInt();
if (!ensureMsgf(ABIdentityInt, TEXT("AB OnlineIdentity interface is nullptr.")))
{
return;
}
// ...
ABIdentityInt->AccelByteOnLobbyConnectionClosedDelegates->RemoveAll(this);
// ...
ABIdentityInt->AccelByteOnLobbyConnectionClosedDelegates->AddUObject(this, &ThisClass::OnLobbyConnectionClosed);
} -
ロビー(手動)自動再接続タイムアウト処理(ゲームコード内)
void UAccelByteWarsOnlineSession::OnLobbyConnectionClosed(int32 LocalUserNum, const FUniqueNetId& UserId, int32 StatusCode, const FString& Reason, bool bWasClean)
{
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
if (!ensureMsgf(GameInstance, TEXT("Game Instance is null")))
{
return;
}
GameInstance->bIsReconnecting = false;
GetPromptSubystem()->HideLoading();
if (StatusCode == static_cast<int32>(AccelByte::EWebsocketErrorTypes::DisconnectFromExternalReconnect))
{
// Do some manual handle to reconnect lobby
LobbyConnect(LocalUserNum);
GameInstance->bIsReconnecting = true;
GetPromptSubystem()->ShowLoading(LOBBY_RECONNECTING_MESSAGE);
}
}ロビー接続がクローズされた際には、異なるタイプの WebSocket エラーがあります。ロビー再接続が長時間かかりタイムアウトした場合、WebSocket エラータイプが
DisconnectFromExternalReconnectの場合のみロビー接続を再実行するようにしてください。
ロビー手動接続
自動ロビー接続がロビー接続を復旧できない場合、Byte Wars はプレイヤーにダイアログボックスを表示し、手動でロビーに接続するかゲームからログアウトするかのオプションを提供します。プレイヤーがゲームからログアウトするオプションを選択した場合、ゲームはメインメニューレベルにリダイレクトされ、ログインページが表示されます。
// ...
#define LOBBY_FAILED_CONNECT_MESSAGE NSLOCTEXT(BYTEWARS_LOCTEXT_NAMESPACE, "Lobby Connection Failed", "Failed to connect AGS Lobby. Try to Reconnect")
#define LOBBY_FAILED_RECONNECT_MESSAGE NSLOCTEXT(BYTEWARS_LOCTEXT_NAMESPACE, "Lobby Reconnect Failed", "Failed to reconnect AGS Lobby. Try to Reconnect")
// ...
#define LOBBY_RECONNECTING_MESSAGE NSLOCTEXT(BYTEWARS_LOCTEXT_NAMESPACE, "Lobby Reconnecting", "Reconnecting to AGS.")
void UAccelByteWarsOnlineSession::OnConnectLobbyComplete(int32 LocalUserNum, bool bSucceeded, const FUniqueNetId& UserId, const FString& Error)
{
// ...
FOnlineIdentityAccelBytePtr ABIdentityInt = GetABIdentityInt();
if (!ensureMsgf(ABIdentityInt, TEXT("AB OnlineIdentity interface is nullptr.")))
{
return;
}
// ...
if (!bSucceeded)
{
// Lobby Connect and Reconnect failed
FText message = GameInstance->bIsReconnecting ? LOBBY_FAILED_RECONNECT_MESSAGE : LOBBY_FAILED_CONNECT_MESSAGE;
GetPromptSubystem()->ShowDialoguePopUp(ERROR_PROMPT_TEXT, message, EPopUpType::ConfirmationYesNo,
FPopUpResultDelegate::CreateWeakLambda(this, [this, LocalUserNum](EPopUpResult Result)
{
switch (Result)
{
// Accept manual reconnect attempt
case Confirmed:
{
GetPromptSubystem()->ShowLoading(LOBBY_RECONNECTING_MESSAGE);
LobbyConnect(LocalUserNum);
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
GameInstance->bIsReconnecting = true;
}
break;
// Deny reconnect and logout
case Declined:
{
UGameplayStatics::OpenLevel(GetWorld(), TEXT("MainMenu"));
// Pending Perform Logout after Main Menu level opened
GetWorld()->GetTimerManager().SetTimerForNextTick(FVoidHandler::CreateLambda([this, LocalUserNum]()
{
// Open Login Menu
UTutorialModuleDataAsset* AuthEssentialsDataAsset =
UTutorialModuleUtility::GetTutorialModuleDataAsset(FPrimaryAssetId("TutorialModule:AUTHESSENTIALS"), this);
TSubclassOf<UAccelByteWarsActivatableWidget> LoginWidgetClass = AuthEssentialsDataAsset->GetTutorialModuleUIClass();
UAccelByteWarsGameInstance* GameInstance = Cast<UAccelByteWarsGameInstance>(GetGameInstance());
GameInstance->GetBaseUIWidget()->PushWidgetToStack(EBaseUIStackType::Menu, LoginWidgetClass.Get());
GetPromptSubystem()->ShowLoading();
GetABIdentityInt()->Logout(LocalUserNum);
GameInstance->bIsReconnecting = false;
}));
}
break;
}
}));
GameInstance->bIsReconnecting = false;
return;
}
// ...
}
現在、Byte Wars Unity プロジェクトは、プレイヤーを強制的にログアウトさせることでロビー切断のみを処理しています。
private void OnLobbyDisconnected(WsCloseCode code)
{
BytewarsLogger.Log($"Lobby service disconnected with code: {code}");
HashSet<WsCloseCode> loginDisconnectCodes = new()
{
WsCloseCode.Normal,
WsCloseCode.DisconnectDueToMultipleSessions,
WsCloseCode.DisconnectDueToIAMLoggedOut
};
if (loginDisconnectCodes.Contains(code))
{
lobby.Connected -= OnLobbyConnected;
lobby.Disconnected -= OnLobbyDisconnected;
AuthEssentialsHelper.OnUserLogout?.Invoke();
}
}
欠落通知の自動取得
ゲームクライアントとサーバーがロビーサービスから切断された場合、重要な通知を見逃す可能性があります。幸いなことに、Unreal SDK と Unity SDK には、WebSocket 接続が復元された後にこれらの欠落した通知を検出して復旧する組み込みシステムがあります。欠落した通知の取得に成功した後、SDK は関連するイベントデリゲートをトリガーし、ゲームが重要なゲームプレイ関連の更新を失わないようにします。SDK は通知のシーケンス番号に基づいてデリゲートをトリガーします。これにより、最も古い欠落通知が最初に処理され、イベントが正しい順序で処理されることが保証されます。
AccelByte のセッションとマッチメイキング機能はロビー通知に大きく依存しているため、このメカニズムはこれらの領域での見逃された更新もチェックします。これにより、WebSocket 接続が一時的に中断されても、マッチメイキングとセッション管理がスムーズに継続されることが保証されます。