Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_request_c = 00003 * "$Id: request.c 17660 2012-03-23 19:08:26Z akirschbaum $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 2001-2006 Mark Wedel 00010 Copyright (C) 1992 Frank Tore Johansen 00011 00012 This program is free software; you can redistribute it and/or modify 00013 it under the terms of the GNU General Public License as published by 00014 the Free Software Foundation; either version 2 of the License, or 00015 (at your option) any later version. 00016 00017 This program is distributed in the hope that it will be useful, 00018 but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 GNU General Public License for more details. 00021 00022 You should have received a copy of the GNU General Public License 00023 along with this program; if not, write to the Free Software 00024 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00025 00026 The author can be reached via e-mail to crossfire-devel@real-time.com 00027 */ 00028 00059 #include <assert.h> 00060 #include <global.h> 00061 #include <sproto.h> 00062 00063 #include <newclient.h> 00064 #include <newserver.h> 00065 #include <living.h> 00066 #include <commands.h> 00067 00068 /* This block is basically taken from socket.c - I assume if it works there, 00069 * it should work here. 00070 */ 00071 #ifndef WIN32 /* ---win32 exclude unix headers */ 00072 #include <sys/types.h> 00073 #include <sys/time.h> 00074 #include <sys/socket.h> 00075 #include <netinet/in.h> 00076 #include <netinet/tcp.h> 00077 #include <netdb.h> 00078 #endif /* win32 */ 00079 00080 #ifdef HAVE_UNISTD_H 00081 #include <unistd.h> 00082 #endif 00083 00084 #ifdef HAVE_SYS_TIME_H 00085 #include <sys/time.h> 00086 #endif 00087 00088 #include "sounds.h" 00089 00096 static const short atnr_cs_stat[NROFATTACKS] = { 00097 CS_STAT_RES_PHYS, CS_STAT_RES_MAG, 00098 CS_STAT_RES_FIRE, CS_STAT_RES_ELEC, 00099 CS_STAT_RES_COLD, CS_STAT_RES_CONF, 00100 CS_STAT_RES_ACID, 00101 CS_STAT_RES_DRAIN, -1 /* weaponmagic */, 00102 CS_STAT_RES_GHOSTHIT, CS_STAT_RES_POISON, 00103 CS_STAT_RES_SLOW, CS_STAT_RES_PARA, 00104 CS_STAT_TURN_UNDEAD, 00105 CS_STAT_RES_FEAR, -1 /* Cancellation */, 00106 CS_STAT_RES_DEPLETE, CS_STAT_RES_DEATH, 00107 -1 /* Chaos */, -1 /* Counterspell */, 00108 -1 /* Godpower */, CS_STAT_RES_HOLYWORD, 00109 CS_STAT_RES_BLIND, 00110 -1, /* Internal */ 00111 -1, /* life stealing */ 00112 -1 /* Disease - not fully done yet */ 00113 }; 00114 00116 void set_up_cmd(char *buf, int len, socket_struct *ns) { 00117 int s; 00118 char *cmd, *param; 00119 SockList sl; 00120 00121 /* run through the cmds of setup 00122 * syntax is setup <cmdname1> <parameter> <cmdname2> <parameter> ... 00123 * 00124 * we send the status of the cmd back, or a FALSE is the cmd 00125 * is the server unknown 00126 * The client then must sort this out 00127 */ 00128 00129 LOG(llevInfo, "Get SetupCmd:: %s\n", buf); 00130 SockList_Init(&sl); 00131 SockList_AddString(&sl, "setup"); 00132 for (s = 0; s < len; ) { 00133 cmd = &buf[s]; 00134 00135 /* find the next space, and put a null there */ 00136 for (; buf[s] && buf[s] != ' '; s++) 00137 ; 00138 if (s >= len) 00139 break; 00140 buf[s++] = 0; 00141 00142 while (buf[s] == ' ') 00143 s++; 00144 if (s >= len) 00145 break; 00146 param = &buf[s]; 00147 00148 for (; buf[s] && buf[s] != ' '; s++) 00149 ; 00150 buf[s++] = 0; 00151 00152 while (s < len && buf[s] == ' ') 00153 s++; 00154 00155 SockList_AddPrintf(&sl, " %s ", cmd); 00156 00157 if (!strcmp(cmd, "sound")) { 00158 /* this is the old sound command, which means the client doesn't understand our sound => mute. */ 00159 ns->sound = 0; 00160 SockList_AddString(&sl, "FALSE"); 00161 } else if (!strcmp(cmd, "sound2")) { 00162 ns->sound = atoi(param)&(SND_EFFECTS|SND_MUSIC|SND_MUTE); 00163 SockList_AddString(&sl, param); 00164 } else if (!strcmp(cmd, "exp64")) { 00165 /* for compatibility, return 1 since older clients can be confused else. */ 00166 SockList_AddString(&sl, "1"); 00167 } else if (!strcmp(cmd, "spellmon")) { 00168 int monitor_spells; 00169 00170 monitor_spells = atoi(param); 00171 if (monitor_spells != 0 && monitor_spells != 1) { 00172 SockList_AddString(&sl, "FALSE"); 00173 } else { 00174 ns->monitor_spells = monitor_spells; 00175 SockList_AddPrintf(&sl, "%d", monitor_spells); 00176 } 00177 } else if (!strcmp(cmd, "darkness")) { 00178 int darkness; 00179 00180 darkness = atoi(param); 00181 if (darkness != 0 && darkness != 1) { 00182 SockList_AddString(&sl, "FALSE"); 00183 } else { 00184 ns->darkness = darkness; 00185 SockList_AddPrintf(&sl, "%d", darkness); 00186 } 00187 } else if (!strcmp(cmd, "map2cmd")) { 00188 int map2cmd; 00189 00190 map2cmd = atoi(param); 00191 if (map2cmd != 1) { 00192 SockList_AddString(&sl, "FALSE"); 00193 } else { 00194 SockList_AddString(&sl, "1"); 00195 } 00196 } else if (!strcmp(cmd, "newmapcmd")) { 00197 int newmapcmd; 00198 00199 newmapcmd = atoi(param); 00200 if (newmapcmd != 0 && newmapcmd != 1) { 00201 SockList_AddString(&sl, "FALSE"); 00202 } else { 00203 ns->newmapcmd = newmapcmd; 00204 SockList_AddPrintf(&sl, "%d", newmapcmd); 00205 } 00206 } else if (!strcmp(cmd, "facecache")) { 00207 int facecache; 00208 00209 facecache = atoi(param); 00210 if (facecache != 0 && facecache != 1) { 00211 SockList_AddString(&sl, "FALSE"); 00212 } else { 00213 ns->facecache = facecache; 00214 SockList_AddPrintf(&sl, "%d", facecache); 00215 } 00216 } else if (!strcmp(cmd, "faceset")) { 00217 int q = atoi(param); 00218 00219 if (is_valid_faceset(q)) 00220 ns->faceset = q; 00221 SockList_AddPrintf(&sl, "%d", ns->faceset); 00222 } else if (!strcmp(cmd, "itemcmd")) { 00223 /* client ignore the value anyway. */ 00224 SockList_AddString(&sl, "2"); 00225 } else if (!strcmp(cmd, "mapsize")) { 00226 int x, y, n; 00227 00228 if (sscanf(param, "%dx%d%n", &x, &y, &n) != 2 || n != (int)strlen(param)) { 00229 x = 0; 00230 y = 0; 00231 } 00232 if (x < 9 || y < 9 || x > MAP_CLIENT_X || y > MAP_CLIENT_Y) { 00233 SockList_AddPrintf(&sl, "%dx%d", MAP_CLIENT_X, MAP_CLIENT_Y); 00234 } else { 00235 ns->mapx = x; 00236 ns->mapy = y; 00237 /* better to send back what we are really using and not the 00238 * param as given to us in case it gets parsed differently. 00239 */ 00240 SockList_AddPrintf(&sl, "%dx%d", x, y); 00241 /* Client and server need to resynchronize on data - treating it as 00242 * a new map is best way to go. 00243 */ 00244 map_newmap_cmd(ns); 00245 } 00246 } else if (!strcmp(cmd, "extendedMapInfos")) { 00247 SockList_AddString(&sl, "1"); 00248 } else if (!strcmp(cmd, "extendedTextInfos")) { 00249 int has_readable_type; 00250 00251 has_readable_type = atoi(param); 00252 if (has_readable_type != 0 && has_readable_type != 1) { 00253 SockList_AddString(&sl, "FALSE"); 00254 } else { 00255 ns->has_readable_type = has_readable_type; 00256 SockList_AddPrintf(&sl, "%d", has_readable_type); 00257 } 00258 } else if (!strcmp(cmd, "tick")) { 00259 int tick; 00260 00261 tick = atoi(param); 00262 if (tick != 0 && tick != 1) { 00263 SockList_AddString(&sl, "FALSE"); 00264 } else { 00265 ns->tick = tick; 00266 SockList_AddPrintf(&sl, "%d", tick); 00267 } 00268 } else if (!strcmp(cmd, "bot")) { 00269 int is_bot; 00270 00271 is_bot = atoi(param); 00272 if (is_bot != 0 && is_bot != 1) { 00273 SockList_AddString(&sl, "FALSE"); 00274 } else { 00275 ns->is_bot = is_bot; 00276 SockList_AddPrintf(&sl, "%d", is_bot); 00277 } 00278 } else if (!strcmp(cmd, "want_pickup")) { 00279 int want_pickup; 00280 00281 want_pickup = atoi(param); 00282 if (want_pickup != 0 && want_pickup != 1) { 00283 SockList_AddString(&sl, "FALSE"); 00284 } else { 00285 ns->want_pickup = want_pickup; 00286 SockList_AddPrintf(&sl, "%d", want_pickup); 00287 } 00288 } else if (!strcmp(cmd, "inscribe")) { 00289 SockList_AddString(&sl, "1"); 00290 } else if (!strcmp(cmd, "num_look_objects")) { 00291 int tmp; 00292 00293 tmp = atoi(param); 00294 if (tmp < MIN_NUM_LOOK_OBJECTS) { 00295 tmp = MIN_NUM_LOOK_OBJECTS; 00296 } else if (tmp > MAX_NUM_LOOK_OBJECTS) { 00297 tmp = MAX_NUM_LOOK_OBJECTS; 00298 } 00299 ns->num_look_objects = (uint8)tmp; 00300 SockList_AddPrintf(&sl, "%d", tmp); 00301 } else { 00302 /* Didn't get a setup command we understood - 00303 * report a failure to the client. 00304 */ 00305 SockList_AddString(&sl, "FALSE"); 00306 } 00307 } /* for processing all the setup commands */ 00308 Send_With_Handling(ns, &sl); 00309 SockList_Term(&sl); 00310 } 00311 00320 void add_me_cmd(char *buf, int len, socket_struct *ns) { 00321 Settings oldsettings; 00322 SockList sl; 00323 00324 oldsettings = settings; 00325 if (ns->status != Ns_Add) { 00326 SockList_Init(&sl); 00327 SockList_AddString(&sl, "addme_failed"); 00328 Send_With_Handling(ns, &sl); 00329 SockList_Term(&sl); 00330 } else { 00331 add_player(ns); 00332 /* Basically, the add_player copies the socket structure into 00333 * the player structure, so this one (which is from init_sockets) 00334 * is not needed anymore. The write below should still work, 00335 * as the stuff in ns is still relevant. 00336 */ 00337 SockList_Init(&sl); 00338 SockList_AddString(&sl, "addme_success"); 00339 Send_With_Handling(ns, &sl); 00340 SockList_Term(&sl); 00341 if (ns->sc_version < 1027 || ns->cs_version < 1023) { 00342 /* The space in the link isn't correct, but in my 00343 * quick test with client 1.1.0, it didn't print it 00344 * out correctly when done as a single line. 00345 */ 00346 SockList_Init(&sl); 00347 SockList_AddString(&sl, "drawinfo 3 Warning: Your client is too old to receive map data. Please update to a new client at http://sourceforge.net/project/showfiles.php ?group_id=13833"); 00348 Send_With_Handling(ns, &sl); 00349 SockList_Term(&sl); 00350 } 00351 00352 socket_info.nconns--; 00353 ns->status = Ns_Avail; 00354 } 00355 settings = oldsettings; 00356 } 00357 00359 void toggle_extended_infos_cmd(char *buf, int len, socket_struct *ns) { 00360 SockList sl; 00361 char command[50]; 00362 int info, nextinfo, smooth = 0; 00363 00364 nextinfo = 0; 00365 while (1) { 00366 /* 1. Extract an info*/ 00367 info = nextinfo; 00368 while (info < len && buf[info] == ' ') 00369 info++; 00370 if (info >= len) 00371 break; 00372 nextinfo = info+1; 00373 while (nextinfo < len && buf[nextinfo] != ' ') 00374 nextinfo++; 00375 if (nextinfo-info >= 49) /*Erroneous info asked*/ 00376 continue; 00377 strncpy(command, &buf[info], nextinfo-info); 00378 command[nextinfo-info] = '\0'; 00379 /* 2. Interpret info*/ 00380 if (!strcmp("smooth", command)) { 00381 /* Toggle smoothing*/ 00382 smooth = 1; 00383 } else { 00384 /*bad value*/ 00385 } 00386 /*3. Next info*/ 00387 } 00388 SockList_Init(&sl); 00389 SockList_AddString(&sl, "ExtendedInfoSet"); 00390 if (smooth) { 00391 SockList_AddString(&sl, " smoothing"); 00392 } 00393 Send_With_Handling(ns, &sl); 00394 SockList_Term(&sl); 00395 } 00396 00398 void toggle_extended_text_cmd(char *buf, int len, socket_struct *ns) { 00399 SockList sl; 00400 char command[50]; 00401 int info, nextinfo, i, flag; 00402 00403 nextinfo = 0; 00404 while (1) { 00405 /* 1. Extract an info*/ 00406 info = nextinfo; 00407 while (info < len && buf[info] == ' ') 00408 info++; 00409 if (info >= len) 00410 break; 00411 nextinfo = info+1; 00412 while (nextinfo < len && buf[nextinfo] != ' ') 00413 nextinfo++; 00414 if (nextinfo-info >= 49) /*Erroneous info asked*/ 00415 continue; 00416 strncpy(command, &buf[info], nextinfo-info); 00417 command[nextinfo-info] = '\0'; 00418 /* 2. Interpret info*/ 00419 i = sscanf(command, "%d", &flag); 00420 if (i == 1 && flag > 0 && flag <= MSG_TYPE_LAST) 00421 ns->supported_readables |= (1<<flag); 00422 /*3. Next info*/ 00423 } 00424 /* Send resulting state */ 00425 SockList_Init(&sl); 00426 SockList_AddString(&sl, "ExtendedTextSet"); 00427 for (i = 0; i <= MSG_TYPE_LAST; i++) 00428 if (ns->supported_readables&(1<<i)) { 00429 SockList_AddPrintf(&sl, " %d", i); 00430 } 00431 Send_With_Handling(ns, &sl); 00432 SockList_Term(&sl); 00433 } 00434 00442 static void send_smooth(socket_struct *ns, uint16 face) { 00443 uint16 smoothface; 00444 SockList sl; 00445 00446 /* If we can't find a face, return and set it so we won't 00447 * try to send this again. 00448 */ 00449 if (!find_smooth(face, &smoothface) 00450 && !find_smooth(smooth_face->number, &smoothface)) { 00451 LOG(llevError, "could not findsmooth for %d. Neither default (%s)\n", face, smooth_face->name); 00452 ns->faces_sent[face] |= NS_FACESENT_SMOOTH; 00453 return; 00454 } 00455 00456 if (!(ns->faces_sent[smoothface]&NS_FACESENT_FACE)) 00457 esrv_send_face(ns, smoothface, 0); 00458 00459 ns->faces_sent[face] |= NS_FACESENT_SMOOTH; 00460 00461 SockList_Init(&sl); 00462 SockList_AddString(&sl, "smooth "); 00463 SockList_AddShort(&sl, face); 00464 SockList_AddShort(&sl, smoothface); 00465 Send_With_Handling(ns, &sl); 00466 SockList_Term(&sl); 00467 } 00468 00473 void ask_smooth_cmd(char *buf, int len, socket_struct *ns) { 00474 uint16 facenbr; 00475 00476 facenbr = atoi(buf); 00477 send_smooth(ns, facenbr); 00478 } 00479 00492 void new_player_cmd(uint8 *buf, int len, player *pl) { 00493 int time, repeat; 00494 short packet; 00495 char command[MAX_BUF]; 00496 SockList sl; 00497 00498 if (len < 7) { 00499 LOG(llevDebug, "Corrupt ncom command - not long enough - discarding\n"); 00500 return; 00501 } 00502 00503 packet = GetShort_String(buf); 00504 repeat = GetInt_String(buf+2); 00505 /* -1 is special - no repeat, but don't update */ 00506 if (repeat != -1) { 00507 pl->count = repeat; 00508 } 00509 if (len-4 >= MAX_BUF) 00510 len = MAX_BUF-5; 00511 00512 strncpy(command, (char *)buf+6, len-4); 00513 command[len-4] = '\0'; 00514 00515 /* The following should never happen with a proper or honest client. 00516 * Therefore, the error message doesn't have to be too clear - if 00517 * someone is playing with a hacked/non working client, this gives them 00518 * an idea of the problem, but they deserve what they get 00519 */ 00520 if (pl->state != ST_PLAYING) { 00521 draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR, 00522 "You can not issue commands - state is not ST_PLAYING (%s)", 00523 "You can not issue commands - state is not ST_PLAYING (%s)", 00524 buf); 00525 return; 00526 } 00527 00528 /* This should not happen anymore. */ 00529 if (pl->ob->speed_left < -1.0) { 00530 LOG(llevError, "Player has negative time - shouldn't do command.\n"); 00531 } 00532 /* In c_new.c */ 00533 execute_newserver_command(pl->ob, command); 00534 /* Perhaps something better should be done with a left over count. 00535 * Cleaning up the input should probably be done first - all actions 00536 * for the command that issued the count should be done before 00537 * any other commands. 00538 */ 00539 pl->count = 0; 00540 00541 /* Send confirmation of command execution now */ 00542 SockList_Init(&sl); 00543 SockList_AddString(&sl, "comc "); 00544 SockList_AddShort(&sl, packet); 00545 if (FABS(pl->ob->speed) < 0.001) 00546 time = MAX_TIME*100; 00547 else 00548 time = (int)(MAX_TIME/FABS(pl->ob->speed)); 00549 SockList_AddInt(&sl, time); 00550 Send_With_Handling(&pl->socket, &sl); 00551 SockList_Term(&sl); 00552 } 00553 00555 void reply_cmd(char *buf, int len, player *pl) { 00556 /* This is to synthesize how the data would be stored if it 00557 * was normally entered. A bit of a hack, and should be cleaned up 00558 * once all the X11 code is removed from the server. 00559 * 00560 * We pass 13 to many of the functions because this way they 00561 * think it was the carriage return that was entered, and the 00562 * function then does not try to do additional input. 00563 */ 00564 snprintf(pl->write_buf, sizeof(pl->write_buf), ":%s", buf); 00565 00566 /* this avoids any hacking here */ 00567 00568 switch (pl->state) { 00569 case ST_PLAYING: 00570 LOG(llevError, "Got reply message with ST_PLAYING input state\n"); 00571 break; 00572 00573 case ST_PLAY_AGAIN: 00574 /* We can check this for return value (2==quit). Maybe we 00575 * should, and do something appropriate? 00576 */ 00577 receive_play_again(pl->ob, buf[0]); 00578 break; 00579 00580 case ST_ROLL_STAT: 00581 key_roll_stat(pl->ob, buf[0]); 00582 break; 00583 00584 case ST_CHANGE_CLASS: 00585 00586 key_change_class(pl->ob, buf[0]); 00587 break; 00588 00589 case ST_CONFIRM_QUIT: 00590 key_confirm_quit(pl->ob, buf[0]); 00591 break; 00592 00593 case ST_GET_NAME: 00594 receive_player_name(pl->ob); 00595 break; 00596 00597 case ST_GET_PASSWORD: 00598 case ST_CONFIRM_PASSWORD: 00599 case ST_CHANGE_PASSWORD_OLD: 00600 case ST_CHANGE_PASSWORD_NEW: 00601 case ST_CHANGE_PASSWORD_CONFIRM: 00602 receive_player_password(pl->ob); 00603 break; 00604 00605 case ST_GET_PARTY_PASSWORD: /* Get password for party */ 00606 receive_party_password(pl->ob); 00607 break; 00608 00609 default: 00610 LOG(llevError, "Unknown input state: %d\n", pl->state); 00611 } 00612 } 00613 00621 void version_cmd(char *buf, int len, socket_struct *ns) { 00622 char *cp; 00623 00624 if (!buf) { 00625 LOG(llevError, "CS: received corrupted version command\n"); 00626 return; 00627 } 00628 00629 ns->cs_version = atoi(buf); 00630 ns->sc_version = ns->cs_version; 00631 if (VERSION_CS != ns->cs_version) { 00632 #ifdef ESRV_DEBUG 00633 LOG(llevDebug, "CS: csversion mismatch (%d,%d)\n", VERSION_CS, ns->cs_version); 00634 #endif 00635 } 00636 cp = strchr(buf+1, ' '); 00637 if (!cp) 00638 return; 00639 ns->sc_version = atoi(cp); 00640 if (VERSION_SC != ns->sc_version) { 00641 #ifdef ESRV_DEBUG 00642 LOG(llevDebug, "CS: scversion mismatch (%d,%d)\n", VERSION_SC, ns->sc_version); 00643 #endif 00644 } 00645 cp = strchr(cp+1, ' '); 00646 if (cp) { 00647 LOG(llevDebug, "CS: connection from client of type <%s>, ip %s\n", cp, ns->host); 00648 } 00649 } 00650 00656 void set_sound_cmd(char *buf, int len, socket_struct *ns) { 00657 } 00658 00662 void map_redraw_cmd(char *buf, int len, player *pl) { 00663 /* This function is currently disabled; just clearing the 00664 * map state results in display errors. It should clear the 00665 * cache and send a newmap command. Unfortunately this 00666 * solution does not work because some client versions send 00667 * a mapredraw command after receiving a newmap command. 00668 */ 00669 } 00670 00672 void map_newmap_cmd(socket_struct *ns) { 00673 /* If getting a newmap command, this scroll information 00674 * is no longer relevant. 00675 */ 00676 ns->map_scroll_x = 0; 00677 ns->map_scroll_y = 0; 00678 00679 if (ns->newmapcmd == 1) { 00680 SockList sl; 00681 00682 memset(&ns->lastmap, 0, sizeof(ns->lastmap)); 00683 SockList_Init(&sl); 00684 SockList_AddString(&sl, "newmap"); 00685 Send_With_Handling(ns, &sl); 00686 SockList_Term(&sl); 00687 } 00688 } 00689 00694 void move_cmd(char *buf, int len, player *pl) { 00695 int vals[3], i; 00696 00697 /* A little funky here. We only cycle for 2 records, because 00698 * we obviously am not going to find a space after the third 00699 * record. Perhaps we should just replace this with a 00700 * sscanf? 00701 */ 00702 for (i = 0; i < 2; i++) { 00703 vals[i] = atoi(buf); 00704 if (!(buf = strchr(buf, ' '))) { 00705 LOG(llevError, "Incomplete move command: %s\n", buf); 00706 return; 00707 } 00708 buf++; 00709 } 00710 vals[2] = atoi(buf); 00711 00712 /* LOG(llevDebug, "Move item %d (nrof=%d) to %d.\n", vals[1], vals[2], vals[0]);*/ 00713 esrv_move_object(pl->ob, vals[0], vals[1], vals[2]); 00714 } 00715 00716 /*************************************************************************** 00717 * 00718 * Start of commands the server sends to the client. 00719 * 00720 *************************************************************************** 00721 */ 00722 00727 void send_query(socket_struct *ns, uint8 flags, const char *text) { 00728 SockList sl; 00729 00730 SockList_Init(&sl); 00731 SockList_AddPrintf(&sl, "query %d %s", flags, text ? text : ""); 00732 Send_With_Handling(ns, &sl); 00733 SockList_Term(&sl); 00734 } 00735 00736 #define AddIfInt64(Old, New, Type) \ 00737 if (Old != New) { \ 00738 Old = New; \ 00739 SockList_AddChar(&sl, Type); \ 00740 SockList_AddInt64(&sl, New); \ 00741 } 00742 00743 #define AddIfInt(Old, New, Type) \ 00744 if (Old != New) { \ 00745 Old = New; \ 00746 SockList_AddChar(&sl, Type); \ 00747 SockList_AddInt(&sl, New); \ 00748 } 00749 00750 #define AddIfShort(Old, New, Type) \ 00751 if (Old != New) { \ 00752 Old = New; \ 00753 SockList_AddChar(&sl, Type); \ 00754 SockList_AddShort(&sl, New); \ 00755 } 00756 00757 #define AddIfFloat(Old, New, Type) \ 00758 if (Old != New) { \ 00759 Old = New; \ 00760 SockList_AddChar(&sl, Type); \ 00761 SockList_AddInt(&sl, (long)(New*FLOAT_MULTI));\ 00762 } 00763 00764 #define AddIfString(Old, New, Type) \ 00765 if (Old == NULL || strcmp(Old, New)) { \ 00766 if (Old) \ 00767 free(Old); \ 00768 Old = strdup_local(New); \ 00769 SockList_AddChar(&sl, Type); \ 00770 SockList_AddLen8Data(&sl, New, strlen(New));\ 00771 } 00772 00779 void esrv_update_stats(player *pl) { 00780 SockList sl; 00781 char buf[MAX_BUF]; 00782 uint16 flags; 00783 uint8 s; 00784 00785 SockList_Init(&sl); 00786 SockList_AddString(&sl, "stats "); 00787 00788 if (pl->ob != NULL) { 00789 AddIfShort(pl->last_stats.hp, pl->ob->stats.hp, CS_STAT_HP); 00790 AddIfShort(pl->last_stats.maxhp, pl->ob->stats.maxhp, CS_STAT_MAXHP); 00791 AddIfShort(pl->last_stats.sp, pl->ob->stats.sp, CS_STAT_SP); 00792 AddIfShort(pl->last_stats.maxsp, pl->ob->stats.maxsp, CS_STAT_MAXSP); 00793 AddIfShort(pl->last_stats.grace, pl->ob->stats.grace, CS_STAT_GRACE); 00794 AddIfShort(pl->last_stats.maxgrace, pl->ob->stats.maxgrace, CS_STAT_MAXGRACE); 00795 AddIfShort(pl->last_stats.Str, pl->ob->stats.Str, CS_STAT_STR); 00796 AddIfShort(pl->last_stats.Int, pl->ob->stats.Int, CS_STAT_INT); 00797 AddIfShort(pl->last_stats.Pow, pl->ob->stats.Pow, CS_STAT_POW); 00798 AddIfShort(pl->last_stats.Wis, pl->ob->stats.Wis, CS_STAT_WIS); 00799 AddIfShort(pl->last_stats.Dex, pl->ob->stats.Dex, CS_STAT_DEX); 00800 AddIfShort(pl->last_stats.Con, pl->ob->stats.Con, CS_STAT_CON); 00801 AddIfShort(pl->last_stats.Cha, pl->ob->stats.Cha, CS_STAT_CHA); 00802 } 00803 00804 for (s = 0; s < NUM_SKILLS; s++) { 00805 if (pl->last_skill_ob[s] 00806 && pl->last_skill_exp[s] != pl->last_skill_ob[s]->stats.exp) { 00807 /* Always send along the level if exp changes. This 00808 * is only 1 extra byte, but keeps processing simpler. 00809 */ 00810 SockList_AddChar(&sl, (char)(s+CS_STAT_SKILLINFO)); 00811 SockList_AddChar(&sl, (char)pl->last_skill_ob[s]->level); 00812 SockList_AddInt64(&sl, pl->last_skill_ob[s]->stats.exp); 00813 pl->last_skill_exp[s] = pl->last_skill_ob[s]->stats.exp; 00814 } 00815 } 00816 AddIfInt64(pl->last_stats.exp, pl->ob->stats.exp, CS_STAT_EXP64); 00817 AddIfShort(pl->last_level, (char)pl->ob->level, CS_STAT_LEVEL); 00818 AddIfShort(pl->last_stats.wc, pl->ob->stats.wc, CS_STAT_WC); 00819 AddIfShort(pl->last_stats.ac, pl->ob->stats.ac, CS_STAT_AC); 00820 AddIfShort(pl->last_stats.dam, pl->ob->stats.dam, CS_STAT_DAM); 00821 AddIfFloat(pl->last_speed, pl->ob->speed, CS_STAT_SPEED); 00822 AddIfShort(pl->last_stats.food, pl->ob->stats.food, CS_STAT_FOOD); 00823 AddIfFloat(pl->last_weapon_sp, pl->weapon_sp, CS_STAT_WEAP_SP); 00824 AddIfInt(pl->last_weight_limit, (sint32)weight_limit[pl->ob->stats.Str], CS_STAT_WEIGHT_LIM); 00825 flags = 0; 00826 if (pl->fire_on) 00827 flags |= SF_FIREON; 00828 if (pl->run_on) 00829 flags |= SF_RUNON; 00830 00831 AddIfShort(pl->last_flags, flags, CS_STAT_FLAGS); 00832 if (pl->socket.sc_version < 1025) { 00833 AddIfShort(pl->last_resist[ATNR_PHYSICAL], pl->ob->resist[ATNR_PHYSICAL], CS_STAT_ARMOUR); 00834 } else { 00835 int i; 00836 00837 for (i = 0; i < NROFATTACKS; i++) { 00838 /* Skip ones we won't send */ 00839 if (atnr_cs_stat[i] == -1) 00840 continue; 00841 AddIfShort(pl->last_resist[i], pl->ob->resist[i], (char)atnr_cs_stat[i]); 00842 } 00843 } 00844 if (pl->socket.monitor_spells) { 00845 AddIfInt(pl->last_path_attuned, pl->ob->path_attuned, CS_STAT_SPELL_ATTUNE); 00846 AddIfInt(pl->last_path_repelled, pl->ob->path_repelled, CS_STAT_SPELL_REPEL); 00847 AddIfInt(pl->last_path_denied, pl->ob->path_denied, CS_STAT_SPELL_DENY); 00848 } 00849 /* we want to use the new fire & run system in new client */ 00850 rangetostring(pl->ob, buf, sizeof(buf)); 00851 AddIfString(pl->socket.stats.range, buf, CS_STAT_RANGE); 00852 set_title(pl->ob, buf, sizeof(buf)); 00853 AddIfString(pl->socket.stats.title, buf, CS_STAT_TITLE); 00854 00855 /* Only send it away if we have some actual data - 2 bytes for length, 6 for "stats ". */ 00856 if (sl.len > 8) { 00857 #ifdef ESRV_DEBUG 00858 LOG(llevDebug, "Sending stats command, %d bytes long.\n", sl.len); 00859 #endif 00860 Send_With_Handling(&pl->socket, &sl); 00861 } 00862 SockList_Term(&sl); 00863 } 00864 00868 void esrv_new_player(player *pl, uint32 weight) { 00869 SockList sl; 00870 00871 pl->last_weight = weight; 00872 00873 if (!(pl->socket.faces_sent[pl->ob->face->number]&NS_FACESENT_FACE)) 00874 esrv_send_face(&pl->socket, pl->ob->face->number, 0); 00875 00876 SockList_Init(&sl); 00877 SockList_AddString(&sl, "player "); 00878 SockList_AddInt(&sl, pl->ob->count); 00879 SockList_AddInt(&sl, weight); 00880 SockList_AddInt(&sl, pl->ob->face->number); 00881 SockList_AddLen8Data(&sl, pl->ob->name, strlen(pl->ob->name)); 00882 00883 Send_With_Handling(&pl->socket, &sl); 00884 SockList_Term(&sl); 00885 SET_FLAG(pl->ob, FLAG_CLIENT_SENT); 00886 } 00887 00895 void esrv_send_animation(socket_struct *ns, short anim_num) { 00896 SockList sl; 00897 int i; 00898 00899 /* Do some checking on the anim_num we got. Note that the animations 00900 * are added in contigous order, so if the number is in the valid 00901 * range, it must be a valid animation. 00902 */ 00903 if (anim_num < 0 || anim_num > num_animations) { 00904 LOG(llevError, "esrv_send_anim (%d) out of bounds??\n", anim_num); 00905 return; 00906 } 00907 00908 SockList_Init(&sl); 00909 SockList_AddString(&sl, "anim "); 00910 SockList_AddShort(&sl, anim_num); 00911 SockList_AddShort(&sl, 0); /* flags - not used right now */ 00912 /* Build up the list of faces. Also, send any information (ie, the 00913 * the face itself) down to the client. 00914 */ 00915 for (i = 0; i < animations[anim_num].num_animations; i++) { 00916 if (!(ns->faces_sent[animations[anim_num].faces[i]]&NS_FACESENT_FACE)) 00917 esrv_send_face(ns, animations[anim_num].faces[i], 0); 00918 /* flags - not used right now */ 00919 SockList_AddShort(&sl, animations[anim_num].faces[i]); 00920 } 00921 Send_With_Handling(ns, &sl); 00922 SockList_Term(&sl); 00923 ns->anims_sent[anim_num] = 1; 00924 } 00925 00926 /**************************************************************************** 00927 * 00928 * Start of map related commands. 00929 * 00930 ****************************************************************************/ 00931 00933 static void map_clearcell(struct map_cell_struct *cell, int face, int count) { 00934 cell->darkness = count; 00935 memset(cell->faces, face, sizeof(cell->faces)); 00936 } 00937 00938 #define MAX_HEAD_POS MAX(MAX_CLIENT_X, MAX_CLIENT_Y) 00939 00947 static object *heads[MAX_HEAD_POS][MAX_HEAD_POS][MAP_LAYERS]; 00948 00949 /**************************************************************************** 00950 * This block is for map2 drawing related commands. 00951 * Note that the map2 still uses other functions. 00952 * 00953 ***************************************************************************/ 00954 00974 static int map2_add_ob(int ax, int ay, int layer, object *ob, SockList *sl, socket_struct *ns, int *has_obj, int is_head) { 00975 uint16 face_num; 00976 uint8 nlayer, smoothlevel = 0; 00977 object *head; 00978 00979 assert(ob != NULL); 00980 00981 head = ob->head ? ob->head : ob; 00982 face_num = ob->face->number; 00983 00984 /* This is a multipart object, and we are not at the lower 00985 * right corner. So we need to store away the lower right corner. 00986 */ 00987 if (!is_head && (head->arch->tail_x || head->arch->tail_y) 00988 && (head->arch->tail_x != ob->arch->clone.x || head->arch->tail_y != ob->arch->clone.y)) { 00989 int bx, by, l; 00990 00991 /* Basically figure out where the offset is from where we 00992 * are right now. the ob->arch->clone.{x,y} values hold 00993 * the offset that this current piece is from the head, 00994 * and the tail is where the tail is from the head. 00995 * Note that bx and by will equal sx and sy if we are 00996 * already working on the bottom right corner. If ob is 00997 * the head, the clone values will be zero, so the right 00998 * thing will still happen. 00999 */ 01000 bx = ax+head->arch->tail_x-ob->arch->clone.x; 01001 by = ay+head->arch->tail_y-ob->arch->clone.y; 01002 01003 /* I don't think this can ever happen, but better to check 01004 * for it just in case. 01005 */ 01006 if (bx < ax || by < ay) { 01007 LOG(llevError, "map2_add_ob: bx (%d) or by (%d) is less than ax (%d) or ay (%d)\n", bx, by, ax, ay); 01008 face_num = 0; 01009 } 01010 /* the target position must be within +/-1 of our current 01011 * layer as the layers are defined. We are basically checking 01012 * to see if we have already stored this object away. 01013 */ 01014 for (l = layer-1; l <= layer+1; l++) { 01015 if (l < 0 || l >= MAP_LAYERS) 01016 continue; 01017 if (heads[by][bx][l] == head) 01018 break; 01019 } 01020 /* Didn't find it. So we need to store it away. Try to store it 01021 * on our original layer, and then move up a layer. 01022 */ 01023 if (l == layer+2) { 01024 if (!heads[by][bx][layer]) 01025 heads[by][bx][layer] = head; 01026 else if (layer+1 < MAP_LAYERS && !heads[by][bx][layer+1]) 01027 heads[by][bx][layer+1] = head; 01028 } 01029 return 0; 01030 /* Ok - All done storing away the head for future use */ 01031 } else { 01032 (*has_obj)++; 01033 if (QUERY_FLAG(ob, FLAG_CLIENT_ANIM_SYNC) 01034 || QUERY_FLAG(ob, FLAG_CLIENT_ANIM_RANDOM)) { 01035 face_num = ob->animation_id|(1<<15); 01036 if (QUERY_FLAG(ob, FLAG_CLIENT_ANIM_SYNC)) 01037 face_num |= ANIM_SYNC; 01038 else if (QUERY_FLAG(ob, FLAG_CLIENT_ANIM_RANDOM)) 01039 face_num |= ANIM_RANDOM; 01040 } 01041 /* Since face_num includes the bits for the animation tag, 01042 * and we will store that away in the faces[] array, below 01043 * check works fine _except_ for the case where animation 01044 * speed chances. 01045 */ 01046 if (ns->lastmap.cells[ax][ay].faces[layer] != face_num) { 01047 uint8 len, anim_speed = 0, i; 01048 01049 /* This block takes care of sending the actual face 01050 * to the client. */ 01051 ns->lastmap.cells[ax][ay].faces[layer] = face_num; 01052 01053 /* Now form the data packet */ 01054 nlayer = 0x10+layer; 01055 01056 len = 2; 01057 01058 if (!MAP_NOSMOOTH(ob->map)) { 01059 smoothlevel = ob->smoothlevel; 01060 if (smoothlevel) 01061 len++; 01062 } 01063 01064 if (QUERY_FLAG(ob, FLAG_CLIENT_ANIM_SYNC) 01065 || QUERY_FLAG(ob, FLAG_CLIENT_ANIM_RANDOM)) { 01066 len++; 01067 /* 1/0.004 == 250, so this is a good cap for an 01068 * upper limit */ 01069 if (ob->anim_speed) 01070 anim_speed = ob->anim_speed; 01071 else if (FABS(ob->speed) < 0.004) 01072 anim_speed = 255; 01073 else if (FABS(ob->speed) >= 1.0) 01074 anim_speed = 1; 01075 else 01076 anim_speed = (int)(1.0/FABS(ob->speed)); 01077 01078 if (!ns->anims_sent[ob->animation_id]) 01079 esrv_send_animation(ns, ob->animation_id); 01080 01081 /* If smoothing, need to send smoothing information 01082 * for all faces in the animation sequence. Since 01083 * smoothlevel is an object attribute, 01084 * it applies to all faces. 01085 */ 01086 if (smoothlevel) { 01087 for (i = 0; i < NUM_ANIMATIONS(ob); i++) { 01088 if (!(ns->faces_sent[animations[ob->animation_id].faces[i]]&NS_FACESENT_SMOOTH)) 01089 send_smooth(ns, animations[ob->animation_id].faces[i]); 01090 } 01091 } 01092 } else if (!(ns->faces_sent[face_num]&NS_FACESENT_FACE)) { 01093 esrv_send_face(ns, face_num, 0); 01094 } 01095 01096 if (smoothlevel 01097 && !(ns->faces_sent[ob->face->number]&NS_FACESENT_SMOOTH)) 01098 send_smooth(ns, ob->face->number); 01099 01100 /* Length of packet */ 01101 nlayer |= len<<5; 01102 01103 SockList_AddChar(sl, nlayer); 01104 SockList_AddShort(sl, face_num); 01105 if (anim_speed) 01106 SockList_AddChar(sl, anim_speed); 01107 if (smoothlevel) 01108 SockList_AddChar(sl, smoothlevel); 01109 return 1; 01110 } /* Face is different */ 01111 } 01112 return 0; 01113 } 01114 01115 /* This function is used see if a layer needs to be cleared. 01116 * It updates the socklist, and returns 1 if the update is 01117 * needed, 0 otherwise. 01118 */ 01119 static int map2_delete_layer(int ax, int ay, int layer, SockList *sl, socket_struct *ns) { 01120 int nlayer; 01121 01122 if (ns->lastmap.cells[ax][ay].faces[layer] != 0) { 01123 /* Now form the data packet */ 01124 nlayer = 0x10+layer+(2<<5); 01125 SockList_AddChar(sl, nlayer); 01126 SockList_AddShort(sl, 0); 01127 ns->lastmap.cells[ax][ay].faces[layer] = 0; 01128 return 1; 01129 } 01130 return 0; 01131 } 01132 01133 /* 01134 * This function is used to check a space (ax, ay) whose only 01135 * data we may care about are any heads. Basically, this 01136 * space is out of direct view. This is only used with the 01137 * Map2 protocol. 01138 * 01139 * @param ax 01140 * viewport relative x-coordinate 01141 * @param ay 01142 * viewport relative y-coordinate 01143 * @param sl 01144 * the reply to append to 01145 * @param ns 01146 * the client socket 01147 */ 01148 static void check_space_for_heads(int ax, int ay, SockList *sl, socket_struct *ns) { 01149 int layer, got_one = 0, del_one = 0, oldlen, has_obj = 0; 01150 uint16 coord; 01151 01152 coord = MAP2_COORD_ENCODE(ax, ay, 0); 01153 oldlen = sl->len; 01154 SockList_AddShort(sl, coord); 01155 01156 for (layer = 0; layer < MAP_LAYERS; layer++) { 01157 object *head; 01158 01159 head = heads[ay][ax][layer]; 01160 if (head) { 01161 /* in this context, got_one should always increase 01162 * because heads should always point to data to really send. 01163 */ 01164 got_one += map2_add_ob(ax, ay, layer, head, sl, ns, &has_obj, 1); 01165 } else { 01166 del_one += map2_delete_layer(ax, ay, layer, sl, ns); 01167 } 01168 } 01169 /* Note - if/when lighting information is added, some code is 01170 * needed here - lighting sources that are out of sight may still 01171 * extend into the viewable area. 01172 */ 01173 01174 /* If nothing to do for this space, we 01175 * can erase the coordinate bytes 01176 */ 01177 if (!del_one && !got_one) { 01178 sl->len = oldlen; 01179 } else if (del_one && !has_obj) { 01180 /* If we're only deleting faces and not adding, and there 01181 * are not any faces on the space we care about, 01182 * more efficient 01183 * to send 0 as the type/len field. 01184 */ 01185 sl->len = oldlen+2; /* 2 bytes for coordinate */ 01186 SockList_AddChar(sl, 0); /* Clear byte */ 01187 SockList_AddChar(sl, 255); /* Termination byte */ 01188 map_clearcell(&ns->lastmap.cells[ax][ay], 0, 0); 01189 } else { 01190 SockList_AddChar(sl, 255); /* Termination byte */ 01191 } 01192 } 01193 01194 void draw_client_map2(object *pl) { 01195 int x, y, ax, ay, d, min_x, max_x, min_y, max_y, oldlen, layer; 01196 size_t startlen; 01197 sint16 nx, ny; 01198 SockList sl; 01199 uint16 coord; 01200 mapstruct *m; 01201 object *ob; 01202 01203 SockList_Init(&sl); 01204 SockList_AddString(&sl, "map2 "); 01205 startlen = sl.len; 01206 01207 /* Handle map scroll */ 01208 if (pl->contr->socket.map_scroll_x || pl->contr->socket.map_scroll_y) { 01209 coord = MAP2_COORD_ENCODE(pl->contr->socket.map_scroll_x, pl->contr->socket.map_scroll_y, 1); 01210 pl->contr->socket.map_scroll_x = 0; 01211 pl->contr->socket.map_scroll_y = 0; 01212 SockList_AddShort(&sl, coord); 01213 } 01214 01215 /* Init data to zero */ 01216 memset(heads, 0, sizeof(heads)); 01217 01218 /* We could do this logic as conditionals in the if statement, 01219 * but that started to get a bit messy to look at. 01220 */ 01221 min_x = pl->x-pl->contr->socket.mapx/2; 01222 min_y = pl->y-pl->contr->socket.mapy/2; 01223 max_x = pl->x+(pl->contr->socket.mapx+1)/2+MAX_HEAD_OFFSET; 01224 max_y = pl->y+(pl->contr->socket.mapy+1)/2+MAX_HEAD_OFFSET; 01225 01226 /* x, y are the real map locations. ax, ay are viewport relative 01227 * locations. 01228 */ 01229 ay = 0; 01230 for (y = min_y; y < max_y; y++, ay++) { 01231 ax = 0; 01232 for (x = min_x; x < max_x; x++, ax++) { 01233 /* If this space is out of the normal viewable area, 01234 * we only check the heads value. This is used to 01235 * handle big images - if they extend to a viewable 01236 * portion, we need to send just the lower right corner. 01237 */ 01238 if (ax >= pl->contr->socket.mapx || ay >= pl->contr->socket.mapy) { 01239 check_space_for_heads(ax, ay, &sl, &pl->contr->socket); 01240 } else { 01241 /* This space is within the viewport of the client. Due 01242 * to walls or darkness, it may still not be visible. 01243 */ 01244 01245 /* Meaning of d: 01246 * 0 - object is in plain sight, full brightness. 01247 * 1 - MAX_DARKNESS - how dark the space is - higher 01248 * value is darker space. If level is at max darkness, 01249 * you can't see the space (too dark) 01250 * 100 - space is blocked from sight. 01251 */ 01252 d = pl->contr->blocked_los[ax][ay]; 01253 01254 /* If the coordinates are not valid, or it is too 01255 * dark to see, we tell the client as such 01256 */ 01257 nx = x; 01258 ny = y; 01259 m = get_map_from_coord(pl->map, &nx, &ny); 01260 coord = MAP2_COORD_ENCODE(ax, ay, 0); 01261 01262 if (!m) { 01263 /* space is out of map. Update space and clear 01264 * values if this hasn't already been done. 01265 * If the space is out of the map, it shouldn't 01266 * have a head. 01267 */ 01268 if (pl->contr->socket.lastmap.cells[ax][ay].darkness != 0) { 01269 SockList_AddShort(&sl, coord); 01270 SockList_AddChar(&sl, 0); 01271 SockList_AddChar(&sl, 255); /* Termination byte */ 01272 map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay], 0, 0); 01273 } 01274 } else if (d >= MAX_LIGHT_RADII) { 01275 /* This block deals with spaces that are not 01276 * visible due to darkness or walls. Still 01277 * need to send the heads for this space. 01278 */ 01279 check_space_for_heads(ax, ay, &sl, &pl->contr->socket); 01280 } else { 01281 int have_darkness = 0, has_obj = 0, got_one = 0, del_one = 0, g1; 01282 01283 /* In this block, the space is visible. */ 01284 01285 /* Rather than try to figure out what everything 01286 * that we might need to send is, then form the 01287 * packet after that, we presume that we will in 01288 * fact form a packet, and update the bits by what 01289 * we do actually send. If we send nothing, we 01290 * just back out sl.len to the old value, and no 01291 * harm is done. 01292 * I think this is simpler than doing a bunch of 01293 * checks to see what if anything we need to send, 01294 * setting the bits, then doing those checks again 01295 * to add the real data. 01296 */ 01297 01298 oldlen = sl.len; 01299 SockList_AddShort(&sl, coord); 01300 01301 /* Darkness changed */ 01302 if (pl->contr->socket.lastmap.cells[ax][ay].darkness != d 01303 && pl->contr->socket.darkness) { 01304 pl->contr->socket.lastmap.cells[ax][ay].darkness = d; 01305 /* Darkness tag & length*/ 01306 SockList_AddChar(&sl, 0x1|1<<5); 01307 SockList_AddChar(&sl, 255-d*(256/MAX_LIGHT_RADII)); 01308 have_darkness = 1; 01309 } 01310 01311 for (layer = 0; layer < MAP_LAYERS; layer++) { 01312 ob = GET_MAP_FACE_OBJ(m, nx, ny, layer); 01313 01314 /* Special case: send player itself if invisible */ 01315 if (!ob 01316 && x == pl->x 01317 && y == pl->y 01318 && (pl->invisible&(pl->invisible < 50 ? 4 : 1)) 01319 && (layer == MAP_LAYER_LIVING1 || layer == MAP_LAYER_LIVING2)) 01320 ob = pl; 01321 01322 if (ob) { 01323 g1 = has_obj; 01324 got_one += map2_add_ob(ax, ay, layer, ob, &sl, &pl->contr->socket, &has_obj, 0); 01325 /* If we are just storing away the head 01326 * for future use, then effectively this 01327 * space/layer is blank, and we should clear 01328 * it if needed. 01329 */ 01330 if (g1 == has_obj) 01331 del_one += map2_delete_layer(ax, ay, layer, &sl, &pl->contr->socket); 01332 } else { 01333 del_one += map2_delete_layer(ax, ay, layer, &sl, &pl->contr->socket); 01334 } 01335 } 01336 /* If nothing to do for this space, we 01337 * can erase the coordinate bytes 01338 */ 01339 if (!del_one && !got_one && !have_darkness) { 01340 sl.len = oldlen; 01341 } else if (del_one && !has_obj) { 01342 /* If we're only deleting faces and don't 01343 * have any objs we care about, just clear 01344 * space. Note it is possible we may have 01345 * darkness, but if there is nothing on the 01346 * space, darkness isn't all that interesting 01347 * - we can send it when an object shows up. 01348 */ 01349 sl.len = oldlen+2; /* 2 bytes for coordinate */ 01350 SockList_AddChar(&sl, 0); /* Clear byte */ 01351 SockList_AddChar(&sl, 255); /* Termination byte */ 01352 map_clearcell(&pl->contr->socket.lastmap.cells[ax][ay], 0, 0); 01353 } else { 01354 SockList_AddChar(&sl, 255); /* Termination byte */ 01355 } 01356 } 01357 } /* else this is a viewable space */ 01358 } /* for x loop */ 01359 } /* for y loop */ 01360 01361 /* Only send this if there are in fact some differences. */ 01362 if (sl.len > startlen) { 01363 Send_With_Handling(&pl->contr->socket, &sl); 01364 } 01365 SockList_Term(&sl); 01366 } 01367 01371 void draw_client_map(object *pl) { 01372 int i, j; 01373 sint16 ax, ay; 01374 int mflags; 01375 mapstruct *m, *pm; 01376 int min_x, min_y, max_x, max_y; 01377 01378 if (pl->type != PLAYER) { 01379 LOG(llevError, "draw_client_map called with non player/non eric-server\n"); 01380 return; 01381 } 01382 01383 if (pl->contr->transport) { 01384 pm = pl->contr->transport->map; 01385 } else 01386 pm = pl->map; 01387 01388 /* If player is just joining the game, he isn't here yet, so 01389 * the map can get swapped out. If so, don't try to send them 01390 * a map. All will be OK once they really log in. 01391 */ 01392 if (pm == NULL || pm->in_memory != MAP_IN_MEMORY) 01393 return; 01394 01395 /* 01396 * This block just makes sure all the spaces are properly 01397 * updated in terms of what they look like. 01398 */ 01399 min_x = pl->x-pl->contr->socket.mapx/2; 01400 min_y = pl->y-pl->contr->socket.mapy/2; 01401 max_x = pl->x+(pl->contr->socket.mapx+1)/2; 01402 max_y = pl->y+(pl->contr->socket.mapy+1)/2; 01403 for (j = min_y; j < max_y; j++) { 01404 for (i = min_x; i < max_x; i++) { 01405 ax = i; 01406 ay = j; 01407 m = pm; 01408 mflags = get_map_flags(m, &m, ax, ay, &ax, &ay); 01409 if (mflags&P_OUT_OF_MAP) 01410 continue; 01411 if (mflags&P_NEED_UPDATE) 01412 update_position(m, ax, ay); 01413 /* If a map is visible to the player, we don't want 01414 * to swap it out just to reload it. This should 01415 * really call something like swap_map, but this is 01416 * much more efficient and 'good enough' 01417 */ 01418 if (mflags&P_NEW_MAP) 01419 m->timeout = 50; 01420 } 01421 } 01422 01423 /* do LOS after calls to update_position */ 01424 if (pl->contr->do_los) { 01425 update_los(pl); 01426 pl->contr->do_los = 0; 01427 } 01428 01429 draw_client_map2(pl); 01430 } 01431 01432 void esrv_map_scroll(socket_struct *ns, int dx, int dy) { 01433 struct Map newmap; 01434 int x, y, mx, my; 01435 01436 ns->map_scroll_x += dx; 01437 ns->map_scroll_y += dy; 01438 01439 mx = ns->mapx+MAX_HEAD_OFFSET; 01440 my = ns->mapy+MAX_HEAD_OFFSET; 01441 01442 /* the x and y here are coordinates for the new map, i.e. if we moved 01443 * (dx,dy), newmap[x][y] = oldmap[x-dx][y-dy]. For this reason, 01444 * if the destination x or y coordinate is outside the viewable 01445 * area, we clear the values - otherwise, the old values 01446 * are preserved, and the check_head thinks it needs to clear them. 01447 */ 01448 for (x = 0; x < mx; x++) { 01449 for (y = 0; y < my; y++) { 01450 if (x >= ns->mapx || y >= ns->mapy) { 01451 /* clear cells outside the viewable area */ 01452 memset(&newmap.cells[x][y], 0, sizeof(newmap.cells[x][y])); 01453 } else if (x+dx < 0 || x+dx >= ns->mapx || y+dy < 0 || y+dy >= ns->mapy) { 01454 /* clear newly visible tiles within the viewable area */ 01455 memset(&newmap.cells[x][y], 0, sizeof(newmap.cells[x][y])); 01456 } else { 01457 memcpy(&newmap.cells[x][y], &ns->lastmap.cells[x+dx][y+dy], sizeof(newmap.cells[x][y])); 01458 } 01459 } 01460 } 01461 01462 memcpy(&ns->lastmap, &newmap, sizeof(ns->lastmap)); 01463 } 01464 01470 void send_plugin_custom_message(object *pl, char *buf) { 01471 SockList sl; 01472 01473 SockList_Init(&sl); 01474 SockList_AddString(&sl, buf); 01475 Send_With_Handling(&pl->contr->socket, &sl); 01476 SockList_Term(&sl); 01477 } 01478 01482 void send_exp_table(socket_struct *ns, char *params) { 01483 SockList sl; 01484 int i; 01485 extern sint64 *levels; 01486 01487 SockList_Init(&sl); 01488 SockList_AddString(&sl, "replyinfo exp_table\n"); 01489 SockList_AddShort(&sl, settings.max_level+1); 01490 for (i = 1; i <= settings.max_level; i++) { 01491 if (SockList_Avail(&sl) < 8) { 01492 LOG(llevError, "Buffer overflow in send_exp_table, not sending all information\n"); 01493 break; 01494 } 01495 SockList_AddInt64(&sl, levels[i]); 01496 } 01497 Send_With_Handling(ns, &sl); 01498 SockList_Term(&sl); 01499 } 01500 01505 void send_skill_info(socket_struct *ns, char *params) { 01506 SockList sl; 01507 int i; 01508 01509 SockList_Init(&sl); 01510 SockList_AddString(&sl, "replyinfo skill_info\n"); 01511 for (i = 1; i < NUM_SKILLS; i++) { 01512 size_t len; 01513 01514 len = 16+strlen(skill_names[i]); /* upper bound for length */ 01515 if (SockList_Avail(&sl) < len) { 01516 LOG(llevError, "Buffer overflow in send_skill_info, not sending all skill information\n"); 01517 break; 01518 } 01519 01520 SockList_AddPrintf(&sl, "%d:%s\n", i+CS_STAT_SKILLINFO, skill_names[i]); 01521 } 01522 Send_With_Handling(ns, &sl); 01523 SockList_Term(&sl); 01524 } 01525 01530 void send_spell_paths(socket_struct *ns, char *params) { 01531 SockList sl; 01532 int i; 01533 01534 SockList_Init(&sl); 01535 SockList_AddString(&sl, "replyinfo spell_paths\n"); 01536 for (i = 0; i < NRSPELLPATHS; i++) { 01537 size_t len; 01538 01539 len = 16+strlen(spellpathnames[i]); /* upper bound for length */ 01540 if (SockList_Avail(&sl) < len) { 01541 LOG(llevError, "Buffer overflow in send_spell_paths, not sending all spell information\n"); 01542 break; 01543 } 01544 01545 SockList_AddPrintf(&sl, "%d:%s\n", 1<<i, spellpathnames[i]); 01546 } 01547 Send_With_Handling(ns, &sl); 01548 SockList_Term(&sl); 01549 } 01550 01557 static void build_race_list_reply(SockList *sl) { 01558 archetype *race; 01559 01560 SockList_AddString(sl, "replyinfo race_list "); 01561 01562 for (race = first_archetype; race; race = race->next) { 01563 if (race->clone.type == PLAYER) { 01564 SockList_AddPrintf(sl, "|%s", race->name); 01565 } 01566 } 01567 } 01568 01578 void send_race_list(socket_struct *ns, char *params) { 01579 static SockList sl; 01580 static int sl_initialized = 0; 01581 01582 if (!sl_initialized) { 01583 sl_initialized = 1; 01584 SockList_Init(&sl); 01585 build_race_list_reply(&sl); 01586 } 01587 01588 Send_With_Handling(ns, &sl); 01589 } 01590 01600 void send_race_info(socket_struct *ns, char *params) { 01601 archetype *race = try_find_archetype(params); 01602 SockList sl; 01603 01604 SockList_Init(&sl); 01605 SockList_AddPrintf(&sl, "replyinfo race_info %s", params); 01606 01607 if (race) { 01608 } 01609 01610 Send_With_Handling(ns, &sl); 01611 SockList_Term(&sl); 01612 } 01613 01620 static void build_class_list_reply(SockList *sl) { 01621 archetype *cl; 01622 01623 SockList_Reset(sl); 01624 SockList_AddString(sl, "replyinfo class_list "); 01625 01626 for (cl = first_archetype; cl; cl = cl->next) { 01627 if (cl->clone.type == CLASS) { 01628 SockList_AddPrintf(sl, "|%s", cl->name); 01629 } 01630 } 01631 } 01632 01642 void send_class_list(socket_struct *ns, char *params) { 01643 static SockList sl; 01644 static int sl_initialized = 0; 01645 01646 if (!sl_initialized) { 01647 sl_initialized = 1; 01648 SockList_Init(&sl); 01649 build_class_list_reply(&sl); 01650 } 01651 01652 Send_With_Handling(ns, &sl); 01653 } 01654 01664 void send_class_info(socket_struct *ns, char *params) { 01665 } 01666 01672 void esrv_update_spells(player *pl) { 01673 SockList sl; 01674 int flags = 0; 01675 object *spell; 01676 client_spell *spell_info; 01677 01678 if (!pl->socket.monitor_spells) 01679 return; 01680 01681 /* Handles problem at login, where this is called from fix_object 01682 * before we have had a chance to send spells to the player. It does seem 01683 * to me that there should never be a case where update_spells is called 01684 * before add_spells has been called. Add_spells() will update the 01685 * spell_state to non null. 01686 */ 01687 if (!pl->spell_state) 01688 return; 01689 01690 for (spell = pl->ob->inv; spell != NULL; spell = spell->below) { 01691 if (spell->type == SPELL) { 01692 spell_info = get_client_spell_state(pl, spell); 01693 /* check if we need to update it*/ 01694 if (spell_info->last_sp != SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA)) { 01695 spell_info->last_sp = SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA); 01696 flags |= UPD_SP_MANA; 01697 } 01698 if (spell_info->last_grace != SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE)) { 01699 spell_info->last_grace = SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE); 01700 flags |= UPD_SP_GRACE; 01701 } 01702 if (spell_info->last_dam != spell->stats.dam+SP_level_dam_adjust(pl->ob, spell)) { 01703 spell_info->last_dam = spell->stats.dam+SP_level_dam_adjust(pl->ob, spell); 01704 flags |= UPD_SP_DAMAGE; 01705 } 01706 if (flags != 0) { 01707 SockList_Init(&sl); 01708 SockList_AddString(&sl, "updspell "); 01709 SockList_AddChar(&sl, flags); 01710 SockList_AddInt(&sl, spell->count); 01711 if (flags&UPD_SP_MANA) 01712 SockList_AddShort(&sl, spell_info->last_sp); 01713 if (flags&UPD_SP_GRACE) 01714 SockList_AddShort(&sl, spell_info->last_grace); 01715 if (flags&UPD_SP_DAMAGE) 01716 SockList_AddShort(&sl, spell_info->last_dam); 01717 flags = 0; 01718 Send_With_Handling(&pl->socket, &sl); 01719 SockList_Term(&sl); 01720 } 01721 } 01722 } 01723 } 01724 01725 void esrv_remove_spell(player *pl, object *spell) { 01726 SockList sl; 01727 01728 if (!pl->socket.monitor_spells) 01729 return; 01730 if (!pl || !spell || spell->env != pl->ob) { 01731 LOG(llevError, "Invalid call to esrv_remove_spell\n"); 01732 return; 01733 } 01734 SockList_Init(&sl); 01735 SockList_AddString(&sl, "delspell "); 01736 SockList_AddInt(&sl, spell->count); 01737 Send_With_Handling(&pl->socket, &sl); 01738 SockList_Term(&sl); 01739 } 01740 01747 void esrv_send_pickup(player *pl) { 01748 SockList sl; 01749 01750 if (!pl->socket.want_pickup) 01751 return; 01752 SockList_Init(&sl); 01753 SockList_AddString(&sl, "pickup "); 01754 SockList_AddInt(&sl, pl->mode); 01755 Send_With_Handling(&pl->socket, &sl); 01756 SockList_Term(&sl); 01757 } 01758 01760 static void append_spell(player *pl, SockList *sl, object *spell) { 01761 client_spell *spell_info; 01762 int len, i, skill = 0; 01763 01764 if (!spell->name) { 01765 LOG(llevError, "item number %d is a spell with no name.\n", spell->count); 01766 return; 01767 } 01768 01769 if (spell->face && !(pl->socket.faces_sent[spell->face->number]&NS_FACESENT_FACE)) 01770 esrv_send_face(&pl->socket, spell->face->number, 0); 01771 01772 spell_info = get_client_spell_state(pl, spell); 01773 SockList_AddInt(sl, spell->count); 01774 SockList_AddShort(sl, spell->level); 01775 SockList_AddShort(sl, spell->casting_time); 01776 /* store costs and damage in the object struct, to compare to later */ 01777 spell_info->last_sp = SP_level_spellpoint_cost(pl->ob, spell, SPELL_MANA); 01778 spell_info->last_grace = SP_level_spellpoint_cost(pl->ob, spell, SPELL_GRACE); 01779 spell_info->last_dam = spell->stats.dam+SP_level_dam_adjust(pl->ob, spell); 01780 /* send the current values */ 01781 SockList_AddShort(sl, spell_info->last_sp); 01782 SockList_AddShort(sl, spell_info->last_grace); 01783 SockList_AddShort(sl, spell_info->last_dam); 01784 01785 /* figure out which skill it uses, if it uses one */ 01786 if (spell->skill) { 01787 for (i = 1; i < NUM_SKILLS; i++) 01788 if (!strcmp(spell->skill, skill_names[i])) { 01789 skill = i+CS_STAT_SKILLINFO; 01790 break; 01791 } 01792 } 01793 SockList_AddChar(sl, skill); 01794 01795 SockList_AddInt(sl, spell->path_attuned); 01796 SockList_AddInt(sl, spell->face ? spell->face->number : 0); 01797 SockList_AddLen8Data(sl, spell->name, strlen(spell->name)); 01798 01799 if (!spell->msg) { 01800 SockList_AddShort(sl, 0); 01801 } else { 01802 len = strlen(spell->msg); 01803 SockList_AddShort(sl, len); 01804 SockList_AddData(sl, spell->msg, len); 01805 } 01806 } 01807 01812 void esrv_add_spells(player *pl, object *spell) { 01813 SockList sl; 01814 01815 if (!pl) { 01816 LOG(llevError, "esrv_add_spells, tried to add a spell to a NULL player\n"); 01817 return; 01818 } 01819 01820 if (!pl->socket.monitor_spells) 01821 return; 01822 01823 SockList_Init(&sl); 01824 SockList_AddString(&sl, "addspell "); 01825 if (!spell) { 01826 for (spell = pl->ob->inv; spell != NULL; spell = spell->below) { 01827 if (spell->type != SPELL) 01828 continue; 01829 /* Were we to simply keep appending data here, we could 01830 * exceed the SockList buffer if the player has enough spells 01831 * to add. We know that append_spell will always append 01832 * 23 data bytes, plus 3 length bytes and 2 strings 01833 * (because that is the spec) so we need to check that 01834 * the length of those 2 strings, plus the 26 bytes, 01835 * won't take us over the length limit for the socket. 01836 * If it does, we need to send what we already have, 01837 * and restart packet formation. 01838 */ 01839 if (SockList_Avail(&sl) < 26+strlen(spell->name)+(spell->msg ? strlen(spell->msg) : 0)) { 01840 Send_With_Handling(&pl->socket, &sl); 01841 SockList_Reset(&sl); 01842 SockList_AddString(&sl, "addspell "); 01843 } 01844 append_spell(pl, &sl, spell); 01845 } 01846 } else if (spell->type != SPELL) { 01847 LOG(llevError, "Asked to send a non-spell object as a spell\n"); 01848 return; 01849 } else 01850 append_spell(pl, &sl, spell); 01851 /* finally, we can send the packet */ 01852 Send_With_Handling(&pl->socket, &sl); 01853 SockList_Term(&sl); 01854 } 01855 01856 /* sends a 'tick' information to the client. 01857 * We also take the opportunity to toggle TCP_NODELAY - 01858 * this forces the data in the socket to be flushed sooner to the 01859 * client - otherwise, the OS tries to wait for full packets 01860 * and will this hold sending the data for some amount of time, 01861 * which thus adds some additional latency. 01862 */ 01863 void send_tick(player *pl) { 01864 SockList sl; 01865 int tmp; 01866 01867 SockList_Init(&sl); 01868 SockList_AddString(&sl, "tick "); 01869 SockList_AddInt(&sl, pticks); 01870 tmp = 1; 01871 if (setsockopt(pl->socket.fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp))) 01872 LOG(llevError, "send_tick: Unable to turn on TCP_NODELAY\n"); 01873 01874 Send_With_Handling(&pl->socket, &sl); 01875 tmp = 0; 01876 if (setsockopt(pl->socket.fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp))) 01877 LOG(llevError, "send_tick: Unable to turn off TCP_NODELAY\n"); 01878 SockList_Term(&sl); 01879 }