Gnu Gaming Zone Design Specification Brent Hendricks bmh@users.sourceforge.net Copyright © 1999, 2000 by Brent Hendricks Design specification for the Gnu Gaming Zone. This document covers the internal server architecture and the client/server communication protocols. _________________________________________________________________ Table of Contents Introduction 1. Design Requirements 2. Client-Server Communications Interactions Server Messages Player Management Information request Table Management Chat Facility Game Interactions 3. Design Overview GGZ-control Interactions between control and running games Interactions between control and config utility Individual Game Servers Config Utility Key Subsystems Options Parser Login/User Database MOTD System Player Statistics Module loading Data Structures _________________________________________________________________ Introduction At some point during the devlopment of NetSpades, I realized that a major rewrite was in order. I wasn't happy with the client/server interactions, and I wanted to add some features which just wouldn't work within the current infrstructure. So I began to formulate grandiose plans for NetSpades v5, but continued to work on the 4.X series. A little later, I began to contemplate adding the ability to play Hearts as well as Spades. But why stop there? Why not develop a general framework for playing networked games? And so my plans have developed into what I called NetGames. Just before I was going to announce my new project on SourceForge, I happened across Rich's announcement and plans for an online gaming system. It was to be in the same vein as Microsoft's Internet Gaming Zone, so he named it the Gnu Gaming Zone(GGZ). His goals and plans were so similar to mine that I suggested we merge projects and collaborate. He readily agreed, and the GGZ was born. _________________________________________________________________ Chapter 1. Design Requirements There are many planned features for GGZ. Some will take longer than others to implement. Some may be discarded at a later date. Planned features include (but are not limited to): * Password-based user accounts * Persistent user statistics * Ability to leave and then rejoin game * Ability to reserve games for specific users * Dynamic run-time configuration of server * Ability to add/remove game types from server without having to recompile * Capability for users to chat without resorting to second port/socket * Basic infrastructure from game developers to write network games without having to worry about connections/login/user accounts/etc. * Multiple "rooms" in which to play games. These rooms may reside on different servers, transparent to the user. _________________________________________________________________ Chapter 2. Client-Server Communications The client and GGZ server will communicate via the protocol described here. Individual games will of course communicate via their own protocol. These messages must be embedded within the GGZ message type REQ_GAME or RSP_GAME. It is recommended that game developers use a message passing scheme similar to the one described here for consistency. _________________________________________________________________ Interactions Three types of data are exchanged between the client and the server: * chr: a 1-byte signed char * int: a 4-byte signed integer in network byte order * str: a multibyte null-terminated string preceeded by its length (including null-termination) as an integer. Interactions take one of three forms: server messages, client requests, and server responses. Each interaction is prefaced by a header indentifying it (some interactions consist solely of the header). The header (stored as an enumerated value) is sent as an int. _________________________________________________________________ Server Messages These messages are received from the server withouy being directly requested by the client. They are used by the server to pass necessary information to the client. MSG_SERVER_ID str: server identification (plus relevant server options) MSG_SERVER_FULL MSG_MOTD str: message of the day (useful or otherwise information from serve r) MSG_CHAT str: name of user who sent chat str: chat string MSG_UPDATE_PLAYERS MSG_UPDATE_TYPES MSG_UPDATE_TABLES _________________________________________________________________ Player Management These are client requests and the corresponding server responses related to managing player sessions and data. REQ_LOGIN_NEW str: login name RSP_LOGIN_NEW chr: success flag (0 for success, -1 invalid name, -2 duplicate nam e) str: initial password (if success) int: game type checksum (if success) REQ_LOGIN str: login name str: password RSP_LOGIN chr: success flag (0 for success, -1 invalid name, -2 duplicate nam e) int: game type checksum (if success) chr: reservation flag REQ_LOGIN_ANON str: name RSP_LOGIN_ANON chr: success flag (0 for success, -1 invalid name, -2 duplicate nam e) int: game type checksum (if success) REQ_LOGOUT RSP_LOGOUT chr: success flag (0 for success, -1 error) REQ_PREF_CHANGE chr: preference flag (indicates pref to change) (str): new data value RSP_PREF_CHANGE chr: success flag (0 for success, -1 error) REQ_REMOVE_USER str: login name str: password RSP_REMOVE_USER chr: success flag (0 for success, -1 error) ______________________________________________________________ ___ Information request These are client requests for information and the corresponding server responses. REQ_LIST_PLAYERS RSP_LIST_PLAYERS int: number of users (-1 for error) sequence of str: user name int: game table index (-1 if user not at a table) REQ_LIST_TYPES chr: verbose flag (0 for short list, 1 for long descriptions) RSP_LIST_TYPES int: number of game types (-1 for error) sequence of int: game type index str: game name str: version str: description (only if long) str: author (only if long) str: homepage (only if long) REQ_LIST_TABLES int: game type index (-1 for all type, -2 for reserved, -3 for open games) RSP_LIST_TABLES int: number of games (-1 for error) sequence of int: table index int: game type index chr: play/wait flag (0 if waiting for players, 1 if playing) int: number of seats at table sequence of int: seat assignment (-1 for OPEN, -2 for COMP, -3 RESV ) str: name of player (if assignment >=0 or == RESV) REQ_TABLE_OPTIONS int: game index RSP_TABLE_OPTIONS int: size of options struct (-1 for error) struct: game options REQ_USER_STAT str: login name (NULL for all users) int: game type index (-1 for all types) RSP_USER_STAT int: number of game types (-1 for error) sequence of int: game type index int: number of users sequence of int: number of games played int: number of games won _________________________________________________________________ Table Management These are client requests related to game tables and the corresponding server responses. REQ_TABLE_LAUNCH int: game type index int: number of seats sequence of int: seat assignment (-1 for OPEN, -2 for COMP, -3 RESV ) str: name for reservation (if status = RESV) int: size of options struct struct: game options RSP_TABLE_LAUNCH chr: success flag (0 for success, negative values for various failu res) REQ_TABLE_JOIN int: game index RSP_TABLE_JOIN chr: success flag (0 for success, -1 for failure) REQ_TABLE_LEAVE RSP_TABLE_LEAVE chr: success flag (0 for success, -1 for failure) _________________________________________________________________ Chat Facility Similar to the "Taunt" feature provided with NetSpades, the GGZ server will provide the ability to send messages to other players via the server. The following interaction describes how the client sends a chat message to the server. Chats are sent to the client with the MSG_CHAT opcode described above. REQ_CHAT str: chat string RSP_CHAT chr: success flag (0 for success, -1 error) ______________________________________________________________ ___ Game Interactions In order to allow the GGZ server to identify chat and other control messages during the course of a game, it is necessary that interactions with a game being played have an identifiable header. This header will be stripped off before the rest of the data is sent to the game itself. Similarly, any messages from the game to the client will have a header prepended. Messages from the client take the following form: REQ_GAME int: size of data in bytes (data) Similarly, messages from the server appear as: RSP_GAME int: size of data in bytes (data) _________________________________________________________________ Chapter 3. Design Overview There are three parts which comprise the server side: * Main GGZ server (called GGZ-control) * Individual game servers * Run-time config utility program We will discuss each of these in turn, but first we'll look at the overall architecture. Figure 3-1. Server Architecture [server_arch.gif] GGZ-control will handle incoming connections, manage the user database, and keep track of all of the games being played (referred to as game tables). Clients are always in direct communication with control. Control will not handle the specifics of how to play any particular game. That logic is contained in the individual game servers. It is expected (and hoped!) that game developers will write their own games servers for use with GGZ. GGZ will attempt to provide a simple franework for writing network games in which developer need not worry about connections or user logins or maintaining statistics. All of that will be done by GGZ. Game devlopers should only have to concern themselves with gameplay. The third item is more loosely connected. We will provide some sort of run-time configuration utility for GGZ, so the main server will not have to be restarted (or worse.. recompiled!) in order for various options to be changed. Some options may include: * Location of game servers * Set auto removal of inactive users + Set inactivity threshold * Set auto clearing of statistics + Set clearing interval * Log level (level of detail in server logs) _________________________________________________________________ GGZ-control This is the main brain for the server side of GGZ. It handles client logins and new user registrations. It manages option negotiation with the clients, and launches new game sessions. It maintains a list of running game sessions and keeps a database of win/lose statistics for each user. Control coordinates games, users, and databases, and is responsible for interacting with the client, the running games, and the config utility. I considered several possble designs before settling on the current one. It is possible that as GGZ develops this design will change as well. Since control must communicate with multiple parties (game tables, users, etc.) I decided to use a multi-threaded concurrent server where each connection (be it user or game table) gets its own thread. This avoids the situation where control is servicing a request and therefore cannot handle any incoming connections or other requests. I chose threads rather than forking child processes because threads have a smaller overhead and it is easier to share memory between threads than between processes. Every time a new user connects, control creates a new thread to handle all requests for that user. This thread is known as the player handler. If the user decides to launch a new game table, the player handler creates a thread to handle all requests from the game table. This new thread is known as the table handler. The table handler waits until enough players have joined the table and then forks a process, known as the game table process, in which to run the game server (The reason game servers are not run within a thread is so that game developers not be required to worry about writing thread-dafe code). During the course of the game, the player thread for each player acts as a liason between the player and the game table, passing requests back and forth transparent to the player and the game server. When a player logs out, the player handler thread is destroyed. _________________________________________________________________ Interactions between control and running games Sample game servers will be provided with GGZ, but it is hoped that others will write game modules, and either submit them for inclusion in the GGZ package, or maintain and distribute them separately. The following API describes how control will interact with the game processes. Four types of data are exchanged between GGZ-control and game servers: * chr: a 1-byte signed char * int: a 4-byte signed integer in network byte order * str: a multibyte null-terminated string preceeded by its length (including null-termination) as an integer. * fd: a file descriptor passed via sendmsg() along with a single byte of dummy data The following is a complete list of messages between the game module and the control section of the server. Again, I have chosen to intersperse game module requests with the corresponding control responses. Please note: the following is not written stone, merely a list of ideas. REQ_TABLE_LAUNCH int: size of options data in bytes (options data) int: number of seats at table sequence of int: seat assignment (-1 for OPEN, -2 for COMP, -3 RESV) str: name of player (if assignment >=0 or == RESV) fd: file dscriptor of player (if assignment >= 0) RSP_TABLE_LAUNCH chr: success flag (0 if OK, -1 if error) REQ_TABLE_JOIN int: seat number str: name of player fd: file dscriptor of player RSP_TABLE_JOIN chr: success flag (0 if OK, -1 if error) REQ_TABLE_LEAVE str: name of player RSP_TABLE_LEAVE chr: success flag (0 if OK, -1 if error) MSG_GAME_OVER int: number of statistics sequence of int: player index int: number of games won int: number of games lost _________________________________________________________________ Interactions between control and config utility Since the server runs non-interactively in the background, there needs to be a run-time configuration tool so that server options may be changed without restarting. This program will communicate with control via a Unix domain socket or a message queue, or some such thing. Messages between control and the config utility include: * Request available game types (loaded modules) * Add/Remove Game types * Remove users * Request list of active games * Clear Player statistics * Modify logging _________________________________________________________________ Individual Game Servers As described above, game servers run in their own processes, and are responsible for handling the gameplay of a particular game. There are three possibilities for game server design. * Compiled in. The game table process calls a statrtup function which begins execution of the game server. This scheme has the benefit that the server has access to control's data structures at the time the process was forked. Communication between the game table and control can be via pipes or a socketpair. The problem with this scheme is that to change which games are offered by a particular server requires a reocmpile. Not good. * Dynamically loaded modules. Similar to the above except that game server exist as loadable modules which may be inserted and removed at runtime. This allows for adding new game types without a recompile. Downside is that it requires both game developers and myself to know how to deal with loadable modules. * Exec() separate program. In this scheme, GGZ acts much like the inet daemon by handling the connections and then doing a fork()/exec() to launch the game server. This scheme also allows for adding new games at runtime and has the bonus effect that game servers can be written in other kanguages that C. The current design requires game servers of type three. It is possible however, that in the future GGZ will allow dynamically loaded server modules as well. Once the game server is running it is necessary that control pass it some required information such as player names and file descriptors. This communication occurs over a pipe or socketpair which is established prior to the forking of the process. The interactions between game modules and the control section are listed above in section 3.1.1.1. _________________________________________________________________ Config Utility Not written _________________________________________________________________ Key Subsystems While the architecture of the server is divided into the aforementioned three parts, there are a few "subsystems" which are necessary. _________________________________________________________________ Options Parser This is a two part system. One to parse the command-line arguments, and one to parse the configuration file. Options specified on the command line should have a higher precedence than those in the config file. Additionally, an alternate config file may be specified on the command line. At the present, I am using the popt library for cmd-line parsing. There have been no decision made about the config file format, or its parser. One possibility is to use libxml which is popular under GNOME, however simplicity may reign instead. _________________________________________________________________ Login/User Database The server will need to store a database of user ID, name, and password at the very least. This system must allow searching by name or ID, and allow for easy addition/deletion. GGZ ill probably use libdb for this. a SQL database is possible but probably not necessary, and just too much for our simple requirements. _________________________________________________________________ MOTD System This section documents the MOTD facility written by rich. _________________________________________________________________ Player Statistics Since the game modules are dynamic, it makes sense to store the statistics on a per-game type basis, rather than on a per-user basis. _________________________________________________________________ Module loading Not written _________________________________________________________________ Data Structures There will be an array of game_t structures on the server. This array will be initialized at startup, and may be changed when dynamic loading of new game modules occurs. The index into this array is referred to as the game type index. game_t { str: short string for name of game (16 chars?) str: long string for description (256 chars?) fnc: pointer to function for launching game chr: allowable player numbers (2^num) chr: allow computer players (1 for yes) int: sizeof options struct in bytes chr: enabled flag (1 if playing this game is enabled) } There will be an array of game_run_t structures represnting running games, This array will be dynamic since as games are started and finished, entries in the array are created and destroyed. The index into this array is referred to as the game index. game_run_t { int: game type index int: number of player slots *int: array of player codes for registered players chr: play/wait flag (0 if waiting for players, 1 if playing) int: process or thread ID *void: pointer to options struct for this game *int: array of player codes for reservations int: number of open player slots int: file descriptor for communication chr: computer players (2^num) } There will also be a large array of user_t structures, representing connected users. As soon as a user connects, an entry is created and the file descriptor filled in. When the user completes the login process, the user code and name are filled in. When the user launches, or joins a game, the game index is filled in. user_t { int: user code (unique user id number) str: user name int: file descriptor for communication int: game index } An array of reservation_t structures holds all of the reservations requested. These are created when a game is launched with reservation requests. They may be altered once the game has been launched. They are deleted when a user accepts a reservation or declines it. reservation_t { int: game index int: user code } There will also be a structure for server options. However, since the options themselves are undertermined, the data strucuture is not laid out here.