Chapter 16

Multiplayer: Client vs. Server

Why an Actor is really several copies kept in sync, authority and roles, who lives where, replicated variables and RPCs, reliability, prediction, lag compensation, and the gotchas that fail silently.

In this chapter

  1. The Core Mental Model & Actor Roles
  2. Which Machines Have Which Actors
  3. The Class Ownership Table
  4. Replicated Variables
  5. RPCs & Reliability
  6. Authoritative vs. Cosmetic: Prediction
  7. Lag Compensation & Server Rewind
  8. Gotchas & Testing
1

The Core Mental Model & Actor Roles

An Actor is not one thing. It is a set of copies of the same thing, living on different machines, that Unreal works to keep looking similar. In a multiplayer match there is no single Character in the world — there is one copy on the server and one on each client that can currently see it. Same class, same code, each with its own separate copy of every variable.

One copy is the authority (normally the server's). It decides things. The others are proxies: they can run code, play animations, and spawn effects, but anything they decide gets overwritten by the next update from the server.

The habit to build: on every line you write, ask "which copies run this, and which of them is the authority?" Once that is automatic, most of the rest is syntax.
ServerClient
AuthorityOwns game state — health, position, scoreOwns local input, cosmetics, UI
TrustAlways trustedNever trusted for game state
ActorsHas all actorsOnly the relevant ones

Actor Roles

RoleMeaning
ROLE_AuthorityI am the real one. My values are the truth.
ROLE_AutonomousProxyA client's copy of something that client controls (your own pawn).
ROLE_SimulatedProxyA client's copy of something someone else controls.

One Character in a session with a server and two clients has all three roles at once: the server's copy is Authority, the owning player's copy is AutonomousProxy, the other player's copy is SimulatedProxy. Same graph, three behaviors.

Two Checks, Two Questions

All four combinations occur. A listen server host's pawn is both. A dedicated server's copy is authority but not locally controlled. A client's own pawn is locally controlled but not authority. A client's copy of someone else's pawn is neither.

if (HasAuthority())        { Health -= FinalDamage; CheckForDeath(); }
if (IsLocallyControlled()) { PlayHitVignette(); UpdateHealthWidget(); }

The common pattern is branching on Has Authority in BeginPlay, to split server-only setup from client-only setup.


2

Which Machines Have Which Actors

If an Actor exists on five machines, BeginPlay fires five times and Tick runs on all five, independently and unsynchronized. So Event BeginPlay → Spawn Actor on a server plus four clients runs five times. This is why "spawn on the server only" is a standing rule.

Three things decide whether an actor exists on a given machine:

  1. The Replicates flag. Off means the actor is local to whichever machine spawned it.
  2. Who spawned it. Server-spawned replicated actors get copies on relevant clients automatically. Client-spawned actors are local to that client forever, regardless of the checkbox. Clients cannot create actors on other machines.
  3. Relevancy. Even replicated actors are not sent to everyone. Net cull distance means a distant player is genuinely not replicated to you.

Exception: input events only fire on the machine where a human is pressing the button. Those do not multiply.


3

The Class Ownership Table

ClassLives on
GameModeServer only. Clients do not have one at all.
GameStateServer plus every client.
PlayerStateServer plus every client. One per player, everyone sees everyone's.
PlayerControllerServer plus ONLY the owning client.
Pawn / CharacterServer plus every client where relevant.
HUD / UMG WidgetsOwning client only.
GameInstanceEvery machine, one each, never replicated.
The GameMode row catches everyone: on a client GetGameMode() returns null because the class does not exist there.

The compressed version: GameMode holds the rules. GameState holds the facts those rules produce. PlayerState holds anything that has to survive the pawn dying.

Practical consequence: to display a teammate's ammo or score you cannot read their PlayerController, because you do not have it. It has to live on their PlayerState.

AI Runs Server-Side

AI Controllers and Behavior Trees run on the server only. Plan AI logic accordingly.

This is the fact underneath the AI Manager architecture in Chapter 5: since clients never run the AI, the manager is pure server-authoritative logic, and only its results need to reach clients.


4

Replicated Variables

Blueprint: the variable's Replication dropdown (None / Replicated / RepNotify). C++, both pieces required:

UPROPERTY(ReplicatedUsing = OnRep_Health)
float Health;

void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& Out) const
{
    Super::GetLifetimeReplicatedProps(Out);
    DOREPLIFETIME(AMyCharacter, Health);
}
Omitting the variable from GetLifetimeReplicatedProps is the most common "why is this not replicating" cause, and it fails silently.

Replication Flows One Direction Only

Server to client, never the reverse. If a client sets a replicated variable, the change happens locally, nothing is sent, and the next server update silently overwrites it. No error. Works perfectly on a listen server and fails only on real clients, which is what makes it so hard to catch.

It Sends State, Not Events

The server sends the current value when it differs from what it last sent. Intermediate values can be skipped: 100 → 80 → 60 in one frame will likely arrive as 100 → 60. So variables are for "what is true right now," RPCs are for "this moment happened." Watching a health variable to detect hits will miss hits.

RepNotify Is How Clients React

OnRep_ runs on clients when the value arrives. Cosmetics go here: health bars, shield break effects, ragdoll on death.

OnRep_ does not fire on the server, so on a listen server the host's own UI will not update. Standard fix is for the server to call it manually:
void AMyCharacter::SetHealth(float NewHealth)
{
    Health = NewHealth;
    OnRep_Health();   // clients get it via replication
}

Restrict Who Receives It

With DOREPLIFETIME_CONDITION(..., COND_OwnerOnly). Ammo counts, damage numbers, and private state should be owner-only. Bandwidth saving plus a small anti-cheat win, since data you never send cannot be read out of memory.


5

