Lesson 6: Multiplayer Networking & Replication
Welcome to Advanced Networking!
Great progress! You've built player movement systems. Now it's time to master the networking layer that makes multiplayer games work smoothly. In this lesson, you'll dive deep into Unreal's replication system, learn advanced techniques, and optimize your game for 50-100 concurrent players.
Learning Objectives
By the end of this lesson, you will:
- ✅ Master Unreal's replication system and architecture
- ✅ Implement advanced replication techniques
- ✅ Create reliable RPC systems for client-server communication
- ✅ Optimize network bandwidth for large player counts
- ✅ Handle lag compensation and client prediction
- ✅ Build robust multiplayer synchronization systems
- ✅ Debug and profile network performance
Part 1: Understanding Replication Fundamentals
Step 1: Replication Architecture Deep Dive
1.1 How Replication Works
Unreal's replication follows this flow:
Server (Authority)
↓
Replicates State
↓
Clients (Receive Updates)
↓
Interpolate & Display
Key Concepts:
Authority:
- Server has authority over all game state
- Server validates all actions
- Server replicates state to clients
Replication:
- Server sends state updates to clients
- Clients receive and apply updates
- Updates happen at network update frequency
Network Roles:
- Authority: Server (source of truth)
- Autonomous Proxy: Client controlling this actor
- Simulated Proxy: Other clients' actors
1.2 Replication Properties
Properties marked with Replicated are automatically synced:
UPROPERTY(Replicated)
float Health; // Automatically replicated
UPROPERTY(Replicated, BlueprintReadOnly)
int32 Score; // Replicated and readable in Blueprint
Replication Conditions:
// Replicate always
UPROPERTY(Replicated)
// Replicate only to owner
UPROPERTY(Replicated, ReplicatedUsing = OnRep_Health)
float Health;
// Replicate only on initial spawn
UPROPERTY(Replicated, ReplicatedUsing = OnRep_Initialized)
bool bInitialized;
Step 2: Replication Functions
2.1 GetLifetimeReplicatedProps
This function defines what gets replicated:
void ABRCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// Always replicate
DOREPLIFETIME(ABRCharacter, Health);
DOREPLIFETIME(ABRCharacter, MaxHealth);
// Replicate conditionally
DOREPLIFETIME_CONDITION(ABRCharacter, Score, COND_OwnerOnly);
DOREPLIFETIME_CONDITION(ABRCharacter, TeamID, COND_InitialOnly);
}
Replication Conditions:
COND_None: Always replicateCOND_InitialOnly: Only on initial spawnCOND_OwnerOnly: Only to owning clientCOND_SkipOwner: To all except ownerCOND_SimulatedOnly: Only to simulated proxies
2.2 OnRep Functions
Called when replicated properties change:
UPROPERTY(Replicated, ReplicatedUsing = OnRep_Health)
float Health;
UFUNCTION()
void OnRep_Health()
{
// Called when Health changes on client
UpdateHealthBar();
PlayDamageEffect();
}
Part 2: Advanced Replication Techniques
Step 3: Replication Graph (For Large Player Counts)
3.1 Enable Replication Graph
For 50-100 players, use Replication Graph:
Project Settings:
- Edit → Project Settings
- Engine → Network
- Enable "Use Replication Graph"
3.2 Custom Replication Graph
Create custom replication graph for optimization:
// BRReplicationGraph.h
UCLASS()
class MULTIPLAYERBATTLEROYALE_API UBRReplicationGraph : public UReplicationGraph
{
GENERATED_BODY()
public:
UBRReplicationGraph();
// Grid node for spatial replication
UPROPERTY()
class UReplicationGraphNode_GridSpatialization2D* GridNode;
// Always relevant node
UPROPERTY()
class UReplicationGraphNode_ActorList* AlwaysRelevantNode;
virtual void InitGlobalGraphNodes() override;
virtual void InitConnectionGraphNodes(UNetReplicationGraphConnection* ConnectionManager) override;
};
3.3 Spatial Replication
Optimize replication based on distance:
void UBRReplicationGraph::InitGlobalGraphNodes()
{
// Create grid node for spatial replication
GridNode = CreateNewNode<UReplicationGraphNode_GridSpatialization2D>();
GridNode->CellSize = 10000.0f; // 10km cells
GridNode->SpatialBias = FVector2D(0.0f, 0.0f);
AddGlobalGraphNode(GridNode);
}
Step 4: Replication Frequency Optimization
4.1 Set Update Frequencies
Optimize update frequencies based on importance:
// In character constructor
GetCharacterMovement()->SetNetUpdateFrequency(30.0f); // 30 Hz for movement
// For less critical actors
SomeActor->SetNetUpdateFrequency(10.0f); // 10 Hz for props
4.2 Priority-Based Replication
Set replication priority:
// High priority (replicates more frequently)
SetNetPriority(2.0f);
// Low priority (replicates less frequently)
SetNetPriority(0.5f);
4.3 Custom Replication Conditions
Create custom conditions for optimization:
virtual bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const override
{
// Only replicate if within 5000 units
float Distance = FVector::Dist(SrcLocation, ViewTarget->GetActorLocation());
return Distance < 5000.0f;
}
Part 3: RPC Systems
Step 5: Reliable RPCs
5.1 Server RPCs
Server RPCs are called from client, executed on server:
// Declaration
UFUNCTION(Server, Reliable, WithValidation)
void ServerFireWeapon(FVector Location, FRotator Rotation);
// Implementation
void ABRCharacter::ServerFireWeapon_Implementation(FVector Location, FRotator Rotation)
{
// Validate on server
if (!CanFire())
{
return;
}
// Execute on server
FireWeapon(Location, Rotation);
}
bool ABRCharacter::ServerFireWeapon_Validate(FVector Location, FRotator Rotation)
{
// Validate input
return Location.IsValid() && !Rotation.ContainsNaN();
}
5.2 Client RPCs
Client RPCs are called from server, executed on clients:
// Declaration
UFUNCTION(Client, Reliable)
void ClientTakeDamage(float DamageAmount, FVector HitLocation);
// Implementation
void ABRCharacter::ClientTakeDamage_Implementation(float DamageAmount, FVector HitLocation)
{
// Execute on client
PlayDamageEffect(HitLocation);
UpdateHealthBar();
PlayDamageSound();
}
5.3 NetMulticast RPCs
NetMulticast RPCs execute on server and all clients:
// Declaration
UFUNCTION(NetMulticast, Reliable)
void MulticastPlayExplosion(FVector Location);
// Implementation
void ABRWeapon::MulticastPlayExplosion_Implementation(FVector Location)
{
// Execute on all clients
SpawnExplosionEffect(Location);
PlayExplosionSound(Location);
}
5.4 RPC Best Practices
Reliable vs Unreliable:
- Reliable: Guaranteed delivery (use for important events)
- Unreliable: Best effort (use for frequent updates)
When to Use Each:
- Server RPC: Client actions that need server validation
- Client RPC: Server events that affect client (damage, effects)
- NetMulticast: Events visible to all players (explosions, spawns)
Part 4: Network Optimization
Step 6: Bandwidth Optimization
6.1 Reduce Replication Frequency
Lower frequency for less critical updates:
// Update every 0.1 seconds instead of every frame
SetNetUpdateFrequency(10.0f);
6.2 Compress Data
Use smaller data types when possible:
// Instead of float (32 bits)
UPROPERTY(Replicated)
uint8 HealthPercent; // 0-100 (8 bits)
// Convert when needed
float GetHealth() const { return HealthPercent / 100.0f * MaxHealth; }
6.3 Conditional Replication
Only replicate when needed:
UPROPERTY(Replicated, ReplicatedUsing = OnRep_TeamID)
int32 TeamID;
// Only replicate when team changes
void SetTeam(int32 NewTeam)
{
if (TeamID != NewTeam)
{
TeamID = NewTeam;
MarkDirtyForReplication();
}
}
6.4 Replication Graph Optimization
Use spatial replication for large maps:
// Only replicate actors within view distance
virtual bool IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const override
{
float Distance = FVector::Dist(SrcLocation, ViewTarget->GetActorLocation());
return Distance < MaxReplicationDistance;
}
Step 7: Network Profiling
7.1 Enable Network Stats
In-game network statistics:
// Toggle network stats
static TAutoConsoleVariable<int32> CVarNetStats(
TEXT("net.NetStats"),
0,
TEXT("Show network statistics")
);
7.2 Profile Replication
Use Unreal's network profiler:
- Window → Developer Tools → Session Frontend
- Enable "Network Profiler"
- Record session and analyze replication
7.3 Monitor Bandwidth
Check bandwidth usage:
// Get network stats
FNetworkStats NetworkStats;
GetWorld()->GetNetDriver()->GetNetworkStats(NetworkStats);
UE_LOG(LogTemp, Warning, TEXT("Incoming: %d KB/s, Outgoing: %d KB/s"),
NetworkStats.IncomingBytesPerSecond / 1024,
NetworkStats.OutgoingBytesPerSecond / 1024);
Part 5: Lag Compensation & Prediction
Step 8: Client Prediction
8.1 How Client Prediction Works
- Client predicts action locally
- Client sends action to server
- Server validates and executes
- Server sends correction if needed
- Client reconciles if prediction was wrong
8.2 Movement Prediction
Unreal handles movement prediction automatically:
// Enable prediction
GetCharacterMovement()->SetIsReplicated(true);
GetCharacterMovement()->SetNetUpdateFrequency(30.0f);
8.3 Custom Prediction
For custom actions, implement prediction:
void ABRCharacter::FireWeapon()
{
// Predict locally
if (IsLocallyControlled())
{
PredictFireWeapon();
}
// Send to server
ServerFireWeapon();
}
void ABRCharacter::PredictFireWeapon()
{
// Immediate local feedback
PlayMuzzleFlash();
PlayFireSound();
SpawnProjectile();
}
Step 9: Lag Compensation
9.1 Server-Side Rewind
For hit detection, rewind time:
void ABRWeapon::ServerProcessHit(FVector_NetQuantize HitLocation, float ServerTime)
{
// Rewind to when client fired
float RTT = GetWorld()->GetFirstPlayerController()->GetPlayerState()->GetPing() / 1000.0f;
float RewindTime = ServerTime - RTT;
// Check hit at rewind time
CheckHitAtTime(HitLocation, RewindTime);
}
9.2 Interpolation
Smooth movement between updates:
// Enable interpolation
GetCharacterMovement()->NetworkSmoothingMode = ENetworkSmoothingMode::Linear;
GetCharacterMovement()->NetworkSmoothingServerMaxDistance = 200.0f;
Part 6: Advanced Synchronization
Step 10: State Synchronization
10.1 Replicated State Machines
Synchronize state machines:
UENUM()
enum class EPlayerState : uint8
{
Idle,
Walking,
Sprinting,
Crouching,
Jumping
};
UPROPERTY(Replicated, ReplicatedUsing = OnRep_PlayerState)
EPlayerState CurrentState;
UFUNCTION()
void OnRep_PlayerState()
{
// Update state on client
UpdateState(CurrentState);
}
10.2 Timestamp Synchronization
Synchronize timestamps for events:
UPROPERTY(Replicated)
float ServerTime;
void ABRGameMode::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (HasAuthority())
{
ServerTime = GetWorld()->GetTimeSeconds();
}
}
10.3 Event Replication
Replicate important events:
// Replicate event
UPROPERTY(Replicated, ReplicatedUsing = OnRep_PlayerKilled)
bool bPlayerKilled;
UFUNCTION()
void OnRep_PlayerKilled()
{
// Handle kill event on all clients
PlayKillEffect();
UpdateScoreboard();
}
Mini Challenge: Build Advanced Replication System
Task: Create a comprehensive replication system for your battle royale game.
Requirements:
- Implement Replication Graph for 50-100 players
- Create reliable RPC system for weapon firing
- Optimize bandwidth with conditional replication
- Implement client prediction for actions
- Add lag compensation for hit detection
- Create network profiling tools
- Test with 50+ simulated players
- Achieve <100KB/s per client bandwidth
Deliverables:
- Custom Replication Graph implementation
- Optimized RPC system
- Network profiling results
- Bandwidth optimization report
- Multiplayer testing results
Pro Tips:
- Start with basic replication, optimize later
- Profile network usage early and often
- Test with realistic player counts
- Use Replication Graph for large player counts
- Compress data when possible
- Use conditional replication to reduce bandwidth
- Test with high latency (200ms+) to ensure robustness
Common Mistakes to Avoid
1. Over-Replicating
- ❌ Replicating everything at high frequency
- ✅ Only replicate what's necessary, optimize frequencies
2. Not Validating RPCs
- ❌ Trusting client input without validation
- ✅ Always validate RPCs on server
3. Ignoring Bandwidth
- ❌ Not monitoring network usage
- ✅ Profile and optimize bandwidth regularly
4. Poor Lag Handling
- ❌ Not accounting for network latency
- ✅ Implement client prediction and lag compensation
5. Inefficient Replication
- ❌ Replicating to all clients unnecessarily
- ✅ Use conditional replication and Replication Graph
Troubleshooting
Q: High bandwidth usage with many players A: Use Replication Graph, reduce update frequencies, compress data, use conditional replication.
Q: Laggy movement in multiplayer A: Increase network update frequency, enable network smoothing, check server tick rate.
Q: RPCs not firing A: Check network roles, ensure RPCs are called correctly, verify server/client setup.
Q: Replication not working A: Check bReplicates flag, verify GetLifetimeReplicatedProps, ensure server authority.
Q: Clients see different game state A: Ensure server-authoritative, check replication conditions, verify RPC reliability.
Key Takeaways
✅ Replication System: Master Unreal's replication architecture and properties ✅ Advanced Techniques: Use Replication Graph for large player counts ✅ RPC Systems: Create reliable Server, Client, and NetMulticast RPCs ✅ Optimization: Reduce bandwidth with frequency optimization and compression ✅ Lag Compensation: Implement client prediction and server-side rewind ✅ Synchronization: Synchronize state machines and events across network ✅ Profiling: Monitor and optimize network performance
What's Next?
In Lesson 7: Map Design & Procedural Generation, we'll:
- Design battle royale map layouts
- Implement procedural map generation
- Create spawn point systems
- Design shrinking zone mechanics
- Build loot distribution systems
- Create map landmarks and points of interest
Get ready to build the world where your battle royale takes place!
Additional Resources
- Unreal Engine Replication Documentation
- Replication Graph Guide
- RPC Best Practices
- Network Optimization Guide
Ready to continue? Move on to Lesson 7 to design your battle royale map!