オンラインサブシステムを使用してサーバーを設定する - AccelByte Multiplayer Servers (AMS) による専用サーバー - (Unreal Engine モジュール)
注釈:本資料はAI技術を用いて翻訳されています。
専用サーバーのフローを理解する
まず、AccelByte Multiplayer Servers (AMS) によってゲームサーバーがどのように管理されるかのフローを確認しましょう。AMS 専用サーバーの状態の詳細については、AGS SDK を使用して専用サーバーを AMS と統合するガイドをご覧ください。
AGS オンラインサブシステムについて
これで AccelByte Gaming Services (AGS) オンラインサブシステム (OSS) を使用する準備が整いました。Byte Wars プロジェクトには、MultiplayerDSEssentialsSubsystemAMS_Starter というサブシステムがあります。このクラスは、専用サーバー機能をすぐに実装できるように、必要な宣言と定義を提供します。
MultiplayerDSEssentialsSubsystemAMS_Starter クラスファイルは以下の場所にあります:
- ヘッダーファイル:
/Source/AccelByteWars/TutorialModules/Play/MultiplayerDSEssentials/MultiplayerDSEssentialsSubsystemAMS_Starter.h - CPP ファイル:
/Source/AccelByteWars/TutorialModules/Play/MultiplayerDSEssentials/MultiplayerDSEssentialsSubsystemAMS_Starter.cpp
クラスで提供されている内容を見てみましょう。
MultiplayerDSEssentialsSubsystemAMS_Starterクラスのヘッダーファイルには、3つの変数宣言と1つの関数があります。GetSessionInfo: キャッシュされたセッション情報を取得するために使用されます。bServerAlreadyRegister:RegisterServerの呼び出しが成功したかどうかを示し、サーバーが不要な HTTP 呼び出しを送信するのを防ぐために使用されます。bUnregisterServerRunning:UnregisterServerが呼び出され、現在レスポンスを待っている間に、サーバーがUnregisterServerを呼び出すのを防ぐために使用されます。ABSessionInt: OSS 自体へのインターフェースです。
private:
// ...
TSharedPtr<FAccelByteModelsV2GameSession> GetSessionInfo(const FName SessionName) const;
bool bServerAlreadyRegister;
bool bUnregisterServerRunning;
FOnlineSessionV2AccelBytePtr ABSessionInt;
MultiplayerDSEssentialsSubsystemAMS_StarterCPP ファイルを開き、その中のUMultiplayerDSEssentialsSubsystemAMS_Starter::Initializeに移動します。インターフェース自体を取得する実装があることに注目してください。これにより、ABSessionIntを使用してすぐに OSS とやり取りできます。
void UMultiplayerDSEssentialsSubsystemAMS_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
ABSessionInt = StaticCastSharedPtr<FOnlineSessionV2AccelByte>(Online::GetSessionInterface(GetWorld()));
ensure(ABSessionInt);
// ...
}
サーバーログイン
ゲームサーバーは、前のチュートリアル(IAM のセットアップ)で設定した AMS にアクセスするためにログインする必要があります。
Unreal Engine には自動ログイン機能が組み込まれているため、DefaultEngine.ini ファイルに以下の AccelByte 設定を行っていれば、サーバーログインは自動的に実行されます。
[OnlineSubsystem]
DefaultPlatformService=AccelByte
サーバーの登録
ログインに成功した後、ゲームサーバーを AMS に登録する必要があります。これにより、AMS がゲームサーバーを認識し、適切に管理できるようになります。
Unreal Engine には、GameSession クラスに組み込みの RegisterServer() 関数があります。Byte Wars プロジェクトでは、AGameSession から派生した AccelByteWarsGameSession というクラスが作成されています。このクラスは RegisterServer() をオーバーライドして OnRegisterServerDelegates デリゲートを呼び出します。この実装では、サーバー登録関数をそのデリゲートにバインドします。参考までに、AccelByteWarsGameSession CPP ファイルは /Source/AccelByteWars/Core/System/AccelByteWarsGameSession.cpp にあります。
基本を理解したところで、AGS OSS を使用してサーバーを登録する実装を行います。
-
MultiplayerDSEssentialsSubsystemAMS_Starterヘッダーファイルを開き、以下の関数を宣言します:private:
void RegisterServer(const FName SessionName); -
引き続きヘッダーファイルで、
RegisterServerが完了したときのコールバックとして使用する別の関数宣言を追加します:private:
// ...
void OnRegisterServerComplete(const bool bSucceeded); -
MultiplayerDSEssentialsSubsystemAMS_StarterCPP ファイルに関数定義を作成します。次に、以下のコードを追加して、ゲームサーバーを AMS に登録します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::RegisterServer(const FName SessionName)
{
UE_LOG_MultiplayerDSEssentials(Verbose, TEXT("called"))
// Abort if the session interface is invalid.
if (!ABSessionInt)
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Session interface is null"))
OnRegisterServerComplete(false);
return;
}
if (!IsRunningDedicatedServer())
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("The game instance is not a dedicated server"));
OnRegisterServerComplete(false);
return;
}
if (bServerAlreadyRegister)
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("The server is already registered"));
OnRegisterServerComplete(false);
return;
}
ABSessionInt->RegisterServer(SessionName, FOnRegisterServerComplete::CreateUObject(
this, &ThisClass::OnRegisterServerComplete));
} -
引き続き CPP ファイルで、コールバックの定義を作成します。ログを呼び出し、専用サーバーを既に登録済みとしてフラグを立て、追加の登録試行をキャンセルして HTTP 呼び出しを節約します。
void UMultiplayerDSEssentialsSubsystemAMS_Starter::OnRegisterServerComplete(const bool bSucceeded)
{
UE_LOG_MultiplayerDSEssentials(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))
if (bSucceeded)
{
bServerAlreadyRegister = true;
}
AAccelByteWarsGameMode::OnRegisterServerCompleteDelegates.Broadcast(bSucceeded);
} -
前述の
OnRegisterServerDelegatesデリゲートにRegisterServerをバインドします。MultiplayerDSEssentialsSubsystemAMS_Starterクラスの CPP ファイルのInitialize()関数に移動し、以下のハイライトされたコードを追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
AAccelByteWarsGameSession::OnRegisterServerDelegates.AddUObject(this, &ThisClass::RegisterServer);
// ...
} -
MultiplayerDSEssentialsSubsystemAMS_Starterが初期化解除されたときにデリゲートをアンバインドします。Deinitialize()関数に移動し、以下のハイライトされたコードを追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Deinitialize()
{
// ...
AAccelByteWarsGameSession::OnRegisterServerDelegates.RemoveAll(this);
// ...
} -
プロジェクトをコンパイルし、エラーがないことを確認します。
サーバー準備完了の送信
前に作成したサーバー登録の実装は、SDK 設定で bManualRegisterServer=false を設定している場合、ゲームサーバーを AMS に対して準備完了として自動的に設定します。サーバーを準備完了としてマークすることは、サーバーが着信プレイヤーを受け入れる準備ができていることを意味します。
ただし、一部のゲームでは、大きなアセットの読み込みなど、サーバーが着信プレイヤーを処理する準備ができる前に処理したいことがあるかもしれません。そのため、サーバーを手動で準備完了としてマークすることが適切なオプションになる場合があります。このセクションでは、AMS でサーバーを準備完了としてマークするリクエストを送信する方法を学びます。
-
まず、SDK 設定で
bManualRegisterServer=trueを有効にしていることを確認してください。 -
MultiplayerDSEssentialsSubsystemAMS_Starterヘッダーファイルを開き、以下の関数を宣言します:private:
// ...
void SendServerReady(const FName SessionName); -
引き続きヘッダーファイルで、
SendServerReadyが完了したときのコールバックとして使用する別の関数宣言を追加します:private:
// ...
void OnSendServerReadyComplete(const bool bSucceeded); -
MultiplayerDSEssentialsSubsystemAMS_StarterCPP ファイルに関数定義を作成します。次に、以下のコードを追加して、AMS サーバーを準備完了状態に設定します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::SendServerReady(const FName SessionName)
{
if (!ABSessionInt)
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Session interface is null"));
OnSendServerReadyComplete(false);
return;
}
if (!IsRunningDedicatedServer())
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("The game instance is not a dedicated server"));
OnSendServerReadyComplete(false);
return;
}
if (bServerAlreadyRegister)
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("The server is already registered and is in the ready state"));
OnSendServerReadyComplete(false);
return;
}
// Registering the server manually by setting it as ready.
ABSessionInt->SendServerReady(SessionName, FOnRegisterServerComplete::CreateUObject(this, &ThisClass::OnSendServerReadyComplete));
} -
コールバックの定義を作成します。この関数では、追加の登録試行を防ぐために、サーバーを既に登録済みとしてフラグを有効にします。
void UMultiplayerDSEssentialsSubsystemAMS_Starter::OnSendServerReadyComplete(const bool bSucceeded)
{
UE_LOG_MultiplayerDSEssentials(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? TEXT("TRUE") : TEXT("FALSE")))
if (bSucceeded)
{
bServerAlreadyRegister = true;
}
} -
Byte Wars では、ゲームサーバーは着信プレイヤーを受け入れる準備ができる前に何も処理する必要がありません。したがって、ゲームサーバーが登録されるとすぐにサーバー準備完了リクエストを送信します。ゲームプロジェクトでは、ゲームアセットが完全に読み込まれたとき、重要な非同期プロセスが完了したときなど、異なる方法で処理したい場合があります。前述の
OnRegisterServerDelegatesデリゲートにSendServerReadyをバインドします。MultiplayerDSEssentialsSubsystemAMS_Starterクラスの CPP ファイルのInitialize()関数に移動し、以下のハイライトされたコードを追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
AAccelByteWarsGameSession::OnRegisterServerDelegates.AddUObject(this, &ThisClass::SendServerReady);
// ...
} -
MultiplayerDSEssentialsSubsystemAMS_Starterが初期化解除されたときにデリゲートをアンバインドします。Deinitialize()関数に移動し、以下のハイライトされたコードを追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Deinitialize()
{
// ...
AAccelByteWarsGameSession::OnRegisterServerDelegates.RemoveAll(this);
// ...
} -
プロジェクトをコンパイルし、エラーがないことを確認します。
サーバーの登録解除とシャットダウン
ゲームが終了したら、ゲームサーバーを AMS から登録解除してシャットダウンする必要があります。これにより、サーバーがゾンビサーバー(セッションは終了したがサーバーはまだアクティブ)になるのを防ぎます。
-
サーバー登録機能と同様に、
GameSessionを使用してサーバーの登録解除を呼び出しますが、組み込みのGameSession(AGameSession) には登録解除サーバーまたは同等の関数がありません。そのため、AccelByteWarsGameSessionにUnregisterServerという新しい関数が作成され、AAccelByteWarsInGameGameMode::CloseGameでトリガーされます。登録解除関数自体を実装するには、実装をAccelByteWarsGameSession::OnUnregisterServerDelegatesデリゲートにバインドする必要があります。 -
サーバー登録解除機能を実装します。
MultiplayerDSEssentialsSubsystemAMS_Starterヘッダーファイルを開き、以下の関数宣言を追加します:private:
// ...
void UnregisterServer(const FName SessionName); -
UnregisterServerのコールバックとして、もう1つの関数宣言を追加します:private:
// ...
void OnUnregisterServerComplete(const bool bSucceeded); -
UnregisterServerの定義を作成します。MultiplayerDSEssentialsSubsystemAMS_StarterCPP ファイルを開き、以下のコードを追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::UnregisterServer(const FName SessionName)
{
UE_LOG_MultiplayerDSEssentials(Verbose, TEXT("called"))
// Abort if the session interface is invalid.
if (!ABSessionInt)
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Session interface is null"))
OnUnregisterServerComplete(false);
return;
}
if (!IsRunningDedicatedServer())
{
UE_LOG_MultiplayerDSEssentials(Warning, TEXT("The game instance is not a dedicated server"));
OnUnregisterServerComplete(false);
return;
}
ABSessionInt->UnregisterServer(SessionName, FOnUnregisterServerComplete::CreateUObject(
this, &ThisClass::OnUnregisterServerComplete));
bUnregisterServerRunning = true;
} -
コールバック関数の定義を作成します。コールバックを受信したときに専用サーバーを閉じるロジックを追加します:
void UMultiplayerDSEssentialsSubsystemAMS_Starter::OnUnregisterServerComplete(const bool bSucceeded)
{
UE_LOG_MultiplayerDSEssentials(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))
bUnregisterServerRunning = false;
FPlatformMisc::RequestExit(false);
} -
前述の
OnUnregisterServerDelegateデリゲートにUnregisterServerをバインドします。MultiplayerDSEssentialsSubsystemAMS_Starterクラスの CPP ファイルのInitialize()関数に移動し、以下のハイライトされたコードを追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
AAccelByteWarsGameSession::OnUnregisterServerDelegates.AddUObject(this, &ThisClass::UnregisterServer);
// ...
} -
デリゲートをアンバインドします。前と同様に、
Deinitialize()関数に移動し、以下のハイライトされたコードを追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Deinitialize()
{
// ...
AAccelByteWarsGameSession::OnUnregisterServerDelegates.RemoveAll(this);
// ...
} -
プロジェクトをコンパイルし、エラーがないことを確認します。
ドレイン状態の処理
ドレイン信号は、専用サーバーが実行されている仮想マシン (VM) が削除予定であることを通知します。理想的には、サーバーはプレイヤーにサービスを提供していないときにのみシャットダウンする必要があります。まだプレイヤーにサービスを提供している場合は、実行を続けてドレイン信号を無視する必要があります。ドレイン動作の詳細については、このページをご覧ください。
Byte Wars では、ドレイン信号を次のように処理します:
Wait for 5 seconds 状態は、セッション情報の更新の潜在的な遅延に対応します。これは、更新が受信される直前にバックエンドでサーバーが要求された場合に、早期のクレーム信号がトリガーされるのを防ぐのに役立ちます。
-
MultiplayerDSEssentialsSubsystemAMS_Starterヘッダーファイルを開き、以下の関数宣言を追加します:private:
// ...
void OnAMSDrainReceived();
void ExecuteDrainLogic(); -
ドレインロジックを実装します。
MultiplayerDSEssentialsSubsystemAMS_StarterCPP ファイルを開き、以下の関数実装を追加します。この関数は、サーバーが現在アクティブなゲームセッションをホストしているかどうかをチェックします。セッションがアクティブな場合は、ドレイン信号を無視してサーバーを実行し続けます。そうでない場合は、サーバーの登録解除に進みます。void UMultiplayerDSEssentialsSubsystemAMS_Starter::ExecuteDrainLogic()
{
const TSharedPtr<FAccelByteModelsV2GameSession> SessionInfo = GetSessionInfo(NAME_GameSession);
if (SessionInfo && SessionInfo->IsActive)
{
UE_LOG_MultiplayerDSEssentials(Log, TEXT("Received AMS drain message when there is still in active session; Ignoring the AMS drain message!"));
return;
}
UE_LOG_MultiplayerDSEssentials(Log, TEXT("Received AMS drain message; Shutting down the server now!"));
UnregisterServer(NAME_GameSession);
} -
ドレイン遅延を開始するドレインコールバックを実装します。以下の関数実装を追加します。
void UMultiplayerDSEssentialsSubsystemAMS_Starter::OnAMSDrainReceived()
{
// Wait for some time to accomodate session info update delay.
// Get configured delay value.
float DrainDelay = AccelByteWarsUtility::GetLaunchParamFloatValueOrDefault(
KEY_DRAIN_LOGIC_DELAY,
SECTION_DRAIN_LOGIC_DELAY,
DEFAULT_DRAIN_LOGIC_DELAY);
// Start one shot timer.
GetWorld()->GetTimerManager().SetTimer(
DrainLogicTimerHandle,
FTimerDelegate::CreateUObject(this, &ThisClass::ExecuteDrainLogic),
DrainDelay,
false,
DrainDelay);
UE_LOG_MultiplayerDSEssentials(Log, TEXT("Received AMS drain message. Wait %f seconds before executing drain logic."), DrainDelay);
} -
MultiplayerDSEssentialsSubsystemAMS_Starterクラスの CPP ファイルのInitialize()関数に移動し、以下のハイライトされたコードを追加してOnAMSDrainReceived()関数を登録します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
ABSessionInt->OnAMSDrainReceivedDelegates.AddUObject(this, &ThisClass::OnAMSDrainReceived);
// ...
} -
UMultiplayerDSEssentialsSubsystemAMS_Starter::Deinitialize()関数内に以下のハイライトされたコードを追加して、デリゲートをアンバインドします:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Deinitialize()
{
// ...
ABSessionInt->OnAMSDrainReceivedDelegates.RemoveAll(this);
// ...
} -
プロジェクトをコンパイルし、コンパイルエラーがないことを確認します。
セッション終了の処理
サーバーがまだアクティブであっても、バックエンドがセッションを終了する場合があります。ここでは、セッション終了通知が発火したときにサーバーが自身の登録を解除するようにします。
-
MultiplayerDSEssentialsSubsystemAMS_Starterヘッダーファイルを開き、以下の関数宣言を追加します:private:
// ...
void OnV2SessionEnded(const FName SessionName); -
セッション終了コールバックを実装します。
MultiplayerDSEssentialsSubsystemAMS_Starterヘッダーファイルを開き、以下の関数宣言を追加します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::OnV2SessionEnded(const FName SessionName)
{
UE_LOG_MultiplayerDSEssentials(Log, TEXT("Received AMS session ended notification; Shutting down the server!"));
if (GetWorld(); AAccelByteWarsGameMode* GameMode = Cast<AAccelByteWarsGameMode>(GetWorld()->GetAuthGameMode()))
{
GameMode->CloseGame(TEXT("Receive AMS session ended notification"));
}
} -
UMultiplayerDSEssentialsSubsystemAMS_Starter::Initialize:の下にOnV2SessionEndedを追加します。void UMultiplayerDSEssentialsSubsystemAMS_Starter::Initialize(FSubsystemCollectionBase& Collection)
{
// ...
ABSessionInt->OnV2SessionEndedDelegates.AddUObject(this, &ThisClass::OnV2SessionEnded);
// ...
} -
UMultiplayerDSEssentialsSubsystemAMS_Starter::Deinitialize()関数の下でデリゲートを削除します:void UMultiplayerDSEssentialsSubsystemAMS_Starter::Deinitialize()
{
// ...
ABSessionInt->OnV2SessionEndedDelegates.RemoveAll(this);
} -
プロジェクトをコンパイルし、コンパイルエラーがないことを確認します。
実装のアクティベーション
ゲームで実装を使用するには、スターターファイルをアクティベートする必要があります。
- Unreal Engine エディターを開きます。
- コンテンツブラウザウィンドウで、
/Content/TutorialModules/Play/MultiplayerDSEssentials/に移動します。 DA_MultiplayerDSEssentialsというデータアセットを開き、Is Starter Mode Activeを有効にします。- データアセットを保存します。

リソース
- このチュートリアルセクションで使用されているファイルは、Byte Wars GitHub リポジトリで入手できます。
- AccelByteWars/Content/TutorialModules/Play/MultiplayerDSEssentials/DA_MultiplayerDSEssentials.uasset
- AccelByteWars/Source/AccelByteWars/TutorialModules/Play/MultiplayerDSEssentials/MultiplayerDSEssentialsSubsystemAMS_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Play/MultiplayerDSEssentials/MultiplayerDSEssentialsSubsystemAMS_Starter.cpp
- AccelByteWars/Source/AccelByteWars/Core/System/AccelByteWarsGameSession.cpp
- AccelByteWars/Source/AccelByteWars/Core/GameModes/AccelByteWarsInGameGameMode.cpp