RPCs & Reliability

Variables move state down. RPCs move requests and events around. An RPC is called on one machine and executes on a different one.

TypeDirection
ServerA client asks the server to run something
ClientThe server tells the owning client to run something
NetMulticastThe server tells all clients to run something

Server RPC

UFUNCTION(Server, Reliable, WithValidation)
void Server_RequestFire(FVector_NetQuantize AimPoint, float ClientTimestamp);

Server RPCs require ownership. The calling client must own the actor, via a chain leading back to that client's PlayerController. Wrong ownership means the call is silently dropped. The _Validate function is the anti-cheat gate; returning false kicks the player. Validate what is physically possible: weapon equipped, fire rate plausible, aim point sane.

Client RPC

The server tells one specific client — UFUNCTION(Client, Reliable). Runs on the owning client only. Correct for hit markers, damage direction, personal damage numbers.

Multicast RPC

The server tells everyone relevant — UFUNCTION(NetMulticast, Unreliable). Correct for shared cosmetics. Called from a client it just runs locally and goes nowhere. Scales badly by nature since one call becomes N sends.

Blueprint: set Replicates in the function or event Details panel. Note that Blueprint functions cannot be Server RPCs; use a custom event.

Reliable vs. Unreliable

Reliable guarantees arrival and ordering by resending until acknowledged. Unreliable is sent once; if it drops it is gone.

The guarantee costs something: unacknowledged reliable RPCs sit in a fixed-size per-connection queue. Generate them faster than they are acknowledged, which is easy on a poor connection or when calling one every frame, and the queue overflows. At that point the engine cannot keep its ordering promise, so it disconnects that client. Not a warning, a kick. Worst with reliable multicasts, where one call queues an entry on every connection at once.

Test: if missing one instance is invisible a second later, it is unreliable.


6

Authoritative vs. Cosmetic: Prediction

Server-authoritative

Anything that affects the outcome: damage, health, scoring, spawning, hit registration, the result of an ability.

Must be decided or validated on the server, so every player agrees and cheating is blocked.

Cosmetic, client-side

Feedback that doesn't change the outcome: muzzle flash, footstep audio, UI animation, particles.

Can run locally for responsiveness. If a client fakes it, there's no competitive impact.

Why You Can't Just Run Ability Logic on the Client

Designing the split well gives you both a responsive feel and fair, consistent outcomes. GAS (Chapter 15) formalizes exactly this, with prediction, server validation, and Gameplay Cues for the cosmetic layer.

Predict the Feel, Never the Consequence

Predict the feel, never predict the consequence.

Predict freely: muzzle flash, sound, recoil, animation, camera shake, cosmetic tracers, UI press states. All fire locally and immediately.

Never predict: health, ammo, kills, score, objective capture. A wrong prediction about a consequence forces a visible rewind in front of the player, which feels worse than a short delay.

Movement is the exception. The Character Movement Component has built-in client prediction and server correction, because unpredicted movement is unplayable. It is the hardest problem in multiplayer and already solved, hence the advice not to write your own.

The Five-Step Framework

Every networked feature decomposes into five steps:

StepWhereWhat
1. InputLocal clientPlayer presses the button
2. PredictionLocal clientImmediate cosmetic feedback
3. RequestClient to server (RPC)"I would like to do this"
4. DecisionServer, authoritativeValidate, resolve, change real state
5. PropagationServer to clientsVariables for state, RPCs for moments

A single shot: client fires, plays local cosmetics immediately, sends Server_RequestFire with a timestamp. Server validates, rewinds hitboxes to that timestamp, traces, resolves damage, changes Health. Health replicates down and OnRep_Health drives health bars. Client_ConfirmHit sends the hit marker to the shooter alone. Multicast_PlayImpactEffect (unreliable) sends sparks to everyone nearby.

Steps 1-2 are feel, 3-4 are truth, 5 is presentation.


7

Lag Compensation & Server Rewind

Server authority means what you see on screen is the past. With 80ms of latency the enemy is drawn where they were 80ms ago, so by the time your fire message arrives they have moved, and a naive server calls your perfect headshot a miss.

Lag compensation fixes this: the server keeps a short history of hitbox positions, and when a shot arrives it rewinds relevant hitboxes to the shooter's timestamp, re-runs the check against the world the shooter actually saw, then restores everything.

This is why any damage event struct needs a tick or timestamp field. Without it, rewind is impossible.

Rewind Favors the Shooter

If you ducked behind cover but the shooter's screen still showed you exposed, you die behind cover. Not a bug, a chosen tradeoff. The alternative is shooters missing visually perfect shots, which players hate more.

Peeker's Advantage

This follows directly: the player rounding a corner sees the defender first, because the defender's screen shows information a round trip old. Every competitive shooter picks a point on this dial, whether by capping the rewind window, tuning it per weapon, or designing map geometry with fewer high-value corners.


8

Gotchas & Testing

Networking bugs are invisible in single-player PIE. Set Number of Players to 2 or 3 and Net Mode to Play As Client, not Listen Server. On a listen server the host is the authority, so every "client tried to change a replicated variable" bug invisibly works and ships. Add latency, because on localhost everything is instant:

Net PktLag=100
Net PktLagVariance=20
showdebug net

Interview drill

Answer each out loud before re-reading the chapter.

  1. What does "replication" mean and why does it matter?
  2. What is the difference between a server-authoritative action and a cosmetic client-side one? Why can't you just run ability logic on the client?
  3. A designer says their pickup spawns multiple times in multiplayer. What do you check first?
  4. Hit marker for the shooter, impact decal for everyone else. Which RPC types, and why not one multicast?
  5. Walk through, machine by machine, what happens when a player shoots another player.
← Chapter 15 ↑ Index Chapter 17 →