UE4でDedicated Serverを試す

UE4のDedicated Serverをさわってみたので内容をメモしておきます。
試したUE4のバージョン：4.24.3


# Dedicated Serverの実行方法

## スタンドアロン実行

* UE4をソースコードビルドしてプロジェクトから利用するようにする（Generate Visual Studio project filesが必要）。
* 以下のTargetファイルを追加する(Generate Visual Studio project filesが必要）。

```c++:[プロジェクト名]/Source/[プロジェクト名]Server.Target.cs

using UnrealBuildTool;
using System.Collections.Generic;

public class [プロジェクト名]ServerTarget : TargetRules
{
	public [プロジェクト名]ServerTarget(TargetInfo Target) : base(Target)
	{
		Type = TargetType.Server;
		DefaultBuildSettings = BuildSettingsVersion.V2;
		ExtraModuleNames.Add("[プロジェクト名]");
	}
}
```

* プロジェクト設定のMap&ModesのServer Default Mapでサーバー用のマップを指定する。
* プロジェクトをビルドする。
* .slnでDevelopment Serverを選択してビルドする。
* [プロジェクト名]/Binariesの中にできた[プロジェクト名]Server*を[ビルドしたディレクトリ]/[プロジェクト名]/Binariesに配置して実行する。

参照リンク

* [Legacy/Dedicated Server Guide (Windows & Linux) - UE4: Community Wiki](https://www.ue4community.wiki/Legacy/Dedicated_Server_Guide_(Windows_%26_Linux))
* [Unreal Engine 4.18.3 Dedicated Server Tutorial (Steam / Advanced Sessions) - YouTube](https://www.youtube.com/watch?v=iMNjp5L7_oA)
* [Standalone Dedicated Server - UE4: Guidebook](https://unreal.gg-labs.com/wiki-archives/networking/standalone-dedicated-server)
* [専用サーバーのコンパイル - Unreal Engine - 時と場所と冬と When, where and who are you? 別館](http://toyashiwasu.web.fc2.com/UnrealEngine/DedicatedServer/index.html) 


## プロジェクトから実行

プロジェクト設定のMap&ModesのServer Default Mapでサーバー用のマップを指定して以下のように実行する。
ビルドしなくてよいので確認のためには良さそう。

```bash
[UE4Editor.exeのパス] [プロジェクトのuprojectへのパス] Game -server -game -log
```

参照リンク

* [【UE4】マルチプレイにおけるDedicated Serverの建て方 - Qiita](https://qiita.com/Developer_KanZG/items/9b8cf4623432c3537eba)


# セッションとDedicated Server

## CreateSession

参考リンクではクライアント側からOpenLevelなどでIPアドレスとポートを指定して接続するように書いているものが多いがその場合ボイスチャットは利用できない（[こちらの記事](https://historia.co.jp/archives/8827/)参照）。
ボイスチャットを使うような場合にはCreateSessionしたセッションにJoinSessionする必要がある。

CreateSessionはそのまま用いると以下のようなエラーが発生して実行できない。

```bash
[2020.04.15-06.43.08:241][  0]LogScript: Warning: Script Msg: CreateSession - Invalid player state
```

[Advanced Sessions Plugin](https://forums.unrealengine.com/community/community-content-tools-and-tutorials/41043-advanced-sessions-plugin)のCreateAdvancedSessionのブループリントノードを使う際にIsDedicatedServerにチェックを入れるか、PlayerNumを0にしてbIsDedicatedをtrueした設定をCreateSession時に渡すような処理をC++で独自実装する必要がある。
ちなみに、IsDedicatedServerというノードでDedicatedServerかどうか判定できるっぽい。

## クライアントからのIP指定のセッション接続

OnlineSubsystemNull（+ WebSocketNetworkPlugin）で以下のような関数を作ってFBlueprintSessionResultをCreateAdvancedSessionに入力したら接続できてボイスチャットもできた。
PortはWebSocketNetworkPluginで指定したポート番号を指定。
TargetName、BuildUniqueIdは適当な値(testと0)でも接続できた。

```C++
#include "OnlineMyUtils.h"
#include "OnlineSessionSettings.h"
#include "OnlineIdentityNull.h"
#include "SocketSubsystem.h"
#include "Misc/DefaultValueHelper.h"

void UOnlineMyUtils::CreateSessionForJoin(const FString TargetName, const FString IpAddress, const int32 Port, const int32 BuildUniqueId, FBlueprintSessionResult& Result)
{
	Result.OnlineResult.PingInMs = 1;
	FOnlineSession* NewSession = &Result.OnlineResult.Session;
	FUniqueNetIdNull* UniqueId = new FUniqueNetIdNull(TargetName);
	NewSession->OwningUserId = MakeShareable(UniqueId);
	NewSession->OwningUserName = TargetName;
	NewSession->NumOpenPrivateConnections = 0;
	NewSession->NumOpenPublicConnections = 5;
	FOnlineSessionInfoNull* NullSessionInfo = new FOnlineSessionInfoNull();

	TArray<FString> IpAddressParts;
	IpAddress.ParseIntoArray(IpAddressParts, TEXT("."));
	int32 IpAddressInt = 0;
	if (IpAddressParts.Num() != 4)
	{
		UE_LOG(LogTemp, Warning, TEXT("Failed to read IpAddress"));
		return;
	}
	for (int i = 3; i >= 0; i--)
	{
		int32 IpAddressPartInt = 0;
		FDefaultValueHelper::ParseInt(IpAddressParts[3 - i], IpAddressPartInt);
		IpAddressInt += (IpAddressPartInt << i * 8);
	}
	
	NullSessionInfo->HostAddr = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM)->CreateInternetAddr();
	NullSessionInfo->HostAddr->SetIp(IpAddressInt);
	NullSessionInfo->HostAddr->SetPort(Port);
	NewSession->SessionInfo = MakeShareable(NullSessionInfo);

	NewSession->SessionSettings.NumPublicConnections = 5;
	NewSession->SessionSettings.NumPrivateConnections = 0;
	NewSession->SessionSettings.bShouldAdvertise = true;
	NewSession->SessionSettings.bIsLANMatch = false;
	NewSession->SessionSettings.bIsDedicated = true;
	NewSession->SessionSettings.bUsesStats = false;
	NewSession->SessionSettings.bAllowJoinInProgress = true;
	NewSession->SessionSettings.bAllowInvites = true;
	NewSession->SessionSettings.bUsesPresence = true;
	NewSession->SessionSettings.bAllowJoinViaPresence = true;
	NewSession->SessionSettings.bAllowJoinViaPresenceFriendsOnly = false;
	NewSession->SessionSettings.bAntiCheatProtected = false;
	NewSession->SessionSettings.BuildUniqueId = BuildUniqueId;

}
```

# AWS EC2でのDedicated Serverの実行

ひとまずThirdPersonならt2.micro、素のAmazon Linux2で動作し接続できた。

GPUインスタンスの場合はGPUが利用される？
ちなみにGPUインスタンスでクライアントを実行するには以下のような設定をする必要があった。

* [Linux インスタンスへの NVIDIA ドライバーのインストール - Amazon Elastic Compute Cloud](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/install-nvidia-driver.html)
 * ドライバ取得のためにはS3へのGETとLISTのRoleをEC2に割り当てるのが必要
* xconfigの設定
 * 参照リンク：[ディスプレイの無い環境でもNvidia GPUでXサーバを作るメモ | 7me](https://7me.oji.0j0.jp/2019/11/11/xorg-virtual-screen-with-nvidia-gpu/)

```bash
$ sudo nvidia-xconfig --enable-all-gpus --allow-empty-initial-configuration --use-display-device=None --virtual=1920x1080
```