QuakeBot C/Sİ Specifications

The Bot Smith's Handbook

V0.95

Jim Rorie

jfrorie@uncc.edu


1.0 Introduction

This document is a description of the architecture of a development project known as QuakeBot C/S, a client/server bot for use with Quake, by id software. It's purpose to give individuals a insight into the project and to generate feedback for future development paths. It is accurate with the possible exception of typographical errors and has been used as a basis for a simple bot. But it is not complete, hence the current version level. I will not reach V1.0 until the project reaches a beta stage.

I intend that in time, this will be an accurate, informative document that can serve as a handbook for future QuakeBot C/S developers and others wishing to design their own bots. Currently though, it is a hack. You can get enough information to establish a connection and piddle a little bit. But that is about it. If you use this and find out something new, EMAIL ME. I will include it and along with appropriate credit.

Also included is a comprehensive treatment of the communication protocol between Quake client and server systems. This is not a proposal, rather the examination of the current protocol as best it can be determined. All steps have been taken to verify accuracy but it is based on manually decoding raw data streams in the C/S environment and its reliability should be taken with a grain of salt at this time. This is just provided to fill in the gaps that you might have or to stimulate thinking about parts not fully described.

With recent revisions, I have included information from id software on their internals. I would like to thank id for their continued and totally amazing support for their products. J. William Pelzer of the Quake Stomping Grounds has been a great resource, finding all kinds of cool stuff to contribute. Oliver T. De Guzman has added his considerable knowledge of the client-server protocol, smoothing out my rough edges. In addition, Oliver Montanuy, another bot developer and author of the Unofficial Quake Specifications, has spotted my many errors and contributed ideas about the bot's grand vision.

2.0 Client/Server Bot Architecture

The operating environment for Quake is based on the Client/Server model. This arrangement consists of a central server running the game and performing game housekeeping, collision detection, and player synchronization. The client is burdened with task of updating the screen based on information supplied by the server and displaying it in real time. One single system, the client and server run locally on the same system and appear to be one functional unit.

In the network environment, things change slightly as they become separated. The server executes on a remote system arbitrating the players, while the client executes on the local system giving runtime feedback to the gamer. On a system running on the server's local network with minimal traffic, the game should appear to execute faster, since the local machine is no longer burdened with the server process.

The idea of a Client/Server QuakeBot is to replace the Quake client with a custom program that communicates with the server. This client can perform a number of tasks depending on the implementation. The idea of a peer client allows it to used on any server, no matter what mods they are running. Thus, you need nothing special, just boot it up.

The question of fairness arises. A C/S QuakeBot would be as fair as the server since it is running as a peer client and receives no special treatment. It does have the advantage of more information than the average player, but the actual game parameters cannot be changed from the client side. This is not the case with a QuakeC bot, which requires modification of the server and can be easily hacked to cheat. I have done this, I know it is possible.

2.1 Packet Information

The Quake packet starts with a 4 byte header. This head contains two bytes of flag information, followed by the a 2 byte packet length, including header. This is shown below:

struct ControlHeader {
char Flag1;
char Flag2;
short PacketLen; // Big Endian or network byte order
};

For the assignment of the header flags, the following have been identified:

{0x80, 0x00} - Control Packet
{0x00, 0x01} - Packet Fragment
{0x00, 0x09} - Final Packet Fragment or Complete Packet
{0x00, 0x02} - Acknowledgment
{0x00, 0x10} - Runtime Game Data Packet (Update)

Certain packets contain the NET_PROTOCOL_VERSION tag. This value exists for backward compatibility with other versions of Quake Client/Server software. Currently, this value is three.

#define NET_PROTOCOL_VERSION 0x03

2.2 Control Packets

Control packets are used for the communication of specific requests of the server. Among these are Query, Connect, Player Information and Rule Information. Each control packet has a specific protocol associated with it as shown in the following section.

2.2.1 Server Information

The client transmits a broadcast packet to determine what servers are on the span. This is responded to with a directed broadcast response from the server giving current game parameters. It also contains the initialization port for initial connection.

struct ClientBroadcast {
        struct Header                  		// { 0x80, 0x00, 0x00, 0x0c };
        char OP_CODE                           	// 0x02, This is CCREQ_SERVER_INFO
        char Data [ ] = { 0x51, 0x55, 0x41, 0x4b, 0x45, 0x00} // "QUAKE", 0x00
        byte net_protocol_version NET_PROTOCOL_VERSION; // 0x03
}
0000: 80 00 00 0c 02 51 55 41 4b 45 00 03			| .....QUAKE.. 
struct ServerResponse {
        struct Header;                         	//{ 0x80, 0x00, 0x00, 0x2e}
        char OP_Code;                  		// 0x83 CCREP_SERVER_INFO
        char Data[];                           	// IP:Port
        char Name[];                           	// pentium-120
        char Map[];                            	// e1m1
        byte current_players           		// 01
        byte max_players                       	// 04
        byte net_protocal_version              	// NET_PROTOCOL_VERSION
};
0000: 80 00 00 2e 83 31 30 30 2e 31 30 30 2e 31 30 30   	| .....100.100.100
0010: 2e 35 30 3a 32 36 30 30 30 00 70 65 6e 74 69 75   	| .50:26000.pentiu
0020: 6d 2d 31 32 30 00 65 31 6d 31 00 01 04 03			| m-120.e1m1.... 

2.2.2 Player Information

The client transmits a directed packet to determine information on a given player. This is responded to with a player info packet giving the information on the requested player. The player number ranges from 0 to current_players-1. Bad player numbers get no response. The formula for color is (shirt *16 + pants). "Colors, frags, and connect_time are in little-endian ordering. Colors use only one byte of the four bytes in the packet. connect_time is in seconds, frags is a signed number." (O.DG.)

struct ClientPlayerInfo {
        struct Header; 
        char OP_CODE; // 0x03, This is CCREQ_PLAYER_INFO
        byte player_number 
};
NO EXAMPLE
struct ServerPlayerInfo {
        struct Header; 
        char OP_Code; // 0x84 CCREP_PLAYER_INFO
        byte player_number;
        char Name[];
        long colors; 
        long frags;
        long connect_time
        char Address[]
};

NO EXAMPLE 

2.2.3 Rule Information

This request can give information on any of the variables set in the Quake server environment. The following rules are currently supported: fraglimit, timelimit, teamplay, noexit, sv_friction, sv_gravity, sv_maxspeed. The client sends the RuleInfo packet to the server. The server responds with a rule and value. Continued querying is performed by sending the last string received form the server until a packet is return that has no data. No rule, no value. It will have a packet length of 5.

struct ClientRuleInfo {
        struct Header; 
        char OP_CODE; // 0x04, This is CCREQ_RULE_INFO
        char Rule[]; // 0x00 for the first request, the previously receive rule for the next. 
}

NO EXAMPLE

struct ServerPlayerInfo {
        struct Header; 
        char OP_Code;                  // 0x85 CCREP_RULE_INFO
        char rule[];
        char Value[]; 
};
NO EXAMPLE 

