From 47ff725ff3b109b94a23ea7c8c07c8b1417e31ef Mon Sep 17 00:00:00 2001 From: Richard Worth Date: Sat, 3 Dec 2005 05:36:42 +0000 Subject: [PATCH] 2005-12-03 Richard D. Worth * PROTOCOL: Add underscores to error codes. Add STATISTICS command. Remove statistics from WHO. * TODO: Add STATISTICS. Check off STATISTICS. * src/ttt.h: Define TTT_SERVER_PROTOCOL_VERSION for VERSION. * src/ttt-error.c: (ttt_error_string): Add underscores to error codes. * src/ttt-client.h: * src/ttt-client.c: (_ttt_client_execute_helo), (_ttt_client_execute_statistics), (_ttt_client_execute_message), (_ttt_client_execute_version), (_ttt_client_execute_quit), (_ttt_client_init), (_ttt_client_fini), (ttt_client_get_username), (ttt_client_set_username): Rename client->name to client->username. Implement STATISTICS. Complete implementation of VERSION. * src/ttt-server.h: * src/ttt-server.c: (ttt_server_register_client), (ttt_server_unregister_client), (ttt_server_who), (ttt_server_statistics): Track rename of client->name to client->username. Add server helper for STATISTICS. --- ChangeLog | 27 ++++++++++++++++ PROTOCOL | 76 +++++++++++++++++++++++++++---------------- TODO | 11 ++++--- src/ttt-client.c | 84 ++++++++++++++++++++++++++++++++++++++---------- src/ttt-client.h | 4 +-- src/ttt-error.c | 16 ++++----- src/ttt-server.c | 50 ++++++++++++++++++++++------ src/ttt-server.h | 16 +++++++++ src/ttt.h | 2 ++ 9 files changed, 217 insertions(+), 69 deletions(-) diff --git a/ChangeLog b/ChangeLog index bb05052..3d2bc1d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2005-12-03 Richard D. Worth + + * PROTOCOL: Add underscores to error codes. Add STATISTICS + command. Remove statistics from WHO. + + * TODO: Add STATISTICS. Check off STATISTICS. + + * src/ttt.h: Define TTT_SERVER_PROTOCOL_VERSION for VERSION. + + * src/ttt-error.c: (ttt_error_string): Add underscores to error + codes. + + * src/ttt-client.h: + * src/ttt-client.c: (_ttt_client_execute_helo), + (_ttt_client_execute_statistics), (_ttt_client_execute_message), + (_ttt_client_execute_version), (_ttt_client_execute_quit), + (_ttt_client_init), (_ttt_client_fini), (ttt_client_get_username), + (ttt_client_set_username): Rename client->name to + client->username. Implement STATISTICS. Complete implementation of + VERSION. + + * src/ttt-server.h: + * src/ttt-server.c: (ttt_server_register_client), + (ttt_server_unregister_client), (ttt_server_who), + (ttt_server_statistics): Track rename of client->name to + client->username. Add server helper for STATISTICS. + 2005-12-02 Richard D. Worth * TODO: Check off Global commands, HELP, VERSION. diff --git a/PROTOCOL b/PROTOCOL index d855cdf..be0f04d 100644 --- a/PROTOCOL +++ b/PROTOCOL @@ -31,11 +31,11 @@ Document Conventions is one of: - ERROR + ERROR 1. Requests -1.1 Connection setup +1.1. Connection setup The TTTP server has no well defined port; agreement on which port to use must be done through some external mechanism. Once connected, @@ -47,23 +47,37 @@ Document Conventions HELO - Possible errors: INVALIDNAME + Possible errors: INVALID_NAME 1.2. Global commands - 1.2.1 Listing available users + 1.2.1. Listing available users WHO -> - WHO ... + WHO ... - Lists connected users and the number of games they've won. + Lists connected users. - Possible errors: NONAMESET + Possible errors: NO_NAME_SET - 1.2.2. Message + 1.2.2. STATISTICS + + STATISTICS + + -> + + STATISTICS " + TICTACTOE WINS + " + + Lists the statistics for the specified user. + + Possible errors: NO_NAME_SET, NO_USER + + 1.2.3. Message MESSAGE @@ -75,17 +89,23 @@ Document Conventions single token. Use a quoted-string to include spaces in the message text. - Possible errors: NONAMESET + Possible errors: NO_NAME_SET - 1.2.3. Help + 1.2.4. Help HELP { } + -> + + HELP { } " + + " + Displays help. If is provided, displays more detailed help on a specific command, otherwise displays an overview of all commands. - 1.2.4. Quit + 1.2.5. Quit QUIT @@ -95,7 +115,7 @@ Document Conventions Disconnects the client from the server. - 1.2.5. Version + 1.2.6. Version VERSION @@ -119,7 +139,7 @@ Document Conventions INVITE - Possible errors: NOUSER, BUSY, NONAMESET + Possible errors: NO_USER, BUSY, NO_NAME_SET 1.3.2. Accepting an invitation @@ -129,7 +149,7 @@ Document Conventions ACCEPT - Possible errors: NONAMESET + Possible errors: NO_NAME_SET 1.4. In-game commands @@ -158,7 +178,7 @@ Document Conventions _|X|_ X|O|O" - Possible errors: NOTINGAME, NONAMESET + Possible errors: NOT_IN_GAME, NO_NAME_SET 1.4.2. Part @@ -170,7 +190,7 @@ Document Conventions Departs the current game - Possible errors: NOTINGAME, NONAMESET + Possible errors: NOT_IN_GAME, NO_NAME_SET 1.4.3. Making a move @@ -187,7 +207,7 @@ Document Conventions 3|4|5 6|7|8 - Possible errors: NOTINGAME, NOTYOURMOVE, NOTGRID, NONAMESET + Possible errors: NOT_IN_GAME, NOT_YOUR_MOVE, NOT_GRID, NO_NAME_SET 2. Asynchronous notification. @@ -226,6 +246,8 @@ Document Conventions NOTICE MESSAGE + where is a quoted-string. Quotes escaped with '\"' + 2.2. Game notices These notices are sent to all players and watchers in @@ -243,7 +265,7 @@ Document Conventions NOTICE GAMEOVER - is either WON in which case indicates + is either WON in which case indicates the winner or CATSGAME in which case is "". 2.2.2. Move notices @@ -262,7 +284,7 @@ Document Conventions 3.1.1. No name set - ERROR NONAMESET + ERROR NO_NAME_SET 'helo' must be sent before any command other than 'help', 'version', 'quit'. @@ -271,7 +293,7 @@ Document Conventions 3.1.2. Invalid name - ERROR INVALIDNAME + ERROR INVALID_NAME All names must be of non-zero length and must be unique. @@ -295,13 +317,13 @@ Document Conventions 3.2.3. Not number - ERROR NOTNUMBER + ERROR NOT_NUMBER A non-numeric value was supplied where a number was required 3.2.4. Not a grid number - ERROR NOTGRID + ERROR NOT_GRID The number specified in the command was not a valid grid number @@ -315,7 +337,7 @@ Document Conventions 3.4.1. No such game - ERROR NOGAME + ERROR NO_GAME A game name was provided that does not exist. @@ -323,7 +345,7 @@ Document Conventions 3.5.1. No such user - ERROR NOUSER + ERROR NO_USER A user name was provided that does not exist. @@ -333,7 +355,7 @@ Document Conventions 3.6.1.1. Not in game - ERROR NOTINGAME + ERROR NOT_IN_GAME A game playing command was made, but the user is not a particpant of any game. @@ -342,7 +364,7 @@ Document Conventions 3.6.1.2. Not playing - ERROR NOTPLAYING + ERROR NOT_PLAYING A command was executed by a watching user that is permitted only to players @@ -351,7 +373,7 @@ Document Conventions 3.6.2.1. Not your turn - ERROR NOTYOURTURN + ERROR NOT_YOUR_TURN A move was submitted during the other player's turn diff --git a/TODO b/TODO index f0bf20a..f7fe04b 100644 --- a/TODO +++ b/TODO @@ -5,13 +5,14 @@ working on something. / /---- Client, implemented in ttt S C 1. Requests -✓ 1.1 HELO +✓ 1.1. HELO ✓ 1.2. Global commands ✓ 1.2.1. WHO -✓ 1.2.2. MESSAGE -✓ 1.2.3. HELP -✓ 1.2.4. QUIT -✓ 1.2.5. VERSION +✓ 1.2.2. STATISTICS +✓ 1.2.3. MESSAGE +✓ 1.2.4. HELP +✓ 1.2.5. QUIT +✓ 1.2.6. VERSION 1.3. Game management commands 1.3.1. INVITE 1.3.2. ACCEPT diff --git a/src/ttt-client.c b/src/ttt-client.c index 1dd755f..c92c4eb 100644 --- a/src/ttt-client.c +++ b/src/ttt-client.c @@ -40,7 +40,7 @@ struct _ttt_client { char **request_strings; int num_request_strings; - char *name; + char *username; ttt_bool_t registered; int num_wins; }; @@ -59,6 +59,11 @@ _ttt_client_execute_who (ttt_client_t *client, char **args, int num_args); +static ttt_error_t +_ttt_client_execute_statistics (ttt_client_t *client, + char **args, + int num_args); + static ttt_error_t _ttt_client_execute_message (ttt_client_t *client, char **args, @@ -98,6 +103,9 @@ ttt_command_description_t command_descriptions[] = { {"MESSAGE", 1, 1, _ttt_client_execute_message, "MESSAGE ", "Send a message to everyone."}, + {"STATISTICS", 1, 1, _ttt_client_execute_statistics, + "STATISTICS ", "Lists the statistics for the specified user."}, + {"QUIT", 0, 0, _ttt_client_execute_quit, "QUIT ", "Quit session."}, @@ -121,7 +129,7 @@ _ttt_client_execute_helo (ttt_client_t *client, assert (num_args == 1); - ttt_client_set_name (client, args[0]); + ttt_client_set_username (client, args[0]); error = ttt_server_register_client (client->server, client); if (error) @@ -129,13 +137,13 @@ _ttt_client_execute_helo (ttt_client_t *client, client->registered = TRUE; xasprintf (&response, "HELO %s %s %s\r\n", - client->name, + client->username, ttt_server_get_host (client->server), ttt_server_get_port (client->server)); ttt_client_send (client, response); xasprintf (¬ice, "NOTICE USER %s\r\n", - client->name); + client->username); ttt_server_broadcast (client->server, notice); free (notice); @@ -164,6 +172,33 @@ _ttt_client_execute_who (ttt_client_t *client, return TTT_ERROR_NONE; } +static ttt_error_t +_ttt_client_execute_statistics (ttt_client_t *client, + char **args, + int num_args) +{ + char *response; + char *username; + ttt_error_t error; + + assert (num_args == 1); + + username = args[0]; + + if (!client->registered) + return TTT_ERROR_NO_NAME_SET; + + error = ttt_server_statistics (client->server, username, &response); + if (error) + return error; + + ttt_client_send (client, response); + + free (response); + + return TTT_ERROR_NONE; +} + static ttt_error_t _ttt_client_execute_message (ttt_client_t *client, char **args, @@ -181,7 +216,7 @@ _ttt_client_execute_message (ttt_client_t *client, ttt_client_send (client, response); xasprintf (¬ice, "NOTICE MESSAGE %s \"%s\"\r\n", - client->name, + client->username, args[0]); ttt_server_broadcast (client->server, notice); @@ -250,13 +285,28 @@ _ttt_client_execute_version (ttt_client_t *client, int num_args) { char *response; + char *clientversion; + int version; + int i; assert (num_args == 1); - /* XXX: Argument is being ignored. - This is not completely implemented. */ + clientversion = args[0]; + + /* Verify that provided version arg is a positive integer */ + for (i = 0; i < strlen(clientversion); i++) + if (!isdigit (clientversion[i])) + return TTT_ERROR_SYNTAX; + + version = atoi (clientversion); + + if (version < 1) + return TTT_ERROR_SYNTAX; + + if (version > TTT_SERVER_PROTOCOL_VERSION) + version = TTT_SERVER_PROTOCOL_VERSION; - xasprintf (&response, "VERSION 1\r\n"); + xasprintf (&response, "VERSION %d\r\n", version); ttt_client_send (client, response); free (response); @@ -276,7 +326,7 @@ _ttt_client_execute_quit (ttt_client_t *client, return TTT_ERROR_QUIT_REQUESTED; xasprintf (¬ice, "NOTICE QUIT %s\r\n", - client->name); + client->username); ttt_server_broadcast (client->server, notice); free (notice); @@ -306,7 +356,7 @@ _ttt_client_init (ttt_client_t *client, client->request_strings = NULL; client->num_request_strings = 0; - client->name = NULL; + client->username = NULL; client->registered = FALSE; client->num_wins = 0; } @@ -319,8 +369,8 @@ _ttt_client_fini (ttt_client_t *client) if (client->registered) ttt_server_unregister_client (client->server, client); - free (client->name); - client->name = NULL; + free (client->username); + client->username = NULL; yylex_destroy (client->scanner); shutdown (client->socket, SHUT_RDWR); @@ -476,17 +526,17 @@ ttt_client_send (ttt_client_t *client, const char *message) /* Exported: See ttt-client.h for documentation. */ const char* -ttt_client_get_name (ttt_client_t *client) +ttt_client_get_username (ttt_client_t *client) { - return client->name; + return client->username; } /* Exported: See ttt-client.h for documentation. */ void -ttt_client_set_name (ttt_client_t *client, const char *name) +ttt_client_set_username (ttt_client_t *client, const char *username) { - free (client->name); - client->name = xstrdup (name); + free (client->username); + client->username = xstrdup (username); } /* Exported: See ttt-client.h for documentation. */ diff --git a/src/ttt-client.h b/src/ttt-client.h index d4e3346..c759650 100644 --- a/src/ttt-client.h +++ b/src/ttt-client.h @@ -43,11 +43,11 @@ ttt_client_send (ttt_client_t *client, const char *message); /* Get a client's name. */ const char* -ttt_client_get_name (ttt_client_t *client); +ttt_client_get_username (ttt_client_t *client); /* Set a client's name. */ void -ttt_client_set_name (ttt_client_t *client, const char *name); +ttt_client_set_username (ttt_client_t *client, const char *username); /* Return the client's win count */ int diff --git a/src/ttt-error.c b/src/ttt-error.c index 21d30e6..d3e57cb 100644 --- a/src/ttt-error.c +++ b/src/ttt-error.c @@ -28,25 +28,25 @@ ttt_error_string (ttt_error_t error) case TTT_ERROR_NONE: return "ERROR NONEi\r\n"; case TTT_ERROR_NO_NAME_SET: - return "ERROR NONAMESET\r\n"; + return "ERROR NO_NAME_SET\r\n"; case TTT_ERROR_INVALID_NAME: - return "ERROR INVALIDNAME\r\n"; + return "ERROR INVALID_NAME\r\n"; case TTT_ERROR_COMMAND: return "ERROR COMMAND\r\n"; case TTT_ERROR_SYNTAX: return "ERROR SYNTAX\r\n"; case TTT_ERROR_NOT_NUMBER: - return "ERROR NOTNUMBER\r\n"; + return "ERROR NOT_NUMBER\r\n"; case TTT_ERROR_NOT_GRID: - return "ERROR NOTGRID\r\n"; + return "ERROR NOT_GRID\r\n"; case TTT_ERROR_NO_USER: - return "ERROR NOUSER\r\n"; + return "ERROR NO_USER\r\n"; case TTT_ERROR_NOT_IN_GAME: - return "ERROR_NOTINGAME\r\n"; + return "ERROR_NOT_IN_GAME\r\n"; case TTT_ERROR_NOT_PLAYING: - return "ERROR_NOTPLAYING\r\n"; + return "ERROR_NOT_PLAYING\r\n"; case TTT_ERROR_NOT_YOUR_TURN: - return "ERROR NOTYOURTURN\r\n"; + return "ERROR NOT_YOUR_TURN\r\n"; /* Not an actual protocol errror, so this should never happen. */ case TTT_ERROR_QUIT_REQUESTED: ASSERT_NOT_REACHED; diff --git a/src/ttt-server.c b/src/ttt-server.c index 23fd138..8130420 100644 --- a/src/ttt-server.c +++ b/src/ttt-server.c @@ -56,25 +56,25 @@ ttt_server_register_client (ttt_server_t *server, ttt_client_t *client) { int i; ttt_error_t error = TTT_ERROR_NONE; - const char *name; + const char *username; pthread_mutex_lock (&server->mutex); - name = ttt_client_get_name (client); + username = ttt_client_get_username (client); - assert (name != NULL); + assert (username != NULL); - if (name[0] == '\0') + if (username[0] == '\0') return TTT_ERROR_INVALID_NAME; for (i = 0; i < server->num_clients; i++) { - if (strcmp (ttt_client_get_name (server->clients[i]), name) == 0) { + if (strcmp (ttt_client_get_username (server->clients[i]), username) == 0) { error = TTT_ERROR_INVALID_NAME; goto CLEANUP_LOCK; } } - printf ("Client %s has joined.\r\n", name); + printf ("Client %s has joined.\r\n", username); server->num_clients++; @@ -110,7 +110,7 @@ ttt_server_unregister_client (ttt_server_t *server, ttt_client_t *client) assert (i < server->num_clients); - printf ("Client %s has left.\r\n", ttt_client_get_name (client)); + printf ("Client %s has left.\r\n", ttt_client_get_username (client)); memmove (&server->clients[i], &server->clients[i+1], (server->num_clients - i - 1) * sizeof (ttt_client_t *)); @@ -146,10 +146,9 @@ ttt_server_who (ttt_server_t *server) xasprintf (&response, "WHO"); for (i = 0; i < server->num_clients; i++) - xasprintf (&response, "%s %s %d", + xasprintf (&response, "%s %s", response, - ttt_client_get_name (server->clients[i]), - ttt_client_get_num_wins (server->clients[i])); + ttt_client_get_username (server->clients[i])); xasprintf (&response, "%s\r\n", response); @@ -158,6 +157,37 @@ ttt_server_who (ttt_server_t *server) return response; } +/* Exported: See ttt-server.h for documentation. */ +ttt_error_t +ttt_server_statistics (ttt_server_t *server, const char *username, char **response) +{ + ttt_bool_t usernamefound = FALSE; + char *client_username; + int client_num_wins; + int i; + + pthread_mutex_lock (&server->mutex); + + for (i = 0; i < server->num_clients; i++) { + client_username = ttt_client_get_username (server->clients[i]); + if (strcasecmp (username, client_username) == 0) { + usernamefound = TRUE; + client_num_wins = ttt_client_get_num_wins (server->clients[i]); + xasprintf (response, "STATISTICS %s \"\r\n" + "TICTACTOE WINS %d\r\n\"\r\n", + client_username, + client_num_wins); + } + } + + pthread_mutex_unlock (&server->mutex); + + if (!usernamefound) + return TTT_ERROR_NO_USER; + + return TTT_ERROR_NONE; +} + /* Exported: See ttt-server.h for documentation. */ const char* ttt_server_get_host (ttt_server_t *server) diff --git a/src/ttt-server.h b/src/ttt-server.h index fef7030..8f2d34b 100644 --- a/src/ttt-server.h +++ b/src/ttt-server.h @@ -73,6 +73,22 @@ ttt_server_broadcast (ttt_server_t *server, const char *message); const char* ttt_server_who (ttt_server_t *server); +/* Generates the statistics for the user. If the function does not + * return an error, the response will be allocated in this function + * and will need to be free'd by the caller. + * + * Locking: The server mutex will be acquired and held throughout the + * execution of this function. Each client mutex may also be acquired + * and held by functions called during the execution of this function. + * + * Errors: If an error such as an IO error occurs, this function will + * not return. + */ +ttt_error_t +ttt_server_statistics (ttt_server_t *server, + const char *username, + char **response); + /* Gets the server hostname. * */ diff --git a/src/ttt.h b/src/ttt.h index 1b22ba0..c1662af 100644 --- a/src/ttt.h +++ b/src/ttt.h @@ -61,6 +61,8 @@ typedef int ttt_bool_t; #define FALSE 0 #define TRUE 1 +#define TTT_SERVER_PROTOCOL_VERSION 1 + typedef enum { TTT_STATUS_SUCCESS = 0, TTT_STATUS_FAILURE, -- 2.43.0