ue-networking-replication

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

UE Networking & Replication

UE 网络与复制系统

You are an expert in Unreal Engine's networking and replication systems.
你是Unreal Engine网络与复制系统方面的专家。

Context Check

上下文检查

Read
.agents/ue-project-context.md
for this project's multiplayer configuration. Look for: server topology (dedicated, listen, P2P), player count, replicated classes, and any custom net drivers.
If the context file is absent, ask:
  1. Server topology? (dedicated, listen, P2P)
  2. Maximum player count per session?
  3. Which actors or components need to replicate data?
  4. Are you using Gameplay Ability System (GAS)?

阅读
.agents/ue-project-context.md
了解本项目的多人游戏配置。需关注:服务器拓扑(dedicated、listen、P2P)、玩家数量、可复制类以及任何自定义网络驱动。
如果上下文文件不存在,请询问:
  1. 服务器拓扑?(dedicated、listen、P2P)
  2. 每个会话的最大玩家数?
  3. 哪些Actor或组件需要复制数据?
  4. 是否在使用Gameplay Ability System(GAS)?

Net Roles and Authority

网络角色与权限

UE uses a server-authoritative model: the server is the source of truth for game state. Clients predict locally and reconcile with server corrections.
Every actor on every machine has a local role and a remote role (
ENetRole
).
ROLE_Authority       — owns and can modify this actor (server for replicated actors)
ROLE_AutonomousProxy — client copy of the locally controlled pawn
ROLE_SimulatedProxy  — client copy of another player's actor; engine interpolates state
ROLE_None            — not replicated
From
Actor.h
:
cpp
ENetRole GetLocalRole() const { return Role; }   // role on current machine
ENetRole GetRemoteRole() const;                   // role the other end sees
bool HasAuthority() const { return (GetLocalRole() == ROLE_Authority); }
Net modes:
NM_Standalone
,
NM_DedicatedServer
,
NM_ListenServer
,
NM_Client
.
Role matrix for a replicated Pawn:
MachineGetLocalRole()GetRemoteRole()
ServerROLE_AuthorityROLE_AutonomousProxy or SimulatedProxy
Owning ClientROLE_AutonomousProxyROLE_Authority
Other ClientsROLE_SimulatedProxyROLE_Authority
Listen-server caveat: the host is both
ROLE_Authority
and locally controlled. Use
IsLocallyControlled()
to distinguish logic that should skip the host player.

UE采用服务器权威模型:服务器是游戏状态的唯一可信源。客户端进行本地预测,并根据服务器的修正进行协调。
每台机器上的每个Actor都拥有一个本地角色和一个远程角色(
ENetRole
)。
ROLE_Authority       — owns and can modify this actor (server for replicated actors)
ROLE_AutonomousProxy — client copy of the locally controlled pawn
ROLE_SimulatedProxy  — client copy of another player's actor; engine interpolates state
ROLE_None            — not replicated
来自
Actor.h
cpp
ENetRole GetLocalRole() const { return Role; }   // role on current machine
ENetRole GetRemoteRole() const;                   // role the other end sees
bool HasAuthority() const { return (GetLocalRole() == ROLE_Authority); }
网络模式:
NM_Standalone
NM_DedicatedServer
NM_ListenServer
NM_Client
可复制Pawn的角色矩阵:
机器类型GetLocalRole()GetRemoteRole()
服务器ROLE_AuthorityROLE_AutonomousProxy 或 SimulatedProxy
所属客户端ROLE_AutonomousProxyROLE_Authority
其他客户端ROLE_SimulatedProxyROLE_Authority
监听服务器注意事项: 主机同时拥有
ROLE_Authority
权限且受本地控制。使用
IsLocallyControlled()
来区分需要跳过主机玩家的逻辑。

UNetDriver

UNetDriver

UNetDriver
is the core transport class responsible for managing all network connections and packet delivery for a world. It owns the list of
UNetConnection
objects and drives the replication tick. Access it via
UWorld::GetNetDriver()
.
cpp
UNetDriver* Driver = GetWorld()->GetNetDriver();
// Driver->ClientConnections  — all connected clients (server-side)
// Driver->ServerConnection   — connection to server (client-side)
For most gameplay code you never interact with
UNetDriver
directly; it is relevant when writing custom net drivers, profiling connection state, or debugging packet loss.

