《Exploring in UE4》Session與Onlinesubsystem[概念理解]

想弄清UE裡面的網路模塊,始終繞不過OnlineSubsystem與Session這兩個概念,我一開始對這一塊也挺頭疼的。後來花了點時間,參考網上的資料,對這一塊進行了一個相對全面的分析,希望對大家能有所幫助。

總的來說,我暫時把UE網路分成三個部分:

第一個部分是網路同步,包括Actor同步機制,RPC等。

第二個部分是移動同步的模擬,這一塊在基本上都在CharacterMovement裡面。

第三個部分是網路連接,包括OnlineSubsystem與Session等。也就是這篇文章所要分析的內容。

一.OnlineSubsystem

1.1 什麼是OnlineSubsystem?

OnlineSubsystem是一個多網路平台對接系統。其本身是抽象的,需要根據不同平台完成具體的實現。你可以通過他對接Steam,GooglePlay,Amazon,Xbox等,進而使用對應平台的各項自定義功能,如語音聊天,在線統計,遊戲大廳與遊戲匹配等。

總之,該系統的目的就是讓開發者可以快速的切換到不同平台去發布遊戲。需要注意的是,在UE裡面,OnlineSubsystem與Session機制息息相關,後面Session部分會做進一步的介紹。

1.2 OnlineSubsystem的初始化流程

首先有一點要聲明,4.8以前的的OnlineSubsystem是以一個Runtime模塊集成在UE的源碼裡面的。而我看到4.13以後(具體從哪個版本沒有確認)該系統改成了以插件Plugin的形式集成在引擎裡面,所以Module的初始化不同版本可能會有差異。

在一開始執行引擎初始化的時候,FEngineLoop::PreInit函數會調用FEngineLoop::AppInit進行OnlineSubSystem的載入。(如果是以插件形式集成,就要通過IPluginManager:: Get()->LoadMoudlesForEnabledPlugins()來完成了)

//FEngineLoop::AppInit LaunchEngineLoop.cpp#if WITH_ENGINE // Earliest place to init the online subsystems // Code needs GConfigFile to be valid // Must be after FThreadStats::StartThread(); // Must be before Render/RHI subsystem D3DCreate() // For platform services that need D3D hooks like Steam FModuleManager::Get().LoadModule(TEXT("OnlineSubsystem")); FModuleManager::Get().LoadModule(TEXT("OnlineSubsystemUtils")); // Init HighRes screenshot system. GetHighResScreenshotConfig().Init();#endif

當執行LoadModule的時候,會將當前模塊添加到FModuleMap Modules;裡面並開始載入該模塊,執行FOnlineSubsystemModule中的StartupModule函數。StartupModule函數決定當前使用哪個平台,並載入相應平台的模塊並初始化,流程如下:

  1. 根據項目Project的Config目錄下的DefaultEngine.ini中配置確定當前使用哪個平台,DefaultEngine.ini中的配置項如下,圖1-1使用的是steam
  2. 調用LoadSubsystemModule,根據配置讀取的字元串去載入對應的OnlineSubsystem +Name的Module,這裡是OnlineSubsystemSteam
  3. 載入成功後還要繼續調用對應平台Module的StartupModule函數。如果是steam,還需要到」../Engine/Binaries/ThirdParty/ Steamworks/Steamv132/Win64/」路徑下去載入其平台的dll文件(路徑可能有些偏差,具體看文件steam_api64.dll的位置) 代碼如下:
  4. 上面對應平台的Dll如果載入成功,需要註冊到基類FOnlineSubsystemModule裡面。其實就是添加到其OnlineFactories列表裡
  5. 前面完成了具體平台的Onlinesubsystem模塊的載入,但是其實真正的系統還沒有構建,只是創建並添加了其Factory而已。所以,繼續執行GetOnlineSubsystem嘗試獲取真正的OnlineSubsystem對象,如果沒有就通過Factory工廠進行創建
  6. 一般默認在非Shipping版本或者配置文件OnlineSubsystemSteam的bEnable為false的情況下在初始化OnlinesubsystemSteam的時候(包括其他平台),會CreateSubsystem失敗,然後Destroy該Onlinesubsystem。這樣引擎會默認創建OnlinesubsystemNull來替代 ,配置見圖1-2
  7. 創建Onlinesubsystem成功且能正確獲取到後設置DefaultPlatformService為當前平台

