Implement Subsystem - Login Queue - (Unreal Engine module)
注釈:本資料はAI技術を用いて翻訳されています。
サブシステムの概要
Byte Warsは、AccelByte Gaming Services (AGS) OSSのラッパーとして機能するLoginQueueSubsystem_Starterという名前のGame Instance Subsystemを使用します。これは、Identity Interfaceから派生したAGS OSSが提供するFOnlineIdentityAccelByteのみを使用します。
AGS OSSのログインキューAsyncTaskはタイムアウトを有効にしていません。理由は、ログインキューの待機時間がAsyncTaskのタイムアウト時間を超える可能性があるためです。したがって、操作はプレイヤーがログインを試みるか、プレイヤーがログインキューをキャンセルするまで待機します。
以下の図は、Login Queue機能がOSSとどのように連携するかを説明しています。
Starter Packの内容
変更するためのスタータークラスLoginQueueSubsystem_Starterは、Resourcesセクションで提供されており、以下で構成されています:
- Headerファイル:
/Source/AccelByteWars/TutorialModules/Access/LoginQueue/LoginQueueSubsystem_Starter.h - CPPファイル:
/Source/AccelByteWars/TutorialModules/Access/LoginQueue/LoginQueueSubsystem_Starter.cpp - Modelファイル:
/Source/AccelByteWars/TutorialModules/Access/LoginQueue/LoginQueueModel.h
スタータークラスには以下の機能が含まれています:
-
Headerファイルに、AccelByteのOnline Identity Interfaceとそのポインタをインクルード:
// ...
#include "OnlineIdentityInterfaceAccelByte.h"protected:
FOnlineIdentityAccelBytePtr IdentityInterface; -
CPPファイルでOnline Identityポインタを取得するコード:
void ULoginQueueSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
// Get Online Subsystem and make sure it's valid.
FOnlineSubsystemAccelByte* Subsystem = static_cast<FOnlineSubsystemAccelByte*>(Online::GetSubsystem(GetWorld()));
if (!ensure(Subsystem))
{
UE_LOG_LOGIN_QUEUE(Warning, TEXT("The online subsystem is invalid. Please make sure OnlineSubsystemAccelByte is enabled and the DefaultPlatformService under [OnlineSubsystem] in the Engine.ini file is set to AccelByte."));
return;
}
// Grab the reference of AccelByte Identity Interface and make sure it's valid.
IdentityInterface = StaticCastSharedPtr<FOnlineIdentityAccelByte>(Subsystem->GetIdentityInterface());
if (!ensure(IdentityInterface.IsValid()))
{
UE_LOG_LOGIN_QUEUE(Warning, TEXT("Identity interface is not valid."));
return;
}
// ...
} -
Modelファイルで、ゲームのロジックをIdentity Interfaceのデリゲートに接続するためのマルチキャストデリゲート宣言:
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnLoginQueueCancelCompleted, const APlayerController*, const FOnlineError&)
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnLoginQueued, const APlayerController*, const FAccelByteModelsLoginQueueTicketInfo& TicketInfo)
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnLoginTicketStatusUpdated, const APlayerController*, const FAccelByteModelsLoginQueueTicketInfo&, const FOnlineError&) -
Player Controllerからローカルプレイヤー番号を取得する、またはその逆を行うヘルパー関数。これらの関数は、UIとIdentity Interface間のやり取りを簡素化するために作成されました。ゲームUIには、所有するPlayer Controllerを返す
GetOwningPlayer()関数がありますが、Identity Interfaceはローカルプレイヤー番号を受け入れます。int32 ULoginQueueSubsystem_Starter::GetLocalUserNumFromPlayerController(const APlayerController* PlayerController) const
{
if (!PlayerController)
{
return INDEX_NONE;
}
const ULocalPlayer* LocalPlayer = PlayerController->GetLocalPlayer();
if (!LocalPlayer)
{
return INDEX_NONE;
}
return LocalPlayer->GetControllerId();
}APlayerController* ULoginQueueSubsystem_Starter::GetPlayerControllerByLocalUserNum(const int32 LocalUserNum) const
{
APlayerController* MatchedPC = nullptr;
for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
{
if (!It->IsValid())
{
continue;
}
if (APlayerController* PC = It->Get(); PC)
{
if (LocalUserNum == GetLocalUserNumFromPlayerController(PC))
{
MatchedPC = PC;
break;
}
}
}
return MatchedPC;
}
Login Queueコールバックの実装
-
LoginQueueSubsystem_StarterHeaderファイルを開き、これら2つのデリゲートを追加します。これらのデリゲートは、OSSのデリゲートとByte WarsのUI間の接続ポイントになります。public:
// ...
FOnLoginQueued OnLoginQueuedDelegates;
FOnLoginTicketStatusUpdated OnLoginTicketStatusUpdatedDelegates; -
引き続きHeaderファイルで、OSSのデリゲートにバインドする以下の関数を追加します。
protected:
void OnLoginQueued(const int32 PlayerNum, const FAccelByteModelsLoginQueueTicketInfo& TicketInfo) const;
void OnLoginTicketStatusUpdated(
const int32 PlayerNum,
bool bWasSuccessful,
const FAccelByteModelsLoginQueueTicketInfo& TicketInfo,
const FOnlineErrorAccelByte& Error) const; -
次に、
LoginQueueSubsystem_StarterCPPファイルを開き、OnLoginQueued()関数を実装します。名前が示すように、これはログイン試行後にプレイヤーがキューに入れられたときのコールバックになります。ここでは、OSSのデリゲートから受け取ったPlayerNumをPlayerControllerに変更してUI側のコードを簡素化し、OnLoginQueuedDelegatesをトリガーします。void ULoginQueueSubsystem_Starter::OnLoginQueued(const int32 PlayerNum, const FAccelByteModelsLoginQueueTicketInfo& TicketInfo) const
{
OnLoginQueuedDelegates.Broadcast(GetPlayerControllerByLocalUserNum(PlayerNum), TicketInfo);
} -
引き続きCPPファイルで、
OnLoginTicketStatusUpdated()関数を実装します。この関数は、ポーリングレスポンスが受信されたときのコールバックになります。前の関数と同様に、この関数は単にPlayerNumをPlayerControllerに変更し、OnLoginTicketStatusUpdatedDelegatesをトリガーします。void ULoginQueueSubsystem_Starter::OnLoginTicketStatusUpdated(
const int32 PlayerNum,
bool bWasSuccessful,
const FAccelByteModelsLoginQueueTicketInfo& TicketInfo,
const FOnlineErrorAccelByte& Error) const
{
// bWasSuccessful false means the player still in the queue, not that the request has failed.
OnLoginTicketStatusUpdatedDelegates.Broadcast(GetPlayerControllerByLocalUserNum(PlayerNum), TicketInfo, Error);
}備考ポーリングリクエスト自体は、Admin Portal(Login Queue Configuration)のPolling Time設定に基づいた遅延でOSSによって実行されます。 これが何らかの理由で0に設定されている場合、OSSはバックエンドから取得したEstimatedWaitingTimeInSeconds + -10から10秒のランダムな数値を使用し、最小3秒、最大40秒に制限されます。
-
引き続きCPPファイルで、実装した関数をOSSのデリゲートにバインドします。
Initialize()関数に移動し、以下のコードを追加します。void ULoginQueueSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
IdentityInterface->AccelByteOnLoginQueuedDelegates->AddUObject(this, &ThisClass::OnLoginQueued);
IdentityInterface->AccelByteOnLoginTicketStatusUpdatedDelegates->AddUObject(this, &ThisClass::OnLoginTicketStatusUpdated);
} -
Deinitialize()関数でデリゲートをアンバインドします。void ULoginQueueSubsystem_Starter::Deinitialize()
{
// ...
IdentityInterface->AccelByteOnLoginQueuedDelegates->RemoveAll(this);
IdentityInterface->AccelByteOnLoginTicketStatusUpdatedDelegates->RemoveAll(this);
}
Cancel Loginの実装
-
LoginQueueSubsystem_StarterHeaderファイルを開き、キャンセル関数をトリガーする関数と、レスポンスのコールバックとしてのデリゲートを追加します。public:
void CancelLoginQueue(const APlayerController* PlayerController) const;
FOnLoginQueueCancelCompleted OnLoginQueueCancelCompletedDelegates; -
引き続きHeaderファイルで、OSSデリゲートにバインドされるコールバックとして以下の関数を宣言します。
protected:
// ...
void OnLoginQueueCancelCompleted(const int32 PlayerNum, bool bWasSuccessful, const FOnlineErrorAccelByte& Error) const; -
LoginQueueSubsystem_StarterCPPファイルを開き、CancelLoginQueue()関数を実装します。PlayerControllerパラメータを関連するローカルプレイヤーインデックスに変換し、ログインキャンセル機能を呼び出します。void ULoginQueueSubsystem_Starter::CancelLoginQueue(const APlayerController* PlayerController) const
{
IdentityInterface->CancelLoginQueue(GetLocalUserNumFromPlayerController(PlayerController));
} -
引き続きCPPファイルで、コールバック関数
OnLoginQueueCancelCompletedを実装します。この関数は、Cancel Loginの実装ステップで作成したOnLoginQueueCancelCompletedDelegatesをトリガーします。これに加えて、このコールバックがトリガーされた直後にOnLoginCompleteもトリガーされることに注意してください。void ULoginQueueSubsystem_Starter::OnLoginQueueCancelCompleted(
const int32 PlayerNum,
bool bWasSuccessful,
const FOnlineErrorAccelByte& Error) const
{
// OnLoginComplete will also be triggered after this is triggered
FOnlineError OutError = Error;
OutError.bSucceeded = bWasSuccessful;
OnLoginQueueCancelCompletedDelegates.Broadcast(GetPlayerControllerByLocalUserNum(PlayerNum), OutError);
} -
関数を実装したので、コールバックをOSSのデリゲートにバインドする必要があります。引き続きCPPファイルで、
Initialize()関数に移動し、以下のコードを追加します。void ULoginQueueSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
IdentityInterface->AccelByteOnLoginQueueCancelCompleteDelegates->AddUObject(this, &ThisClass::OnLoginQueueCancelCompleted);
// ...
} -
最後に、
Deinitialize()関数で前のデリゲートをアンバインドします。void ULoginQueueSubsystem_Starter::Deinitialize()
{
// ...
IdentityInterface->AccelByteOnLoginQueueCancelCompleteDelegates->RemoveAll(this);
// ...
} -
AccelByteWarsプロジェクトをビルドし、コンパイルエラーがないことを確認します。
Resources
- このチュートリアルセクションで使用されるファイルは、Byte Wars Unreal GitHubリポジトリで入手できます。