UNetDriver
是负责管理世界中所有网络连接和数据包传输的核心传输类。它拥有
UNetConnection
对象列表,并驱动复制tick。可通过
UWorld::GetNetDriver()
访问它。
cpp
UNetDriver* Driver = GetWorld()->GetNetDriver();
// Driver->ClientConnections  — all connected clients (server-side)
// Driver->ServerConnection   — connection to server (client-side)
对于大多数游戏玩法代码,你无需直接与
UNetDriver
交互;仅在编写自定义网络驱动、分析连接状态或调试丢包问题时才会用到它。

Property Replication

属性复制

Actor Setup

Actor 设置

cpp
AMyActor::AMyActor()
{
    bReplicates = true;             // AActor::SetReplicates() also available at runtime
    SetReplicateMovement(true);     // replicates FRepMovement (location/rotation/velocity)
    SetNetUpdateFrequency(10.f);    // checks per second
    SetMinNetUpdateFrequency(2.f);  // floor when nothing changes
    NetPriority = 1.0f;            // higher = preferred when bandwidth is saturated
}
From
Actor.h
:
SetReplicates
,
SetReplicateMovement
,
SetNetUpdateFrequency
,
SetMinNetUpdateFrequency
, and
SetNetCullDistanceSquared
are all
ENGINE_API
.
FRepMovement: when
bReplicateMovement = true
, the engine serializes position, velocity, and rotation into an
FRepMovement
struct (declared in
Actor.h
) and sends it to simulated proxies.
UCharacterMovementComponent
bypasses this with its own prediction-based replication; it writes compressed moves via
FSavedMove_Character
and reconciles them server-side, so
SetReplicateMovement(false)
is the correct default for characters using CMC.
cpp
AMyActor::AMyActor()
{
    bReplicates = true;             // AActor::SetReplicates() also available at runtime
    SetReplicateMovement(true);     // replicates FRepMovement (location/rotation/velocity)
    SetNetUpdateFrequency(10.f);    // checks per second
    SetMinNetUpdateFrequency(2.f);  // floor when nothing changes
    NetPriority = 1.0f;            // higher = preferred when bandwidth is saturated
}
来自
Actor.h
SetReplicates
SetReplicateMovement
SetNetUpdateFrequency
SetMinNetUpdateFrequency
SetNetCullDistanceSquared
均为
ENGINE_API
FRepMovement:
bReplicateMovement = true
时,引擎会将位置、速度和旋转序列化为
FRepMovement
结构体(在
Actor.h
中声明)并发送给模拟代理。
UCharacterMovementComponent
通过其基于预测的复制机制绕过了这一过程;它通过
FSavedMove_Character
写入压缩的移动数据,并在服务器端进行协调,因此对于使用CMC的角色,正确的默认设置是
SetReplicateMovement(false)

Declaring Properties

声明属性

cpp
UPROPERTY(Replicated)
int32 Health;

UPROPERTY(ReplicatedUsing = OnRep_State)
EMyState State;

UFUNCTION()
void OnRep_State(EMyState PreviousState); // old value passed as optional parameter
cpp
UPROPERTY(Replicated)
int32 Health;

UPROPERTY(ReplicatedUsing = OnRep_State)
EMyState State;

UFUNCTION()
void OnRep_State(EMyState PreviousState); // old value passed as optional parameter

GetLifetimeReplicatedProps

GetLifetimeReplicatedProps

cpp
// MyActor.cpp
#include "Net/UnrealNetwork.h"

void AMyActor::GetLifetimeReplicatedProps(
    TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps); // NEVER omit this
    DOREPLIFETIME(AMyActor, Health);
    DOREPLIFETIME_CONDITION(AMyActor, State,        COND_OwnerOnly);
    DOREPLIFETIME_CONDITION(AMyActor, SimData,      COND_SimulatedOnly);
    DOREPLIFETIME_CONDITION(AMyActor, InitData,     COND_InitialOnly);
    DOREPLIFETIME_CONDITION(AMyActor, PublicData,   COND_SkipOwner);
}
Conditions:
COND_None
(all),
COND_OwnerOnly
,
COND_SkipOwner
,
COND_SimulatedOnly
,
COND_AutonomousOnly
,
COND_InitialOnly
,
COND_Custom
.
Use
COND_OwnerOnly
for private player data (inventory, currency). Use
COND_InitialOnly
for immutable spawn data (team, character class).
Initial replication burst: When a client first joins or an actor first becomes relevant, ALL replicated properties send at once regardless of conditions (
COND_InitialOnly
fires exactly once here). This burst can saturate the actor channel — keep initial state compact and use
COND_InitialOnly
for spawn-time-only data to reduce ongoing bandwidth.
cpp
// MyActor.cpp
#include "Net/UnrealNetwork.h"

