Difference between revisions of "Network protocol (Worms Armageddon)"
From Worms Knowledge Base
Madewokherd (Talk | contribs) (→Step 1) |
m |
||
(48 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
+ | {{ParentArticle|[[Game logic]]}} | ||
== General == | == General == | ||
* Sizes and offsets are in bytes | * Sizes and offsets are in bytes | ||
Line 4: | Line 5: | ||
* Word size = 2 bytes | * Word size = 2 bytes | ||
* DWord size = 4 bytes | * DWord size = 4 bytes | ||
− | * Everything is LSB on the wire | + | * Everything is LSB on the wire (Little Endian) |
+ | |||
+ | Is it possible to write a bot? | ||
+ | |||
+ | It surely is. However the bot doesn't have access to very much information. It cannot tell who won or lost for example. The game relies on all of the clients performing identical simulations. So nothing other than what you get in a replay file is transmitted for each turn. This excludes any win/or loss data or anything like that. Your bot could spectate games, chat, and perform all the lobby actions and that's about it on its own. A bot ''could'' hook into the actual game, but doing so without informing other players is the difference between writing your own AI and cheating with an aimbot. | ||
+ | |||
+ | A minimal client with a readline command interface is available here https://github.com/giannitedesco/wabs under the terms of the GPLv3. | ||
== Message header == | == Message header == | ||
− | {| | + | {| class="wikitable" |
− | !Offset | + | ! Offset !! Size !! Value |
− | !Size | + | |
− | !Value | + | |
|- | |- | ||
− | | | + | | 0 || Byte || 1 for frontend messages, 2 for ingame (compression ?) |
− | |Byte | + | |
− | |1 for frontend messages, 2 for ingame (compression?) | + | |
|- | |- | ||
− | | | + | | 1 || Byte || (??) may be ignored |
− | |Byte | + | |
− | |(??) | + | |
|- | |- | ||
− | | | + | | 2 || Word || Message size (max is 0x1414, if larger, the header(s) is(are) sent again) |
− | |Word | + | |
− | |Message size | + | |
|- | |- | ||
− | | | + | | 4 || Word || Command |
− | |Word | + | |
− | |Command | + | |
|} | |} | ||
Length: 6 | Length: 6 | ||
+ | |||
+ | For in-game messages where the second byte is set to 2, the structure is slightly different. | ||
+ | {| class="wikitable" | ||
+ | ! Offset !! Size !! Value | ||
+ | |- | ||
+ | | 0 || Byte || 2 | ||
+ | |- | ||
+ | | 1 || Byte || (??) may be ignored | ||
+ | |- | ||
+ | | 2 || Word || Message size (max is 0x1414, if larger, the header(s) is(are) sent again) | ||
+ | |- | ||
+ | | 4 || Byte || Player ID | ||
+ | |- | ||
+ | | 5 || Word || Flags? Maybe a bitmap? Observed values of 1, 2 and 4. | ||
+ | |- | ||
+ | | 7 || Word || Frame sequence number. Incremented every time a message is sent. | ||
+ | |} | ||
+ | Length:9 | ||
+ | |||
+ | == Packet Commands List == | ||
+ | {| class="wikitable" | ||
+ | ! Hex !! Dec !! Description | ||
+ | |- | ||
+ | | 0x00 || 00 || [[#Chat Message|Chat message]] | ||
+ | |- | ||
+ | | 0x01 || 01 || ??????? | ||
+ | |- | ||
+ | | 0x02 || 02 || ??????? | ||
+ | |- | ||
+ | | 0x03 || 03 || ??????? | ||
+ | |- | ||
+ | | 0x04 || 04 || Login from client (completeme) | ||
+ | |- | ||
+ | | 0x05 || 05 || Another login from client (completeme) | ||
+ | |- | ||
+ | | 0x06 || 06 || Client sends password (completeme) | ||
+ | |- | ||
+ | | 0x07 || 07 || Password Required (no data) | ||
+ | |- | ||
+ | | 0x08 || 08 || Sucessfully joined (or password ok) need to figure out what the data means (version of the host ??) | ||
+ | |- | ||
+ | | 0x09 || 09 || Wrong password (no data) | ||
+ | |- | ||
+ | | 0x0A || 10 || Error joining (nickname already used, or missing, or flag missing/incorrect) < need to figure out exact symptoms | ||
+ | |- | ||
+ | | 0x0B || 11 || [[#Player list|Player list]] | ||
+ | |- | ||
+ | | 0x0C || 12 || [[#Team list|Team list]] | ||
+ | |- | ||
+ | | 0x0D || 13 || [[#Custom Scheme data|Custom scheme data]] | ||
+ | |- | ||
+ | | 0x0E || 14 || Player joined (sent to already connected players) | ||
+ | |- | ||
+ | | 0x0F || 15 || [[#Ready signal|Ready signal]] | ||
+ | |- | ||
+ | | 0x10 || 16 || [[#Remove Team|Remove team]] | ||
+ | |- | ||
+ | | 0x11 || 17 || [[#Changing Scheme data|Changing scheme data]] Round Time | ||
+ | |- | ||
+ | | 0x12 || 18 || [[#Changing Scheme data|Changing scheme data]] Turn Time | ||
+ | |- | ||
+ | | 0x13 || 19 || [[#Changing Scheme data|Changing scheme data]] Victories needed | ||
+ | |- | ||
+ | | 0x14 || 20 || [[#Changing Scheme data|Changing scheme data]] Worm Selection | ||
+ | |- | ||
+ | | 0x15 || 21 || [[#Changing Scheme data|Changing scheme data]] Worms HP | ||
+ | |- | ||
+ | | 0x16 || 22 || [[#Change Team|Change team]] - team color | ||
+ | |- | ||
+ | | 0x17 || 23 || [[#Change Team|Change team]] - team handicap | ||
+ | |- | ||
+ | | 0x18 || 24 || [[#Change Team|Change team]] - number of worms | ||
+ | |- | ||
+ | | 0x19 || 25 || Player left the game (sent to connected players) | ||
+ | |- | ||
+ | | 0x1A || 26 || [[#Add Team|Add team]] | ||
+ | |- | ||
+ | | 0x1B || 27 || You have been Kicked (no data) | ||
+ | |- | ||
+ | | 0x1C || 28 || [[#Starting Game|Starting game]] | ||
+ | |- | ||
+ | | 0x1D || 29 || [[#Changing Scheme data|Changing scheme data]] Worm Placment | ||
+ | |- | ||
+ | | 0x1E || 30 || ????????? | ||
+ | |- | ||
+ | | 0x1F || 31 || [[#Default Scheme data|Default scheme data]] | ||
+ | |- | ||
+ | | 0x20 || 32 || Default Maps (like ArtClass) just a String with the map name, 40 bytes long | ||
+ | |- | ||
+ | | 0x22 || 34 || This packet is sent from server when version = 0, very weird. | ||
+ | |- | ||
+ | | 0x23 || 35 || The client sends this after the 0x22 , can't get it to work. | ||
+ | |- | ||
+ | | 0x24 || 36 || Server sends this when a game is over. It seems to be a sort of ping. | ||
+ | |- | ||
+ | | 0x25 || 37 || Client sends this in response to a 0x24. If not the server keeps resending the 0x24 packet and the player appears to have timed out in the post-game lobby area. | ||
+ | |- | ||
+ | | 0x26 || 38 || The client sends this after the 0x22 , can't get it to work. The server also sends this after a game has finished, and after sending the 0x24 packet. | ||
+ | |- | ||
+ | | 0x27 || 39 || Gameid incorrect (or no more place ?) | ||
+ | |- | ||
+ | | 0x2B || 43 || [[#Map|Map]] | ||
+ | |} | ||
+ | |||
+ | == Chat Message == | ||
+ | {| style="text-align: left" | ||
+ | !Command: | ||
+ | |0x0000 | ||
+ | |- | ||
+ | !Direction: | ||
+ | |Both | ||
+ | |- | ||
+ | !Size: | ||
+ | |Variable | ||
+ | |} | ||
+ | Just a String, 00 Terminated. | ||
+ | |||
+ | Type:FromNick:ToNick:Text | ||
+ | |||
+ | Differents Types | ||
+ | |||
+ | Normal Message : GLB | ||
+ | |||
+ | Private Message : PRV | ||
+ | |||
+ | System Message : SYS | ||
+ | |||
+ | Notes : | ||
+ | |||
+ | - ToNick is often set to "ALL" to be sent to everyone (doesn't work for whispers) | ||
+ | |||
+ | - When X types /me Text, it sends this : SYS:X:ALL:X Text | ||
+ | |||
+ | - For systems messages, FromNick can be null, since it's not shown | ||
== Join == | == Join == | ||
Line 47: | Line 178: | ||
* Nick at 06, null terminated, ends (last place the null could be) at 22. | * Nick at 06, null terminated, ends (last place the null could be) at 22. | ||
* Gameid (ascii) at 23, null string for IP games. | * Gameid (ascii) at 23, null string for IP games. | ||
+ | * Version at 64, composed of 30 24 F4 (hex) | ||
+ | NB : these 3 parameters are sufficient to enter a game. Everything else can be 0 and it's ok. | ||
Figure out what follows. | Figure out what follows. | ||
Line 77: | Line 210: | ||
* Nick at 06, null terminated. | * Nick at 06, null terminated. | ||
+ | * Flag (+1) at 72 (if 13 is France, then to put French flag, you have to put 13+1 = 0x0E) | ||
Figure out what follows. | Figure out what follows. | ||
Line 106: | Line 240: | ||
Figure out what the rest is. | Figure out what the rest is. | ||
− | + | There are 6 bytes of unknown, followed by 7 player structs. The structure for each player is: | |
− | + | {| class="wikitable" | |
+ | ! Offset !! Size !! Value | ||
+ | |- | ||
+ | | 0 || 17 || name | ||
+ | |- | ||
+ | | 17 || 108 || unknown | ||
+ | |- | ||
+ | | 125 || 2 || prev, signed integer pointing to previous player, set to -1 for first entry | ||
+ | |- | ||
+ | | 127 || 2 || unknown | ||
+ | |- | ||
+ | | 129 || 1 || country code (for flag) | ||
+ | |- | ||
+ | | 130 || 7 || unknown | ||
+ | |} | ||
− | + | After that, there is 520 bytes unknown, a 16 byte integer with the total number of valid players and then 2 bytes unknown. | |
− | + | ||
== Team list == | == Team list == | ||
Line 124: | Line 271: | ||
|} | |} | ||
− | + | The structure here is subtly different to the WGT format. Furthermore the [[#Add Team]] message is completely different again. And reverse engineering that is complicated by the fact that the game doesn't appear to scrub the unused bytes in the structure to zero. So it leaks some random heap or stack that way. | |
− | == Scheme data == | + | {| class="wikitable" |
+ | ! Offset !! Size !! Value | ||
+ | |- | ||
+ | | 0 || Word || Unknown | ||
+ | |- | ||
+ | | 2 || Word || Slot number in the teams-list | ||
+ | |- | ||
+ | | 4 || Word || Unknown | ||
+ | |- | ||
+ | | 6 || Byte || Player ID | ||
+ | |- | ||
+ | | 7 || Byte || Color | ||
+ | |- | ||
+ | | 8 || Byte || Unknown | ||
+ | |- | ||
+ | | 9 || 17 || Team name | ||
+ | |- | ||
+ | | 26 || 32 || Soundbankk name | ||
+ | |- | ||
+ | | 58 || Word || Soundbank flags? | ||
+ | |- | ||
+ | | 60 || 30 || Fanfare | ||
+ | |- | ||
+ | | 90 || Byte || Unknown | ||
+ | |- | ||
+ | | 91 || 30 || Fanfare again, why? | ||
+ | |- | ||
+ | | 121 || 41 || Unknown | ||
+ | |- | ||
+ | | 162 || 8 * 17 || Worm names | ||
+ | |- | ||
+ | | 298 || 3160 || Unknown, but some similarities to the WGT file. There's certainly a flag bitmap in there among other things | ||
+ | |} | ||
+ | |||
+ | == Custom Scheme data == | ||
{| style="text-align: left" | {| style="text-align: left" | ||
!Command: | !Command: | ||
− | |0x000d | + | |0x000d (13dec) |
|- | |- | ||
!Direction: | !Direction: | ||
Line 138: | Line 319: | ||
|} | |} | ||
− | + | {| class="wikitable" | |
− | Word | + | ! Offset !! Size !! Value |
− | + | |- | |
+ | | 0 || Word || (??) | ||
+ | |- | ||
+ | | 2 || DWord || An identifier for the scheme. (-1 for custom, scheme, 1-13 for default (edited) scheme) | ||
+ | |- | ||
+ | | 6 || 292 bytes || Scheme file withouth "SCHM" header | ||
+ | |- | ||
+ | | 298 || DWORD || (?) | ||
+ | |} | ||
− | + | == Default Scheme data == | |
− | + | {| style="text-align: left" | |
+ | !Command: | ||
+ | |0x001f (31dec) | ||
+ | |- | ||
+ | !Direction: | ||
+ | |To client | ||
+ | |- | ||
+ | !Size: | ||
+ | |12 | ||
+ | |} | ||
+ | {| class="wikitable" | ||
+ | ! Offset !! Size !! Value | ||
+ | |- | ||
+ | | 0 || Word || (??) | ||
+ | |- | ||
+ | | 2 || DWord || An identifier for the scheme. | ||
+ | |} | ||
+ | {| class="wikitable" | ||
+ | ! Identifier !! Scheme | ||
+ | |- | ||
+ | | 1 || Beginner | ||
+ | |- | ||
+ | | 2 || Intermediate | ||
+ | |- | ||
+ | | 3 || Pro | ||
+ | |- | ||
+ | | 6 || Artillery | ||
+ | |- | ||
+ | | 7 || Classic | ||
+ | |- | ||
+ | | 8 || Armageddon | ||
+ | |- | ||
+ | | 9 || The Darkside | ||
+ | |- | ||
+ | | 11 || Retro | ||
+ | |- | ||
+ | | 13 || Strategic | ||
+ | |- | ||
+ | | 14 || Sudden Sinking | ||
+ | |- | ||
+ | | 15 || Tournament | ||
+ | |- | ||
+ | | 16 || Blast Zone | ||
+ | |- | ||
+ | | 17 || The Full Wormage | ||
+ | |} | ||
+ | If you put 0 as Scheme Identifier, it will crash the game. | ||
+ | Any other number that doesn't appear in the list will put Intermediate. | ||
− | + | == Changing Scheme data == | |
− | + | {| style="text-align: left" | |
− | + | !Command: | |
− | + | |Depends | |
+ | |- | ||
+ | !Direction: | ||
+ | |To client | ||
+ | |- | ||
+ | !Size: | ||
+ | |12 | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" | ||
+ | ! Offset !! Size !! Value | ||
+ | |- | ||
+ | | 0 || Word || Always 0x3E (62dec) ? | ||
+ | |- | ||
+ | | 2 || DWord || Value | ||
+ | |} | ||
+ | |||
+ | {| class="wikitable" | ||
+ | ! Command !! Command (dec) !! Description | ||
+ | |- | ||
+ | | 0x11 || 17 || Round Time (Positive = Minutes, Negative = Seconds) | ||
+ | |- | ||
+ | | 0x12 || 18 || Turn Time (FFFFFFFF = Infinite) | ||
+ | |- | ||
+ | | 0x13 || 19 || Victories needed | ||
+ | |- | ||
+ | | 0x14 || 20 || Worm Selection (0 = Disabled ; 1 = Enabled) | ||
+ | |- | ||
+ | | 0x15 || 21 || Worms HP | ||
+ | |- | ||
+ | | 0x1D || 29 || Worm Placment (0 = Disabled ; 1 = Enabled) | ||
+ | |} | ||
== Ready signal == | == Ready signal == | ||
Line 162: | Line 429: | ||
|} | |} | ||
− | + | {| class="wikitable" | |
+ | ! Offset !! Size !! Value | ||
+ | |- | ||
+ | | 08 || DWord || on/off | ||
+ | |- | ||
+ | | 0B || DWord || Player number | ||
+ | |} | ||
− | + | == Starting Game == | |
− | + | {| style="text-align: left" | |
+ | !Command: | ||
+ | |0x001c | ||
+ | |- | ||
+ | !Direction: | ||
+ | |To client | ||
+ | |- | ||
+ | !Size: | ||
+ | |20 | ||
+ | |} | ||
− | = | + | {| class="wikitable" |
− | ( | + | ! Offset !! Size !! Value |
+ | |- | ||
+ | | 6 || Word || I guess this is only not important junk and can be anything (need verification) | ||
+ | |- | ||
+ | | 08 || DWord || HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Data\LogicSeed | ||
+ | |- | ||
+ | | 0C || DWord || "GSAW" (this is hardcoded, but i think it can be anything, not important) | ||
+ | |- | ||
+ | | 10 || DWord || Version of the Game (4C for the normal 3.6.28.0) | ||
+ | |} | ||
+ | |||
+ | After this, we switch to type-2 packets documented above. All players send loading status reports up to frame number 0x1b. The report for 0x1b contains some sort of magic which is presumably a sort of checksum on the game logic such as worm placement and map generation. If it's not correct then the game notifies of invalid data and won't let the game start. Best bet is to listen for one player to finish and echo his magic number back. | ||
+ | |||
+ | Here is an example of what is received when 2 other players are in the game: | ||
+ | |||
+ | <tt> | ||
+ | Frame: player=0 frame=0x00000001 len=4 | ||
+ | | 00000 : .... c0 0a 00 00 | ||
+ | Frame: player=0 frame=0x00000002 len=4 | ||
+ | | 00000 : .... c0 0a 04 00 | ||
+ | Frame: player=2 frame=0x00000001 len=4 | ||
+ | | 00000 : .... c0 0a 00 00 | ||
+ | Frame: player=2 frame=0x00000002 len=4 | ||
+ | | 00000 : .... c0 0a 04 00 | ||
+ | Frame: player=2 frame=0x00000003 len=4 | ||
+ | | 00000 : .... c0 0a 08 00 | ||
+ | Frame: player=0 frame=0x00000003 len=4 | ||
+ | | 00000 : .... c0 0a 08 00 | ||
+ | Frame: player=0 frame=0x00000004 len=4 | ||
+ | | 00000 : .... c0 0a 0c 00 | ||
+ | |||
+ | |||
+ | snip ... | ||
+ | |||
+ | Frame: player=0 frame=0x0000001a len=4 | ||
+ | | 00000 : ..d. c0 0a 64 00 | ||
+ | Frame: player=2 frame=0x0000001a len=4 | ||
+ | | 00000 : ..d. c0 0a 64 00 | ||
+ | Frame: player=2 frame=0x0200001b len=7 | ||
+ | | 00000 : @.GQ{7. 40 09 47 51 7b 37 00 | ||
+ | Frame: player=0 frame=0x0200001b len=7 | ||
+ | | 00000 : @.GQ{7. 40 09 47 51 7b 37 00 | ||
+ | </tt> | ||
+ | |||
+ | == Map == | ||
{| style="text-align: left" | {| style="text-align: left" | ||
Line 181: | Line 507: | ||
|} | |} | ||
− | + | {| class="wikitable" | |
+ | ! Offset !! Size !! Value | ||
+ | |- | ||
+ | | 06 || Byte || (??) | ||
+ | |- | ||
+ | | 07 || Byte || Always 01 ? | ||
+ | |- | ||
+ | | 08 || DWord || File Length | ||
+ | |- | ||
+ | | 0C || DWord || Data Offset | ||
+ | |- | ||
+ | | 10 || DWord || Data Length | ||
+ | |- | ||
+ | | 14 || Data Length || Data | ||
+ | |} | ||
+ | |||
+ | Note : A DWord is put at the beginning of the File to tell what type of Map is sent. | ||
+ | |||
+ | 1 = .BIT [[Monochrome map (.bit, .lev)]] | ||
+ | |||
+ | 2 = .LEV [[Monochrome map (.bit, .lev)]] | ||
+ | |||
+ | 3 = .PNG [[Colour map]] | ||
+ | |||
+ | Exemple of a multi-packeted BIT-file (135434(0x34DE)bytes file): | ||
+ | |||
+ | <01 48 1414 2B00> <A2 01 [E2 34 00 00] [00 00 00 00] [00 14 00 00]> [ 01 00 00 00 { .BIT file DATA } ] | ||
+ | |||
+ | <01 48 1414 2B00> <A2 01 [E2 34 00 00] [00 14 00 00] [00 14 00 00]> [ { .BIT file DATA } ] | ||
+ | |||
+ | <01 9C F60C 2B00> <A5 01 [E2 34 00 00] [00 28 00 00] [E2 0C 00 00]> [ { .BIT file DATA } ] |
Latest revision as of 22:05, 16 April 2022
Contents
General
- Sizes and offsets are in bytes
- All offsets are from the start of the packet's data
- Word size = 2 bytes
- DWord size = 4 bytes
- Everything is LSB on the wire (Little Endian)
Is it possible to write a bot?
It surely is. However the bot doesn't have access to very much information. It cannot tell who won or lost for example. The game relies on all of the clients performing identical simulations. So nothing other than what you get in a replay file is transmitted for each turn. This excludes any win/or loss data or anything like that. Your bot could spectate games, chat, and perform all the lobby actions and that's about it on its own. A bot could hook into the actual game, but doing so without informing other players is the difference between writing your own AI and cheating with an aimbot.
A minimal client with a readline command interface is available here https://github.com/giannitedesco/wabs under the terms of the GPLv3.
Message header
Offset | Size | Value |
---|---|---|
0 | Byte | 1 for frontend messages, 2 for ingame (compression ?) |
1 | Byte | (??) may be ignored |
2 | Word | Message size (max is 0x1414, if larger, the header(s) is(are) sent again) |
4 | Word | Command |
Length: 6
For in-game messages where the second byte is set to 2, the structure is slightly different.
Offset | Size | Value |
---|---|---|
0 | Byte | 2 |
1 | Byte | (??) may be ignored |
2 | Word | Message size (max is 0x1414, if larger, the header(s) is(are) sent again) |
4 | Byte | Player ID |
5 | Word | Flags? Maybe a bitmap? Observed values of 1, 2 and 4. |
7 | Word | Frame sequence number. Incremented every time a message is sent. |
Length:9
Packet Commands List
Hex | Dec | Description |
---|---|---|
0x00 | 00 | Chat message |
0x01 | 01 | ??????? |
0x02 | 02 | ??????? |
0x03 | 03 | ??????? |
0x04 | 04 | Login from client (completeme) |
0x05 | 05 | Another login from client (completeme) |
0x06 | 06 | Client sends password (completeme) |
0x07 | 07 | Password Required (no data) |
0x08 | 08 | Sucessfully joined (or password ok) need to figure out what the data means (version of the host ??) |
0x09 | 09 | Wrong password (no data) |
0x0A | 10 | Error joining (nickname already used, or missing, or flag missing/incorrect) < need to figure out exact symptoms |
0x0B | 11 | Player list |
0x0C | 12 | Team list |
0x0D | 13 | Custom scheme data |
0x0E | 14 | Player joined (sent to already connected players) |
0x0F | 15 | Ready signal |
0x10 | 16 | Remove team |
0x11 | 17 | Changing scheme data Round Time |
0x12 | 18 | Changing scheme data Turn Time |
0x13 | 19 | Changing scheme data Victories needed |
0x14 | 20 | Changing scheme data Worm Selection |
0x15 | 21 | Changing scheme data Worms HP |
0x16 | 22 | Change team - team color |
0x17 | 23 | Change team - team handicap |
0x18 | 24 | Change team - number of worms |
0x19 | 25 | Player left the game (sent to connected players) |
0x1A | 26 | Add team |
0x1B | 27 | You have been Kicked (no data) |
0x1C | 28 | Starting game |
0x1D | 29 | Changing scheme data Worm Placment |
0x1E | 30 | ????????? |
0x1F | 31 | Default scheme data |
0x20 | 32 | Default Maps (like ArtClass) just a String with the map name, 40 bytes long |
0x22 | 34 | This packet is sent from server when version = 0, very weird. |
0x23 | 35 | The client sends this after the 0x22 , can't get it to work. |
0x24 | 36 | Server sends this when a game is over. It seems to be a sort of ping. |
0x25 | 37 | Client sends this in response to a 0x24. If not the server keeps resending the 0x24 packet and the player appears to have timed out in the post-game lobby area. |
0x26 | 38 | The client sends this after the 0x22 , can't get it to work. The server also sends this after a game has finished, and after sending the 0x24 packet. |
0x27 | 39 | Gameid incorrect (or no more place ?) |
0x2B | 43 | Map |
Chat Message
Command: | 0x0000 |
---|---|
Direction: | Both |
Size: | Variable |
Just a String, 00 Terminated.
Type:FromNick:ToNick:Text
Differents Types
Normal Message : GLB
Private Message : PRV
System Message : SYS
Notes :
- ToNick is often set to "ALL" to be sent to everyone (doesn't work for whispers)
- When X types /me Text, it sends this : SYS:X:ALL:X Text
- For systems messages, FromNick can be null, since it's not shown
Join
Joining consists of several steps:
Step 1
Command: | 0x0004 |
---|---|
Direction: | From client |
Size: | 128 |
- Nick at 06, null terminated, ends (last place the null could be) at 22.
- Gameid (ascii) at 23, null string for IP games.
- Version at 64, composed of 30 24 F4 (hex)
NB : these 3 parameters are sufficient to enter a game. Everything else can be 0 and it's ok. Figure out what follows.
Step 2
Command: | 0x0008 |
---|---|
Direction: | To client |
Size: | 6 |
Acknowledgement. Empty message. Figure out error messages. (different commands?)
Step 3
Command: | 0x0005 |
---|---|
Direction: | From client |
Size: | 114 |
- Nick at 06, null terminated.
- Flag (+1) at 72 (if 13 is France, then to put French flag, you have to put 13+1 = 0x0E)
Figure out what follows.
Step 4
Server sends:
- Player list, see below.
- Team list (when not empty), see below.
- Map data, see below. (Map is not sent when we rejoin?)
- A message with command 0x001f? What is it?
Figure out how the scheme is sent here when it's not the default Intermediate
Player list
Command: | 0x000b |
---|---|
Direction: | To client |
Size: | 1176 |
This looks like it's sending a raw array of player structures. Each player structure is 120 bytes in size.
Names are limited to 17 characters, and are padded with 0 bytes. (If a name is exactly 17 characters, there will NOT be a null-terminator) Figure out what the rest is.
There are 6 bytes of unknown, followed by 7 player structs. The structure for each player is:
Offset | Size | Value |
---|---|---|
0 | 17 | name |
17 | 108 | unknown |
125 | 2 | prev, signed integer pointing to previous player, set to -1 for first entry |
127 | 2 | unknown |
129 | 1 | country code (for flag) |
130 | 7 | unknown |
After that, there is 520 bytes unknown, a 16 byte integer with the total number of valid players and then 2 bytes unknown.
Team list
Command: | 0x000c |
---|---|
Direction: | To client |
Size: | ?? |
The structure here is subtly different to the WGT format. Furthermore the #Add Team message is completely different again. And reverse engineering that is complicated by the fact that the game doesn't appear to scrub the unused bytes in the structure to zero. So it leaks some random heap or stack that way.
Offset | Size | Value |
---|---|---|
0 | Word | Unknown |
2 | Word | Slot number in the teams-list |
4 | Word | Unknown |
6 | Byte | Player ID |
7 | Byte | Color |
8 | Byte | Unknown |
9 | 17 | Team name |
26 | 32 | Soundbankk name |
58 | Word | Soundbank flags? |
60 | 30 | Fanfare |
90 | Byte | Unknown |
91 | 30 | Fanfare again, why? |
121 | 41 | Unknown |
162 | 8 * 17 | Worm names |
298 | 3160 | Unknown, but some similarities to the WGT file. There's certainly a flag bitmap in there among other things |
Custom Scheme data
Command: | 0x000d (13dec) |
---|---|
Direction: | To client |
Size: | 308 |
Offset | Size | Value |
---|---|---|
0 | Word | (??) |
2 | DWord | An identifier for the scheme. (-1 for custom, scheme, 1-13 for default (edited) scheme) |
6 | 292 bytes | Scheme file withouth "SCHM" header |
298 | DWORD | (?) |
Default Scheme data
Command: | 0x001f (31dec) |
---|---|
Direction: | To client |
Size: | 12 |
Offset | Size | Value |
---|---|---|
0 | Word | (??) |
2 | DWord | An identifier for the scheme. |
Identifier | Scheme |
---|---|
1 | Beginner |
2 | Intermediate |
3 | Pro |
6 | Artillery |
7 | Classic |
8 | Armageddon |
9 | The Darkside |
11 | Retro |
13 | Strategic |
14 | Sudden Sinking |
15 | Tournament |
16 | Blast Zone |
17 | The Full Wormage |
If you put 0 as Scheme Identifier, it will crash the game. Any other number that doesn't appear in the list will put Intermediate.
Changing Scheme data
Command: | Depends |
---|---|
Direction: | To client |
Size: | 12 |
Offset | Size | Value |
---|---|---|
0 | Word | Always 0x3E (62dec) ? |
2 | DWord | Value |
Command | Command (dec) | Description |
---|---|---|
0x11 | 17 | Round Time (Positive = Minutes, Negative = Seconds) |
0x12 | 18 | Turn Time (FFFFFFFF = Infinite) |
0x13 | 19 | Victories needed |
0x14 | 20 | Worm Selection (0 = Disabled ; 1 = Enabled) |
0x15 | 21 | Worms HP |
0x1D | 29 | Worm Placment (0 = Disabled ; 1 = Enabled) |
Ready signal
Command: | 0x000f |
---|---|
Direction: | Both ways |
Size: | 16 |
Offset | Size | Value |
---|---|---|
08 | DWord | on/off |
0B | DWord | Player number |
Starting Game
Command: | 0x001c |
---|---|
Direction: | To client |
Size: | 20 |
Offset | Size | Value |
---|---|---|
6 | Word | I guess this is only not important junk and can be anything (need verification) |
08 | DWord | HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Data\LogicSeed |
0C | DWord | "GSAW" (this is hardcoded, but i think it can be anything, not important) |
10 | DWord | Version of the Game (4C for the normal 3.6.28.0) |
After this, we switch to type-2 packets documented above. All players send loading status reports up to frame number 0x1b. The report for 0x1b contains some sort of magic which is presumably a sort of checksum on the game logic such as worm placement and map generation. If it's not correct then the game notifies of invalid data and won't let the game start. Best bet is to listen for one player to finish and echo his magic number back.
Here is an example of what is received when 2 other players are in the game:
Frame: player=0 frame=0x00000001 len=4 | 00000 : .... c0 0a 00 00 Frame: player=0 frame=0x00000002 len=4 | 00000 : .... c0 0a 04 00 Frame: player=2 frame=0x00000001 len=4 | 00000 : .... c0 0a 00 00 Frame: player=2 frame=0x00000002 len=4 | 00000 : .... c0 0a 04 00 Frame: player=2 frame=0x00000003 len=4 | 00000 : .... c0 0a 08 00 Frame: player=0 frame=0x00000003 len=4 | 00000 : .... c0 0a 08 00 Frame: player=0 frame=0x00000004 len=4 | 00000 : .... c0 0a 0c 00
snip ...
Frame: player=0 frame=0x0000001a len=4 | 00000 : ..d. c0 0a 64 00 Frame: player=2 frame=0x0000001a len=4 | 00000 : ..d. c0 0a 64 00 Frame: player=2 frame=0x0200001b len=7 | 00000 : @.GQ{7. 40 09 47 51 7b 37 00 Frame: player=0 frame=0x0200001b len=7 | 00000 : @.GQ{7. 40 09 47 51 7b 37 00
Map
Command: | 0x002b |
---|---|
Direction: | To client |
Size: | 64 |
Offset | Size | Value |
---|---|---|
06 | Byte | (??) |
07 | Byte | Always 01 ? |
08 | DWord | File Length |
0C | DWord | Data Offset |
10 | DWord | Data Length |
14 | Data Length | Data |
Note : A DWord is put at the beginning of the File to tell what type of Map is sent.
1 = .BIT Monochrome map (.bit, .lev)
2 = .LEV Monochrome map (.bit, .lev)
3 = .PNG Colour map
Exemple of a multi-packeted BIT-file (135434(0x34DE)bytes file):
<01 48 1414 2B00> <A2 01 [E2 34 00 00] [00 00 00 00] [00 14 00 00]> [ 01 00 00 00 { .BIT file DATA } ]
<01 48 1414 2B00> <A2 01 [E2 34 00 00] [00 14 00 00] [00 14 00 00]> [ { .BIT file DATA } ]
<01 9C F60C 2B00> <A5 01 [E2 34 00 00] [00 28 00 00] [E2 0C 00 00]> [ { .BIT file DATA } ]