关于 ue-networking-replication
面向 Unreal Engine 5.8 开发者的网络复制技能,讲解服务器权威模型、属性与 RPC 复制,以及 Fast Array、Push Model 和可选复制系统。
主要功能
- 01网络角色与权威判断
- 02属性复制与生命周期条件
- 03Push Model 脏标记
- 04Fast Array 增量序列化
- 05Server、Client 与 Multicast RPC
- 06Iris 与 Replication Graph
适用场景
- 01同步 Actor 属性和移动状态
- 02按连接控制属性接收范围
- 03复制会变化的库存数组
- 04选择 RPC 类型并配置服务器调用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ quodsoler/ unreal-engine-skills --skill ue-networking-replication安装后在 Claude Code 中输入/ue-networking-replication确认已加载。
附带文件references/replication-patterns.mdreferences/rpc-decision-guide.md
详细介绍
UE 网络与复制
目标引擎:UE 5.8。以下 API 已根据 5.8 头文件验证;较旧的形式列在“已弃用 — 请勿使用”下。
为 Actor、组件和子对象提供服务器权威复制。核心 API 位于 Engine 模块(GameFramework/Actor.h、Engine/NetDriver.h、Net/UnrealNetwork.h)和 NetCore 模块(Net/Core/PushModel/PushModel.h、Net/Serialization/FastArraySerializer.h)。ELifetimeCondition 和 REPNOTIFY_* 来自 CoreUObject(UObject/CoreNetTypes.h)。可选系统:Iris 复制系统(IrisCore 模块,在 Build.cs 中使用 SetupIrisSupport(Target))以及 ReplicationGraph 插件(在 5.8 中处于 Beta 阶段,Build.cs 模块为 ReplicationGraph)。
上下文
如果 .agents/ue-project-context.md 存在,请阅读它(模块名称、约定、启用的插件、GAS/网络设置)。如果文件不存在,请继续操作。
根据请求和代码库确定所属领域。仅当两种合理解读会产生不同代码时才提问。
| 请求涉及…… | 转到 |
|---|---|
| 权限检查、角色、专用服务器与监听服务器 | 网络角色、网络模式与权限 |
复制变量、OnRep_、GetLifetimeReplicatedProps | 属性复制 |
谁会接收某个属性(COND_*) | 生命周期条件 |
| 降低逐帧比较开销 | 推送模型 |
| 复制数组 / 背包 | 快速数组序列化 |
| 服务器 / 客户端 / 多播调用、验证 | 远程过程调用 |
SetOwner、相关性、剔除、休眠 | 所有权、相关性与休眠 |
复制由 Actor 或组件拥有的 UObject | 子对象复制 |
| 模拟刚体、物理预测 | 物理复制 |
Iris、net.Iris.UseIrisReplication | Iris 复制系统 |
| 50 个以上连接、空间相关性 | Replication Graph |
UNetDriver、UNetConnection、断开连接检测 | 网络驱动程序与连接 |
端到端示例:复制模式。RPC 选择表和引擎 RPC 声明原文:RPC 决策指南。
网络角色、网络模式与权限
ENetRole 定义于 Engine/Classes/Engine/EngineTypes.h;访问器位于 AActor 上。
ROLE_None // not replicated
ROLE_SimulatedProxy // remote copy driven by replicated state
ROLE_AutonomousProxy // remote copy of the locally controlled pawn/controller
ROLE_Authority // owns and may mutate this actor
来自 GameFramework/Actor.h:
ENetRole GetLocalRole() const;
ENetRole GetRemoteRole() const;
bool HasAuthority() const;
ENetMode GetNetMode() const;
bool IsNetMode(ENetMode Mode) const;
ENetMode(Engine/Classes/Engine/EngineBaseTypes.h):NM_Standalone、NM_DedicatedServer、NM_ListenServer、NM_Client。所有低于 NM_Client 的模式都是服务器。
| 机器 | GetLocalRole() | GetRemoteRole() |
|---|---|---|
| 服务器 | ROLE_Authority | ROLE_AutonomousProxy 或 ROLE_SimulatedProxy |
| 所有者客户端 | ROLE_AutonomousProxy | ROLE_Authority |
| 其他客户端 | ROLE_SimulatedProxy | ROLE_Authority |
在监听服务器上,主机既是 ROLE_Authority,也受本地控制:使用 APawn::IsLocallyControlled() 跳过仅限主机或仅限客户端的执行路径。
属性复制
// MyReplicatedActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyReplicatedActor.generated.h"
UCLASS()
class MYGAME_API AMyReplicatedActor : public AActor
{
GENERATED_BODY()
public:
AMyReplicatedActor();
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
protected:
UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health = 100.f;
UPROPERTY(Replicated)
int32 TeamId = 0;
UFUNCTION()
void OnRep_Health(float PreviousHealth);
};
// MyReplicatedActor.cpp
#include "MyReplicatedActor.h"
#include "Net/UnrealNetwork.h"
AMyReplicatedActor::AMyReplicatedActor()
{
bReplicates = true; // or SetReplicates(true) at runtime
SetReplicateMovement(true);
SetNetUpdateFrequency(10.f); // replication considerations per second
SetMinNetUpdateFrequency(2.f); // throttled floor when nothing changes
SetNetCullDistanceSquared(225000000.f); // 150 m squared
NetPriority = 1.f; // still a public field in 5.8
}
void AMyReplicatedActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps); // never omit
DOREPLIFETIME(AMyReplicatedActor, Health);
DOREPLIFETIME_CONDITION(AMyReplicatedActor, TeamId, COND_InitialOnly);
}
void AMyReplicatedActor::OnRep_Health(float PreviousHealth)
{
// Clients only. Must be idempotent: relevancy changes re-fire it.
}
规则:
OnRep_必须是UFUNCTION()。它的可选单个参数是先前的值,且类型必须与属性匹配。- 只有权威端可以写入复制属性。进行更改的机器不会触发
OnRep_。 - 复制结构体中的
UPROPERTY(NotReplicated)会将该成员排除在外。 FVector_NetQuantize/_NetQuantize10/_NetQuantize100/_NetQuantizeNormal(Engine/NetSerialization.h)可减少世界位置所占的带宽:分别保留 0、1 和 2 位小数;法线每个分量占 16 位。FRepMovement(Engine/Classes/Engine/ReplicatedState.h)会在调用SetReplicateMovement(true)时传递位置/旋转/速度;可重写virtual void OnRep_ReplicatedMovement()进行后处理。UCharacterMovementComponent会自行复制移动信息,详见ue-character-movement。- 对某个连接发送第一个 bunch 时,会一次性发送所有符合条件的属性。请保持初始状态较小,并将仅在生成时使用的数据放在
COND_InitialOnly后面。
宏(Net/UnrealNetwork.h):DOREPLIFETIME、DOREPLIFETIME_CONDITION、DOREPLIFETIME_CONDITION_NOTIFY、DOREPLIFETIME_WITH_PARAMS、DOREPLIFETIME_WITH_PARAMS_FAST、DOREPLIFETIME_WITH_PARAMS_FAST_STATIC_ARRAY、DOREPLIFETIME_ACTIVE_OVERRIDE、DOREPLIFETIME_ACTIVE_OVERRIDE_FAST、DISABLE_REPLICATED_PROPERTY。
FDoRepLifetimeParams(Net/UnrealNetwork.h)是 _WITH_PARAMS 系列形式使用的结构体:
ELifetimeCondition Condition = COND_None;
ELifetimeRepNotifyCondition RepNotifyCondition = REPNOTIFY_OnChanged; // or REPNOTIFY_Always
bool bIsPushBased = false;
生命周期条件
完整的 ELifetimeCondition 枚举位于 CoreUObject/Public/UObject/CoreNetTypes.h:
| 值 | 发送至 |
|---|---|
COND_None | 每个连接 |
COND_InitialOnly | 仅初始 bunch |
COND_OwnerOnly | Actor 的所属连接 |
COND_SkipOwner | 除所属连接外的所有连接 |
COND_SimulatedOnly | Actor 是模拟代理的连接 |
COND_AutonomousOnly | Actor 是自主代理的连接 |
COND_SimulatedOrPhysics | 模拟代理或 bRepPhysics Actor |
COND_InitialOrOwner | 初始 bunch,或所属连接 |
COND_Custom | 始终符合条件;通过 active-override 宏在运行时切换 |
COND_ReplayOrOwner | 回放连接或所属连接 |
COND_ReplayOnly | 仅回放连接 |
COND_SimulatedOnlyNoReplay | 模拟代理,不包括回放 |
COND_SimulatedOrPhysicsNoReplay | 模拟代理或 bRepPhysics,不包括回放 |
COND_SkipReplay | 除回放连接外的所有连接 |
COND_Dynamic | 条件在运行时被覆盖;设置前会持续复制 |
COND_Never | 从不复制 |
COND_NetGroup | 仅限子对象——处于同一网络条件组中的连接 |
COND_NetGroup 与 UE::Net::FNetConditionGroupManager 配合使用(Net/Core/Misc/NetConditionGroupManager.h,模块 NetCore),通过 UNetworkSubsystem::GetNetConditionGroupManager()(Net/Subsystems/NetworkSubsystem.h)访问:
#include "Net/Core/Misc/NetConditionGroupManager.h"
#include "Net/Subsystems/NetworkSubsystem.h"
AddReplicatedSubObject(MySubObject, COND_NetGroup); // on the owning actor/component
UNetworkSubsystem* NetSubsystem = GetWorld()->GetSubsystem<UNetworkSubsystem>();
NetSubsystem->GetNetConditionGroupManager().RegisterSubObjectInGroup(MySubObject, FName("RedTeam"));
MyPlayerController->IncludeInNetConditionGroup(FName("RedTeam"));
UE::Net::NetGroupOwner 和 UE::Net::NetGroupReplay 是引擎管理的组;不要将玩家控制器添加到这些组中。APlayerController 还提供 RemoveFromNetConditionGroup、IsMemberOfNetConditionGroup 和 GetNetConditionGroups。
推送模型
推送模型可移除复制属性逐帧进行的 memcmp:只有在将属性标记为脏后,才会比较该属性。它适用于经典网络驱动程序和 Iris——这并非 Iris 的专属功能。
// MyPushActor.cpp
#include "MyPushActor.h"
#include "Net/UnrealNetwork.h"
#include "Net/Core/PushModel/PushModel.h"
void AMyPushActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
FDoRepLifetimeParams Params;
Params.bIsPushBased = true;
Params.Condition = COND_OwnerOnly;
DOREPLIFETIME_WITH_PARAMS_FAST(AMyPushActor, Ammo, Params);
}
void AMyPushActor::ConsumeAmmo(int32 Count)
{
if (!HasAuthority())
{
return;
}
Ammo -= Count;
MARK_PROPERTY_DIRTY_FROM_NAME(AMyPushActor, Ammo, this);
}
- 首选使用
MARK_PROPERTY_DIRTY_FROM_NAME(ClassName, PropertyName, Object)标记。静态数组使用MARK_PROPERTY_DIRTY_FROM_NAME_STATIC_ARRAY_INDEX(ClassName, PropertyName, ArrayIndex, Object)或MARK_PROPERTY_DIRTY_FROM_NAME_STATIC_ARRAY(ClassName, PropertyName, Object)。 - 需要进行两处启用:在游戏/服务器的
*.Target.cs中设置bWithPushModel = true;(默认仅对 Editor 目标启用,TargetRules.cs:1524),并设置Net.IsPushModelEnabled=1(默认关闭,PushModel.cpp:434)。两者都启用前,属性仍会按常规方式进行比较。 - 当
WITH_PUSH_MODEL关闭时,这些宏会编译为空,因此代码始终可以安全发布(例外:不启用推送时,MARK_PROPERTY_DIRTY_FROM_NAME_STATIC_ARRAY的存根需要 4 个参数,见PushModel.h:489,因此 3 参数形式会编译失败)。 - 通过引用修改的结构体属性,需要提供一个访问器,在返回引用之前将其标记为脏。
- 启用推送模型后,漏掉任意一个标记都会导致该属性悄然停止复制——这是选择启用推送模型的主要代价。
快速数组序列化
FFastArraySerializer 只发送发生变化的项,而非整个数组。
#include "Net/Serialization/FastArraySerializer.h" // module NetCore
项结构体继承自 FFastArraySerializerItem,并可实现以下任意方法:
void PreReplicatedRemove(const struct FMyItemArray& InArraySerializer);
void PostReplicatedAdd(const struct FMyItemArray& InArraySerializer);
void PostReplicatedChange(const struct FMyItemArray& InArraySerializer);
数组结构体继承自 FFastArraySerializer,通过 FastArrayDeltaSerialize<ItemType, ArrayType>(Items, DeltaParms, *this) 实现 NetDeltaSerialize,并需要 TStructOpsTypeTraits<...> { WithNetDeltaSerializer = true }。它还可以实现数组级别的接收钩子:
void PostReplicatedReceive(const FFastArraySerializer::FPostReplicatedReceiveParameters& Parameters);
FPostReplicatedReceiveParameters::OldArraySize 是更新前的大小,因此当“整个列表发生变化,需要重建 UI”时,这里正是合适的处理位置。
脏标记规则——两条规则都必须遵守,而且这是“我的数组无法复制”的常见原因:
- 添加或修改项后调用
MarkItemDirty(Item)。 - 移除项后调用
MarkArrayDirty()(它也会使 ID 映射失效)。
完整清单实现:复制模式,模式 3。
远程过程调用
UFUNCTION 网络说明符(UObject/ObjectMacros.h):Server、Client、NetMulticast、Reliable、Unreliable、WithValidation、BlueprintAuthorityOnly、BlueprintCosmetic。Reliable/Unreliable 只能与 Server、Client 或 NetMulticast 搭配使用。
// Client calls, server executes. WithValidation generates the _Validate pair.
UFUNCTION(Server, Reliable, WithValidation)
void ServerFireWeapon(FVector_NetQuantize Origin, FVector_NetQuantizeNormal Direction);
// Server calls, the owning client executes.
UFUNCTION(Client, Reliable)
void ClientShowKillConfirm(int32 VictimId);
// Server calls, server and every client with this actor relevant execute.
UFUNCTION(NetMulticast, Unreliable)
void MulticastPlayHitEffect(FVector_NetQuantize ImpactPoint);
你需要编写 _Implementation 函数体;使用 WithValidation 时,还要编写 _Validate,返回 false 会断开调用方的连接。
| 规则 | 详情 |
|---|---|
| 所有权 | 只有当调用客户端通过 SetOwner 链拥有该 Actor,且所有权链最终指向其 APlayerController 时,Server RPC 才会路由。无所有者的 Actor 会丢弃调用;唯一的跟踪记录是 LogNet Warning "No owning connection for actor…"(NetDriver.cpp:8274)。 |
| 客户端 RPC 目标 | 需要一个拥有连接的 Actor;APlayerController 是可靠的选择。 |
| 可靠性 | 状态变更使用 Reliable,高频外观效果使用 Unreliable。不要每帧发送 Reliable RPC:一旦某个通道上未确认的可靠 bunch 数量超过 RELIABLE_BUFFER(512,NetConnection.h:82),连接就会因 ReliableBufferOverflow 而关闭(DataChannel.cpp:1445)。 |
| 后加入者 | 多播不会重放。持久状态应放在复制属性中。 |
| 参数 | 仅限网络可寻址类型。既未复制也未稳定命名(资源、类、放置在地图中的 Actor)的 UObject* 到达时会是 nullptr;请传递一个 id,并在远程解析。 |
| 方向 | 在客户端上调用的 Client/NetMulticast 永远不会发送;它们只会在该客户端本地运行(Actor.cpp:5569-5573、5587-5592)。在调用处使用 HasAuthority() 进行防护。 |
决策表、可靠性矩阵和 PlayerController.h 声明原文:RPC 决策指南。
所有权、相关性与休眠
Weapon->SetOwner(OwningPlayerController); // server only; drives RPC routing and COND_OwnerOnly
AActor 上的相关性字段和钩子:
bAlwaysRelevant = true; // ignore distance culling
bOnlyRelevantToOwner = true; // owner connection only
virtual bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const override;
virtual float GetNetPriority(const FVector& ViewPos, const FVector& ViewDir, AActor* Viewer, AActor* ViewTarget, UActorChannel* InChannel, float Time, bool bLowBandwidth) override;
SetNetCullDistanceSquared() 设置半径的平方值;带宽饱和时,NetPriority 用于打破平局;录制回放时,GetReplayPriority() 也用于此目的。
休眠(EngineTypes.h 中的 ENetDormancy:DORM_Never、DORM_Awake、DORM_DormantAll、DORM_DormantPartial、DORM_Initial):
NetDormancy = DORM_Initial; // constructor: map-placed actors start dormant
SetNetDormancy(DORM_DormantAll); // runtime
FlushNetDormancy(); // push one update, then return to dormant
休眠 Actor 唤醒时,会与连接在其进入休眠时保留的复制器进行差异比较(DataChannel.cpp:2510-2519),因此通常只会发送变更;如果某个 Actor 因相关性而关闭了通道,那么它重新进入范围时会收到完整的初始 bunch,并再次触发其 OnRep_。编写能够容忍重复调用的 OnRep_ 处理程序。
子对象复制
在 5.8 中,bReplicateUsingRegisteredSubObjectList 默认值为 false(Engine/Private/Components/ActorComponent.cpp 中的 GDefaultUseSubObjectReplicationList = false,项目级 CVar net.SubObjects.DefaultUseSubObjectReplicationList)。按类启用;否则,任何已注册对象都不会复制:
AMyQuestActor::AMyQuestActor()
{
bReplicates = true;
bReplicateUsingRegisteredSubObjectList = true; // protected field; set in the constructor
}
AActor 注册 API(GameFramework/Actor.h);UActorComponent(Components/ActorComponent.h)具有除 ActorComponent 变体外的所有对应 API:
void AddReplicatedSubObject(UObject* SubObject, ELifetimeCondition NetCondition = COND_None);
void RemoveReplicatedSubObject(UObject* SubObject);
void AddActorComponentReplicatedSubObject(UActorComponent* OwnerComponent, UObject* SubObject, ELifetimeCondition NetCondition = COND_None);
void RemoveActorComponentReplicatedSubObject(UActorComponent* OwnerComponent, UObject* SubObject);
void DestroyReplicatedSubObjectOnRemotePeers(UObject* SubObject);
void TearOffReplicatedSubObjectOnRemotePeers(UObject* SubObject);
bool IsUsingRegisteredSubObjectList() const;
RemoveReplicatedSubObject 只会停止后续更新——客户端仍会保留其副本。使用 DestroyReplicatedSubObjectOnRemotePeers 可在远端删除该对象,或使用 TearOffReplicatedSubObjectOnRemotePeers 留下孤立的本地副本。子对象必须重写 virtual bool IsSupportedForNetworking() const 并返回 true,且声明自己的 GetLifetimeReplicatedProps。
标志为 false 时,引擎会改为调用仍有效的虚函数:
virtual bool ReplicateSubobjects(class UActorChannel* Channel, class FOutBunch* Bunch, FReplicationFlags* RepFlags) override;
在其中转发到 Super:: 并调用 Channel->ReplicateSubobject(SubObject, *Bunch, *RepFlags)。已注册子对象:复制模式,模式 5。
物理复制
EPhysicsReplicationMode(EngineTypes.h)用于选择如何修正模拟刚体:Default、PredictiveInterpolation、Resimulation、None。
SetPhysicsReplicationMode(EPhysicsReplicationMode::PredictiveInterpolation);
EPhysicsReplicationMode Mode = GetPhysicsReplicationMode();
PredictiveInterpolation 和 Resimulation 需要在项目设置 → Physics → Physics Prediction 中启用。所有相互作用的刚体都应使用相同模式。对于回滚式网络物理,请使用 UNetworkPhysicsComponent(Engine/Public/Physics/NetworkPhysicsComponent.h)以及 APlayerController 上的 FAsyncPhysicsTimestamp 握手(ClientSetupNetworkPhysicsTimestamp、ServerSendLatestAsyncPhysicsTimestamp);碰撞和刚体设置请参阅 ue-physics-collision。
Iris 复制系统
Iris 是另一种复制后端(插件 Iris,在 5.8 中标记为 Beta;Epic 的 5.8 说明称其已可供被许可方用于生产环境)。运行时模块 IrisCore 位于 Source/Runtime/Net/Iris,引擎集成代码位于 Engine/Public/Net/Iris。
- 在模块的 Build.cs 中启用:
SetupIrisSupport(Target); - 启用
Iris插件(EnabledByDefault: false),并设置运行时开关net.Iris.UseIrisReplication=1;默认值为 0,即经典复制系统(IrisConfig.cpp:14)。 - 属性声明、RPC、条件和推送模型均保持不变。Iris 更改的是后端,而不是面向游戏玩法的 API。
Iris 处于启用状态时的 Actor 钩子(GameFramework/Actor.h):
virtual void FillReplicationParams(const FFillReplicationParamsContext& Context, FActorReplicationParams& OutParams) override; // public
virtual void OnReplicationStartedForIris(const FOnReplicationStartedParams&) override; // protected
virtual void OnStopReplicationForIris(const FOnStopReplicationParams&) override; // protected
UReplicationBridge 和 UObjectReplicationBridge(Iris/ReplicationSystem/)是内部实现;游戏玩法代码不应继承它们。UReplicationBridge 不再拥有以前单独的基类;应将其视为内部实现。
复制图
对于大量连接的情况,可用 ReplicationGraph 插件取代逐连接的相关性扫描(在 5.8 中标记为 Beta)。Build.cs 模块:ReplicationGraph。
; DefaultEngine.ini
[/Script/OnlineSubsystemUtils.IpNetDriver]
ReplicationDriverClassName="/Script/MyGame.MyReplicationGraph"
// MyReplicationGraph.h
#pragma once
#include "CoreMinimal.h"
#include "ReplicationGraph.h"
#include "MyReplicationGraph.generated.h"
UCLASS()
class MYGAME_API UMyReplicationGraph : public UReplicationGraph
{
GENERATED_BODY()
public:
virtual void InitGlobalActorClassSettings() override;
virtual void InitGlobalGraphNodes() override;
virtual void InitConnectionGraphNodes(UNetReplicationGraphConnection* ConnectionManager) override;
};
节点类(ReplicationGraph.h):UReplicationGraphNode_GridSpatialization2D(世界网格)、UReplicationGraphNode_AlwaysRelevant(游戏状态、管理器)、UReplicationGraphNode_AlwaysRelevant_ForConnection(控制器、玩家状态)、UReplicationGraphNode_ActorList(手动构建的列表)、UReplicationGraphNode_DormancyNode、UReplicationGraphNode_DynamicSpatialFrequency。
网络驱动程序和连接
UNetDriver(Engine/Classes/Engine/NetDriver.h)负责管理连接,并驱动世界的复制。
if (UNetDriver* Driver = GetWorld()->GetNetDriver())
{
// Driver->ClientConnections — server side, one per client
// Driver->ServerConnection — client side, the single upstream connection
}
UNetConnection(Engine/Classes/Engine/NetConnection.h)是单个客户端的连接。可通过 AActor::GetNetConnection()(由 APlayerController 重写)获取。分屏玩家会拥有一个挂接在父连接上的 UChildConnection(Engine/ChildConnection.h)。
UE::Net::FConnectionHandle Handle = Connection->GetConnectionHandle();
UNetConnection* Found = Driver->GetConnectionByHandle(Handle);
const bool bGone = Connection->IsClosingOrClosed(); // USOCK_Closing or USOCK_Closed
EConnectionState State = Connection->GetConnectionState();
处理断开连接时,优先使用 AGameModeBase::Logout,而不是轮询连接状态。
APlayerController 只存在于服务器和其所属客户端上。每个客户端都需要的数据应放在 APawn、APlayerState 或 AGameStateBase 上——请参阅 ue-gameplay-framework。
已弃用 — 请勿使用
| 不要发出 | 在 5.8 中使用 | 来源 |
|---|---|---|
NetUpdateFrequency = X; | SetNetUpdateFrequency(X) / GetNetUpdateFrequency() | GameFramework/Actor.h 中的 UE_DEPRECATED(5.5) |
MinNetUpdateFrequency = X; | SetMinNetUpdateFrequency(X) / GetMinNetUpdateFrequency() | GameFramework/Actor.h 中的 UE_DEPRECATED(5.5) |
NetCullDistanceSquared = X; | SetNetCullDistanceSquared(X) / GetNetCullDistanceSquared() | GameFramework/Actor.h 中的 UE_DEPRECATED(5.5) |
virtual void BeginReplication() | OnReplicationStartedForIris 或 FillReplicationParams | GameFramework/Actor.h 中的 UE_DEPRECATED(5.7) |
virtual void EndReplication(EEndPlayReason::Type) | OnStopReplicationForIris | GameFramework/Actor.h 中的 UE_DEPRECATED(5.7) |
BeginReplication(const FActorReplicationParams&) | 重写 FillReplicationParams | GameFramework/Actor.h 中的 UE_DEPRECATED(5.7) |
UNetConnection::GetConnectionId() | GetConnectionHandle() | Engine/NetConnection.h 中的 UE_DEPRECATED(5.6) |
UNetConnection::SetConnectionId() | 无 — 外部代码不得设置此项(SetConnectionHandle() 受保护,NetConnection.h:2006) | Engine/NetConnection.h 中的 UE_DEPRECATED(5.6) |
UNetConnection::GetParentConnectionId() | GetConnectionHandle() | Engine/NetConnection.h 中的 UE_DEPRECATED(5.6) |
UNetDriver::GetConnectionById(uint32) | GetConnectionByHandle(FConnectionHandle) | Engine/NetDriver.h 中的 UE_DEPRECATED(5.6) |
UNetDriver::SetPendingDestruction() | RequestNetDriverDestruction() | Engine/NetDriver.h 中的 UE_DEPRECATED(5.6) |
FPostReplicatedReceiveParameters::bHasMoreUnmappedReferences | 仅读取 OldArraySize | Net/Serialization/FastArraySerializer.h 中的 UE_DEPRECATED(5.4) |
UObjectReplicationBridge::DetachInstanceFromRemote | DetachRootObjectFromRemote / DetachSubObjectFromRemote | Iris/ReplicationSystem/ObjectReplicationBridge.h 中的 UE_DEPRECATED(5.8) |
UNetObjectFactory::FDestroyedContext | FDetachContext | Iris/ReplicationSystem/NetObjectFactory.h 中的 UE_DEPRECATED(5.8) |
常见错误
假设已启用已注册子对象列表: 默认情况下该功能为 false,因此仅调用 AddReplicatedSubObject 不会复制任何内容。在构造函数中设置 bReplicateUsingRegisteredSubObjectList = true(或切换 net.SubObjects.DefaultUseSubObjectReplicationList),并通过 IsUsingRegisteredSubObjectList() 确认。
省略 Super::GetLifetimeReplicatedProps: 所有继承的复制属性都会被丢弃,包括 AActor 和 APawn 上的引擎状态。始终先调用 Super。
在客户端修改复制状态: 下一次服务器更新会覆盖该状态,且不会发生回滚。使用 if (!HasAuthority()) return; 进行保护,并在 OnRep_ 中应用权威值。
推送模型没有标记: 声明了 Params.bIsPushBased = true 却从未标记为脏的属性会停止复制。每条写入路径都需要 MARK_PROPERTY_DIRTY_FROM_NAME。
从快速数组中移除元素却未调用 MarkArrayDirty(): 移除操作不会传到客户端。MarkItemDirty 仅涵盖修改操作。
使用 NetMulticast 表示持久状态: 后加入的客户端永远不会收到它。Multicast 用于一次性视觉效果;状态应放在复制属性中。
在无所有权的 Actor 上调用服务器 RPC: 调用会被丢弃,且只会记录一条 LogNet Warning。在生成后于服务器上调用 SetOwner(),并确保所有权链一直延伸到 APlayerController。
OnRep_ 不是 UFUNCTION: 如果 ReplicatedUsing 指向普通成员函数,UHT 会报错。在其上方声明 UFUNCTION()。
服务器 RPC 未使用 WithValidation 却修改状态: 客户端可以发送任意参数值。在 _Validate 中进行验证;更好的做法是发送一个 id,并在服务器端查找权威数据。
OnRep_ 不具备幂等性: 相关性发生变化时会重新发送完整状态,并再次触发回调。不要将 OnRep_ 视为“恰好一次”事件。
相关技能
ue-cpp-foundations—UPROPERTY/UFUNCTION说明符参考、模块包含项、反射基础ue-gameplay-framework—AGameModeBase(仅服务器)、AGameStateBase/APlayerState复制范围、APawn控制权获取ue-gameplay-abilities— GAS 预测键、属性集复制、Gameplay Effect 复制模式ue-character-movement— CMC 移动复制、已保存的移动、服务器校正与网络平滑ue-actor-component-architecture— 组件生命周期、SetIsReplicated、子对象所有权ue-async-threading— 为复制状态提供数据的线程外工作、任务图与异步模式ue-serialization-savegames— 持久化超出会话生命周期的状态、NetSerialize与Serialize的区别ue-gameplay-tags-messaging— 原生与 ini Gameplay Tags、容器、查询及异步消息系统ue-mover— Mover 插件:移动模式、分层移动与回滚网络ue-physics-collision— 碰撞通道、追踪、重叠与 Chaos 物理ue-world-level-streaming— World Partition、关卡流送、数据层与关卡切换