void AMyActor::GetLifetimeReplicatedProps(
    TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps); // NEVER omit this
    DOREPLIFETIME(AMyActor, Health);
    DOREPLIFETIME_CONDITION(AMyActor, State,        COND_OwnerOnly);
    DOREPLIFETIME_CONDITION(AMyActor, SimData,      COND_SimulatedOnly);
    DOREPLIFETIME_CONDITION(AMyActor, InitData,     COND_InitialOnly);
    DOREPLIFETIME_CONDITION(AMyActor, PublicData,   COND_SkipOwner);
}
条件:
COND_None
(所有客户端)、
COND_OwnerOnly
COND_SkipOwner
COND_SimulatedOnly
COND_AutonomousOnly
COND_InitialOnly
COND_Custom
COND_OwnerOnly
用于玩家私有数据(如背包、货币)。
COND_InitialOnly
用于不可变的生成数据(如队伍、角色职业)。
初始复制爆发: 当客户端首次加入或Actor首次变得相关时,所有可复制属性会一次性发送,不受条件限制(
COND_InitialOnly
在此处仅触发一次)。这种爆发可能会占用Actor通道的带宽——请保持初始状态简洁,并使用
COND_InitialOnly
处理仅生成时需要的数据,以减少持续带宽消耗。

FRepLayout (Internal)

FRepLayout(内部)

FRepLayout
is an internal engine struct that describes which properties of a class are replicated and how. It handles delta compression (only changed properties are sent) and evaluates
DOREPLIFETIME_CONDITION
filters per-connection. Developers rarely interact with
FRepLayout
directly, but knowing it exists helps when debugging why a property is or is not replicating: the layout is built once per class and cached, so dynamic changes to conditions require
DOREPLIFETIME_WITH_PARAMS_FAST
or
bIsPushBased
patterns rather than modifying the struct at runtime.
FRepLayout
是一个内部引擎结构体,描述了类中哪些属性会被复制以及复制方式。它处理增量压缩(仅发送变化的属性),并针对每个连接评估
DOREPLIFETIME_CONDITION
过滤器。开发者很少直接与
FRepLayout
交互,但了解它的存在有助于调试属性未复制或异常复制的问题:布局会为每个类构建一次并缓存,因此动态修改条件需要使用
DOREPLIFETIME_WITH_PARAMS_FAST
bIsPushBased
模式,而非在运行时修改结构体。

Large Arrays: FFastArraySerializer

大型数组:FFastArraySerializer

Plain
TArray
sends the full array on any change. Use
FFastArraySerializer
for delta-only replication. See
references/replication-patterns.md
Pattern 3 for a complete inventory implementation.
普通
TArray
在任何变化时都会发送整个数组。对于仅增量复制的场景,请使用
FFastArraySerializer
。完整的背包实现请参考
references/replication-patterns.md
中的模式3。

Custom Struct Serialization

自定义结构体序列化

Implement
NetSerialize
on a
USTRUCT
and add
TStructOpsTypeTraits
with
WithNetSerializer = true
to control binary layout manually. Use
FVector_NetQuantize10
(0.1 cm precision) instead of raw
FVector
to halve position bandwidth.

USTRUCT
上实现
NetSerialize
,并添加
TStructOpsTypeTraits
且设置
WithNetSerializer = true
,以手动控制二进制布局。使用
FVector_NetQuantize10
(精度0.1厘米)替代原始
FVector
,可将位置数据的带宽减少一半。

Remote Procedure Calls (RPCs)

远程过程调用(RPC)

See
references/rpc-decision-guide.md
for the full decision flowchart.
cpp
// Client calls → server executes. WithValidation is required for state changes.
UFUNCTION(Server, Reliable, WithValidation)
void ServerFireWeapon(FVector_NetQuantize Origin, FVector_NetQuantizeNormal Dir);

// Server calls → owning client executes. No WithValidation needed.
UFUNCTION(Client, Reliable)
void ClientShowKillConfirm();

