Skip to content

Instantly share code, notes, and snippets.

@niepiekm
Created August 24, 2026 22:29
Show Gist options
  • Select an option

  • Save niepiekm/8e9d42524c5cab1bbec9ce695bb52a7a to your computer and use it in GitHub Desktop.

Select an option

Save niepiekm/8e9d42524c5cab1bbec9ce695bb52a7a to your computer and use it in GitHub Desktop.
Lessons from 2004 RuneScape protocol design
Lessons from "How 2004 RuneScape fit a multiplayer RPG into 56k dial-up"
https://jkm.dev/posts/how-2004-runescape-fit-a-multiplayer-rpg-into-56k-dialup/
Core theme: the client and server aren't two separate systems exchanging messages — they're one system split across a TCP connection. Every byte saved comes from knowledge both ends share without ever transmitting it.
Concrete techniques the protocol uses:
1. Encrypt only what must be secret. Only the opcode byte is enciphered (ISAAC stream cipher) — enough to defeat casual packet sniffing, cheap enough not to cost anything on the body.
2. Send deltas, not absolutes. Walk paths send corner waypoints as signed-byte deltas from the first point, not full coordinates per tile — the server already knows where you started and walks the line itself.
3. Make the common case (no change) cost almost nothing. A player who didn't move or change costs a single bit per cycle. Since "nothing happened" is the overwhelmingly common state across dozens of visible players, this is the single biggest saving in the protocol.
4. Recenter coordinates on what's actually possible. A newly-visible player's position is sent as a 5-bit signed delta (±15 tiles) relative to the local player, not a 32-bit absolute world coordinate — because visibility range bounds what the value could ever be.
5. Bit-pack only where the savings compound. High-frequency, low-information updates (movement, presence) are packed bit-by-bit. Low-frequency, information-dense updates (appearance, chat) are left byte-aligned, because there's no slack to reclaim and the CPU cost of a bit cursor isn't worth paying there.
6. Cache and splice expensive-to-build, rarely-changing data. Appearance blocks are built once per player per change and spliced as a raw byte copy into any observer's packet — byte alignment is what makes that splice O(1) instead of requiring a bit-shift per observer.
7. Only spend bits on the exceptional case. Flags are packed into a single byte, with a marker bit extending to a second byte only for the rarer nine-flag overflow.
The meta-lesson: tight coupling between client and server (shared pathfinding, shared assumptions about "default," "visible," and "what can change") is what makes extreme compactness possible. That's the opposite of how modern decoupled/self-describing/versioned APIs are built — and that's not a regression, it's a different tradeoff for a different binding constraint (bytes vs. team velocity/independent deployability). Reach for RuneScape's approach when both ends ship together and every byte is genuinely contested (competitive games, market-data feeds); reach for the verbose, self-describing approach when the constraint is organizational, not physical.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment