Net Worm Race consists of two programs: the client and the server. The user is supposed to run the client. Then, after choosing the "Create a new game" menu item, the client does the following:
This is done by the gamemaster only. Other players connect to the server as usual. After connecting, the client registers the server file descriptor by gdk_input_add and waits for receiving data from server (the data format will be specified later). When acceptance is received, the user is told that the game was succesfully created. If any error occurs, the connection is closed. If the user wants to interrupt waiting for the server or close the opened connection to the server, he can do so by choosing "Leave game". When the gamemaster leaves the game, the server exits.
When the server starts, it checks the connection to the gamemaster (file descriptor 3) by trying to send him acceptance data. If any error occurs, it exits immediately. Anytime the server loses connection to the gamemaster, it exits as well. If writing to the gamemaster was succesfull, the server creates a socket listening on the port 5000 and waits for new players.
Both the client and the server store some information about players in the game. They have an array (of maximum size MAX_PLAYERS) and every player has itsunique index which server gave him. Gamemaster is always 0, the other players are always given the lowest available number. When identifying players, these indices are used.
All data (except for initial acceptances) are sent via input/output buffers. Two structures for data processing are defined in the following way:
/* The struct that is used in client and server to process data. */
struct data{
enum data_code code;
int byte_cnt;
int author;
int index2;
unsigned char * data;
};
/* A structure that is used by the buffer to send or receive data. */
struct part_proc_data{
int transf_bytes_cnt;
int total_size;
unsigned char * byte_array;
struct part_proc_data * p_next;
};
The second one is used by the buffer to receive/send the data, the first one is the internal program structure used for processing the data. The buffer does the converting, FIFO managing, EOF indicating etc. To store information about players, game settings and other things, several more structs are defined in globals.h.
Data received (by server or client) are processed in a large switch. They are supposed to have this header:
The rest are size bytes of the data.
The server always marks the data with sender index. When receiving bad data (data with an unknown code, too large data, data with mandatory index2 and invalid value of index2 or so), the data is read completely, but processed in a special way. When the server finds out that someone is sending what he should not, it closes his connection immediately. The client is more dependant on data received from server, so he only forgots the invalid data and waits for some other.
Two main cases are generally recognized: "running a game" and "not running the game" (stored in the variable game_is_running). Some data codes are ignored in one of the cases. For example, player settings cannot be modified when running a game with the player playing; the game settings cannot be modified during the game either. Game actions (DC_LEFT, DC_RIGHT, DC_GAS, ...) have no meaning when the game is not running. To find out more about data processing, see the main switches in process_data() in netserver.c and netclient.c.
To be added to the game, the player must connect to the server and send a request to it. The request has a header as usual and its contents is a structure containing the player settings (color, nickname etc.). If the server detects connection attempt, he accepts the client and waits for the request. If no request is sent after a certain time limit, connection to the client is closed.
The gamemaster choose whether or not to authorize new players. If not, the server accepts the player immediately after receiving the request. Otherwise network information (client IP address and port number) is appended to the data and the data is forwarded to the gamemaster. He chooses to accept or refuse the client and the server does so when receiving gamemaster reply.
After adding a new player, the server saves his settings (extracted from the request) and sends it to all other players as DC_JOIN data. Analogically, when a player leaves the game, the server lets everyone know that he has done so.
Every player has its status. It can be one of:
Most statuses are used by the server only. The client does not use PLS_NEW, PLS_WAITING and PLS_DYING. The player status is shown for each player in the Game settings window. By default, the player is not ready. He becomes ready by clicking the "I am ready" button in the Game settings window. When a game starts, only players who are ready will be included. After end of a game, they are set to not ready again. The player can override this by choosing the "I am always ready" option in the My settings window.
After the user clicks on the "Start the game" button (which can be done by the gamemaster only), the client sends data with code DC_GAME to the server. The server counts the number of ready players, makes them playing and generates random positions for them. The positions can be modified by the StartMiddlePercent settings which bound the inner area where a worm's starting position can be. The speed is set to minimum for every player. Then the coords of ready players are filled in a byte array, four bytes per ready player: two for x coordinate and two for y. If there is one player only, the game is in the "training mode"; otherwise, it is in the normal mode (important for distinguishing end of game). The array is sent to all (not only ready) players with code DC_GAME (the same array is used later for game steps as well). After that, the server initializes the "tick timer". The timer expresses the time (in microseconds) to wait before next action. In the beginning, it is set to wait for several seconds (according to GAME_START_DELAY_SEC). For game steps it is always increased by game_settings.movement_timer_usec.
The server uses one select for both network and time events. When woken up, it first checks if the select ended because of timeout and if another step should be performed. If not, proceeds with other things. If so, actualizes the positions and sends the current position array to players. It also checks possible worm deaths, if there is one player left (for normal mode) or none (training mode), ends the game by sending DC_ENDGAME to the players along with the index of the winner (if there is any). There are two reasons for which there could be no winner - training mode or concurrent death of all playing players.
The servers multiplies speeds by GAME_POS_SPEED_MULT and coordinates by GAME_POS_COORD_MULT to avoid rounding errors. Values are always recalculated before sending to clients.
The server has an internal game bitmap which is created after each game start. It is padded with "gutters" - borders of the bitmap filled with ones so that all crashes can be detected by looking into the bitmap.
After all worms are moved in a step, the bitmap is filled with their positions. A circle with the same radius as WormWidth and with the appropriate coords is filled with ones. If the speed is high, several circles are made to keep the worm continuous. Before the filling server checks who has crashed during the step. It checks it using the btmap and looks at the position where the worm "is looking", i.e. the current center of the circle plus the radius oriented according to the current direction. In the following bitmap dumps, the '@' sign means the position described, '+' is the center of the worm, 'X' is a 1 and '.' is a 0 - an empty place in the bitmap. The last circle is not filled.
A worm crashing into another one: .......................................xxxxxxxxxxxxxxxxxxxxxxxx......................................................... .........................................xxxxxxxxxxxxxxxxxxxxxxxx....................................................... ..........................................xxxxxxxxxxxxxxxxxxxxxxxxx..................................................... ............................................xxxxxxxxxxxxxxxxxxxxxxxxx................................................... ..............................................xxxxxxxxxxxxxxxxxxxxxxxx.................................................. ................................................xxxxxxxxxxxxxxxxxxxxxxxx................................................ .................................................xxxxxxxxxxxxxxxxxxxxxxxxx.............................................. ...................................................xxxxxxxxxxxxxxxxxxxxxxxx............................................. .....................................................xxxxxxxxxxxxxxxxxxxxxxxx........................................... .......................................................xxxxxxxxxxxxxxxxxxxxxxxx......................................... ........................................................xxxxxxxxxxxxxxxxxxxxxxxxx....................................... ..........................................................xxxxxxxxxxxxxxxxxxxxxxxx...................................... ............................................................xxxxxxxxxxxxxxxxxxxxxxxx.................................... ..........................................................x...xxxxxxxxxxxxxxxxxxxxxxxx.................................. .......................................................xxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxx................................ .....................................................xxxxxxxxxx..@xxxxxxxxxxxxxxxxxxxxxxx............................... ....................................................xxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxx.............................. ..................................................xxxxxxxxxxxxxx....xxxxxxxxxxxxxxxxxxxxxxx............................. ................................................xxxxxxxxxxxx+xxx......xxxxxxxxxxxxxxxxxxxxxx............................ ..............................................xxxxxxxxxxxxxxxxxxx.......xxxxxxxxxxxxxxxxxxxxx........................... .............................................xxxxxxxxxxxxxxxxxxx..........xxxxxxxxxxxxxxxxxxxx.......................... ...........................................xxxxxxxxxxxxxxxxxxxxx...........xxxxxxxxxxxxxxxxxxx.......................... .........................................xxxxxxxxxxxxxxxxxxxxxxx.............xxxxxxxxxxxxxxxxxx......................... ........................................xxxxxxxxxxxxxxxxxxxxxxx................xxxxxxxxxxxxxxxx......................... ......................................xxxxxxxxxxxxxxxxxxxxxxxx...................xxxxxxxxxxxxxxx........................ ....................................xxxxxxxxxxxxxxxxxxxxxxxx......................xxxxxxxxxxxxxx........................ ..................................xxxxxxxxxxxxxxxxxxxxxxxxx........................xxxxxxxxxxxxx........................ .................................xxxxxxxxxxxxxxxxxxxxxxxx...........................xxxxxxxxxxxxx....................... ...............................xxxxxxxxxxxxxxxxxxxxxxxx.............................xxxxxxxxxxxx........................ .............................xxxxxxxxxxxxxxxxxxxxxxxx...............................xxxxxxxxxxxxx....................... ...........................xxxxxxxxxxxxxxxxxxxxxxxxx.................................xxxxxxxxxxx........................ ..........................xxxxxxxxxxxxxxxxxxxxxxxx..................................xxxxxxxxxxxxx....................... ........................xxxxxxxxxxxxxxxxxxxxxxxx....................................xxxxxxxxxxxx........................ ......................xxxxxxxxxxxxxxxxxxxxxxxxx....................................xxxxxxxxxxxxxx....................... ....................xxxxxxxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxx........................ ...................xxxxxxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxx........................ .................xxxxxxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxx........................ ................xxxxxxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxx......................... ...............xxxxxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxxxx......................... ...............xxxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxx.......................... ..............xxxxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxxx........................... .............xxxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxxxxxxx........................... ............xxxxxxxxxxxxxxxxxxx.....................................xxxxxxxxxxxxxxxxxxxxxxxx............................ ...........xxxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxxxxxx............................. ..........xxxxxxxxxxxxxxxxx......................................xxxxxxxxxxxxxxxxxxxxxxxx............................... A worm crashing into the gutter: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxx@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxx.............x......................................................... xxxxxxxxxxx..........x+xxxxx...................................................... xxxxxxxxxxx.........xxxxxxxxx..................................................... xxxxxxxxxxx........xxxxxxxxxxx.................................................... xxxxxxxxxxx........xxxxxxxxxxx.................................................... xxxxxxxxxxx........xxxxxxxxxxxx................................................... xxxxxxxxxxx.......xxxxxxxxxxxxxx.................................................. xxxxxxxxxxx........xxxxxxxxxxxxxx................................................. xxxxxxxxxxx........xxxxxxxxxxxxxx................................................. xxxxxxxxxxx........xxxxxxxxxxxxxx................................................. xxxxxxxxxxx.........xxxxxxxxxxxxxx................................................ xxxxxxxxxxx..........xxxxxxxxxxxxxx............................................... xxxxxxxxxxx...........xxxxxxxxxxxxx............................................... xxxxxxxxxxx...........xxxxxxxxxxxxx............................................... xxxxxxxxxxx............xxxxxxxxxxxxx.............................................. xxxxxxxxxxx.............xxxxxxxxxxxxx............................................. xxxxxxxxxxx.............xxxxxxxxxxxxxx............................................ xxxxxxxxxxx.............xxxxxxxxxxxxxx............................................ xxxxxxxxxxx..............xxxxxxxxxxxxx............................................ xxxxxxxxxxx...............xxxxxxxxxxxxx........................................... xxxxxxxxxxx................xxxxxxxxxxxxx.......................................... xxxxxxxxxxx................xxxxxxxxxxxxx.......................................... xxxxxxxxxxx................xxxxxxxxxxxxxx......................................... xxxxxxxxxxx.................xxxxxxxxxxxxxx........................................ xxxxxxxxxxx..................xxxxxxxxxxxxxx....................................... xxxxxxxxxxx..................xxxxxxxxxxxxxx....................................... xxxxxxxxxxx..................xxxxxxxxxxxxxx....................................... xxxxxxxxxxx...................xxxxxxxxxxxxxx...................................... xxxxxxxxxxx....................xxxxxxxxxxxxx...................................... xxxxxxxxxxx.....................xxxxxxxxxxxxx..................................... xxxxxxxxxxx.....................xxxxxxxxxxxxx.....................................
There is a problem with checking the crashes which has not yet been solved completely: if the worm is too slow, it may crash into itself from time to time. That's why the minimum speed is 10 and I recommend you to set it even higher to prevent such accidents.
Game actions correspond to keys pressed by the players. The rotation and acceleration actions are processed similarly, the only difference is in what they affect (speed or angle). Each player has a counter for speed and direction actions. Its value and meaning depends on the settings - continuous or quantum. The original value is 0. In quantum mode every action increments or decrements the counter. In continuous the counter value is one of 0 (going forward/not accelerating), 1 (turning right/accelerating now), -1 (turning left/deccelerating now). The counter value is used when processing a step. In quantum mode the step always sets the values to zero after processing.
If a player has crashed, he cannot die immediately - the server must send his final position to all. That's why the state PLS_DYING is used - until the next step, the player is PLS_DYING and dies after that.
The client processed each game step by drawing the circles according to current player positions. It also stores last player positions and if the distance between the old and the new one is too large, more circles are drawn to fill the gap.
The client uses GTK+ 2.0. It has several windows:
For every window a structure is defined to encapsulate the window widget and several other widgets packed in the window. The structures are defined in interface.h and the creation of windows is in interface.c. Callbacks are declared in callbacks.h and defined in callbacks.c. For every window (except for Request window), only one instance can exist. So the pointers to widgets in the instance are stored in the above mentioned structure. Most windows are created during the program initialization and exist until the end; they are always recreated when having been destroyed.
The game area contents are stored in a pixmap. When redrawing of the area is needed, the requested part is just copied from the pixmap. After each change of the area size the old pixmap is freed (using g_object_unref) and a new one is created. Apart from the pixmap, the gc pointer is stored, too, so we do not have to create it in every game step.
The worm position is drawn as a circle with the radius equal to that in game settings. If the worm speed is too high, more circles are drawn to fill the gap between the two positions.