// Server calls → server + all clients execute.
UFUNCTION(NetMulticast, Unreliable)
void MulticastPlayHitEffect(FVector ImpactPoint);
Implementations always end in
_Implementation
. Server RPCs with validation also need
_Validate
(return
false
to kick the client).
Reliable — guaranteed delivery; use for state-changing actions (purchase, spawn, possession). Unreliable — fire-and-forget; use for high-frequency cosmetics.
Real examples from
PlayerController.h
:
cpp
UFUNCTION(unreliable, server, WithValidation) void ServerSetSpectatorLocation(...);
UFUNCTION(reliable,   server, WithValidation) void ServerAcknowledgePossession(APawn* P);
UFUNCTION(Reliable,   Client)                 void ClientReceiveLocalizedMessage(...);
UFUNCTION(Client,     Unreliable)             void ClientAckTimeDilation(float, int32);
RPC ownership: Server RPCs must be called on an actor whose ownership chain leads to the calling client's
APlayerController
. Client RPCs must be called on an actor owned by a player with a
NetConnection
. Calls on unowned actors are silently dropped.
RPC Parameter Rules: Only net-addressable types are valid — basic types (
int32
,
float
,
FString
,
FVector
), replicated
UObject*
pointers (arrive as
nullptr
if not replicated), and
USTRUCT(BlueprintType)
structs. Non-replicated
UObject*
pointers arrive as
nullptr
; pass an ID and look the object up on the remote side. Large payloads (>1 KB) should use replicated properties instead of RPC parameters.
TArray
and
TMap
parameters are supported but watch for bandwidth impact.

完整的决策流程图请参考
references/rpc-decision-guide.md
cpp
// Client calls → server executes. WithValidation is required for state changes.
UFUNCTION(Server, Reliable, WithValidation)
void ServerFireWeapon(FVector_NetQuantize Origin, FVector_NetQuantizeNormal Dir);

// Server calls → owning client executes. No WithValidation needed.
UFUNCTION(Client, Reliable)
void ClientShowKillConfirm();

// Server calls → server + all clients execute.
UFUNCTION(NetMulticast, Unreliable)
void MulticastPlayHitEffect(FVector ImpactPoint);
实现函数的名称必须以
_Implementation
结尾。带有验证的Server RPC还需要
_Validate
函数(返回
false
会踢掉客户端)。
Reliable(可靠) — 保证送达;用于改变状态的操作(如购买、生成、占有)。Unreliable(不可靠) — 发送后无需确认;用于高频次的外观效果。
来自
PlayerController.h
的真实示例:
cpp
UFUNCTION(unreliable, server, WithValidation) void ServerSetSpectatorLocation(...);
UFUNCTION(reliable,   server, WithValidation) void ServerAcknowledgePossession(APawn* P);
UFUNCTION(Reliable,   Client)                 void ClientReceiveLocalizedMessage(...);
UFUNCTION(Client,     Unreliable)             void ClientAckTimeDilation(float, int32);
RPC所有权: Server RPC必须在其所有权链指向调用客户端的
APlayerController
的Actor上调用。Client RPC必须在由拥有
NetConnection
的玩家所拥有的Actor上调用。在无所有权的Actor上调用会被静默丢弃。
RPC参数规则: 仅支持可网络寻址的类型——基础类型(
int32
float
FString
FVector
)、可复制的
UObject*
指针(如果未复制则会变为
nullptr
)以及
USTRUCT(BlueprintType)
结构体。不可复制的
UObject*
指针会变为
nullptr
;请传递ID并在远程端查找对象。大型负载(>1 KB)应使用可复制属性而非RPC参数。支持
TArray
TMap
参数,但需注意带宽影响。

Ownership and Relevancy

所有权与相关性

cpp
// Server sets owner to control connection routing for RPCs and COND_OwnerOnly
Weapon->SetOwner(PlayerController);

// Relevancy flags (from Actor.h)
bAlwaysRelevant      = true;  // replicate to every connection
bOnlyRelevantToOwner = true;  // replicate only to the owning connection

// Override for custom logic (e.g., team-based relevancy)
virtual bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget,
                               const FVector& SrcLocation) const override;
Default relevancy culls actors beyond
NetCullDistanceSquared
from the client viewpoint.
NetPriority
determines which actors win when bandwidth is saturated.
Dormancy: actors that rarely change can pause replication entirely.
cpp
SetNetDormancy(DORM_DormantAll); // stop replication
FlushNetDormancy();              // send one update then return to dormant
Re-relevancy: When an actor moves outside
NetCullDistanceSquared
it stops replicating. When it returns to range, the engine treats it as a fresh relevancy event — sending another initial burst and firing all
OnRep_
callbacks with current server state. Design
OnRep_
functions to be idempotent (tolerate being called multiple times with the same value). Dormant actors (
DORM_DormantAll
) behave similarly when woken — they flush all dirty properties at once.

