Crossfire Client, Branch  R11627
commands.c
Go to the documentation of this file.
00001 const char * const rcsid_common_commands_c =
00002     "$Id: commands.c 9719 2008-08-04 18:57:47Z ryo_saeba $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 
00026 /* Handles commands received by the server.  This does not necessarily
00027  * handle all commands - some might be in other files (like init.c)
00028  *
00029  * This file handles commans from the server->client.  See player.c
00030  * for client->server commands.
00031  *
00032  * this file contains most of the commands for the dispatch loop most of
00033  * the functions are self-explanatory, the pixmap/bitmap commands recieve
00034  * the picture, and display it.  The drawinfo command draws a string
00035  * in the info window, the stats command updates the local copy of the stats
00036  * and displays it. handle_query prompts the user for input.
00037  * send_reply sends off the reply for the input.
00038  * player command gets the player information.
00039  * MapScroll scrolls the map on the client by some amount
00040  * MapCmd displays the map either with layer packing or stack packing.
00041  *   packing/unpacking is best understood by looking at the server code
00042  *   (server/ericserver.c)
00043  *   stack packing is easy, for every map entry that changed, we pack
00044  *   1 byte for the x/y location, 1 byte for the count, and 2 bytes per
00045  *   face in the stack.
00046  *   layer packing is harder, but I seem to remember more efficient:
00047  *   first we pack in a list of all map cells that changed and are now
00048  *   empty.  The end of this list is a 255, which is bigger that 121, the
00049  *   maximum packed map location.
00050  *   For each changed location we also pack in a list of all the faces and
00051  *   X/Y coordinates by layer, where the layer is the depth in the map.
00052  *   This essentially takes slices through the map rather than stacks.
00053  *   Then for each layer, (max is MAXMAPCELLFACES, a bad name) we start
00054  *   packing the layer into the message.  First we pack in a face, then
00055  *   for each place on the layer with the same face, we pack in the x/y
00056  *   location.  We mark the last x/y location with the high bit on
00057  *   (11*11 = 121 < 128).  We then continue on with the next face, which
00058  *   is why the code marks the faces as -1 if they are finished.  Finally
00059  *   we mark the last face in the layer again with the high bit, clearly
00060  *   limiting the total number of faces to 32767, the code comments it's
00061  *   16384, I'm not clear why, but the second bit may be used somewhere
00062  *   else as well.
00063  *   The unpacking routines basically perform the opposite operations.
00064  */
00065 
00066 int mapupdatesent = 0;
00067 
00068 #include <client.h>
00069 #include <external.h>
00070 #include <assert.h>
00071 
00072 #include "mapdata.h"
00073 
00074 
00075 static void get_exp_info(const unsigned char *data, int len) {
00076     int pos, level;
00077 
00078     if (len < 2) {
00079         LOG(LOG_ERROR, "common::get_exp_info", "no max level info from server provided");
00080         return;
00081     }
00082 
00083     exp_table_max = GetShort_String(data);
00084     pos = 2;
00085     exp_table = calloc(exp_table_max, sizeof(uint64));
00086     for (level = 1; level <= exp_table_max && pos < len; level++) {
00087         exp_table[level] = GetInt64_String(data+pos);
00088         pos += 8;
00089     }
00090     if (level != exp_table_max) {
00091         LOG(LOG_ERROR, "common::get_exp_info",
00092              "Incomplete table sent - got %d entries, wanted %d", level, exp_table_max);
00093     }
00094 }
00095 
00096 static void get_skill_info(char *data, int len) {
00097     char *cp, *nl, *sn;
00098     int val;
00099 
00100     cp = data;
00101     do {
00102         nl = strchr(cp, '\n');
00103         if (nl) {
00104             *nl = 0;
00105             nl++;
00106         }
00107         sn = strchr(cp, ':');
00108         if (!sn) {
00109             LOG(LOG_WARNING, "common::get_skill_info", "corrupt line: /%s/", cp);
00110             return;
00111         }
00112 
00113         *sn = 0;
00114         sn++;
00115         val = atoi(cp);
00116         val -= CS_STAT_SKILLINFO;
00117         if (val < 0 || val> CS_NUM_SKILLS) {
00118             LOG(LOG_WARNING, "common::get_skill_info", "invalid skill number %d", val);
00119             return;
00120         }
00121 
00122         free(skill_names[val]);
00123         skill_names[val] = strdup_local(sn);
00124         cp = nl;
00125     } while (cp < data+len);
00126 }
00127 
00128 /* handles the response from a 'requestinfo' command.  This function doesn't
00129  * do much itself other than dispatch to other functions.
00130  */
00131 void ReplyInfoCmd(uint8 *buf, int len) {
00132     uint8 *cp;
00133     int i;
00134 
00135     /* Covers a bug in the server in that it could send a replyinfo with no parameters */
00136     if (!buf) {
00137         return;
00138     }
00139 
00140     for (i = 0; i < len; i++) {
00141         /* Either a space or newline represents a break */
00142         if (*(buf+i) == ' ' || *(buf+i) == '\n') {
00143             break;
00144         }
00145     }
00146     if (i >= len) {
00147         /* Don't print buf, as it may contain binary data */
00148         /* Downgrade this to DEBUG - if the client issued an unsupported requestinfo
00149          * info to the server, we'll end up here - this could be normal behaviour
00150          */
00151         LOG(LOG_DEBUG, "common::ReplyInfoCmd", "Never found a space in the replyinfo");
00152         return;
00153     }
00154 
00155     /* Null out the space and put cp beyond it */
00156     cp = buf+i;
00157     *cp++ = '\0';
00158     if (!strcmp((char*)buf, "image_info")) {
00159         get_image_info(cp, len-i-1);   /* located in common/image.c */
00160     } else if (!strcmp((char*)buf, "image_sums")) {
00161         get_image_sums((char*)cp, len-i-1);   /* located in common/image.c */
00162     } else if (!strcmp((char*)buf, "skill_info")) {
00163         get_skill_info((char*)cp, len-i-1);   /* located in common/commands.c */
00164     } else if (!strcmp((char*)buf, "exp_table")) {
00165         get_exp_info(cp, len-i-1);   /* located in common/commands.c */
00166     }
00167 }
00168 
00169 /* Received a response to a setup from the server.
00170  * This function is basically the same as the server side function - we
00171  * just do some different processing on the data.
00172  */
00173 void SetupCmd(char *buf, int len) {
00174     int s;
00175     char *cmd, *param;
00176 
00177     /* run through the cmds of setup
00178      * syntax is setup <cmdname1> <parameter> <cmdname2> <parameter> ...
00179      *
00180      * we send the status of the cmd back, or a FALSE is the cmd is the server unknown
00181      * The client then must sort this out
00182      */
00183 
00184     LOG(LOG_DEBUG, "common::SetupCmd", "%s", buf);
00185     for (s = 0; ; ) {
00186         if (s >= len) { /* ugly, but for secure...*/
00187             break;
00188         }
00189 
00190         cmd = &buf[s];
00191 
00192         /* find the next space, and put a null there */
00193         for (; buf[s] && buf[s] != ' '; s++)
00194             ;
00195         buf[s++] = 0;
00196         while (buf[s] == ' ') {
00197             s++;
00198         }
00199         if (s >= len) {
00200             break;
00201         }
00202 
00203         param = &buf[s];
00204 
00205         for (; buf[s] && buf[s] != ' '; s++)
00206             ;
00207         buf[s++] = 0;
00208         while (s < len && buf[s] == ' ') {
00209             s++;
00210         }
00211 
00212         /* what we do with the returned data depends on what the server
00213          * returns to us.  In some cases, we may fall back to other
00214          * methods, just report on error, or try another setup command.
00215          */
00216         if (!strcmp(cmd, "sound2")) {
00217             /* No parsing needed, but we don't want a warning about unknown
00218              * setup option below.
00219              */
00220         } else if (!strcmp(cmd, "sound")) {
00221             /* No, this should not be !strcmp()... */
00222             if (strcmp(param, "FALSE")) {
00223                 cs_print_string(csocket.fd, "setsound %d", want_config[CONFIG_SOUND]);
00224             }
00225         } else if (!strcmp(cmd, "mapsize")) {
00226             int x, y = 0;
00227             char *cp, tmpbuf[MAX_BUF];
00228 
00229             if (!strcasecmp(param, "false")) {
00230                 draw_info("Server only supports standard sized maps (11x11)", NDI_RED);
00231                 /* Do this because we may have been playing on a big server before */
00232                 use_config[CONFIG_MAPWIDTH] = 11;
00233                 use_config[CONFIG_MAPHEIGHT] = 11;
00234                 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
00235                 resize_map_window(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
00236                 continue;
00237             }
00238             x = atoi(param);
00239             for (cp = param; *cp != 0; cp++) {
00240                 if (*cp == 'x' || *cp == 'X') {
00241                     y = atoi(cp+1);
00242                     break;
00243                 }
00244             }
00245             /* we wanted a size larger than the server supports.  Reduce our
00246              * size to server maximum, and re-sent the setup command.
00247              * Update our want sizes, and also tell the player what we are doing
00248              */
00249             if (use_config[CONFIG_MAPWIDTH] > x || use_config[CONFIG_MAPHEIGHT] > y) {
00250                 if (use_config[CONFIG_MAPWIDTH] > x) use_config[CONFIG_MAPWIDTH] = x;
00251                 if (use_config[CONFIG_MAPHEIGHT] > y) use_config[CONFIG_MAPHEIGHT] = y;
00252                 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
00253                 cs_print_string(csocket.fd,
00254                                 "setup mapsize %dx%d", use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
00255                 snprintf(tmpbuf, sizeof(tmpbuf), "Server supports a max mapsize of %d x %d - requesting a %d x %d mapsize",
00256                     x, y, use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
00257                 draw_info(tmpbuf, NDI_RED);
00258             } else if (use_config[CONFIG_MAPWIDTH] == x && use_config[CONFIG_MAPHEIGHT] == y) {
00259                 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
00260                 resize_map_window(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
00261             } else {
00262                 /* Our request was not bigger than what server supports, and
00263                  * not the same size, so whats the problem?  Tell the user that
00264                  * something is wrong.
00265                  */
00266                 snprintf(tmpbuf, sizeof(tmpbuf), "Unable to set mapsize on server - we wanted %d x %d, server returned %d x %d",
00267                     use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT], x, y);
00268                 draw_info(tmpbuf, NDI_RED);
00269             }
00270         } else if (!strcmp(cmd, "sexp") || !strcmp(cmd, "darkness") ||
00271             !strcmp(cmd, "newmapcmd") || !strcmp(cmd, "spellmon")) {
00272             /* this really isn't an error or bug - in fact, it is expected if
00273              * the user is playing on an older server.
00274              */
00275             if (!strcmp(param, "FALSE")) {
00276                 LOG(LOG_WARNING, "common::SetupCmd", "Server returned FALSE on setup command %s", cmd);
00277 #if 0
00278 /* This should really be a callback to the gui if it needs to re-examine
00279  * the results here.
00280  */
00281                 if (!strcmp(cmd, "newmapcmd") && fog_of_war == TRUE) {
00282                     fprintf(stderr, "**Warning: Fog of war is active but server does not support the newmap command\n");
00283                 }
00284 #endif
00285             }
00286         } else if (!strcmp(cmd, "facecache")) {
00287             if (!strcmp(param, "FALSE") && want_config[CONFIG_CACHE]) {
00288                 SendSetFaceMode(csocket, CF_FACE_CACHE);
00289             } else {
00290                 use_config[CONFIG_CACHE] = atoi(param);
00291             }
00292         } else if (!strcmp(cmd, "faceset")) {
00293             if (!strcmp(param, "FALSE")) {
00294                 draw_info("Server does not support other image sets, will use default", NDI_RED);
00295                 face_info.faceset = 0;
00296             }
00297         } else if (!strcmp(cmd, "map2cmd")) {
00298             if (!strcmp(param, "FALSE")) {
00299                 draw_info("Server does not support map2cmd!", NDI_RED);
00300                 draw_info("This server is too old to support this client!", NDI_RED);
00301 #ifdef WIN32
00302                 closesocket(csocket.fd);
00303 #else
00304                 close(csocket.fd);
00305 #endif
00306                 csocket.fd = -1;
00307             }
00308         } else if (!strcmp(cmd, "itemcmd")) {
00309             /* don't really care - currently, the server will just
00310              * fall back to the item1 transport mode.  If more item modes
00311              * are used in the future, we should probably fall back one at
00312              * a time or the like.
00313              */
00314         } else if (!strcmp(cmd, "exp64")) {
00315             /* If this server does not support the new skill code,
00316              * error out. If the server does support new exp system,
00317              * send a request to get the mapping information.
00318              */
00319             if (!strcmp(param, "FALSE")) {
00320                 draw_info("Server does not support exp64!", NDI_RED);
00321                 draw_info("This server is too old to support this client!", NDI_RED);
00322 #ifdef WIN32
00323                 closesocket(csocket.fd);
00324 #else
00325                 close(csocket.fd);
00326 #endif
00327                 csocket.fd = -1;
00328             } else {
00329                 cs_print_string(csocket.fd, "requestinfo skill_info");
00330             }
00331         } else if (!strcmp(cmd, "extendedMapInfos")) {
00332             if (!strcmp(param, "FALSE")) {
00333                 use_config[CONFIG_SMOOTH] = 0;
00334             } else {
00335                 /* Request all extended infos we want
00336                  * Should regroup everything for easyness
00337                  */
00338                 if (use_config[CONFIG_SMOOTH]) {
00339                     cs_print_string(csocket.fd, "toggleextendedinfos smooth");
00340                 }
00341             }
00342         } else if (!strcmp(cmd, "extendedTextInfos")) {
00343             if (strcmp(param, "FALSE")) { /* server didn't send FALSE*/
00344                 /* Server seems to accept extended text infos. Let's tell
00345                  * it what extended text info we want
00346                  */
00347                 char exttext[MAX_BUF];
00348                 TextManager *manager = firstTextManager;
00349 
00350                 while (manager) {
00351                     snprintf(exttext, sizeof(exttext), "toggleextendedtext %d", manager->type);
00352                     cs_print_string(csocket.fd, exttext);
00353                     manager = manager->next;
00354                 }
00355             }
00356         } else if (!strcmp(cmd, "want_pickup")) {
00357             /* Nothing to do specially, it's info pushed from server, not having it isn't that bad. */
00358         } else if (!strcmp(cmd, "inscribe")) {
00359             if (strcmp(param, "FALSE"))
00360                 command_inscribe = atoi(param);
00361             else
00362                 command_inscribe = 0;
00363         } else {
00364             LOG(LOG_INFO, "common::SetupCmd", "Got setup for a command we don't understand: %s %s",
00365                 cmd, param);
00366         }
00367     }
00368 }
00369 
00370 void ExtendedInfoSetCmd(char *data, int len) {
00371     (void)data; /* __UNUSED__ */
00372     (void)len; /* __UNUSED__ */
00373 
00374     /* Do nothing for now, perhaps later add some
00375      * support to check what server knows.
00376      */
00377     /* commented, no waranty string data is null terminated
00378        draw_info("ExtendedInfoSet returned from server: ", NDI_BLACK);
00379        draw_info(data, NDI_BLACK);
00380     */
00381 }
00382 
00383 /* Handles when the server says we can't be added.  In reality, we need to
00384  * close the connection and quit out, because the client is going to close
00385  * us down anyways.
00386  */
00387 void AddMeFail(char *data, int len) {
00388     (void)data; /* __UNUSED__ */
00389     (void)len; /* __UNUSED__ */
00390 
00391     LOG(LOG_INFO, "common::AddMeFail", "addme_failed received.");
00392     return;
00393 }
00394 
00395 /* This is really a throwaway command - there really isn't any reason to
00396  * send addme_success commands.
00397  */
00398 void AddMeSuccess(char *data, int len) {
00399     (void)data; /* __UNUSED__ */
00400     (void)len; /* __UNUSED__ */
00401 
00402     LOG(LOG_INFO, "common::AddMeSuccess", "addme_success received.");
00403     return;
00404 }
00405 
00406 void GoodbyeCmd(char *data, int len) {
00407     (void)data; /* __UNUSED__ */
00408     (void)len; /* __UNUSED__ */
00409 
00410     /* This could probably be greatly improved - I am not sure if anything
00411      * needs to be saved here, but certainly it should be possible to
00412      * reconnect to the server or a different server without having to
00413      * rerun the client.
00414      */
00415     LOG(LOG_WARNING, "common::GoodbyeCmd", "Received goodbye command from server - exiting");
00416     exit(0);
00417 }
00418 
00419 Animations animations[MAXANIM];
00420 
00421 void AnimCmd(unsigned char *data, int len) {
00422     short anum;
00423     int i, j;
00424 
00425     anum = GetShort_String(data);
00426     if (anum < 0 || anum > MAXANIM) {
00427         LOG(LOG_WARNING, "common::AnimCmd", "animation number invalid: %d", anum);
00428         return;
00429     }
00430 
00431     animations[anum].flags = GetShort_String(data+2);
00432     animations[anum].num_animations = (len-4)/2;
00433     if (animations[anum].num_animations < 1) {
00434         LOG(LOG_WARNING, "common::AnimCmd", "num animations invalid: %d",
00435             animations[anum].num_animations);
00436         return;
00437     }
00438     animations[anum].faces = malloc(sizeof(uint16)*animations[anum].num_animations);
00439     for (i = 4, j = 0; i < len; i += 2, j++) {
00440         animations[anum].faces[j] = GetShort_String(data+i);
00441     }
00442 
00443     if (j != animations[anum].num_animations) {
00444         LOG(LOG_WARNING, "common::AnimCmd",
00445             "Calculated animations does not equal stored animations? (%d!=%d)",
00446             j, animations[anum].num_animations);
00447     }
00448 
00449     animations[anum].speed = 0;
00450     animations[anum].speed_left = 0;
00451     animations[anum].phase = 0;
00452 
00453     LOG(LOG_DEBUG, "common::AnimCmd", "Received animation %d, %d faces", anum, animations[anum].num_animations);
00454 }
00455 
00456 /* This receives the smooth mapping from the server.  Because this
00457  * information is reference a lot, the smoothing face is stored
00458  * in the pixmap data - this makes access much faster than searching
00459  * an array of data for the face to use.
00460  */
00461 void SmoothCmd(unsigned char *data, int len) {
00462     uint16 faceid;
00463     uint16 smoothing;
00464 
00465     /* len is unused.
00466      * We should check that we don't have an invalid short command.
00467      * Hence, the compiler warning is valid.
00468      */
00469 
00470     faceid = GetShort_String(data);
00471     smoothing = GetShort_String(data+2);
00472     addsmooth(faceid, smoothing);
00473 }
00474 
00475 void DrawInfoCmd(char *data, int len) {
00476     int color = atoi(data);
00477     char *buf;
00478 
00479     (void)len; /* __UNUSED__ */
00480 
00481     buf = strchr(data, ' ');
00482     if (!buf) {
00483         LOG(LOG_WARNING, "common::DrawInfoCmd", "got no data");
00484         buf = "";
00485     } else {
00486         buf++;
00487     }
00488     if (color != NDI_BLACK) {
00489         draw_color_info(color, buf);
00490     } else {
00491         draw_info(buf, NDI_BLACK);
00492     }
00493 }
00494 
00495 TextManager *firstTextManager = NULL;
00496 
00497 void setTextManager(int type, ExtTextManager callback) {
00498     TextManager *current = firstTextManager;
00499 
00500     while (current != NULL) {
00501         if (current->type == type) {
00502             current->callback = callback;
00503             return;
00504         }
00505         current = current->next;
00506     }
00507     current = malloc(sizeof(TextManager));
00508     current->type = type;
00509     current->callback = callback;
00510     current->next = firstTextManager;
00511     firstTextManager = current;
00512 }
00513 
00514 static ExtTextManager getTextManager(int type) {
00515     TextManager *current = firstTextManager;
00516     while (current != NULL) {
00517         if (current->type == type) {
00518             return current->callback;
00519         }
00520         current = current->next;
00521     }
00522     return NULL;
00523 }
00524 
00525 /* We must extract color, type, subtype and dispatch to callback*/
00526 void DrawExtInfoCmd(char *data, int len) {
00527     int color;
00528     int type, subtype;
00529     char *buf = data;
00530     int wordCount = 3;
00531     ExtTextManager fnct;
00532 
00533     while (wordCount > 0) {
00534         while (buf[0] == ' ') {
00535             buf++;
00536         }
00537         wordCount--;
00538         while (buf[0] != ' ') {
00539             if (buf[0] == '\0') {
00540                 LOG(LOG_WARNING,
00541                     "common::DrawExtInfoCmd", "Data is missing %d parameters %s",
00542                     wordCount,
00543                     data);
00544                 return;
00545             } else {
00546                 buf++;
00547             }
00548         }
00549         if (buf[0] == ' ') {
00550             buf++; /*remove trailing space to send clean data to callback */
00551         }
00552     }
00553     wordCount = sscanf(data, "%d %d %d", &color, &type, &subtype);
00554     if (wordCount != 3) {
00555         LOG(LOG_WARNING,
00556             "common::DrawExtInfoCmd", "Wrong parameters received. Could only parse %d out of 3 int in %s",
00557             wordCount,
00558             data);
00559         return;
00560     }
00561     fnct = getTextManager(type);
00562     if (fnct == NULL) {
00563         LOG(LOG_WARNING,
00564             "common::DrawExtInfoCmd", "Server send us a type %d but i can't find any callback for it",
00565             type);
00566         return;
00567     }
00568     fnct(color, type, subtype, buf);
00569 }
00570 
00571 /* Maintain the last_used_skills LRU list for displaying the recently used
00572  * skills first. */
00573 void use_skill(int skill_id)
00574 {
00575    int i = 0;
00576    int next;
00577    int prev = last_used_skills[0];
00578    /*
00579    char buf[100];
00580    sprintf(buf, "use_skill(%d '%s')\n", skill_id, skill_names(skill_id));
00581    draw_info(, NDI_BLUE);
00582    */
00583 
00584    if(last_used_skills[0] == skill_id) return;
00585 
00586    do
00587    {
00588           next = last_used_skills[i+1];
00589           last_used_skills[i+1] = prev;
00590           prev = next;
00591           ++i;
00592    } while(next != skill_id && next >= 0);
00593    last_used_skills[0] = skill_id;
00594 }
00595 
00596 void StatsCmd(unsigned char *data, int len) {
00597     int i = 0, c, redraw = 0;
00598     sint64 last_exp;
00599 
00600     while (i < len) {
00601         c = data[i++];
00602         if (c >= CS_STAT_RESIST_START && c <= CS_STAT_RESIST_END) {
00603             cpl.stats.resists[c-CS_STAT_RESIST_START] = GetShort_String(data+i);
00604             i += 2;
00605             cpl.stats.resist_change = 1;
00606         } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
00607             /* We track to see if the exp has gone from 0 to some total value -
00608              * we do this because the draw logic currently only draws skills where
00609              * the player has exp.  We need to communicate to the draw function
00610              * that it should draw all the players skills.  Using redraw is
00611              * a little overkill, because a lot of the data may not be changing.
00612              * OTOH, such a transition should only happen rarely, not not be a very
00613              * big deal.
00614              */
00615             cpl.stats.skill_level[c-CS_STAT_SKILLINFO] = data[i++];
00616             last_exp = cpl.stats.skill_exp[c-CS_STAT_SKILLINFO];
00617             cpl.stats.skill_exp[c-CS_STAT_SKILLINFO] = GetInt64_String(data+i);
00618                         use_skill(c-CS_STAT_SKILLINFO);
00619             if (last_exp == 0 && cpl.stats.skill_exp[c-CS_STAT_SKILLINFO]) {
00620                 redraw = 1;
00621             }
00622             i += 8;
00623         } else {
00624             switch (c) {
00625             case CS_STAT_HP:      cpl.stats.hp = GetShort_String(data+i); i += 2; break;
00626             case CS_STAT_MAXHP:   cpl.stats.maxhp = GetShort_String(data+i); i += 2; break;
00627             case CS_STAT_SP:      cpl.stats.sp = GetShort_String(data+i); i += 2; break;
00628             case CS_STAT_MAXSP:   cpl.stats.maxsp = GetShort_String(data+i); i += 2; break;
00629             case CS_STAT_GRACE:   cpl.stats.grace = GetShort_String(data+i); i += 2; break;
00630             case CS_STAT_MAXGRACE:cpl.stats.maxgrace = GetShort_String(data+i); i += 2; break;
00631             case CS_STAT_STR:     cpl.stats.Str = GetShort_String(data+i); i += 2; break;
00632             case CS_STAT_INT:     cpl.stats.Int = GetShort_String(data+i); i += 2; break;
00633             case CS_STAT_POW:     cpl.stats.Pow = GetShort_String(data+i); i += 2; break;
00634             case CS_STAT_WIS:     cpl.stats.Wis = GetShort_String(data+i); i += 2; break;
00635             case CS_STAT_DEX:     cpl.stats.Dex = GetShort_String(data+i); i += 2; break;
00636             case CS_STAT_CON:     cpl.stats.Con = GetShort_String(data+i); i += 2; break;
00637             case CS_STAT_CHA:     cpl.stats.Cha = GetShort_String(data+i); i += 2; break;
00638             case CS_STAT_EXP:     cpl.stats.exp = GetInt_String(data+i); i += 4; break;
00639             case CS_STAT_EXP64:   cpl.stats.exp = GetInt64_String(data+i); i += 8; break;
00640             case CS_STAT_LEVEL:   cpl.stats.level = GetShort_String(data+i); i += 2; break;
00641             case CS_STAT_WC:      cpl.stats.wc = GetShort_String(data+i); i += 2; break;
00642             case CS_STAT_AC:      cpl.stats.ac = GetShort_String(data+i); i += 2; break;
00643             case CS_STAT_DAM:     cpl.stats.dam = GetShort_String(data+i); i += 2; break;
00644             case CS_STAT_ARMOUR:  cpl.stats.resists[0] = GetShort_String(data+i); i += 2; break;
00645             case CS_STAT_SPEED:   cpl.stats.speed = GetInt_String(data+i); i += 4; break;
00646             case CS_STAT_FOOD:    cpl.stats.food = GetShort_String(data+i); i += 2; break;
00647             case CS_STAT_WEAP_SP: cpl.stats.weapon_sp = GetInt_String(data+i); i += 4; break;
00648             case CS_STAT_SPELL_ATTUNE:cpl.stats.attuned = GetInt_String(data+i); i += 4; cpl.spells_updated = 1; break;
00649             case CS_STAT_SPELL_REPEL:cpl.stats.repelled = GetInt_String(data+i); i += 4; cpl.spells_updated = 1; break;
00650             case CS_STAT_SPELL_DENY:cpl.stats.denied = GetInt_String(data+i); i += 4; cpl.spells_updated = 1; break;
00651 
00652             case CS_STAT_FLAGS: cpl.stats.flags = GetShort_String(data+i); i += 2; break;
00653             case CS_STAT_WEIGHT_LIM:set_weight_limit(cpl.stats.weight_limit = GetInt_String(data+i)); i += 4; break;
00654 
00655                 /* Skill experience handling */
00656                 /* We make the assumption based on current bindings in the protocol
00657                  * that these skip 2 values and are otherwise in order.
00658                  */
00659             case CS_STAT_SKILLEXP_AGILITY:
00660             case CS_STAT_SKILLEXP_PERSONAL:
00661             case CS_STAT_SKILLEXP_MENTAL:
00662             case CS_STAT_SKILLEXP_PHYSIQUE:
00663             case CS_STAT_SKILLEXP_MAGIC:
00664             case CS_STAT_SKILLEXP_WISDOM:
00665                                   {
00666                                          int skill_id = (c-CS_STAT_SKILLEXP_START)/2;
00667                                    cpl.stats.skill_exp[skill_id] = GetInt_String(data+i);
00668                                    use_skill(skill_id);
00669                 i += 4;
00670                                   }
00671                 break;
00672 
00673             case CS_STAT_SKILLEXP_AGLEVEL:
00674             case CS_STAT_SKILLEXP_PELEVEL:
00675             case CS_STAT_SKILLEXP_MELEVEL:
00676             case CS_STAT_SKILLEXP_PHLEVEL:
00677             case CS_STAT_SKILLEXP_MALEVEL:
00678             case CS_STAT_SKILLEXP_WILEVEL:
00679                 cpl.stats.skill_level[(c-CS_STAT_SKILLEXP_START-1)/2] = GetShort_String(data+i);
00680                 i += 2;
00681                 break;
00682 
00683             case CS_STAT_RANGE: {
00684                 int rlen = data[i++];
00685                 strncpy(cpl.range, (const char*)data+i, rlen);
00686                 cpl.range[rlen] = '\0';
00687                 i += rlen;
00688                 break;
00689             }
00690 
00691             case CS_STAT_TITLE: {
00692                 int rlen = data[i++];
00693                 strncpy(cpl.title, (const char*)data+i, rlen);
00694                 cpl.title[rlen] = '\0';
00695                 i += rlen;
00696                 break;
00697             }
00698 
00699             default:
00700                 LOG(LOG_WARNING, "common::StatsCmd", "Unknown stat number %d", c);
00701 /*              abort();*/
00702                 break;
00703             }
00704         }
00705     }
00706 
00707     if (i > len) {
00708         LOG(LOG_WARNING, "common::StatsCmd", "got stats overflow, processed %d bytes out of %d", i, len);
00709     }
00710     draw_stats(redraw);
00711     draw_message_window(0);
00712 #ifdef HAVE_LUA
00713     script_lua_stats();
00714 #endif
00715 }
00716 
00717 void handle_query(char *data, int len) {
00718     char *buf, *cp;
00719     uint8 flags = atoi(data);
00720 
00721     (void)len; /* __UNUSED__ */
00722 
00723     if (flags&CS_QUERY_HIDEINPUT) { /* no echo */
00724         cpl.no_echo = 1;
00725     } else {
00726         cpl.no_echo = 0;
00727     }
00728 
00729     /* Let the window system know this may have changed */
00730     x_set_echo();
00731 
00732     /* The actual text is optional */
00733     buf = strchr(data, ' ');
00734     if (buf) {
00735         buf++;
00736     }
00737 
00738     /* If we just get passed an empty string, why draw this? */
00739     if (buf) {
00740         cp = buf;
00741         while ((buf = strchr(buf, '\n')) != NULL) {
00742             *buf++ = '\0';
00743             draw_info(cp, NDI_BLACK);
00744             cp = buf;
00745         }
00746         /* Yes/no - don't do anything with it now */
00747         if (flags&CS_QUERY_YESNO) {
00748         }
00749 
00750         /* one character response expected */
00751         if (flags&CS_QUERY_SINGLECHAR) {
00752             cpl.input_state = Reply_One;
00753         } else {
00754             cpl.input_state = Reply_Many;
00755         }
00756 
00757         if (cp) {
00758             draw_prompt(cp);
00759         }
00760     }
00761 
00762     LOG(LOG_DEBUG, "common::handle_query", "Received query.  Input state now %d", cpl.input_state);
00763 }
00764 
00765 /* Sends a reply to the server.  text contains the null terminated
00766  * string of text to send.  This function basically just packs
00767  * the stuff up.
00768  */
00769 void send_reply(const char *text) {
00770     cs_print_string(csocket.fd, "reply %s", text);
00771 
00772     /* Let the window system know that the (possibly hidden) query is over. */
00773     cpl.no_echo = 0;
00774     x_set_echo();
00775 }
00776 
00777 /* This function copies relevant data from the archetype to the
00778  * object.  Only copies data that was not set in the object
00779  * structure.
00780  *
00781  */
00782 void PlayerCmd(unsigned char *data, int len) {
00783     char name[MAX_BUF];
00784     int tag, weight, face, i = 0, nlen;
00785 
00786     reset_player_data();
00787     tag = GetInt_String(data); i += 4;
00788     weight = GetInt_String(data+i); i += 4;
00789     face = GetInt_String(data+i); i += 4;
00790     nlen = data[i++];
00791     memcpy(name, (const char*)data+i, nlen);
00792     name[nlen] = '\0';
00793     i += nlen;
00794 
00795     if (i != len) {
00796         LOG(LOG_WARNING, "common::PlayerCmd", "lengths do not match (%d!=%d)", len, i);
00797     }
00798     new_player(tag, name, weight, face);
00799 }
00800 
00801 void item_actions(item *op) {
00802     if (!op) {
00803         return;
00804     }
00805 
00806     if (op->open) {
00807         open_container(op);
00808         cpl.container = op;
00809     } else if (op->was_open) {
00810         close_container(op);
00811         cpl.container = NULL;
00812     }
00813 }
00814 
00815 /* common_item_cmd parses the data send to us from the server.
00816  * revision is what item command the data came from - newer
00817  * ones have addition fields.
00818  */
00819 static void common_item_command(uint8 *data, int len) {
00820 
00821     int weight, loc, tag, face, flags, pos = 0, nlen, anim, nrof, type;
00822     uint8 animspeed;
00823     char name[MAX_BUF];
00824 
00825     loc = GetInt_String(data);
00826     pos += 4;
00827 
00828     if (pos == len) {
00829         LOG(LOG_WARNING, "common::common_item_command", "Got location with no other data");
00830         return;
00831     } else if (loc < 0) { /* delete following items */
00832         LOG(LOG_WARNING, "common::common_item_command", "Got location with negative value (%d)", loc);
00833         return;
00834     } else {
00835         while (pos < len) {
00836             tag = GetInt_String(data+pos); pos += 4;
00837             flags = GetInt_String(data+pos); pos += 4;
00838             weight = GetInt_String(data+pos); pos += 4;
00839             face = GetInt_String(data+pos); pos += 4;
00840             nlen = data[pos++];
00841             memcpy(name, (char*)data+pos, nlen);
00842             pos += nlen;
00843             name[nlen] = '\0';
00844             anim = GetShort_String(data+pos); pos += 2;
00845             animspeed = data[pos++];
00846             nrof = GetInt_String(data+pos); pos += 4;
00847             type = GetShort_String(data+pos); pos += 2;
00848             update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, type);
00849             item_actions(locate_item(tag));
00850         }
00851         if (pos > len) {
00852             LOG(LOG_WARNING, "common::common_item_cmd", "Overread buffer: %d > %d", pos, len);
00853         }
00854     }
00855 }
00856 
00857 void Item2Cmd(unsigned char *data, int len) {
00858     common_item_command(data, len);
00859 }
00860 
00861 /* UpdateItemCmd updates some attributes of an item */
00862 void UpdateItemCmd(unsigned char *data, int len) {
00863     int weight, loc, tag, face, sendflags, flags, pos = 0, nlen, anim;
00864     uint32 nrof;
00865     char name[MAX_BUF];
00866     item *ip, *env = NULL;
00867     uint8 animspeed;
00868 
00869     sendflags = data[0];
00870     pos += 1;
00871     tag = GetInt_String(data+pos);
00872     pos += 4;
00873     ip = locate_item(tag);
00874     if (!ip) {
00875 /*
00876         fprintf(stderr, "Got update_item command for item we don't have (%d)\n", tag);
00877 */
00878         return;
00879     }
00880 
00881     /* Copy all of these so we can pass the values to update_item and
00882      * don't need to figure out which ones were modified by this function.
00883      */
00884     *name = '\0';
00885     loc = ip->env ? ip->env->tag : 0;
00886     weight = ip->weight*1000;
00887     face = ip->face;
00888     flags = ip->flagsval;
00889     anim = ip->animation_id;
00890     animspeed = ip->anim_speed;
00891     nrof = ip->nrof;
00892 
00893     if (sendflags&UPD_LOCATION) {
00894         loc = GetInt_String(data+pos);
00895         env = locate_item(loc);
00896         LOG(LOG_WARNING, "common::UpdateItemCmd", "Got tag of unknown object (%d) for new location", loc);
00897         pos += 4;
00898     }
00899     if (sendflags&UPD_FLAGS) {
00900         flags = GetInt_String(data+pos);
00901         pos += 4;
00902     }
00903     if (sendflags&UPD_WEIGHT) {
00904         weight = GetInt_String(data+pos);
00905         pos += 4;
00906     }
00907     if (sendflags&UPD_FACE) {
00908         face = GetInt_String(data+pos);
00909         pos += 4;
00910     }
00911     if (sendflags&UPD_NAME) {
00912         nlen = data[pos++];
00913         memcpy(name, (char*)data+pos, nlen);
00914         pos += nlen;
00915         name[nlen] = '\0';
00916     }
00917     if (pos > len) {
00918         LOG(LOG_WARNING, "common::UpdateItemCmd", "Overread buffer: %d > %d", pos, len);
00919         return; /* we have bad data, probably don't want to store it then */
00920     }
00921     if (sendflags&UPD_ANIM) {
00922         anim = GetShort_String(data+pos);
00923         pos += 2;
00924     }
00925     if (sendflags&UPD_ANIMSPEED) {
00926         animspeed = data[pos++];
00927     }
00928     if (sendflags&UPD_NROF) {
00929         nrof = (uint32)GetInt_String(data+pos);
00930         pos += 4;
00931     }
00932     /* update_item calls set_item_values which will then set the list
00933      * redraw flag, so we don't need to do an explicit redraw here.  Actually,
00934      * calling update_item is a little bit of overkill, since we
00935      * already determined some of the values in this function.
00936      */
00937     update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, ip->type);
00938     item_actions(locate_item(tag));
00939 }
00940 
00941 void DeleteItem(unsigned char *data, int len) {
00942     int pos = 0, tag;
00943 
00944     while (pos < len) {
00945         item *op;
00946 
00947         tag = GetInt_String(data+pos); pos += 4;
00948         op = locate_item(tag);
00949         if (op != NULL) {
00950             remove_item(op);
00951         } else {
00952             LOG(LOG_WARNING, "common::DeleteItem", "Cannot find tag %d", tag);
00953         }
00954     }
00955     if (pos > len) {
00956         LOG(LOG_WARNING, "common::DeleteItem", "Overread buffer: %d > %d", pos, len);
00957     }
00958 }
00959 
00960 void DeleteInventory(unsigned char *data, int len) {
00961     int tag;
00962     item *op;
00963 
00964     (void)len; /* __UNUSED__ */
00965 
00966     tag = atoi((const char*)data);
00967     op = locate_item(tag);
00968     if (op != NULL) {
00969         remove_item_inventory(op);
00970     } else {
00971         LOG(LOG_WARNING, "common::DeleteInventory", "Invalid tag: %d", tag);
00972     }
00973 }
00974 
00975 /******************************************************************************
00976  * Start of spell commands
00977  *****************************************************************************/
00978 
00979 void AddspellCmd(unsigned char *data, int len) {
00980     uint8 nlen;
00981     uint16 mlen, pos = 0;
00982     Spell *newspell, *tmp;
00983 
00984     while (pos < len) {
00985         newspell = calloc(1, sizeof(Spell));
00986         newspell->tag = GetInt_String(data+pos); pos += 4;
00987         newspell->level = GetShort_String(data+pos); pos += 2;
00988         newspell->time = GetShort_String(data+pos); pos += 2;
00989         newspell->sp = GetShort_String(data+pos); pos += 2;
00990         newspell->grace = GetShort_String(data+pos); pos += 2;
00991         newspell->dam = GetShort_String(data+pos); pos += 2;
00992         newspell->skill_number = GetChar_String(data+pos); pos += 1;
00993         newspell->path = GetInt_String(data+pos); pos += 4;
00994         newspell->face = GetInt_String(data+pos); pos += 4;
00995         nlen = GetChar_String(data+pos); pos += 1;
00996         strncpy(newspell->name, (char*)data+pos, nlen); pos += nlen;
00997         newspell->name[nlen] = '\0'; /* to ensure we are null terminated */
00998         mlen = GetShort_String(data+pos); pos += 2;
00999         strncpy(newspell->message, (char*)data+pos, mlen); pos += mlen;
01000         newspell->message[mlen] = '\0'; /* to ensure we are null terminated */
01001         newspell->skill = skill_names[newspell->skill_number-CS_STAT_SKILLINFO];
01002 
01003         /* ok, we're done with putting in data, now to add to the player struct */
01004         if (!cpl.spelldata) {
01005             cpl.spelldata = newspell;
01006         } else {
01007             for (tmp = cpl.spelldata; tmp->next; tmp = tmp->next)
01008                 ;
01009             tmp->next = newspell;
01010         }
01011         /* now we'll check to see if we have more spells */
01012     }
01013     if (pos > len) {
01014         LOG(LOG_WARNING, "common::AddspellCmd", "Overread buffer: %d > %d", pos, len);
01015     }
01016     cpl.spells_updated = 1;
01017 }
01018 
01019 void UpdspellCmd(unsigned char *data, int len) {
01020     int flags, tag, pos = 0;
01021     Spell *tmp;
01022 
01023     if (!cpl.spelldata) {
01024         LOG(LOG_WARNING, "common::UpdspellCmd", "I know no spells to update");
01025         return;
01026     }
01027 
01028     flags = GetChar_String(data+pos); pos += 1;
01029     tag = GetInt_String(data+pos); pos += 4;
01030     for (tmp = cpl.spelldata; tmp && tmp->tag != tag; tmp = tmp->next)
01031         ;
01032     if (!tmp) {
01033         LOG(LOG_WARNING, "common::UpdspellCmd", "Invalid tag: %d", tag);
01034         return;
01035     }
01036     if (flags&UPD_SP_MANA) {
01037         tmp->sp = GetShort_String(data+pos); pos += 2;
01038     }
01039     if (flags&UPD_SP_GRACE) {
01040         tmp->grace = GetShort_String(data+pos); pos += 2;
01041     }
01042     if (flags&UPD_SP_DAMAGE) {
01043         tmp->dam = GetShort_String(data+pos); pos += 2;
01044     }
01045     if (pos > len) {
01046         LOG(LOG_WARNING, "common::UpdspellCmd", "Overread buffer: %d > %d", pos, len);
01047     }
01048     cpl.spells_updated = 1;
01049 }
01050 
01051 void DeleteSpell(unsigned char *data, int len) {
01052     int tag;
01053     Spell *tmp, *target;
01054 
01055     if (!cpl.spelldata) {
01056         LOG(LOG_WARNING, "common::DeleteSpell", "I know no spells to delete");
01057         return;
01058     }
01059 
01060     tag = GetInt_String(data);
01061     /* special case, the first spell is the one removed */
01062     if (cpl.spelldata->tag == tag) {
01063         target = cpl.spelldata;
01064         if (target->next) {
01065             cpl.spelldata = target->next;
01066         } else {
01067             cpl.spelldata = NULL;
01068         }
01069         free(target);
01070         return;
01071     }
01072 
01073     for (tmp = cpl.spelldata; tmp->next && tmp->next->tag != tag; tmp = tmp->next)
01074         ;
01075     if (!tmp->next) {
01076         LOG(LOG_WARNING, "common::DeleteSpell", "Invalid tag: %d", tag);
01077         return;
01078     }
01079     target = tmp->next;
01080     if (target->next) {
01081         tmp->next = target->next;
01082     } else {
01083         tmp->next = NULL;
01084     }
01085     free(target);
01086     cpl.spells_updated = 1;
01087 }
01088 
01089 /******************************************************************************
01090  * Start of map commands
01091  *****************************************************************************/
01092 
01093 void NewmapCmd(unsigned char *data, int len) {
01094     (void)data; /* __UNUSED__ */
01095     (void)len; /* __UNUSED__ */
01096 
01097     mapdata_newmap();
01098 }
01099 
01100 /* This is the common processing block for the map1 and
01101  * map1a protocol commands.  The map1a mieks minor extensions
01102  * and are easy to deal with inline (in fact, this code
01103  * doesn't even care what rev is - just certain bits will
01104  * only bet set when using the map1a command.
01105  * rev is 0 for map1,
01106  * 1 for map1a.  It conceivable that there could be future
01107  * revisions.
01108  */
01109 
01110 /* NUM_LAYERS should only be used for the map1{a} which only
01111  * has a few layers.  Map2 has 10 layers.  However, some of the
01112  * map1 logic requires this to be set right.
01113  */
01114 #define NUM_LAYERS (MAP1_LAYERS-1)
01115 
01116 void Map2Cmd(unsigned char *data, int len) {
01117     int mask, x, y, pos = 0, space_len, value;
01118     uint8 type;
01119 
01120     display_map_startupdate();
01121     /* Not really using map1 protocol, but some draw logic differs from
01122      * the original draw logic, and map2 is closest.
01123      */
01124     while (pos < len) {
01125         mask = GetShort_String(data+pos); pos += 2;
01126         x = ((mask>>10)&0x3f)-MAP2_COORD_OFFSET;
01127         y = ((mask>>4)&0x3f)-MAP2_COORD_OFFSET;
01128 
01129         /* This is a scroll then.  Go back and fetch another coordinate */
01130         if (mask&0x1) {
01131             mapdata_scroll(x, y);
01132             continue;
01133         }
01134 
01135         if (x<0) {
01136             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative x!");
01137             x = 0;
01138         } else if (x >= MAX_VIEW) {
01139             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got x >= MAX_VIEW!");
01140             x = MAX_VIEW - 1;
01141         }
01142 
01143         if (y<0) {
01144             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative y!");
01145             y = 0;
01146         } else if (y >= MAX_VIEW) {
01147             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got y >= MAX_VIEW!");
01148             y = MAX_VIEW - 1;
01149         }
01150 
01151         assert(0 <= x && x < MAX_VIEW);
01152         assert(0 <= y && y < MAX_VIEW);
01153         /* Clear the old cell data if needed. Used to be done in
01154          * mapdata_set_face_layer() however that caused darkness to only
01155          * work if sent after the layers.
01156          */
01157         mapdata_clear_old(x, y);
01158 
01159         /* Inner loop is for the data on the space itself */
01160         while (pos < len) {
01161             type = data[pos++];
01162             /* type == 255 means nothing more for this space */
01163             if (type == 255) {
01164                 mapdata_set_check_space(x, y);
01165                 break;
01166             }
01167             space_len = type>>5;
01168             type &= 0x1f;
01169             /* Clear the space */
01170             if (type == 0) {
01171                 mapdata_clear_space(x, y);
01172                 continue;
01173             } else if (type == 1) {
01174                 value = data[pos++];
01175                 mapdata_set_darkness(x, y, value);
01176                 continue;
01177             } else if (type >= 0x10 && type <= 0x1a) {
01178                 int layer, opt;
01179 
01180                 /* This is face information for a layer. */
01181                 layer = type&0xf;
01182 
01183                 if (layer < 0) {
01184                     LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative layer!");
01185                     layer = 0;
01186                 } else if (layer >= MAXLAYERS) {
01187                     LOG(LOG_WARNING, "commands.c::Map2Cmd", "got layer >= MAXLAYERS!");
01188                     layer = MAXLAYERS - 1;
01189                 }
01190                 assert(0 <= layer && layer < MAXLAYERS);
01191 
01192                 /* This is the face */
01193                 value = GetShort_String(data+pos); pos += 2;
01194                 if (!(value&FACE_IS_ANIM)) {
01195                     mapdata_set_face_layer(x, y, value, layer);
01196                 }
01197 
01198                 if (space_len > 2) {
01199                     opt = data[pos++];
01200                     if (value&FACE_IS_ANIM) {
01201                         /* Animation speed */
01202                         mapdata_set_anim_layer(x, y, value, opt, layer);
01203                     } else {
01204                         /* Smooth info */
01205                         mapdata_set_smooth(x, y, opt, layer);
01206                     }
01207                 }
01208                 /* Currently, if 4 bytes, must be a smooth byte */
01209                 if (space_len > 3) {
01210                     opt = data[pos++];
01211                     mapdata_set_smooth(x, y, opt, layer);
01212                 }
01213                 continue;
01214             } /* if image layer */
01215         } /* while pos<len inner loop for space */
01216     } /* While pos<len outer loop */
01217     mapupdatesent = 0;
01218     display_map_doneupdate(FALSE, FALSE);
01219 }
01220 
01221 void map_scrollCmd(char *data, int len) {
01222     int dx, dy;
01223     char *buf;
01224 
01225     (void)len; /* __UNUSED__ */
01226 
01227     dx = atoi(data);
01228     buf = strchr(data, ' ');
01229     if (!buf) {
01230         LOG(LOG_WARNING, "common::map_scrollCmd", "Got short packet.");
01231         return;
01232     }
01233     buf++;
01234     dy = atoi(buf);
01235 
01236     display_map_startupdate();
01237     mapdata_scroll(dx, dy);
01238     display_map_doneupdate(FALSE, TRUE);
01239 }
01240 
01241 /* Extract smoothing infos from an extendedmapinfo packet part
01242  * data is located at the beginning of the smooth datas
01243  */
01244 int ExtSmooth(unsigned char *data, int len, int x, int y, int layer) {
01245     static int dx[8] = { 0, 1, 1, 1, 0, -1, -1, -1, };
01246     static int dy[8] = { -1, -1, 0, 1, 1, 1, 0, -1, };
01247     int i, rx, ry;
01248     int newsm;
01249 
01250     if (len < 1) {
01251         return 0;
01252     }
01253 
01254     x += pl_pos.x;
01255     y += pl_pos.y;
01256     newsm = GetChar_String(data);
01257 
01258     if (the_map.cells[x][y].smooth[layer] != newsm) {
01259         for (i = 0; i < 8; i++) {
01260             rx = x+dx[i];
01261             ry = y+dy[i];
01262             if (rx < 0 || ry < 0 || the_map.x <= rx || the_map.y <= ry) {
01263                 continue;
01264             }
01265             the_map.cells[x][y].need_resmooth = 1;
01266         }
01267     }
01268     the_map.cells[x][y].smooth[layer] = newsm;
01269     return 1;/*Cause smooth infos only use 1 byte*/
01270 }
01271 
01272 /* Handle MapExtended command
01273  * Warning! if you add commands to extended, take
01274  * care that the 'layer' argument of main loop is
01275  * the opposite of the layer of the map.
01276  * so if you reference a layer, use NUM_LAYERS-layer
01277  */
01278 void MapExtendedCmd(unsigned char *data, int len) {
01279     int mask, x, y, pos = 0, layer;
01280     int noredraw = 0;
01281     int hassmooth = 0;
01282     int entrysize;
01283     int startpackentry;
01284 
01285     if (!mapupdatesent) {
01286         display_map_startupdate();
01287     }
01288     mapupdatesent = 1;
01289     mask = GetChar_String(data+pos); pos += 1;
01290     if (mask&EMI_NOREDRAW) {
01291         noredraw = 1;
01292     }
01293     if (mask&EMI_SMOOTH) {
01294         hassmooth = 1;
01295     }
01296     while (mask&EMI_HASMOREBITS) {
01297         /*There may be bits we ignore about*/
01298         mask = GetChar_String(data+pos);
01299         pos += 1;
01300     }
01301     entrysize = GetChar_String(data+pos);
01302     pos = pos+1;
01303 
01304     while (pos+entrysize+2 <= len) {
01305         mask = GetShort_String(data+pos); pos += 2;
01306         x = (mask>>10)&0x3f;
01307         y = (mask>>4)&0x3f;
01308         for (layer = NUM_LAYERS; layer >= 0; layer--) {
01309             if (mask&(1<<layer)) {
01310                 /*handle an entry*/
01311                 if (pos+entrysize > len) { /*erroneous packet*/
01312                     break;
01313                 }
01314                 startpackentry = pos;
01315                 /* If you had extended infos to the server, this
01316                  * is where, in the client, you may add your code
01317                  */
01318                 if (hassmooth) {
01319                     pos = pos+ExtSmooth(data+pos, len-pos, x, y, NUM_LAYERS-layer);
01320                 }
01321                 /* continue with other if you add new extended
01322                  * infos to server
01323                  */
01324 
01325                 /* Now point to the next data */
01326                 pos = startpackentry+entrysize;
01327             }
01328         }
01329     }
01330     if (!noredraw) {
01331         display_map_doneupdate(FALSE, FALSE);
01332         mapupdatesent = 0;
01333     }
01334 }
01335 
01336 void MagicMapCmd(unsigned char *data, int len) {
01337     unsigned char *cp;
01338     int i;
01339 
01340     /* First, extract the size/position information. */
01341     if (sscanf((const char*)data, "%hd %hd %hd %hd", &cpl.mmapx, &cpl.mmapy, &cpl.pmapx, &cpl.pmapy) != 4) {
01342         LOG(LOG_WARNING, "common::MagicMapCmd", "Was not able to properly extract magic map size, pos");
01343         return;
01344     }
01345 
01346     /* Now we need to find the start of the actual data.  There are 4
01347      * space characters we need to skip over.
01348      */
01349     for (cp = data, i = 0; i < 4 && cp < data+len; cp++) {
01350         if (*cp == ' ') {
01351             i++;
01352         }
01353     }
01354     if (i != 4) {
01355         LOG(LOG_WARNING, "common::MagicMapCmd", "Was unable to find start of magic map data");
01356         return;
01357     }
01358     i = len-(cp-data); /* This should be the number of bytes left */
01359     if (i != cpl.mmapx*cpl.mmapy) {
01360         LOG(LOG_WARNING, "common::MagicMapCmd", "Magic map size mismatch.  Have %d bytes, should have %d",
01361             i, cpl.mmapx*cpl.mmapy);
01362         return;
01363     }
01364     free(cpl.magicmap);
01365     cpl.magicmap = malloc(cpl.mmapx*cpl.mmapy);
01366     /* Order the server puts it in should be just fine.  Note that
01367      * the only requirement that this works is that magicmap by 8 bits,
01368      * being that is the size specified in the protocol and what the
01369      * server sends us.
01370      */
01371     memcpy(cpl.magicmap, cp, cpl.mmapx*cpl.mmapy);
01372     cpl.showmagic = 1;
01373     draw_magic_map();
01374 }
01375 
01376 void SinkCmd(unsigned char *data, int len) {
01377 }
01378 
01379 /* got a tick from the server.  We currently
01380  * don't care what tick number it is, but
01381  * just have the code in case at some time we do.
01382  */
01383 void TickCmd(uint8 *data, int len) {
01384 
01385     tick = GetInt_String(data);
01386 
01387     /* Up to the specific client to decide what to do */
01388     client_tick(tick);
01389 }
01390 
01399 void PickupCmd(uint8 *data, int len) {
01400     uint32 pickup = GetInt_String(data);
01401     client_pickup(pickup);
01402 }