Multiplayer Testing Guide
Overview
Multiplayer testing validates network code, synchronization, and the player experience under real-world conditions. Network bugs are notoriously hard to reproduce—systematic testing is essential.
Test Categories
Synchronization Testing
| Test Type |
Description |
Priority |
| State sync |
All clients see consistent game state |
P0 |
| Position sync |
Character positions match across clients |
P0 |
| Event ordering |
Actions occur in correct sequence |
P0 |
| Conflict resolution |
Simultaneous actions handled correctly |
P1 |
| Late join |
New players sync correctly mid-game |
P1 |
Network Conditions
| Condition |
Simulation Method |
Test Focus |
| High latency |
200-500ms delay |
Input responsiveness |
| Packet loss |
5-20% drop rate |
State recovery |
| Jitter |
Variable delay |
Interpolation smoothness |
| Bandwidth limit |
Throttle to 1Mbps |
Data prioritization |
| Disconnection |
Kill connection |
Reconnection handling |
Test Scenarios
Basic Multiplayer
SCENARIO: Player Join/Leave
GIVEN host has started multiplayer session
WHEN Player 2 joins
THEN Player 2 appears in host's game
AND Player 1 appears in Player 2's game
AND player counts sync across all clients
SCENARIO: State Synchronization
GIVEN 4 players in match
WHEN Player 1 picks up item at position (10, 5)
THEN item disappears for all players
AND Player 1's inventory updates for all players
AND no duplicate pickups possible
SCENARIO: Combat Synchronization
GIVEN Player 1 attacks Player 2
WHEN attack hits
THEN damage is consistent on all clients
AND hit effects play for all players
AND health updates sync within 100ms
Network Degradation
SCENARIO: High Latency Gameplay
GIVEN 200ms latency between players
WHEN Player 1 moves forward
THEN movement is smooth on Player 1's screen
AND other players see interpolated movement
AND position converges within 500ms
SCENARIO: Packet Loss Recovery
GIVEN 10% packet loss
WHEN important game event occurs (goal, kill, etc.)
THEN event is eventually delivered
AND game state remains consistent
AND no duplicate events processed
SCENARIO: Player Disconnection
GIVEN Player 2 disconnects unexpectedly
WHEN 5 seconds pass
THEN other players are notified
AND Player 2's character handles gracefully (despawn/AI takeover)
AND game continues without crash
Edge Cases
SCENARIO: Simultaneous Actions
GIVEN Player 1 and Player 2 grab same item simultaneously
WHEN both inputs arrive at server
THEN only one player receives item
AND other player sees consistent state
AND no item duplication
SCENARIO: Host Migration
GIVEN host disconnects
WHEN migration begins
THEN new host is selected
AND game state transfers correctly
AND gameplay resumes within 10 seconds
SCENARIO: Reconnection
GIVEN Player 2 disconnects temporarily
WHEN Player 2 reconnects within 60 seconds
THEN Player 2 rejoins same session
AND state is synchronized
AND progress is preserved
Network Simulation Tools
Unity
// Using Unity Transport with Network Simulator
using Unity.Netcode;
public class NetworkSimulator : MonoBehaviour
{
[SerializeField] private int latencyMs = 100;
[SerializeField] private float packetLossPercent = 5f;
[SerializeField] private int jitterMs = 20;
void Start()
{
var transport = NetworkManager.Singleton.GetComponent<UnityTransport>();
var simulator = transport.GetSimulatorParameters();
simulator.PacketDelayMS = latencyMs;
simulator.PacketDropRate = (int)(packetLossPercent * 100);
simulator.PacketJitterMS = jitterMs;
}
}
// Test
[UnityTest]
public IEnumerator Position_UnderLatency_ConvergesWithinThreshold()
{
EnableNetworkSimulation(latencyMs: 200);
// Move player
player1.Move(Vector3.forward * 10);
yield return new WaitForSeconds(1f);
// Check other client's view
var player1OnClient2 = client2.GetPlayerPosition(player1.Id);
var actualPosition = player1.transform.position;
Assert.Less(Vector3.Distance(player1OnClient2, actualPosition), 0.5f);
}
Unreal
// Using Network Emulation
void UNetworkTestHelper::EnableLatencySimulation(int32 LatencyMs)
{
if (UNetDriver* NetDriver = GetWorld()->GetNetDriver())
{
FPacketSimulationSettings Settings;
Settings.PktLag = LatencyMs;
Settings.PktLagVariance = LatencyMs / 10;
Settings.PktLoss = 0;
NetDriver->SetPacketSimulationSettings(Settings);
}
}
// Functional test for sync
void AMultiplayerSyncTest::StartTest()
{
Super::StartTest();
// Spawn item on server
APickupItem* Item = GetWorld()->SpawnActor<APickupItem>(
ItemClass, FVector(0, 0, 100));
// Wait for replication
FTimerHandle TimerHandle;
GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this, Item]()
{
// Verify client has item
if (VerifyItemExistsOnAllClients(Item))
{
FinishTest(EFunctionalTestResult::Succeeded, "Item replicated");
}
else
{
FinishTest(EFunctionalTestResult::Failed, "Item not found on clients");
}
}, 2.0f, false);
}
Godot
# Network simulation
extends Node
var simulated_latency_ms := 0
var packet_loss_percent := 0.0
func _ready():
# Hook into network to simulate conditions
multiplayer.peer_packet_received.connect(_on_packet_received)
func _on_packet_received(id: int, packet: PackedByteArray):
if packet_loss_percent > 0 and randf() < packet_loss_percent / 100:
return # Drop packet
if simulated_latency_ms > 0:
await get_tree().create_timer(simulated_latency_ms / 1000.0).timeout
_process_packet(id, packet)
# Test
func test_position_sync_under_latency():
NetworkSimulator.simulated_latency_ms = 200
# Move player on host
host_player.position = Vector3(100, 0, 100)
await get_tree().create_timer(1.0).timeout
# Check client view
var client_view_position = client.get_remote_player_position(host_player.id)
var distance = host_player.position.distance_to(client_view_position)
assert_lt(distance, 1.0, "Position should converge within 1 unit")
Dedicated Server Testing
Test Matrix
| Scenario |
Test Focus |
| Server startup |
Clean initialization, port binding |
| Client authentication |
Login validation, session management |
| Server tick rate |
Consistent updates under load |
| Maximum players |
Performance at player cap |
| Server crash recovery |
State preservation, reconnection |
Load Testing
SCENARIO: Maximum Players
GIVEN server configured for 64 players
WHEN 64 players connect
THEN all connections succeed
AND server tick rate stays above 60Hz
AND latency stays below 50ms
SCENARIO: Stress Test
GIVEN 64 players performing actions simultaneously
WHEN running for 10 minutes
THEN no memory leaks
AND no desync events
AND server CPU below 80%
Matchmaking Testing
SCENARIO: Skill-Based Matching
GIVEN players with skill ratings [1000, 1050, 2000, 2100]
WHEN matchmaking runs
THEN [1000, 1050] are grouped together
AND [2000, 2100] are grouped together
SCENARIO: Region Matching
GIVEN players from US-East, US-West, EU
WHEN matchmaking runs
THEN players prefer same-region matches
AND cross-region only when necessary
AND latency is acceptable for all players
SCENARIO: Queue Timeout
GIVEN player waiting in queue
WHEN 3 minutes pass without match
THEN matchmaking expands search criteria
AND player is notified of expanded search
Security Testing
| Vulnerability |
Test Method |
| Speed hacking |
Validate movement on server |
| Teleportation |
Check position delta limits |
| Damage hacking |
Server-authoritative damage |
| Packet injection |
Validate packet checksums |
| Replay attacks |
Use unique session tokens |
Performance Metrics
| Metric |
Good |
Acceptable |
Poor |
| Round-trip latency |
< 50ms |
< 100ms |
> 150ms |
| Sync delta |
< 100ms |
< 200ms |
> 500ms |
| Packet loss tolerance |
< 5% |
< 10% |
> 15% |
| Bandwidth per player |
< 10 KB/s |
< 50 KB/s |
> 100 KB/s |
| Server tick rate |
60+ Hz |
30+ Hz |
< 20 Hz |
Best Practices
DO
- Test with real network conditions, not just localhost
- Simulate worst-case scenarios (high latency + packet loss)
- Use server-authoritative design for competitive games
- Implement lag compensation for fast-paced games
- Test host migration paths
- Log network events for debugging
DON'T
- Trust client data for important game state
- Assume stable connections
- Skip testing with maximum player counts
- Ignore edge cases (simultaneous actions)
- Test only in ideal network conditions
- Forget to test reconnection flows