2005-12-03 Richard D. Worth <richard@theworths.org>
authorRichard Worth <richard@theworths.org>
Sat, 3 Dec 2005 05:36:42 +0000 (05:36 +0000)
committerRichard Worth <richard@theworths.org>
Sat, 3 Dec 2005 05:36:42 +0000 (05:36 +0000)
        * 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
PROTOCOL
TODO
src/ttt-client.c
src/ttt-client.h
src/ttt-error.c
src/ttt-server.c
src/ttt-server.h
src/ttt.h

index bb0505276a4bd31310d1253dec6291377fd41f37..3d2bc1d2b7452a216ab47ccc5e78b898d3f3f5ed 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2005-12-03  Richard D. Worth  <richard@theworths.org>
+
+       * 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  <richard@theworths.org>
 
        * TODO: Check off Global commands, HELP, VERSION.
index d855cdf348780bc1edecce3e10e5ddb93d36d471..be0f04d04a6b2a5640215c4622d366c672145c9b 100644 (file)
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -31,11 +31,11 @@ Document Conventions
        <response> is one of:
 
        <command> <args>
-       ERROR <message>
+       ERROR <error-code>
 
 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 <username> <server-addr> <server-port>
 
-    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 <username1> <games1> <username2> <games2> ...
+       WHO <username1> <username2> ...
 
-       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 <username>
+
+       ->
+
+       STATISTICS <username> "
+       TICTACTOE WINS <wins>
+       "
+
+       Lists the statistics for the specified user.
+
+       Possible errors: NO_NAME_SET, NO_USER
+
+    1.2.3. Message
 
        MESSAGE <text>
 
@@ -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 { <command> }
 
+       ->
+
+       HELP { <command> } "
+       <overview or detailed help>
+       "
+
        Displays help.  If <command> 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 <client-version-number>
 
@@ -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 <username> <text>
 
+       where <text> 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 <outcome> <username>
 
-           <outcame> is either WON in which case <username> indicates
+           <outcome> is either WON in which case <username> indicates
            the winner or CATSGAME in which case <username> 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 f0bf20a4ec4a2c0da44f17ca505c52a58ae46c88..f7fe04b16eed3e9e4b48918776271d7a736305c4 100644 (file)
--- 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
index 1dd755f83651e3d7c6f80f176e0befa05f8af8dc..c92c4ebe1cd8e174e772d29c1aa40ed23a52eabf 100644 (file)
@@ -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 <message>       ", "Send a message to everyone."},
 
+    {"STATISTICS", 1, 1, _ttt_client_execute_statistics,
+     "STATISTICS <username>   ", "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 (&notice, "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 (&notice, "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 (&notice, "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. */
index d4e33464a3658246c921e94fcf074b8744c4f673..c7596508e4681a3965ee22c5a0c59afff8ddac1a 100644 (file)
@@ -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
index 21d30e687bc2b5c49e568b94fde9d9431df8bc4b..d3e57cba6791da535d6ac64f90ad2aad5de51bc7 100644 (file)
@@ -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;
index 23fd138cb375d24f611eb85b45bc7d0460267f1e..813042044873fa20b03d9ff571dfb3ab0a7f3b9f 100644 (file)
@@ -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)
index fef70308dbb59b91df593de7b7fc39c2ba1d85e3..8f2d34b7c1bd5b2b6c08cef9aa0f8d3867b9b2ec 100644 (file)
@@ -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.
  *
  */
index 1b22ba0bc0d33db5c1e9a427df7c593512a07d57..c1662afd8aaf6875ff38e961c6fa822a2d5dc1b4 100644 (file)
--- 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,