Crossfire Client, Branch
R11627
|
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 }