圖1-1

圖1-2

//對應上面流程第三步FString RootSteamPath = GetSteamModulePath(); FPlatformProcess::PushDllDirectory(*RootSteamPath); SteamDLLHandle = FPlatformProcess::GetDllHandle(*(RootSteamPath + "steam_api64.dll "));

//OnlineSubsystemModule的啟動代碼void FOnlineSubsystemModule::StartupModule(){ FString InterfaceString; // Load the platform defined "default" online services module if (GConfig->GetString(TEXT("OnlineSubsystem"), TEXT("DefaultPlatformService"), InterfaceString, GEngineIni) && InterfaceString.Len() > 0) { FName InterfaceName = FName(*InterfaceString); UE_LOG(LogOnline, Warning, TEXT("Begint to load default OnlineSubsystem module %s, using NULL interface"), *InterfaceString); // A module loaded with its factory method set for creation and a default instance of the online subsystem is required 前面的步驟2到步驟5都在這裡執行 if (LoadSubsystemModule(InterfaceString).IsValid() && OnlineFactories.Contains(InterfaceName) && GetOnlineSubsystem(InterfaceName) != NULL) { DefaultPlatformService = InterfaceName; } else { UE_LOG(LogOnline, Warning, TEXT("Unable to load default OnlineSubsystem module %s, using NULL interface"), *InterfaceString); InterfaceString = TEXT("Null"); InterfaceName = FName(*InterfaceString); // A module loaded with its factory method set for creation and a default instance of the online subsystem is required if (LoadSubsystemModule(InterfaceString).IsValid() && OnlineFactories.Contains(InterfaceName) && GetOnlineSubsystem(InterfaceName) != NULL) { DefaultPlatformService = InterfaceName; } } } else { UE_LOG(LogOnline, Warning, TEXT("No default platform service specified for OnlineSubsystem")); }}

關於OnlineSubsystemsteam的啟動:

如果Steam平台的Onlinesubsystem可以初始化成功(FOnlineSubsystemSteam::Init),那麼該函數會針對伺服器和客戶端分別對Steam平台進行初始化,即InitSteamworksServer以及InitSteamworksClient,另外還會進行官方伺服器列表的啟動更新操作。其中InitSteamworksClient函數除了在客戶端模式下進行Steam的初始化,還會根據Steam平台的語言來設置遊戲客戶端的語言。(這裡是讀取配置文件的Culture信息,實際上要到本地化數據的位置去查找如圖1-3)

另外,Steam模塊還重寫了IPNetDriver以及NetConnection的部分介面,所以啟動steam後真正載入的是USteamNetDriver以及USteamNetConnection。(NetDriver通過CreateNamedNetDriver_Local創建,這裡會首先根據配置文件DefaultEngine.ini裡面的內容嘗試載入配置的NetDriver,如果創建失敗就會創建默認的NetDriver)

圖1-3

1.3 OnlineSubsystem相關類關係

前面描述完流程後,可能覺得還是有點暈,下面整理了Onlinesubsystem相關類的類圖。簡單總結一下,

  1. Onlinesubsystem系統屬於UE眾多Module(模塊)的一個,所以需要通過一個管理類來管理所有Module的載入,這個管理類就是FMoudlemanager
  2. FModuleManager::Get().LoadModule(TEXT(「OnlineSubsystem」));這裡首先載入的模塊是FOnlineSubsystemModule(各個OnlineSubsystemModule的基類),他會讀取配置文件然後進一步載入指定的OnlineSubsystemModule(如FOnlineSubsystemSteamModule)
  3. FOnlineSubsystemModule有一個介面 GetOnlineSubsystem,這裡會根據對應Subsystem的FOnlineFactory(工廠模式)創建對應的FOnlineSubsystem
  4. 具體的OnlineSubsystem創建的成功表示他已經完成了相關的初始化Init(),不同的平台的初始化內容各不相同,比如steam是針對客戶端與伺服器分別執行初始化的
  5. 不同的OnlineSubsystem擁有不同的OnlineSession,因為不同平台對Session的處理有很大的差異,這個是由平台來決定的

