Home / 03 — The Wire

Networking in 1998

The internet Brood War was built for: screeching modems, 56 kilobits per second, and hundreds of milliseconds of latency. Those limits didn't constrain the design — they were the design.

The era

Three ways to connect, all of them small.

When StarCraft shipped, "online" meant one of three things, and none of them had bandwidth to spare.

📞 Dial-up modem

Two players, one phone line each, connecting peer-to-peer. A 56k modem peaked near 56 kilobits/sec downstream and far less up — with round-trip latency often 150–300 ms. You could not afford to send game state.

🔌 IPX over LAN

At a LAN party, machines spoke IPX/SPX (Novell's protocol, the era's default for games) over the local network. Fast and low-latency, but the game logic couldn't assume it — the same code had to run over a modem too.

🌐 Battle.net

Blizzard's free service (launched 1997 with Diablo) handled matchmaking, chat, and ladder — finding you an opponent and brokering the connection. Critically, Battle.net mostly got out of the way once the match started: the actual in-game traffic was the lean command stream between the players, not a fat stream through Blizzard's servers. That's why it scaled cheaply.

On the wire (original BW): in-game data ran over UDP, port 6112 (walks up 6112–6119 if busy). LAN could also use IPX; direct play used modem/serial. Battle.net chat/matchmaking itself used TCP, but gameplay packets were UDP — small, frequent, loss-tolerant, with light reliability built on top for the command turns.
⬡ BW vs OpenBW. OpenBW does not use UDP or talk to original BW clients. Its multiplayer layer is its own, carrying the command stream over TCP or a Unix socket through an explicit coordinator (see below). The shared part with real BW is the simulation, not the transport.
The forcing function

Do the bandwidth math.

A late-game StarCraft battle has on the order of hundreds of units, each with a position, facing, health, and order. Suppose you tried to stream that state. Even a stingy encoding — say 16 bytes per unit — for 400 units, 24 times a second, is:

400 units × 16 bytes × 24 fps × 8 bits = 1,228,800 bits/sec ≈ 1.2 Mbit/s

That's ~22× more than a 56k modem could carry — in the best case, ignoring overhead and the upstream being far slower. Streaming state was simply impossible. But the commands a player issues? A handful of clicks per second, a few bytes each. That fits in a whisper of bandwidth — and it's the same few bytes whether you command 4 units or 400.

bandwidth: stream state vs. stream commands

drag the army size — see why lockstep was the only option on a 56k link

The command line barely moves because it doesn't depend on army size — it depends on how fast a human can click. That decoupling is the whole point.

Topology

Eight players, no referee.

"Peer-to-peer" means one specific thing: Blizzard's servers aren't in the gameplay loop. Battle.net matches you, then hands off. One player — the host — coordinates. No central server simulates the game, because there's nothing to simulate: every machine computes the full, identical game itself.

With up to 8 players, the job is smaller than it sounds:

The turn clock

So who decides what goes on which frame?

This is why one bad connection lags everyone. A turn can't run until the slowest player's commands show up — so a single laggy player freezes all 8 machines at once. That universal "Waiting for players…" stall is lockstep's defining trade-off: no one can run ahead, by design.
⬡ BW vs OpenBW. The exact wire shape of original BW — whether peers send directly to all others, or to the host who rebroadcasts — is a Storm/Battle.net implementation detail not pinned down here; what's certain is the host-coordinated, all-commands-to-everyone model above. OpenBW makes the coordinator explicit: clients connect to a syncer (a star topology) that collects each client's scheduled_actions and gates frame advance with all_clients_in_sync(). Same lockstep contract, concrete server-mediated plumbing.
Design consequences

What scarce bandwidth forced.

Every property people associate with Brood War's feel falls out of the network budget:

The constraintThe design it forced
Can't send stateLockstep — send commands, simulate locally
Local simulation must matchDeterminism — fixed-point math, seeded RNG
Round-trips are slowLatency buffering — commands take effect 2 frames later
No authority to correct driftDesync detection — rolling state checksums
Commands are tiny & orderedCheap replays — record the command stream, replay it
Selection capped at 12 units. A famous BW quirk — you can only select 12 units at once — is partly a UI-era decision, but it also keeps command encodings small and bounded: a select command is 1 + (n × 2) bytes, never more than 25 (actions.h:1005).
The transport, today

The same command stream, modern plumbing.

OpenBW keeps the lockstep protocol but carries it over a modern async transport (Boost.ASIO) with interchangeable backends — so the identical command stream can run over a real socket, a Unix pipe, or in-process for tooling and AI:

BackendFileUse
TCP socketsync_server_asio_tcp.hnetworked play; DNS resolution, reuse-address
Unix domain socketsync_server_asio_local.hsame-machine clients, low overhead
POSIX stream / generic socketsync_server_asio_socket.hshared framing & send-queue logic

The wire framing is deliberately minimal — a 2-byte little-endian length prefix, then the payload:

// every message on the wire
[uint16_t size]  // payload length, little-endian (header not counted)
[ payload  ]  // the command bytes

Reads happen in 0x1000-byte chunks; sends are queued per client and may span 0x2000-byte buffers (sync_server_asio_socket.h). Small, predictable, and easy to reason about — the same virtues the original needed on a modem.

Where the actual command bytes come from deeper

The payload is the serialized player action — a 1-byte action ID followed by its parameters (see the action stream). Those bytes are produced once, locally, and broadcast verbatim to every peer. Each peer feeds the identical bytes into its identical simulation. Nothing in the message describes results — only the player's intent.

Why no authoritative game server? deeper

Because there's nothing for one to authoritatively simulate — every client already computes the full, identical game. A central state server would be pure overhead and added latency. Battle.net's job was matchmaking and brokering the connection; the host player coordinates the match itself, with no Blizzard server in the gameplay loop. That's also why a free service was viable in 1998: there's no per-match server cost. The downside is no authority to arbitrate cheating or repair a desync — hence the strict checksum approach instead of correction.