The game has two phases. In the first phase connections are established and game settings are negotiated. In the second phase the game is being played.
The program is divided into several modules each implementing one phase of the game on the client or server side. The main module is common for both server and client and it process command line options. The description of modules follows.
Contains command line options processing and some common auxiliary functions used in other parts of program. Starts server main function or client main function depending on command line options ("-s" for a server or "-c" for a client).
Contains server_main function which starts UDP server thread and TCP server thread and finally it waits for signals. All threads has all signals blocked and signals are managed only by the main thread.
UDP thread waits for requests and sends an information about the game managed by TCP server to any questioner.
TCP thread accepts incoming connections. For each connection an init-game thread is created.
Init-game thread negotiates game settings with a client. These settings currently consist only of the name of the player which must be unique in a scope of the game. Init-game reacts on two types of requests: REQ_LIST_PLAYERS and REQ_JOIN_GAME. The response to the former one is the list of currently joined players. The latter one comes along with the player's name and the response is wheather or not this name is unique and so whather the player has been joined to the game. If a negotiation was terminated (socket had been closed) then the init-game thread would return the PLAYER_NOT_JOINED_GAME vlaue. Otherwise it would return the PLAYER_JOINED_GAME value.
Init-game thread is accessing and changing the game global data structure which contains information about the game such as the total player's count, the joined player count, players' names etc. Because there may be several init-game threads accessing this structure it is protected by a mutex.
After all players has started their negotiations the TCP thread waits while all init-game threads terminates (it calls pthread_join on them). If some players quit during their negotiations (their respective init-game thread returns the PLAYER_JOINED_GAME value) then the TCP thread accepts incoming connections again. Other clients have to connect.
After all players has joined the game the list of players is sent to each player along with its identification number. This id is the index in each array containing data of all players. The server_play_game function implemented in server_game.c is started then.
Contains client_main function which finds all available games by using broadcast and lets the user to choose one of them. After the game is selected connects to the respective TCP server, negotiates game settings and joins the game. During the negotiation a list of joined players is downloaded from the server and the user is expected to enter its name. The name is sent to the server. If it is accepted then the function waits for all players list and an identification number of the local player. Then the game thread is started. The main routine of the game thread (client_game_thread) is implemented in the client_game.c file.
The main thread is then waiting for incoming signals and manages them. Game thread has all signals blocked.
Contains headers of common functions used in other parts of the program and also several structures typedefs which forms the communication protocol used by routines in server.c and client.c:
If the request was REQ_LIST_PLAYERS it contains the number of names and the total lenght of data which will be sent immediately after this structure. Names are sent as a sequence if strings separated by '\0' character (e.g. 'xyz\0uvw\0'). The ending '\0' is also sent.
If the request was REQ_JOIN_GAME an error value is sent. If the name sent in the request is not unique E_PLAYER_NAME_USED value is sent. Otherwise, E_NONE is sent.
This structure is also sent when all players have joined the game and the TCP server starts the game. In such case, the player list is sent in TcpResponse structure followed by the player names followed by one byte long player identification number.
Contains the server_play_game function which creates a map, places snakes and a food to it and sends it to all players. Then retrieves codes of keys pressed by players, updates positions of snakes and their states and sends all these changes to each player. These actions forms one game turn and are repeated until the game is over. Actions of clients are described later. State of the game is stored in the game global structure.
Each snake's state is one of the following (defined in game.h):
Each snake has its default direction in which it moves. In each turn a client is expected to send a key code. If no key was pressed by player client sends zero key code. In such case, the direction of the snake remains unchanged. If the server receives arrow key code the direction changes accordingly.
Each turn consists of several actions on the server-side. These are:
The main client's game logic is implemented by the play function. The game thread main routine (client_game_thread) starts the key-reader thread at first. All the key-reader is doing is waiting for the key pressed by user. Then the play function is called. It performs these actions until the game is over:
The game state is maintained in the game structure (which differs from the server's one). It contains the server address and port, the name of the game, names of all players, id of local player and current states and scores of players. This structure is updated once when the game is starting and only a state and a score of each player changes every turn. The map is loaded from the server into the world global structure when the game is starting. The map is updated by information received from the server in each turn.
Contains the World structure typedef defining the map description. Constants and macros related to the content of the map and its changing are also declared here.
Moreover, contains the typedefs of structures forming the game protocol.