UDP Protocol

This documentation has been written based on the 1.23 version of the game. Earlier/later versions of the game may differ and are not covered by this article.

Introduction

Let us have a look at how the UDP packets are structured. They have a fairly easy to remember structure that consists of a header and some optional external data. I will be referring to the first three bytes of any packet as the header because they are common to all UDP packets. In a brief description we can describe the packet structure as such:
struct UDP_Packet {
	short PacketChecksum; // Checksum of the packet
	byte  PacketID;       // Identified type of packet
	misc  ExtraData;      // Can be of variable size
};
Not all UDP packets are in-game packets —- some are used during the server list phase.

The third byte of the packet tells us what type of packet it is and what kind of data follows the header, if any. The packet ID byte allows up to 256 different values, but for UDP packets only a very narrow range is actually used.

UDP Packet Checksum

The first 2 bytes of the packet is the checksum of the rest of the packet. Here’s a function to alter a buffer with the checksum. Note that the first two bytes of the buffer will get replaced with the checksum.
h4(#h-2-1). Implementation in C/C++

void udpchecksum(unsigned char* buffer, int size) {
	// Let x and y be the first 2 bytes respectively and initialize them with value of 1
	int x = 1, y = 1;
	// The main formula
	for (int i = 2; i < size; i++) {
		x += buffer[ i];
		y += x;
	}
	// Finalising
	buffer[0] = x % 251;
	buffer[1] = y % 251;
	return;
}
h4(#h-2-2). Implementation in JavaScript For Node.JS:
var udpchecksum = function (buf) {
	var x = 1, y = 1, size = buf.length;
	for(var i = 2; i < size; i++) {
		x += buf[ i];
		y += x;
	}
	buf[0] = x % 251;
	buf[1] = y % 251;
	return buf;
};

ID 0×03 – Ping (Client to Server)

checksum[2] 0x03 numberInList unknownData[4] versionString[4]

ID 0×04 – Pong (Server to Client)

checksum[2] 0x04 numberInListFromPing unknownData[4] gameModeEtc
gameModeEtc:
gameMode = and(byte,7);

ID 0×05 – Query (Client to Server)

checksum[2] 0x05 numberInList

ID 0×06 – Query reply (Server to Client)

struct queryReply {
	short checksum;
	byte 0x06;
	byte numberInList;
	byte timerSync?;
	byte lapsOnTimerSync?;
	byte unknownData[2];
	char versionString[4];
	byte playerCount;
	byte unknownData[1];
	byte gameMode;
	byte playerLimit;
	byte serverNameLength;
	char serverName[serverNameLength];
	byte unknownData[2]
}
h4(#h-6-1). TODO: Check if correct

ID 0×0A – Send password (Client to Server)

checksum[2] 0x0A passwordLength password
Example packet:
HEX: 63 E9 0A 03 73 65 78

ID 0×0B – Password reply (Server to Client)

checksum[2] 0x0B welcome
If welcome is 0 you’re not allowed to join, otherwise you are.