52.8K Views
July 04, 17
スライド概要
2017年7月2日に行われたライセンシー様向けMO勉強会の資料です。基礎編 A (登壇者: historia 原龍さま)
Unreal Engineを開発・提供しているエピック ゲームズ ジャパンによる公式アカウントです。 勉強会や配信などで行った講演資料を公開しています。 公式サイトはこちら https://www.unrealengine.com/ja/
Multiplayer Online Deep Dive - Traveling historia Inc. 原 龍 © historia Inc. #UE4DD
Traveling ? ▪ Listen Server を開始したり、Server に接続したり、 各プレイヤーの接続やレベル移動周りの話です ▪ 今回は OnlineSubsystem の Session 等は解説しません © historia Inc. #UE4DD
Listen Server としてレベルを開始する → Listen 付き OpenLevel Server Map A Standalone Map X Map A で Listen 開始 © historia Inc. #UE4DD
Client として Server に接続する → ClientTravel Server Map A 接続させて Standalone Map X Client として接続リクエスト © historia Inc. #UE4DD
Client として Server に接続する → ClientTravel Server Map A 読み込むレベルを通知 いいよ Client Map A © historia Inc. #UE4DD
Server がレベル移動する → ServerTravel Server Map B 移動先のレベルを通知 移動します Client Map A © historia Inc. #UE4DD
Server がレベル移動する → ServerTravel Server Map B Client Map B 移動しました © historia Inc. #UE4DD
Listen Server としてレベルを開始する © historia Inc. #UE4DD
Listen Server としてレベルを開始する → Listen 付き OpenLevel Server Map A Standalone Map X Map A で Listen 開始 © historia Inc. #UE4DD
Listen Server としてレベルを開始する C++ /** Start Server */ UGameplayStatics::OpenLevel(GetWorld(), “LevelName", true, "listen"); © historia Inc. #UE4DD
Listen Server としてレベルを開始する Blueprint © historia Inc. #UE4DD
Server UGameplayStatics::OpenLevel Listen Server 開始リクエスト UEngine::Browse Listen Server 開始 UWorld::Listen UEngine::CreateNamedNetDriver NetDriver 作成 UNetDriver::InitListen © historia Inc. #UE4DD
Client として Server に接続する © historia Inc. #UE4DD
Client として Server に接続する → ClientTravel Server Map A Server 接続させて Standalone Map A いいよ Map X Client © historia Inc. Map A #UE4DD
Client として Server に接続する C++ APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), 0); /** Start Client */ PlayerController->ClientTravel( “XXX.XXX.XXX.XXX”, ETravelType::TRAVEL_Absolute, false); // IP Address // トラベルタイプ // シームレストラベル有効フラグ © historia Inc. #UE4DD
Client として Server に接続する Blueprint © historia Inc. #UE4DD
Client APlayerController::ClientTravel Server 接続リクエスト UEngine::Browse UPendingNetGame::InitNetDriver UEngine::CreateNamedNetDriver NetDriver 作成 接続開始 UNetDriver::InitConnect © historia Inc. #UE4DD
Client 接続開始 UNetDriver::InitConnect UConnection::CreateChannel ※CHTYPE_Control FNetControlMessage<NMT_Hello>::Send © historia Inc. Control Channel 作成 Server へ Hello メッセージ送信 #UE4DD
UPendingNetGame ? ▪ Control Channel で Server に Hello メッセージして待っている間、 UPendingNetGame オブジェクトが Server からの返答待ち及び、 Server が Closed になっていないかどうかの監視を行う © historia Inc. #UE4DD
Control Channel ? ▪ C/S 間の接続の制御に利用されるチャンネル ▪ UConnection 生成時に一つ生成される © historia Inc. #UE4DD
2 1 Hello Client Challenge Server Client Server 4 3 Login Client Welcome Server Client © historia Inc. Server #UE4DD
Server UWorld::NotifyControlMessage FNetworkVersion::IsNetworkCompatible FNetControlMessage<NMT_Challenge>::Send © historia Inc. Hello メッセージ受信 Network Version チェック Client へ Challenge メッセージ送信 #UE4DD
Network Version ? ▪ C/S 間でアプリケーションの Network Version の照合を行うことができる ▪ Server で Hello 受信時にバージョンチェックを行い、問題があれば Upgrade メッセージを Client に送信し、Client ではエラーとして処理する Hello 更新して出直して来い こんにちは Client ver. 1.0 Upgrade © historia Inc. Server ver. 1.1 #UE4DD
Network Version ? ▪ デフォルトだとバージョン情報は下記を繋げた文字列のハッシュ値 – Game Name – Project Version – ENGINE_NET_VERSION – Engine Network Protocol Version – Game Network Protocol Version © historia Inc. #UE4DD
Network Version を制御するには ? ▪ Game Network Protocol Version をアップデートに合わせて変更する – FNetworkVersion::GameCompatibleNetworkProtocolVersion ▪ もしバージョン違いをある程度許容したい場合は下記をバインドして独自制御する FNetworkVersion /** Called in GetLocalNetworkVersion if bound */ DECLARE_DELEGATE_RetVal( uint32, FGetLocalNetworkVersionOverride ); static FGetLocalNetworkVersionOverride GetLocalNetworkVersionOverride; /** Called in IsNetworkCompatible if bound */ DECLARE_DELEGATE_RetVal_TwoParams( bool, FIsNetworkCompatibleOverride, uint32, uint32 ); static FIsNetworkCompatibleOverride IsNetworkCompatibleOverride; © historia Inc. #UE4DD
Client UPendingNetGame::NotifyControlMessage Challenge メッセージ受信 ULocalPlayer::GetPreferredUniqueNetId OnlineSubsystem から Net ID を取得 FNetControlMessage<NMT_Login>::Send © historia Inc. Server へ Login メッセージ送信 #UE4DD
Server UWorld::NotifyControlMessage Login メッセージ受信 ログイン前処理 AGameModeBase::PreLogin UWorld::WelcomPlayer AGameModeBase::GameWelcomPlayer FNetControlMessage<NMT_Welcome>::Send © historia Inc. Client へ Welcome メッセージ #UE4DD
PreLogin でログイン許可判定 ▪ AGameModeBase::PreLogin では ErrorMessage を返すことができる ▪ 規定人数に達していたり、何らかの要因でログインさせたくない場合は ErrorMessage に何らかの文字を入れることで、 Client には Control Channel から Failure メッセージが飛ぶ Login ログインしたい お前はダメ Client Server Failure © historia Inc. #UE4DD
Client UPendingNetGame::NotifyControlMessage Welcome メッセージ受信 Map 名を受け取る UEngine::TickWorldTravel Map 読み込み UEngine::LoadMap UPendingNetGame::LoadMapCompleted FNetControlMessage<NMT_Join>::Send © historia Inc. Server へ Join メッセージ送信 #UE4DD
Server UWorld::NotifyControlMessage Join メッセージ受信 PlayerController をスポーン UWorld::SpawnPlayActor ログイン受け入れ AGameModeBase::Login AGameModeBase::PostLogin © historia Inc. #UE4DD
ここまでの内容をまとめると © historia Inc. #UE4DD
Server Server / Client • UNetConnection • UControlChannel • AGameModeBase • UNetDriver Client 1 • Server / Client • UNetConnection • UControlChannel Client 2 UNetDriver • © historia Inc. UNetDriver #UE4DD
Server がレベル移動する © historia Inc. #UE4DD
Server がレベル移動する → ServerTravel Server Map B Server Map B Client Map B 移動します Client Map A 移動しました © historia Inc. #UE4DD
Server がレベル移動する C++ /** Start ServerTravel */ GetWorld()->ServerTravel(“LevelName”, false); © historia Inc. #UE4DD
Server がレベル移動する Blueprint © historia Inc. #UE4DD
Server ServerTravel 開始 UWorld::ServerTravel AGameModeBase::CanServerTravel ServerTravel 可否判定 AGameModeBase::ProcessServerTravel NextURL 設定 (後に UEngine::Browse でレベル移動する) AGameModeBase::ProcessClientTravel 各 Client で ClientTravel を実行 APlayerController::ClientTravel © historia Inc. (Run on Owning Client & Reliable) #UE4DD
Client APlayerController::ClientTravel Server 接続リクエスト 以下、前述した ClientTravel の流れと同じ Control Channel を介してログイン処理を行う © historia Inc. #UE4DD
つまり、ServerTravel でレベル移動を行うと、 移動する度に Server へのログイン処理が実行される © historia Inc. #UE4DD
接続を維持したままレベル移動を行いたい場合は? → SeamlessTravel を利用する © historia Inc. #UE4DD
SeamlessTravel ? ▪ レベルをストリーミングで読み込み、Transition Map を経由しながら GameMode 等の Actor を引き継いだままレベル遷移する事で、オーバーヘッドを小さくできる ▪ 非 Seamless な ServerTravel と違い、Client との接続は保持したまま BeforeMap Transition Map © historia Inc. AfterMap #UE4DD
SeamlessTravel を有効にする AGameModeBase public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=GameMode) uint32 bUseSeamlessTravel : 1; © historia Inc. #UE4DD
Transition Map ? ▪ ワールドを保持したままレベル遷移するため、古いレベルがオンメモリのまま 新しいレベルが読み込まれる ▪ つまり瞬間的に二つのレベルが共存するため、メモリに優しくない ▪ そのため小さな Transition Map を間に挟むことで、古いレベルを解放してから 新しいレベルのロードが始まるように処理されている © historia Inc. #UE4DD
Transition Map を設定する © historia Inc. #UE4DD
Server AGameModeBase::ProcessServerTravel NextURL は 設定しない (UEngine::Browse されない) UWorld::SeamlessTravel FSeamlessTravelHander::StartTravel FSeamlessTravelHander::LoadPackageAsync © historia Inc. SeamlessTravel 開始 レベルストリーミング開始 #UE4DD
FSeamlessTravelHander ? ▪ SeamlessTravel 時のシーケンス管理用オブジェクト ▪ TransitionMap を LoadAsync して完了待ちを行い、完了したら古いレベルを PendingKill してから CollectGarbage する ▪ TransitionMap への移行が完了したら、新しいレベルへの移行シーケンスを開始する © historia Inc. #UE4DD
ServerTravel - PreClientTravel NotifyLoadedWorld PostSeamlessTravel SeamlessTravel ServerNotifyLoadedWorld Server Client / Reliable Server / Reliable Client SeamlessTravel NotifyLoadedWorld ClientTravel - PreClientTravel © historia Inc. #UE4DD
開始 / 終了のトリガー ServerTravel - PreClientTravel NotifyLoadedWorld PostSeamlessTravel SeamlessTravel ServerNotifyLoadedWorld Server Client / Reliable Server / Reliable Client SeamlessTravel NotifyLoadedWorld ClientTravel - PreClientTravel © historia Inc. #UE4DD
SeamlessTravel 時に引き継がれるアクターは? ▪ [Server] GameMode / GameState ▪ [Server] 有効な PlayerState を持つ全ての Controller ▪ [Server] 全ての PlayerController ▪ [Server / Client] 各 Server / Client が所持する PlayerController © historia Inc. #UE4DD
ゲーム都合で引き継ぐアクターを追加したい場合は? ▪ 下記関数をオーバーライドして ActorList に追加する – AGameModeBase ▪ GetSeamlessTravelActorList(bool bToTransition, TArray<AActor*>& ActorList) – APlayerController ▪ GetSeamlessTravelActorList(bool bToEntry, TArray<class AActor*>& ActorList) © historia Inc. #UE4DD
と、ドキュメントには記載があり、大体合ってるが… © historia Inc. #UE4DD
SpawnPlayerController 新規に PlayerController を スポーンして Swap している © historia Inc. #UE4DD
PlayerController で引き継がれるプロパティは? ▪ Location, Rotation ▪ 保持する PlayerState の全プロパティ(コピーされる) ▪ RemoteRole 等、Online 系のプロパティ © historia Inc. #UE4DD
ゲーム都合で引き継ぐプロパティを追加したい場合は? AGameModeBase /** Called when a PlayerController is swapped to a new one during seamless travel */ UFUNCTION(BlueprintImplementableEvent, Category=Game, meta=(DisplayName="OnSwapPlayerControllers")) void K2_OnSwapPlayerControllers(APlayerController* OldPC, APlayerController* NewPC); イベント呼び出し時に 必要なものをコピーする © historia Inc. #UE4DD
OnSwapPlayerControllers の注意点 ▪ PlayerController が Local or Remote で Swap タイミングが異なる – Local PlayerController は Swap -> BeginPlay の順番 – Remote PlayerController は BeginPlay -> Swap の順番 ▪ Swap が呼び出されるのは Server のみ © historia Inc. #UE4DD
SeamlessTravel をデバッグする © historia Inc. #UE4DD
SeamlessTravel をデバッグする時の注意点 ▪ PIE は現状だと非サポート ▪ デバッグするためには Standalone で起動する必要がある – イテレーション速度の低下 – そもそもエディタからは複数プロセスで Standalone 起動はできない ▪ game オプション付きで起動するバッチファイルを作るのがオススメ © historia Inc. #UE4DD
Visual Studio でデバッグする ▪ エディタもしくはバッチファイルから Standalone 起動した場合、 Visual Studio でデバッグしたいなら Attach to Process するしかない ▪ デバッグする度に、検索窓も無いプロセス一覧から特定のプロセスを探して アタッチして…、というのは非常に面倒 © historia Inc. #UE4DD
UnrealVS を使ってコマンドラインに渡す引数を追加する [ProjectName] [起動したいMap] –game [その他オプション] と設定して Start Debugging すると、デバッグしながら起動できる 実行したコマンドラインオプションは履歴が残り、プルダウンで設定できるので便利 © historia Inc. #UE4DD
UnrealVS で楽にはなるが、それでも PIE 非対応は辛い… AGameModeBase::CanServerTravel // NOTE - This is a temp check while we work on a long term fix // There are a few issues with seamless travel using single process PIE, so we're disabling that for now while working on a fix SeamlessTravel + PIE は現状だといくつかのバグを抱えていて、 将来的にこれは解決される見込みとのことなので、今後に期待 © historia Inc. #UE4DD
まとめ ▪ Server としてレベル移動する時、Client として Server に接続する 初回のレベル移動はブロッキングが発生する ▪ C/S 間接続のキモは UControlChannel で、メッセージタイプも多くないので 何がサポートされているのかを最初に眺めてみると良い ▪ SeamlessTravel はデバッグが辛いので早期改善を希望 © historia Inc. #UE4DD
まとめ ▪ 色々困ったら下記クラスの実装を追うと良い – UWorld – AGameModeBase – APlayerController – UNetConnection – UControlChannel – FSeamlessTravelHandler © historia Inc. #UE4DD
ご清聴ありがとうございました historia Inc. 原 龍 © historia Inc. #UE4DD