United Offensive network protocol

Status and scope

This document specifies the multiplayer wire protocol used by Call of Duty: United Offensive 1.51, identified on the wire as protocol 22. It describes the stock client/server protocol, including the legacy Activision master and authorization exchanges. The protocol is carried primarily over IPv4 UDP.

The specification was reconstructed from the maintained CoD:UO client and dedicated-server recovery, whose packet constructors, parsers, constants, and descriptor tables are checked against the original Windows and Linux machine code. Unless a section is explicitly labelled as an implementation note, the field order and widths below are wire-level requirements.

This is not a security protocol. The in-band XOR transform is reversible obfuscation and synchronization, not encryption or authentication. A robust implementation must treat every length, index, count, and string as untrusted network input.

Protocol profile

Property

Value

Game

Call of Duty: United Offensive 1.51

Advertised protocol

22

Default game port

UDP 28960

Legacy authorization port

UDP 20600

Legacy master-server port

UDP 20610

Maximum logical message

32,768 bytes

Netchan fragment payload

1,300 bytes

Netchan datagram buffer

1,400 bytes

Reliable-command window

64 commands in each direction

Maximum user commands/packet

32

Snapshot history

32 frames

Entity number domain

10 bits; 1,024 values

Client number domain

6 bits; 64 values

Configstring domain

2,048 indexes

In-band download window

8 blocks of at most 2,048 bytes

Layer model

Every received UDP datagram enters one of two paths according to its first four bytes:

