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:
Type | Purpose | C Name |
---|---|---|
u8 | Unsigned 8 bit integer. Min value 0, max value 256. | unsigned char |
u16 | Unsigned 16 bit integer. Min value 0, max value 65536. | unsigned short |
u32 | Unsigned 32 bit integer. Min value 0, max value 4294967296. | unsigned int |
u64 | Unsigned 64 bit integer. Min value 0, max value 18446744073709551616. | unsigned long long |
i32 | Unsigned 32 bit integer. Min value -2147483648, max value 4294967296. | signed int |
Bool | Unsigned 1 bit integer. 0 means false and all other values mean true . | unsigned char |
Bool32 | Unsigned 4 bit integer. 0 means false and all other values mean true . | unsigned int |
PackedGuid | Guid sent in the "packed" format. See PackedGuid. | - |
Guid | Unsigned 8 bit integer. Can be replaced with a u64 . | unsigned long long |
NamedGuid | A Guid (u64 ) followed by a CString if the value of the Guid is not 0 . | - |
DateTime | u32 in a special format. See DateTime. | unsigned int |
f32 | Floating point value of 4 bytes. | f32 |
CString | UTF-8 string type that is terminated by a zero byte value. | char* |
SizedCString | A 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* |
UpdateMask | Update values sent in a relatively complex format. See UpdateMask. | - |
MonsterMoveSplines | Array of positions. See MonsterMoveSpline. | - |
AuraMask | Aura values sent using a mask. See Masks. | - |
AchievementDoneArray | Array that terminates on a sentinel value. See AchievementDoneArray | - |
AchievementInProgressArray | Array that terminates on a sentinel value. See AchievementInProgressArray | - |
EnchantMask | Enchant values sent using a mask. See EnchantMasks. | - |
InspectTalentGearMask | InspectTalentGear values sent using a mask. See Masks. | - |
Gold | Alias for u32 . | unsigned int |
Level | Alias for u8 . | unsigned char |
Level16 | Alias for u16 . | unsigned short |
Level32 | Alias for u32 . | unsigned int |
VariableItemRandomProperty | A u32 followed by another u32 if the first value is not equal to 0 . | - |
AddonArray | Array of Addons for TBC and Wrath that rely on externally knowing the amount of array members. See AddonArray. | - |
Seconds | Alias for u32 . | unsigned int |
Milliseconds | Alias for u32 . | unsigned int |
Spell | Alias for u32 that represents a spell. | unsigned int |
Spell16 | Alias for u16 that represents a spell. | unsigned short |
Item | Alias for u32 that represents an item entry. | unsigned int |
CacheMask | Client 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