Doom networking component
From its first release, Doom supported networked multiplayer gameplay in addition to its single-player gameplay. It was one of the first popular games to support networked play.
Players can see other players in a multiplayer game. Each player has a different suit color to identify them (green, indigo, brown, and red). A different drawing function (
R_DrawTranslatedColumn) is used to draw player sprites, offsetting the green colors in the player sprite to different palette values in order to change the suit color. The background of the face in the hud will also change color to reflect the player color. Single player Doom uses the color for player two as the default color.
The status bar code includes code to allow talking to other players. Pressing 'T' brings up a cursor where the player can type their message. Pressing the first letter of a player's color name ('G', 'I', 'B' or 'R') allows a message to be sent to an individual player. The text is sent serially: each game tic command has a field for a single character used for multiplayer chat. Typed characters are placed into a queue. When tic commands are generated, characters are read from the queue and stored inside the tic command. The messages are reassembled by the other players.
Early versions of Doom (v1.1) included support for IPX networks inside the Doom executable. This was later replaced with a modular system wherein external executable "driver" files provide network functionality to the main Doom executable.
The later versions of Doom, Heretic and Hexen include two executables, IPXSETUP.EXE and SERSETUP.EXE, which provide IPX network and serial connection functionality respectively. The executables are passed the appropriate parameters to set up the connection. IPXSETUP uses broadcast packets to locate other computers setting up a game and SERSETUP is used for serial connection games via a computers COM port. SERSETUP supports both a null-modem cable and modems. SERSETUP can handle modem dialing or listen for incoming calls. Once a connection has been established, the driver will invoke the main Doom, Heretic or Hexen executable.
In starting game, the driver passes a
-net command line parameter. This is used to specify the memory address of a structure inside the driver's memory. This is possible because of DOS's lack of memory protection; it is impossible to do this kind of "hack" inside a modern operating system. The passed structure contains various parameters to be used in the game: the number of players, the start level, gameplay mode (cooperative or deathmatch), as well as various fields used in communication between the driver and the main game executable.
The driver sets up an interrupt handler. Doom, Heretic and Hexen can then send and receive packets by setting a field inside the structure to specify the command type (send or receive packet) and invoking the interrupt. The interrupt is caught by the driver which performs the desired operation. The communication structure includes a buffer in which received data and data to be transmitted is placed.
Because of the modular nature of the external driver system, it is possible to write new drivers to support other kinds of networks. id Software and Raven Software released the source code to SERSETUP and IPXSETUP in 1994. Following this, several new drivers were developed, some improved versions of the original utilities (such as Doomatic), others supporting entirely different network systems. The released source code was initially under a restrictive license, but permission was obtained in 2005 for the code to be relicensed under the GNU GPL.
The first version of the Doom IPX network code transmitted its data as broadcast data. As a result of this, all machines on a network where a game of Doom was being played would receive the data, even if the machine was not involved in the game. The degrading effect on network performance forced the system administrators for many office networks to ban Doom.
The Doom, Heretic and Hexen networking component is a peer-to-peer system. Each player in the game is an independent "peer" running its own copy of the game. Periodically (every 1/35th of a second), the input from each player (in the form of keyboard, mouse, joystick, etc.) is sampled and placed into a tic command. This is a simple structure which stores how the player wishes to move: there are fields for forward/backward movement, sideways movement (strafing), turning, and actions such as "use" and "fire".
After being generated, the tic commands are placed into a queue. They are then transmitted to all other players in the game. The networking component periodically checks for newly received packets. Tic commands from other players are stored in a buffer. When the tic commands for all players have been received, the game advances.
Generation of tic commands is independent of the game advancing; for example, a player may generate commands to be used several tics into the future even though the game has not yet advanced to that point. Because of this, in multiplayer games the game itself will not slow down, but there may be a short delay between the pressing of a key and the desired action occurring. The delay depends on the latency between players; hence, the playability of the game is dependent on the player with the slowest connection speed.
The protocol used in Doom, Heretic and Hexen is based around negative acknowledgements. Each packet includes a number of tic commands and the number of the first command. If the packet is not the next expected packet, it is assumed that a packet has been lost and a resend request is transmitted to the sender.
The "official" (as stated in the IPXSETUP source code) IPX network socket for Doom is 869C hex (34460 decimal). This was apparently registered with Novell, as it appears in the list of well-known IPX sockets published by Novell. However, due to an integer overflow, the actual socket number that is used is 869B hex (34461 decimal).
The code structure is divided into system-specific and system-independent sections. The file i_net.c contains a system-specific interface for sending packets. The component can run over any system which allows packets to be sent and received. Under DOS, i_net.c uses the external driver system to communicate with IPXSETUP and SERSETUP. In the Unix ports of Doom, UDP/IP is used.
The higher level d_net.c file contains the system-independent portion of the networking code. The code has separate concepts of node and player: a node is simply a computer connected to the game. Each player has a corresponding node, but a node does not necessarily have a player. This was used to implement the three screen mode that existed in early versions of Doom: the "left" and "right" screens were nodes but not players.
Particularly important in the d_net.c file is the
NetUpdate function. This checks to see if the component should generate a new tic command. To ensure playability, this function is called frequently, even from inside the rendering code. The
TryRunTics function checks if enough tic commands have been received from all players and if so, advances the game.
The following variables are important:
- Locally generated tic commands. These are stored in a circular buffer.
- This is a circular buffer into which received tic commands are placed. A maximum of BACKUPTICS (usually 12) tic commands can be queued.
- The last tic command received from each player.
- This is true if a particular node is connected to the game. It is set to false when a node leaves the game.
- This is true if a particular player is in the game. It is set to false when a player leaves the game.
- The next game tic to be run.
- The next game tic to generate a command for. Ideally this should be as close to gametic as possible.
- Each player's corresponding node.
- (example of a minimalist implementation of a DOS Doom networking driver, with source code)
- id Software. "IPXSETUP.C, from the IPXSETUP source code."