2.2.4 Connection Request

The client transmits a directed packet to the server with a connect request. The server responds with an Ack and a new port to connect to for all future communications.

struct ClientConnect {
        struct Header ;                        //{ 0x80, 0x00, 0x00, 0x0c}
        char OP_Code                           // 0x01
        char Data [ ] = { 0x51, 0x55, 0x41, 0x4b, 0x45, 0x00} // "QUAKE", 0x00
        byte net_protocol_version NET_PROTOCOL_VERSION; // 0x03
};
0000: 80 00 00 0c 01 51 55 41 4b 45 00 03                       | .....QUAKE.. 
struct ServerConnect Response {
        struct Header;                          // { 0x80, 0x00, 0x00, 0x09 }
        char OP_Code;                    // 0x81 This is CCREP_ACCEPT 
        short Port;                      // This is your port assigned
        	                      	// by the server
        char Unknown[ 2] = {0x00, 0x00 };    // The packet header shows these two byte
                                                // to be valid data, I dunno what, tho.
};
0000: 80 00 00 09 81 0a 04 00 00 20 20 20 20 20 20 20   | ......... 
0010: 20 20                                                     | 

Below would be a reject response:

struct ServerConnect Response {
        struct Header;
        char OP_Code; /./ 0x82 This is CCREP_REJECT 
        char Data[]; // A string telling you why;
};

NO EXAMPLE

2.3 Reliable Packets

Packet Fragment and Final Packet Fragment are basically the same packet format with an additional flag set. Messages transmitted using this mode can appear during game initialization and runtime. These are reliable packets and receipt must be confirmed with an appropriate acknowledgment. Large messages use the packet fragment flag to split data over several packets to prevent overloading of the socket protocol. If packets are fragmented, the final packet will modify the header to reflect this fact.

2.4 Acknowledgment Packets

Acknowledgments are responses to reliable transmissions. The acknowledgment consists of nothing more that a sequence to differentiate it from other packets that might have change order. Acknowledgments are required for all full packet and packet fragment transmissions.

struct ClientAcknowledge {
	struct Header; {0x00, 0x20, 0x00, 0x08}
	long Sequence; // Big endian order
};
0000: 00 02 00 08 00 00 00 03 | ........ 

2.5 Runtime Packets

Runtime packets are used for the transmission of unreliable data. These packet usually appear only during game runtime and do not require an acknowledgment. They are more fully explained in the runtime section of the protocol specification.

3.0 Game Initialization Protocol

The protocol as is currently decoded, is shown below. For some packets, there are DEM commands parsed from the data stream. Below each packet structure, there is a Ethernet packet dump from an actual game. The format for server packets is a packet header followed by a series of DEM format commands. For those unaware, the DEM format is a series of commands used to store demo's of Quake sessions for playback at a later time. Therefore, they are capable of describing fully the entire process of the game. Most of these structures are more fully explained in the Unofficial DEM File Format Specifications. I will not list the message formats here, since this spec has very good treatment of the material. WARNING!! Look at the code fragments, not the specification write up for data types. This threw me way off.

3.1 Overview

This is a listing of the steps involved in initializing a Quake session.

  1. Client sends a connect control packet. Server responds
  2. Server sends a series of reliable precache packets and a signon 1 command. Client acknowledges each. ( Server may transmit each packet more that once!!!)
  3. Client sends a NO-OP. Server acknowledges.
  4. Client sends a prespawn console command. Server acknowledges.
  5. Server sends series of entity initialization packets and a signon 2 command. Client acknowledges each.
  6. Client sends its player parameters and a spawn console command.
  7. Server sends lighting and player information with a signon 3 command.
  8. Client sends a begin console command.

Note that step 7 sometimes appears after step 8. They are not cause-effect related.

3.2 Precache Initialization Phase

This phase contains the names of the level and other resources used in the games. This is initiated by the server, transmitting a continuous stream of filenames and embedded ASCII data in a variable number of packets. The final DEM command in the stream is a Signon command to instruct the client to the prespawn phase. The following are an example of the DEM format messages received when parsing these packets. Notice the partial message statement. The packets are transmitted fracturing the complete DEM message, so you will need to reassemble them.

Command: print
Message:
VERSION 1.03 SERVER (51103 CRC)

Command: serverinfo
String = maps/e1m1.bsp
String = *1
String = *2

[truncated for space]

String = *55
String = *56
String = *57
String = progs/player.mdl
String = progs/eyes.mdl
String = progs/h_player.mdl
String = progs/gib1.mdl
[truncated for space]
String = progs/g_nail.mdl
String = maps/b_rock0.bsp
String = maps/b_shell1.bsp

Partial Message, getting more....

String = progs/invulner.mdl
String = progs/suit.mdl
String = maps/b_explob.bsp

Sound Precache
String = weapons/r_exp3.wav
String = weapons/rocket1i.wav
String = weapons/sgun1.wav
String = weapons/guncock.wav

[truncated for space]

String = items/suit2.wav
String = misc/secret.wav
String = ambience/comp1.wav
String = ambience/drone6.wav
String =

Command: CD Track
Range 6 - 6

Command: Setview
Entity: 1

Command: Signon
Action: 1