圖1-4

二.Session

2.1.什麼是Session?

其實session在WEB領域應用的更為廣泛,直譯為會話。廣義來講,Session可以理解為一種客戶端到伺服器保持連接的一種解決方案。狹義來說,Session是一種數據,用來記錄保持這個連接的相關內容。

進一步到UE裡面,我們可以更形象的理解為遊戲中的開房間。伺服器運行後,就好比一個玩家開了一個房間,然後等待其他玩家的加入。其他玩家可以在網路上(包括區域網,互聯網)搜索到這個房間並加入進去。所以,把房間換成Session,就可以簡單理解為伺服器創建一個Session,然後客戶端搜索並加入這個Session。整個Session相關的各種類與數據就構成了Session機制,用於管理客戶端與伺服器的連接。

那麼session是必須的么?並不是,在4.14版本裡面,整個OnlineSubsystem系統被作為一個插件集成在引擎裡面,完全可以關掉,其相關的session功能也就基本上無效了。理論上如果伺服器不做任何限制,我們只要獲取到伺服器的IP與埠,我們客戶端就可以連接上去。

由此看來,Session最基本的功能就是:在一個多人遊戲中,客戶端需要通過Session機制連接到伺服器,以便伺服器驗證客戶端的合法性,控制連接人數等等。

2.2. UE4中的Session

UE中帶session的類確實不少,這可能給大家理解上帶來很多困難。你可以看到在遊戲初始化過程中有AGameSession,在OnlineSubsystem文件夾下有各種OnlineSessionXXX,我把最重要的幾個類拿出來畫了一個類圖。如下圖4-1:

圖2-1

總體來說,上面幾個類是Session機制的核心。

AGameSession顧名思義,其實其本身並不是Session信息的產生者與擁有者,他主要的目的就是負責Gameplay遊戲邏輯與具體的底層Session機制的交互。比如遊戲裡面有一個在網路上尋找Session的功能,那麼一般我們會通過遊戲邏輯(比如UI按鈕事件)調用GameSession的查找Session功能,GameSession會進一步從OnlineSubsystem裡面查詢Session。

二.OnlineSubsystem與Session是什麼關係

我們可以認為,目前UE的機制裡面,Session信息都是包含在OnlineSubsystem裡面的。因為不同的平台有不同的驗證機制,所以除了基本的IP地址埠等信息外,不同平台對Session的處理還可能有不同的內容,這樣就出現了IOnlineSession介面以及對應平台的如FOnlineSubsystemNull這樣的類,他把具體的Session信息封裝,加入一些與當前OnlineSubsystem相關的操作與處理。

最後幾個類(FOnineSession,FNamedOnlineSession, FOnlineSesssionInfo),其實就是具體的Session信息。裡面都是常見的Session數據,比如用戶唯一ID,伺服器IP地址,玩家數量配置等。前面不管是GameSession或者是OnlineSubsystem,最終操作的都是這裡的數據。

最後,還有需要注意的是區分客戶端與伺服器,Session的創建是在伺服器上進行的,玩家的註冊也是在伺服器上進行的(參考RegisterServer與RegisterPlayer),但是不代表客戶端沒有Session。客戶端也擁有圖中的各項Session數據,而且客戶端還可能通過搜索找到多個Session(OnlineSubsystemNull有FNamedOnlineSession的數組),每個Session表示不同的伺服器,這就是我們在網路上搜索Session並加入其中的基本原理。


推薦閱讀:

《伏龍記》ea實錄(week6)
GML 學習手記(GML概況二)
神谷英樹和他弟弟的遊戲回憶錄
遊戲性能優化(1)-why & benchmark

TAG:遊戲開發 | 虛幻引擎 | 遊戲引擎 |