Crossfire Server, Branch 1.12  R12190
loop.c
Go to the documentation of this file.
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 }