Implementing world messages

If you have autogenerated the login messages you will have a reasonable base for proceeding with the world messages, however this is not a requirement.

The world message data can be found in the intermediate_representation.json file. The intermediate_representation_schema.json contains a JSON type def schema for the intermediate_representation.json. The json-typedef-codegen program can be used to generate bindings for many popular languages.

The wow_messages_python repository contains the Python code used to generate the Python message library. It can be used as inspiration for your own implementation. The generator directory contains the actual code generation, and the wow_world_messages directory contains the generated library.

Python code will be shown in this document to provide examples of how libraries could be implemented. If you want to use the Python library then just bypass this and use the library directly instead. A C# library is available here.

Message Layout

For Vanilla and TBC, all messages start with a 2 byte big endian size field.

For Wrath, all messages from the client (CMSG) start with a 2 byte big endian size field, but at some unknown point in Wrath the possibility of having SMSG with 3 byte opcode was added. If the most significant (first) byte of the size field has the most significant bit set (that is, first_size_byte & 0x80 != 0) the size field is 3 bytes instead of 2. When sending messages from the server you must handle this special case for messages that are larger than 0x7FF bytes.

For all versions, messages sent from the server (SMSG) have a 2 byte opcode field and messages sent from the client (CMSG) have a 4 byte opcode field.

For all versions, the size field contains the size of the opcode field and the size of the remaining message body.

So in the case of the CMSG_PLAYER_LOGIN message:

test CMSG_PLAYER_LOGIN {
    guid = 0xDEADBEEF;
} [
    0x00, 0x0C, /* size */
    0x3D, 0x00, 0x00, 0x00, /* opcode */
    0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, /* guid */
]

Has a single Guid field (of 8 bytes), but the reported size is 0x0C/12 because it includes the 4 byte opcode field.

Encryption

SMSG_AUTH_CHALLENGE and CMSG_AUTH_RESPONSE are sent in plain text and used to negotiate a simple header encryption based on the session key. It is described in detail for 1.12 in this blog post The wow_srp library written in Rust has implementations for Vanilla (1.12), TBC (2.4.3), and Wrath (3.3.5).

CMSG_PING and SMSG_PONG can also be sent unencrypted if the client has not yet received a reply to CMSG_AUTH_SESSION. These four messages are the only messages that can be sent unencrypted.

Types used for login messages

World messages use the following types, including enums, flags, and structs:

TypePurposeC Name
u8Unsigned 8 bit integer. Min value 0, max value 256.unsigned char
u16Unsigned 16 bit integer. Min value 0, max value 65536.unsigned short
u32Unsigned 32 bit integer. Min value 0, max value 4294967296.unsigned int
u64Unsigned 64 bit integer. Min value 0, max value 18446744073709551616.unsigned long long
i32Unsigned 32 bit integer. Min value -2147483648, max value 4294967296.signed int
BoolUnsigned 1 bit integer. 0 means false and all other values mean true.unsigned char
Bool32Unsigned 4 bit integer. 0 means false and all other values mean true.unsigned int
PackedGuidGuid sent in the "packed" format. See PackedGuid.-
GuidUnsigned 8 bit integer. Can be replaced with a u64.unsigned long long
NamedGuidA Guid (u64) followed by a CString if the value of the Guid is not 0.-
DateTimeu32 in a special format. See DateTime.unsigned int
f32Floating point value of 4 bytes.f32
CStringUTF-8 string type that is terminated by a zero byte value.char*
SizedCStringA u32 field that determines the size of the string followed by a UTF-8 string type that is terminated by a zero byte value.unsigned int + char*
UpdateMaskUpdate values sent in a relatively complex format. See UpdateMask.-
MonsterMoveSplinesArray of positions. See MonsterMoveSpline.-
AuraMaskAura values sent using a mask. See Masks.-
AchievementDoneArrayArray that terminates on a sentinel value. See AchievementDoneArray-
AchievementInProgressArrayArray that terminates on a sentinel value. See AchievementInProgressArray-
EnchantMaskEnchant values sent using a mask. See EnchantMasks.-
InspectTalentGearMaskInspectTalentGear values sent using a mask. See Masks.-
GoldAlias for u32.unsigned int
LevelAlias for u8.unsigned char
Level16Alias for u16.unsigned short
Level32Alias for u32.unsigned int
VariableItemRandomPropertyA u32 followed by another u32 if the first value is not equal to 0.-
AddonArrayArray of Addons for TBC and Wrath that rely on externally knowing the amount of array members. See AddonArray.-
SecondsAlias for u32.unsigned int
MillisecondsAlias for u32.unsigned int
SpellAlias for u32 that represents a spell.unsigned int
Spell16Alias for u16 that represents a spell.unsigned short
ItemAlias for u32 that represents an item entry.unsigned int
CacheMaskClient info sent using a mask. See CacheMask.-

Library layout

Consider what you want the layout of the library to look like regarding files and directories.

You will likely want to limit yourself to a limited set of client versions such as Vanilla (1.12), TBC (2.4.3), and Wrath (3.3.5).

The exact layout of your library will depend on the scope. For the Python library I decided to have a single wow_world_messages package with a module each for Vanilla, TBC, and Wrath.

This is the layout of the Python library:

├── README.md
└── wow_world_messages
    ├── __init__.py
    ├── wrath.py
    ├── tbc.py
    ├── util.py
    └── vanilla.py