Comment on page
This page describes TERA's network protocol, which is built on top of TCP. It is a structured protocol that can be serialized and deserialized automatically based on uniform packet definitions; no special serialization logic is required for any fields.
- C/C++-like primitive types and
structs will be used.
boolis equivalent to
uint8_tbut only allows the values
- Integers (
int16_t, etc) are little endian.
uint16_tindexes into a packet, including the header.
doubleare IEEE 754
- Characters (i.e.
char16_t) are UTF-16 and little endian.
- Strings (i.e.
u16string) are a series of valid
char16_tcharacters followed by a NUL character.
- Fields are laid out in the declared order with no implied padding anywhere.
TODO: Describe the (insecure) key exchange and the PIKE algorithm.
A packet starts with a simple header:
lengthspecifies the full length of the packet, including the header. Thus, the maximum length of a packet is
65531bytes for the payload). For certain large packets (e.g. achievements and inventory), the game works around this by sending 'continuation' packets after the first packet.
codeis the operation code. This tells the client or server what the structure of the payload is.
The packet body consists of a series of fields. Some examples:
Fields are written in the order that they are declared. However, complex fields (strings, object arrays, and byte arrays) are written as
offset_tvalues that point to the actual data elsewhere in the payload. Primitive types such as integers, floating point numbers, and Boolean values are written in the obvious way as they appear.
Complex types are written after all primitive types in the current 'object' (be that the root packet body, or an object nested arbitrarily within arrays). At the place where the complex field appears, an
offset_tvalue is written pointing to where the actual data for the field is written in the payload. For object arrays and byte arrays, this value is accompanied by a
uint16_tvalue representing the number of elements in the array.
When there are multiple complex fields in an object, they are written at the end of the object in the order that they appear in the structure. For example, in
CEditPrivateChannelPacket, the contents of the
membersarray are written after
password, and the
channel_namestring contents after that.
String pointers are represented as follows:
When writing the string contents, the characters are written contiguously, followed by a NUL character.
Object array pointers are represented as follows:
Each element within the array has an object pointer that links to the next element:
startpoints to a
PacketObjectPointer, which is immediately followed by the contents of the first element. The
nextpointer points to another
PacketObjectPointer, which is immediately followed by the contents of the second element, and so on. This continues
nextvalue for the last element is
0. In each element,
hereis just a pointer to itself; it is not clear what purpose it serves.
Due to the way that arrays are serialized, in theory, it would be possible to spread the elements all over the place in a payload, in whatever way would make the most sense for compactness and locality. In practice, the client and official servers almost always do the straightforward thing, with only a few curious (and seemingly nonsensical) exceptions. Regardless, neither party actually cares how arrays are laid out for the purposes of serialization.
Byte array pointers are represented as follows:
start, unlike object arrays.
Byte arrays are serialized in a more compact fashion than object arrays:
startpoints to an area in the payload containing
countbytes, making up the contents of the byte array. There are no pointer values to follow for each individual element.
Last modified 1yr ago