libenet / league of legends protocol success!

Last year I looked into the League of Legends real time protocol, the format of the UDP messages between the game server and clients. My recent lag problems motivated me to look again and I’ve gotten somewhere this time.

The protocol is said to be based on libenet 1.2 with some modifications. The body of the protocol itself is encrypted, presumably to make it harder for third party hacks to sniff data from the game. (It doesn’t make it impossible though, so once again DRM frustrates legit use without dissuading cheaters.) But I don’t care about the body, I just want the enet headers, the sequence numbers and retransmits and stuff. And that’s in the clear!

The best guide I’ve found is this Wireshark dissector, or rather an updated version Boreeas kindly gave me that isn’t checked in anywhere. The key thing is inside the UDP packet, the first 8 bytes are a not-very-interesting header. Then the 9th byte is an Enet command, the 10th byte is a channel number, and the 11th and 12th bytes are the sequence number. (To complicate things sometimes only the first 6 bytes are the header, so the command is the 7th byte, etc.) ACK packets have some bogus sequence number, but the first 2 bytes after that are the sequence number of the packet being acknowledged.

So I put all that together and wrote some Python Impacket code and now I have something printing a summary of UDP captures I made. Below is a few lines from my outputter. This shows the server sending a reliable packet on channel 3 numbered 01a9, then another numbered 01aa, then another number 01ac. My client acknowledges all three. But what happened to 01ab? The server sends that along 200ms later, either because it arrived out of order or else because the server knew to try to send it again. Note that in the meantime 01ad also went missing; that never showed up, so I’m confused. Still, it’s a start. I assume the LoL client shows lag if a reliable packet doesn’t arrive properly.

SREL   03 01a9 00167.521401   00000000: 0D 89 78 BC A7 80 96 CE  86 03 01 A9 00 38 D6 23  ..x..........8.#
  ACKK 03 01a9 00167.521538   00000000: F3 CC 6D 92 A7 03 01 03  00 00 01 A9 96 CE        ..m...........
  SUNS 01 0000 00167.542210   00000000: F3 CC 6D 92 A7 03 49 01  00 00 03 8C 00 0A 87 99  ..m...I.........
SREL   03 01aa 00167.562514   00000000: 0D 89 78 BC A7 80 96 E5  86 03 01 AA 00 07 6F 00  ..x...........o.
  ACKK 03 01aa 00167.562663   00000000: F3 CC 6D 92 A7 03 01 03  00 00 01 AA 96 E5        ..m...........
  SREL 01 0065 00167.583485   00000000: F3 CC 6D 92 A7 83 8E B4  86 01 00 65 00 1F 6B 4F  ..m........e..kO
SREL   03 01ac 00167.629336   00000000: 0D 89 78 BC A7 80 97 28  86 03 01 AC 00 18 D8 AB  ..x....(........
ACKK   01 0065 00167.629393   00000000: 0D 89 78 BC A7 00 01 01  01 AC 00 65 8E B4        ..x........e..
  ACKK 03 01ac 00167.629477   00000000: F3 CC 6D 92 A7 03 01 03  00 65 01 AC 97 28        ..m......e...(
SUNS   04 0000 00167.688278   00000000: 0D 89 78 BC A7 00 49 04  00 00 03 DD 00 3B BA D6  ..x...I......;..
SREL   03 01ae 00167.688330   00000000: 0D 89 78 BC A7 80 97 64  86 03 01 AE 00 10 F5 54  ..x....d.......T
  ACKK 03 01ae 00167.688425   00000000: F3 CC 6D 92 A7 03 01 03  00 65 01 AE 97 64        ..m......e...d
  SREL 01 0066 00167.709221   00000000: F3 CC 6D 92 A7 83 8F 31  86 01 00 66 00 1F 6B 4F  ..m....1...f..kO
SREL   03 01af 00167.736732   00000000: 0D 89 78 BC A7 80 97 A5  86 03 01 AF 00 44 2B 25  ..x..........D+%
  ACKK 03 01af 00167.736863   00000000: F3 CC 6D 92 A7 03 01 03  00 66 01 AF 97 A5        ..m......f....
ACKK   01 0066 00167.739947   00000000: 0D 89 78 BC A7 00 01 01  01 93 00 66 8F 31        ..x........f.1
  SUNS 01 0000 00167.740008   00000000: F3 CC 6D 92 A7 03 49 01  00 00 03 8F 00 0A 23 68  ..m...I.......#h
SREL   03 01ab 00167.765224   00000000: 0D 89 78 BC A7 80 97 C2  86 03 01 AB 00 1B BC 8D  ..x.............
  ACKK 03 01ab 00167.765389   00000000: F3 CC 6D 92 A7 03 01 03  00 00 01 AB 97 C2 01 03  ..m.............
SUNS   04 0000 00167.770715   00000000: 0D 89 78 BC A7 00 49 04  00 00 03 DF 00 23 31 79  ..x...I......#1y

Here’s a breakdown of packet types from an ARAM game I recorded. 23.77% of packets are from my client to the server, “send unsequenced”. Huh, what does my client have to say to the server that’s so volumnious and yet unimportant? About 41% of traffic is the server sending reliable packets and the client acknowledging them and another 11% is the client sending reliable packets and the server acknowledging them. 21% of the traffic is unsequenced packets from the server, or about the same number of packets as reliable ones. I’d expected more of a bias to unreliable packets from the server than that.

   20086 .2377   SUNS
   17498 .2070   ACKK
   17475 .2068 SREL
   15406 .1823 SUNS
    5670 .0671   SREL
    5101 .0604 ACKK
    1528 .0181 SUNR
    1311 .0155   PING
     203 .0024   SUNR
     197 .0023 PING
      40 .0005 SFRG
       1 .0000 VCON
       1 .0000   CONN

3 thoughts on “libenet / league of legends protocol success!

  1. What happens to you gamewise in LoL when you get a packetlossy game?
    The client->server SUNS stuff which seems to be the Enet equivalent of ‘just use UDP’ would make a lot of sense for things where you want minimum latency without the overhead of packet-level ack. In many FPSes (and WoW) this might be stuff like player position where the server periodically imposes consistency (like a ‘semantic ACK’) but doesn’t babysit the client excessively to keep the game smooth.

  2. With this kind of lag, what I see is almost no lag but occasionally the whole game pauses 250ms or more. Often this happens in the middle of a crucial skill; I’ll see the animation that the skill started but then it pauses before the skill goes through and I see the result. My assumption was the game stalled waiting for the reliable packet from the server telling it what happened with the skillshot, but that’s just a guess.

    I’m still puzzled about all the traffic my client is sending to the server. Naively, the only thing the client should send is user actions: mouseclicks and keystrokes. And ACKs and ping responses. That heavy SUNS traffic is something else. Maybe it’s just the client reporting more info to the server like where the user’s camera is, etc? That’s more application-specific reverse engineering than I want to do, I hope to just stick to transport protocol basics.

  3. Your guess is almost certainly (or at least, asymptotically) right, I just hadn’t got it through my head that such games don’t work like, say, Quake 3 (http://trac.bookofhook.com/bookofhook/trac.cgi/wiki/Quake3Networking). In fact, it seems the opposite is true and RTSes (and their MOBA progeny) are much more synchronous. It’s an interesting space of design trade-offs on which there is next to bupkis of publicly available information, even in relatively general terms.

    I similarly/naively thought the whole thing looked way too chatty from your original packet/sec graphs that regularly go over 90 to 100 packets/sec but sort of assumed I’m just not thinking about it hard enough or you accidentally mislabeled the axis.

Comments are closed.