mcpField
U
ue-networking-replication

ue-networking-replication

github.com/quodsoler开发4191.2k 次安装4 周前更新
代码仓库

关于 ue-networking-replication

面向 Unreal Engine 5.8 开发者的网络复制技能,讲解服务器权威模型、属性与 RPC 复制,以及 Fast Array、Push Model 和可选复制系统。

主要功能

  1. 01网络角色与权威判断
  2. 02属性复制与生命周期条件
  3. 03Push Model 脏标记
  4. 04Fast Array 增量序列化
  5. 05Server、Client 与 Multicast RPC
  6. 06Iris 与 Replication Graph

适用场景

  1. 01同步 Actor 属性和移动状态
  2. 02按连接控制属性接收范围
  3. 03复制会变化的库存数组
  4. 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.UseIrisReplicationIris 复制系统
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_AuthorityROLE_AutonomousProxy 或 ROLE_SimulatedProxy
所有者客户端ROLE_AutonomousProxyROLE_Authority
其他客户端ROLE_SimulatedProxyROLE_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_OwnerOnlyActor 的所属连接
COND_SkipOwner除所属连接外的所有连接
COND_SimulatedOnlyActor 是模拟代理的连接
COND_AutonomousOnlyActor 是自主代理的连接
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 或 FillReplicationParamsGameFramework/Actor.h 中的 UE_DEPRECATED(5.7)
virtual void EndReplication(EEndPlayReason::Type)OnStopReplicationForIrisGameFramework/Actor.h 中的 UE_DEPRECATED(5.7)
BeginReplication(const FActorReplicationParams&)重写 FillReplicationParamsGameFramework/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仅读取 OldArraySizeNet/Serialization/FastArraySerializer.h 中的 UE_DEPRECATED(5.4)
UObjectReplicationBridge::DetachInstanceFromRemoteDetachRootObjectFromRemote / DetachSubObjectFromRemoteIris/ReplicationSystem/ObjectReplicationBridge.h 中的 UE_DEPRECATED(5.8)
UNetObjectFactory::FDestroyedContextFDetachContextIris/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、关卡流送、数据层与关卡切换

评论

支持 Markdown