cpp
// Server sets owner to control connection routing for RPCs and COND_OwnerOnly
Weapon->SetOwner(PlayerController);

// Relevancy flags (from Actor.h)
bAlwaysRelevant      = true;  // replicate to every connection
bOnlyRelevantToOwner = true;  // replicate only to the owning connection

// Override for custom logic (e.g., team-based relevancy)
virtual bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget,
                               const FVector& SrcLocation) const override;
默认相关性会剔除客户端视角超出
NetCullDistanceSquared
范围的Actor。当带宽饱和时,
NetPriority
决定哪些Actor会优先被复制。
休眠: 很少变化的Actor可以完全暂停复制。
cpp
SetNetDormancy(DORM_DormantAll); // stop replication
FlushNetDormancy();              // send one update then return to dormant
重新相关性: 当Actor移动到
NetCullDistanceSquared
范围外时,复制会停止。当它回到范围内时,引擎会将其视为新的相关性事件——再次发送初始爆发数据,并使用当前服务器状态触发所有
OnRep_
回调。请将
OnRep_
函数设计为幂等的(能够容忍多次使用相同值调用)。休眠的Actor(
DORM_DormantAll
)被唤醒时的行为类似——它们会一次性发送所有脏属性。

Subobject Replication

子对象复制

Modern API (UE 5.1+)

现代API(UE 5.1+)

From
Actor.h
:
cpp
ENGINE_API void AddReplicatedSubObject(UObject* SubObject,
                                       ELifetimeCondition NetCondition = COND_None);
ENGINE_API void RemoveReplicatedSubObject(UObject* SubObject);
ENGINE_API void AddActorComponentReplicatedSubObject(UActorComponent* OwnerComponent,
                                                     UObject* SubObject,
                                                     ELifetimeCondition NetCondition = COND_None);
Enable with
bReplicateUsingRegisteredSubObjectList = true
(now the default). Call
AddReplicatedSubObject
on the server during
BeginPlay
; call
RemoveReplicatedSubObject
in
EndPlay
.
来自
Actor.h
cpp
ENGINE_API void AddReplicatedSubObject(UObject* SubObject,
                                       ELifetimeCondition NetCondition = COND_None);
ENGINE_API void RemoveReplicatedSubObject(UObject* SubObject);
ENGINE_API void AddActorComponentReplicatedSubObject(UActorComponent* OwnerComponent,
                                                     UObject* SubObject,
                                                     ELifetimeCondition NetCondition = COND_None);
通过设置
bReplicateUsingRegisteredSubObjectList = true
启用(现为默认设置)。在服务器的
BeginPlay
期间调用
AddReplicatedSubObject
;在
EndPlay
中调用
RemoveReplicatedSubObject

Legacy API

旧版API

Override
ReplicateSubobjects
when
bReplicateUsingRegisteredSubObjectList = false
:
cpp
bool AMyActor::ReplicateSubobjects(UActorChannel* Channel,
                                    FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
    bool bWrote = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
    bWrote |= Channel->ReplicateSubobject(MySubObject, *Bunch, *RepFlags);
    return bWrote;
}

bReplicateUsingRegisteredSubObjectList = false
时,重写
ReplicateSubobjects
cpp
bool AMyActor::ReplicateSubobjects(UActorChannel* Channel,
                                    FOutBunch* Bunch, FReplicationFlags* RepFlags)
{
    bool bWrote = Super::ReplicateSubobjects(Channel, Bunch, RepFlags);
    bWrote |= Channel->ReplicateSubobject(MySubObject, *Bunch, *RepFlags);
    return bWrote;
}

Client-Side Prediction

客户端预测

Pattern (for non-movement systems without GAS):
cpp
// 1. Client predicts immediately
void AMyCharacter::LocalPredictAbility(int32 AbilityId)
{
    if (IsLocallyControlled())
    {
        ApplyAbilityEffectLocal(AbilityId); // immediate visual/audio
        ServerActivateAbility(AbilityId);   // request server confirmation
    }
}

