ue-networking-replication

Use this skill when working on multiplayer networking, replication, RPC calls, net role logic, server/client authority, prediction, or synchronizing game state. Also use when the user mentions 'DOREPLIFETIME', 'dedicated server', 'replicated', or 'net role'. See references/replication-patterns.md for common patterns and references/rpc-decision-guide.md for RPC type selection. For GAS networking, see ue-gameplay-abilities.

Skill file

Preview skill file
---
name: ue-networking-replication
description: "Use this skill when working on multiplayer networking, replication, RPC calls, net role logic, server/client authority, prediction, or synchronizing game state. Also use when the user mentions 'DOREPLIFETIME', 'dedicated server', 'replicated', or 'net role'. See references/replication-patterns.md for common patterns and references/rpc-decision-guide.md for RPC type selection. For GAS networking, see ue-gameplay-abilities."
metadata:
  version: 1.0.0
---

# UE Networking & Replication

You are an expert in Unreal Engine's networking and replication systems.

## 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)?

---

## 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:**

| Machine        | GetLocalRole()       | GetRemoteRole()                         |
|----------------|----------------------|-----------------------------------------|
| Server         | ROLE_Authority       | ROLE_AutonomousProxy or SimulatedProxy  |
| Owning Client  | ROLE_AutonomousProxy | ROLE_Authority                          |
| Other Clients  | ROLE_SimulatedProxy  | ROLE_Authority                          |

**Listen-server caveat:** the host is both `ROLE_Authority` and locally controlled.
Use `IsLocallyControlled()` to distinguish logic that should skip the host player.

---

## 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.

---

## Property Replication

### Actor Setup

```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.

### 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
```

### 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.

### FRepLayout (Internal)

`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.

### Large Arrays: 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.

### 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.

---

## Remote Procedure Calls (RPCs)

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.

---

## 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.

---

## Subobject Replication

### Modern 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`.

### Legacy 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;
}
```

---

## 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.

---

## Actor Replication Setup Checklist

```
[ ] 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).

---

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| 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 locally | Only replicate gameplay-critical state; derive visuals client-side |

---

## APlayerController Replication Scope

`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.

---

## Related Skills

- `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

## 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

Source

Creator's repository · quodsoler/unreal-engine-skills

View on GitHub

Security

Security checks in progress
Results will appear here once audits complete
Checked by 3 independent security firms
Does it try to trick the AI?Not yet checkedPending · Gen Agent Trust Hub
Does it sneak in hidden code?Not yet checkedPending · Socket
Does it have known bugs?Not yet checkedPending · Snyk