0000: 00 01 04 08 00 00 00 00 0b 0f 00 00 00 04 01 74 | ...............t
0010: 68 65 20 53 6c 69 70 67 61 74 65 20 43 6f 6d 70 | he Slipgate Comp
0020: 6c 65 78 00 6d 61 70 73 2f 65 31 6d 31 2e 62 73 | lex.maps/e1m1.bs
0030: 70 00 2a 31 00 2a 32 00 2a 33 00 2a 34 00 2a 35 | p.*1.*2.*3.*4.*5
0040: 00 2a 36 00 2a 37 00 2a 38 00 2a 39 00 2a 31 30 | .*6.*7.*8.*9.*10
0050: 00 2a 31 31 00 2a 31 32 00 2a 31 33 00 2a 31 34 | .*11.*12.*13.*14
0060: 00 2a 31 35 00 2a 31 36 00 2a 31 37 00 2a 31 38 | .*15.*16.*17.*18
0070: 00 2a 31 39 00 2a 32 30 00 2a 32 31 00 2a 32 32 | .*19.*20.*21.*22
0080: 00 2a 32 33 00 2a 32 34 00 2a 32 35 00 2a 32 36 | .*23.*24.*25.*26
0090: 00 2a 32 37 00 2a 32 38 00 2a 32 39 00 2a 33 30 | .*27.*28.*29.*30
00a0: 00 2a 33 31 00 2a 33 32 00 2a 33 33 00 2a 33 34 | .*31.*32.*33.*34
00b0: 00 2a 33 35 00 2a 33 36 00 2a 33 37 00 2a 33 38 | .*35.*36.*37.*38
00c0: 00 2a 33 39 00 2a 34 30 00 2a 34 31 00 2a 34 32 | .*39.*40.*41.*42
00d0: 00 2a 34 33 00 2a 34 34 00 2a 34 35 00 2a 34 36 | .*43.*44.*45.*46
00e0: 00 2a 34 37 00 2a 34 38 00 2a 34 39 00 2a 35 30 | .*47.*48.*49.*50
00f0: 00 2a 35 31 00 2a 35 32 00 2a 35 33 00 2a 35 34 | .*51.*52.*53.*54
0100: 00 2a 35 35 00 2a 35 36 00 2a 35 37 00 70 72 6f | .*55.*56.*57.pro
0110: 67 73 2f 70 6c 61 79 65 72 2e 6d 64 6c 00 70 72 | gs/player.mdl.pr
0120: 6f 67 73 2f 65 79 65 73 2e 6d 64 6c 00 70 72 6f | ogs/eyes.mdl.pro
0130: 67 73 2f 68 5f 70 6c 61 79 65 72 2e 6d 64 6c 00 | gs/h_player.mdl.
0140: 70 72 6f 67 73 2f 67 69 62 31 2e 6d 64 6c 00 70 | progs/gib1.mdl.p
0150: 72 6f 67 73 2f 67 69 62 32 2e 6d 64 6c 00 70 72 | rogs/gib2.mdl.pr
0160: 6f 67 73 2f 67 69 62 33 2e 6d 64 6c 00 70 72 6f | ogs/gib3.mdl.pro
0170: 67 73 2f 73 5f 62 75 62 62 6c 65 2e 73 70 72 00 | gs/s_bubble.spr.
0180: 70 72 6f 67 73 2f 73 5f 65 78 70 6c 6f 64 2e 73 | progs/s_explod.s
0190: 70 72 00 70 72 6f 67 73 2f 76 5f 61 78 65 2e 6d | pr.progs/v_axe.m
01a0: 64 6c 00 70 72 6f 67 73 2f 76 5f 73 68 6f 74 2e | dl.progs/v_shot.
01b0: 6d 64 6c 00 70 72 6f 67 73 2f 76 5f 6e 61 69 6c | mdl.progs/v_nail
01c0: 2e 6d 64 6c 00 70 72 6f 67 73 2f 76 5f 72 6f 63 | .mdl.progs/v_roc
01d0: 6b 2e 6d 64 6c 00 70 72 6f 67 73 2f 76 5f 73 68 | k.mdl.progs/v_sh
01e0: 6f 74 32 2e 6d 64 6c 00 70 72 6f 67 73 2f 76 5f | ot2.mdl.progs/v_
01f0: 6e 61 69 6c 32 2e 6d 64 6c 00 70 72 6f 67 73 2f | nail2.mdl.progs/
0200: 76 5f 72 6f 63 6b 32 2e 6d 64 6c 00 70 72 6f 67 | v_rock2.mdl.prog
0210: 73 2f 62 6f 6c 74 2e 6d 64 6c 00 70 72 6f 67 73 | s/bolt.mdl.progs
0220: 2f 62 6f 6c 74 32 2e 6d 64 6c 00 70 72 6f 67 73 | /bolt2.mdl.progs
0230: 2f 62 6f 6c 74 33 2e 6d 64 6c 00 70 72 6f 67 73 | /bolt3.mdl.progs
0240: 2f 6c 61 76 61 62 61 6c 6c 2e 6d 64 6c 00 70 72 | /lavaball.mdl.pr
0250: 6f 67 73 2f 6d 69 73 73 69 6c 65 2e 6d 64 6c 00 | ogs/missile.mdl.
0260: 70 72 6f 67 73 2f 67 72 65 6e 61 64 65 2e 6d 64 | progs/grenade.md
0270: 6c 00 70 72 6f 67 73 2f 73 70 69 6b 65 2e 6d 64 | l.progs/spike.md
0280: 6c 00 70 72 6f 67 73 2f 73 5f 73 70 69 6b 65 2e | l.progs/s_spike.
0290: 6d 64 6c 00 70 72 6f 67 73 2f 62 61 63 6b 70 61 | mdl.progs/backpa
02a0: 63 6b 2e 6d 64 6c 00 70 72 6f 67 73 2f 7a 6f 6d | ck.mdl.progs/zom
02b0: 5f 67 69 62 2e 6d 64 6c 00 70 72 6f 67 73 2f 76 | _gib.mdl.progs/v
02c0: 5f 6c 69 67 68 74 2e 6d 64 6c 00 70 72 6f 67 73 | _light.mdl.progs
02d0: 2f 61 72 6d 6f 72 2e 6d 64 6c 00 6d 61 70 73 2f | /armor.mdl.maps/
02e0: 62 5f 6e 61 69 6c 30 2e 62 73 70 00 70 72 6f 67 | b_nail0.bsp.prog
02f0: 73 2f 71 75 61 64 64 61 6d 61 2e 6d 64 6c 00 6d | s/quaddama.mdl.m
0300: 61 70 73 2f 62 5f 62 68 31 30 30 2e 62 73 70 00 | aps/b_bh100.bsp.
0310: 70 72 6f 67 73 2f 67 5f 72 6f 63 6b 32 2e 6d 64 | progs/g_rock2.md
0320: 6c 00 70 72 6f 67 73 2f 67 5f 72 6f 63 6b 2e 6d | l.progs/g_rock.m
0330: 64 6c 00 6d 61 70 73 2f 62 5f 72 6f 63 6b 31 2e | dl.maps/b_rock1.
0340: 62 73 70 00 70 72 6f 67 73 2f 67 5f 6e 61 69 6c | bsp.progs/g_nail
0350: 32 2e 6d 64 6c 00 70 72 6f 67 73 2f 67 5f 73 68 | 2.mdl.progs/g_sh
0360: 6f 74 2e 6d 64 6c 00 6d 61 70 73 2f 62 5f 73 68 | ot.mdl.maps/b_sh
0370: 65 6c 6c 30 2e 62 73 70 00 6d 61 70 73 2f 62 5f | ell0.bsp.maps/b_
0380: 62 68 31 30 2e 62 73 70 00 6d 61 70 73 2f 62 5f | bh10.bsp.maps/b_
0390: 62 68 32 35 2e 62 73 70 00 6d 61 70 73 2f 62 5f | bh25.bsp.maps/b_
03a0: 6e 61 69 6c 31 2e 62 73 70 00 70 72 6f 67 73 2f | nail1.bsp.progs/
03b0: 67 5f 6e 61 69 6c 2e 6d 64 6c 00 6d 61 70 73 2f | g_nail.mdl.maps/
03c0: 62 5f 72 6f 63 6b 30 2e 62 73 70 00 6d 61 70 73 | b_rock0.bsp.maps
03d0: 2f 62 5f 73 68 65 6c 6c 31 2e 62 73 70 00 70 72 | /b_shell1.bsp.pr
03e0: 6f 67 73 2f 69 6e 76 75 6c 6e 65 72 2e 6d 64 6c | ogs/invulner.mdl
03f0: 00 70 72 6f 67 73 2f 73 75 69 74 2e 6d 64 6c 00 | .progs/suit.mdl.
0400: 6d 61 70 73 2f 62 5f 65 | maps/b_e