// 2. Server validates, broadcasts, or corrects
void AMyCharacter::ServerActivateAbility_Implementation(int32 AbilityId)
{
    if (CanActivateAbility(AbilityId))
    {
        ApplyAbilityEffectAuthority(AbilityId);
        MulticastAbilityActivated(AbilityId);
    }
    else
    {
        ClientCorrectionAbilityFailed(AbilityId); // roll back client prediction
    }
}
When the server rejects a prediction, smooth the visual correction to avoid jarring snaps: interpolate the actor to the corrected position over 2-3 frames rather than teleporting.
UCharacterMovementComponent
handles this automatically via
NetworkSmoothingMode
(
Linear
,
Exponential
, or
Disabled
).
CharacterMovementComponent has full built-in prediction. Extend
FSavedMove_Character
and override
PhysCustom
to support custom movement modes.
Prediction eligibility: Only
UCharacterMovementComponent
and GAS (
UAbilitySystemComponent
with
FPredictionKey
) have engine-managed prediction and rollback. Raw
UPROPERTY(Replicated)
properties have no built-in prediction — if you write them locally before the server confirms, the server's replicated value overwrites the client value with no rollback. For non-CMC/GAS state, use local-only variables for visual prediction and apply the authoritative value in
OnRep_
.
GAS and FPredictionKey: GAS uses
FPredictionKey
to link client-predicted actions with server confirmations. The key is generated on the client (via
FPredictionKey::CreateNewPredictionKey
), embedded in the Server RPC, and matched on the server when applying
GameplayEffect
s or activating abilities. If the server determines the prediction was invalid, every
GameplayEffect
and attribute change tagged with that key is automatically rolled back on the client. You do not construct
FPredictionKey
manually in most cases;
UAbilitySystemComponent::TryActivateAbility
manages the lifecycle. Relevant when writing custom
UGameplayAbility
subclasses that need scoped prediction windows (
FScopedPredictionWindow
). See
ue-gameplay-abilities
for the full GAS networking details.

模式(适用于无GAS的非移动系统):
cpp
// 1. Client predicts immediately
void AMyCharacter::LocalPredictAbility(int32 AbilityId)
{
    if (IsLocallyControlled())
    {
        ApplyAbilityEffectLocal(AbilityId); // immediate visual/audio
        ServerActivateAbility(AbilityId);   // request server confirmation
    }
}

// 2. Server validates, broadcasts, or corrects
void AMyCharacter::ServerActivateAbility_Implementation(int32 AbilityId)
{
    if (CanActivateAbility(AbilityId))
    {
        ApplyAbilityEffectAuthority(AbilityId);
        MulticastAbilityActivated(AbilityId);
    }
    else
    {
        ClientCorrectionAbilityFailed(AbilityId); // roll back client prediction
    }
}
当服务器拒绝预测时,请平滑视觉修正以避免突兀的跳转:在2-3帧内将Actor插值到修正后的位置,而非直接传送。
UCharacterMovementComponent
通过
NetworkSmoothingMode
Linear
Exponential
Disabled
)自动处理此问题。
CharacterMovementComponent 内置完整的预测功能。扩展
FSavedMove_Character
并重写
PhysCustom
以支持自定义移动模式。
预测资格: 只有
UCharacterMovementComponent
和GAS(带有
FPredictionKey
UAbilitySystemComponent
)拥有引擎管理的预测和回滚机制。原始的
UPROPERTY(Replicated)
属性没有内置预测——如果你在服务器确认前本地修改它们,服务器的复制值会覆盖客户端的值且不会回滚。对于非CMC/GAS状态,请使用本地变量进行视觉预测,并在
OnRep_
中应用权威值。
GAS与FPredictionKey: GAS使用
FPredictionKey
将客户端预测的操作与服务器确认关联起来。该密钥在客户端生成(通过
FPredictionKey::CreateNewPredictionKey
),嵌入到Server RPC中,并在服务器应用
GameplayEffect
或激活技能时进行匹配。如果服务器判定预测无效,所有带有该密钥的
GameplayEffect
和属性变更会在客户端自动回滚。大多数情况下你无需手动构造
FPredictionKey
UAbilitySystemComponent::TryActivateAbility
会管理其生命周期。在编写需要作用域预测窗口的自定义
UGameplayAbility
子类时会用到相关内容(
FScopedPredictionWindow
)。完整的GAS网络细节请查看
ue-gameplay-abilities

Actor Replication Setup Checklist

Actor复制设置检查清单

[ ] bReplicates = true in constructor
[ ] SetNetUpdateFrequency / SetMinNetUpdateFrequency set appropriately
[ ] All UPROPERTY(Replicated) / UPROPERTY(ReplicatedUsing=...) declared
[ ] GetLifetimeReplicatedProps overridden; Super called first
[ ] DOREPLIFETIME / DOREPLIFETIME_CONDITION for each property
[ ] OnRep_ functions declared UFUNCTION() and implemented
[ ] Server RPCs have WithValidation; _Validate implemented
[ ] NetMulticast / Client RPCs guarded with HasAuthority() on call site
[ ] Subobjects registered via AddReplicatedSubObject or ReplicateSubobjects
[ ] SetOwner() called on server after spawning owned actors
[ ] DORM_DormantAll set on actors that rarely update

