Highlights
- Unity Netcode and Unreal replication use different approaches to authoritative multiplayer networking.
- Client prediction, reconciliation, rollback, and interpolation determine how multiplayer games feel under latency.
- Simulated lag testing in Unity and Unreal helps developers identify desync and hit-registration issues early.
Most single-player games ship with one broken system, and players forgive it. A multiplayer game ships with broken netcode, and the community never lets it go. Teleporting characters, shots that register on one screen but miss on another, and health bars that disagree across two clients are not edge cases.
They are what happens when a developer skips the foundational decisions that multiplayer game development demands before writing a single networked function.
This guide addresses those decisions head-on, starting with topology and the five concepts that determine how a game feels under latency. It then moves into two working implementations using Unity networking and Unreal multiplayer, with latency simulation built into the testing workflow.
The Topology Decision That Shapes Everything
Every multiplayer game begins with one architectural question: Which machine holds the authoritative game state?
In a client-server model, one dedicated machine runs the simulation. Players send their inputs to that server, the server determines the outcome, and every connected client receives the result. No client can unilaterally declare its own position or health value valid. This structural constraint makes cheating significantly harder, and places lag compensation, anti-cheat enforcement, and state management in a single controlled location.
As the Unreal Engine networking documentation explains, the default model in Unreal is server-authoritative, meaning the server always has authority over game state and information always flows from the server to clients. The cost is infrastructure: someone has to run that server.
In a peer-to-peer (P2P) model, each player connects directly to every other player. The most direct route between two machines often produces the lowest raw ping, which benefits reaction-sensitive genres. The tradeoff is significant. Without a single authoritative entity, clients can desynchronize, leaving two players running different versions of the game world. Cheating is also structurally easier because any peer can manipulate its own reported state.
A detailed breakdown of network topologies by a software engineer at Google, Yuan Gao, notes that P2P introduces no single authoritative host, making lag compensation harder and desynchronization a real risk. For most projects, a client-server architecture is the more defensible starting point.
Multiplayer Game Development: Five Concepts That Determine Feel
Architecture is the skeleton. These five concepts determine whether the game feels smooth or broken to a player 200 milliseconds (MS) away from the server.
Client Prediction: Even a 60ms round-trip produces visible input lag if the client waits for server confirmation before displaying movement. Client prediction solves this by letting the local client apply its own input immediately and render the result without waiting. The client stores a history of unconfirmed inputs while the server processes and returns its authoritative verdict.
A naive implementation without prediction leaves the player pressing a key and watching the character take half a second to respond. As Gabriel Gambetta's widely referenced series on fast-paced multiplayer establishes, that kind of delay is unacceptable in any real-time genre.
Server Reconciliation: When the server's authoritative position arrives, it rarely matches the client's prediction. Physics rounding, timing differences, and dropped packets all introduce small divergences. Reconciliation is the process of comparing the server state against the stored prediction history, identifying the diverging frame, and re-simulating forward from the corrected state using the inputs the client retained.
Gambetta's article on client-side prediction and server reconciliation walks through how the client discards acknowledged inputs and re-applies only those that the server has not yet processed. Done correctly, the player sees nothing. Done poorly, they see rubber-banding.
Lag Compensation: This addresses a fairness problem on the server side. When a player fires a weapon, the server processes the shot after a network delay, and the target has already moved during that time. Lag compensation rewinds the server's game state to what the shooting client's screen showed at the moment of the shot, runs the hit check against that rewound snapshot, and returns to the present.
Gambetta's lag compensation article describes this as the mechanism behind moments where a player appears to die after reaching cover: the server's rewound state placed them in the open.
Rollback: Rollback is a prediction strategy most associated with fighting games. Rather than inserting an artificial delay to wait for remote input, the local client predicts what the opponent did, advances the simulation, and renders immediately. When real remote input arrives and contradicts the prediction, the client restores the last fully confirmed game state, applies correct inputs across the missed frames, and re-simulates to the present.
SnapNet's breakdown of rollback netcode describes this correction as needing to happen within a single frame to remain invisible to the player. Under high jitter, brief visual snaps can appear, though rollback still outperforms pure delay-based solutions for most real-world connections, as Edgegap's analysis of input delay versus rollback confirms.
Entity Interpolation: While client prediction handles the local player's movement, other players on screen still appear to stutter without an additional technique. Entity interpolation solves this by rendering remote characters slightly in the past, smoothly blending between the last two known server positions rather than snapping to each new update as it arrives. The result is fluid movement for all players in the session, not just the one holding the controller.
Gambetta's article on entity interpolation explains that the tradeoff is a small, deliberate delay in how remote entities are displayed, which is invisible to most players under normal network conditions.
Unity Networking Implementation: Two-Player Movement Demo
Unity networking through Netcode for GameObjects (NGO) is the officially supported high-level library for Unity 6 and the 2024 LTS release. As documented on the Unity Netcode features page, NGO abstracts the transport layer to help developers manage gameplay in variable latency environments, letting a netcode beginner focus on game logic rather than raw socket management.
Step 1 - Install the Packages:
Navigate to Window, then Package Manager, click the + icon, and select Add package by name. Enter com.unity.netcode.gameobjects for the logic core and com.unity.transport for the connection layer. Both appear under Unity Registry. Full setup instructions are available in Unity's official multiplayer session guide.
Step 2 - Add a NetworkManager:
Create an empty GameObject named NetworkManager and attach the NetworkManager component via Add Component, then Netcode. Assign Unity Transport as the transport. The NetworkManager is the central singleton hub for all Unity networking settings and connection flow, as described in Unity Learn's Netcode for GameObjects tutorial.
Step 3 - Create the Networked Player:
Create a Capsule, name it Player, and attach a NetworkObject component. NetworkObject is required to give the GameObject a unique network identity across all clients. Instead of the default NetworkTransform, attach a ClientNetworkTransform component, available in the NGO samples package. The default NetworkTransform is server-authoritative, meaning the server owns and controls the position. Using it with client-side movement code will cause the server to constantly snap the player back to its last-known, authoritative position. ClientNetworkTransform transfers that authority to the owning client, allowing the script below to work correctly.
Create a script called PlayerMovement inheriting from NetworkBehaviour:
using Unity.Netcode; using UnityEngine;
public class PlayerMovement : NetworkBehaviour { [SerializeField] private float speed = 5f;
void Update()
{
// Strictly required: prevents controlling other players
if (!IsOwner) return;
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 move = new Vector3(h, 0, v) * speed * Time.deltaTime;
transform.Translate(move);
}
}
The IsOwner guard is the most important line here. Without it, every client processes input for every player in the session simultaneously. Drag the Player into the Project panel to create a prefab, then assign it as the Player Prefab from the Project folder into the Player Prefab slot on the NetworkManager component.
Note: ClientNetworkTransform grants movement authority to the client, which is suitable for a beginner demo. In a production game where cheating prevention matters, the industry-standard approach is to keep NetworkTransform server-authoritative and send movement requests to the server using a ServerRPC, where the server validates and applies the position.
Step 4 - Connect Two Players:
Create a Canvas with a Host button wired to NetworkManager.Singleton.StartHost() and a Join button wired to NetworkManager.Singleton.StartClient(). The host runs a combined server and client. The joining player calls StartClient, targeting the host's IP address.
Step 5 - Test Locally:
In Unity 6, open Window, then Multiplayer, followed by Multiplayer Play Mode, and enable Player 2. Two Editor instances share the same project. Press Play in both, click Host in one, and Join in the other. Both capsules should appear and respond to independent input, confirming the Unity networking setup works correctly.
Unreal Multiplayer Implementation: A Basic Replicated Shooter
Unreal's replication system distributes property changes and function calls from the server to clients through annotations in C++ or Blueprint Class Defaults. The Unreal Engine networking and multiplayer documentation describes replication as the higher-level abstraction that powers the engine's client-server architecture across some of the world's most popular online games.
Step 1 - Enable Replication:
In the Actor constructor, set bReplicates = true and bReplicateMovement = true. In Blueprint, enable both flags in the Class Defaults panel. As covered in Unreal's property replication guide, any Actor without these flags will not participate in replication regardless of what else is configured.
Step 2 - Replicate a Health Property:
Declare the property with the Replicated specifier and register it in GetLifetimeReplicatedProps using the DOREPLIFETIME macro. Only the server writes Health. Every connected client receives the updated value automatically and cannot overwrite it.
Step 3 - Handle Shooting with a Server RPC:
Clients have no authority to cause damage. They request it through a Server RPC marked Reliable, which guarantees delivery. According to Unreal's function replication documentation, reliable calls are guaranteed to occur, while unreliable calls may be dropped during heavy traffic. Use Unreliable for high-frequency position updates where occasional packet drops are preferable to the overhead of guaranteed delivery.
Step 4 - Broadcast Cosmetic Effects with Multicast:
After a confirmed hit, broadcast a muzzle flash or impact effect using a Multicast RPC marked Unreliable. Multicast RPCs run on the server and every connected client and are appropriate for visuals that carry no game-state consequences. The Unreal networking overview advises using Multicast functions sparingly, as they create extra network traffic for each connected client in a session.
Step 5 - Test with Two PIE Instances:
Set the Editor toolbar to 2 Players and Net Mode to play as a listen server. Two viewports are open. Fire in one and confirm health updates in the other, verifying that Unreal multiplayer replication is functioning correctly.
How to Test Unity and Unreal Multiplayer Netcode With Simulated Lag
Local network testing at sub-1ms ping hides every networking flaw. Three practical options cover most development setups.
For Unity networking, the Multiplayer Tools package includes a Network Simulator component. Add it to any GameObject that persists across scenes, and configure simulated latency of around 80ms, jitter of plus or minus 20ms, and packet loss of 5%. The tool also ships with built-in presets covering home broadband, home fiber, home cable, and mobile connections ranging from 2.5G through to 5G, so manual configuration is optional for standard test scenarios.
Run the two-player demo under these settings to surface snapping or reconciliation failures before any real players encounter them.
For Unreal multiplayer, go to Edit, then Project Settings, then Network Emulation to define a latency profile directly in the Editor. In UE5, the correct console command for applying a delay at runtime is NetEmulation.PktLag 100 with no equals sign, as the older net PktLag=100 syntax from UE4 is no longer recognized, per Epic's console debugging documentation. Setting values between 150 and 200ms stress-tests lag compensation logic and confirms whether shots register correctly from the server's perspective.
At the operating system level, the tc qdisc command on Linux applies network conditions system-wide across all processes simultaneously. On Windows, Clumsy provides a GUI equivalent that developers use to simulate lag without touching game code. On macOS, Network Link Conditioner ships with Apple's Hardware IO tools and covers the same use case.
Any of these tools will expose timing assumptions baked into netcode that only surface under realistic delay conditions.
The Road Ahead in Multiplayer Game Development
A two-player movement demo and a basic replicated shooter cover the ground a netcode beginner needs to ship a small-scale game. The next layer involves interest management, which limits state updates to clients who can actually see the relevant objects, matchmaking through Unity Gaming Services or Epic Online Services, and bandwidth reduction via delta compression.
For larger Unreal multiplayer projects, the Replication Graph plugin scales authoritative server replication to hundreds of simultaneous players. As documented in Epic's Replication Graph overview, Fortnite Battle Royale begins each session with 100 connected players and around 50K replicated actors, a load that the standard replication strategy cannot handle without this system.
For Unity networking at high player counts, the Netcode for Entities framework introduces deterministic simulation suited to competitive environments.
Sound architecture, applied consistently, is what keeps every player in the same version of the game world. Everything else follows from that.

