Multiplayer in a Mobile Game: Testing Network, Lag and Reconnect Without a Hundred Players
The most common way I’ve seen multiplayer “tested”: two phones, one Wi-Fi, play a match, everything moves, done. Then the game ships and the first reviews arrive: “it teleports”, “I shot and did no damage”, “got kicked from the match, couldn’t get back in, counted as a loss”. None of those bugs are about game logic. All of them are about the network, and the network is simply not tested on one Wi-Fi: two milliseconds of latency, zero loss, no drops.
A real player sits on the subway on 4G with 150 ms latency and 3 % packet loss, backgrounds the game for a call, hops from Wi-Fi to LTE, steps into an elevator. That is what you need to reproduce. Below: how game networking works so you know what to break, which bug classes it produces, and how to test all of it alone without a hundred players.
How game networking works, briefly and without math
To break things on purpose you need to know what’s under the hood. In practice there are three schemes.
Authoritative server. The client sends only input (pressed forward, fired), the server simulates the world and broadcasts state to everyone. The client trusts nobody, and nobody trusts the client. Most shooters and anything with money on the line work this way. The downside: a full round-trip passes between the press and the server’s reply, and without tricks the character reacts with a delay.
Client-side prediction and interpolation. Those tricks. The client doesn’t wait for the server, it moves its own character immediately, assuming the server will agree. If the server disagrees, the client is “rolled back” to the server’s state, and that is the snap backwards players call rubber-banding. Other players are shown not “where they are now” but “where they were 100 ms ago”, smoothly interpolated between snapshots. The classic write-ups are Glenn Fiedler’s What Every Programmer Needs To Know About Game Networking and Snapshot Interpolation. The other must-read is Valve’s Source Multiplayer Networking, which also explains lag compensation: when validating a shot, the server “rewinds” the world to the moment the shooter saw.
Deterministic lockstep and rollback. Strategy games and fighting games. All clients run the same simulation from identical input, and only input travels over the network. It works as long as the simulation is strictly deterministic: the same input gives the same state on every device. One difference in float math between CPUs and the clients “drift apart”, that’s a desync. The rollback variant (GGPO) guesses the opponent’s input and, on a wrong guess, rolls back and re-simulates a few frames.
Then there is P2P and the “one player is the host” model, common in co-op and on Photon-like platforms. It brings its own class of problems: the host left, so who is the host now.
Why a tester cares: each scheme fails in its own way. An authoritative server gives rubber-banding and “shot the wall”. Lockstep gives desync. P2P gives broken host migration. Without knowing the scheme you chase symptoms blind.
What breaks: multiplayer bug classes
Latency. At 150–250 ms prediction starts disagreeing with the server. Symptoms: snaps backwards, teleporting opponents, hits with no damage, a pickup that “cancels” half a second later. Look separately at what happens to animation and sound on rollback: often the shot already played its sound and muzzle flash, then the server rejected it, and the player is convinced the game is lying.
Packet loss and jitter. Loss by itself is fine if the protocol is ready for it. What hurts is events arriving out of order or duplicated. Typical bugs: a picked-up item appears twice, a door opens and closes, a chat message arrives before the event it describes. Jitter (uneven latency) breaks interpolation: other players stutter even with a good average ping.
Desync. Bug number one for lockstep games. Two clients show different worlds and only find out when checksums are compared, or when a unit is alive on one screen and dead on the other. The cause is almost always non-determinism: floats on different architectures, dictionary iteration order, a random number not drawn from the shared seed, a dependency on frame time. You cannot test this by eye, you need a state hash that clients compare every N ticks.
Reconnect. The most expensive category for the player. They lost connection for ten seconds and got a defeat, a rating hit, a lost stake. What to check: a 3-second drop (should be survived unnoticed), 30 seconds (return to the same match at the current state), 5 minutes (the match is over, what does the player see and what is written to their stats). A drop at the moment of a key action: a purchase, the end of a round, the moment of death. Wi-Fi to LTE switch: the IP changes, the session must survive. Backgrounding: after a while iOS suspends the process, sockets die, and on return the game must reconnect on its own, not hang on “Connecting…”.
Host migration. If the game is P2P or host-client: the host left, backgrounded the game, their phone died. Who becomes host, what happens to match state, in how many seconds, what do the others see. A separate case: the host leaves exactly when another player is performing an action.
Matchmaking. Cancel search and immediately search again. Two fast taps on “Play”. Searching with an empty queue: timeout and what next, bots or a message. Losing the network during search. Region: a player from Europe matched with Asia, and now 300 ms ping is legitimate. Inviting a friend who is already in another match.
Trusting the client. Anything the client sends as a fact rather than as input will be forged. If the server accepts a “dealt 9999 damage” packet without validation, cheaters find it in a day. I covered server authority and request tampering in detail in the mobile game economy piece; here it’s the same thing in real time.
Time. Is the round timer computed on the server or on the client? If on the client, set the phone clock an hour ahead and see what happens to the match. The round must end by server time, otherwise one player’s round is over while another’s is still running.
Room load. Maximum players per room plus one. Action spam: a hundred presses per second, chat flood, one player rapidly joining and leaving. Snapshot size grows with object count, and a protocol that flew with 4 players may not fit into the MTU with 20 and start fragmenting.
How to reproduce the network alone
The key idea: multiplayer is tested with network quality, not with headcount. Two devices cover almost everything if a controlled bad network sits between them.
Network condition tools.
- Network Link Conditioner for iOS and macOS. Installed from Additional Tools for Xcode, appears on the iPhone under Settings → Developer. It has 3G, Edge and loss presets, and you can define your own profile: latency, loss, bandwidth separately for uplink and downlink.
- The Android emulator takes
-netdelayand-netspeedon the command line, and Extended Controls let you pick a network type; see Emulator networking. For a real Android device the easiest way is to route traffic through a laptop acting as a router and break it there. - tc netem on Linux (man tc-netem): latency, jitter, loss, duplication, reordering. Put it on the game server or on a Raspberry Pi that serves Wi-Fi to the phones. It is the most honest way to break the network between real devices.
- Toxiproxy by Shopify (github): a TCP proxy with “toxics” for latency, bandwidth, timeout, slicer. Ideal for matchmaking, auth and every HTTP dependency around the match.
- clumsy on Windows (jagt.github.io/clumsy): the same in one small window, handy for a desktop client or the editor.
- Unity Network Simulator from Multiplayer Tools (docs): network presets right in the editor, works with Netcode for GameObjects. And Multiplayer Play Mode launches several clients from one editor, no device builds needed.
Network profiles I keep at hand.
- “Good 4G”: 60 ms, jitter 10, loss 0.5 %. Everything must be perfect here.
- “Bad 4G”: 200 ms, jitter 80, loss 5 %. The game must stay playable, rubber-banding is acceptable, lost progress is not.
- “Elevator”: 100 % loss for 10 seconds, then normal. Tests silent reconnect.
- “Subway”: alternating 30 seconds normal and 20 seconds dropped, in a loop. Tests that reconnect works not once but ten times in a row.
- “Asymmetry”: uplink 50 kbit/s, downlink normal. Catches games that send too much from the client.
- “Network switch”: physically turn off Wi-Fi mid-match, wait for LTE.
Bots instead of people. To fill a 20-player room you don’t need 20 people. You need a headless client that connects and sends random input. Developers usually already have one for load testing, and arranging a dozen instances is cheaper than gathering a team. If the game is on Unity, the same Multiplayer Play Mode gives you several clients for free.
The reconnect matrix
This is the most valuable artifact you can bring to the team. Rows: the moment of the drop. Columns: duration. In each cell: the expected behavior, agreed with the game designer before testing, not after.
Drop moments that must be in the rows:
- in the lobby and during matchmaking;
- in the first seconds of the match, before the first snapshot;
- mid-match, in a calm moment;
- at the moment of your own action: a shot, a purchase, using an ability;
- at the moment of your own death or victory;
- on the results screen, before rewards are granted;
- while backgrounding the app and during a phone call.
Durations: 3 seconds, 30 seconds, 3 minutes, “until the match ends”. For every cell, three questions: what does the player see at the moment of the drop, what do the others see, what was written to stats and wallet. Especially the last one: if the match reward is granted on the results screen and the player never saw it, the reward is either lost or granted twice on re-entry. I have seen both in production.
What to watch beyond the screen
Your eyes catch a third of the bugs. The rest is in numbers, and it helps if the developers expose them through a debug overlay.
- RTT and its spread. If the game shows ping, compare it with the real one you set in the conditioner. A twofold discrepancy is a bug in the indicator by itself.
- Loss and retransmits. Do they grow on “good 4G”? Then the protocol is creating its own problem.
- Server tick rate under room load. Dropped from 30 to 15, and suddenly all rubber-banding is “the network”, though the network is fine.
- Snapshot size and traffic per second. A mobile player pays for data and battery. A game that eats 30 MB per match gets “drains my data” reviews.
- State hash for lockstep. If there isn’t one, ask for it. Without it desync is not reproducible, only “sometimes weird”.
- Reconnect counter per session and the time of each one. A reconnect that takes 12 seconds after a 3-second drop is a bug too.
The checklist, so you don’t keep it all in your head
- Learned the network scheme: authoritative server, lockstep or P2P. It decides what to look for.
- Ran a match on six network profiles, not on office Wi-Fi.
- Filled the reconnect matrix: 7 moments × 4 durations, with expected behavior from the game designer.
- Checked Wi-Fi → LTE switch and backgrounding the app mid-match.
- Checked a drop at the moment of reward granting: neither lost nor doubled.
- Matchmaking: double tap, cancel and retry, empty queue, drop during search.
- Host migration if the scheme is P2P: host leaves in a calm moment and during someone else’s action.
- Tampered a “fact” packet instead of input through a proxy and confirmed the server rejected it.
- Changed the client clock and checked match timers.
- Filled the room with bots to max plus one, watched tick rate and traffic size.
- For lockstep: enabled state hash comparison and ran a long match.
- Looked at what the game shows the player at the moment of the drop: “Connecting…” with a timeout and an exit, not an infinite spinner.
Multiplayer looks like the hardest area of testing, but in practice it comes down to one skill: making the network exactly as bad as needed, at exactly the moment needed. Two phones, a laptop between them and an hour of setup pay for themselves with the first “got kicked and counted as a loss” you find.