Wing Commander co-op for DOS finally works after 11 years of reversing

danielrh has written up an 11-year project to play the DOS game Wing Commander cooperatively. As kids, he and his brother Patrick shared one fighter, one steering and the other on lasers and afterburner. They always wanted to fly as separate wingmates instead.

The work began on October 4, 2015, when he realised the whole game's memory probably fit in 640K and that finding the X,Y,Z coordinates of the player and the wingman would let network packets control the wingman. To find them he emptied the game of other entities: he skipped the wingman Paladin, flew into empty space until everything despawned, and dumped memory snapshots with the DOSBox MEMDUMPBIN debug command. Comparing a stopped ship, background churn and a moved ship left 35 distinct clusters of changed bytes in the data segment. Visual inspection cut that to about a dozen candidates, and the chase camera view narrowed it to 13D3:A9C2. Adding integers there with the debugger's SM command made the ship jolt forward.

Rotation was harder. In September 2016 he wrote a small tool to compare snapshots automatically rather than use the vbindiff GUI. The game's vectors were 4-byte scalars with 256 as 1.0, so small rotations changed a single byte. The game reserved 64 entity slots (0x00 to 0x3F); the programmers used 0x3F for temporary values and put the camera in 0x3D, which placed a temporary variable two slots from the camera. Rolling the ship tripped sign extension, flipping three adjacent bytes from 0x00 to 0xff, and that exposed the camera and temporary bytes. Writing to the camera copy did nothing because it was overwritten each frame, but bytes written into the next vector, forward[0], visibly shifted the starfield. He then put watchpoints on the addresses and traced the vector-add function up through per-ship physics and the per-frame function to the main loop, and began annotating the game in IDA.

December 2016 showed that writes to global memory alone could not drive the game; laser fire and heat seekers were too complicated, so the original game functions had to be called. Many sat in Borland overlays that were loaded at different places depending on the player's earlier path. The brothers built a trampoline in the game's own string area (first the "Loading WING COMMANDER" string at 13D3:0187, later the error-message string at 13D3:0395). DOSBox pushed CS:IP, moved the instruction pointer into the stub, which saved registers, pushed arguments, made a far call and returned. Calling it made the laser cannons fire. In March 2017 they found a predictable hook point in the main loop (0560:20E3), and in July they located do_damage and stopped the game calling it for the secondary player.

The network protocol came in August 2017: a simple TCP client/server design with protobuf messages. Each frame the leader sends every ship's position plus fire events, despawning and damages; the client replies with its own position, any lasers fired and cataclysmic events such as ejection, landing or death. Each player is entity 0 on their own machine and the other player is entity 1, so packets are swapped on receipt. Packets either write memory directly or join a job queue of events for that frame. A first stub of pushf, pusha, nop, popa, popf, retf killed the game at random from October to December: Borland's overlay loader walks the chain of saved BP frames on the stack and patches return addresses pointing into overlays, and the frameless stub gave it garbage. The fix was a trampoline that looks like a Borland function (push bp, mov bp,sp, nop, pop bp, retf), with DOSBox injecting the call when the program counter reaches the nop.

In October 2017 they found that ephemeral objects landed in different slots for different players. They hooked the spawn function's return to see which slot was used, then changed placement to confine ephemeral objects to slots 12 to 15 out of the 61 shared entity slots. In December the client began keeping a map of server ship IDs to local IDs, and asteroids finally worked in multiplayer. 2018 went to explosions, missiles and autopilot.

In June 2019 mission victory was flaky, so they copied the whole 64K data segment across the network and bisected it, finding 80 bytes at 13D3:D248 that held the mission tree. In October they found the overall victory point counter at 13D3:C280 and mission numbers at 13D3:C255 and C256, which later allowed choosing a mission from a dropdown. In early 2020 they identified the function that plays comm text, so typed chat could cross the network and appear on the wingman's VDU.

By the end of 2020 the game was playable, but sharing it was a bridge too far: players would need to find a copy of the old game, get a strange DOSBox build, swap public IPs and punch through firewalls. There were a few family playthroughs and the project sat mostly idle on GitHub.

In 2026 he revived it with WebRTC, emscripten and a new system called lobbylink. lobbylink is a simple Go lobby server that depends only on a websocket library and sets up peer-to-peer WebRTC connections. Players meet through a shared room code; the server keeps a static allowlist of origins, notifies everyone in a room of joiners, and also runs coturn and mints TURN credentials so players behind restrictive firewalls can connect. He moved the transport onto lobbylink and asked an LLM to configure the build scripts for emscripten and to take a GOG Inno Setup installer package of Wing Commander, unzip it and use it as data files. The result: download Wing Commander from GOG, drag and drop it into client-side browser storage, enter a room code, and play.

Key facts

  • The project started on October 4, 2015 and took 11 years: danielrh and his brother Patrick reverse engineered DOS Wing Commander in DOSBox to add two-player co-op.
  • Memory diffing found the player position at 13D3:A9C2 after 35 changed byte clusters were narrowed to about a dozen; the game's functions were then called through a trampoline hidden in its own string area.
  • The network protocol (August 2017) is TCP client/server with protobuf: the leader sends every ship's position plus fire, despawn and damage events each frame, and the client replies with its own position and actions.
  • In 2026 lobbylink, a Go lobby server for peer-to-peer WebRTC with coturn TURN credentials, and an emscripten build made the game shareable by room code.
  • Players supply their own GOG copy of Wing Commander, drag and drop it into browser storage, enter a room code and play; an LLM was used to set up the emscripten build scripts and GOG data handling.

Why it matters

This is a worked example of how far one can get with patient reverse engineering of a 1990s DOS game. The article walks through the actual techniques: snapshot diffing to find coordinates, spotting a vector layout from single-byte changes, hooking the main loop, calling overlay-loaded functions through a trampoline, and bisecting the data segment to locate the victory tally. It also shows why the project lay idle after 2020: getting a game, a custom DOSBox and open firewall ports onto friends' machines was too much. The 2026 change was making joining as simple as a room code in a browser.

Who it affects

Mostly retro-game fans, emulator and modding hobbyists, and anyone interested in reverse engineering. Wing Commander players who want to fly together are the direct audience. The lobbylink server is also described as usable by browsers or client software in almost any language, so developers who need peer-to-peer WebRTC matchmaking by room code can look at it.

How to use it

Per the article, you download Wing Commander from GOG, drag and drop it into the client-side browser storage, enter a room code and play. The player supplies the game files. lobbylink is at github.com/danielrh/lobbylink; it runs on any small server, keeps a static allowlist of origins and bundles coturn for players behind restrictive firewalls. No price or licence terms for the mod are given in the text covered here.

How solid is it

The write-up is a first-person account by the author, with specific addresses, dates and mechanisms, and it says the game was in a playable state by the end of 2020 and had a few family playthroughs. The text covered here gives no URL for the playable web version and says nothing about player counts, performance or other titles in the series, so how well the browser version works for strangers is not established. It does not say which LLM was used or how much of the 2026 work it did beyond configuring the emscripten build and GOG installer data.

Risks and caveats

The approach depends on game-specific memory addresses and hooks into one version of the game, and on a modified DOSBox; the author describes the original setup as hard to share. Players must own and obtain a GOG copy themselves. Early on, the author found that writing to memory alone could not drive the game, and a badly built stub crashed the game at random for months, so hooks of this kind are fragile. The Bill Gates quote the article cites about 640K is given as the actual quote, in contrast to the apocryphal version, and is background, not part of the technical work.

“Some final work in 2020 left the game in a playable state, but we were out of ideas on how to actually share the fun with friends.”

— danielrh, author of the article