Crossfire Server, Branch 1.12
R12190
|
00001 00002 /* 00003 * static char *rcsid_loop_c = 00004 * "$Id: loop.c 11578 2009-02-23 22:02:27Z lalo $"; 00005 */ 00006 00007 /* 00008 CrossFire, A Multiplayer game for X-windows 00009 00010 Copyright (C) 2006 Mark Wedel & The Crossfire Development Team 00011 Copyright (C) 1992 Frank Tore Johansen 00012 00013 This program is free software; you can redistribute it and/or modify 00014 it under the terms of the GNU General Public License as published by 00015 the Free Software Foundation; either version 2 of the License, or 00016 (at your option) any later version. 00017 00018 This program is distributed in the hope that it will be useful, 00019 but WITHOUT ANY WARRANTY; without even the implied warranty of 00020 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00021 GNU General Public License for more details. 00022 00023 You should have received a copy of the GNU General Public License 00024 along with this program; if not, write to the Free Software 00025 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00026 00027 The author can be reached via e-mail to crossfire-devel@real-time.com 00028 */ 00029 00041 #include <global.h> 00042 #ifndef __CEXTRACT__ 00043 #include <sproto.h> 00044 #include <sockproto.h> 00045 #endif 00046 00047 #ifndef WIN32 /* ---win32 exclude unix headers */ 00048 #include <sys/types.h> 00049 #include <sys/time.h> 00050 #include <sys/socket.h> 00051 #include <netinet/in.h> 00052 #include <netdb.h> 00053 #endif /* end win32 */ 00054 00055 #ifdef HAVE_UNISTD_H 00056 #include <unistd.h> 00057 #endif 00058 00059 #ifdef HAVE_ARPA_INET_H 00060 #include <arpa/inet.h> 00061 #endif 00062 00063 #include <loader.h> 00064 #include <newserver.h> 00065 00066 /***************************************************************************** 00067 * Start of command dispatch area. 00068 * The commands here are protocol commands. 00069 ****************************************************************************/ 00070 00071 /* Either keep this near the start or end of the file so it is 00072 * at least reasonablye easy to find. 00073 * There are really 2 commands - those which are sent/received 00074 * before player joins, and those happen after the player has joined. 00075 * As such, we have function types that might be called, so 00076 * we end up having 2 tables. 00077 */ 00078 00080 typedef void (*func_uint8_int_ns)(char *, int, socket_struct *); 00081 00083 struct client_cmd_mapping { 00084 const char *cmdname; 00085 const func_uint8_int_ns cmdproc; 00086 }; 00087 00089 typedef void (*func_uint8_int_pl)(char *, int, player *); 00091 struct player_cmd_mapping { 00092 const char *cmdname; 00093 const func_uint8_int_pl cmdproc; 00094 const uint8 flag; 00095 }; 00096 00112 static const struct player_cmd_mapping player_commands[] = { 00113 { "examine", examine_cmd, 1 }, 00114 { "apply", apply_cmd, 1 }, 00115 { "move", move_cmd, 1 }, 00116 { "reply", reply_cmd, 0 }, 00117 { "ncom", (func_uint8_int_pl)new_player_cmd, 1 }, 00118 { "lookat", look_at_cmd, 1 }, 00119 { "lock", (func_uint8_int_pl)lock_item_cmd, 1 }, 00120 { "mark", (func_uint8_int_pl)mark_item_cmd, 1 }, 00121 { "mapredraw", map_redraw_cmd, 0 }, /* Added: phil */ 00122 { "inscribe", inscribe_scroll_cmd, 0 }, 00123 { NULL, NULL, 0 } /* terminator */ 00124 }; 00125 00127 static const struct client_cmd_mapping client_commands[] = { 00128 { "addme", add_me_cmd }, 00129 { "askface", send_face_cmd }, /* Added: phil */ 00130 { "requestinfo", request_info_cmd }, 00131 { "setfacemode", set_face_mode_cmd }, 00132 { "setsound", set_sound_cmd }, 00133 { "setup", set_up_cmd }, 00134 { "version", version_cmd }, 00135 { "toggleextendedinfos", toggle_extended_infos_cmd }, /*Added: tchize*/ 00136 { "toggleextendedtext", toggle_extended_text_cmd }, /*Added: tchize*/ 00137 { "asksmooth", ask_smooth_cmd }, /*Added: tchize (smoothing technologies)*/ 00138 { NULL, NULL } /* terminator (I, II & III)*/ 00139 }; 00140 00146 void request_info_cmd(char *buf, int len, socket_struct *ns) { 00147 char *params = NULL, *cp; 00148 /* No match */ 00149 SockList sl; 00150 00151 /* Set up replyinfo before we modify any of the buffers - this is used 00152 * if we don't find a match. 00153 */ 00154 SockList_Init(&sl); 00155 SockList_AddString(&sl, "replyinfo "); 00156 SockList_AddString(&sl, buf); 00157 00158 /* find the first space, make it null, and update the 00159 * params pointer. 00160 */ 00161 for (cp = buf; *cp != '\0'; cp++) 00162 if (*cp == ' ') { 00163 *cp = '\0'; 00164 params = cp+1; 00165 break; 00166 } 00167 if (!strcmp(buf, "image_info")) 00168 send_image_info(ns, params); 00169 else if (!strcmp(buf, "image_sums")) 00170 send_image_sums(ns, params); 00171 else if (!strcmp(buf, "skill_info")) 00172 send_skill_info(ns, params); 00173 else if (!strcmp(buf, "spell_paths")) 00174 send_spell_paths(ns, params); 00175 else if (!strcmp(buf, "exp_table")) 00176 send_exp_table(ns, params); 00177 else if (!strcmp(buf, "race_list")) 00178 send_race_list(ns, params); 00179 else if (!strcmp(buf, "race_info")) 00180 send_race_info(ns, params); 00181 else if (!strcmp(buf, "class_list")) 00182 send_class_list(ns, params); 00183 else if (!strcmp(buf, "class_info")) 00184 send_class_info(ns, params); 00185 else 00186 Send_With_Handling(ns, &sl); 00187 SockList_Term(&sl); 00188 } 00189 00200 void handle_client(socket_struct *ns, player *pl) { 00201 int len, i; 00202 unsigned char *data; 00203 00204 /* Loop through this - maybe we have several complete packets here. */ 00205 while (1) { 00206 /* If it is a player, and they don't have any speed left, we 00207 * return, and will read in the data when they do have time. 00208 */ 00209 if (pl && pl->state == ST_PLAYING && pl->ob != NULL && pl->ob->speed_left < 0) { 00210 return; 00211 } 00212 00213 i = SockList_ReadPacket(ns->fd, &ns->inbuf, sizeof(ns->inbuf.buf)-1); 00214 if (i < 0) { 00215 #ifdef ESRV_DEBUG 00216 LOG(llevDebug, "handle_client: Read error on connection player %s\n", (pl ? pl->ob->name : "None")); 00217 #endif 00218 /* Caller will take care of cleaning this up */ 00219 ns->status = Ns_Dead; 00220 return; 00221 } 00222 /* Still dont have a full packet */ 00223 if (i == 0) 00224 return; 00225 00226 SockList_NullTerminate(&ns->inbuf); /* Terminate buffer - useful for string data */ 00227 00228 /* First, break out beginning word. There are at least 00229 * a few commands that do not have any paremeters. If 00230 * we get such a command, don't worry about trying 00231 * to break it up. 00232 */ 00233 data = (unsigned char *)strchr((char *)ns->inbuf.buf+2, ' '); 00234 if (data) { 00235 *data = '\0'; 00236 data++; 00237 len = ns->inbuf.len-(data-ns->inbuf.buf); 00238 } else 00239 len = 0; 00240 00241 for (i = 0; client_commands[i].cmdname != NULL; i++) { 00242 if (strcmp((char *)ns->inbuf.buf+2, client_commands[i].cmdname) == 0) { 00243 client_commands[i].cmdproc((char *)data, len, ns); 00244 SockList_ResetRead(&ns->inbuf); 00245 return; 00246 } 00247 } 00248 /* Player must be in the playing state or the flag on the 00249 * the command must be zero for the user to use the command - 00250 * otherwise, a player cam save, be in the play_again state, and 00251 * the map they were on getsswapped out, yet things that try to look 00252 * at the map causes a crash. If the command is valid, but 00253 * one they can't use, we still swallow it up. 00254 */ 00255 if (pl) 00256 for (i = 0; player_commands[i].cmdname != NULL; i++) { 00257 if (strcmp((char *)ns->inbuf.buf+2, player_commands[i].cmdname) == 0) { 00258 if (pl->state == ST_PLAYING || player_commands[i].flag == 0) 00259 player_commands[i].cmdproc((char *)data, len, pl); 00260 SockList_ResetRead(&ns->inbuf); 00261 return; 00262 } 00263 } 00264 /* If we get here, we didn't find a valid command. Logging 00265 * this might be questionable, because a broken client/malicious 00266 * user could certainly send a whole bunch of invalid commands. 00267 */ 00268 LOG(llevDebug, "Bad command from client (%s)\n", ns->inbuf.buf+2); 00269 SockList_ResetRead(&ns->inbuf); 00270 } 00271 } 00272 00273 /***************************************************************************** 00274 * 00275 * Low level socket looping - select calls and watchdog udp packet 00276 * sending. 00277 * 00278 ******************************************************************************/ 00279 00280 #ifdef WATCHDOG 00281 00287 void watchdog(void) { 00288 static int fd = -1; 00289 static struct sockaddr_in insock; 00290 00291 if (fd == -1) { 00292 struct protoent *protoent; 00293 00294 if ((protoent = getprotobyname("udp")) == NULL 00295 || (fd = socket(PF_INET, SOCK_DGRAM, protoent->p_proto)) == -1) { 00296 return; 00297 } 00298 insock.sin_family = AF_INET; 00299 insock.sin_port = htons((unsigned short)13325); 00300 insock.sin_addr.s_addr = inet_addr("127.0.0.1"); 00301 } 00302 sendto(fd, (void *)&fd, 1, 0, (struct sockaddr *)&insock, sizeof(insock)); 00303 } 00304 #endif 00305 00306 extern unsigned long todtick; 00307 00309 static void block_until_new_connection(void) { 00310 struct timeval Timeout; 00311 fd_set readfs; 00312 int cycles; 00313 00314 LOG(llevInfo, "Waiting for connections...\n"); 00315 00316 cycles = 1; 00317 do { 00318 /* Every minutes is a bit often for updates - especially if nothing is going 00319 * on. This slows it down to every 6 minutes. 00320 */ 00321 cycles++; 00322 if (cycles%2 == 0) 00323 tick_the_clock(); 00324 00325 FD_ZERO(&readfs); 00326 FD_SET((uint32)init_sockets[0].fd, &readfs); 00327 00328 /* If fastclock is set, we need to seriously slow down the updates 00329 * to the metaserver as well as watchdog. Do same for flush_old_maps() - 00330 * that is time sensitive, so there is no good reason to call it 2000 times 00331 * a second. 00332 */ 00333 if (settings.fastclock > 0) { 00334 #ifdef WATCHDOG 00335 if (cycles%120000 == 0) { 00336 watchdog(); 00337 flush_old_maps(); 00338 } 00339 #endif 00340 if (cycles == 720000) { 00341 metaserver_update(); 00342 cycles = 1; 00343 } 00344 Timeout.tv_sec = 0; 00345 Timeout.tv_usec = 50; 00346 } else { 00347 Timeout.tv_sec = 60; 00348 Timeout.tv_usec = 0; 00349 if (cycles == 7) { 00350 metaserver_update(); 00351 cycles = 1; 00352 } 00353 flush_old_maps(); 00354 } 00355 } while (select(socket_info.max_filedescriptor, &readfs, NULL, NULL, &Timeout) == 0); 00356 00357 reset_sleep(); /* Or the game would go too fast */ 00358 } 00359 00368 static int is_fd_valid(int fd) { 00369 #ifndef WIN32 00370 return fcntl(fd, F_GETFL) != -1 || errno != EBADF; 00371 #else 00372 return 1; 00373 #endif 00374 } 00375 00383 void do_server(void) { 00384 int i, pollret; 00385 fd_set tmp_read, tmp_exceptions, tmp_write; 00386 struct sockaddr_in addr; 00387 socklen_t addrlen = sizeof(struct sockaddr); 00388 player *pl, *next; 00389 char err[MAX_BUF]; 00390 00391 #ifdef CS_LOGSTATS 00392 if ((time(NULL)-cst_lst.time_start) >= CS_LOGTIME) 00393 write_cs_stats(); 00394 #endif 00395 00396 FD_ZERO(&tmp_read); 00397 FD_ZERO(&tmp_write); 00398 FD_ZERO(&tmp_exceptions); 00399 00400 for (i = 0; i < socket_info.allocated_sockets; i++) { 00401 if (init_sockets[i].status == Ns_Add && !is_fd_valid(init_sockets[i].fd)) { 00402 LOG(llevError, "do_server: invalid waiting fd %d\n", i); 00403 init_sockets[i].status = Ns_Dead; 00404 } 00405 if (init_sockets[i].status == Ns_Dead) { 00406 free_newsocket(&init_sockets[i]); 00407 init_sockets[i].status = Ns_Avail; 00408 socket_info.nconns--; 00409 } else if (init_sockets[i].status != Ns_Avail) { 00410 FD_SET((uint32)init_sockets[i].fd, &tmp_read); 00411 FD_SET((uint32)init_sockets[i].fd, &tmp_write); 00412 FD_SET((uint32)init_sockets[i].fd, &tmp_exceptions); 00413 } 00414 } 00415 00416 /* Go through the players. Let the loop set the next pl value, 00417 * since we may remove some 00418 */ 00419 for (pl = first_player; pl != NULL; ) { 00420 if (pl->socket.status != Ns_Dead && !is_fd_valid(pl->socket.fd)) { 00421 LOG(llevError, "do_server: invalid file descriptor for player %s [%s]: %d\n", (pl->ob && pl->ob->name) ? pl->ob->name : "(unnamed player?)", (pl->socket.host) ? pl->socket.host : "(unknown ip?)", pl->socket.fd); 00422 pl->socket.status = Ns_Dead; 00423 } 00424 00425 if (pl->socket.status == Ns_Dead) { 00426 player *npl = pl->next; 00427 00428 save_player(pl->ob, 0); 00429 if (!QUERY_FLAG(pl->ob, FLAG_REMOVED)) { 00430 terminate_all_pets(pl->ob); 00431 remove_ob(pl->ob); 00432 } 00433 leave(pl, 1); 00434 final_free_player(pl); 00435 pl = npl; 00436 } else { 00437 FD_SET((uint32)pl->socket.fd, &tmp_read); 00438 FD_SET((uint32)pl->socket.fd, &tmp_write); 00439 FD_SET((uint32)pl->socket.fd, &tmp_exceptions); 00440 pl = pl->next; 00441 } 00442 } 00443 00444 if (socket_info.nconns == 1 && first_player == NULL) 00445 block_until_new_connection(); 00446 00447 /* Reset timeout each time, since some OS's will change the values on 00448 * the return from select. 00449 */ 00450 socket_info.timeout.tv_sec = 0; 00451 socket_info.timeout.tv_usec = 0; 00452 00453 pollret = select(socket_info.max_filedescriptor, &tmp_read, &tmp_write, &tmp_exceptions, &socket_info.timeout); 00454 00455 if (pollret == -1) { 00456 LOG(llevError, "select failed: %s\n", strerror_local(errno, err, sizeof(err))); 00457 return; 00458 } 00459 00460 /* We need to do some of the processing below regardless */ 00461 /* if (!pollret) return;*/ 00462 00463 /* Following adds a new connection */ 00464 if (pollret && FD_ISSET(init_sockets[0].fd, &tmp_read)) { 00465 int newsocknum = 0; 00466 00467 #ifdef ESRV_DEBUG 00468 LOG(llevDebug, "do_server: New Connection\n"); 00469 #endif 00470 /* If this is the case, all sockets currently in used */ 00471 if (socket_info.allocated_sockets <= socket_info.nconns) { 00472 init_sockets = realloc(init_sockets, sizeof(socket_struct)*(socket_info.nconns+1)); 00473 if (!init_sockets) 00474 fatal(OUT_OF_MEMORY); 00475 newsocknum = socket_info.allocated_sockets; 00476 socket_info.allocated_sockets++; 00477 init_sockets[newsocknum].faces_sent_len = nrofpixmaps; 00478 init_sockets[newsocknum].faces_sent = calloc(1, nrofpixmaps*sizeof(*init_sockets[newsocknum].faces_sent)); 00479 if (!init_sockets[newsocknum].faces_sent) 00480 fatal(OUT_OF_MEMORY); 00481 init_sockets[newsocknum].status = Ns_Avail; 00482 } else { 00483 int j; 00484 00485 for (j = 1; j < socket_info.allocated_sockets; j++) 00486 if (init_sockets[j].status == Ns_Avail) { 00487 newsocknum = j; 00488 break; 00489 } 00490 } 00491 init_sockets[newsocknum].fd = accept(init_sockets[0].fd, (struct sockaddr *)&addr, &addrlen); 00492 if (init_sockets[newsocknum].fd == -1) { 00493 LOG(llevError, "accept failed: %s\n", strerror_local(errno, err, sizeof(err))); 00494 } else { 00495 char buf[MAX_BUF]; 00496 long ip; 00497 socket_struct *ns; 00498 00499 ns = &init_sockets[newsocknum]; 00500 00501 ip = ntohl(addr.sin_addr.s_addr); 00502 snprintf(buf, sizeof(buf), "%ld.%ld.%ld.%ld", (ip>>24)&255, (ip>>16)&255, (ip>>8)&255, ip&255); 00503 00504 if (checkbanned(NULL, buf)) { 00505 LOG(llevInfo, "Banned host tried to connect: [%s]\n", buf); 00506 close(init_sockets[newsocknum].fd); 00507 init_sockets[newsocknum].fd = -1; 00508 } else { 00509 init_connection(ns, buf); 00510 socket_info.nconns++; 00511 } 00512 } 00513 } 00514 00515 /* Check for any exceptions/input on the sockets */ 00516 if (pollret) 00517 for (i = 1; i < socket_info.allocated_sockets; i++) { 00518 if (init_sockets[i].status == Ns_Avail) 00519 continue; 00520 if (FD_ISSET(init_sockets[i].fd, &tmp_exceptions)) { 00521 free_newsocket(&init_sockets[i]); 00522 init_sockets[i].status = Ns_Avail; 00523 socket_info.nconns--; 00524 continue; 00525 } 00526 if (FD_ISSET(init_sockets[i].fd, &tmp_read)) { 00527 handle_client(&init_sockets[i], NULL); 00528 } 00529 if (FD_ISSET(init_sockets[i].fd, &tmp_write)) { 00530 init_sockets[i].can_write = 1; 00531 } 00532 } 00533 00534 /* This does roughly the same thing, but for the players now */ 00535 for (pl = first_player; pl != NULL; pl = next) { 00536 next = pl->next; 00537 if (pl->socket.status == Ns_Dead) 00538 continue; 00539 00540 if (FD_ISSET(pl->socket.fd, &tmp_write)) { 00541 if (!pl->socket.can_write) { 00542 pl->socket.can_write = 1; 00543 write_socket_buffer(&pl->socket); 00544 } 00545 /* if we get an error on the write_socket buffer, no reason to 00546 * continue on this socket. 00547 */ 00548 if (pl->socket.status == Ns_Dead) 00549 continue; 00550 } else 00551 pl->socket.can_write = 0; 00552 00553 if (FD_ISSET(pl->socket.fd, &tmp_exceptions)) { 00554 save_player(pl->ob, 0); 00555 if (!QUERY_FLAG(pl->ob, FLAG_REMOVED)) { 00556 terminate_all_pets(pl->ob); 00557 remove_ob(pl->ob); 00558 } 00559 leave(pl, 1); 00560 final_free_player(pl); 00561 } else { 00562 handle_client(&pl->socket, pl); 00563 00564 /* There seems to be rare cases where next points to a removed/freed player. 00565 * My belief is that this player does something (shout, move, whatever) 00566 * that causes data to be sent to the next player on the list, but 00567 * that player is defunct, so the socket codes removes that player. 00568 * End result is that next now points at the removed player, and 00569 * that has garbage data so we crash. So update the next pointer 00570 * while pl is still valid. MSW 2007-04-21 00571 */ 00572 next = pl->next; 00573 00574 00575 /* If the player has left the game, then the socket status 00576 * will be set to this be the leave function. We don't 00577 * need to call leave again, as it has already been called 00578 * once. 00579 */ 00580 if (pl->socket.status == Ns_Dead) { 00581 save_player(pl->ob, 0); 00582 if (!QUERY_FLAG(pl->ob, FLAG_REMOVED)) { 00583 terminate_all_pets(pl->ob); 00584 remove_ob(pl->ob); 00585 } 00586 leave(pl, 1); 00587 final_free_player(pl); 00588 } else { 00589 /* Update the players stats once per tick. More efficient than 00590 * sending them whenever they change, and probably just as useful 00591 */ 00592 esrv_update_stats(pl); 00593 if (pl->last_weight != -1 && pl->last_weight != WEIGHT(pl->ob)) { 00594 esrv_update_item(UPD_WEIGHT, pl->ob, pl->ob); 00595 if (pl->last_weight != WEIGHT(pl->ob)) 00596 LOG(llevError, "esrv_update_item(UPD_WEIGHT) did not set player weight: is %lu, should be %lu\n", (unsigned long)pl->last_weight, (unsigned long)WEIGHT(pl->ob)); 00597 } 00598 /* draw_client_map does sanity checking that map is 00599 * valid, so don't do it here. 00600 */ 00601 draw_client_map(pl->ob); 00602 if (pl->socket.update_look) 00603 esrv_draw_look(pl->ob); 00604 if (pl->socket.tick) 00605 send_tick(pl); 00606 00607 } 00608 } 00609 } 00610 }