From 3e1c2bf267dea8c592f41636c3d0fb1c7253ee26 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 20 Feb 2011 13:51:49 +0800 Subject: [PATCH 1/1] Item statuses --- notes/proto.txt | 6 +- src/client/main.c | 211 +++++++++++++++++++++++++++----------------- src/server/server.c | 36 ++++++-- 3 files changed, 162 insertions(+), 91 deletions(-) diff --git a/notes/proto.txt b/notes/proto.txt index df3f9cc..d48031e 100644 --- a/notes/proto.txt +++ b/notes/proto.txt @@ -74,13 +74,13 @@ s 200 Add OK\n or 402 No balance\n or 403 Not Coke\n or 404 Bad User\n --- Get Item list --- c ENUM_ITEMS\n s 201 Items \n -s 202 Item \n +s >> Response to ITEM_INFO ... s 200 List End\n --- Get Item Information --- c ITEM_INFO \n -s 202 Item \n - +s 202 Item \n + "avail", "sold", or "error" --- Get Users' Balances --- c ENUM_USERS[ min_balance:][ max_balance:][ flags:][ last_seen_before:][ last_seen_after:][ sort:[-desc]]\n s 201 Users \n diff --git a/src/client/main.c b/src/client/main.c index a241f2e..aa62471 100644 --- a/src/client/main.c +++ b/src/client/main.c @@ -42,6 +42,7 @@ enum eUI_Modes typedef struct sItem { char *Type; int ID; + int Status; // 0: Availiable, 1: Sold out, -1: Error char *Desc; int Price; } tItem; @@ -51,7 +52,7 @@ typedef struct sItem { void ShowUsage(void); // --- GUI --- int ShowNCursesUI(void); - int ShowItemAt(int Row, int Col, int Width, int Index); + int ShowItemAt(int Row, int Col, int Width, int Index, int bHilighted); void PrintAlign(int Row, int Col, int Width, const char *Left, char Pad1, const char *Mid, char Pad2, const char *Right, ...); // --- Coke Server Communication --- int OpenConnection(const char *Host, int Port); @@ -105,8 +106,8 @@ int main(int argc, char *argv[]) // -- Create regular expressions // > Code Type Count ... CompileRegex(&gArrayRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([0-9]+)", REG_EXTENDED); // - // > Code Type Ident Price Desc - CompileRegex(&gItemRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([A-Za-z]+):([0-9]+)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED); + // > Code Type Ident Status Price Desc + CompileRegex(&gItemRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+([A-Za-z]+):([0-9]+)\\s+(avail|sold|error)\\s+([0-9]+)\\s+(.+)$", REG_EXTENDED); // > Code 'SALT' salt CompileRegex(&gSaltRegex, "^([0-9]{3})\\s+([A-Za-z]+)\\s+(.+)$", REG_EXTENDED); // > Code 'User' Username Balance Flags @@ -564,7 +565,7 @@ int ShowNCursesUI(void) int itemCount; int maxItemIndex; int itemBase = 0; - int currentItem = 0; + int currentItem; int ret = -2; // -2: Used for marking "no return yet" char balance_str[5+1+2+1]; // If $9999.99 is too little, something's wrong @@ -588,14 +589,15 @@ int ShowNCursesUI(void) raw(); noecho(); // Get max index - maxItemIndex = ShowItemAt(0, 0, 0, -1); + maxItemIndex = ShowItemAt(0, 0, 0, -1, 0); // Get item count per screen // - 6: randomly chosen (Need at least 3) itemCount = LINES - 6; if( itemCount > maxItemIndex ) itemCount = maxItemIndex; // Get first index - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + currentItem = 0; + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem ++; @@ -618,28 +620,27 @@ int ShowNCursesUI(void) int pos = 0; move( yBase + 1 + i, xBase ); + printw("| "); - if( currentItem == itemBase + i ) { - printw("| ->"); - } - else { - printw("| "); - } - pos += 4; + pos += 2; // Check for the '...' row // - Oh god, magic numbers! if( (i == 0 && itemBase > 0) || (i == itemCount - 1 && itemBase < maxItemIndex - itemCount) ) { - printw(" ..."); pos += 6; + printw(" ..."); pos += 8; times = (width - pos) - 1; - //times = width-1 - 8 - 3; while(times--) addch(' '); } // Show an item else { - ShowItemAt( yBase + 1 + i, xBase + pos, (width - pos) - 3, itemBase + i); + ShowItemAt( + yBase + 1 + i, xBase + pos, // Position + (width - pos) - 3, // Width + itemBase + i, // Index + !!(currentItem == itemBase + i) // Hilighted + ); printw(" "); } @@ -688,26 +689,26 @@ int ShowNCursesUI(void) case 'B': currentItem ++; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem ++; if( currentItem >= maxItemIndex ) { currentItem = 0; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem ++; } break; case 'A': currentItem --; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem --; if( currentItem < 0 ) { currentItem = maxItemIndex - 1; // Skip over spacers - while( ShowItemAt(0, 0, 0, currentItem) == -1 ) + while( ShowItemAt(0, 0, 0, currentItem, 0) == -1 ) currentItem --; } break; @@ -726,7 +727,7 @@ int ShowNCursesUI(void) switch(ch) { case '\n': - ret = ShowItemAt(0, 0, 0, currentItem); + ret = ShowItemAt(0, 0, 0, currentItem, 0); break; case 'q': ret = -1; // -1: Return with no dispense @@ -750,11 +751,12 @@ int ShowNCursesUI(void) * \return Dispense index of item * \note Part of the NCurses UI */ -int ShowItemAt(int Row, int Col, int Width, int Index) +int ShowItemAt(int Row, int Col, int Width, int Index, int bHilighted) { int _x, _y, times; char *name = NULL; int price = 0; + int status = -1; switch(giUIMode) { @@ -779,6 +781,7 @@ int ShowItemAt(int Row, int Col, int Width, int Index) { name = gaItems[Index].Desc; price = gaItems[Index].Price; + status = gaItems[Index].Status; break; } Index -= 7; @@ -794,6 +797,7 @@ int ShowItemAt(int Row, int Col, int Width, int Index) Index += 7; name = gaItems[Index].Desc; price = gaItems[Index].Price; + status = gaItems[Index].Status; break; default: return -1; @@ -806,24 +810,48 @@ int ShowItemAt(int Row, int Col, int Width, int Index) if( Index >= 0 ) { - printw("%02i %s", Index, name); + // Show hilight and status + switch( status ) + { + case 0: + if( bHilighted ) + printw("-> "); + else + printw(" "); + break; + case 1: + printw("SLD"); + break; + + default: + case -1: + printw("ERR"); + break; + } + + printw(" %s", name); getyx(stdscr, _y, _x); // Assumes max 4 digit prices - times = Width - 4 - (_x - Col); // TODO: Better handling for large prices + times = Width - 5 - (_x - Col); // TODO: Better handling for large prices while(times--) addch(' '); - printw("%4i", price); + + printw(" %4i", price); } else { printw("-- %s", name); getyx(stdscr, _y, _x); - times = Width - 4 - (_x - Col); // TODO: Better handling for large prices + times = Width - 4 - (_x - Col); while(times--) addch(' '); - printw(" --", price); + printw(" "); } } + // If the item isn't availiable for sale, return -1 (so it's skipped) + if( status ) + Index = -1; + return Index; } @@ -1141,6 +1169,72 @@ int GetUserBalance(int Socket) return 0; } +/** + * \brief Read an item info response from the server + * \param Dest Destination for the read item (strings will be on the heap) + */ +int ReadItemInfo(int Socket, tItem *Dest) +{ + char *buf; + int responseCode; + + regmatch_t matches[8]; + char *statusStr; + + // Get item info + buf = ReadLine(Socket); + responseCode = atoi(buf); + + switch(responseCode) + { + case 202: break; + + case 406: + printf("Bad item name\n"); + free(buf); + return 1; + + default: + fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n%s", responseCode, buf); + exit(-1); + } + + RunRegex(&gItemRegex, buf, 8, matches, "Malformed server response"); + + buf[ matches[3].rm_eo ] = '\0'; + buf[ matches[5].rm_eo ] = '\0'; + buf[ matches[7].rm_eo ] = '\0'; + + statusStr = &buf[ matches[5].rm_so ]; + + Dest->ID = atoi( buf + matches[4].rm_so ); + + if( strcmp(statusStr, "avail") == 0 ) + Dest->Status = 0; + else if( strcmp(statusStr, "sold") == 0 ) + Dest->Status = 1; + else if( strcmp(statusStr, "error") == 0 ) + Dest->Status = -1; + else { + fprintf(stderr, "Unknown response from dispense server (status '%s')\n", + statusStr); + return 1; + } + Dest->Price = atoi( buf + matches[6].rm_so ); + + // Hack a little to reduce heap fragmentation + { + char tmpType[strlen(buf + matches[3].rm_so) + 1]; + char tmpDesc[strlen(buf + matches[7].rm_so) + 1]; + strcpy(tmpType, buf + matches[3].rm_so); + strcpy(tmpDesc, buf + matches[7].rm_so); + free(buf); + Dest->Type = strdup( tmpType ); + Dest->Desc = strdup( tmpDesc ); + } + + return 0; +} /** * \brief Fill the item information structure @@ -1195,27 +1289,7 @@ void PopulateItemList(int Socket) // Fetch item information for( i = 0; i < giNumItems; i ++ ) { - regmatch_t matches[7]; - - // Get item info - buf = ReadLine(Socket); - responseCode = atoi(buf); - - if( responseCode != 202 ) { - fprintf(stderr, "Unknown response from dispense server (Response Code %i)\n", responseCode); - exit(-1); - } - - RunRegex(&gItemRegex, buf, 7, matches, "Malformed server response"); - - buf[ matches[3].rm_eo ] = '\0'; - - gaItems[i].Type = strdup( buf + matches[3].rm_so ); - gaItems[i].ID = atoi( buf + matches[4].rm_so ); - gaItems[i].Price = atoi( buf + matches[5].rm_so ); - gaItems[i].Desc = strdup( buf + matches[6].rm_so ); - - free(buf); + ReadItemInfo( Socket, &gaItems[i] ); } // Read end of list @@ -1239,46 +1313,23 @@ void PopulateItemList(int Socket) */ int Dispense_ItemInfo(int Socket, const char *Type, int ID) { - int responseCode; - char *buf; - regmatch_t matches[7]; - char *type, *desc; - int id, price; + tItem item; - // Dispense! + // Query sendf(Socket, "ITEM_INFO %s:%i\n", Type, ID); - buf = ReadLine(Socket); - responseCode = atoi(buf); - switch( responseCode ) + if( ReadItemInfo(Socket, &item) ) { - case 202: - break; - - case 406: - printf("Bad item name\n"); - free(buf); - return 1; - - default: - printf("Unknown response code %i ('%s')\n", responseCode, buf); - free(buf); return -1; } - - RunRegex(&gItemRegex, buf, 7, matches, "Malformed server response"); - - buf[ matches[3].rm_eo ] = '\0'; - - type = buf + matches[3].rm_so; buf[matches[3].rm_eo] = '\0'; - id = atoi( buf + matches[4].rm_so ); - price = atoi( buf + matches[5].rm_so ); - desc = buf + matches[6].rm_so; buf[matches[6].rm_eo] = '\0'; + printf("%8s:%-2i %2i.%02i %s\n", + item.Type, item.ID, + item.Price/100, item.Price%100, + item.Desc); - printf("%8s:%-2i %2i.%02i %s\n", type, id, price/100, price%100, desc); - - free(buf); + free(item.Type); + free(item.Desc); return 0; } diff --git a/src/server/server.c b/src/server/server.c index dbb08cb..a536121 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -472,6 +472,32 @@ void Server_Cmd_SETEUSER(tClient *Client, char *Args) sendf(Client->Socket, "200 User set\n"); } +/** + * \brief Send an item status to the client + * \param Client Who to? + * \param Item Item to send + */ +void Server_int_SendItem(tClient *Client, tItem *Item) +{ + char *status = "avail"; + + if( Item->Handler->CanDispense ) + { + switch(Item->Handler->CanDispense(Item->ID, Client->UID)) + { + case 0: status = "avail"; break; + case 1: status = "sold"; break; + default: + case -1: status = "error"; break; + } + } + + sendf(Client->Socket, + "202 Item %s:%i %s %i %s\n", + Item->Handler->Name, Item->ID, status, Item->Price, Item->Name + ); +} + /** * \brief Enumerate the items that the server knows about */ @@ -495,10 +521,7 @@ void Server_Cmd_ENUMITEMS(tClient *Client, char *Args) for( i = 0; i < giNumItems; i ++ ) { if( gaItems[i].bHidden ) continue; - sendf(Client->Socket, - "202 Item %s:%i %i %s\n", - gaItems[i].Handler->Name, gaItems[i].ID, gaItems[i].Price, gaItems[i].Name - ); + Server_int_SendItem( Client, &gaItems[i] ); } sendf(Client->Socket, "200 List end\n"); @@ -560,10 +583,7 @@ void Server_Cmd_ITEMINFO(tClient *Client, char *Args) return ; } - sendf(Client->Socket, - "202 Item %s:%i %i %s\n", - item->Handler->Name, item->ID, item->Price, item->Name - ); + Server_int_SendItem( Client, item ); } void Server_Cmd_DISPENSE(tClient *Client, char *Args) -- 2.20.1