SMSG UPDATE OBJECT
When the server wants to notify the client that something (an object, a player, whatever) spawned, moved or disappeared from the world, it sends a SMSG_UPDATE_OBJECT packet with only the relevant data, to save bandwidth.
Header
Name | Value |
---|---|
PARTIAL | 0 |
MOVEMENT | 1 |
CREATE_OBJECT | 2 |
FAR_OBJECTS | 3 |
NEAR_OBJECTS | 4 |
Update types can be grouped as follows:
- Update data of an object (PARTIAL)
- Spawn or move an object (MOVEMENT, CREATE_OBJECT)
- destroy object (FAR_OBJECTS, NEAR_OBJECTS)
Type 1 includes only update fields, types 2 and 3 include movement information block and update fields, types 4 and 5 are just list of GUIDs.
Name | Value |
---|---|
OBJECT | 0 |
ITEM | 1 |
CONTAINER | 2 |
UNIT | 3 |
PLAYER | 4 |
GAME_OBJECT | 5 |
DYNAMIC_OBJECT | 6 |
CORPSE | 7 |
Type | Name | Description |
---|---|---|
uint32 | count | |
uint8 | hasTransport | |
uint8 | update_type (see UpdateType) | |
uint64 | guid |
A note about the GUID: during Vanilla, the decision was taken that a meaningful optimisation would be that, as GUID are uint64, they should be packed like update fields. "Packed GUID" goes like this: first, an uint8 bit mask and then the actual data. Lets say the mask will be 1101001, then the "data" will contain 0-th, 3-rd, 5-th and 6-th bytes of uint64.
If the update type needs an object_type (e.g. CREATE_OBJECT needs it):
Type | Name | Description |
---|---|---|
uint8 | object_type (see ObjectType) |
Movement block
For all update types that need movement information:
Type | Name | Description |
---|---|---|
uint32 | flags | |
int32 | unk | |
float[4] | position | position + orientation |
uint64 | tr_guid | (flags & ON_TRANSPORT) transport GUID |
float[4] | tr_position | (flags & ON_TRANSPORT) transport position and orientation |
uint32 | swim_pitch | (flags & IS_SWIMMING) |
uint32 | time | (flags & IS_FALLING) |
float | velocity | (flags & IS_FALLING) |
float | sin | (flags & IS_FALLING) |
float | cos | (flags & IS_FALLING) |
float | xy_speed | (flags & IS_FALLING) |
float | unk | (flags & SPLINE_ELEVATION) |
float[6] | speeds | walk, run, run backward, swim, swim backward and turn |
There can be more data due to flags but I haven't look into that. These are sufficient for basic emulation though.
Name | Value |
---|---|
FORWARD | 0x00000001 |
BACKWARD | 0x00000002 |
STRAFE_LEFT | 0x00000004 |
STRAFE_RIGHT | 0x00000008 |
TURN_LEFT | 0x00000010 |
TURN_RIGHT | 0x00000020 |
IS_FALLING | 0x00002000 |
IS_SWIMMING | 0x00200000 |
ON_TRANSPORT | 0x02000000 |
SPLINE_ELEVATION | 0x04000000 |
Update fields
Before the update fields, for some update types (e.g. CREATE_OBJECT), there is information about the unit being updated:
Type | Name | Description |
---|---|---|
uint32 | is_player | 1 if this object is in fact the player |
uint32 | attack_cycle | |
uint32 | timer_id | |
uint64 | victim_guid |
Then come the update fields:
Type | Name | Description |
---|---|---|
uint8 | num_mask_blocks | hard coded limit at 0x1C |
uint32[] | mask_blocks | |
uint32[] | update_blocks |
Every object has a lot of different characteristics, sometimes specific to their type. All objects have a GUID, but only an item has durability field; both players and units have a health field, but only players have a guild ID field.
There a lot of different fields (around a thousand), so to send this data to clients without dumping a full table of fields, only relevant fields are sent in an update packet, as update_blocks. And to know what fields are present in the update_blocks, the index of each field is set in the bitmasks of mask_blocks.
To not have to worry about the size of each field, they're all sent as uint32. Beware though, some fields like GUID are uint64, so they take 2 update blocks. The client knows how to parse each field.
Example with field values from 1.1.2.4125 (look in your binary for values): I have a Player, so it has object, unit and player fields, and I want to send to a client his GUID (id: 0), his health (id: 0x16) and his experience (id: 0x9E). I will have to send 2 update blocks for the GUID, one for the health and one for the experience: update_blocks has 4 elements. The indices to set are 0, 22 and 158, so we set bits 0 and 22 of the 0-th uint32 mask block, and the bit 30 of the 4-th mask block (because 158 // 4 = 30 and 158 % 32 = 4). The num_mask_blocks value will have to be 5 because we have at least 5 mask blocks.
Object type | Fields |
---|---|
OBJECT | object |
ITEM | object, item |
CONTAINER | object, item, container |
UNIT | object, unit |
PLAYER | object, unit, player |
GAME_OBJECT | object, game_object |
DYNAMIC_OBJECT | object, dynamic_object |
CORPSE | object, corpse |