Unofficial QuakeWorld v28 Protocol Spec

Preface

The information in this document is mostly sourced from the GPLv2 QuakeWorld source code. I'm not sure if that means this document counts as being a derivative of a GPL-ed work, so take caution if you plan to write an implementation of this protocol. This document does not contain any of the original source code. I am not a lawyer. To anyone who knows more on the subject, please get in contact with me.

Quake is a trademark of ZeniMax Media, Inc.

Type Names

[Control] Structure

The first byte is a [U8] bitfield describing which portions of the movement update have been sent.

It is then followed by output of this procedure (which isn't in the same order as the bits above):

Checksums

QW28 uses a CRC16 checksum in many places using the CRC-16-CCITT prime 4129.

Transport

QW28 uses UDP/IP, on port 27500 by default.

'Out-of-band' Packets

Out-of-band packets begin with the [I32] value -1, immediately followed by data.

Regular Packets

Packets begin with a header of two [I32] values. Both use the first 31 bits as a monotonic packet counter used to detect dropped packets, and the 32nd bit is used for marking attempted reliable transport. Clients will also append an [I16] receiving port number as part of this header when sending these.

After this header is a single opcode [U8], then the packet data.

Connection Handshake

Client-to-Server Packet Types

Packet ID numbers not listed here are invalid.

1: No-op

There is no data. Nothing happens.

3: Player Movement [Checksum?: U8, Drop Percent: U8, Millisec?: U8, Data: Control]

4: Player Text [Text: StrLine]

This packet is used for communicating console commands and chat messages to the server.

The vanilla client sends three reliable player text packets containing [StrNul] "drop" before dropping its connection.

5: Delta Update Request? [Unknown: U8]

Not sure yet.

6: Arbitrary Camera Position? [X: Pos, Y: Pos, Z: Pos]

This packet contains three Pos values. It seems to be used in spectator mode by the client to request viewing a certain position?

7: Data Upload [Size: I16, Percent: U8, Data: U8…]

This seems to have been used a long, long time ago for uploading .pcx format screenshots to a server at the end of a map.

Info String

This is a simple key-value pair format separated by ASCII $5C.

Each pair begins with a backslash, then the key string, another backslash, then the value. Keys and values are limited to 63 bytes long.

Pairs with keys starting with ASCII $2A are derived from certain values and cannot be set by the player.

Some implementations also include empty pairs in this string, so beware when parsing it.

Example Vanilla Client Info String
\*ver\2.40-8860\noaim\0\msg\1\rate\2500\bottomcolor\0\topcolor\0\team\My Cool Team\name\Cool Person\*ip\quakeworld.example

Server-to-Client Packet Types

Packet ID numbers not listed here are invalid. The apparent gaps in opcode numbers are obsolete remnants of older protocol revisions.

1: No-op

Nothing happens. There is no packet data.

2: Disconnect

Sent to the client to tell them they've been disconnected and should stop sending packets. There is no packet data.

3: Set Stat A [Stat ID: U8, Value: U8]

Updates the health, armor, and ammo values displayed on the client. The first [U8] is which stat to be updated, the second [U8] is the value to set it to.

6: Play Sound [Channel: I16, …, Sample: U8, X: Pos, Y: Pos, Z: Pos]

The first [I16] contains the target audio channel as well as two bitflags.

The second [U8] contains an identifier for the sound sample being played. These identifiers are set up by a caching system.

The last three [Pos] values are the position of the sound being played.

8: Print [Priority: U8, Text: StrLine]

9: Client Command [Command: StrLine]

Runs a console command on the client. You should not use this packet lightly.

10: Set View Angle [X: Angle8, Y: Angle8, Z: Angle8]

11: Server Info [Protocol: I32, Connected Clients: I32, Client Slot: U8, Level Name: StrNul, Gravity: F32, Stop Speed: F32, Max Speed: F32, Max Spectator Speed: F32, Acceleration: F32, Air Acceleration: F32, Water Acceleration: F32, Friction: F32, Water Friction: F32, Other Gravity?: F32]

The vanilla client will only accept a protocol version of 26, 27, or 28.

If the 7th bit of the client slot is set, the client will expect to be spawned as a spectator rather than a player.

After receiving this packet, the vanilla client immediately sends a player text packet to retreive a list of sound sample identifiers.

12: Light Flicker Pattern [ID: U8, Pattern: StrNul]

14: Set Player Frags [Client ID: U8, Frags: I16]

16: Stop Sound [Data: I16]

The first three bits of the data field is the sound channel, and the rest of the bits are the ID of the object that is producing the sound.

19: Player Damage [Armor Damage: U8, Health Damage: U8, Source X: Pos, Source Y: Pos, Source Z: Pos]

Does not actually affect the player's health or armor values. This packet is used for the screen effects that play when a player is damaged.

20: Spawn Static Object [Model ID: U8, Animation Frame: U8, Palette Map: U8, Skin ID: U8, Position X: Pos, Rotation X: Angle8, Position Y: Pos, Rotation Y: Angle8, Position Z: Pos, Rotation Z: Angle8]

22: Spawn Normal Object [Model ID: U8, Animation Frame: U8, Palette Map: U8, Skin ID: U8, Position X: Pos, Rotation X: Angle8, Position Y: Pos, Rotation Y: Angle8, Position Z: Pos, Rotation Z: Angle8]

23: Spawn Particle Effect [Kind: U8, …]

The content of this packet is dependent on the value of the 'kind' field.

Some of these effects also play sound effects on the vanilla client.

The separate 'lightning bolt' effects only differ in using a different model variant on the vanilla client.

24: Set Paused [Enable: U8]

Pauses the current music track, displays an indicator on the screen, and prevents player movement.

26: Announcement [Text: StrNul]

Displays text in the center of the client's screen.

27: Increment 'Monsters Killed' Stat

28: Increment 'Secrets Found' Stat

29: Play Ambient Sound [X: Pos, Y: Pos, Z: Pos, Sample ID: U8, Volume: U8, Attenuation: U8]

30: End of Level Intermission [View Position X: Pos, View Position Y: Pos, View Position Z: Pos, View Angle X: Angle8, View Angle Y: Angle8, View Angle Z: Angle8]

Sets the client's view position and shows a leaderboard. The client can only leave this state when the map changes.

31: End of Episode Intermission [Text: StrNul]

Prevents player movement and slowly types out text to the center of the screen. The vanilla client also shows a 'Congratulations' header on screen. The client can only leave this state when the map changes.

32: Play Music Track [Track ID: U8]

Originally used to play tracks from a physical CD. Track 0 is not allowed.

33: Registration Info Screen

The vanilla client displays the first page of the help screen.

34: Small View Punch

35: Large View Punch

36: Set Ping [Client ID: U8, Ping: I16]

Sets a player's ping value in the scoreboard.

37: Set Join Time [Client ID: U8, Seconds: F32]

Sets how long a player has been connected.

38: Set Stat B [Stat ID: U8, Value: I32]

Similar to Set Stat A, but with a 32-bit value instead of an 8-bit one.

39: Muzzle Flash Light

Flashes a dynamic light centered on the player's position for 0.1 seconds on the vanilla client.

40: User Info Update [Client ID: U8, 'User ID'?: I32, Info: StrNul]

41: Download [Size: I16, Data: U8…]

42: Player Info [Client ID: U8, Bitflags: I16, X: Pos, Y: Pos, Z: Pos, Animation Frame: U8, …]

43: Spawn Projectile [Number of projectiles: U8, TODO]

This packet is a mess of bit ops.

44: 'Choke Count' [Number of 'choked' packets: U8]

45: Model ID List [Number of models: U8, TODO]

46: Sound ID List [Number of sounds: U8, TODO]

47: Object Update [TODO]

Another mess.

This is an example of why you should prefer statically-sized packets over variably-sized ones when designing net protocols.

48: Delta Object Update [TODO]

Another mess.

From what I've seen, pretty much the same as 47 Object Update but has a [U8] at the start.

49: Set Max Speed [Value: F32]

50: Set Gravity [Value: F32]

51: Set User Info Key [Client ID: U8, Key: StrNul, Value: StrNul]

52: Set Server Info Key [Key: StrNul, Value: StrNul]

53: Update Packet Loss Value [Client ID: U8, amt: U8]

I think this only changes the 'PL' value on the scoreboard.

Master Server Protocol

This took me way, way too long to figure out on my own. It's not documented anywhere and the only references to it are in a few clients' server browsers.

Step 1: Master Request

Send these bytes to a master server (usually on port 27000) and wait a bit for a response. If you don't get one within a second or two, something went wrong.

$63 $0A $00

Step 2: Master Response

$FF $FF $FF $FF $64 $0A

After this header, the packet contains tightly packed binary IPv4 address-port pairs. The first 4 bytes are the address, the last two are a big-endian port number. There's nothing that says how many you get, so you just have to read through them until you run out of packet data to read.

Step 3: Server Status

$FF $FF $FF $FF $73 $74 $61 $74 $75 $73 $0A $00

Send a packet containing these bytes to each of the addresses you got in the last step and you (should) get back '$FF $FF $FF $FF $6E' followed by a serverinfo string.