UDP datagram
  |
  +-- first dword == 0xffffffff
  |     connectionless command or response
  |     (text, except the compressed tail of `connect` and binary master lists)
  |
  `-- any other first dword
        netchan sequence and optional fragment header
          -> fragment reassembly
          -> direction-specific XOR transform
          -> fixed-Huffman body decompression
          -> client or server message command stream
               -> reliable commands, usercmds, gamestate,
                  snapshots, deltas, or downloads

The connectionless and sequenced formats deliberately share the same socket. The value 0xffffffff is therefore reserved and cannot be a normal netchan sequence.

Representation conventions

Integer and float representation

byte, short, and long mean 8-, 16-, and 32-bit quantities in this document. Multi-byte integers in game, netchan, and download messages are little-endian unless a field explicitly says otherwise. Master-list ports are the important exception: they are two bytes in network order.

Signedness is a parser-level interpretation. A 16-bit field read with the stock ReadShort primitive is sign-extended to 32 bits. Raw bit fields are assembled into the low bits of a 32-bit word.

Floats transmitted in the generic and player-state delta codecs are IEEE-754 binary32 bit patterns when their compact integer form cannot be used. Angles using the special angle16 mode are encoded as:

encoded = trunc(angle_degrees * 65536 / 360) & 0xffff
decoded = int16(encoded) * 360 / 65536

The original Windows and Linux x87 encoders differ only for pathological conversion-boundary inputs. Ordinary finite gameplay angles are compatible.

Bit and byte cursors

Bit fields are written least-significant bit first. Bit zero occupies the least-significant bit of its physical byte.

The message codec maintains a byte cursor and a bit cursor. On the first bit of a new bit byte, that byte is allocated at the byte cursor and the byte cursor advances. Byte, short, long, string, and raw-data operations always use the byte cursor. If such an operation occurs while a bit byte is only partly filled, later bit operations resume that earlier bit byte until it is full, then jump to the current byte cursor. The reader mirrors this behavior.

Consequently, an implementation should reproduce the message primitives rather than assume every field is concatenated by one conventional bitstream writer.

Strings

In-band strings are NUL-terminated byte sequences:

  • ordinary strings have a 1,024-byte storage limit, including the terminator;

  • big strings have an 8,192-byte storage limit;

  • outgoing text is cleaned by the engine character sanitizer;

  • line reads stop at newline or NUL;

  • connectionless text normally has no transmitted NUL and may end at the UDP datagram boundary;

  • the stock RCON client is an exception and includes its final NUL.

Information strings use the id-style form \key\value\key\value. They are text, not a map type on the wire. Keys and values cannot contain the separator backslash.

Connectionless packets

General format

Offset

Size

Meaning

0

4

Marker ff ff ff ff

4

variable

Command name and arguments, usually text

Command matching is case-insensitive for the normal dispatcher. The first line is tokenized as a console-style command. Several responses then consume additional newline-delimited lines directly from the message.

Compressed connect request

The client constructs this logical text without a terminating NUL:

connect "\protocol\22\qport\<qport>\challenge\<challenge>\...userinfo..."

Only the prefix through the space after connect remains plain text. The rest is adaptive-Huffman compressed:

Compressed connect datagram

Offset

Size

Meaning

0

4

Connectionless marker

4

8

ASCII connect followed by one space

12

2

Uncompressed tail length, big-endian

14

variable

Adaptive-Huffman stream for the opening quote, userinfo, and closing quote

The adaptive tree begins with one not-yet-transmitted (NYT) leaf. An unseen symbol is represented by the NYT code followed by the literal symbol as eight bits, most-significant bit first in symbol value. Encoder and decoder update the adaptive tree after every symbol. The two length bytes occupy the first 16 bit positions of the compressed tail.

This adaptive format is used for connect only. It is not the fixed, pretrained Huffman transform used for sequenced message bodies.

After the last adaptive code bit, stock calculates the compressed-tail byte count as (finalBitOffset + 8) >> 3; finalBitOffset already includes the 16-bit length header. This is ordinary round-up for a partial final byte, but retains one extra byte when the code ends exactly on a byte boundary. The decoder stops after producing the declared number of bytes and ignores that trailing storage. A deterministic implementation should clear all padding and the optional trailing byte.

Public server commands

The following commands are accepted by a game server:

Connectionless requests handled by a game server

Request

Sender

Response

Notes

getinfo <challenge>

Browser/client

infoResponse\n<info>

Compact server-browser metadata. The challenge is copied into the response information string.

getstatus [challenge]

Browser/client/master

statusResponse\n<info>\n<players>

Full server information followed by zero or more player lines.

getchallenge

Connecting client

challengeResponse <number> [onlyVisibleClients]

Allocates or reuses a challenge for the complete source address, including UDP port.

connect <compressed-userinfo>

Challenged client

connectResponse or error\n<reason>

The tail is adaptive-Huffman compressed as described above.

rcon <password> <command...>

Administrator

One or more print\n<text> packets

Globally throttled to one accepted attempt per 500 ms. Output is split into chunks of at most 1,300 text bytes.

disconnect

Peer

None

Silently ignored by the connectionless dispatcher.

ipAuthorize ...

Legacy authorization service

Challenge, CD-key request, or error sent to the client

Accepted only from the configured authorization host address.

pb_* / PB_*

PunkBuster component

Component-defined

Optional retired subsystem, consumed before ordinary dispatch.

infoResponse information keys

The server always emits challenge, protocol, hostname, mapname, gametype, hw, pb, and mod. It conditionally emits the following keys when applicable: clients, sv_maxclients, pure, minPing, maxPing, game, sv_allowAnonymous, pswrd, ff, kc, timeoutsAllowed, jps, and tnk.

statusResponse format

The response is:

ffffffff statusResponse\n
\key\value\key\value...\n
<score> <ping> "<name>"\n
<score> <ping> "<name>"\n
...

The information line is built from server-info cvars and additionally carries the request challenge, password state pswrd, modification state mod, and optionally con_disabled. Only clients in server state CONNECTED or later appear in the player list.

Client-side connectionless responses

The stock client recognizes:

Connectionless commands handled by a client

Command

Payload

Purpose

challengeResponse

Signed challenge and optional visibility-policy integer

Advances CONNECTING to CHALLENGING.

connectResponse

No payload

Creates the client netchan and advances to CONNECTED.

infoResponse

Newline followed by one information string

Completes a browser/ping request or adds a LAN discovery result.

statusResponse

Settings line followed by player lines

Completes a server-status request.

disconnect

No payload

Drops only when sent by the connected endpoint and no sequenced packet has arrived for at least three seconds.

echo

One token

Sends that token back as a connectionless packet.

print

Newline followed by a big string

Displays server/RCON output.

error

Newline followed by a big string

Drops the matching pending or active connection.

motd

Information string containing the outstanding challenge and motd

Legacy message-of-the-day response.

getserversResponse

Binary address list

Populates the global server browser.

needcdkey

No required payload

Requests another legacy client authorization attempt.

loadingnewmap

Newline, map name, newline, game type

Prepares a connected client for a server map change.

updateResponse

Availability, download URL, and version tokens

Legacy Windows auto-update response.

keyAuthorize

Legacy authorization data

Recognized and ignored by the recovered client dispatcher.

Connection handshake

Normal remote connection

The connection is established with the following exchange. Arrows describe UDP datagrams, not a stream:

client                                      server
   |                                           |
   |  OOB: getchallenge                        |
   |------------------------------------------>|
   |                                           |  optional legacy
   |                                           |  authorization exchange
   |  OOB: challengeResponse C [visibility]    |
   |<------------------------------------------|
   |                                           |
   |  OOB: connect "\protocol\22              |
   |       \qport\Q\challenge\C\..."          |
   |------------------------------------------>|
   |                                           |
   |  OOB: connectResponse                     |
   |<------------------------------------------|
   |                                           |
   |  sequenced client packet                  |
   |------------------------------------------>|
   |                                           |
   |  sequenced gamestate                      |
   |<------------------------------------------|
   |                                           |
   |  sequenced move / moveNoDelta             |
   |------------------------------------------>|
   |                                           |  client enters ACTIVE
   |  sequenced snapshot                       |
   |<------------------------------------------|

The client retransmits the current getchallenge or connect request every 3,000 ms. The server requires protocol 22 and, for non-local clients, a matching challenge issued to the same IP address and UDP port. Reconnect attempts matching the base address plus qport or translated UDP port are subject to the server’s reconnect interval.

The connect userinfo must contain at least:

  • protocol=22;

  • the client’s 16-bit qport value rendered as decimal text;

  • the issued challenge;

  • ordinary userinfo such as name, rate, snapshots/second, password, and download capability.

The server clamps the advertised rate to 1,000..90,000 bytes/second and snaps to 1..30. Absence of these keys selects 5,000 bytes/second and a 50-ms snapshot interval in the stock server.

Connection states

The client progresses through DISCONNECTED (0), CONNECTING (1), CHALLENGING (2), CONNECTED (3), LOADING (4), PRIMED (5), and ACTIVE (6). CINEMATIC (7) and LOGO (8) are local presentation states rather than network handshake stages.

The server tracks FREE (0), ZOMBIE (1), CONNECTED (2), PRIMED (3), and ACTIVE (4). It enters PRIMED when sending a gamestate and enters ACTIVE when the first valid user-command bundle arrives.

Netchan transport

Unfragmented headers

Client-to-server datagram:

Offset

Size

Type

Meaning

0

4

uint32 LE

Sequence; bit 31 clear

4

2

uint16 LE

qport

6

n

bytes

Encoded logical client message

Server-to-client datagram:

Offset

Size

Type

Meaning

0

4

uint32 LE

Sequence; bit 31 clear

4

n

bytes

Encoded logical server message

Sequences start at 1 independently in each direction. A receiver accepts only a sequence greater than its last accepted sequence. Older and duplicate datagrams are discarded. The gap between accepted sequences is recorded as a drop count, but there is no packet-level retransmission acknowledgement in the header.

qport and translated ports

Only client-to-server datagrams carry qport. The server identifies a channel by source base address and qport, deliberately ignoring the UDP source port at first. If the port changed, the channel’s remote port is updated. This permits continued operation when a NAT remaps the client’s UDP source port.

Fragmented headers

Fragmentation is indicated by sequence bit 31. The low 31 bits remain the message sequence.

Client-to-server fragment:

Offset

Size

Type

Meaning

0

4

uint32 LE

sequence | 0x80000000

4

2

uint16 LE

qport

6

2

int16 LE

Fragment start offset

8

2

int16 LE

Fragment payload length

10

n

bytes

Fragment payload

Server-to-client fragment omits qport, so fragmentStart is at offset 4, fragmentLength at 6, and data at 8.

Fragmentation and reassembly rules

  • A logical message of 1,300 bytes or more is fragmented. Shorter messages are sent as one datagram.

  • Every fragment of one logical message uses the same sequence.

  • Fragment starts are 0, 1,300, 2,600, and so on.

  • A receiver accepts a fragment only when its start equals the number of bytes already accumulated for that sequence. There is no out-of-order fragment queue.

  • A fragment of exactly 1,300 bytes means more fragments follow.

  • A fragment shorter than 1,300 bytes terminates the message.

  • Therefore, a message whose size is an exact multiple of 1,300 requires a final zero-length fragment.

  • Reassembled content may not exceed 32,768 bytes.

  • The qport and fragment fields are transport-only. The delivered reassembled message contains the four-byte sequence followed immediately by the logical payload.

Fragment loss prevents that logical message from completing. Higher-level reliable commands and gamestate recovery are repeated in later logical messages; netchan itself does not resend a missing fragment.

Sequence-space caveat

Stock uses the sign/high bit of a signed, unbounded sequence as the fragment flag. After sequence 0x7fffffff the two roles collide. Implementations seeking exact stock interoperability should reconnect or otherwise avoid crossing that boundary rather than silently assigning bit 31 a different meaning.

Sequenced payload pipeline

Sending applies these operations in order:

  1. Construct the direction-specific clear logical header and uncompressed command body.

  2. Fixed-Huffman compress only the body.

  3. XOR-transform only the compressed body.

  4. Add the netchan header and, when required, fragment the transformed logical message.

Receiving performs the exact reverse after netchan reassembly.

Clear logical headers

Uncompressed and unobfuscated logical prefixes

Direction

Size

Fields

Purpose

Client to server

9 bytes

serverId:u8, messageAcknowledge:i32, reliableAcknowledge:i32

Selects the server generation, acknowledges the latest server netchan message, and acknowledges server reliable commands.

Server to client

4 bytes

lastClientCommand:i32

Acknowledges the latest executed client reliable command.

The fixed-Huffman stream begins immediately after these clear prefixes.

XOR transform

The same byte loop encodes and decodes because XOR is symmetric:

uint8_t key = initial_key;
size_t command_index = 0;

for (size_t i = 0; i < body_length; ++i) {
    if (command[command_index] == '\0')
        command_index = 0;
    key ^= (uint8_t)command[command_index] << (i & 1);
    ++command_index;
    body[i] ^= key;
}

All operations reduce to eight bits. An empty command string contributes zero on every iteration.

XOR synchronization inputs

Direction

Initial key

Repeating command string

Client to server

low byte of serverId ^ challenge ^ messageAcknowledge

Latest server command named by reliableAcknowledge

Server to client

low byte of netchanSequence ^ challenge

Latest client command named by the clear lastClientCommand acknowledgement

Because command text participates in the key stream, a peer that loses reliable-command synchronization cannot decode subsequent bodies correctly.

Fixed-Huffman body compression

Sequenced bodies use one fixed binary Huffman tree over all 256 byte symbols. The tree is built at startup by initializing an adaptive Huffman state and adding symbol 0 the number of times specified by seed entry 0, then symbol 1, and so on through 255. Compression and decompression thereafter traverse that pretrained tree without updating it.

Codes are emitted root-to-leaf into the low-to-high bit positions of the output. The compressed stream has no uncompressed-size or exact-bit-count header. Its UDP/logical byte extent bounds decoding, and the command stream’s EOF selector provides the semantic terminator. Unused high bits in the last compressed byte are zero. See United Offensive protocol 22 wire-schema appendix for the complete 256-entry seed.

Client-to-server messages

Clear header

Offset

Size

Type

Meaning

0

1

uint8

Current server id

1

4

int32 LE

Latest received server netchan sequence

5

4

int32 LE

Latest received server-command sequence

9

n

bytes

Fixed-Huffman compressed, XOR-transformed command body

The server compares the low/high server-id domains to detect restarts and dropped gamestates. If the client’s generation is stale and its acknowledgement has passed the previous gamestate, the server resends a full gamestate.

Client command selectors

The decompressed body is controlled by two-bit selectors:

Value

Name

Payload

0

move

User-command count and keyed delta usercmds

1

moveNoDelta

Same payload; requests a full snapshot

2

clientCommand

Reliable sequence and NUL string

3

EOF

No payload

The emitted grammar is:

clientCommand* (move | moveNoDelta)? EOF

The server consumes all leading reliable commands, then at most one move bundle. A move makes the snapshot named by messageAcknowledge the requested delta base. moveNoDelta sets the delta base to none.

Reliable client command record

Reliable client command fields

Field

Type

Meaning

selector

2 bits

2

sequence

int32 LE

Monotonic reliable-command number

text

NUL string

Console/game command, at most 1,023 bytes

The client repeats every command after the server’s latest acknowledged lastClientCommand. The server ignores duplicates, requires newly observed commands to be exactly consecutive, and drops the client on a gap. Built-in engine commands include userinfo, disconnect, cp, vdr, download, nextdl, stopdl, donedl, retransdl, and wwwdl; other commands are offered to the game module.

User-command bundle

After selector 0 or 1, one byte gives the command count. Valid values are 1..32. Commands are ordered oldest to newest. The first delta base is a default command derived from the latest player state; each later command is based on the command immediately before it.

The per-bundle key is:

key = messageAcknowledge
    ^ gamestateChecksumFeed
    ^ HashKey(last acknowledged server command, 32)

The stock HashKey operation and the XOR masks in the usercmd codec are part of the wire format. The usercmd record contains commandTime, two button bytes, a 7-bit weapon, three 16-bit angle words held in 32-bit members, and signed forward/right/up movement bytes.

commandTime is represented first: a one bit followed by an unsigned byte means baseTime + byte for deltas below 256; a zero bit is followed by the absolute 32-bit time.

Two keyed control bits then select one of three forms:

Delta usercmd forms

First control

Second control

Meaning

0

not present

All fields except the already-decoded command time equal the base.

1

0

Compact form: fire bit, pitch, yaw, and horizontal movement may change. Other fields equal the base.

1

1

Full form: compact fields plus roll, upper six button bits, secondary buttons, vertical movement, and weapon may change.

Control values and changed field values are XOR-masked with the low requested number of bits from the key. Delta-coded fields carry a one-bit changed flag; changed bytes/shorts or bit fields are XORed with the current key. The codec XORs commandTime into the key before the compact form’s field payload and before the full form’s roll-and-later payload, matching the stock operation order.

Movement is quantized before comparison:

  • values greater than 10 become +127;

  • values less than -10 become -127;

  • values from -10 through 10 become zero;

  • forward/right use four flags in four bits; up uses two flags in two bits.

Server-to-client messages

Clear header

Offset

Size

Type

Meaning

0

4

int32 LE

Latest executed client-command sequence

4

n

bytes

Fixed-Huffman compressed, XOR-transformed service-command stream

Service opcodes

Service commands begin on byte boundaries in the decompressed body:

Server service opcodes

Value

Name

Context and payload

0

svc_bad

Invalid; never emitted

1

svc_nop

No payload

2

svc_gamestate

Full configstrings and entity baselines

3

svc_configstring

Legal only inside svc_gamestate

4

svc_baseline

Legal only inside svc_gamestate

5

svc_serverCommand

Reliable server command

6

svc_download

In-band block or HTTP redirect

7

svc_snapshot

Delta-compressed world snapshot

8

svc_EOF

End of command stream

One logical message can contain multiple server commands and download blocks before its snapshot and final svc_EOF.

Reliable server command

svc_serverCommand carries a 32-bit sequence followed by a NUL-terminated string. The server repeats unacknowledged commands. The client ignores a sequence not newer than its current value, otherwise stores the text in a 64-entry ring and publishes that sequence in the next client clear header.

The server internally distinguishes reliable and ignorable commands. Both use the same wire record and acknowledgement sequence; ignorable commands may be coalesced or discarded before transmission when the backlog grows. This policy must not be confused with UDP-level reliability.

Gamestate

A gamestate logical body is:

int32 lastClientCommand                     # clear logical prefix
[svc_serverCommand records]                 # pending commands
byte  svc_gamestate                         # 2
int32 serverCommandSequence
(
    byte  svc_configstring                  # 3
    int16 configstringIndex
    bigString value
  |
    byte  svc_baseline                      # 4
    deltaEntity from an all-zero baseline
)*
byte  svc_EOF                               # closes embedded gamestate
int32 clientNum
int32 gamestateChecksumFeed
byte  svc_EOF                               # closes outer message

Only non-empty configstrings and baselines whose entity number is nonzero are sent. Configstring indexes are 0..2,047. The checksum feed becomes part of the usercmd key and pure-file validation.

The embedded gamestate parser accepts only svc_configstring, svc_baseline, and its terminating svc_EOF. The final outer EOF is consumed by the ordinary server-message parser.

Snapshot header

svc_snapshot is followed by:

Field

Type

Meaning

serverTime

int32 LE

Server simulation time in ms

deltaDistance

uint8

0 for full snapshot, otherwise current message sequence minus base sequence

snapFlags

uint8

Snapshot state flags

player state

bit/byte mix

Delta from the base player state or all-zero state

entity stream

bit/byte mix

Sorted entity delta records, then entity number 1023

client stream

bit/byte mix

Sorted client records, then a zero bit

Snapshot flag bits are:

Bit

Name

Meaning

0

rate delayed

Server rate scheduling delayed this frame

1

not active

Snapshot cannot activate the client

2

server count/restart

Toggles across a server restart

The client accepts a delta only when the referenced 32-frame history slot still names the expected message and its entity/client parse-ring content has not aged out. It still consumes an unusable delta from the stream but discards the completed snapshot. The server uses at most 28 frames of delta distance (32 - 4) and falls back to a full snapshot when the frame or entity ring is too old.

Snapshot entity and client merge streams

Old and new entity/client lists are sorted by number and merged. This permits unchanged records to be omitted, new records to delta from a baseline, and removed records to carry only their number and removal bit.

Entity records use a 10-bit number. Value 1023 terminates the stream and is also the engine’s ENTITYNUM_NONE sentinel. Each non-terminal entity then carries a one-bit normal/vehicle table selector, a removal bit, and, when not removed, a changed bit. A changed entity carries an 8-bit lastChanged count and field deltas through that descriptor index.

Client records begin with a one-bit present marker. Zero terminates the client stream. One is followed by a 6-bit client number, removal bit, changed bit, and optional 8-bit lastChanged plus field deltas.

The exact generic delta grammar, field modes, and ordered tables are in United Offensive protocol 22 wire-schema appendix.

Player-state delta

Player state begins with an 8-bit lastChanged count for the ordered 114-field main table. Fields from zero through lastChanged - 1 each carry a changed bit and, when changed, their mode-specific value. Later main fields are copied from the base.

The main table is followed by these auxiliary groups in fixed order:

  1. one presence bit, a 6-bit changed mask, and changed stats;

  2. one overall ammo-presence bit, then four optional 16-entry ammo groups;

  3. four independently optional 16-entry clip groups;

  4. one objective-array presence bit, then all 16 objective states and field deltas;

  5. one HUD presence bit, then archival and current HUD arrays.

Stats 0, 1, 2, and 4 use signed shorts; stat 3 uses six bits; stat 5 uses one byte. Ammo and clip values use shorts. Each ammo/clip group uses a 16-bit changed mask.

Each objective starts with a three-bit state. If any of its six descriptor fields differs, a presence bit is one and every descriptor receives its own changed bit. HUD arrays begin with a six-bit active element count (0..63). Each active element has a five-bit final descriptor index followed by deltas from descriptor zero through that index.

Delta field modes

For a generic entity/client/objective/HUD field, a zero changed bit copies the base. A one changed bit selects a new value:

Generic delta descriptor modes

Descriptor bits

Encoding after the changed bit

0

Float mode. One bit selects zero versus nonzero. A nonzero float uses another bit: zero means an exact integer in -4096..4095 encoded as value + 4096 across 13 bits; one means a raw 32-bit float.

-100

Angle16 mode. One bit selects zero versus a 16-bit encoded angle.

any other value

One bit selects zero versus nonzero. A nonzero value transmits abs(bits) low bits. Negative values other than -100 do not add a sign bit; the magnitude is the physical bit width.

The player-state main table uses a closely related but distinct codec: after the changed bit it omits the generic zero/nonzero bit for ordinary integers and floats. Float mode directly selects compact integer versus raw float; angle16 retains its zero/value selector.

Downloads

Download control commands

The client initiates and acknowledges downloads through reliable client commands:

Client download commands

Command

Meaning

download <qpath>

Close any previous transfer and request a server-relative file.

nextdl <block>

Acknowledge the next expected in-band block.

retransdl <block>

Ask the server to restart transmission from the current acknowledged block.

stopdl

Abort and close the in-band transfer.

donedl

Request a fresh gamestate after the download set completes.

wwwdl ack

Accept an HTTP redirect.

wwwdl done

Report redirect completion.

wwwdl fail

Report redirect failure and request in-band fallback/recovery.

wwwdl chkfail

Report a redirected file checksum failure.

wwwdl bbl8r

Accept a redirect that requires disconnecting from the game server.

In-band block record

Each block is one svc_download record:

Field

Type

Meaning

opcode

uint8

6

block number

int16 LE

Ordered block number

total size

int32 LE

Present only for block 0

block length

int16 LE

0..2,048

data

raw bytes

Present when length is nonzero

Blocks are numbered from zero. The server buffers at most eight unacknowledged blocks. A zero-length block is an explicit end-of-file block and is itself acknowledged with nextdl. If all buffered blocks were transmitted and no acknowledgement arrives for more than 1,000 ms, transmission restarts from the oldest unacknowledged block.

Failure record

Block number 0 followed by total size -1 denotes failure. A NUL-terminated error string follows the size; there is no block-length field in this form.

HTTP redirect record

Block number -1 selects a redirect:

Field

Type

Meaning

opcode

uint8

6

block number

int16 LE

-1

URL

NUL string

Redirect target

file size

int32 LE

Expected bytes

flags

int32 LE

Bit 0 disconnect; bit 1 open URL/quit

Server discovery and legacy services

LAN discovery

The client broadcasts getinfo xxx twice to each UDP port 28960 through 28963. Responding servers return the ordinary infoResponse. An unsolicited valid protocol-22 response is admitted to the local-server list only while a LAN scan is active.

Global master query

The client sends the legacy master at UDP 20610:

ffffffff getservers <protocol> [keyword ...]

For CoD:UO the protocol argument is normally 22. A restricted/demo client adds the keyword demo.

The master response begins with connectionless text whose first token starts with getserversResponse. The client then scans the complete datagram for records of this form:

Offset

Size

Type

Meaning

0

1

byte

ASCII backslash 0x5c

1

4

bytes

IPv4 address

5

2

uint16 BE

UDP port in network order

The next byte must be another backslash. \EOT terminates the list. A single response packet contributes at most 256 parsed addresses.

Master heartbeat

A public dedicated server resolves coduomaster.activision.com:20610 and sends heartbeat <name>\n at most every 180,000 ms. It sends a full statusResponse to the master at most every 600,000 ms and sends gameCompleteStatus after a completed game. Shutdown forces a heartbeat flatline\n.

Legacy authorization

For non-LAN clients, the stock server may ask coduoauthorize.activision.com:20600:

getIpAuthorize <challenge> <a.b.c.d> <fs_game> <allowAnonymous>

The service replies with ipAuthorize <challenge> <result> <reason> <numericGuid> <guidText>. Results include accept, deny, and demo. The server relays a challenge, needcdkey, or an error to the client. If authorization does not answer within 5,000 ms, the server permits the challenge path to continue under the stock timeout rules.

The original Activision master, authorization, MOTD, and auto-update services are historical dependencies and should not be assumed to remain available. Their formats are documented for compatibility and replacement services.

Timing, rate, and recovery behavior

Important stock timing and policy values

Mechanism

Default/limit

Behavior

Handshake resend

3,000 ms

Retransmit current challenge/connect request.

Client timeout cvar

200 seconds

Six consecutive expired checks are required before drop.

Server timeout cvar

240 seconds

Six consecutive maintenance scans are required before drop.

Server zombie time

2 seconds

Retains a dropped slot briefly before returning it to FREE.

Snapshot request rate

snaps 1..30

Server interval is integer 1000 / snaps ms; default 50 ms.

Client packet rate

at least 15/s; stock platform maximum 100 or 125

LAN and loopback bypass this pacing gate.

Client packet duplication

0..5 old packets

Expands the usercmd bundle to repeat commands since an older sent packet; it does not duplicate the UDP datagram itself.

Server rate accounting

message capped at 1,500 bytes plus 48 bytes overhead

Determines next snapshot time from client/server rate limits.

Connecting client snapshot floor

1,000 ms

Applied when not active and not downloading.

Reliable command capacity

64 each direction

Falling outside the acknowledged ring loses synchronization and causes reset/drop behavior.

Error handling and interoperability requirements

A compatible parser should enforce at least the following invariants:

  • reject datagrams too short for their selected header;

  • never accept a stale netchan sequence;

  • require fragment starts to be contiguous and bound the reassembled message to 32,768 bytes;

  • validate fixed-Huffman input/output extents before parsing commands;

  • require client reliable commands to be consecutive;

  • bound usercmd count to 1..32;

  • bound configstrings to 0..2,047, entities to 0..1,023, and clients to 0..63;

  • reject descriptor counts outside their exact table extents;

  • validate every variable download length before copying or writing;

  • treat all connectionless text and info-string values as untrusted data, not format strings or filesystem paths;

  • retain EOF-driven command parsing even if padding causes a Huffman decoder to produce an additional trailing byte.

Stock protocol and hardened implementations can remain wire-compatible. Bounds checks, safe formatting, and rejection of impossible indexes do not require a protocol version change. Conversely, enlarging a transmitted index domain, changing a descriptor order, using a different Huffman tree, or changing the XOR synchronization inputs is a protocol break and requires a new advertised protocol.

Implementation checklist

An independent protocol-22 implementation should be tested in this order:

  1. Connectionless marker, text parsing, getinfo, and getstatus.

  2. Adaptive-Huffman connect tail and challenge validation.

  3. Directional netchan headers, qport matching, sequence rejection, and exact 1,300-byte fragmentation including zero-length terminators.

  4. Fixed-Huffman tree construction from the exact seed and round-trip body compression.

  5. Directional XOR transforms with empty and non-empty reliable-command keys.

  6. Clear logical acknowledgements and reliable command rings.

  7. Gamestate configstrings and entity baselines.

  8. Usercmd keying and all three delta forms.

  9. Full and delta snapshots using the exact ordered wire tables.

  10. In-band block download, retransmission, EOF, failure, and redirect forms.

  11. LAN discovery and binary master-list parsing.

Golden tests should compare complete datagrams, not merely reconstructed states. This catches bit order, cursor interleaving, compression padding, XOR start offsets, and descriptor-order mistakes that a semantic round trip can hide.

Wire-schema appendix

The exact fixed-Huffman seed and delta descriptor tables are maintained in United Offensive protocol 22 wire-schema appendix.