WCF Extensibility part 1: background on game server protocols
Talking to game servers
There are various kinds of multiplayer games that allow you to battle against other gamers. I’ll focus on the First Person Shooters (FPS) and Real Time Strategy (RTS) games and skip the (Massive Multiplayer Online) Role Playing Games (MMORPG and RPG). Usually the FPS and RTS games will connect to a central server that is located on your LAN or out in the Internet. The game players will interact by means of the server using a special protocol. The administrators of the server are regular game players for most of the time, but have elevated privileges to control the game server. Typical things that an admin will do is change the currently played map, restart game rounds, and kick or ban misbehaving players from the server.
This type of control can be done “locally” when the admin is one of the players connected to the server, or remotely. The latter is called remote control and it uses a special protocol particular to the type of game engine. Luckily most of these protocols are reasonably well documented. There are not that many game engines out there and hence a small number of protocols. The most famous ones are:
- Unreal (1, 2 and 3)
- Quake (1, 2 and 3)
- Half-Life (aka Goldsource, based on Quake engine)
- Doom 3
Right now, we’ll zoom in on the Source engine with its protocol for remote control of game servers based on this engine.
If you have a Source-based game like Half-Life 2 or Team Fortress 2 it is pretty easy to use control from the game interface. You can simply connect to the server to control by joining a multiplayer game. Once on the server you can access a console window to send commands. A comprehensive list of commands is available. Here are some examples of common commands:
The first command rcon_password is needed to prevent unwanted control of a game server.
You can even use remote control by specifying the server address and password first, so you do not have to connect to the game at all.
The game itself is needed to control the server, because the game knows the protocol specific to the engine. But other than that it is inconvenient that you would need to own and start the game just to control the server. There are a fair number of code implementations out there that capture the protocol and allow a programmer to use the API to control a server remotely. I wrote several myself, which is a nice programming exercise with an interesting theme of gaming. Well, at least it beats the common customer/order/product samples.
Understanding the protocol
The Source engine is created and maintained by Valve and their developer wiki holds a pretty good explanation of the Source remote control (rcon) protocol.
Here’s my take on the protocol:
Source uses a TCP connection from the client to the game server. After connecting the client should perform an authentication handshake. If the authentication was successful, you can start issuing commands. The protocol uses multiple message exchanges between client and server to accomplish both. Every message has the same structure:
packet size (int): entire message excluding packet size integer. It must be at least 10.
request id (int): random number used for correlating responses to requests
server data (int): depending on purpose of message the server data has one of the following values
string1 (null-terminated string): either command to run, password or result
string2 (null-terminated string): reserverd, must be empty string ("");
- SERVERDATA_AUTH = 3: initial authentication handshake message
- SERVERDATA_EXECCOMMAND = 2: command message
- SERVERDATA_RESPONSE_VALUE = 0: response from command message
- SERVERDATA_AUTH_RESPONSE = 2: acknowledgement of authentication message
A typical rcon session will look like this:
Let’s take a look at some of the individual packets. Let’s assume that the password is “abc123” and that the command that was issued is “changelevel de_dust2”
||4 + 4 + 7 + 1 == 16
||4 + 4 + 1 + 1 == 10
||4 + 4 + 1 + 1 == 10
||4 + 4 + 21 + 1 == 30
||4 + 4 + (string1.length + 1) + 1 == 10
||“\0” (command specific,
varies depending of success or failure of method)
After the exec command and corresponding reply packets you can continue issuing exec commands.
The sequence of message above is the ideal picture. In reality things might be a little more difficult, because:
- a reply may consist of multiple reply packets.
- authentication might not be successful because of a wrong password.
Eventually we will take everything into account, but for now we will pretend that multi-packet responses do not exist and that authentication is always successful.
If that works, the exchange of raw bytes across the network should look something like this (for the 5 messages listed above):
0000 10 00 00 00 2e 0e 00 00 03 00 00 00 61 62 63 31 ............abc1
0010 32 33 00 00 23..
0000 0a 00 00 00 2e 0e 00 00 00 00 00 00 00 00 ..............
0000 0a 00 00 00 2e 0e 00 00 02 00 00 00 00 00 ..............
0000 1e 00 00 00 d2 01 00 00 02 00 00 00 63 68 61 6e ............chan
0010 67 65 6c 65 76 65 6c 20 64 65 5f 64 75 73 74 32 gelevel de_dust2
0020 00 00 ..
0000 0a 00 00 00 d2 01 00 00 00 00 00 00 00 00 ..............
Our task at hand is to learn WCF to speak this protocol, while maintaining the channel layer concepts.