Hacking the Mario Kart 8 Online Track Selection

2024-10-04 by Yannik Marchand

Suspicious feelings

When Nintendo released the DLC packs for Mario Kart 8 on the Wii U, the new tracks also became playable online. In case you are not familiar, every player can either vote for one of three options, or vote for an unknown track:

After every player has made a choice, a roulette animation picks a random player's choice:

At some point, I got the feeling that some tracks appeared more often than others, although I could not prove this yet. It was just a feeling. Especially the DLC tracks seemed to appear as an option much more often than non-DLC tracks. While the DLC packs have some good tracks too, I was starting to miss some of my favorite non-DLC tracks, such as Grumble Volcano and Mount Wario. It felt like these tracks were appearing much less often than, say, the DLC track Baby Park.

Counting the tracks

Because I wanted to know for sure, I started to count which tracks were votable in online races. After playing 150 races, I was able to produce the following plot:

The vertical axis shows how often a given track was votable in an online race. The horizontal axis contains one bar for each track, sorted by frequency. The blue bars represent the 32 tracks that were originally in the game. The orange bars represent the 8 tracks that were added in the first DLC pack, and the red bars represent the 8 tracks that were added in the second DLC pack.

The plot shows that two tracks were votable 24 times while one track was only votable once in 150 races! The colors also make it clear that the DLC tracks appear more often than the standard track, with the second DLC pack appearing more often than the first. It was very unlikely that this was a coincidence.

Bias confirmed

At this point, I started to reverse engineer the game's code. I really wanted to know what was going on. And indeed, after putting in some effort, I found proof that the track selection is biased.

Before picking a track, the game decides which packs it will choose them from, with predefined weights for each pack. Even though there are much less DLC tracks than non-DLC tracks, the DLC tracks are still given a higher weight. When one DLC pack is enabled, the following probabilities are assigned to each pack:

  • Track 1: Always non-DLC
  • Track 2: 80% non-DLC, 20% DLC
  • Track 3: 20% non-DLC, 80% DLC
  • Random track: 80% non-DLC, 20% DLC

When both DLC packs are enabled, the bias is even worse:

  • Track 1: Always non-DLC
  • Track 2: 30% non-DLC, 30% DLC 1, 40% DLC 2
  • Track 3: 40% DLC 1, 60% DLC 2
  • Random track: 20% non-DLC, 35% DLC 1, 45% DLC 2

More than half of the votable tracks come from a DLC pack, even though there are twice as many tracks in the original game than in a DLC pack. The bias was confirmed.

Hacking the track selection

I was sad. While the DLC tracks are fun, I wanted to play every track equally often. Therefore, I decided to fix this. Almost all online games on Nintendo consoles are implemented using a peer-to-peer protocol. This means that no server is involved in an online race. Instead, the consoles communicate directly with each other. In the peer-to-peer protocol of Mario Kart 8, one console is given the role of a 'host'. This console makes important decisions such as choosing the tracks that are presented to the players. Theoretically, if we can become the host, we can choose which tracks are presented to players and remove the bias from the game.

The host is always the console that has been part of the peer-to-peer network for the longest amount of time. This means that there are two ways to become the host:

  • We join a random matchmake session and keep playing until all other players in the session have left, so that we are the oldest player in the session and become the host.
  • We patch our game so that it always creates a new matchmake session instead of joining an existing one.

I decided to try the first method. Your mileage may vary, but after 24 races (1:25 hours) I finally became the host. Now, I used pyGecko to patch the track selection algorithm on my Wii U, so that it always presents the same track:

It worked! And not long after that, I was finally able to play Mario Kart 8 online, with all tracks being votable equally often, without bias.

Final remarks

For some reason, Nintendo decided to make DLC tracks votable much more often than non-DLC tracks in Mario Kart 8, but since the protocol is based on peer-to-peer we could patch that bias away. I cannot think of a good reason why Nintendo would do that. Perhaps it was a way of advertising their DLC pack? No one outside of Nintendo knows.

Unfortunately, there was no good way to make the patch persistent, and it only worked once we became the host. But in any case, I learned a lot about peer-to-peer protocols from this project, and it was fun to work on it!