3.3 Sync Phase

This appears to be a synchronization phase. The first packet transmitted is a no-op, the second is a prespawn console command. These are transmitted from the client and acknowledged by the server.

struct ClientPacket{
	struct Header; 
	struct Sequence;
	char Data = 0x01;
};

0000: 00 09 00 09 00 00 00 00 01 | .........

struct ClientPrespawn{
	struct Header;
	struct Sequence;
	char RuleByte;		// I am making an assumption here
	char Prespawn[] = "prespawn"
};

0000: 00 09 00 12 00 00 00 01 04 70 72 65 73 70 61 77 | .........prespaw
0010: 6e 00 | n.

3.4 Entity Initialization Phase

The section consists of numerous spawns, creating the entities for use the in the game. A signon 2 command is appended to move the client into the next phase. Below is a sample DEM parse session from the data stream. Again, the packets split the DEM message in the middle so you must reassemble.

Command: spawnstaticsound
Command: spawnstaticsound
Command: spawnstaticsound
Command: spawnstaticsound

{ Truncated]

Command: spawnstaticsound
Command: spawnstaticsound
Command: spawnstaticsound
Command: spawnstaticsound
Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline

{ Truncated]

Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline

Partial Message, getting more....

Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline

{ Truncated]

Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline
Command: Spawn Baseline
Command: Signon
Action: 2

