Lesson 7: Lag, Prediction, and Reconciliation Primer (Server-Authoritative Feel)
If your multiplayer slice only feels good on localhost, players will call it broken even when replication is technically correct.
This lesson gives you a small, practical pattern: the client predicts immediate movement feedback, the server remains authoritative, and periodic reconciliation corrects drift without visible snapping.
Lesson objective
By the end of this lesson, you will have:
- A lightweight prediction loop for local responsiveness
- A server-state snapshot path for authoritative correction
- Reconciliation thresholds that avoid over-correction jitter
- A repeatable latency test routine for 50ms, 120ms, and 200ms
Why this matters
Players judge control quality in the first 30 seconds. If every action waits on round-trip latency, your game feels unresponsive. If you trust clients too much, you create exploit surface.
Prediction plus reconciliation is the middle path: responsive feel plus server truth.
Core model in one sentence
Client predicts now, server validates soon, client reconciles gently.
Use that sentence as your review checklist for every movement or combat action in this vertical slice.
Step-by-step implementation pass
Step 1 - Separate input sampling from authoritative simulation
Keep clear roles:
- Client samples local input each tick
- Client performs short-horizon visual prediction
- Server runs authoritative simulation and emits canonical state
Do not mix those responsibilities into one monolithic script.
Step 2 - Tag inputs with sequence numbers
Each client input packet should include:
inputSequence- Tick or timestamp
- Compressed intent values (move axis, jump, action)
Sequence IDs let you compare "what client predicted" versus "what server accepted."
Step 3 - Replay unacknowledged inputs after correction
When server state arrives:
- Move client state toward authoritative state
- Reapply unacknowledged inputs after that baseline
- Keep correction smoothing bounded to avoid rubber-banding
This is the heart of reliable reconciliation.
Step 4 - Add correction thresholds
Use two thresholds:
- Minor error threshold for smooth interpolation
- Major error threshold for immediate snap-to-authority
Without thresholds, you either jitter constantly or hide large divergence too long.
Step 5 - Run a latency matrix test
Test at least:
- 50ms stable latency
- 120ms stable latency
- 200ms with occasional jitter spikes
Capture one short clip per case and note:
- Input feel rating
- Visible correction frequency
- Largest observed positional error
Example pattern - prediction buffer with server reconciliation
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
public class PredictedMover : NetworkBehaviour
{
private struct InputFrame
{
public int sequence;
public Vector2 move;
}
private readonly Queue<InputFrame> _pendingInputs = new();
private int _nextSequence;
private Vector3 _predictedPosition;
private const float MinorErrorThreshold = 0.15f;
private const float MajorErrorThreshold = 1.0f;
private void Update()
{
if (!IsOwner) return;
var input = new Vector2(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"));
var frame = new InputFrame { sequence = _nextSequence++, move = input };
_pendingInputs.Enqueue(frame);
// Local prediction for responsiveness.
_predictedPosition += new Vector3(input.x, 0f, input.y) * Time.deltaTime * 5f;
transform.position = _predictedPosition;
SubmitInputServerRpc(frame.sequence, input);
}
[ServerRpc]
private void SubmitInputServerRpc(int sequence, Vector2 move, ServerRpcParams rpcParams = default)
{
// Server applies authoritative movement and sends snapshot back.
}
[ClientRpc]
public void ReceiveAuthoritativeStateClientRpc(int ackSequence, Vector3 serverPosition)
{
while (_pendingInputs.Count > 0 && _pendingInputs.Peek().sequence <= ackSequence)
{
_pendingInputs.Dequeue();
}
var error = Vector3.Distance(_predictedPosition, serverPosition);
if (error > MajorErrorThreshold)
{
_predictedPosition = serverPosition;
}
else if (error > MinorErrorThreshold)
{
_predictedPosition = Vector3.Lerp(_predictedPosition, serverPosition, 0.5f);
}
}
}
Keep this as a primer implementation. You can evolve it later for full rollback or advanced hit validation.
Mini challenge
Create a lag-test-notes.md file for your project and log one pass for each latency tier (50/120/200ms).
For each tier, record:
- One thing that still feels responsive
- One correction artifact you observed
- One practical tweak you will test next sprint
Pro tips
- Use a fixed networking tick and keep simulation deterministic where possible.
- Reconcile position and velocity together for cleaner motion.
- Debug with on-screen sequence IDs so drift analysis is visible during playtests.
Common mistakes
- Predicting gameplay outcomes the server never validates
- Applying reconciliation every frame without thresholds
- Ignoring packet jitter and only testing fixed latency
Troubleshooting
"Movement feels sticky at 120ms even with prediction."
Check that local visual prediction runs immediately from input, not after waiting for outgoing RPC confirmation.
"Players snap backward too often."
Your minor threshold may be too low, or server snapshots may be too infrequent for your movement speed.
"Desync grows during long strafes."
Verify sequence acknowledgment and replay logic. Missing or out-of-order input cleanup often causes accumulating drift.
FAQ
Should I predict everything or only movement?
For a vertical slice, start with movement and low-risk interactions. Predicting every system too early can hide authority bugs.
Is reconciliation the same as rollback netcode?
No. This lesson uses forward prediction plus correction. Full rollback requires deterministic re-simulation of prior states.
Do I need dedicated servers before this works?
No. The same pattern works in host-authoritative tests. Dedicated hosting changes topology, not the core prediction concept.
Recap
You now have a practical latency baseline: local prediction for feel, server authority for trust, and reconciliation for alignment.
Next lesson teaser
Lesson 8 moves from simulation feel to player flow: lobby and join code UX, so players can actually find and start sessions reliably.
Related links
- Lesson 6: Scene Management with Networking
- Unity Netcode Client-Side Interpolation and Prediction Concepts
- Unity Guide - Multiplayer Networking Chapter
Save your latency notes. They become your baseline evidence before adding more complex combat networking.