SMSG UPDATE OBJECT

From wowdev
(Redirected from Packets/UpdateObject)
Jump to navigation Jump to search

SMSG_UPDATE_OBJECT is a World Packet that is sent in order to update the state of objects in the game world.

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.

Large SMSG_UPDATE_OBJECT packets should be sent as compressed SMSG_COMPRESSED_UPDATE_OBJECT packets instead.

Packet Layout

SMSG Header
Offset Size / Endianness Type Name Description
0x0 2 / Big uint16 size Size of the packet including the opcode field.
0x2 2 / Little uint16 opcode Opcode for the packet.
Determines the structure of the body.
Always 0x0A9.


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)
Packed GUID guid

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

Example Packets

The following two are valid packets for logging in with a Vanilla (1.12.1) client. Before these packets the client expects valid:

If either of those packets are missing the client will SEGFAULT unless they have been cached from previous connections.

The GUID values do not appear to need to be the same as the character shown on the character screen, but it could be a source of issues. If you are unable to move but can still use the menu, the keybindings can be reset through the system menu.

The first is the absolute minimum required to log into the world.

char SMSG_UPDATE_OBJECT[] = {
   1, 0, 0, 0, // Block Count (1)
   0, // Has transport
   3, // Update type = CREATE_NEW_OBJECT2
   1, 4, // Packed GUID. GUID is 4, the mask byte indicates that only the first (least significant) byte is sent.
   4, // OBJECT_TYPE = WO_PLAYER
   113, // Update flags 0x71
   0, 0, 0, 0, // Movement flags
   0, 0, 0, 0, // Timestamp
   205, 215, 11, 198, // Position x (-8949.95)
   53, 126, 4, 195, // Position y (-132.493)
   249, 15, 167, 66, // Position z (83.5312) Human start position (Northshire Abbey)
   0, 0, 0, 0, // Orientation (0.0)
   0, 0, 0, 0, // Fall time (0)
   0, 0, 128, 63, // Walk speed (1.0)
   0, 0, 140, 66, // Run speed (70.0)
   0, 0, 144, 64, // Run back speed (4.5)
   0, 0, 0, 0, // Swim speed (0.0)
   0, 0, 0, 0, // Swim back speed (0.0)
   219, 15, 73, 64, // Turn speed (3.1415 (pi))
   1, // Is player
   1, 0, 0, // Unknown hardcoded
   2, // Amount of u32 mask blocks that will follow
   // Mask blocks
   7, 0, 64, 0,   16, 0, 0, 0,
   // End of mask blocks
   4, 0, 0, 0, 0, 0, 0, 0, // OBJECT_FIELD_GUID (4) (notice unpacked u64)
   25, 0, 0, 0, // OBJECT_FIELD_TYPE (16 | 8 | 1) (TYPE_PLAYER | TYPE_UNIT | TYPE_OBJECT)
   100, 0, 0, 0, // UNIT_FIELD_HEALTH (100)
   1, // UNIT_FIELD_BYTES[0] // Race (Human)
   1, // UNIT_FIELD_BYTES[1] // Class (Warrior)
   1, // UNIT_FIELD_BYTES[2] // Gender (Female)
   1, // UNIT_FIELD_BYTES[3] // Power (Rage)
};

The second adds slightly more fields and spawns an level 1 human female warrior with 100/100 health.

char SMSG_UPDATE_OBJECT[] = {
   1, 0, 0, 0, // Block Count
   0, // Has transport
   3, // Update type = CREATE_NEW_OBJECT2
   1, 4, // Packed GUID. GUID is 4, mask byte indicates that only the first (least significant) byte is sent.
   4, // Object Type WO_PLAYER
   113, // Update flags 0x71
   0, 0, 0, 0, // Movement flags
   0, 0, 0, 0, // Timestamp
   205, 215, 11, 198, // Position x (-8949.95)
   53, 126, 4, 195, // Position y (-132.493)
   249, 15, 167, 66, // Position z (83.5312) Human start position (Northshire Abbey)
   0, 0, 0, 0, // Orientation (0.0)
   0, 0, 0, 0, // Fall time (0)
   0, 0, 128, 63, // Walk speed (1.0)
   0, 0, 140, 66, // Run speed (70.0)
   0, 0, 144, 64, // Run back speed (4.5)
   0, 0, 0, 0, // Swim speed (0.0)
   0, 0, 0, 0, // Swim back speed (0.0)
   219, 15, 73, 64, // Turn speed (3.1415 (pi))
   1, // Is player
   1, 0, 0, // Unknown hardcoded
   5, // Amount of u32 mask blocks that will follow
   // Mask blocks
   23, 0, 64, 16,    28, 0, 0, 0,    0, 0, 0, 0,    0, 0, 0, 0,    24, 0, 0, 0,
   // End of mask blocks
   4, 0, 0, 0, 0, 0, 0, 0, // OBJECT_FIELD_GUID (4) (notice unpacked u64)
   25, 0, 0, 0, // OBJECT_FIELD_TYPE (16 | 8 | 1) (TYPE_PLAYER | TYPE_UNIT | TYPE_OBJECT)
   0, 0, 128, 63, // Scale (1.0)
   100, 0, 0, 0, // UNIT_FIELD_HEALTH (100)
   100, 0, 0, 0, // UNIT_FIELD_MAXHEALTH (100)
   1, 0, 0, 0, // UNIT_FIELD_LEVEL (1)
   1, 0, 0, 0, // UNIT_FIELD_FACTIONTEMPLATE (1)
   1, // UNIT_FIELD_BYTES[0] // Race (Human)
   1, // UNIT_FIELD_BYTES[1] // Class (Warrior)
   1, // UNIT_FIELD_BYTES[2] // Gender (Female)
   1, // UNIT_FIELD_BYTES[3] // Power (Rage)
   50, 0, 0, 0, // UNIT_FIELD_DISPLAYD (50, Human Female)
   50, 0, 0, 0, // UNIT_FIELD_NATIVEDISPLAYID (50, Human Female)
};