Protocol/Packets

From Ace of Spades Wiki
Jump to: navigation, search

Server-to-Client

0x69: Server Details [0x31]
  • QWord steam_id
  • DWord ipv4_address
  • DWord port (UDP ports are actually only 2 bytes, but there's plenty of cases where someone decided to use 4 in this protocol)
  • ASCIIZ game_mode_title
  • ASCIIZ game_mode_description
  • ASCIIZ game_mode_infographics[3]
  • ASCIIZ map_name_printable
  • ASCIIZ map_name_filename
  • DWord map_checksum (appears to be ignored)
  • Byte game_mode_index
  • Byte is_ugc?
  • Word master_port
  • Byte classic_mode?
  • Byte can_modify_bottom_solid_layer?
  • unknown 9 bytes
  • ASCIIZ skin_name (usually empty, set to "mafia" for VIP-mode stuff)
  • unknown 7 bytes
  • Byte u4_size
  • Byte u4[u4_size]
  • unknown Byte
  • Byte u5_size
  • Word u5[u5_size]
  • unknown 2 bytes
  • ASCIIZ server_name
  • Byte ground_colours
    • Byte bgr[3]?
    • Byte elevation?
  • Byte water_fall_damage?
  • Byte can_shoot_with_intel?
  • Byte friendly_fire?
  • Byte rules_size
    • ASCIIZ rule_name
    • ASCIIZ rule_value
  • Byte u3 (usually 0x01)
  • unknown Byte

There is still a lot of information we don't know about this packet.

There are some very weird things going on in this packet which would be interesting to tap. This appears to be where corpse explosions and shooting with the intel are set. Some other interesting things happen here, too.

Note, these logs are from v122. v163 seems to be the current proto but with a different version number, so technically this is just old info:

'\x01\x034\x1fA\xf9\r@\x01\x0b\x00C>\x7f\x80\x00\x00TDM_TITLE\x00TDM_DESCRIPTION\x00TDM_INFOGRAPHIC_TEXT1\x00TDM_INFOGRAPHIC_TEXT2\x00TDM_INFOGRAPHIC_TEXT3\x00Tokyo Neon\x00TokyoNeon\x00\xbe\x0b\r\xc4\x06\xebi\x00\x01\x00\xc0\x01\x00\x01\x01\x01\x00\x01\x00\x01\x00\x01@\x00@\x00\x02%&\x00\x0e@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00\x01Europe FF8-9\x00\x02\x14(7\xee\x14(7\xef\x00\x00\x00\x00\x01'
'\x01\x03\x18b\xd0;\x0e@\x01\xe5\xd0\xf5\xad\x8b\x80\x00\x00TDM_TITLE\x00TDM_DESCRIPTION\x00TDM_INFOGRAPHIC_TEXT1\x00TDM_INFOGRAPHIC_TEXT2\x00TDM_INFOGRAPHIC_TEXT3\x00Double Dragon\x00DoubleDragon\x00sa\n\xc0\x06\xf7i\x00\x01\x00\xc0\x01\x00\x01\x01\x01\x00\x01\x00\x01\x00\x01@\x00@\x00\x02%&\x00\x0e@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00\x01Australia SY2-21\x00\x03U<\x1e \x1f\x16\x0b\xee\n\r,\xef\x00\x00\x00\x00\x01'
'\x01\x03\x18b\xd0;\x0e@\x01\xe5\xd0\xf5\xad\x8b\x80\x00\x00TDM_TITLE\x00TDM_DESCRIPTION\x00TDM_INFOGRAPHIC_TEXT1\x00TDM_INFOGRAPHIC_TEXT2\x00TDM_INFOGRAPHIC_TEXT3\x00Spooky Mansion\x00SpookyMansion\x00T\x04\xd6w\x06\xf7i\x00\x01\x00\xc0\x01\x00\x01\x01\x01\x00\x01\x00\x01\x00\x01@\x00@\x00\x02%&\x00\x0e@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00\x01Australia SY2-21\x00\x03U<\x1e \x1f\x16\x0b\xeeW``\xef\x00\x00\x00\x00\x01'
'\x01\x02\xe8\xf4\x81<\x0e@\x01\x0b\x00C>\x82\x80\x00\x00ZOMBIE_MODE_TITLE\x00ZOMBIE_MODE_DESCRIPTION\x00ZOM_INFOGRAPHIC_TEXT1\x00ZOM_INFOGRAPHIC_TEXT2\x00ZOM_INFOGRAPHIC_TEXT3\x00The Colosseum\x00TheColosseum\x00\xa6\xb4\xe0\x05\x02\xeei\x00\x01\x00\xc0\x01\x00\x01\x01\x01\x00\x01\x00\x01\x00\x01@\x00@\x00\x02%&\x00\x0e@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00@\x00\x01Europe FF8-12\x00\x02\xf7\xaaT\xee\xacw@\xef\x00\x00\x00\x01RULE_CRATES_SPAWN_TIME\x0060\x00\x01'

The first server on this list is 62.67.0.11, port 32895. As you might be able to see, the IP and port are sent, but the client doesn't even check to see if this even matches what was punched in.

0x00: Ping Pong

  • DWord client_time
  • DWord server_time

0x02: World Update

  • DWord sequence_number (increments by 2 each time)
  • Word num_players:
    • Byte player_id
    • Float <x, y, z> position
    • Float <x, y, z> direction
    • Float <x, y, z> velocity
    • Word ping
    • DWord local_sequence_number (for lag compensation?)
    • Word health
    • Word input?
    • unknown Byte (usually 0x00)
    • Byte current_tool
    • unknown Byte (usually 0xFF) - "owner_player_id"?
    • Fixed rocket_fuel (max 100.0)
    • unknown DWord (either 0x000000000 or 0x00008001)
  • Word num_entities:
    • Word entity_id
    • Fixed <x, y, z> position
    • Fixed <x, y, z> velocity
    • unknown Word
  • Word num_map_items:
    • Word entity_id
    • Fixed <x, y> position

(unknown_1/unknown_2 information not up to date for v163, but the rest is)

Here's some data from my packet log featuring one entry in the "unknown_1" array.

trailing: 01 00 B5 14 7E 46 11 5B B4 27 47 87 70 04 6B 05 00 00
trailing: 01 00 B5 14 41 46 37 5B E4 27 22 87 5A 04 AF 05 00 00
trailing: 01 00 B5 14 1E 46 4C 5B 00 28 FC 86 43 04 C0 05 00 00
trailing: 01 00 B5 14 1E 46 4C 5B 00 28 7E 83 21 02 E0 02 00 00
trailing: 01 00 B5 14 1E 46 4C 5B 00 28 7E 83 21 02 E0 02 00 00
trailing: 01 00 B5 14 1E 46 4C 5B 00 28 7E 83 21 02 E0 02 00 00
trailing: 01 00 B5 14 1E 46 4C 5B 00 28 7E 83 21 02 E0 02 00 00
trailing: 01 00 B5 14 1E 46 4C 5B 00 28 7E 83 21 02 E0 02 00 00
trailing: 01 00 B5 14 10 46 55 5B 0C 28 7E 83 21 02 E0 02 00 00

This showed up with one entry in the "unknown_2" array.

D5 14 C0 3D 90 59

0x03: Entity update

  • Word length:
    • Word entity_id
    • Fixed <x, y, z> position
    • Fixed <x, y, z> velocity?

This is based on the v122 data, where the packet was 0x07.

Here are some example packets. The log I'm looking at only shows updates of 9 entities.

07 09 00
C1 14 C0 04 00 40 00 3B 00 00 00 00 00 00
B1 14 40 23 80 38 C0 39 00 00 00 00 00 00
B3 14 C0 45 80 38 80 36 00 00 00 00 00 00
B5 14 C0 04 80 38 00 3B 00 00 00 00 00 00
B7 14 C0 04 40 31 00 3B 00 00 00 00 00 00
B9 14 C0 45 40 31 40 36 00 00 00 00 00 00
BB 14 40 23 40 31 C0 39 00 00 00 00 00 00
BD 14 C0 45 00 40 C0 36 00 00 00 00 00 00
BF 14 40 23 00 40 80 39 00 00 00 00 00 00

Here are two in a row. They might be identical. I haven't checked.

07 15 00
C1 14 40 49 80 28 00 2F 00 00 00 00 00 00
C3 14 C0 1A 00 36 80 34 00 00 00 00 00 00
C5 14 40 4F C0 47 40 3A 00 00 00 00 00 00
E7 14 00 4D 40 4A 40 3A 00 00 00 00 00 00
C7 14 00 29 00 40 80 35 00 00 00 00 00 00
CA 14 C0 1A 00 35 C0 33 00 00 00 00 00 00
AB 14 00 45 40 40 40 3A 00 00 00 00 00 00
CC 14 40 66 80 3E C0 37 00 00 00 00 00 00
AD 14 80 26 80 3F 00 36 00 00 00 00 00 00
CE 14 00 45 40 42 40 3A 00 00 00 00 00 00
AF 14 80 63 80 43 00 37 00 00 00 00 00 00
D0 14 C0 4B 80 28 00 2F 00 00 00 00 00 00
B1 14 80 48 00 5D C0 2E 00 00 00 00 00 00
D2 14 80 4B 00 5D 00 2F 00 00 00 00 00 00
B3 14 C0 1A 00 37 C0 33 00 00 00 00 00 00
D4 14 C0 49 00 4A 80 3A 00 00 00 00 00 00
B6 14 C0 47 80 28 C0 2E 00 00 00 00 00 00
B8 14 C0 43 C0 3E 40 3A 00 00 00 00 00 00
BA 14 80 2B 00 3F 80 36 00 00 00 00 00 00
BD 14 C0 49 00 5D 00 2F 00 00 00 00 00 00
BF 14 80 65 40 48 00 37 00 00 00 00 00 00
07 15 00
C1 14 40 49 80 28 00 2F 00 00 00 00 00 00
C3 14 C0 1A 00 36 80 34 00 00 00 00 00 00
C5 14 40 4F C0 47 40 3A 00 00 00 00 00 00
E7 14 00 4D 40 4A 40 3A 00 00 00 00 00 00
C7 14 00 29 00 40 80 35 00 00 00 00 00 00
CA 14 C0 1A 00 35 C0 33 00 00 00 00 00 00
AB 14 00 45 40 40 40 3A 00 00 00 00 00 00
CC 14 40 66 80 3E C0 37 00 00 00 00 00 00
AD 14 80 26 80 3F 00 36 00 00 00 00 00 00
CE 14 00 45 40 42 40 3A 00 00 00 00 00 00
AF 14 80 63 80 43 00 37 00 00 00 00 00 00
D0 14 C0 4B 80 28 00 2F 00 00 00 00 00 00
B1 14 80 48 00 5D C0 2E 00 00 00 00 00 00
D2 14 80 4B 00 5D 00 2F 00 00 00 00 00 00
B3 14 C0 1A 00 37 C0 33 00 00 00 00 00 00
D4 14 C0 49 00 4A 80 3A 00 00 00 00 00 00
B6 14 C0 47 80 28 C0 2E 00 00 00 00 00 00
B8 14 C0 43 C0 3E 40 3A 00 00 00 00 00 00
BA 14 80 2B 00 3F 80 36 00 00 00 00 00 00
BD 14 C0 49 00 5D 00 2F 00 00 00 00 00 00
BF 14 80 65 40 48 00 37 00 00 00 00 00 00

0x0D: Set Player Loadout

  • Byte player_id
  • Byte class
  • Byte unknown (usually 0x00)
  • Byte loadout_size
  • Byte loadout[loadout_size]
  • Byte prefab_count
  • ASCIIZ prefab_list[prefab_count]
  • Byte unknown (usually 0x00)

0x0E: Existing Player

  • Byte player_id
  • Byte team
  • Byte class
  • Byte tool
  • unknown 2 bytes
  • Word score
  • unknown 2 bytes
  • Byte block_color_bgr[3]
  • ASCIIZ name
  • Byte loadout_size
  • Byte loadout[loadout_size]
  • Byte prefab_count
  • ASCIIZ prefab_list[prefab_count]

(Information not up to date for v163 - information is from v122)

Here's a decoded list from my packet log. Notice how much ultra, super and safety there is.

player: 10 0E 03 00 0C FF 00 00 00 00 00 2C B3 89 'Camper Hunter' 05 02 08 0C 20 17 1E 19 1A 1E ['prefab_ultrabarrier', 'prefab_superbarrier', 'prefab_supersmallwall']
player: 10 00 02 01 13 FF 00 8C 02 00 00 51 60 69 'Tschda2356' 05 00 13 11 14 17 16 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge']
player: 10 0F 03 00 08 FF 00 00 00 00 00 2C B3 89 'Dylan' 05 02 08 0C 0B 17 1E 19 1A 1E ['prefab_ultrabarrier', 'prefab_superbarrier', 'prefab_supersmallwall']
player: 10 02 02 03 17 FF 00 9C FF 00 00 B3 75 2C 'Eddie03' 05 03 0A 0E 15 17 1E 19 1A 1E ['prefab_superdome', 'prefab_superpole', 'prefab_safety_corridor']
player: 10 07 03 01 12 FF 00 64 00 00 00 2C B3 89 'KingLx' 05 00 12 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 0C 02 00 0B FF 00 9C FF 00 00 B3 75 2C 'FSK18 | Fth26 | Fatih' 05 02 08 0C 0B 17 1E 19 1A 1E ['prefab_ultrabarrier', 'prefab_superbarrier', 'prefab_supersmallwall']
player: 10 05 03 00 0C FF 00 2C 01 00 00 2C B3 89 'frazerlaing' 05 01 08 0C 20 17 1E 19 1A 1E ['prefab_ultrabarrier', 'prefab_superbarrier', 'prefab_supersmallwall']
player: 10 03 03 01 05 FF 00 00 00 00 00 2C B3 89 'ReVeR' 05 00 13 11 14 17 16 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge']
player: 10 01 02 01 11 FF 01 00 00 00 00 B3 75 2C 'psycho k' 05 00 12 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']

Here's a decoded list from another packet log.

player: 10 12 02 00 08 FF 00 00 00 00 00 B3 75 2C 'nicki.deschenes' 05 01 08 0C 0B 17 1E 19 1A 1E ['prefab_ultrabarrier', 'prefab_superbarrier', 'prefab_supersmallwall']
player: 10 09 02 01 12 FF 00 00 00 00 00 B3 75 2C 'Humid' 05 00 12 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 07 03 01 11 FF 00 00 00 00 00 2C B3 89 '~Xx~SouL~xX~' 05 01 12 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 01 02 01 13 FF 00 00 00 00 00 B3 75 2C 'kpepper' 05 00 13 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 0B 02 01 12 FF 00 00 00 00 00 B3 75 2C 'jellyman.wilson' 05 00 12 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 08 02 00 0C FF 00 00 00 00 00 B3 75 2C 'Preecha' 05 02 08 0C 0B 17 1E 19 1A 1E ['prefab_ultrabarrier', 'prefab_superbarrier', 'prefab_supersmallwall']
player: 10 05 03 01 12 FF 00 00 00 00 00 2C B3 89 'Brumby' 05 00 12 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 02 02 0C 17 FF 00 00 00 00 00 B3 75 2C 'Satsz' 05 00 07 1D 2C 17 1E 19 1A 1E ['prefab_supertower', 'prefab_platform', 'prefab_superdome']
player: 10 06 03 01 12 FF 00 00 00 00 00 2C B3 89 'Gossip_Girl_n.n' 05 00 12 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 0C 02 01 05 FF 00 00 00 00 00 32 34 35 'andrew.gillies' 05 00 13 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']
player: 10 10 03 0C 07 FF 00 00 00 00 00 2C B3 89 'Tim' 05 00 07 1D 2C 17 1E 19 1A 1E ['prefab_caltrop', 'prefab_supertower', 'prefab_platform']
player: 10 0E 03 00 01 FF 00 00 00 00 00 2C B3 89 '[NfC] T-Rex' 05 01 08 0D 20 17 1E 19 1A 1E ['prefab_ultrabarrier', 'prefab_superbarrier', 'prefab_supersmallwall']
player: 10 04 03 01 17 FF 00 00 00 00 00 2C B3 89 'King Fighter' 05 01 13 11 14 17 1E 19 1A 1E ['prefab_supertower', 'prefab_superbridge', 'prefab_superminibunker']

I doubt any of those have steam IDs in them.

0x1C: Spawn Player

  • Byte player_id
  • Byte spectator_player_id (usually 0x00)
  • Byte class
  • Byte team
  • Byte is_dead
  • Byte language
  • Fixed <x, y, z> position
  • Fixed <x, y, z> direction
  • ASCIIZ name
  • Byte loadout_size
  • Byte loadout[loadout_size]
  • Byte prefab_count
  • ASCIIZ prefab_list[prefab_count]

0x26: Block State List

  • DWord damaged_count:
    • Word <x, y, z> position
    • Byte block_health?
    • Byte bgr[3]?
  • DWord user_count: (Could be for lighting, e.g. a Flare Block)
    • Word <x, y, z> position
    • Byte unknown (usually 0x24)
  • DWord occupied_count:
    • unknown structure

This is the sort of stuff you don't want on your CV. The first array is absolutely HUGE.

0x2D: Game State [0x31]

  • Byte player_id
  • Byte fog_bgr[3]?
  • Fixed gravity?
  • unknown 23 bytes - bulk of this is probably lighting information
  • Fixed game_speed
  • Byte max_score
  • Byte game_mode
  • unknown Byte - may indicate how score is calculated?
  • 2 teams:
    • ASCIIZ team_name
    • Byte bgr[3] (Could be block colour; could be team colour)
    • Word score
    • Byte flags (Note, these are guesses!)
      • bit 0: Locked
      • bit 1: Other team visible
      • bit 2: Show score
      • bit 3: Show score limit
      • bit 4?: Infinite blocks
      • bit 5: Class locked - cannot change class
      • bit 6?: Score locked? Meaning is unclear.
    • Byte class_list_size
    • Byte class_list[class_list_size]
  • Byte lock_team_swap?
  • Byte lock_spectator_swap?
  • unknown Byte (is this even here or did I make a mistake?)
  • Word entity_list_size:
    • Word entity_index
    • Byte entity_type
    • Byte team_id
    • Byte owner_player_id (0xFF if none, observed to be set for 0x09 Rocket Turret)
    • Fixed <x, y, z> position
    • unknown 8 Bytes
    • Fixed <x, y, z> unknown vector
    • unknown Fixed (likely used for timing)
    • unknown Byte (0x04 observed)
    • unknown 5 Bytes
  • Byte screenshot_positions:
    • Fixed <x, y, z> position
  • Byte screenshot_locations:
    • Fixed <x, y, z> location
  • Byte has_map_ended?

DD FB FF 1A 00 13 00 26 80 16 28 4D 06 80 40 80 13 00 22 31 6D 10 00 40 00 C8 06 01

Packet log from a TDM game (v122):

2C 07 41 6F A3 40 00 DD FB FF 1A 00 13 00 26 80 16 28 4D 06 80 40 80 13 00 22 31 6D 10 00 40 00 C8 06 01
54 45 41 4D 31 5F 43 4F 4C 4F 52 00 // "TEAM1_COLOR"
B3 75 2C 09 00 0C 04 {00 01 0C 03}
54 45 41 4D 32 5F 43 4F 4C 4F 52 00 // "TEAM2_COLOR"
2C B3 89 1A 00 0C 04 {00 01 0C 03}
01 00

3D 00
4C 14 14 01 FF C0 5D 00 20 40 34 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
53 14 09 03 01 A0 41 E0 3D C0 31 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
54 14 14 01 FF C0 1E C0 22 40 31 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
55 14 14 01 FF 00 48 C0 3C 40 34 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
56 14 09 02 0B 60 43 20 34 00 34 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
57 14 03 01 FF C0 4A 00 39 40 34 00 00 00 00 08 80 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
58 14 04 01 FF 00 46 C0 38 40 34 00 00 00 00 08 80 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
59 14 09 03 01 A0 41 60 3E C0 31 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
5A 14 05 01 FF C0 36 C0 51 00 32 00 00 00 00 08 80 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
...

04
C0 2E 40 33 00 45
40 28 00 4F 00 3E
80 2C 80 4A 80 42
C0 2D C0 45 C0 4B 

04
00 00 A6 A0 1A 83
00 00 0D BD C0 83
00 00 66 F9 33 84
00 00 33 CB 0D 85

00

Packet log from a Zombie game (v122):

2C 0A 40 6E A2 40 00 DD FB FF 1A 00 13 00 26 80 41 56 59 06 80 06 80 26 00 2A 38 3C 06 00 40 00 0A 02 00
5A 4F 4D 42 49 45 5F 54 45 41 4D 00 // "ZOMBIE_TEAM"
B3 75 2C 00 00 0A 01 {04}
53 55 52 56 49 56 4F 52 5F 54 45 41 4D 00 // "SURVIVOR_TEAM"
2C B3 89 00 00 29 04 {00 01 0C 03}
00 00

46 00
AB 14 04 01 FF C0 4E 00 36 40 35 00 00 00 00 08 80 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
AD 14 03 01 FF 80 34 40 3C 40 31 00 00 00 00 08 80 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
AE 14 13 01 FF 40 38 00 52 40 35 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
AF 14 12 01 FF 00 48 00 3C C0 30 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
B0 14 0D 01 FF C0 3E 00 4E 00 2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 01 04 00 00
B2 14 0D 01 FF 40 3B 00 4E 00 2F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 01 04 00 00
B3 14 05 01 FF 00 4F 80 46 80 35 00 00 00 00 08 80 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
B4 14 14 01 FF 00 4F 80 46 80 35 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
B5 14 18 01 07 E8 49 F3 46 B0 26 2A 05 1C 8B EE 02 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
B6 14 04 01 FF 40 38 00 52 40 35 00 00 00 00 08 80 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
BB 14 0B 02 FF AC 42 8F 3D 7C 38 00 00 00 00 C8 00 00 00 C0 3F C0 3F C0 3F 00 00 04 5A 01
BE 14 12 01 FF C0 38 00 3D 40 35 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
BF 14 0D 01 FF C0 38 00 4A C0 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 01 04 00 00
...
02
80 2B 40 44 40 32
C0 2C C0 36 80 4B

02
00 00 E6 47 F3 85
00 00 4D 21 73 88

00

Packet log from a Classic CTF game (v122):

2C 06 7F 80 80 40 00 DC C0 B4 0D 00 33 00 00 00 40 40 40 06 80 26 80 13 00 40 38 34 0D 00 40 00 05 08 01
54 45 41 4D 31 5F 43 4F 4C 4F 52 00
B3 75 2C 00 00 0C 01 {05}
54 45 41 4D 32 5F 43 4F 4C 4F 52 00
2C B3 89 01 00 0C 01 {05}
01 00

04 00
21 00 10 03 FF 23 75 62 46 80 3B 00 00 00 00 08 80 00 00 40 22 C0 2C 00 0B 00 00 04 00 0F
22 00 19 03 FF 80 73 40 45 00 3A 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00
23 00 10 02 FF DB 12 31 3E C0 3A 00 00 00 00 08 80 00 00 00 0B 40 1D C0 2C 00 00 04 00 0F
24 00 19 02 FF 40 09 80 40 00 3A 00 00 00 00 00 00 00 00 C0 3F C0 3F C0 3F 00 00 04 00 00

03
40 29 00 59 00 4A
C0 2B 40 0D 40 1E
C0 2B FF 7F 80 5B

03
00 00 73 08 9A 8A
00 00 33 A4 26 8A
00 00 1A 08 73 8A

00

0x31: Chat Message

  • Byte player_id
  • Byte message_type (0x00 = global, 0x01 = team)
  • ASCIIZ_String chat_message

0x33: Mesh File

  • ASCIIZ_String mesh_file

Let M = mesh_file minus the .txt extension.

Then, read "mesh/" + M + "/" + M + ".txt" as a JSON file.

The mesh format hasn't been properly looked into yet, but as far as binary formats go, it should be easy to work out.

0x36: Map Send Start

Marks the start of map transmission. This only happens if you're missing a map or if you have the incorrect version of a map.

(Information is for v122 - needs updating for v163)

0x37: Map Sync Start [0x32]

Marks the start of delta map data transmission.

Unlike AoS 0.x, this has no extra data, so if you're using C, get ready to realloc().

0x38: Map Send Data

  • Byte loading_bar
  • Word size
  • Byte[size] data

Map data gets sent in chunks!

(Information is for v122 - needs updating for v163)

0x39: Map Sync Data [0x31]

  • Byte loading_bar? (seems to always be 100)
  • Word size
  • Byte[size] data

Delta map data gets sent in chunks!

0x3A: Map Send End

Marks the end of map data transmission.

(Information is for v122 - needs updating for v163)

The map data is zlib-compressed.

After this, the server sends a checksum (0x3B) packet, just to make sure you received the map correctly. The client then sends a checksum (0x3B) back, and the server starts doing the Map Sync stuff.

The format of this data has not been confirmed because I don't know the format of some packets yet, but it's probably just .vxl data.

0x3B: Map Sync End [0x31]

Marks the end of delta map data transmission.

The map data is zlib-compressed.

It's not pure vxl, though. It looks like some sort of delta.

The map format is an array of entries of this format:

Note, the "light level" tends to be things other than 0xFF. Sometimes it's 0x80. The 0x80 probably comes from the prefabs, which are KV6 files.

This is the most well-written part of the protocol I have seen so far. It's such a relief when you encounter nice things like this.

0x3C: Incoming Map Checksum [0x31]

  • DWord checksum

This is just a regular CRC-32 on the .vxl data. Nothing fancy.

Client-to-Server

Good news everyone! Client-to-server communications are uncompressed!

Bad news. They're encrypted with your "steam key".

Good news, the encryption is easy to break.

Note, some packets are compressed. They start with 0x31 instead of a 0x30, and they are most likely compressed immediately after encraption.

0x62: Steam Key (unencrypted)

  • DWord key_length (includes everything after this DWord)
  • key_length bytes (hex-encoded in Beta; straight binary data in 122):
    • QWord steam_id

Then some blocks follow. Expect your Steam ID to appear a total of 3 times in this packet.

Everything after the key_length field is used as a mask to XOR against. We'll call this variable "steam_key" for lack of imagination. When we run out of key, we wrap around.

Here's some Python code to illustrate this. This actually works.

def str_xor(s):
        rs = ""

        for i in xrange(len(s)):
                rs += chr(ord(s[i]) ^ ord(steam_key[i % len(steam_key)]))

        return rs

Here's some C code. This hasn't been tested at all.

char *steam_key;
int steam_key_length;

void str_decrypt(char *s, int size)
{
	int i;

	for(i = 0; i < size; i++)
		s[i] ^= steam_key[i % steam_key_length];
}

Both are placed in the public domain because I am awful at explaining some things. Use them. Please.

v163 uses a hex string, but you use the hex string directly for compression instead of decoding the hex first.

0x00: Ping Pong

  • DWord client_time
  • DWord server_time (0 for client to server packets)

0x04: Player Input

  • DWord timestamp
  • Byte player_id (should be 0x00 for C->S)
  • Byte tool_id
  • Fixed <x, y, z> direction
  • Byte sync_counter (0x00 through 0x0F, increments every packet)
  • Word input
  • Word unknown (usually 0x0000)

Appears to be unchanged in v163.

0x06: Shoot

  • DWord timestamp
  • Byte attacker_player_id (should be 0x00 for C->S)
  • DWord world_update_timestamp - this is to tell the server which World Update frame to take the shot from
  • Float <x,y,z> position
  • Float <x,y,z> orientation
  • Byte tool_id?
  • unknown Word
  • unknown Byte

Data unaccounted for (names ripped from module strings):

  • Damage
  • Penetration
  • Affect shooter
  • Secondary
  • Seed
packet 0x6 39 ............C...C.}.B..O?..../c.>...... E6 14 00 00 00 B8 14 00 00 CC AC 85 43 D0 BB 8B 43 91 7D DD 42 0D C8 4F 3F FE 13 00 BF 2F 63 9A 3E 01 00 02 00 00 E0 
packet 0x6 39 ............C...C.}.B..P?....vi.>.....: C6 14 00 00 00 BC 14 00 00 F1 AC 85 43 D0 BB 8B 43 91 7D DD 42 D3 9B 50 3F A3 7F 00 BF 76 69 94 3E 01 00 02 00 00 3A
packet 0x6 39 ............C...C.}.B@.M?.......>...... CD 14 00 00 00 C0 14 00 00 09 AD 85 43 D0 BB 8B 43 91 7D DD 42 40 16 4D 3F AF 18 07 BF 0F 8F 90 3E 01 00 02 00 00 0B 
packet 0x6 39 ............Cz..C.}.B..A?."..`..>...... D4 14 00 00 00 C4 14 00 00 D5 AE 85 43 7A BA 8B 43 91 7D DD 42 EB 7F 41 3F 7F 22 18 BF 60 B2 8C 3E 01 00 02 00 00 83
packet 0x6 39 ............C9..C.}.BN4<?.o!...~>.....d DB 14 00 00 00 C6 14 00 00 A5 C3 85 43 39 AA 8B 43 91 7D DD 42 4E 34 3C 3F E9 6F 21 BF 90 A8 7E 3E 01 00 02 00 00 64 
packet 0x6 39 ............C...C.}.B..&?R.5.G..>...... E2 14 00 00 00 CA 14 00 00 E7 E7 85 43 09 8C 8B 43 91 7D DD 42 CC 86 26 3F 52 18 35 BF 47 8E 8D 3E 01 00 02 00 00 BB
packet 0x6 39 ............C}b.C.}.B*Q.>..O..N.>...... D6 14 00 00 00 CE 14 00 00 84 13 86 43 7D 62 8B 43 91 7D DD 42 2A 51 FB 3E CD 8D 4F BF A2 4E A3 3E 01 00 02 00 00 07 
packet 0x6 39 ..........?.C...C...B8..>..^....>...... DD 14 00 00 00 D2 14 00 00 96 3F 86 43 F0 2E 8B 43 9A A9 DD 42 38 ED C2 3E 96 80 5E BF C0 9C A1 3E 01 00 02 00 00 89
packet 0x6 39 ..........D.C...C...B8aC..7..yr.?...... E2 15 00 00 00 CE 15 00 00 CF 44 86 43 16 A5 86 43 C6 F6 E3 42 38 61 43 BF AB 37 A1 BE 79 72 10 3F 01 00 02 00 00 BE 
packet 0x6 39 ".........<.C.^.C.}.B.t+....?c..>...... 22 17 00 00 00 16 17 00 00 8F 3C 88 43 C6 5E 86 43 BB 7D DD 42 92 74 2B BF 8C 2E 1B 3F 63 9D DB 3E 05 00 00 00 00 00
packet 0x6 39 <....$...{;.C.`.C.}.B.+{.F..?.{S?...... 3C 17 00 00 00 24 17 00 00 7B 3B 88 43 AE 60 86 43 BB 7D DD 42 9C 2B 7B BE 46 E1 01 3F 2E 7B 53 3F 05 00 00 00 00 00 
packet 0x6 39 [....B......CQ..C.J.B...>kg*?.".?...... 5B 17 00 00 00 42 17 00 00 E3 14 88 43 51 04 87 43 88 4A DF 42 F2 FF DE 3E 6B 67 2A 3F 0E 22 1B 3F 05 00 00 00 00 00
packet 0x6 39 t....N....F.C?D.C.J.B...?..(?W..?...... 74 17 00 00 00 4E 17 00 00 04 46 88 43 3F 44 87 43 88 4A DF 42 9B 06 04 3F AB FF 28 3F 57 CD 0B 3F 05 00 00 00 00 00

0x0A: Throw

  • DWord timestamp
  • Byte player_id (should be 0x00 for C->S)
  • Byte tool_id
  • Word value?
  • Fixed <x,y,z> position?
  • Fixed <x,y,z> velocity?

7E 15 00 00 00 0B 00 00 63 43 75 43 30 1C 1E 8E 6D 86 8A 0A 7E 15 00 00 00 0B 00 00 63 43 75 43 30 1C 1E 8E 6D 86 8A 0A

packet 0x4 17 |................ 7C 15 00 00 00 0B 17 98 F6 8A FD 11 07 00 11 00 00                               
packet 0x4 17 }................ 7D 15 00 00 00 0B 17 98 F6 8A FD 11 08 00 11 00 00                               
packet 0xa 20 ~.......cCuC0...m... 7E 15 00 00 00 0B 00 00 63 43 75 43 30 1C 1E 8E 6D 86 8A 0A                   
packet 0xa 20 ~.......cCuC0...m... 7E 15 00 00 00 0B 00 00 63 43 75 43 30 1C 1E 8E 6D 86 8A 0A                   
packet 0x4 17 ~................ 7E 15 00 00 00 0B 17 98 F6 8A FD 11 09 00 00 00 00                               
packet 0x4 17 ................. 7F 15 00 00 00 0B 17 98 F6 8A FD 11 0A 00 08 00 00

0x0B: Block Colour

  • Byte unknown (usually 0x00, probably the player ID)
  • Byte bgr[3]

0x0D: Choose Loadout

  • Byte player_id (usually 0xFF)
  • Byte class
  • Byte is_auto_selected? (usually 0x00)
  • Byte loadout_size
  • Byte loadout[loadout_size]
  • Byte prefab_count
  • ASCIIZ prefab_list[prefab_count]
  • Byte ugc_size
    • unknown structure

This is the only packet I've seen that gets "compressed" by the client, but the compression in this case is awful. You can send this one uncompressed if you like. It still works.

Example packet, with data in bold (encrypted before LZF-compression applied): 31 0F 0D FF 05 00 09 05 04 06 1F 19 1A 1E 16 17 00 00

0x0F: Announce Player

  • Byte team
  • Byte class
  • Byte forced_to_team
  • Byte language
  • ASCIIZ display_name

Here's the language codes:

  • 0x00: English
  • 0x01: German
  • 0x02: French
  • 0x03: Spanish
  • 0x04: Italian
  • 0x05: Brazillian Portuguese
  • 0x06: Russian
  • 0x07: Polish
  • 0x08: Turkish
  • 0x09: Mexican

0x31: Send Chat Message

  • Byte player_id (should be 0x00 for C->S)
  • Byte message_type (0x00 = main chat, 0x01 = team chat)
  • ASCIIZ message

Shouldn't be hard to work out.

0x3C: Expected Map Checksum

  • DWord checksum

This is just a regular CRC-32 on the .vxl data. Nothing fancy.

If the CRC-32 on the server matches this, then the server can just skip sending the map data. I think.

0x4D: Set Team

  • Byte player_id (should be 0x00 for C->S)
  • Byte team

0x4E: Set Class

  • Byte player_id (should be 0x00 for C->S)
  • Byte class

0x67: Menu open

  • Byte is_open (either 0x00 or 0x01)

Tells the server that you have a menu open, so it doesn't try to respawn you inconveniently.

I've suspected this for a while.