Quick answer: OnRep functions fire on changes, and the initial replicated value does trigger OnRep on the client — but only if it differs from the CDO default. Apply initial state explicitly in BeginPlay too.
A weapon’s replicated Ammo drives a UI via OnRep_Ammo. On a freshly-joined client the UI shows nothing — OnRep didn’t run for the value that was already set.
How Initial Replication Works
When an actor first replicates to a client, OnRep fires for replicated properties whose value differs from the class default. If your initial value equals the default (e.g. Ammo defaults to 30 and starts at 30), OnRep won’t fire — there’s “no change”.
Apply Initial State Yourself
void AWeapon::BeginPlay()
{
Super::BeginPlay();
// clients: push current replicated state into the UI now
OnRep_Ammo();
}
Calling the OnRep manually in BeginPlay guarantees the client’s UI reflects the current value, regardless of whether the change-detection fired.
OnRep Should Be Idempotent
Because OnRep can run from initial replication and your manual call, write it so running twice is harmless — it should just “apply the current value”, not toggle or increment.
Don't Confuse with RepNotify Timing
On a client, the property is already updated by the time OnRep runs — read the member directly inside OnRep, don’t expect a “new value” parameter unless you declared one.
Verifying
A client joining mid-game sees correct ammo immediately. Subsequent ammo changes update via OnRep. Running OnRep twice produces no glitches.
“OnRep fires on change, not guaranteed for initial state. Call it from BeginPlay and keep it idempotent.”
Make every OnRep a pure ‘apply current value’ function — then calling it from BeginPlay, on join, or on change all just work.