Crossfire Server, Branch 1.12  R12190
server.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_server_c =
00003  *    "$Id: server.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2006 Mark Wedel & Crossfire Development Team
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 authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00034 #include <global.h>
00035 #include <object.h>
00036 #include <tod.h>
00037 #include <version.h>
00038 
00039 #ifdef HAVE_DES_H
00040 #include <des.h>
00041 #else
00042 #  ifdef HAVE_CRYPT_H
00043 #  include <crypt.h>
00044 #  endif
00045 #endif
00046 
00047 #ifndef __CEXTRACT__
00048 #include <sproto.h>
00049 #endif
00050 
00051 #ifdef HAVE_TIME_H
00052 #include <time.h>
00053 #endif
00054 
00055 #ifndef WIN32
00056 #  include <unistd.h>
00057 #  include <sys/types.h>
00058 #endif
00059 
00060 #include <../random_maps/random_map.h>
00061 #include <../random_maps/rproto.h>
00062 #include "path.h"
00063 
00065 static const char days[7][4] = {
00066     "Sun",
00067     "Mon",
00068     "Tue",
00069     "Wed",
00070     "Thu",
00071     "Fri",
00072     "Sat"
00073 };
00074 
00081 void version(object *op) {
00082     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_VERSION,
00083                          "This is Crossfire v%s",
00084                          "This is Crossfire v%s",
00085                          FULL_VERSION);
00086     draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_VERSION,
00087                   "The authors can be reached at crossfire@metalforge.org", NULL);
00088 
00089 }
00090 
00097 void start_info(object *op) {
00098     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN,
00099                          "Welcome to Crossfire, v%s!\nPress `?' for help\n",
00100                          "Welcome to Crossfire, v%s!\nPress `?' for help\n",
00101                          VERSION);
00102 
00103     draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, op,
00104                          MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
00105                          "%s entered the game.",
00106                          "%s entered the game.",
00107                          op->name);
00108 
00109     if (!op->contr->name_changed) {
00110         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN,
00111                       "Note that you must set your name with the name command to enter the highscore list.", NULL);
00112     }
00113 }
00114 
00130 char *crypt_string(char *str, char *salt) {
00131 #if defined(WIN32) || (defined(__FreeBSD__) && !defined(HAVE_LIBDES))
00132     return(str);
00133 #else
00134     static const char *const c = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./";
00135     char s[2];
00136 
00137     if (salt == NULL)
00138         s[0] = c[RANDOM()%(int)strlen(c)],
00139         s[1] = c[RANDOM()%(int)strlen(c)];
00140     else
00141         s[0] = salt[0],
00142         s[1] = salt[1];
00143 
00144 #  ifdef HAVE_LIBDES
00145     return (char *)des_crypt(str, s);
00146 #  endif
00147     /* Default case - just use crypt */
00148     return (char *)crypt(str, s);
00149 #endif
00150 }
00151 
00162 int check_password(char *typed, char *crypted) {
00163     return !strcmp(crypt_string(typed, crypted), crypted);
00164 }
00165 
00175 void enter_player_savebed(object *op) {
00176     mapstruct *oldmap = op->map;
00177     object *tmp;
00178 
00179     tmp = get_object();
00180 
00181     EXIT_PATH(tmp) = add_string(op->contr->savebed_map);
00182     EXIT_X(tmp) = op->contr->bed_x;
00183     EXIT_Y(tmp) = op->contr->bed_y;
00184     enter_exit(op, tmp);
00185     /* If the player has not changed maps and the name does not match
00186      * that of the savebed, his savebed map is gone.  Lets go back
00187      * to the emergency path.  Update what the players savebed is
00188      * while we're at it.
00189      */
00190     if (oldmap == op->map && strcmp(op->contr->savebed_map, oldmap->path)) {
00191         LOG(llevDebug, "Player %s savebed location %s is invalid - going to emergency location (%s)\n", settings.emergency_mapname, op->name, op->contr->savebed_map);
00192         strcpy(op->contr->savebed_map, settings.emergency_mapname);
00193         op->contr->bed_x = settings.emergency_x;
00194         op->contr->bed_y = settings.emergency_y;
00195         free_string(op->contr->savebed_map);
00196         EXIT_PATH(tmp) = add_string(op->contr->savebed_map);
00197         EXIT_X(tmp) = op->contr->bed_x;
00198         EXIT_Y(tmp) = op->contr->bed_y;
00199         enter_exit(op, tmp);
00200     }
00201     free_object(tmp);
00202 }
00203 
00215 static void enter_map(object *op, mapstruct *newmap, int x, int y) {
00216     mapstruct *oldmap = op->map;
00217 
00218     if (out_of_map(newmap, x, y)) {
00219         LOG(llevError, "enter_map: supplied coordinates are not within the map! (%s: %d, %d)\n", newmap->path, x, y);
00220         x = MAP_ENTER_X(newmap);
00221         y = MAP_ENTER_Y(newmap);
00222         if (out_of_map(newmap, x, y)) {
00223             LOG(llevError, "enter_map: map %s provides invalid default enter location (%d, %d) > (%d, %d)\n", newmap->path, x, y, MAP_WIDTH(newmap), MAP_HEIGHT(newmap));
00224             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
00225                           "The exit is closed", NULL);
00226             return;
00227         }
00228     }
00229     /* try to find a spot for the player */
00230     if (ob_blocked(op, newmap, x, y)) {        /* First choice blocked */
00231         /* We try to find a spot for the player, starting closest in.
00232          * We could use find_first_free_spot, but that doesn't randomize it at all,
00233          * So for example, if the north space is free, you would always end up there even
00234          * if other spaces around are available.
00235          * Note that for the second and third calls, we could start at a position other
00236          * than one, but then we could end up on the other side of walls and so forth.
00237          */
00238         int i = find_free_spot(op, newmap, x, y, 1, SIZEOFFREE1+1);
00239         if (i == -1) {
00240             i = find_free_spot(op, newmap, x, y, 1, SIZEOFFREE2+1);
00241             if (i == -1)
00242                 i = find_free_spot(op, newmap, x, y, 1, SIZEOFFREE);
00243         }
00244         if (i != -1) {
00245             x += freearr_x[i];
00246             y += freearr_y[i];
00247         } else {
00248             /* not much we can do in this case. */
00249             LOG(llevInfo, "enter_map: Could not find free spot for player - will dump on top of object (%s: %d, %d)\n", newmap->path, x, y);
00250         }
00251     } /* end if looking for free spot */
00252 
00253     /* If it is a player login, he has yet to be inserted anyplace.
00254      * otherwise, we need to deal with removing the playe here.
00255      */
00256     if (!QUERY_FLAG(op, FLAG_REMOVED))
00257         remove_ob(op);
00258     if (op->map != NULL) {
00259         /* Lauwenmark : Here we handle the MAPLEAVE global event */
00260         execute_global_event(EVENT_MAPLEAVE, op, op->map);
00261     }
00262     /* remove_ob clears these so they must be reset after the remove_ob call */
00263     op->x = x;
00264     op->y = y;
00265     op->map = newmap;
00266     insert_ob_in_map(op, op->map, NULL, INS_NO_WALK_ON);
00267 
00268     /* Lauwenmark : Here we handle the MAPENTER global event */
00269     execute_global_event(EVENT_MAPENTER, op, op->map);
00270 
00271     if (op->contr) {
00272         send_background_music(op->contr, newmap->background_music);
00273     }
00274 
00275     newmap->timeout = 0;
00276     op->enemy = NULL;
00277 
00278     if (op->contr) {
00279         strcpy(op->contr->maplevel, newmap->path);
00280         op->contr->count = 0;
00281     }
00282 
00283     /* Update any golems */
00284     if (op->type == PLAYER && op->contr->ranges[range_golem] != NULL) {
00285         int i = find_free_spot(op->contr->ranges[range_golem], newmap, x, y, 1, SIZEOFFREE);
00286         remove_ob(op->contr->ranges[range_golem]);
00287         if (i == -1) {
00288             remove_friendly_object(op->contr->ranges[range_golem]);
00289             free_object(op->contr->ranges[range_golem]);
00290             op->contr->ranges[range_golem] = NULL;
00291             op->contr->golem_count = 0;
00292         } else {
00293             object *tmp;
00294 
00295             for (tmp = op->contr->ranges[range_golem]; tmp != NULL; tmp = tmp->more) {
00296                 tmp->x = x+freearr_x[i]+(tmp->arch == NULL ? 0 : tmp->arch->clone.x);
00297                 tmp->y = y+freearr_y[i]+(tmp->arch == NULL ? 0 : tmp->arch->clone.y);
00298                 tmp->map = newmap;
00299             }
00300             insert_ob_in_map(op->contr->ranges[range_golem], newmap, NULL, 0);
00301             op->contr->ranges[range_golem]->direction = find_dir_2(op->x-op->contr->ranges[range_golem]->x, op->y-op->contr->ranges[range_golem]->y);
00302         }
00303     }
00304     op->direction = 0;
00305 
00306     /* since the players map is already loaded, we don't need to worry
00307      * about pending objects.
00308      */
00309     remove_all_pets();
00310 
00311     /* If the player is changing maps, we need to do some special things
00312      * Do this after the player is on the new map - otherwise the force swap of the
00313      * old map does not work.
00314      */
00315     if (oldmap != newmap) {
00316         if (oldmap) { /* adjust old map */
00317             if (oldmap->players <= 0) /* can be less than zero due to errors in tracking this */
00318                 set_map_timeout(oldmap);
00319         }
00320     }
00321     swap_below_max(newmap->path);
00322 
00323     if (op->type == PLAYER)
00324         map_newmap_cmd(&op->contr->socket);
00325 }
00326 
00333 void set_map_timeout(mapstruct *oldmap) {
00334 #if MAP_MAXTIMEOUT
00335     oldmap->timeout = MAP_TIMEOUT(oldmap);
00336     /* Do MINTIMEOUT first, so that MAXTIMEOUT is used if that is
00337      * lower than the min value.
00338      */
00339 #if MAP_MINTIMEOUT
00340     if (oldmap->timeout < MAP_MINTIMEOUT) {
00341         oldmap->timeout = MAP_MINTIMEOUT;
00342     }
00343 #endif
00344     if (oldmap->timeout > MAP_MAXTIMEOUT) {
00345         oldmap->timeout = MAP_MAXTIMEOUT;
00346     }
00347 #else
00348     /* save out the map */
00349     swap_map(oldmap);
00350 #endif /* MAP_MAXTIMEOUT */
00351 }
00352 
00366 static char *clean_path(const char *file, char *newpath, int size) {
00367     char *cp;
00368 
00369     snprintf(newpath, size, "%s", file);
00370     for (cp = newpath; *cp != '\0'; cp++) {
00371         if (*cp == '/')
00372             *cp = '_';
00373     }
00374     return newpath;
00375 }
00376 
00394 static char *unclean_path(const char *src, char *newpath, int size) {
00395     char *cp;
00396 
00397     cp = strrchr(src, '/');
00398     if (cp)
00399         snprintf(newpath, size, "%s", cp+1);
00400     else
00401         snprintf(newpath, size, "%s", src);
00402 
00403     for (cp = newpath; *cp != '\0'; cp++) {
00404         if (*cp == '_')
00405             *cp = '/';
00406     }
00407     return newpath;
00408 }
00409 
00410 
00420 static void enter_random_map(object *pl, object *exit_ob) {
00421     mapstruct *new_map;
00422     char newmap_name[HUGE_BUF], *cp;
00423     static int reference_number = 0;
00424     RMParms rp;
00425 
00426     memset(&rp, 0, sizeof(RMParms));
00427     rp.Xsize = -1;
00428     rp.Ysize = -1;
00429     rp.region = get_region_by_map(exit_ob->map);
00430     if (exit_ob->msg)
00431         set_random_map_variable(&rp, exit_ob->msg);
00432     rp.origin_x = exit_ob->x;
00433     rp.origin_y = exit_ob->y;
00434     strcpy(rp.origin_map, pl->map->path);
00435 
00436     /* If we have a final_map, use it as a base name to give some clue
00437      * as where the player is.  Otherwise, use the origin map.
00438      * Take the last component (after the last slash) to give
00439      * shorter names without bogus slashes.
00440      */
00441     if (rp.final_map[0]) {
00442         cp = strrchr(rp.final_map, '/');
00443         if (!cp)
00444             cp = rp.final_map;
00445     } else {
00446         char buf[HUGE_BUF];
00447 
00448         cp = strrchr(rp.origin_map, '/');
00449         if (!cp)
00450             cp = rp.origin_map;
00451         /* Need to strip of any trailing digits, if it has them */
00452         snprintf(buf, sizeof(buf), "%s", cp);
00453         while (isdigit(buf[strlen(buf)-1]))
00454             buf[strlen(buf)-1] = 0;
00455         cp = buf;
00456     }
00457 
00458     snprintf(newmap_name, sizeof(newmap_name), "/random/%s%04d", cp+1, reference_number++);
00459 
00460     /* now to generate the actual map. */
00461     new_map = generate_random_map(newmap_name, &rp, NULL);
00462 
00463     /* Update the exit_ob so it now points directly at the newly created
00464      * random maps.  Not that it is likely to happen, but it does mean that a
00465      * exit in a unique map leading to a random map will not work properly.
00466      * It also means that if the created random map gets reset before
00467      * the exit leading to it, that the exit will no longer work.
00468      */
00469     if (new_map) {
00470         int x, y;
00471 
00472         x = EXIT_X(exit_ob) = MAP_ENTER_X(new_map);
00473         y = EXIT_Y(exit_ob) = MAP_ENTER_Y(new_map);
00474         EXIT_PATH(exit_ob) = add_string(newmap_name);
00475         snprintf(new_map->path, sizeof(new_map->path), "%s", newmap_name);
00476         enter_map(pl, new_map, x, y);
00477     }
00478 }
00479 
00489 static void enter_fixed_template_map(object *pl, object *exit_ob) {
00490     mapstruct *new_map;
00491     char tmpnum[32], exitpath[HUGE_BUF], resultname[HUGE_BUF], tmpstring[HUGE_BUF], *sourcemap;
00492     char new_map_name[MAX_BUF];
00493 
00494     /* Split the exit path string into two parts, one
00495      * for where to store the map, and one for were
00496      * to generate the map from.
00497      */
00498     snprintf(exitpath, sizeof(exitpath), "%s", EXIT_PATH(exit_ob)+2);
00499     sourcemap = strchr(exitpath, '!');
00500     if (!sourcemap) {
00501         draw_ext_info_format(NDI_UNIQUE, 0, pl,
00502                              MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
00503                              "The %s is closed.",
00504                              "The %s is closed.",
00505                              exit_ob->name);
00506         /* Should only occur when no source map is set.
00507          */
00508         LOG(llevError, "enter_fixed_template_map: Exit %s (%d,%d) on map %s has no source template.\n", exit_ob->name, exit_ob->x, exit_ob->y, exit_ob->map->path);
00509         return;
00510     }
00511     *sourcemap++ = '\0';
00512 
00513     /* If we are not coming from a template map, we can use relative directories
00514      * for the map to generate from.
00515      */
00516     if (!exit_ob->map->is_template) {
00517         /* We can't use exitpath directly, as sourcemap points there. */
00518         path_combine_and_normalize(exit_ob->map->path, sourcemap, tmpstring, sizeof(tmpstring));
00519         snprintf(exitpath, sizeof(exitpath), "%s", tmpstring);
00520         sourcemap = exitpath;
00521     }
00522 
00523     /* Do replacement of %x, %y, and %n to the x coord of the exit, the y coord
00524      * of the exit, and the name of the map the exit is on, respectively.
00525      */
00526     snprintf(tmpnum, sizeof(tmpnum), "%d", exit_ob->x);
00527     replace(exitpath, "%x", tmpnum, resultname,  sizeof(resultname));
00528 
00529     snprintf(tmpnum, sizeof(tmpnum), "%d", exit_ob->y);
00530     snprintf(tmpstring, sizeof(tmpstring), "%s", resultname);
00531     replace(tmpstring, "%y", tmpnum, resultname,  sizeof(resultname));
00532 
00533     snprintf(tmpstring, sizeof(tmpstring), "%s", resultname);
00534     replace(tmpstring, "%n", exit_ob->map->name, resultname,  sizeof(resultname));
00535 
00536     /* If we are coming from another template map, use reletive paths unless
00537      * indicated otherwise.
00538      */
00539     if (exit_ob->map->is_template && (resultname[0] != '/')) {
00540         path_combine_and_normalize(exit_ob->map->path, resultname, new_map_name, sizeof(new_map_name));
00541     } else {
00542         create_template_pathname(resultname, new_map_name, sizeof(new_map_name));
00543     }
00544 
00545     /* Attempt to load the map, if unable to, then
00546      * create the map from the template.
00547      */
00548     new_map = ready_map_name(new_map_name, MAP_PLAYER_UNIQUE);
00549     if (!new_map) {
00550         char path[MAX_BUF];
00551 
00552         create_pathname(sourcemap, path, MAX_BUF);
00553         new_map = load_original_map(path, MAP_PLAYER_UNIQUE);
00554         if (new_map)
00555             fix_auto_apply(new_map);
00556     }
00557 
00558     if (new_map) {
00559         /* set the path of the map to where it should be
00560          * so we don't just save over the source map.
00561          */
00562         snprintf(new_map->path, sizeof(new_map->path), "%s", new_map_name);
00563         new_map->is_template = 1;
00564         enter_map(pl, new_map, EXIT_X(exit_ob), EXIT_Y(exit_ob));
00565     } else {
00566         draw_ext_info_format(NDI_UNIQUE, 0, pl,
00567                              MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
00568                              "The %s is closed.",
00569                              "The %s is closed.",
00570                              exit_ob->name);
00571         /* Should only occur when an invalid source map is set.
00572          */
00573         LOG(llevDebug, "enter_fixed_template_map: Exit %s (%d,%d) on map %s leads no where.\n", exit_ob->name, exit_ob->x, exit_ob->y, exit_ob->map->path);
00574     }
00575 }
00576 
00586 static void enter_random_template_map(object *pl, object *exit_ob) {
00587     mapstruct *new_map;
00588     char tmpnum[32], resultname[HUGE_BUF], tmpstring[HUGE_BUF];
00589     char new_map_name[MAX_BUF];
00590     RMParms rp;
00591 
00592     /* Do replacement of %x, %y, and %n to the x coord of the exit, the y coord
00593      * of the exit, and the name of the map the exit is on, respectively.
00594      */
00595     snprintf(tmpnum, sizeof(tmpnum), "%d", exit_ob->x);
00596     replace(EXIT_PATH(exit_ob)+3, "%x", tmpnum, resultname,  sizeof(resultname));
00597 
00598     snprintf(tmpnum, sizeof(tmpnum), "%d", exit_ob->y);
00599     snprintf(tmpstring, sizeof(tmpstring), "%s", resultname);
00600     replace(tmpstring, "%y", tmpnum, resultname,  sizeof(resultname));
00601 
00602     snprintf(tmpstring, sizeof(tmpstring), "%s", resultname);
00603     replace(tmpstring, "%n", exit_ob->map->name, resultname,  sizeof(resultname));
00604 
00605     /* If we are coming from another template map, use reletive paths unless
00606      * indicated otherwise.
00607      */
00608     if (exit_ob->map->is_template && (resultname[0] != '/')) {
00609         path_combine_and_normalize(exit_ob->map->path, resultname, new_map_name, sizeof(new_map_name));
00610     } else {
00611         create_template_pathname(resultname, new_map_name, sizeof(new_map_name));
00612     }
00613 
00614     new_map = ready_map_name(new_map_name, MAP_PLAYER_UNIQUE);
00615     if (!new_map) {
00616         memset(&rp, 0, sizeof(RMParms));
00617         rp.Xsize = -1;
00618         rp.Ysize = -1;
00619         rp.region = get_region_by_map(exit_ob->map);
00620         if (exit_ob->msg)
00621             set_random_map_variable(&rp, exit_ob->msg);
00622         rp.origin_x = exit_ob->x;
00623         rp.origin_y = exit_ob->y;
00624         strcpy(rp.origin_map, pl->map->path);
00625 
00626         /* now to generate the actual map. */
00627         new_map = generate_random_map(new_map_name, &rp, NULL);
00628     }
00629 
00630     /* Update the exit_ob so it now points directly at the newly created
00631      * random maps.  Not that it is likely to happen, but it does mean that a
00632      * exit in a unique map leading to a random map will not work properly.
00633      * It also means that if the created random map gets reset before
00634      * the exit leading to it, that the exit will no longer work.
00635      */
00636     if (new_map) {
00637         int x, y;
00638 
00639         x = EXIT_X(exit_ob) = MAP_ENTER_X(new_map);
00640         y = EXIT_Y(exit_ob) = MAP_ENTER_Y(new_map);
00641         new_map->is_template = 1;
00642         enter_map(pl, new_map, x, y);
00643     }
00644 }
00645 
00654 static void enter_unique_map(object *op, object *exit_ob) {
00655     char apartment[HUGE_BUF], path[MAX_BUF];
00656     mapstruct        *newmap;
00657 
00658     if (EXIT_PATH(exit_ob)[0] == '/') {
00659         snprintf(apartment, sizeof(apartment), "%s/%s/%s/%s", settings.localdir, settings.playerdir, op->name, clean_path(EXIT_PATH(exit_ob), path, sizeof(path)));
00660         newmap = ready_map_name(apartment, MAP_PLAYER_UNIQUE);
00661         if (!newmap) {
00662             create_pathname(EXIT_PATH(exit_ob), path, sizeof(path));
00663             newmap = load_original_map(path, MAP_PLAYER_UNIQUE);
00664             if (newmap)
00665                 fix_auto_apply(newmap);
00666         }
00667     } else { /* relative directory */
00668         char reldir[HUGE_BUF], tmpc[HUGE_BUF], *cp;
00669 
00670         if (exit_ob->map->unique) {
00671 
00672             unclean_path(exit_ob->map->path, reldir, sizeof(reldir));
00673 
00674             /* Need to copy this over, as clean_path only has one static return buffer */
00675             clean_path(reldir, tmpc, sizeof(tmpc));
00676             /* Remove final component, if any */
00677             if ((cp = strrchr(tmpc, '_')) != NULL)
00678                 *cp = 0;
00679 
00680             snprintf(apartment, sizeof(apartment), "%s/%s/%s/%s_%s", settings.localdir, settings.playerdir, op->name, tmpc, clean_path(EXIT_PATH(exit_ob), path, sizeof(path)));
00681 
00682             newmap = ready_map_name(apartment, MAP_PLAYER_UNIQUE);
00683             if (!newmap) {
00684                 create_pathname(path_combine_and_normalize(reldir, EXIT_PATH(exit_ob), tmpc, sizeof(tmpc)), path, sizeof(path));
00685                 newmap = load_original_map(path, MAP_PLAYER_UNIQUE);
00686                 if (newmap)
00687                     fix_auto_apply(newmap);
00688             }
00689         } else {
00690             /* The exit is unique, but the map we are coming from is not unique.  So
00691              * use the basic logic - don't need to demangle the path name
00692              */
00693             path_combine_and_normalize(exit_ob->map->path, EXIT_PATH(exit_ob), reldir, sizeof(reldir));
00694             snprintf(apartment, sizeof(apartment), "%s/%s/%s/%s", settings.localdir, settings.playerdir, op->name, clean_path(reldir, path, sizeof(path)));
00695             newmap = ready_map_name(apartment, MAP_PLAYER_UNIQUE);
00696             if (!newmap) {
00697                 path_combine_and_normalize(exit_ob->map->path, EXIT_PATH(exit_ob), reldir, sizeof(reldir));
00698                 newmap = ready_map_name(reldir, 0);
00699                 if (newmap)
00700                     fix_auto_apply(newmap);
00701             }
00702         }
00703     }
00704 
00705     if (newmap) {
00706         snprintf(newmap->path, sizeof(newmap->path), "%s", apartment);
00707         newmap->unique = 1;
00708         enter_map(op, newmap, EXIT_X(exit_ob), EXIT_Y(exit_ob));
00709     } else {
00710         draw_ext_info_format(NDI_UNIQUE, 0, op,
00711                              MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
00712                              "The %s is closed.",
00713                              "The %s is closed.",
00714                              exit_ob->name);
00715         /* Perhaps not critical, but I would think that the unique maps
00716          * should be new enough this does not happen.  This also creates
00717          * a strange situation where some players could perhaps have visited
00718          * such a map before it was removed, so they have the private
00719          * map, but other players can't get it anymore.
00720          */
00721         LOG(llevDebug, "enter_unique_map: Exit %s (%d,%d) on map %s is leads no where.\n", exit_ob->name, exit_ob->x, exit_ob->y, exit_ob->map->path);
00722     }
00723 
00724 }
00725 
00740 void enter_exit(object *op, object *exit_ob) {
00741 #define PORTAL_DESTINATION_NAME "Town portal destination" /* this one should really be in a header file */
00742     object *tmp;
00743     /* It may be nice to support other creatures moving across
00744      * exits, but right now a lot of the code looks at op->contr,
00745      * so thta is an RFE.
00746      */
00747     if (op->type != PLAYER)
00748         return;
00749 
00750     /* Need to remove player from transport */
00751     if (op->contr->transport)
00752         ob_apply(op->contr->transport, op, AP_UNAPPLY);
00753 
00754     /* First, lets figure out what map the player is going to go to */
00755     if (exit_ob) {
00756         /* check to see if we make a template map */
00757         if (EXIT_PATH(exit_ob) && EXIT_PATH(exit_ob)[1] == '@') {
00758             if (EXIT_PATH(exit_ob)[2] == '!') {
00759                 /* generate a template map randomly */
00760                 enter_random_template_map(op, exit_ob);
00761             } else {
00762                 /* generate a template map from a fixed template */
00763                 enter_fixed_template_map(op, exit_ob);
00764             }
00765         }
00766         /* check to see if we make a randomly generated map */
00767         else if (EXIT_PATH(exit_ob) && EXIT_PATH(exit_ob)[1] == '!') {
00768             enter_random_map(op, exit_ob);
00769         } else if (QUERY_FLAG(exit_ob, FLAG_UNIQUE)) {
00770             enter_unique_map(op, exit_ob);
00771         } else {
00772             int x = EXIT_X(exit_ob), y = EXIT_Y(exit_ob);
00773             /* 'Normal' exits that do not do anything special
00774             * Simple enough we don't need another routine for it.
00775             */
00776             mapstruct *newmap;
00777             if (exit_ob->map) {
00778                 char tmp_path[HUGE_BUF];
00779 
00780                 path_combine_and_normalize(exit_ob->map->path, EXIT_PATH(exit_ob), tmp_path, sizeof(tmp_path));
00781                 newmap = ready_map_name(tmp_path, 0);
00782                 /* Random map was previously generated, but is no longer about.  Lets generate a new
00783                  * map.
00784                  */
00785                 if (!newmap && !strncmp(EXIT_PATH(exit_ob), "/random/", 8)) {
00786                     /* Maps that go down have a message set.  However, maps that go
00787                      * up, don't.  If the going home has reset, there isn't much
00788                      * point generating a random map, because it won't match the maps.
00789                      */
00790                     if (exit_ob->msg) {
00791                         enter_random_map(op, exit_ob);
00792                     } else {
00793                         draw_ext_info_format(NDI_UNIQUE, 0, op,
00794                                              MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
00795                                              "The %s is closed.",
00796                                              "The %s is closed.",
00797                                              exit_ob->name);
00798                         return;
00799                     }
00800 
00801                     /* For exits that cause damages (like pits).  Don't know if any
00802                      * random maps use this or not.
00803                      */
00804                     if (exit_ob->stats.dam && op->type == PLAYER)
00805                         hit_player(op, exit_ob->stats.dam, exit_ob, exit_ob->attacktype, 1);
00806                     return;
00807                 }
00808             } else {
00809                 /* For word of recall and other force objects
00810                  * They contain the full pathname of the map to go back to,
00811                  * so we don't need to normalize it.
00812                  * But we do need to see if it is unique or not
00813                  */
00814                 if (!strncmp(EXIT_PATH(exit_ob), settings.localdir, strlen(settings.localdir)))
00815                     newmap = ready_map_name(EXIT_PATH(exit_ob), MAP_PLAYER_UNIQUE);
00816                 else
00817                     newmap = ready_map_name(EXIT_PATH(exit_ob), 0);
00818             }
00819             if (!newmap) {
00820                 if (exit_ob->name)
00821                     draw_ext_info_format(NDI_UNIQUE, 0, op,
00822                                          MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
00823                                          "The %s is closed.",
00824                                          "The %s is closed.",
00825                                          exit_ob->name);
00826                 /* don't cry to momma if name is not set - as in tmp objects
00827                  * used by the savebed code and character creation */
00828                 return;
00829             }
00830 
00831             /* This supports the old behaviour, but it really should not be used.
00832              * I will note for example that with this method, it is impossible to
00833              * set 0,0 destination coordinates.  Really, if we want to support
00834              * using the new maps default coordinates, the exit ob should use
00835              * something like -1, -1 so it is clear to do that.
00836              */
00837             if (x == 0 && y == 0) {
00838                 x = MAP_ENTER_X(newmap);
00839                 y = MAP_ENTER_Y(newmap);
00840                 LOG(llevDebug, "enter_exit: Exit %s (%d,%d) on map %s is 0 destination coordinates\n",
00841                     exit_ob->name ? exit_ob->name : "(none)", exit_ob->x, exit_ob->y,
00842                     exit_ob->map ? exit_ob->map->path : "(none)");
00843             }
00844 
00845             /* mids 02/13/2002 if exit is damned, update players death & WoR home-position and delete town portal */
00846             if (QUERY_FLAG(exit_ob, FLAG_DAMNED)) {
00847                 /* remove an old force with a slaying field == PORTAL_DESTINATION_NAME */
00848                 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
00849                     if (tmp->type == FORCE && tmp->slaying && !strcmp(tmp->slaying, PORTAL_DESTINATION_NAME))
00850                         break;
00851                 }
00852                 if (tmp) {
00853                     remove_ob(tmp);
00854                     free_object(tmp);
00855                 }
00856 
00857                 path_combine_and_normalize(exit_ob->map->path, EXIT_PATH(exit_ob), op->contr->savebed_map, sizeof(op->contr->savebed_map));
00858                 op->contr->bed_x = EXIT_X(exit_ob), op->contr->bed_y = EXIT_Y(exit_ob);
00859                 save_player(op, 1);
00860                 /* LOG(llevDebug, "enter_exit: Taking damned exit %s to (%d,%d) on map %s\n",
00861                  *     exit_ob->name ? exit_ob->name : "(none)", exit_ob->x, exit_ob->y,
00862                  *     path_combine_and_normalize(exit_ob->map->path, EXIT_PATH(exit_ob))); */
00863             }
00864 
00865             enter_map(op, newmap, x, y);
00866         }
00867         /* For exits that cause damages (like pits) */
00868         if (exit_ob->stats.dam && op->type == PLAYER)
00869             hit_player(op, exit_ob->stats.dam, exit_ob, exit_ob->attacktype, 1);
00870     } else {
00871         int flags = 0;
00872         mapstruct *newmap;
00873 
00874         /* Hypothetically, I guess its possible that a standard map matches
00875          * the localdir, but that seems pretty unlikely - unlikely enough that
00876          * I'm not going to attempt to try to deal with that possibility.
00877          * We use the fact that when a player saves on a unique map, it prepends
00878          * the localdir to that name.  So its an easy way to see of the map is
00879          * unique or not.
00880          */
00881         if (!strncmp(op->contr->maplevel, settings.localdir, strlen(settings.localdir)))
00882             flags = MAP_PLAYER_UNIQUE;
00883 
00884         /* newmap returns the map (if already loaded), or loads it for us. */
00885         newmap = ready_map_name(op->contr->maplevel, flags);
00886         if (!newmap) {
00887             LOG(llevError, "enter_exit: Pathname to map does not exist! (%s)\n", op->contr->maplevel);
00888             newmap = ready_map_name(settings.emergency_mapname, 0);
00889             op->x = settings.emergency_x;
00890             op->y = settings.emergency_y;
00891             /* If we can't load the emergency map, something is probably really
00892              * screwed up, so bail out now.
00893              */
00894             if (!newmap) {
00895                 LOG(llevError, "enter_exit: could not load emergency map? Fatal error\n");
00896                 abort();
00897             }
00898         }
00899         enter_map(op, newmap, op->x, op->y);
00900     }
00901 }
00902 
00908 static void process_players1(void) {
00909     int flag;
00910     player *pl, *plnext;
00911 
00912     /* Basically, we keep looping until all the players have done their actions. */
00913     for (flag = 1; flag != 0; ) {
00914         flag = 0;
00915         for (pl = first_player; pl != NULL; pl = plnext) {
00916             plnext = pl->next; /* In case a player exits the game in handle_player() */
00917 
00918             if (pl->ob == NULL)
00919                 continue;
00920 
00922             if (pl->followed_player) {
00923                 player *followed = find_player_partial_name(pl->followed_player);
00924                 if (followed && followed->ob && followed->ob->map) {
00925                     rv_vector rv;
00926 
00927                     get_rangevector(pl->ob, followed->ob, &rv, 0);
00928                     if (rv.distance > 4) {
00929                         int space = find_free_spot(pl->ob, followed->ob->map, followed->ob->x, followed->ob->y, 1, 25);
00930                         if (space == -1)
00932                             space = 0;
00933                         remove_ob(pl->ob);
00934                         insert_ob_in_map_at(pl->ob, followed->ob->map, NULL, 0, followed->ob->x+freearr_x[space], followed->ob->y+freearr_y[space]);
00935                         map_newmap_cmd(&pl->socket);
00936                     }
00937                 } else {
00938                     draw_ext_info_format(NDI_UNIQUE, 0, pl->ob, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_DM, "Player %s left or ambiguous name.", NULL, pl->followed_player);
00939                     FREE_AND_CLEAR_STR(pl->followed_player);
00940                 }
00941             } 
00943             if (pl->ob->speed_left > 0) {
00944                 if (handle_newcs_player(pl->ob))
00945                     flag = 1;
00946             } /* end if player has speed left */
00947 
00948             /* If the player is not actively playing, don't make a
00949              * backup save - nothing to save anyway.  Plus, the
00950              * map may not longer be valid.  This can happen when the
00951              * player quits - they exist for purposes of tracking on the map,
00952              * but don't actually reside on any actual map.
00953              */
00954             if (QUERY_FLAG(pl->ob, FLAG_REMOVED))
00955                 continue;
00956 
00957 #ifdef AUTOSAVE
00958             /* check for ST_PLAYING state so that we don't try to save off when
00959              * the player is logging in.
00960              */
00961             if ((pl->last_save_tick+AUTOSAVE) < pticks && pl->state == ST_PLAYING) {
00962                 /* Don't save the player on unholy ground.  Instead, increase the
00963                  * tick time so it will be about 10 seconds before we try and save
00964                  * again.
00965                  */
00966                 if (get_map_flags(pl->ob->map, NULL, pl->ob->x, pl->ob->y, NULL, NULL)&P_NO_CLERIC) {
00967                     pl->last_save_tick += 100;
00968                 } else {
00969                     save_player(pl->ob, 1);
00970                     pl->last_save_tick = pticks;
00971                     check_score(pl->ob, 1);
00972                 }
00973             }
00974 #endif
00975         } /* end of for loop for all the players */
00976     } /* for flag */
00977     for(pl=first_player;pl!=NULL;pl=pl->next) {
00978         pl->socket.sounds_this_tick = 0;
00979         if (settings.casting_time == TRUE) {
00980             if (pl->ob->casting_time > 0){
00981                 pl->ob->casting_time--;
00982             }
00983         }
00984         do_some_living(pl->ob);
00985 /*      draw(pl->ob);*/ /* updated in socket code */
00986     }
00987 }
00988 
00996 static void process_players2(void) {
00997     player *pl;
00998 
00999     /* Then check if any players should use weapon-speed instead of speed */
01000     for (pl=first_player;pl!=NULL;pl=pl->next) {
01001 
01002         /* The code that did weapon_sp handling here was out of place -
01003          * this isn't called until after the player has finished there
01004          * actions, and is thus out of place.  All we do here is bounds
01005          * checking.
01006          */
01007         if (pl->has_hit) {
01008             if (pl->ob->speed_left > pl->weapon_sp) pl->ob->speed_left = pl->weapon_sp;
01009 
01010             /* This needs to be here - if the player is running, we need to
01011              * clear this each tick, but new commands are not being received
01012              * so execute_newserver_command() is never called
01013              */
01014             pl->has_hit=0;
01015 
01016         } else if (pl->ob->speed_left > pl->ob->speed)
01017             pl->ob->speed_left = pl->ob->speed;
01018     }
01019 }
01020 
01021 #define SPEED_DEBUG
01022 
01026 void process_events(void) {
01027     object *op;
01028     object marker;
01029     tag_t tag;
01030 
01031     process_players1();
01032 
01033     memset(&marker, 0, sizeof(object));
01034     /* Put marker object at beginning of active list */
01035     marker.active_next = active_objects;
01036 
01037     if (marker.active_next)
01038         marker.active_next->active_prev = &marker;
01039     marker.active_prev = NULL;
01040     active_objects = &marker;
01041 
01042     while (marker.active_next) {
01043         op = marker.active_next;
01044         tag = op->count;
01045 
01046         /* Move marker forward - swap op and marker */
01047         op->active_prev = marker.active_prev;
01048 
01049         if (op->active_prev)
01050             op->active_prev->active_next = op;
01051         else
01052             active_objects = op;
01053 
01054         marker.active_next = op->active_next;
01055 
01056         if (marker.active_next)
01057             marker.active_next->active_prev = &marker;
01058         marker.active_prev = op;
01059         op->active_next = &marker;
01060 
01061         /* Now process op */
01062         if (QUERY_FLAG(op, FLAG_FREED)) {
01063             LOG(llevError, "BUG: process_events(): Free object on list\n");
01064             op->speed = 0;
01065             update_ob_speed(op);
01066             continue;
01067         }
01068 
01069         /* I've seen occasional crashes due to this - the object is removed,
01070          * and thus the map it points to (last map it was on) may be bogus
01071          * The real bug is to try to find out the cause of this - someone
01072          * is probably calling remove_ob without either an insert_ob or
01073          * free_object afterwards, leaving an object dangling.  But I'd
01074          * rather log this and continue on instead of crashing.
01075          * Don't remove players - when a player quits, the object is in
01076          * sort of a limbo, of removed, but something we want to keep
01077          * around.
01078          */
01079         if (QUERY_FLAG(op, FLAG_REMOVED)
01080         && op->type != PLAYER
01081         && op->map
01082         && op->map->in_memory != MAP_IN_MEMORY) {
01083             StringBuffer *sb;
01084             char *diff;
01085 
01086             LOG(llevError, "BUG: process_events(): Removed object on list\n");
01087             sb = stringbuffer_new();
01088             dump_object(op, sb);
01089             diff = stringbuffer_finish(sb);
01090             LOG(llevError, "%s\n", diff);
01091             free(diff);
01092             free_object(op);
01093             continue;
01094         }
01095 
01096         if (!op->speed) {
01097             LOG(llevError, "BUG: process_events(): Object %s has no speed, but is on active list\n", op->arch->name);
01098             update_ob_speed(op);
01099             continue;
01100         }
01101 
01102         if (op->map == NULL
01103         && op->env == NULL
01104         && op->name
01105         && op->type != MAP) {
01106             LOG(llevError, "BUG: process_events(): Object without map or inventory is on active list: %s (%d)\n", op->name, op->count);
01107             op->speed = 0;
01108             update_ob_speed(op);
01109             continue;
01110         }
01111 
01112         /* Seen some cases where process_object() is crashing because
01113          * the object is on a swapped out map.  But can't be sure if
01114          * something in the chain of events caused the object to
01115          * change maps or was just never removed - this will
01116          * give some clue as to its state before call to
01117          * process_object
01118          */
01119         if (op->map && op->map->in_memory != MAP_IN_MEMORY) {
01120             LOG(llevError, "BUG: process_events(): Processing object on swapped out map: %s (%d), map=%s\n", op->name, op->count, op->map->path);
01121         }
01122 
01123         /* Animate the object.  Bug of feature that andim_speed
01124          * is based on ticks, and not the creatures speed?
01125          */
01126         if ((op->anim_speed && op->last_anim >= op->anim_speed)
01127         || (op->temp_animation_id && op->last_anim >= op->temp_anim_speed)) {
01128             op->state++;
01129             if ((op->type == PLAYER) || (op->type == MONSTER))
01130                 animate_object(op, op->facing);
01131             else
01132                 animate_object(op, op->direction);
01133             op->last_anim = 1;
01134         } else {
01135             op->last_anim++;
01136         }
01137 
01138         if (op->speed_left > 0) {
01139             --op->speed_left;
01140             process_object(op);
01141             if (was_destroyed(op, tag))
01142                 continue;
01143         }
01144         if (settings.casting_time == TRUE && op->casting_time > 0)
01145             op->casting_time--;
01146         if (op->speed_left <= 0)
01147             op->speed_left += FABS(op->speed);
01148     }
01149 
01150     /* Remove marker object from active list */
01151     if (marker.active_prev != NULL)
01152         marker.active_prev->active_next = NULL;
01153     else
01154         active_objects = NULL;
01155 
01156     process_players2();
01157 }
01158 
01164 void clean_tmp_files(void) {
01165     mapstruct *m, *next;
01166 
01167     LOG(llevInfo, "Cleaning up...\n");
01168 
01169     /* We save the maps - it may not be intuitive why, but if there are unique
01170      * items, we need to save the map so they get saved off.  Perhaps we should
01171      * just make a special function that only saves the unique items.
01172      */
01173     for (m = first_map; m != NULL; m = next) {
01174         next = m->next;
01175         if (m->in_memory == MAP_IN_MEMORY) {
01176             /* If we want to reuse the temp maps, swap it out (note that will also
01177              * update the log file.  Otherwise, save the map (mostly for unique item
01178              * stuff).  Note that the clean_tmp_map is called after the end of
01179              * the for loop but is in the #else bracket.  IF we are recycling the maps,
01180              * we certainly don't want the temp maps removed.
01181              */
01182 
01183             /* XXX The above comment is dead wrong */
01184             if (settings.recycle_tmp_maps == TRUE)
01185                 swap_map(m);
01186             else {
01187                 save_map(m, SAVE_MODE_NORMAL); /* note we save here into a overlay map */
01188                 clean_tmp_map(m);
01189             }
01190         }
01191     }
01192     write_todclock(); /* lets just write the clock here */
01193 }
01194 
01196 void cleanup(void) {
01197     LOG(llevDebug, "Cleanup called.  freeing data.\n");
01198     clean_tmp_files();
01199     write_book_archive();
01200 
01201 #ifdef MEMORY_DEBUG
01202     free_all_maps();
01203     free_style_maps();
01204 #endif
01205     cleanupPlugins();
01206 #ifdef MEMORY_DEBUG
01207     free_all_archs();
01208     free_all_treasures();
01209     free_all_images();
01210     free_all_newserver();
01211     free_all_recipes();
01212     free_all_readable();
01213     free_all_god();
01214     free_all_anim();
01215     free_loader();
01216     free_globals();
01217     free_server();
01218     free_all_object_data();
01219     /* See what the string data that is out there that hasn't been freed. */
01220     /*    LOG(llevDebug, "%s", ss_dump_table(0xff));*/
01221 #endif
01222     exit(0);
01223 }
01224 
01234 void leave(player *pl, int draw_exit) {
01235 
01236     if (pl != NULL) {
01237         pl->socket.status = Ns_Dead;
01238         LOG(llevInfo, "LOGOUT: Player named %s from ip %s\n", pl->ob->name, pl->socket.host);
01239 
01240         check_score(pl->ob, 1);
01241 
01242         /* If this player is the captain of the transport, need to do
01243          * some extra work.  By the time we get here, remove_ob()
01244          * should have already been called.
01245          */
01246         if (pl->transport && pl->transport->contr == pl) {
01247             /* If inv is a non player, inv->contr will be NULL, but that
01248              * is OK.
01249              */
01250             if (pl->transport->inv)
01251                 pl->transport->contr = pl->transport->inv->contr;
01252             else
01253                 pl->transport->contr = NULL;
01254 
01255             if (pl->transport->contr) {
01256                 char name[MAX_BUF];
01257 
01258                 query_name(pl->transport, name, MAX_BUF);
01259                 draw_ext_info_format(NDI_UNIQUE, 0, pl->transport->contr->ob,
01260                                      MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
01261                                      "%s has left.  You are now the captain of %s",
01262                                      "%s has left.  You are now the captain of %s",
01263                                      pl->ob->name, name);
01264             }
01265         }
01266 
01267         if (pl->ob->map) {
01268             if (pl->ob->map->in_memory == MAP_IN_MEMORY)
01269                 pl->ob->map->timeout = MAP_TIMEOUT(pl->ob->map);
01270             /* we need to update player count, since remove_ob() isn't called */
01271             if (!pl->hidden)
01272                 pl->ob->map->players--;
01273             pl->ob->map = NULL;
01274         }
01275         pl->ob->type = DEAD_OBJECT; /* To avoid problems with inventory window */
01276     }
01277     /* If a hidden dm dropped connection do not create
01278     * inconsistencies by showing that they have left the game
01279     */
01280     if (!(QUERY_FLAG(pl->ob, FLAG_WIZ) && pl->ob->contr->hidden)
01281     && (pl != NULL && draw_exit) && (pl->state != ST_GET_NAME && pl->state != ST_GET_PASSWORD && pl->state != ST_CONFIRM_PASSWORD))
01282         draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL,
01283                              MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
01284                              "%s left the game.",
01285                              "%s left the game.",
01286                              pl->ob->name);
01287 }
01288 
01296 int forbid_play(void) {
01297 #if !defined(_IBMR2) && !defined(___IBMR2) && defined(PERM_FILE)
01298     char buf[MAX_BUF], day[MAX_BUF];
01299     FILE *fp;
01300     time_t clock;
01301     struct tm *tm;
01302     int i, start, stop, forbit = 0, comp;
01303 
01304     clock = time(NULL);
01305     tm = (struct tm *)localtime(&clock);
01306 
01307     snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, PERM_FILE);
01308     if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL)
01309         return 0;
01310 
01311     while (fgets(buf, sizeof(buf), fp)) {
01312         if (buf[0] == '#')
01313             continue;
01314         if (!strncmp(buf, "msg", 3)) {
01315             if (forbit)
01316                 while (fgets(buf, sizeof(buf), fp))   /* print message */
01317                     fputs(buf, logfile);
01318             break;
01319         } else if (sscanf(buf, "%s %d%*c%d\n", day, &start, &stop) != 3) {
01320             LOG(llevDebug, "Warning: Incomplete line in permission file ignored.\n");
01321             continue;
01322         }
01323 
01324         for (i = 0; i < 7; i++) {
01325             if (!strncmp(buf, days[i], 3)
01326             && (tm->tm_wday == i)
01327             && (tm->tm_hour >= start)
01328             && (tm->tm_hour < stop))
01329                 forbit = 1;
01330         }
01331     }
01332 
01333     close_and_delete(fp, comp);
01334 
01335     return forbit;
01336 #else
01337     return 0;
01338 #endif
01339 }
01340 
01341 extern unsigned long todtick;
01342 
01357 static void do_specials(void) {
01358 
01359 #ifdef WATCHDOG
01360     if (!(pticks%503))
01361         watchdog();
01362 #endif
01363 
01364     if (!(pticks%PTICKS_PER_CLOCK))
01365         tick_the_clock();
01366 
01367     if (!(pticks%509))
01368         flush_old_maps();    /* Clears the tmp-files of maps which have reset */
01369 
01370     if (!(pticks%2503))
01371         fix_weight();        /* Hack to fix weightproblems caused by bugs */
01372 
01373     if (!(pticks%2521))
01374         metaserver_update();    /* 2500 ticks is about 5 minutes */
01375 
01376     if (!(pticks%5003))
01377         write_book_archive();
01378 
01379     if (!(pticks%5009))
01380         clean_friendly_list();
01381 
01382     if (!(pticks%5011))
01383         obsolete_parties();
01384 
01385     if (!(pticks%12503))
01386         fix_luck();
01387 }
01388 
01399 int server_main(int argc, char **argv) {
01400 #ifdef WIN32 /* ---win32 this sets the win32 from 0d0a to 0a handling */
01401     _fmode = _O_BINARY;
01402     bRunning = 1;
01403 #endif
01404 
01405 #ifndef WIN32
01406     /* Here we check that we aren't root or suid */
01407     if (getuid() == 0 || geteuid() == 0) {
01408         fputs("Don't run crossfire as root, it is unsupported.\n", stderr);
01409         fputs("Instead run it as a normal unprivileged user.\n", stderr);
01410         fputs("Aborting...\n", stderr);
01411         return 1;
01412     }
01413 #endif
01414 
01415 #ifdef DEBUG_MALLOC_LEVEL
01416     malloc_debug(DEBUG_MALLOC_LEVEL);
01417 #endif
01418 
01419     settings.argc = argc;
01420     settings.argv = argv;
01421     init(argc, argv);
01422     initPlugins();        /* GROS - Init the Plugins */
01423 #ifdef WIN32
01424     while (bRunning) {
01425 #else
01426     for (;;) {
01427 #endif
01428         nroferrors = 0;
01429 
01430         do_server();
01431         process_events();         /* "do" something with objects with speed */
01432         cftimer_process_timers(); /* Process the crossfire Timers */
01433         /* Lauwenmark : Here we handle the CLOCK global event */
01434         execute_global_event(EVENT_CLOCK);
01435         check_active_maps(); /* Removes unused maps after a certain timeout */
01436         do_specials();       /* Routines called from time to time. */
01437 
01438         sleep_delta();       /* Sleep proper amount of time before next tick */
01439     }
01440     emergency_save(0);
01441     cleanup();
01442     return 0;
01443 }