UE4 ShooterGame Server研究

以下内容都是基于Unreal Engine版本:4.18.2-0+++UE4+Release-4.18。

操作系统是:Window10 x64专业版。

编译工具:VS2017 企业版。

在上一篇文章UE4 ShooterGame Standalone Dedicated Server(Windows & Linux),主要讲述如何编译、部署一台ShooterGame服务器。这篇文章里会讲述一些UE4 Server相关的基础知识,以及如何修改ShooterGame Dedicated Server的内容为我们所用。

阅读过一点ShooterGame代码再读此文效果更加,后面Server若无特指,均表示Dedicated Server。

参考文献:https://docs.unrealengine.com/latest/CHN/Gameplay/Networking/Server/index.html

UE4关于ENetRole的解释

UE4的ClientServer公用一套代码,从编译结果的角度来看就是他们依赖同一个.pak文件。所以在UE4内部,对于如何区分Client/Server有一套完整的机制,以Dedicated Server模式为例

enum ENetRole

ROLE_None这个对象没有网络连接,并不需要在C/S同步;

ROLE_SimulatedProxy 模拟对象,仅仅用于Client本地模拟,它的状态不能在本地主动被改变(只能被动改变);

ROLE_AutonomousProxy 自治对象,在Client接受各种输入,并且主动与Server通信;

ROLE_Authority Server权威,它表示一个真正的对象(为什么这么说呢?UE4官方建议:只有Server(包含Listen Server后文会提及)能产生需要在C/S间复制对象。如果Client自主产生了一个对象,那么它仅仅只会在创建它的Client上存在);

 

概念有点绕,举个实际的例子,比如我有二个客户端ClientAClinetB,他们通过Dedicated Server进行游戏,对应的ENetRole如下:

Server侧 – ClientA.Role = ROLE_Authority;ClientB.Role = ROLE_Authority;

ClientA侧 – ClientA.Role = ROLE_AutonomousProxy;ClientB.Role = ROLE_SimulatedProxy;

ClientB侧 – ClientA.Role = ROLE_SimulatedProxy;ClientB.Role = ROLE_AutonomousProxy;

简单的一句话概括就是:服务器拥有对象实体是ROLE_Authority,客户端拥有的本地对象是ROLE_AutonomousProxy,客户端展现的其他客户端对象是ROLE_SimulatedProxy。

 

UE4关于ENetMode的解释

ENetMode

NM_Standalone单机服务器模式,可以有1-n个本地客户端,无网络连接;

NM_DedicatedServer专用服务器模式,没有本地客户端;

NM_ListenServer监听服务器模式,一个本地客户端做主机;

NM_Client客户端,连接远程服务器;

那么问题来了,ENetRoleENetMode是不是有部分定义重复了?

不是。一些个人理解:ENetRole主要是用在和玩家角色直接相关内容上,往往会通过if (Role < ROLE_Authority)来判断代码是否需要对服务端进行RPCENetMode更宏观,提供了区分Listen ServerDedicated Server的方法例如,一些粒子效果不需要在Dedicated Server展现,但是需要在Listen Server展现时,可以依靠ENetMode区分。反过来看,ENetRole提供了区分本地操控客户端(ROLE_AutonomousProxy )与本地模拟客户端(ROLE_SimulatedProxy )的方法。

 

UE4 ShooterGame Server启动流程

ShooterGame Server的启动流程依附与UE4自身一套流程,UE4的一些基础类比如UGameInstanceAGameMode会暴露一些虚函数,ShooterGame继承这些类,并按需实现虚函数。实现中通过super关键字来调用基类的实现,达到嵌入ShooterGame自身逻辑的目的,附图:

图很简单,里面省略了很多资源的初始化与加载。实际我们需要关心的,GEngine是全局唯一的,然后创建GameInstance,确定游戏地图后才会创建GameMode,在GameMode里面初始化GameStateGameSession。可以看到核心还是Main()->Init()->Loop()的套路。

 

修改ShooterGame Server

先分析一下,如果目标是:

  1. ShooterGame Server作为一场战斗的核心逻辑;
  2. 战前匹配,战后结算放置到自定义(非UE4)的服务器;

需要解决的问题(只列了与ShooterGame 相关的):

  1. 需要一个控制服务器(ControlServer),来管理ShooterGame Server,这里包含了Socket通信,状态控制等问题;
  2. 搞清楚ShooterGame ClientServer的握手原理,因为需要加入自定义的参数,目的是为了验证客户端合法性;
  3. ShooterGame的状态扭转流程;

问题1限于篇幅后续有时间会再出一篇博文详细说明。先分析问题2、3:

UE4 Client连接Server步骤
  1. Client发送连接请求;
  2. Server如果接受请求,发送当前地图;
  3. Server等待Client加载地图;
  4. 加载完成后,Server会调用AGameMode::PreLogin
    • ShooterGame对其进行了重载AShooterGameMode::PreLogin,你可以在这里加入自己的自定义参数进行验证
  5. 然后服务器会调用 AGameMode::Login;
    •  ShooterGame并没有重载这个函数;
    • 该函数的作用是创建一个 PlayerController,可用于在今后复制到新连接的客户端。成功接收后,这个 PlayerController 将替代客户端的临时 PlayerController (之前被用作连接过程中的占位符);
    • 此时将调用 APlayerController::BeginPlay。应当注意的是,此时调用 RPC 函数尚存在安全风险。您应当等待 AGameMode::PostLogin 被调用完成;
  6. 如果一切顺利,AGameMode::PostLogin 将被调用;
    • ShooterGame对其进行了重载AShooterGameMode::PostLogin;
    • 在这里处理一些成功Login之后的逻辑,也可以放心的让服务器在此 PlayerController 上开始调用 RPC 函数;
ShooterGame游戏状态流程

核心的处理函数是AShooterGameMode::DefaultTimer,它是一个定时器函数,每1秒会执行一次。整个流程参考下图

需要做的:

  • WaitToStart加入自己等待开始的逻辑;
  • 在进入WaitingPostMatch前,进行战斗结算与空闲通知,断开与玩家的socket连接;

 

写的比较杂,在年前看没有时间把控制服务器相关的东西整理一下总结出来。UE4总体来说是一个“坑”比较多的引擎,需要时间去积累经验,社区资源比较匮乏,有些问题官方文档说的不详细,经常导致一个问题需要解决很久的情况。不过天道酬勤,新的一年加油。

(全文结束)

 

 


转载文章请注明出处:漫漫路 - lanindex.com

Leave a Comment

Your email address will not be published.