📡 Lesson 10.2: Property Replication & RepNotify
Replication is Unreal's magic: mark a property replicated, change it on the server, and every client's copy updates automatically — no networking code from you. Add RepNotify and each client can react to the change. This is step 3 of the networking pattern, and it rides the reflection system you already know.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Enable actor replication with
bReplicates - Replicate a property with
UPROPERTY(Replicated)andGetLifetimeReplicatedProps - React to replicated changes with
ReplicatedUsing(RepNotify) and anOnRep_function - Explain replication direction and when values arrive
- Replicate the
UHealthComponentand drive the HUD everywhere
Estimated Time: 60 minutes
Engine Version: Unreal Engine 5.8
In This Lesson
Enabling Replication
Two switches turn an actor into a networked one, set in the constructor:
AEnemy::AEnemy()
{
bReplicates = true; // this actor exists on clients & syncs
// For actors that move under physics/movement and must sync position:
SetReplicateMovement(true); // replicate the transform
}
📖 bReplicates = "this actor is networked"
With bReplicates = true, the server creates the actor on all clients and keeps replicated properties in sync. SetReplicateMovement additionally syncs the actor's transform (great for simple moving actors; Characters handle their own movement replication via the movement component from Lesson 4.2). Without bReplicates, the actor is server-local — clients never see it.
Replicating a Property
Marking a property replicated takes two pieces: the Replicated specifier on the UPROPERTY, and registering it in GetLifetimeReplicatedProps.
// Enemy.h
#include "Net/UnrealNetwork.h" // for replication macros (in the .cpp)
UPROPERTY(Replicated)
float Health = 100.0f;
// Override this to declare which properties replicate:
virtual void GetLifetimeReplicatedProps(
TArray<FLifetimeProperty>& OutLifetimeProps) const override;
// Enemy.cpp
#include "Net/UnrealNetwork.h"
void AEnemy::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Register Health for replication. This macro is required.
DOREPLIFETIME(AEnemy, Health);
}
⚠️ Both pieces are mandatory
A UPROPERTY(Replicated) that you forget to register with DOREPLIFETIME won't replicate — and there's no error, just a value that never updates on clients (a classic silent bug). Include Net/UnrealNetwork.h in the .cpp. Now, whenever the server changes Health, every client's copy updates automatically. You wrote no send/receive code — the reflection system (Lesson 2.2) does it, the fourth payoff realized.
✅ Conditional replication
Variants like DOREPLIFETIME_CONDITION(AEnemy, Ammo, COND_OwnerOnly) replicate only to certain clients (e.g. only the owner sees their own ammo) — a bandwidth optimization. Start with plain DOREPLIFETIME; reach for conditions when you need to limit who receives a value.
Direction & Timing
Property replication flows in exactly one direction: server → clients. Clients never replicate properties up to the server (that's what RPCs are for, Lesson 10.3).
⚠️ Only change replicated properties on the server
Setting a replicated property on a client does nothing useful — the value isn't sent up, and the next server replication overwrites it. Always change replicated state inside if (HasAuthority()) (Lesson 10.1). Replication is not instant either — values arrive over the network with latency and are batched, so don't assume a client sees a change the same frame the server made it.
RepNotify (OnRep)
Plain replication updates the value silently. Often you want each client to react when it changes — update a health bar, play a hit effect. ReplicatedUsing = OnRep_Fn calls a function on clients whenever the value arrives.
// Enemy.h — replicate WITH a notify callback.
UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health = 100.0f;
UFUNCTION()
void OnRep_Health(); // runs on clients when Health replicates in
// Enemy.cpp — register it the same way (RepNotify still needs DOREPLIFETIME):
void AEnemy::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const
{
Super::GetLifetimeReplicatedProps(Out);
DOREPLIFETIME(AEnemy, Health);
}
// Called on CLIENTS when the new Health value arrives from the server.
void AEnemy::OnRep_Health()
{
UE_LOG(LogTemp, Log, TEXT("Health replicated to %.0f"), Health);
// Update health bar, play a hit flash, etc. — reacting to the new value.
if (Health <= 0.0f) { PlayDeathEffects(); }
}
📖 OnRep runs on clients, not the server
The OnRep_ function fires on clients when the replicated value updates — not on the server that changed it. So if the server also needs to react, call your reaction logic directly there too. A common idiom: the server changes Health and calls OnRep_Health() manually to run the same reaction locally, so both server and clients respond consistently. RepNotify is how replicated state becomes reactive events on every client — much like the delegates from Lesson 5.4, but driven by the network.
Replicating Health
Let's network the UHealthComponent that's threaded through the whole course. Components replicate too — set SetIsReplicatedByDefault(true) and register the property.
// HealthComponent.cpp
UHealthComponent::UHealthComponent()
{
PrimaryComponentTick.bCanEverTick = false;
SetIsReplicatedByDefault(true); // this component replicates
}
void UHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const
{
Super::GetLifetimeReplicatedProps(Out);
DOREPLIFETIME(UHealthComponent, CurrentHealth);
}
bool UHealthComponent::ApplyDamage(float Amount)
{
if (!GetOwner()->HasAuthority()) { return false; } // server-only (Lesson 10.1)
CurrentHealth = FMath::Clamp(CurrentHealth - Amount, 0.0f, MaxHealth);
OnHealthChanged.Broadcast(CurrentHealth, MaxHealth); // local (server) reaction
return CurrentHealth <= 0.0f;
}
// HealthComponent.h — RepNotify so clients fire the SAME event the HUD listens to.
UPROPERTY(ReplicatedUsing = OnRep_Health, VisibleAnywhere, BlueprintReadOnly)
float CurrentHealth = 0.0f;
UFUNCTION()
void OnRep_Health()
{
// On clients, broadcast the same event the HUD (Lesson 9.2) subscribes to.
OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
}
✅ Watch the whole course converge
The server applies damage (authority, 10.1), CurrentHealth replicates to clients (10.2), each client's OnRep_Health broadcasts OnHealthChanged (5.4), and the HUD widget updates its bar (9.2). One value change flows correctly to every player's screen — and you wrote no manual networking. This is the reward for building on the same patterns all along: they compose into multiplayer almost for free.
Hands-on Exercise & Quiz
🏋️ Exercise: Replicate a door's open state
Objective: Replicate state and react on clients.
- On
ADoor, setbReplicates = true. - Add
UPROPERTY(ReplicatedUsing = OnRep_IsOpen) bool bIsOpen;and register it inGetLifetimeReplicatedProps. - The server sets
bIsOpen(guard withHasAuthority()); inOnRep_IsOpen, play the open/close animation. - Why put the animation in
OnRep_IsOpenrather than in the function that setsbIsOpen?
✅ Answer to Q4
Because the setter runs only on the server, but the animation must play on every client. OnRep_IsOpen fires on each client when the value replicates in, so all players see the door animate. (Call OnRep_IsOpen() manually on the server too, so the host/listen-server player also sees it.)
🎯 Quick Quiz
Question 1: Marking UPROPERTY(Replicated) is enough on its own.
Question 2: Property replication flows in which direction?
Question 3: An OnRep_ function runs on:
Summary
🎉 Key Takeaways
- Enable networking with
bReplicates = true(andSetReplicateMovement/SetIsReplicatedByDefaultfor movement / components). - Replicate a property with both
UPROPERTY(Replicated)andDOREPLIFETIMEinGetLifetimeReplicatedProps— miss either and it silently won't sync. - Properties replicate server → clients only, with latency — change them on the server (
HasAuthority()), never on clients. ReplicatedUsing = OnRep_FncallsOnRep_on clients when the value arrives — how state becomes a reactive event.- Replicating
CurrentHealth+ broadcastingOnHealthChangedinOnRepdrives every client's HUD with no manual net code.
📚 Additional Resources
🚀 What's Next?
Properties get server state down to clients. Now the other direction — a client asking the server to do something: RPCs (Server, Client, Multicast), step 1 of the networking pattern.
🎉 Lesson complete!
State flows to every client. Now let's send commands.