8.1 Replication 同步系统


文档摘要

8.1 Replication 同步系统 本节摘要:Replication 的核心是把状态划归权威方,再按带宽预算决定广播什么。本节讲清三种角色身份、属性同步与三类 RPC 的用法边界,以及相关性剔除与优先级怎么在带宽里腾挪,最后用一扇多人共见的门做完整示范。 每个状态都需要一个裁判 两个人同时拉开一扇门,听谁的?网络系统必须先回答这个问题才能工作。引擎的答案是服务器权威:专用服务器(或主机玩家)上的状态是唯一真相,客户端的一切修改都只是"请求"加"本地预演"。三类角色由此划分——权威端持有真相,自治代理是"这具身体的主人"那台客户端(自己角色的预测表现),模拟代理是其余客户端上的远端副本(只播放收到的状态)。诊断多人问题的第一问永远是:这个状态在权威端算过了吗?

8.1 Replication 同步系统

本节摘要:Replication 的核心是把状态划归权威方,再按带宽预算决定广播什么。本节讲清三种角色身份、属性同步与三类 RPC 的用法边界,以及相关性剔除与优先级怎么在带宽里腾挪,最后用一扇多人共见的门做完整示范。

每个状态都需要一个裁判

两个人同时拉开一扇门,听谁的?网络系统必须先回答这个问题才能工作。引擎的答案是服务器权威:专用服务器(或主机玩家)上的状态是唯一真相,客户端的一切修改都只是"请求"加"本地预演"。三类角色由此划分——权威端持有真相,自治代理是"这具身体的主人"那台客户端(自己角色的预测表现),模拟代理是其余客户端上的远端副本(只播放收到的状态)。诊断多人问题的第一问永远是:这个状态在权威端算过了吗?客户端直接改的任何东西,都只是自己屏幕上的自欺。

本节在知识体系中的位置:它是网络章的语法节,为 8.2 的会话层提供同步原语,把第七章的技能预测、破坏结算接上"谁说了算"的插座。账本视角:带宽是按秒计的刚性预算,属性同步与 RPC 都在花它,相关性剔除就是它的省钱算法。

属性同步:改了才发的广播

属性同步的语义是"权威端改了就广播给相关客户端"。用法三件套:属性标记 Replicated;类里重写属性同步清单函数把属性登记进去;需要"改了之后做点事"的属性标记 RepNotify,引擎在客户端收到新值时回调通知函数——门的开合动画就挂在通知里,而不是挂在每帧轮询里。三条纪律:只同步玩家需要知道的状态(服务器的 AI 内部决策值同步出去纯属烧带宽);高频变化的数值(连续帧更新的血量过渡值)先在本地插值表现,低频同步真实值;结构体同步要整体标记,内部改动才能被检测。

RPC 是"调用一次、跨机执行"的通道,三个方向各有语义:服务器 RPC 由客户端呼叫、只在服务器执行("我要开门"的请求通道);客户端 RPC 由服务器呼叫、指定某台客户端执行("把开 door 动画播给你看");多播 RPC 由服务器呼叫、所有相关客户端执行(爆炸那种人人都该看到的瞬间表现)。RPC 的纪律只有一条:请求与通知走 RPC,状态本身走属性同步——用 RPC 同步状态是新手最贵的错误,掉线重连、迟到加入全都对不上账。

// 多人共见的门(节选) UCLASS() class MYPROJECT_API ADoorActor : public AActor { GENERATED_BODY() public: ADoorActor(); // 状态本身:权威端修改后自动广播,RepNotify 在客户端触发表现 UPROPERTY(ReplicatedUsing = OnRep_IsOpen) bool bIsOpen = false; protected: virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const override { Super::GetLifetimeReplicatedProps(Out); DOREPLIFETIME(ADoorActor, bIsOpen); } UFUNCTION() void OnRep_IsOpen() // 客户端:收到新状态,播放开合动画 { PlayDoorAnim(bIsOpen); } public: UFUNCTION(BlueprintCallable, Category = "Door") void RequestToggle() // 任意客户端:只发请求,不自己改状态 { if (!HasAuthority()) ServerToggle(); else ToggleInternal(); } private: UFUNCTION(Server, Reliable) // 服务器 RPC:权威端真正改状态 void ServerToggle(); void ToggleInternal() { bIsOpen = !bIsOpen; PlayDoorAnim(bIsOpen); } };

相关性与带宽:发给谁、先发谁

带宽预算的分配分两步。第一步相关性剔除:玩家附近的Actor才值得同步,Actor 的网络相关距离决定它对某个客户端是否"存在"——远处的门对你是空气,它的状态变化一个字节都不发。第二步优先级排序:相关但带宽不够时,按Actor的优先级(重要性加权,含最近被玩家看到的加成)排队发送。这套机制省下的每一字节都是设计出来的:把装饰性Actor的相关距离调小、把玩法关键Actor设为高优先级,就是最直接的网络调优。

一次开门请求的跨机旅程

看懂这张图就抓到了要点:两台客户端收到的都是属性同步的广播结果,谁都不是"直接改了门"——请求经权威校验,状态由权威分发,表现由各自的本地通知驱动。

案例:把单机门改成多人共见的门

背景:第一章与第七章反复出现的那扇门,在双人联机测试里暴露两个故障:玩家二永远看到门关着;门被打开后重进关卡的玩家一看到门又关上了。目标:按上面三件套改造并验证。

操作:第一步,给门类加属性同步三件套(上面代码),开合状态成为唯一同步的状态。第二步,把原来自动的感应触发逻辑改造为"任何客户端的重叠都发服务器请求",触发判定保留在本地但执行收敛到权威端。第三步,动画表现挂到 RepNotify 通知上,删掉所有"客户端自己改状态"的代码路径。第四步,把门的相关距离按关卡尺度设置(走廊场景几百单位足够),确认远处门不占带宽。第五步,联机验证三项:两台机器同时看到开合、迟加入的玩家拿到当前状态(属性同步天然覆盖)、服务器掉线重开后状态一致。

结果:三处故障全部消失。解读:故障一源于旧代码把状态改在了本地——多人下等于每个客户端各玩各的;故障二源于状态没有登记进同步清单——迟加入者无从得知历史。两者共同的病根是"状态没有权威归属",这与第七章 GAS"一切数值走效果管线"是同一纪律的网络版:状态变更收敛到权威端,其余都只是请求与广播。变式一:多人推箱子——位置状态走属性同步加插值,推这个动作走服务器 RPC,注意高频位置更新用引擎内建的运动组件同步而非自写。变式二:机关陷阱——触发判定放服务器防作弊,但把触发后的爆炸表现走多播 RPC 让全员看到同一瞬间。

常见坑:用多播 RPC 广播会频繁变化的状态。多播是瞬时通知不是状态通道,迟加入者收不到历史广播,状态永远对不齐——状态归属性同步,RPC 只管"这一瞬间发生了事"。

本节要点回顾

  • 权威、自治、模拟三种身份,诊断第一问永远是"权威端算过了吗"。
  • 属性同步管状态、RPC 管请求与瞬间通知,两者串用是最大的网络事故源。
  • RepNotify 是表现挂钩点,客户端收到状态变化再播动画,不轮询。
  • 相关性剔除与优先级是带宽预算的两级分配器,按玩法重要性配置。
  • 迟加入与重连的一致性天然由属性同步保障,这是"状态走同步"的最好理由。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U