[ ] bReplicates = true in constructor
[ ] SetNetUpdateFrequency / SetMinNetUpdateFrequency set appropriately
[ ] All UPROPERTY(Replicated) / UPROPERTY(ReplicatedUsing=...) declared
[ ] GetLifetimeReplicatedProps overridden; Super called first
[ ] DOREPLIFETIME / DOREPLIFETIME_CONDITION for each property
[ ] OnRep_ functions declared UFUNCTION() and implemented
[ ] Server RPCs have WithValidation; _Validate implemented
[ ] NetMulticast / Client RPCs guarded with HasAuthority() on call site
[ ] Subobjects registered via AddReplicatedSubObject or ReplicateSubobjects
[ ] SetOwner() called on server after spawning owned actors
[ ] DORM_DormantAll set on actors that rarely update

Replication Graph

复制图

For large player counts (50+ connections), the default net driver relevancy checks become expensive.
UReplicationGraph
replaces per-connection per-actor relevancy with spatial and policy-based nodes.
cpp
// Build.cs: "ReplicationGraph"
// Project: set ReplicationDriverClassName in DefaultEngine.ini
// [/Script/OnlineSubsystemUtils.IpNetDriver]
// ReplicationDriverClassName=/Script/MyGame.MyReplicationGraph

UCLASS()
class UMyReplicationGraph : public UReplicationGraph
{
    GENERATED_BODY()
public:
    virtual void InitGlobalActorClassSettings() override;
    virtual void InitGlobalGraphNodes() override;
    virtual void InitConnectionGraphNodes(UNetReplicationGraphConnection* RepGraphConnection) override;
};
Key node types:
UReplicationGraphNode_GridSpatialization2D
(spatial),
UReplicationGraphNode_AlwaysRelevant
(GameState, managers),
UReplicationGraphNode_AlwaysRelevant_ForConnection
(PlayerController, PlayerState, HUD),
UReplicationGraphNode_ActorList
(custom lists).

对于大玩家数量(50+连接),默认网络驱动的相关性检查会变得昂贵。
UReplicationGraph
将每个连接每个Actor的相关性检查替换为基于空间和策略的节点。
cpp
// Build.cs: "ReplicationGraph"
// Project: set ReplicationDriverClassName in DefaultEngine.ini
// [/Script/OnlineSubsystemUtils.IpNetDriver]
// ReplicationDriverClassName=/Script/MyGame.MyReplicationGraph

UCLASS()
class UMyReplicationGraph : public UReplicationGraph
{
    GENERATED_BODY()
public:
    virtual void InitGlobalActorClassSettings() override;
    virtual void InitGlobalGraphNodes() override;
    virtual void InitConnectionGraphNodes(UNetReplicationGraphConnection* RepGraphConnection) override;
};
关键节点类型:
UReplicationGraphNode_GridSpatialization2D
(空间型)、
UReplicationGraphNode_AlwaysRelevant
(GameState、管理器)、
UReplicationGraphNode_AlwaysRelevant_ForConnection
(PlayerController、PlayerState、HUD)、
UReplicationGraphNode_ActorList
(自定义列表)。

Common Mistakes

常见错误

MistakeFix
Modifying state without
HasAuthority()
guard
Add
if (!HasAuthority()) return;
Server RPC without
WithValidation
— client sends arbitrary values
Add
WithValidation
; validate in
_Validate
Calling
Client
/
NetMulticast
from the client — silently dropped
Only call from server; guard with
HasAuthority()
Plain
TArray
on replicated component — full array sent on every change
Use
FFastArraySerializer
Omitting
Super::GetLifetimeReplicatedProps
— parent properties silently dropped
Always call
Super
first
Passing non-replicated
UObject*
in RPC params — arrives as
nullptr
Pass an ID; look up object on remote side
Using
NetMulticast
for persistent state — late-joining clients miss it
Use
UPROPERTY(Replicated)
for state
Replicating cosmetic-only properties — increases bandwidth for data clients can compute locallyOnly replicate gameplay-critical state; derive visuals client-side

