SMSG UPDATE OBJECT

From wowdev
Revision as of 17:44, 7 February 2016 by Warpten (talk | contribs) (Make information about GUID packing a bit more obvious.)
Jump to navigation Jump to search

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.


A note about object GUIDs

In a lot of packets (but unfortunately, not all of them, albeit almost), Blizzard sends object GUIDs in a "packed" form. Seeing as GUIDs are uint64, they chose to pack information so that only the bytes that are set are sent. They first send an uint8 where every bit defines whether or not the corresponding byte is sent. This field is commonly called a mask. Then all the bytes are sent in that order.

Assuming the mask is 0b01101001, then they will send 0-th, 3-rd, 5-th and 6-th bytes of the guid, in that order, and only those.

It is worth noting that at some point (WoD or MoP, i'm not sure - Warpten), they decided to make GUIDs 128 bits. They still pack them, except that this time, they send two masks before the actual bytes: the first one for the GUID lopart, and the second one for the GUID hipart.

Header

UpdateType (1.1.2.4125)
Name Value
PARTIAL 0
MOVEMENT 1
CREATE_OBJECT 2
FAR_OBJECTS 3
NEAR_OBJECTS 4

Update types can be grouped as follows:

  1. Update data of an object (PARTIAL)
  2. Spawn or move an object (MOVEMENT, CREATE_OBJECT)
  3. destroy object (FAR_OBJECTS, NEAR_OBJECTS)

Type 1 includes only update fields, types 2 include movement information block and update fields, and types 3 are just list of GUIDs.

ObjectType (1.1.2.4125)
Name Value
OBJECT 0
ITEM 1
CONTAINER 2
UNIT 3
PLAYER 4
GAME_OBJECT 5
DYNAMIC_OBJECT 6
CORPSE 7
UpdateObjectPacketHeader (1.1.2.4125)
Type Name Description
uint32 count
uint8 hasTransport
uint8 update_type (see UpdateType)
uint64 guid

About GUID: Although depicted as an uint64 in the above table, from 1.12 (or earlier?), GUIDs are packed. See the note about object GUIDs section for more informations.

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:

MovementBlock (1.1.2.4125)
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.

MovementFlags (1.1.2.4125) (incomplete)
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:

UnitInformation (1.1.2.4125)
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:

UpdateFields
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.

Fields for each object type
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