client-server架构
-
一个服务器,一个或多个服务端 (对比:P2P一对一全链接,去中心化网络)
-
客户端不可信,所有重要信息经过服务器验证
-
两种服务器类型
- Listen Server: 服务器上可以有游戏客户端
- Dedicated Server:专用服务器,没有游戏客户端运行
-
网络信息传递的两种方式
- Replication (服务器–>客户端)
- RPC (双向)
网络复制Replication
-
Actor及其派生的子类才能复制
-
只能从服务端到客户端单向(客户端到服务端使用RPC)
-
判断服务器和客户端的方法
- 蓝图节点:has authority / switch has authority (Authority服务端,Remote客户端)
- C++:HasAuthority()
-
三种类型
- Actor Repliaction:整个对象的复制
- Property Replication:Actor中属性变量的复制,有两种(Replicated / Rep_Notify)
- Component Replication:组件的复制 (如CharacterMovement具有完备的网络复制功能)
-
Actor Repliaction
- 如果一个Actor对象开启repliaction,则由服务器生成,所有客户端生成镜像对象
- 设置方法:
- 蓝图:Class Defaults - 勾选replicates
- C++:bReplicates = true;
- 判断并只在服务端生成Actor
- has authority + spawn actor
-
Property Replication
-
两种类型:Replicated / Rep_Notify
-
Replicated
-
蓝图:Actor的变量Details 的Relication设置为Replicated
-
C++:
- UPROPERTY(Replicated);
- GetLifetimeReplicatedProps函数(overide, 不用声明)中添加DOREPLIFETIME(类名,变量名)
-
Rep_Notify
- 作用:当一个变量设置成Rep_Notify,当该变量发生replicatiion时
- 蓝图:服务端和客户端都可调用一个自定义函数
- C++:只在客户端调用自定义函数
- 设置方法:
- 蓝图:Details的Replication下拉设置成Rep_Notify后,自动生成一个onRep_变量名的空函数,添加实现即可
- C++:
- UPROPERTY(ReplicatedUsing=函数名) // 函数名一般取名为OnRep_变量名,UFUNCTION()修饰
- GetLifetimeReplicatedProps函数(overide, 不用声明)中添加DOREPLIFETIME(类名,变量名)
- 作用:当一个变量设置成Rep_Notify,当该变量发生replicatiion时
-
-
创建一个复制Actor的CheckList
-
设置Actor Replication
-
若复制Actor需要移动,将复制移动(Replicates Movement)设为True
-
生成或销毁复制Actor时,确保在服务器上执行该操作
-
设置Property Replication,这通常适用于以gameplay为基础的变量。指定类型为RepNotify 或 Replicated,RepNotify需要设置对应的Notify函数
-
远程过程调用RPC
-
三种类型:
- Server:仅在服务器调用
- Client:仅在客户端调用
- NetMulticast:服务器+客户端均调用
-
设置方法:
-
蓝图:将事件或函数的细节面板-replicates下拉菜单设置为三种类型的一种后启用,同时将 可靠(Reliable) 设为 true/false,蓝图里默认是不可靠
-
C++
-
提供对应 UFUNCTION 宏中的 Server、Client 或 NetMulticast 说明符,可在将C++函数指定为RPC
-
WithValidation说明符表示该RPC除了有实现函数(_Implementation后缀的函数)还有验证函数(_Validation后缀的函数),只有验证返回true才会执行实现函数
-
//服务器RPC MyFunction的声明。 UFUNCTION(Server, Reliable, WithValidation) void MyFunction(int myInt); //服务器RPC MyFunction的实现。 void AExampleClass::MyFunction_Implementation(int myInt) { //游戏代码在此。 } //服务器RPC MyFunction的验证 bool AExampleClass::MyFunction_Validation(int myInt) { /* 若myInt的值为负,建议不允许运行MyFunction_Implementation。 因此仅在myInt大于零时返回true。 */ return myInt >= 0; }
-
-
-
可靠和不可靠RPC
- 不可靠RPC无法保证必会到达预定目的地,但其发送速度和频率高于可靠的RPC。其最适用于对gameplay而言不重要或经常调用的函数。例如,由于Actor移动每帧都可能变换,因此使用不可靠RPC复制该Actor移动
- 可靠的RPC保证到达预定目的地,并在成功接收之前一直保留在队列中。其最适合用于对gameplay很关键或者不经常调用的函数。相关例子包括碰撞事件、武器发射的开始或结束,或生成Actor
- 滥用可靠函数可能导致其队列溢出,此操作将强制断开连接。若逐帧调用复制函数,应将其设为不可靠。若拥有与玩家输入绑定的可靠函数,应限制玩家调用该函数的频率
-
RPC调用与执行的位置
Client和Server的区分
-
AActor 中都有个 ENetRole Role 变量是用来识别角色的 Actor 的身份的。ENetRole 的几个值:
- ROLE_None:默认值
- ROLE_SimulatedProxy:这个actor是其他客户端在本机客户端的一个模拟代理
- ROLE_AutonomousProxy:这个actor是本机客户端的自己控制的角色
- ROLE_Authority:这个actor是服务器上的actor
-
举例:一个服务器上有一个玩家ServerA和一个NPC ServerB,客户端上拥有从服务器复制过来的这个玩家ClientA与NPC ClientB。由于ServerA与ServerB都是在服务器上生成的,所以他们两在服务器上的所有权Role都是ROLE_Authority。ClientA在客户端上由于被玩家控制,他的Role是ROLE_AutonomousProxy。ClientB在客户端是完全通过服务器同步来控制的,他的Role就是ROLE_SimulatedProxy。
Actor的相关性和优先级
-
相关性概念
- 场景的规模可能非常大,在特定时刻某个玩家只能看到关卡中的一小部分 Actor。场景中的其他大多数 Actor 都不会被看到和听到, 对玩家也不会产生显著的影响。被服务器认为可见或能够影响客户端的 Actor 组会被视为该客户端的相关 Actor 组。
- 虚幻引擎的网络代码中包含一处重要的带宽优化:服务器只会让客户端知道其相关组内的 Actor
- 规则实施函数: AActor::IsNetRelevantFor()
-
优先级概念
-
虚幻引擎采用了负载平衡技术来安排所有 Actor 的优先级,并根据它们对游戏的重要性为其分别提供一个公平的带宽份额
-
每个 Actor 都有一个名为 NetPriority 的浮点变量。这个变量的数值越大,Actor 相对于其他"同伴"的带宽就越多。和优先级为 1.0 的 Actor 相比,优先级是 2.0 的 Actor 可以得到两倍的更新频度。
-
优先级计算函数:AActor::GetNetPriority()
-
UE4建立网络链接的流程
-
主要步骤
- 客户端发送连接请求
- 服务器将在本地调用 AGameMode::PreLogin。这样可以使 GameMode 有机会拒绝连接
- 如果服务器接受连接,则发送当前地图
- 服务器等待客户端加载此地图,客户端如果加载成功,会发送Join信息到服务器
- 如果接受连接,服务器将调用 AGameMode::Login该函数的作用是创建一个PlayerController,可用于在今后复制到新连接的客户端。成功接收后,这个PlayerController 将替代客户端的临时PlayerController (之前被用作连接过程中的占位符)。此时将调用 APlayerController::BeginPlay。应当注意的是,在此 actor 上调用RPC 函数尚存在安全风险。您应当等待 AGameMode::PostLogin 被调用完成
- 如果一切顺利,AGameMode::PostLogin 将被调用。这时,可以放心的让服务器在此 PlayerController 上开始调用RPC 函数
-
链接信息存在哪?
存储在PlayerController的里面,而这个PlayerController不能是随随便便创建的PlayerController,一定是客户端第一次链接到服务器,服务器同步过来的这个PlayerController(拥有连接的PlayerController)。进一步来说,这个Controller里面包含着相关的NetDriver,Connection以及Session信息。
官方建议
- 尽可能少用RPC,在合适情况下改用RepNotify
- 若游戏频繁调用RP图哦如C或复制函数,如tick时,则应将其设为不可靠
- 检查Actor是否为网络角色: ROLE_Authority
- 检查Pawn是否受本地控制:IsLocallyControlled(构造期间Pawn可能未被指定控制器,因此避免在构造函数中使用)