RepNotify variables and local presentation¶
RepNotify is the normal beginner bridge from replicated gameplay state to local UI, audio, materials, and visual effects. The variable is the truth. The OnRep path is each machine reacting to that truth arriving or changing.
The one-minute version¶
- Replicated sends a variable's value from the authoritative server copy to client copies.
- RepNotify does that too, and also gives you an
OnRep_<VariableName>function/event to run local follow-up work when that copy observes the replicated value change. - Put durable gameplay facts in the variable: health, ammo, door open state, team score, ready state, equipped item ID.
- Put local presentation in the OnRep/apply path: update widgets, play sounds, start/stop Niagara, change material parameters, play a door timeline.
- RepNotify is not client-to-server communication. Clients still need Server RPCs or server-owned gameplay paths to request authoritative changes.
- RepNotify order across different variables is not deterministic. If two values must be interpreted together, replicate one struct or one state enum instead of racing two OnRep functions.
What RepNotify actually does¶
Replication sends changed state from the server's authoritative actor to client copies. RepNotify adds a named hook that runs when the local copy handles that replicated value.
pseudocode of the relationship - not engine source
Server:
Health = 60
Health is marked RepNotify
Network replication:
Health is considered for each relevant client
changed value is sent when the actor replicates
Client receives update:
local Health becomes 60
OnRep_Health runs locally
local widget/material/audio reads Health and updates
The important part is the direction: the server changes authoritative state, then clients react when state reaches their local copies. The OnRep function is not a permission check and not a request path.
How to create it in Blueprint¶
For a replicated actor variable:
Actor class defaults:
-> Replicates = true
Variable:
-> select variable
-> Details panel -> Replication -> RepNotify
-> Blueprint creates/uses OnRep_<VariableName>
Server gameplay:
-> set the variable on the server
OnRep_<VariableName>:
-> call Apply<Thing>Presentation
Use a separate helper for the visible work:
SetHealthOnServer(NewHealth):
Health = clamp(NewHealth, 0, MaxHealth)
ApplyHealthPresentation() // keeps server/listen-server view current too
OnRep_Health:
ApplyHealthPresentation()
ApplyHealthPresentation:
update local health bar if one exists
set local dynamic material parameter
play local low-health cue if this is the owning player
That helper avoids two common traps: duplicating visual code in two places, and forgetting that the server copy may also need to update a listen-server host's view or world presentation.
Blueprint RepNotify examples often show the generated OnRep path running on
both authority and remote machines when the value changes. C++ ReplicatedUsing
is usually treated as a receive hook on clients, with server-side parallel work
called manually after the server changes the variable. The helper pattern works
for both, which is why it is the best beginner default.
Replicated vs RepNotify¶
| Setting | What clients get | What extra code runs | Use when |
|---|---|---|---|
| Replicated | The current value | Nothing automatic beyond the value update | Other code already polls/reads the value, or no immediate reaction is needed. |
| RepNotify | The current value | OnRep_<VariableName> on copies that observe the change |
A local response should happen when the value arrives. |
Both are server-to-client state replication. RepNotify is not "stronger replication"; it is replication plus a callback.
What belongs in the variable¶
Replicate state that still matters after the packet is gone:
| Good RepNotify state | Local presentation derived from it |
|---|---|
Health = 45 |
Health bar percent, blood vignette, low-health sound gate. |
bDoorOpen = true |
Door mesh timeline, collision mode, opening sound. |
AmmoInClip = 3 |
Ammo text, reload prompt, dry-fire click eligibility. |
TeamId = Blue |
Nameplate color, outline material, scoreboard grouping. |
EquippedItemId = Rifle_01 |
First-person mesh swap, HUD icon, equip sound. |
MatchPhase = InProgress |
Timer widget, announcer cue, ready button visibility. |
Do not replicate the widget, sound component, dynamic material instance, or Niagara component itself as the main truth. Those are local presentation objects. Replicate the compact state that lets each machine create or update its own presentation.
What OnRep should do¶
Good OnRep jobs:
- copy the replicated value into a widget through a stored local UI reference;
- update a progress bar, text label, nameplate, or inventory slot view;
- play a one-shot local sound if the transition matters;
- start, stop, or retarget a Niagara effect;
- set a dynamic material parameter;
- call an idempotent
ApplyStatehelper on an actor or component; - rebuild local presentation after the actor becomes relevant or a late client receives the current state.
Poor OnRep jobs:
- deciding whether damage is valid;
- subtracting ammo or currency;
- spawning authoritative gameplay actors;
- calling Server RPCs just because the value arrived;
- assuming another replicated variable's OnRep already ran;
- doing expensive world searches every time a value changes;
- mutating widgets as if the widget were the source of multiplayer truth.
OnRep is the "state arrived; update this local copy" hook. Authority decisions still belong on the server.
Ordering and missed intermediate values¶
Replicated properties are not a frame-by-frame event log. They are state updates. If the server sets:
Health = 90
Health = 70
Health = 40
before the actor's next replication update reaches a client, that client may
only need to learn the current value, 40. That is good for health bars and
bad for systems that wanted three separate hit reactions. If every hit reaction
matters, replicate durable hit state or use a carefully chosen RPC for the
transient cue.
Different RepNotify variables also do not have a deterministic order relative to each other. This can break graphs like:
OnRep_CurrentWeaponId expects CurrentAmmo to already be updated
OnRep_CurrentAmmo expects CurrentWeaponId to already be updated
Better shapes:
RepNotify struct:
FEquippedWeaponState
WeaponId
AmmoInClip
ReserveAmmo
or
RepNotify enum/state:
InventoryPresentationState
If values must be interpreted together, replicate them together.
Patterns that work¶
Health bar¶
Server damage path:
Apply Damage / authoritative hit
-> HealthComponent clamps Health
-> Health = NewHealth (RepNotify)
-> ApplyHealthPresentation on server copy if needed
Clients:
OnRep_Health
-> ApplyHealthPresentation
-> local widget updates from Health / MaxHealth
The widget is not replicated. Each client owns its own widget and reads the replicated health value on its local actor copy.
Door state¶
Client interact request:
ServerTryInteract(Door)
Server door:
bOpen = true (RepNotify)
ApplyDoorState
Clients:
OnRep_Open
-> ApplyDoorState
-> play timeline to open pose
-> set local sound/VFX/materials
Late or newly relevant clients receive bOpen = true and can apply the open
pose instead of needing the original "open door" multicast.
Match phase¶
GameState:
MatchPhase = Waiting / Countdown / InProgress / Finished (RepNotify)
RemainingTime replicated separately or in one struct if phase/time must align
Clients:
OnRep_MatchPhase
-> local scoreboard and HUD choose visible controls
The GameState owns public match state. Each client's UI presents it locally.
When it fails, and what failure looks like¶
- The actor does not replicate. A RepNotify variable on a non-replicated actor is just local state for that actor copy.
- The server never set the value. A client changed its local copy, but the authoritative copy did not change, so other machines never learn it.
- The value did not change meaningfully. Setting the same value again is not a reliable event source. Use an explicit sequence/count/state if repeats matter.
- The actor is not relevant yet. A client cannot receive property updates for an actor it has not received or is not currently relevant for.
- You raced two variables. Separate OnRep calls can arrive in an order your graph did not expect.
- You updated UI on the wrong machine. The RepNotify ran on a copy without that local player's widget, or on a dedicated server with no UI.
- You put authority inside OnRep. Clients ran code that should have been server-only, then the server corrected or ignored it.
Debug by printing actor name, net mode, authority/local control, old value if you store one, and new value inside the OnRep/apply helper. If the print appears on the wrong machine, solve role/ownership first.
Lookalikes - which one do I want?¶
| Tool | Use when | Not for |
|---|---|---|
| Replicated variable | Clients need the current value and no immediate callback is needed. | Local presentation that must update exactly when the value arrives. |
| RepNotify variable | Clients need the value and should react locally on arrival/change. | Client requests or server validation. |
| Run on Server RPC | Owning client asks the server to perform gameplay work. | Durable state that clients should be able to read later. |
| Run on Owning Client RPC | Server sends private local feedback to one client. | Public match or player state. |
| Multicast RPC | Server sends a transient event to relevant clients. | State that late clients/relevant-later clients must reconstruct. |
| Event Dispatcher | Local UI/component listeners need a local event. | Crossing the network. |
| Widget binding | Tiny local display expression with no heavy work. | Polling replicated actors every frame because OnRep was skipped. |
The quick path is to Multicast every visible change. The concrete cost is that late joiners and newly relevant clients miss the original event. A RepNotify state value gives them something durable to apply.
Going deeper¶
- Replication basics - the actor replication and RPC model around RepNotify.
- Blueprint Custom Events as RPCs - how clients ask the server to change the state that RepNotify later presents.
- Updating a Widget: Bindings vs Events - local UI refresh patterns that pair well with RepNotify.
- Material Instances and Dynamic Material Instances - updating local dynamic material parameters from replicated state.
- Spawn Niagara System at Location / Attached - spawning local VFX from replicated state without trying to replicate the VFX component as truth.
- Official docs: Replicate Actor Properties, Replicated Object Execution Order, Networking Overview, and Multiplayer in Blueprints.
- Engine source (requires engine access - we do not reproduce it here):
replicated property registration starts from
GetLifetimeReplicatedPropsdeclarations on replicated classes, using helpers fromEngine/Source/Runtime/Engine/Public/Net/UnrealNetwork.h; property replication and RepNotify dispatch are handled through networking code such asFObjectReplicatorinEngine/Source/Runtime/Engine/Private/DataReplication.cppand rep layout logic inEngine/Source/Runtime/Engine/Private/RepLayout.cpp.