Fundamentals
By learning how the data is encoded and decoded, you can understand how the fundamentals of packets work. Here, you will understand the basic structure of the packets and the main points in sending and receiving them. By understanding these two pages, things will get easier.
Packet Flags
Flags are used to define the type of packet and extra information. The flag value is present in the packet header of every kind of packet. Every flag can be combined with others.
The available flags are:
| Value | Final Value | Name |
|---|---|---|
| 1 << 0 | 1 | FLAG_TYPE_CONTROL |
| 1 << 1 | 2 | FLAG_TYPE_CONNECTION_LESS |
| 1 << 2 | 4 | FLAG_RESEND |
| 1 << 3 | 8 | FLAG_COMPRESSION |
INFO
Connectionless Packets use a separate six-byte header. Once a packet is identified as connectionless, decode that header instead of the flags and ACK layout used by control messages and default packets.
Sequence Number
Control messages and default packets use a sequence number on the packet header to ensure that vital chunks are received in the correct order. You will see this number as ACK (Acknowledged sequence number) on this documentation from now on.
The ACK number defines how many vital chunks were successfully received. As a server, you will have an ACK number to track how many vital chunks you received from the client, and the client will have an ACK number to track how many vital chunks it received from you (the server).
ACK and vital chunk sequence numbers range from 0 to 1023. After 1023, the next sequence number is 0. This return to 0 is called sequence number rollover. Because of this, a lower number is not always older than a higher one. Your implementation must check whether a sequence number compared to the ACK is in the backroom.
INFO
Backroom is the range of sequence numbers already acknowledged, accounting for rollover. For example, with ACK 1, sequence 1023 is older even though its numerical value is greater.
Your implementation must also limit how many vital chunks can wait for an ACK. Otherwise, a new chunk could be mistaken for one that has already been acknowledged. You can learn more at the Resend Buffer section.
Packet Types
The protocol has three types of packets: control messages, connectionless packets, and default packets. Each of them has its purpose and its own header and structure.
Connectionless Packets
These packets are used to request and send server information, such as the server name, version, map, and client list. They do not require an established connection, but are also used while already in-game to fill the client's Server tab. Their structure is described on the Connectionless Packets page.
Control Messages
These packets are used to establish and maintain the connection. They are utilized to connect, disconnect, and keep the connection alive. Control Messages are a type of packet with a simple structure and are not related to the game itself but the connection.
Default Packets
These packets are used to send game-related information. They are utilized to send and receive game data, such as player input, game state, and other game-related information. Every default packet may contain multiple data, and each data is called "chunk".
INFO
Each chunk have it chunk flags. "VITAL" is one of the chunk flags. This is explained on Default Packets, Chunk Concept. and Chunk Structure page.
Tick System
Like any type of game, your implementation must have a tick system. The only supported tick rate is 50 ticks per second.
The minimum and maximum tick number supported is 0 and 0x6FFFFFFF (1879048191). After reaching the maximum, you will need to restart your implementation entirely.
Size Limits
The protocol has some size limits. The maximum size of any kind of packet is 1400 bytes. This size includes the packet header and the packet payload.
Each chunk also has a size limit of 1023 bytes, including its message ID and payload, but excluding its chunk header. You can learn more at the Chunk Size section.
Combining Flags
Since flags are not mutually exclusive states, you can combine flags using bitwise operations. For example, to combine the FLAG_TYPE_CONTROL and FLAG_COMPRESSION flags, you can implement the following pseudo-code:
FLAG_TYPE_CONTROL = 1 << 0
FLAG_COMPRESSION = 1 << 3
flags = FLAG_TYPE_CONTROL | FLAG_COMPRESSIONInteger Overflow
In some cases, the reference implementation uses signed 32-bit integers, which have the maximum value of 2147483647 (0x7FFFFFFF). When the value reaches the maximum, it will overflow to the minimum value of -2147483648. Depending on the language you are using, integers may overflow differently from the expected.
This overflow is expected in some cases, and these cases will be highlighted in the documentation from now on, and should be handled by your implementation. You can use the following pseudo-code to ensure the correct overflow:
function toInt32(value)
{
value = value & 0xFFFFFFFF
return value & 0x80000000 ? -((~value & 0xFFFFFFFF) + 1) : value
}Extracting Flags and ACK
Control Messages and Default Packets have in common the presence of Flags and ACK. However, this information is "mixed" into two bytes of the packet header, as follows:
byte[0] // 6bit flags, 2bit ack
byte[1] // 8bit ack
byte[2-...] // packet...The first byte contains the flags and the first 2 bits of the ACK number. The second byte contains the remaining 8 bits of the ACK number (totalizing 10 bits). To extract the flags and the ACK number, you can implement the following pseudo-code:
flags = byte[0] >> 4
ack = (byte[0] & 0x0F) << 8 | byte[1]