I

Ramon Saraiva

Beep beep

This one is about Albion Online.

Albion Online is a sandbox MMORPG with a single global server and a big (really really big) map. There are 4 zones with different risks/rewards. Green, yellow, red and black.

Green zones are noob-friendly, everything is calm, slow-paced and peaceful. Yellow zones have optional PvP, but you don't drop anything if you die - warms you up into the game and have slightly better fame (exp) and resources gain. Red zones still have optional PvP, but you drop everything equipped + inventory if you die - even better fame and resources gain. Black zones are the top tier zones, everyone who walks into it has PvP automatically turned on, full loot - high rewards, best fame and resources gain in the game.

albion online zones

In this genre of games, a few questions are always raised..

Yeah, any minor advantage you get in a full loot PVP game puts you ahead. I had similar questions when I was playing the game and I wanted to know how the game avoided those things, in scale.

But why mentioning scale? What does scale actually means in this case?

Well, as mentioned a few lines above, there is a single global server and a big map. You have loading screens when hopping from a zone to another, so in theory, each zone could actually be served in a separate node - and I'm pretty sure they are.

When thinking about a single zone/map, everything that happens there will be handled by a single server (or something similar to that), i.e.:

Let's focus on AoI (Area of Interest). It is a radius based on your character position, and every entity that overlaps this radius is going to be transmitted from the game server to your game client (players, resources, buildings, npcs, mobs, ..). Your game client starts rendering/pre-rendering that information as soon as it receives it.

area of interest

Imagine that players can use different screens, different resolutions, so rendering things as soon as you "can see it" is tricky, cause most likely you'll get a terrible UX by watching things pop right in front of you.

To avoid that, servers usually send you information about things you're going to see in the "near future", so your client can pre-render those. So in reality, we can know that things are "there" before we can actually see them.

My idea was to exploit this AoI concept sniffing the data trasmitted from the server to my game client and understanding what type of packets correspond to players.

I spinned wireshark, filtered for UDP packets and went to a busy city in-game, recorded all transmitted packets for a few minutes, logged out and started analyzing it.

Most of the data was somehow encrypted, but guess what, the player metadata was actually readable, by simply decoding the incoming bytes with UTF-8. Also, it was pretty clear that player-related packets had a bigger payload size compared to others.

I wrote a small Python application that was able to initialize a UDP socket stream, receiving all packets that were sent to my local net IP address.

        s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
        s.bind((self.network_ip, 0))
        s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
        s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
        

Then, parsing the IP ([:20] bytes) + UDP header ([20:28] bytes) to retrieve the length of the payload, ignoring everything under "X" bytes (that was probably not a player packet)

        ip_header = struct.unpack('!BBHHHBBH4s4s', buffer[:20])
        udp_header = struct.unpack('!HHHH', buffer[20:28])
        

Now, from those packets, I realized that whenever I saw a player metadata, the packet had this hex chunk in it b'f30401002'. So checking if b'f30401002' in buffer[payload_offset:] was sufficient to filter the packets I was looking for.

I made the application trigger a "beep" sound winsound.Beep whenever a new player was getting close to me, plus writing to the stdout the name of the player and what guild it was in - I was able to get that information from the packet with these two offsets:

            entity_id_boffset = b'\x01\x73\x00'
            guild_id_bosset = b'\x08\x73\x00'
        

That was back in 2016, and you're now thinking that I used a map hack while playing Albion Online, for years. That's wrong. A few hours after I implemented this application, I messaged SBI (Sandbox Interactive) support, attaching the "beep beep" source code and asked if there was anything they could do to avoid that.

Guess what was the answer? "There is nothing we can do. This is part of the base net code of the game and it is unviable to change."

A few weeks after that they also messaged me to work for them as a freelancer in their support team. Beep beep!

Oh, by the way, "beep beep" still works.