Skip to content

Segmented launcher protocol rework

Sean Baggaley requested to merge topic/default/segmented-rework into branch/default

The existing implementation of segmented protocol has some issues (see #4178) and was a little awkward for launchers to implement, so I thought it best to re-work it. This implementation is a lot simpler and less clever than the old one, but should probably work better in reality. It merely just generates a regular response like normal, and splits it up into multiple packets. As a result, the packets are not standalone: all the data must arrive to be useful. This wasn't supposed to be a problem in the design of the old one but it was in practice: it was still possible for a packet to exceed MTU (there was no way of handling if a packet was longer than sv_maxpacketsize, so it still resulted in a massive packet), and some fields were dependant on prior fields, which made them unreadable if packets were missing or out of order.

This is also limited to the size of g_MasterServerBuffer, but no server with massive launcher responses has yet to reach MAX_UDP_PACKET, and if that becomes an issue then that buffer just can have its size increased or NETBUFFER_s could gain automatic resizing somehow.

This is backwards-compatible with the old protocols. I sort of now question the usefulness of a separate LAUNCHER_SERVER_SEGMENTED_CHALLENGE challenge, as the old LAUNCHER_SERVER_CHALLENGE also allows specifying whether to get a segmented response and is backwards compatible while LAUNCHER_SERVER_SEGMENTED_CHALLENGE is not.

Pseudo-code for handling this could be:

segmented_data = null
segs_read = 0

response = readLong()
if (response == SERVER_LAUNCHER_SEGMENTED_CHALLENGE) {
    seg_num = readByte()
    seg_count = readByte()
    seg_offset = readShort()
    seg_size = readShort()
    data_size = readShort()

    if (segmented_data == null)
        segmented_data = malloc(data_size)

    // 12 is the size of the header above, including challenge
    memcpy(segmented_data + seg_offset, this_packet + 12, seg_size)
    segs_read += 1

    if (segs_read == seg_count) {
        // read the accumulated data from segmented_data like a normal
        // LAUNCHER_SERVER_CHALLENGE packet, just without the challenge
        time = readLong()
        version = readString()
        flags = readLong()
    }
}

Merge request reports