Quick answer: Make sure you override GetLifetimeReplicatedProps and call DOREPLIFETIME_CONDITION for the right property. Verify the actor has an owning PlayerController for COND_OwnerOnly to work.
You added a private inventory array to a player’s pawn and used DOREPLIFETIME_CONDITION(AMyPawn, Inventory, COND_OwnerOnly) to keep it from leaking to opponents. Open a 4-player session, log Inventory contents from each client — every client sees every player’s inventory. The condition is being ignored.
How Replication Conditions Work
Each replicated property has a per-frame check the engine performs against every connection. COND_OwnerOnly resolves “owner” via AActor::GetNetOwner, which walks up the owner chain to a APlayerController. If the actor has no owning PlayerController, “owner” is the server (or null), so the engine sends the property to nobody — or to everyone, depending on the condition.
Common ways to break this:
GetLifetimeReplicatedPropsisn’t actually being called (typo in signature, no override marker).- The actor is never assigned an owner on the server.
- The property declaration is missing
UPROPERTY(ReplicatedUsing=...)orReplicated. - The actor itself has
bReplicates = false.
Step 1: Correct Replication Registration
// MyPawn.h
UCLASS()
class AMyPawn : public APawn
{
GENERATED_BODY()
public:
AMyPawn() { bReplicates = true; }
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const override;
protected:
UPROPERTY(Replicated)
TArray<FName> Inventory;
};
// MyPawn.cpp
void AMyPawn::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const
{
Super::GetLifetimeReplicatedProps(Out);
DOREPLIFETIME_CONDITION(AMyPawn, Inventory, COND_OwnerOnly);
}
The const override on the function signature must match exactly — if it’s missing override and the parent signature changes, your method silently stops being called.
Step 2: Ensure Server Owner Is Assigned
When the player spawns, the server assigns the pawn to the PlayerController via possession. Verify with a log:
// On the server, after spawn
APawn* P = ...;
P->SetOwner(PlayerController);
UE_LOG(LogTemp, Log, TEXT("Pawn owner: %s"),
P->GetOwner() ? *P->GetOwner()->GetName() : TEXT("none"));
If the log says “none”, the PlayerController didn’t take ownership. Without ownership, COND_OwnerOnly excludes the property from every client — including the one you intended to send it to. In practice this often means the inventory looks empty on all clients, which is the inverse of the original bug.
Step 3: Confirm the Condition Macro
Common confusion table:
- COND_None — always replicate (the same as plain DOREPLIFETIME).
- COND_OwnerOnly — only to the owning connection.
- COND_SkipOwner — everyone except the owner.
- COND_SimulatedOnly — to non-owning clients only (same idea as SkipOwner but with autonomous role considered).
- COND_AutonomousOnly — only to the autonomous proxy.
- COND_InitialOnly — one-shot, sent at first replication only.
Step 4: Use Network Profiler
From the console on the server:
NetProfile start
// play for 30s
NetProfile stop
Open the saved profile in Tools → Profiling → Network Profiler. You’ll see per-property bandwidth and per-connection deltas. If Inventory shows up under more clients than expected, the condition isn’t being honored.
Verifying
Add a log to OnRep_Inventory and run 4 clients. Only the owning client should log inventory contents. Other clients should never call OnRep. If they do, double-check ownership and condition setup.
“Replication conditions need three correct things: registration, ownership, and the right macro. One missing piece = condition ignored.”
For player-private data, COND_OwnerOnly + SetOwner is the safe combination. Verify owner is set before any replication starts.