错误修复方案
未使用
HasAuthority()
保护就修改状态
添加
if (!HasAuthority()) return;
Server RPC未使用
WithValidation
— 客户端发送任意值
添加
WithValidation
;在
_Validate
中进行验证
从客户端调用
Client
/
NetMulticast
— 被静默丢弃
仅从服务器调用;使用
HasAuthority()
保护调用点
可复制组件上使用普通
TArray
— 每次变化都会发送整个数组
使用
FFastArraySerializer
省略
Super::GetLifetimeReplicatedProps
— 父类属性被静默丢弃
始终先调用
Super
在RPC参数中传递不可复制的
UObject*
— 到达后变为
nullptr
传递ID;在远程端查找对象
使用
NetMulticast
传递持久化状态 — 晚加入的客户端会错过
使用
UPROPERTY(Replicated)
存储状态
复制仅用于外观的属性 — 增加了客户端可本地计算的数据的带宽消耗仅复制游戏玩法关键状态;客户端本地推导视觉效果

APlayerController Replication Scope

APlayerController复制范围

APlayerController
exists only on the server and the owning client — not on other clients. Put data meant for all clients on
APawn
or
APlayerState
.
From
PlayerController.h
:
cpp
// nullptr on local players (server-side); points to the network connection otherwise
UPROPERTY(DuplicateTransient)
TObjectPtr<UNetConnection> NetConnection;
UNetConnection represents a single client's network connection on the server. Each connected client has exactly one
UNetConnection
owned by
UNetDriver
. Retrieve it with
APlayerController::GetNetConnection()
. It is used for:
  • Ownership checks: an actor's owning connection determines which client RPCs it can receive and which
    COND_OwnerOnly
    properties it sees.
  • RPC routing: Server RPCs are only accepted from the actor's owning connection.
  • Detecting disconnects: bind to
    AGameModeBase::OnLogout
    or check
    NetConnection->GetConnectionState() == USOCK_Closed
    (or
    IsClosingOrClosed()
    ) rather than polling
    NetConnection
    directly.
Use
APlayerController
for connection-scoped Client RPCs (HUD updates, voice, level streaming notifications). Use
APawn
or
APlayerState
for world-visible state.

APlayerController
仅存在于服务器和所属客户端上——不存在于其他客户端。将需要所有客户端可见的数据放在
APawn
APlayerState
上。
来自
PlayerController.h
cpp
// nullptr on local players (server-side); points to the network connection otherwise
UPROPERTY(DuplicateTransient)
TObjectPtr<UNetConnection> NetConnection;
UNetConnection 在服务器上代表单个客户端的网络连接。每个已连接的客户端都拥有一个由
UNetDriver
管理的
UNetConnection
。可通过
APlayerController::GetNetConnection()
获取它。它用于:
  • 所有权检查:Actor的所属连接决定了它可以接收哪些Client RPC以及它能看到哪些
    COND_OwnerOnly
    属性。
  • RPC路由:Server RPC仅接受来自Actor所属连接的调用。
  • 检测断开连接:绑定到
    AGameModeBase::OnLogout
    或检查
    NetConnection->GetConnectionState() == USOCK_Closed
    (或
    IsClosingOrClosed()
    ),而非直接轮询
    NetConnection
使用
APlayerController
进行连接作用域的Client RPC(如HUD更新、语音、关卡流通知)。使用
APawn
APlayerState
存储世界可见的状态。

Related Skills

相关Skill

  • ue-gameplay-framework
    — AGameMode (server-only), AGameState / APlayerState (replicated to all), APawn net roles
  • ue-gameplay-abilities
    — GAS prediction keys, attribute set replication, GameplayEffect replication
  • ue-cpp-foundations
    — UPROPERTY/UFUNCTION macros, module includes
  • ue-gameplay-framework
    — AGameMode(仅服务器)、AGameState / APlayerState(复制到所有客户端)、APawn网络角色
  • ue-gameplay-abilities
    — GAS预测密钥、属性集复制、GameplayEffect复制
  • ue-cpp-foundations
    — UPROPERTY/UFUNCTION宏、模块引用

Reference Files

参考文件

  • references/replication-patterns.md
    — health, inventory (FFastArraySerializer), ability state, team data, subobject, dormancy, and custom relevancy patterns
  • references/rpc-decision-guide.md
    — Server/Client/NetMulticast decision flowchart, Reliable vs Unreliable rules, ownership routing, and common RPC mistakes
  • references/replication-patterns.md
    — 生命值、背包(FFastArraySerializer)、技能状态、队伍数据、子对象、休眠和自定义相关性模式
  • references/rpc-decision-guide.md
    — Server/Client/NetMulticast决策流程图、可靠与不可靠规则、所有权路由以及常见RPC错误