S>C 0x11 MCP_REQUESTLADDERDATA

Transport Layer:Transmission Control Protocol (TCP)
Application Layer:Diablo II Realm Messages (MCP)
Message Id:0x11
Message Name:MCP_REQUESTLADDERDATA
Direction:Server to Client
Used By:Diablo II, Diablo II Lord of Destruction
Message Format:
(does not include protocol header)
 (UINT8) Ladder type
 (UINT16) Total response size
 (UINT16) Current message size
 (UINT16) Total size of unreceived messages
 (UINT16) Rank of first entry
 (UINT16) Unknown (0)
 (VOID) Message data

Message data:
(UINT32) Number of entries
(UINT32) Unknown (0x10)
For each entry:
    (UINT64)     Character experience
     (UINT8)     Character flags
     (UINT8)     Character title
     (UINT16)     Character level
     (UINT8)[16] Character name

Remarks

Received when requesting ladder data.

Multiple packets are received until all of the Message data is entirely received.

Total response size: The size of the entire batch of SID_REQUESTLADDERDATA messages, excluding their headers.

Current message size: The size of the current message, excluding its header.

Total size of unreceived messages: The total size of all the unreceived messages in the batch, excluding their headers and first bytes. In the last packet, this value is 0, since there are no unreceived messages.

Rank of first entry: Always zero, except in the last message. In the last message, this specifies the zero-based rank of the first entry. (For example if this is 17 in the last packet, then ladder entries 18-33 were retrieved.)

The server may respond with one or more of these messages. The client must not handle the data until the last packet in the batch is received.

The Message data should be concatenated backwards. For example, if 3 packets were received, then the data buffer should contain the data of the 3rd packet, followed by the data of the 2nd packet, followed by the data of the 1st packet. Only after the last packet was received, the data buffer should be parsed.

Important note: If the entry is the last entry in the packet, the character name might be smaller than 16 bytes. In this case, the client MUST add null bytes to the end of the packet, BEFORE adding it to the data buffer.

Ladder packets are NOT SENT IN ANY ORDER - They are often sent completely out of order and must be placed back into the proper order. You have to infer the sequencing based on the 'how big' fields in the 10 byte header.

The Message data, after reconstruction, contains:

Character flags

  • AND 0x07 to get character:
    • 0x00: Amazon
    • 0x01: Sorceress
    • 0x02: Necromancer
    • 0x03: Paladin
    • 0x04: Barbarian
    • 0x05: Druid
    • 0x06: Assassin
  • 0x08: Highlight this (if response to C > S [0x16] MCP_CHARRANK)
  • 0x10: Dead character (hardcore only)
  • 0x20: Hardcore character
  • 0x40: Expansion character

Character name: The character name is always 16 bytes. If the name is shorter than 16 bytes, the string is padded with nulls. The last byte is always null, since character names are limited to 15 chars.

| Edited: LordVader

Comments

RealityRipple

This information is out of date. I'm not certain of the system the official client uses to store these packets, but I'll attempt to explain what I've discovered -

The 10-byte header is in every packet you receive of this type. However, The size values have an extra 4, 8, or 12 bytes, depending on things....
The first WORD is the total number of bytes in the list (plus 12).
The second WORD is the number of bytes in this set of entries (plus 4 or 8 or 12, depending)
The third WORD is the number of bytes written thus far, not the number remaining. It also has a plus, depending)
The fourth WORD is the rank of the first entry on the first set, and some unknown value on subsequent sets of the same list.
The fifth WORD is null.

The "Message Data" DWORDs only show up on the first set. After that, they're not there. If they are there, the length values for the current packet are +8, if not, +4, and if it's the only set in the list, +12. I don't quite understand the logic behind it, but that's what it seems to do.

After this follow some entries, though not necessarily the "number of entries", unless it's a short list. The Flags values are, in actuality, bitwise, using 0x10 for Dead, 0x20 for Hardcore, and 0x40 for Expansion. The character class can easily be determined using Flags AND 0x0F. The Title values are the act the user has beaten, starting from 0. Note that all normal characters count as Act 1 (0), and act 5 doesn't count, just like D2 statstrings. To determine the "Character Title" from this value, you can use the ranges 00-03 = None, 04-07 = Beat Normal, 08-0B = Beat Nightmare, 0C+ = Beat Hell for classic, and 00-04, 05-09, 0A-0E, 0F+ for expansion. Character names are not strictly null padded, but often will contain junk data after the first null byte.

Finally, the server usually sends these in the right order now, apparently, although, of course, you shouldn't rely on it. You can, however, easily sort by the third WORD in the header.

Kaylin

In order to shed some light on any potential confusion regarding the modern format of this message here have been my findings with regards to the formatting. These messages are encapsulated by the MCP protocol so they all have their 3-byte MCP protocol header. The ladder data message always also contains a 7 byte header present in all the ladder data messages. It is as follows:

(Byte) Ladder Type
(Word) Total Size
(Word) Chunk Size
(Word) Data Index

The rest of the message is then:

(void) Data Payload

The Ladder type is the type of ladder. Possible values for this byte are given in the request message documentation here: < https://bnetdocs.org/packet/99/mcp-requestladderdata > The full data payload may be split across multiple of these messages and each message contains this 7 byte header above. The ultimate length of the reassembled payload is Total Size. The Chunk Size is the the size of the chunk of data payload included in this individual message. The Data Index is where to insert this payload chunk into the byte sequence to reassemble the stream.

The Data payload in its entirety is then as follows. There seem to be no short messages at least anymore; no weird length calculations. The Reassembled payload looks like this:

(Dword) first entry rank
(Dword) actual number of entries
(Dword) unknown (always 16)
struct {
(Dword) Experience
(Dword) Zero (may be high dword of exp)
(Byte) Character Flags (Dead, sorceress, etc)
(Byte) Number of Completed Acts
(Word) Level
(Byte) Name[16]
}[actual number of entries]

I say actual number of entries because this varies but it never seems to be more than 16. The (Dword) unknown may in fact be the maximum number of entries returned. I note (Qword) Experience described above is probably two dwords since Diablo II is a 32-bit program. Name is a fixed length 16-byte field containing a null terminated character string followed by enough bytes to ensure the total length of that field is 16 bytes. Remember this is the reassembled payload! This data may be split into multiples of the MCP_REQUESTLADDERDATA reply and may break in the middle of data fields. Once reassembled it seems this structure has no weird length fields, short strings, or sometimes missing headers. My tests overnight indicated the multiple payload messages were received in order but you might not wish to bank on this being the case.