- 复制是单向的,S—>C;
- RPC客户端调用是需要有Net Owner(PlayerController,Pawn/Character(被PlayerController控制),PlayerState)
(1)游戏架构
参考: https://docs.unrealengine.com/4.26/zh-CN/InteractiveExperiences/Networking/Blueprints/
●GameInstance: 不参与网络复制,作为游戏实例而存在,在关卡切换载入时依然存在,可以用于存储用户本身的数据
●GameMode: 只存在于Server上,核心逻辑存在于这里
●GameState: 在Server和所有Client之间复制,用于存储当前游戏相关的数据
●PlayerController: 每个Client只持有自己的PlayerController,并与Server进行同步
●PlayerState: 存储每个Player的信息,会同步到所有的Client。
●Pawns: 与所有的Client同步,但是与PlayerController以及PlayerState不同的是,Pawn死亡之后会被销毁,直到Respawn为止。
(2)Role与同步
Server和Client都可以使用Role系统来判断持有的Actor状态,Role被分为四种
●ROLE_Authority:对Actor拥有控制权
●ROLE_None:不参与网络复制
●ROLE_SimulatedProxy:接受来自服务器更新的模拟控制器,使用已知信息进行预判
●ROLE_AutonomousProxy:接受来自玩家的直接控制,配合玩家输入进行预判
客户端的模拟预判可以有效防止网络延迟引起的Lag,但是在处理角色动作,尤其是Blink之类的操作时,依然会导致奇怪的现象。
为了防止这些现象,官方对CharacterMovmentComponent作了增强,这个类会维护一个动作列表,在同步时,Client会被要求进行动作列表的重放,而不是直接更新位置。参考:https://docs.unrealengine.com/4.26/zh-CN/InteractiveExperiences/Networking/CharacterMovementComponent/
(3)所有权
(4)属性回调与RPC
属性回调一定会执行;
RPC可能因为同步问题得不到执行。
(5)RPC与Actor同步谁先执行?
问题:服务器ActorA在创建一个新的ActorB的函数里同时执行自身的一个Client的RPC函数,RPC与ActorB的同步哪个先执行?(原答案不准确已修改)
答案是不确定。
我们可以通过设置控制台变量net.DelayUnmappedRPCs 1允许客户端等到这个对象生成的时候再去执行,但是仅限于可靠的RPC。
(5.1)拥有连接
一定是客户端第一次链接到服务器,服务器同步过来的这个PlayerController(也就是拥有连接的PlayerController)。进一步来说,这个Controller里面包含着相关的NetDriver,Connection以及Session信息。
一旦Actor(客户端上)有连接,他的Role(控制权限)就是ROLE_AutonomousProxy,如果没有连接,他的Role(控制权限)就是ROLE_SimulatedProxy 。
参考:
http://www.cppblog.com/flyindark/archive/2017/01/14/214596.html
(6)角色 Net Role
AActor.Role描述了角色的网络属性,从而决定了rpc和replicated时的行为表现。这3个网络Role属性分别是:
*ROLE_Authority,
*ROLE_AutonomousProxy,
*ROLE_SimulatedProxy
ROLE_Authority,在服务器上的所有角色都是Authority属性;
ROLE_AutonomousProxy,客户端上的本地角色;
ROLE_SimulatedProxy,客户端上的网络角色;
so
服务器上的所有角色都是Authority属性,当前控制的角色可以用IsLocalControlled区分
客户上当前控制的角色具有Autonomous属性,
客户端上的远程角色具有SimulatedProxy属性;
(7)网络模式 Net Mode
ENetMode AActor.GetNetMode()
NM_Standalone, /** 单机游戏,无网络链接,可以是多玩家,类似于单服务器 */
NM_DedicatedServer, /** 专用服务器,没有本地玩家 */
NM_ListenServer, /** 监听服务器,拥有一个本地玩家. */
NM_Client, /** 客户端,连接到一个服务器;枚举值NetMode < NM_Client即为某种服务器 */
(8)Replicated Data
Actor中的Replicated数据自动复制到所有客户端上
客户端数据不能复制到服务器,只会在客户端本地生效
so
如果数据定义为replicated,最好仅在server上进心更新,在client上只读,避免引起不必要的混淆。
这样判断端的属性:
在C++中判断是否为服务器:Role == ROLE_Authority(必要不充分);NetMode < NM_Client(充分必要);
在BP中判断是否为服务器:HasAuthority(必要不充分);IsServer(充分必要);
(8.1)Replicated Data in C++
#include "Net/UnrealNetwork.h"
UPROPERTY(BlueprintReadOnly,Replicated)
float Health;
------
voidATestNetworkCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty> & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ATestNetworkCharacter, Health);
}
如果不包含头文件UnrealNetwork.h会报错:
error C3861: 'DOREPLIFETIME': identifier not found
(9)RPC
事件有3种replication方式
Multicast---在服务器调用,然后自动转到客户端;
Server---被客户端调用,仅在服务器执行;必须在有Net Owner的Actor上使用;
Client---被服务器调用,仅在其所有者客户端执行;必须在有Net Owner的Actor上使用;
(10)Net Owner
Actor如果是Player controller或被Player Controller所拥有,则此actor有Net Owner;
也就是说除了多播,RPC函数如果想调用成功必须有以下限制条件:
*Actor是一个Player Controller类型;
*Actor被一个Player Controller所拥有(Pawn、Character、PlayerState);
(10.1)您必须满足一些要求才能充分发挥 RPC 的作用:https://docs.unrealengine.com/latest/CHN/Gameplay/Networking/Actors/RPCs/index.html
*它们必须从 Actor 上调用。
*Actor 必须被复制。
*如果 RPC 是从服务器调用并在客户端上执行,则只有实际拥有这个 Actor 的客户端才会执行函数。
*如果 RPC 是从客户端调用并在服务器上执行,客户端就必须拥有(Net Owner)调用 RPC 的 Actor。