文/Jerish 专栏:https://zhuanlan.zhihu.com/c_164452593


想弄清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:reInit函数会调用FEngineLoop::AppInit进行OnlineSubSystem的加载。(如果是以插件形式集成,就要通过IPluginManager:: Get()->LoadMoudlesForEnabledPlugins()来完成了)

 

 

//FEngineLoop::AppInitLaunchEngineLoop.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为当前平台

094646jflx5v2r0l5ufwe4.jpg图1-1
094645v5pzrqmzmdm27g27.jpg图1-2

 

  1. //对应上面流程第三步
  2. FString RootSteamPath = GetSteamModulePath();
  3. FPlatformProcess::PushDllDirectory(*RootSteamPath);
  4. SteamDLLHandle = FPlatformProcess::GetDllHandle(*(RootSteamPath + steam_api64.dll ));
图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的处理有很大的差异,这个是由平台来决定的

094646jj1upe15xx1y1j0e.jpg图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:

094647d669nl344o4tk3k4.jpg图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并加入其中的基本原理。

锐亚教育

锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