0000: 00 01 04 08 00 00 00 03 1d 80 15 00 06 80 02 3e | ...............>
0010: 7f c0 1d 80 12 00 11 c0 02 44 7f c0 1d 80 05 80 | .........D......
0020: 36 40 fb 44 7f c0 1d 80 02 80 36 40 fb 44 7f c0 | 6@.D......6@.D..
0030: 1d 00 0d 80 40 80 fc 44 7f c0 1d 00 0d 80 3d 80 | ....@..D......=.
0040: fc 44 7f c0 1d c0 0a 40 45 40 fd 3e 7f c0 1d 40 | .D.....@E@.>...@
0050: f1 80 40 40 fd 44 7f c0 1d 00 29 00 22 80 f4 51 | ..@@.D....)."..Q
0060: 7f c0 1d d0 07 10 06 40 02 5d ff c0 1d 50 16 10 | .......@.]...P..
0070: 06 40 02 5d ff c0 1d 90 13 50 40 c0 fc 5d ff c0 | .@.].....P@..]..
0080: 1d 90 0e 90 45 c0 fc 5d ff c0 1d 10 29 10 0e c0 | ....E..]....)...
0090: f9 5e 7f c0 16 00 00 01 00 00 00 00 00 00 00 00 | .^..............
00a0: 00 00 00 00 16 01 00 3b 00 01 00 00 00 00 00 00 | .......;........
00b0: 00 00 00 00 16 02 00 3b 00 02 00 00 00 00 00 00 | .......;........
00c0: 00 00 00 00 16 03 00 3b 00 03 00 00 00 00 00 00 | .......;........
00d0: 00 00 00 00 16 04 00 3b 00 04 00 00 00 00 00 00 | .......;........
00e0: 00 00 00 00 16 0b 00 02 00 00 00 00 00 00 00 00 | ................
00f0: 00 00 00 00 16 0c 00 03 00 00 00 00 00 00 00 00 | ................
0100: 00 00 00 00 16 0e 00 55 00 00 00 80 15 00 00 0f | .......U........
0110: 00 80 02 00 16 0f 00 04 00 00 00 00 00 00 00 00 | ................
0120: 00 00 00 00 16 10 00 05 00 00 00 00 00 00 00 00 | ................
0130: 00 00 00 00 16 11 00 06 00 00 00 00 00 00 00 00 | ................
0140: 00 00 00 00 16 12 00 07 00 00 00 00 00 00 00 00 | ................
0150: 00 00 00 00 16 19 00 08 00 00 00 00 00 00 00 00 | ................
0160: 00 40 fb 00 16 1b 00 09 00 00 00 00 00 00 80 f8 | .@..............
0170: 00 00 00 00 16 1c 00 0a 00 00 00 00 00 00 00 00 | ................
0180: 00 00 00 00 16 22 00 0b 00 00 00 00 00 00 00 00 | ....."..........
0190: 00 00 00 00 16 24 00 56 00 00 00 80 08 00 80 49 | .....$.V.......I
01a0: 00 00 02 00 16 2a 00 0e 00 00 00 00 00 00 00 00 | .....*..........
01b0: 00 00 00 00 16 2b 00 0f 00 00 00 00 00 00 00 00 | .....+..........
01c0: 00 00 00 00 16 2c 00 10 00 00 00 00 00 00 00 00 | .....,..........
01d0: 00 f0 fd 00 16 2e 00 57 00 00 00 00 11 00 80 4d | .......W.......M
01e0: 00 40 fd 00 16 2f 00 14 00 00 00 00 00 00 00 00 | .@.../..........
01f0: 00 00 00 00 16 31 00 16 00 00 00 00 00 00 00 00 | .....1..........
0200: 00 00 00 00 16 32 00 17 00 00 00 00 00 00 00 00 | .....2..........
0210: 00 80 f3 00 16 34 00 18 00 00 00 00 00 00 00 00 | .....4..........
0220: 00 00 00 00 16 36 00 58 00 00 00 80 1d 00 80 1f | .....6.X........
0230: 00 80 f7 00 16 37 00 59 00 00 00 80 04 00 80 49 | .....7.Y.......I
0240: 00 80 00 00 16 38 00 5a 00 00 00 00 26 00 80 20 | .....8.Z....&..
0250: 00 80 f2 00 16 39 00 5b 00 00 00 80 2b 00 00 20 | .....9.[....+..
0260: 00 80 f2 00 16 3b 00 5c 00 00 00 00 1a 00 80 4c | .....;.\.......L
0270: 00 80 f4 00 16 3c 00 5d 00 00 00 00 04 00 00 26 | .....<.].......&
0280: 00 80 f9 00 16 3d 00 5e 00 00 00 40 09 00 c0 42 | .....=.^...@...B
0290: 00 00 fa 00 16 3e 00 5f 00 00 00 80 2c 00 40 1c | .....>._....,.@.
02a0: 00 80 f2 00 16 3f 00 60 00 00 00 00 2b 00 40 19 | .....?.`....+.@.
02b0: 00 80 f2 00 16 40 00 60 00 00 00 c0 24 00 40 1d | .....@.`....$.@.
02c0: 00 80 f2 00 16 46 00 23 00 00 00 00 00 00 00 00 | .....F.#........
02d0: 00 80 ff 00 16 47 00 24 00 00 00 00 00 00 00 00 | .....G.$........
02e0: 00 80 ff 00 16 48 00 25 00 00 00 00 00 00 00 00 | .....H.%........
02f0: 00 80 ff 00 16 49 00 26 00 00 00 00 00 00 00 00 | .....I.&........
0300: 00 80 ff 00 16 4b 00 61 00 00 00 00 2b 00 00 20 | .....K.a....+..
0310: 00 80 f7 00 16 4c 00 60 00 00 00 00 25 00 00 1f | .....L.`....%...
0320: 00 80 f7 00 16 4d 00 5f 00 00 00 00 2b 00 c0 1a | .....M._....+...
0330: 00 80 f7 00 16 4e 00 5f 00 00 00 40 27 00 40 35 | .....N._...@'.@5
0340: 00 80 f2 00 16 53 00 62 00 00 00 00 0f 00 00 12 | .....S.b........
0350: 00 00 00 00 16 54 00 61 00 00 00 80 0e 00 c0 16 | ....T.a........
0360: 00 00 02 00 16 55 00 60 00 00 00 40 0a 00 80 1a | .....U.`...@....
0370: 00 00 f9 00 16 56 00 60 00 00 00 c0 0a 00 c0 1c | .....V.`........
0380: 00 00 f9 00 16 57 00 5f 00 00 00 80 ff 00 80 40 | .....W._.......@
0390: 00 80 f9 00 16 58 00 63 00 00 00 00 f1 00 00 46 | .....X.c.......F
03a0: 00 00 fb 00 16 59 00 64 00 00 00 00 fd 00 c0 4c | .....Y.d.......L
03b0: 00 80 00 00 16 5a 00 5b 00 00 00 c0 fc 00 40 45 | .....Z.[......@E
03c0: 00 80 00 00 16 5b 00 65 00 00 00 00 08 00 80 38 | .....[.e.......8
03d0: 00 c0 fe 00 16 5c 00 66 00 00 00 40 16 00 c0 3f | .....\.f...@...?
03e0: 40 40 f3 00 16 5d 00 5b 00 00 00 80 29 00 40 4f | @@...].[....).@O
03f0: 00 80 ef 00 16 5e 00 58 00 00 00 a0 1c 00 80 4b | .....^.X.......K
0400: 00 c0 fb 00 16 62 00 2a | .....b.*

0000: 00 09 00 c6 00 00 00 04 00 00 00 00 00 00 00 00   | ................
0010: 00 00 00 00 16 63 00 5d 00 00 00 c0 f4 00 00 5b   | .....c.].......[
0020: 00 80 fd 00 16 65 00 5e 00 00 00 80 10 00 80 16   | .....e.^........
0030: 00 80 02 00 16 72 00 5f 00 00 00 40 f4 00 40 35   | .....r._...@..@5
0040: 00 00 f9 00 16 73 00 2c 00 00 00 00 00 00 00 00   | .....s.,........
0050: 00 00 00 00 16 74 00 5e 00 00 00 00 15 00 c0 fe   | .....t.^........
0060: 00 80 01 00 16 7b 00 5f 00 00 00 c0 12 00 c0 44   | .....{._.......D
0070: 00 00 fc 00 16 7c 00 67 00 00 00 40 02 00 40 40   | .....|.g...@..@@
0080: 00 81 f9 00 16 7e 00 5f 00 00 00 40 26 00 00 4d   | .....~._...@&..M
0090: 00 80 f6 00 16 80 00 55 00 00 01 00 29 00 c0 20   | .......U....).. 
00a0: 00 80 f2 00 16 8c 00 39 00 00 00 00 00 00 00 00   | .......9........
00b0: 00 00 00 00 16 8d 00 3a 00 00 00 00 00 00 00 00   | .......:........
00c0: 00 00 00 00 19 02                                 | ......

 

3.5 Client Parameter Transfer

The client transmits the name, color and other local parameters to the server. Each parameter is preceded with a 0x04 which is a console command. The final command is a spawn, which indicates that the client is initialized and ready to go to runtime. The server will respond with an ack.

 

struct ClientPlayerData {
        struct Header; // { 0x00, 0x09, 0x00, 0x31}
        struct Sequence; { 0x00, 0x00, 0x00, 0x02 }
        const char tag1[] = "\0x04name ";
        char Name[] // ASCII name
        const char tail1[] = { 0x0a, 0x00};
        const char tag2[] = "\0x04color ";
        char fgcolor[] = // ASCII color number
        const char tag3 = " ";
        char bgcolor[] = // ASCII color number
        char tail3[] = {0x0a, 0x00, 0x04, "spawn ", 0x00};
};
0000: 00 09 00 31 00 00 00 02 04 6e 61 6d 65 20 42 69   | ...1.....name Bi
0010: 67 5f 42 61 64 5f 6a 69 6d 0a 00 04 63 6f 6c 6f   | g_Bad_jim...colo
0020: 72 20 31 33 20 31 33 0a 00 04 73 70 61 77 6e 20   | r 13 13...spawn 
0030: 00                                                | .

3.6 Client Sync

The client now instructs the server that it has initialized and is ready to begin the game.

struct ClientBegin {
        struct Header; // { 0x00, 0x09, 0x00, 0x0f }
        struct Sequence; // {0x00, 0x00, 0x00, 0x03 };
        char Data[7] = { 0x04, "begin", 0x00 };
};
0000: 00 09 00 0f 00 00 00 03 04 62 65 67 69 6e 00                     | .........begin. 

3.7 Server Parameter Transfer Phase

This packet transmits the current parameters for all the player on the server as well as additional lighting information. The final command instructs the client to advance to sign on state 3, Runtime.

struct ServerParameterPacket {   // length Varies
      	struct Header;                 // {0x00 0x10 0x00 0x6f}
      	struct Sequence;                // Most significant byte of the packet ordering
      	struct DEMTimeStamp          // Some type of time stamp
      	struct DEMUpdateClientParamters;  
	struct DEMUpdateEntity;
     	// There is usually more data appended at this point.
        
};
Example DEM parsing:

Command: Time
Time = 1183438817.000000
Command: Update Client Data
Health: 100, Ammo: 25, Armour: 0, Weapon: 1
Shells: 25, Nails: 0, Rockets: 0, Cells: 0
Command: Update Entity (ce)
Mask: 4E, Entity: 1, ModelIndex: 0, Frame: 14, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 15, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 16, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 18, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0

[Truncated]

Command: Update Entity (80)
Mask: 0, Entity: 119, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 120, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Temp Entity
Command: Update Name
player # = 0
Player Name = QuakeBot
Command: Update Frags
Player # = 0
Frags # = 0
Command: Update Colors
Command: Update Name
player # = 1
Player Name = 
Command: Update Frags
Player # = 1
Frags # = 0
Command: Update Colors
Command: lightstyle
Light style = 0
Effect String = m
New offset: 69
Command: lightstyle
Light style = 1
Effect String = mmnmmommommnonmmonqnmmo
New offset: 83
Command: lightstyle
Light style = 2
Effect String = abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba
[Truncated]
Command: Update Stat
PlayerState[11] = 6
Command: Update Stat
PlayerState[12] = 0
Command: Update Stat
PlayerState[13] = 0
Command: Update Stat
PlayerState[14] = 0
Command: Set Angle
Vector 0.000000, 90.000000, 0.000000
Command: Set Angle
Vector 0.000000, 90.000000, 0.000000
Command: Update Client Data
Health: 100, Ammo: 25, Armor: 0, Weapon: 1
Shells: 25, Nails: 0, Rockets: 0, Cells: 0
Command: Signon
Action: 3
0000: 00 10 00 6f 00 00 00 14 07 1f 60 91 45 0f 00 46   | ...o......`.E..F
0010: 01 11 00 00 44 64 00 19 19 00 00 00 01 ce 02 0c   | ....Dd..........
0020: 80 f7 80 5b 41 fe df 0c 05 3b 45 01 00 fc ff 49   | ...[A....;E....I
0030: 45 c0 01 9f 0e 06 3d 02 58 ff c5 53 7b 81 fd 01   | E.....=.X..S{...
0040: 9f 0e 07 3d 02 0e f8 41 5a 0f 81 fd fb df 0c 08   | ...=...AZ.......
0050: 3b 3c 01 96 fc 7f 5d b9 41 fe 80 19 84 1b 00 00   | ;<....].A.......
0060: c4 1c 01 e1 ff 80 58 80 5a 80 62 84 63 0c 5b      | ......X.Z.b.c.[ 

4.0 Runtime Phase

The game has now begun. The server sends out a packet of data every 50 or so milliseconds that gives the current parameters for everyone on the system. The client responds with a complementary packet giving the current state of the system. The client system appears to work by way of digital sampling. That is, the current state of the machine is frozen and transmitted to the remote system as opposed to transmitting every little change. Of interest at this point are the Packet Sequence bytes. These bytes appear to be counters that increment sequentially with each transmitted packet. This is prevents packets that follow different routes from getting out of order.

4.1 Keep Alive

This packet's function is to let the destination know that the source is still present, but is not presently transmitting data. This packet can be sent by either the server or the client, during either runtime or game initialization.

struct RuntimeKeepAlivePacket { // 
	struct Header; // {0x00 0x10 0x00 0x09}
	long Sequence;  // Network byte order
	char Command;	// 0x01 for keep alive
}; 

4.2 Disconnect

This packet is transmitted by the client to inform the server that is the client is ending its session. It is very important for the client to transmit this packet and not to simply end the connection. Doing so will confuse the server and require multiple attempts from the next client to establish connection.

struct RuntimeDisconnectPacket { // 
	struct Header; // {0x00 0x10 0x00 0x09}
	long Sequence;  // Network byte order
	char Command;	// 0x02 for Disconnect
}; 

4.3 Client Movement

The client to server movement packet is of fixed length and contains the information shown in the structure below. The action byte is a bit mapped value that represents the state of key presses on the client side. Fire and Jump have been determined in this area. The run key is not reflected in here as it is conveyed by an increased value in the inline and lateral velocity.

struct RuntimeClientMovementPacket { // length 24 bytes
	struct Header; // {0x00 0x10 0x00 0x18}
	long Sequence;  // Network byte order
	char Command;	// 0x03 for movement
	float TimeStamp; // Some type of time stamp
	char Roll;
	char Rotation; // Absolute Rotation
	char ViewAngle; // 00: Horizontal, Positive down, negative up
	short InlineSpeed; // Speed in forward/reverse
	short LateralSpeed; // Speed, side to side
     	short VerticleSpeed;
     	char Impulse; // 01=Fire, 02 Jump
}; 
0000: 00 10 00 18 00 00 00 00 03 3b 59 91 45 00 00 00   | .........;Y.E...
0010: 00 00 00 00 00 00 00 00                           | ....

4.4 Console Command

This packet is transmitted by the client to instruct the server to perform a command. The most common use for this function is the say command. The character string supplied must be terminated by 0x0 and formatted exactly as the command would be typed in the server console.

struct RuntmeConsoleCommandPacket { // 
	struct Header; // {0x00 0x10, 0x??, 0x??}
	long Sequence;  // Network byte order
	char Command;	// 0x04 for Commands
	char String[]; //  The command to send to the server.
}; 

4.5 Server Packet

The server packet is not several large structures, but a series of concatenated smaller structures. These packets are buffered and transmitted all at once to reduce network packet overhead. This was documented in A. Oliver's Unofficial Quake Network Protocol. It gives reference to the Unofficial DEM File Format Specifications as the structure for the server packet. The DEM specs can be misleading. One should follow the code, not the text descriptions. The type specifications are different and will result in a mess.

Positioning appears to work in absolute coordinates. For reference, on start.map, facing the three hallways from the platform, forward is in the Y-axis direction. Additionally, the rotation value is 0x40 for that direction, where 0x00 is directly to the right and 0x80 is to the left.

Messages and collisions are sent as DEM messages and will either be contained in the runtime packet or transmitted in their own packet. Both have been observed.

struct RuntimeServerPacket {   // length Varies
      	struct Header;                 // {0x00 0x10 0x00 0x6f}
      	struct Sequence;                // Most significant byte of the packet ordering
      	struct DEMTimeStamp          // Some type of time stamp
      	struct DEMUpdateClientParamters;  
	struct DEMUpdateEntity;
     	// There is usually more data appended at this point.
};
Example DEM parsing:

Command: Time
Time = 1183438817.000000
Command: Update Client Data
Health: 100, Ammo: 25, Armour: 0, Weapon: 1
Shells: 25, Nails: 0, Rockets: 0, Cells: 0
Command: Update Entity (ce)
Mask: 4E, Entity: 1, ModelIndex: 0, Frame: 14, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 15, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 16, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 18, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 119, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
Command: Update Entity (80)
Mask: 0, Entity: 120, ModelIndex: 0, Frame: 0, Map: 0, Skin: 0, State: 0
NO EXAMPLE

 

5.0 Data Interpretation

The largest problem facing the Bot designer is interpreting the data that is received. Although you have a lot of information at your disposal, what to do with it becomes a major concern. In order to fully interact in the game, several key pieces of information need to be extract form the data at hand. I will list how I resolved some of these problem. These are not necessarily the best solutions but they have worked to some limited extent. I am open to all suggestions if someone comes up with something better.

Some of the information that is needed:

5.1 Client Statistics

Information on your client is transmitted via the Update Client command. This command summarizes all information about your current state, including weapons, armor, orientation and health. It is important to maintain a these parameters between transmissions since the DEM format only updates information that changes between updates. Most information contained in the DEM specs on the Update Client command is self explanatory.

5.2 Player Identification

Well, you know who you are, but how do you find out who the server thinks you are? This becomes important for determining your absolute coordinates in the level. Also, you need to find out who else is playing and their respective statistics.

Each player and any other moveable object in the level are represented by dynamic entities. These entities are used by the server to encapsulate information about any given object. They are communicated to the client through the use of the spawn baseline and update entity commands.

The spawn baseline command creates a dynamic entity. This command is received once for each new entity at the beginning of the level. Once created, the entity record is modified through the Update Entity command. This command is sent to the client for every entity that is in potential view on every server transmission. Notice the word POTENTIAL. This includes items that are behind and possibly obscured by other objects. In this way, the Bot has an advantage in that is knows more about its environment that the human player does at a given moment.

The QuakeBot's entity can be identified by the Set View command. This commands specific which entity is the current viewpoint for the camera. Individual players are identified by their respective names sent through the Update Name command. This combined with the Update Frags command gives the most accurate source of player statistics.

The current life/death state of an opponent appears to be obtained through the model frame value. This value changes for each variation in representation of an entity. It appears that certain model frames represent death frames and from this, one can determine the death of a player. Once a player dies, its entity changes to a death frame. After regeneration, the body is represented by another entity number > MAXCLIENTS + 1 and the player entity again represents the opponent.

5.3 Object Identification

Currently, the only way to determine an object in the wad is through mapping the .mdl files through the precache table to find out what it is. This is a very grisly approach and will not stand custom modifications. Quake has to positively identify health packs, weapons, etc. to the game, therefore, there must be some mechanism to tell the function of the entity. I have noticed a descriptor in the .MAP file specs that might be of use, but this gets compiled and I don't know where it goes afterward.

5.4 Navigation

Eeeeek. This is a nasty problem. For simple functional tests, you can calculate paths as the crow files and pray that nothing is in between you and your target. Unfortunately, this is rarely the case. Most levels use insidious transport mechanisms to make getting some objects more difficult. Combine this with a little gratuitous BSP tree traversal and you have got yourself an A-1 mess. Just a few of the navigation issues:

  1. Transporters - Easy but requires a little 4th dimensional thinking
  2. Elevators - Single levels aint bad, but multilevel systems are hairy. Don't forget the push to activate type!!!
  3. Moving Platforms - A variation on the elevator theme, but more possibilities.
  4. Jumps - I can't figure out how to make some of the jumps, but the bot might. The question is how to figure out the need to jump to begin with.
  5. Water - Probably not really that bad of a problem, but it still gives me the creeps.
  6. Traps - The bot can see them better than people, but what to do is another issue.
  7. Dynamic Path Changes - Some paths reorient themselves based on a button or other gizmo. I don't even want to think about this one. Maybe avoid these areas?

6.0 QuakeBot Functional Architecture

The current QuakeBot C/S architecture is divided into the following sections:

6.1 Socket Layer

This, the lowest level of the QuakeBot C/S architecture, handles basic I/O between network nodes. Currently, it implements TCP/IP, but will evolve into IPX in time. Its basic form of communication is a raw packet:

void SocketObj::SendPacket(char *Data, int DataLen) - Sends buffer to IP specified by object. Data is raw.

int SocketObj::GetMessage() - Receive data from IP specified by object. Data is raw.

void AddTimeoutHandler(long Timeout, void (*Handler)(void)) - Adds routine to handle receive time-outs.

6.2 Network Services Layer

This layer is burdened with the task of handling communications between the Quake server and the QuakeBot Host. It also handles communications between bots, referred to here as interbot communications.

The server communications functions consist of creating a virtual circuit through the UDP data stream by interpretation of the Quake packet sequence numbers and implementing the C/S handshake protocol. This results in a seamless data stream presented to the command layer. The commands implemented in the layer are:

void PacketObj::SendMessage() - Sends a packet to the destination specified by the object. Sequencing, and Packet Length are automatically handled.

int PacketObj::GetMessage() - Receives a packet from a remote system specified by the object. Out of sequence packets are dropped until next proper value is received. Returns the number of bytes received.

BotObj::ConnectBot() - Establishes a session with a peer bot.

BotObj::DisconnectBot() - Drops connection.

BotObj::SpawnBot() - Create a new Bot on same machine (if possible, I dunno yet)

QueryBot(char *IPADDRESS) - Queries unconnected bots.

void BotObj::Message(CommandObj Command) - Sends a message to peer bot informing of completion of a task.

6.3 Primitive Command Layer

The command layer implements the functions for sending the server movement and attack commands. It also implements the commands necessary for sending and receiving of directives to and from peer bots. It would necessarily implement map traversal and map inventory, including regeneration time for important entities.

void PlayerObj::SendBroadcastMessage(const char *String) - Sends a message to server. Greetings, taunts, abuse, etc.

int PlayerObj::Aim(PlayerObj Player) - Changes player command variables to aim at player. Returns the distance to player.

int PlayerObj::Distance(PlayerObj Player) - Calculates distance to player in three dimensions.

int PlayerObj::SelectBestWeapon(void) - Selects the most powerful weapon based on the weapons in inventory and current ammo.

int PlayerObj::Dead(void) - Determines if a player is dead.

int PlayerObj::SelectSafeWeapon(void) - Selects the most powerful weapon based on the weapons in inventory and current ammo. Rocket and Grenade launchers are excluded to prevent shooting into a wall and killing your self.

6.4 Heuristic Command Layer - Preliminary

This is the bot's brains. The QuakeBot C/S specification defines, but does not include this layer. It is intended to be supplied by the end user. Our intention is to provide a framework where by individuals with minimal network programming experience can develop their own heuristic modules that are functionally compatible with the interface. This module may either access the lower level routines through DLL calls or program hooks. Both are to be defined once the command level functionality is in place.

int BotObj::GotoXYZ(struct Coord) - Instructs a peer bot to traverse map to get to location XYZ. Returns path distance or -1 if the point is unreachable.

int PlayerObj::Track(PlayerObj Player, int Distance) - Finds Player and keeps within a specified distance.

void PlayerObj::Evade(PlayerObj Player) - Evades Player.

struct Coord Find(EntityObj Entity) - Returns nearest Entity. Weapon, armor, health, powerup, etc.

struct Coord FindNext(EntityObj Entity) - Returns next nearest Entity. Weapon, armor, health, powerup, etc.

int Exist(EntityObj Entity) - Returns the a normalized probability of the existence of Entity based on last appearance and regeneration times.

6.4.1 Navigation Object

Navigation algorithms should be modular. This would allow a bot to be upgraded to a new navigation rev if someone comes up with a better algorithm. The higher heuristic levels would be able to benefit from more accurate navigation without changing their underlying functions. I would propose a Navigation object. This object would exist in a separate thread, operate on the BSP tree directly and encapsulate the following data :

struct Movement - Returned commands would be in the form of vectors to new locations. The it would be up to the player object to determine how to complete the move accounting for velocity and acceleration. A Flag would be necessary to differentiate between a ramp/stair system and the need for a jump. Also, some way of conveying timing information for the player is needed for dynamic platforms, elevators and the like. Ex. Entity x at (0,0,0) then proceed.

struct Movement NavObj::FirstPath(PlayerObject Player, struct Coord) - Return the first move to get to locations XYZ. Returns path distance or -1 if the point is unreachable.

struct Movement NavObj::NextPath(PlayerObject Player, struct Coord) - Return the next move to get to locations XYZ. Returns path distance or -1 if the point is unreachable.

7.0 Bot Communications Language

The Bot Communication Language specification allows bots created by different authors to communicate through a common interface. The need for this may not be readily apparent as it should be obvious that a client bot by itself can be a far superior opponent to a human player. So why communicate?

There are applications for client bots that exceed the traditional deathmatch opponent role. They could be used as referees or as intelligent data collectors. To provide complete coverage of a level, several bots could be deployed. Each could perform a separate function or cover a section of a level. Interbot communications could help coordinate this complicated task by allowing the bots to keep track of each other. For the true cyber challenge, teams of bots could challenge other teams to find out who is the better opponent.

Communications via sockets would provide a robust method of transmission. Sockets allow point to point communications, relieving the burden placed on a Quake server. The question of fairness arises in this situation since other players would not be aware of the information exchange between clients. It should be obvious that interbot communication as a whole is so completely unfair that it really makes the question irrelevant. IBC should be reserved for high level functions or bot vs bot team competitions. Anything else is equivalent to squirrel hunting with a bazooka.

...but sometimes that squirrel is just beggin' for it. ;)

In this case, a bot can display a little diagnostic information that might give the humans a slightly larger edge.

 

7.1 Bot Information

The client transmits a broadcast packet to determine what bots are on the span. This is responded to with a directed broadcast response from a bot giving current its current parameters. It also contains the initialization port for initial connection. It is the same format as the QUAKE broadcast packet but substitute "QUAKEBOT" for the "QUAKE" string). Query port is 27000 by default.

struct BotQuery {
        struct Header                  		// { 0x80, 0x00, 0x00, 0x0F };
        char OP_CODE                           	// 
        char Data [ ] = "QUAKEBOT"
        byte net_protocol_version NET_PROTOCOL_VERSION; // 0x03
}
struct ClientResponse {
        struct Header;                         	//{ 0x80, 0x00, 0x00, ....}
        char OP_Code;                  		// 
        char Name[];                           	// Bot Name
        byte VersionID				// 
        char VersionString[];                   // Client Specific Version String
        short Port;                      	// This is your IBC port
};

 

7.2 Bot Connection Request

The client transmits a directed packet to the bot with a connect request. The bot responds with a new port to connect to for all future communications. It is similar to a connect packet but has a password, Could be null for low security set up. Query port is 27000 by default. The bot returns a runtime port. Once authenicated, the bot will use the runtime port for all comm. otherwise a reject message.

struct BotConnect {
        struct Header ;                        //{ 0x80, 0x00, 0x00, 0x0c}
        char OP_Code                           // 0x01
        char Data [ ] = "QUAKEBOT"
        char UserID [ ] = "USERID"
        char Password [ ] = "Password"
};
struct BotConnect Response {
        struct Header;                          // { 0x80, 0x00, 0x00, 0x09 }
        char OP_Code;                    	// 0x81 This is CCREP_ACCEPT 
        short Port;                      	// This is your port assigned
        	                      		// by the server
};

Below would be a reject response:

struct BotConnect Response {
        struct Header;
        char OP_Code; 				// 0x82 This is CCREP_REJECT 
        char Data[]; 				// A string telling you why;
};

 

7.3 Bot Runtime Commands

These would be unreliable packets that would combine a series of commands to be sent to a Client Bot. The format would be a series of appended commands similar to the DEM commands structure (minus the code compression). Some commands would server two purposes, such as Set/Report. These functions would be interpreted differently depending on the mode the bot is in. For example, a bot running as a master would interpret a Set/Report Location command as a report while a slave would see it as a set command.

 

struct BotRuntime {
        struct Header ;                        //{ 0x00, 0x10, 0x00, 0x0c}
        char Command;
        char Data [ ];
        char Command;
        char Data [ ];
	...
};

 

7.3.1 0x00 Illegal Instruction

This would be an illegal instruction to catch invalid data

 

7.3.2 0x01 - No Operation

This command would be used by itself in packet to perfrom a keep alive function.

 

7.3.3 0x02 - Disconnect

This would instruct another client that the bot has been disconnected.

 

7.3.4 0x03 - Update/Report Bot State

This updates a value stored in the Bot Client State

Byte Index
Long Value

7.3.5 0x04 - Get Bot State

This requests an update for a value stored in the Bot Client State

Byte Index

7.3.6 0x08 - Print

This instructs a bot to print a message

char[] String

7.3.7 0x0A - Set/Report Angle

This instructs a slave bot to face in a certain orientation or informs a master of the bot's orientation

Angle View[0];
Angle View[1];
Angle View[2];

7.3.8 0x0B - Get Angle

This instructs a bot to send its orientation

 

7.3.9 0x0C - Set/Report Location

This instructs a slave bot to go to a certain location or informs a master of the bot's location

Coord Map[0];
Coord Map[1];
Coord Map[2];

7.3.10 0x0D - Get Location

This instructs a bot to send its location

 

7.3.11 0x0E - Report Ownership

This informs player that another player has a certain object

Byte PlayerID
Long ObjectID

7.3.11 0x1B - Kill/Killed Player

This informs a master that a bot has killed an opponent or instructs slave to attack a certain player. The latter is particularly useful in a two on two coordinated attack.

byte Player

7.3.11 0x1C - Killed By Player

This informs a master that a bot has been killed by an opponent.

byte Player

8.0 Additional Functions

The following courtesy functions should be implemented to allow server operators to ban bots. Also, individual players can determine whether they wish to compete with the bot.

9.0 Experimentation

So, ya wanna play? Well, you can, sorta. You have enough information here to establish a connection and move around. Just copy the client side packets and acknowledge the server packets. You don't have to worry about doing anything with the server info, just ignore it. The server does all movement calculations and collisions. Once you have completed initialization, send the client packet with a continuously increasing sequence and you should see the results if another player is standing by. I have released a version of C++ source that will perform this task for you.

8.1 Stupid Client Tricks - Quake Client as a Monitor

One will notice that in cases of extreme network lag, the client will freeze. It requires a continuous flow of data and updates its display based on it. It is this point that brings up a interesting characteristic that can be used to the botsmith's advantage. The earlier problems of DOOM's synchronization have been eliminated by the client/server protocol in that the client is operating as a terminal, albeit a pretty damn bright one. Can this terminal be used for purposes other than a strict Quake player.

The answer: YES. What will this allow me to do you ask? Let says for example that you have a client/server bot running around the map, but you are getting tired of chasing it down using another player just to see what it is doing. Well, chase no more. How about designing a server proxy? It runs on a separate system and looks like server to all quake clients. Modify it to take commands from one client and display the results to another. It's very simple, all you need to do is change the IP address and forward it to the server and vice versa for the server packets. You also need to add set view angle commands to reset the spatial orientation of the client. All communications to the server from the proxy appear as one quake session. It can take client commands from the bot client and display the results in the quake client window. Now your bot has eyes. No need to reinvent the wheel. Not bad, eh?

If you have some problem getting anything to work, drop me a line and I will check my typing. Good hunting!


Return to The QuakeBot Page