Crossfire Server, Branch 1.12  R12190
swap.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_swap_c =
00003  *    "$Id: swap.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2002 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 author can be reached via e-mail to crossfire-devel@real-time.com
00027 */
00028 
00034 #include <global.h>
00035 #ifndef __CEXTRACT__
00036 #include <sproto.h>
00037 #endif
00038 #include <object.h>
00039 
00046 static void write_map_log(void) {
00047     FILE *fp;
00048     mapstruct *map;
00049     char buf[MAX_BUF];
00050     long current_time = time(NULL);
00051 
00052     snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
00053     if (!(fp = fopen(buf, "w"))) {
00054         LOG(llevError, "Could not open %s for writing\n", buf);
00055         return;
00056     }
00057     for (map = first_map; map != NULL; map = map->next) {
00058         /* If tmpname is null, it is probably a unique player map,
00059          * so don't save information on it.
00060          */
00061         if (map->in_memory != MAP_IN_MEMORY
00062         && (map->tmpname != NULL)
00063         && (strncmp(map->path, "/random", 7))) {
00064             /* the 0 written out is a leftover from the lock number for
00065              * unique items and second one is from encounter maps.
00066              * Keep using it so that old temp files continue
00067              * to work.
00068              */
00069             fprintf(fp, "%s:%s:%ld:0:0:%d:0:%d\n", map->path, map->tmpname,
00070                     (map->reset_time == -1 ? -1 : map->reset_time-current_time),
00071                     map->difficulty,
00072                     map->darkness);
00073         }
00074     }
00075     fclose(fp);
00076 }
00077 
00083 void read_map_log(void) {
00084     FILE *fp;
00085     mapstruct *map;
00086     char buf[MAX_BUF], *cp, *cp1;
00087     int do_los, darkness, lock;
00088     long sec = seconds();
00089 
00090     snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
00091     if (!(fp = fopen(buf, "r"))) {
00092         LOG(llevDebug, "Could not open %s for reading\n", buf);
00093         return;
00094     }
00095     while (fgets(buf, MAX_BUF, fp) != NULL) {
00096         map = get_linked_map();
00097         /* scanf doesn't work all that great on strings, so we break
00098          * out that manually.  strdup is used for tmpname, since other
00099          * routines will try to free that pointer.
00100          */
00101         cp = strchr(buf, ':');
00102         *cp++ = '\0';
00103         strcpy(map->path, buf);
00104         cp1 = strchr(cp, ':');
00105         *cp1++ = '\0';
00106         map->tmpname = strdup_local(cp);
00107 
00108         /* Lock is left over from the lock items - we just toss it now.
00109          * We use it twice - second one is from encounter, but as we
00110          * don't care about the value, this works fine
00111          */
00112         sscanf(cp1, "%u:%d:%d:%hu:%d:%d\n", &map->reset_time, &lock, &lock, &map->difficulty, &do_los, &darkness);
00113 
00114         map->in_memory = MAP_SWAPPED;
00115         map->darkness = darkness;
00116         map->timeout = 0;
00117 
00118         /* When the reset time is saved out, it is adjusted so that
00119          * the current time is subtracted (thus, it is saved as number
00120          * of seconds from current time that it should reset).  We need
00121          * to add in the current seconds for this to work right.
00122          * On metalforge, strange behavior was observed with really high
00123          * reset times - I don't know how they got to that state,
00124          * but easy enough to do some sanity checking here.
00125          */
00126         map->reset_time += sec;
00127         if (map->reset_time > (sec+MAP_MAXRESET))
00128             map->reset_time = 0;
00129 
00130     }
00131     fclose(fp);
00132 }
00133 
00146 int swap_map(mapstruct *map) {
00147     player *pl;
00148     int res;
00149 
00150     if (map->in_memory != MAP_IN_MEMORY) {
00151         LOG(llevError, "Tried to swap out map which was not in memory.\n");
00152         return SAVE_ERROR_NOT_IN_MEMORY;
00153     }
00154     for (pl = first_player; pl != NULL; pl = pl->next)
00155         if (pl->ob == NULL || (!(QUERY_FLAG(pl->ob, FLAG_REMOVED)) && pl->ob->map == map))
00156             break;
00157 
00158     if (pl != NULL) {
00159         LOG(llevDebug, "Wanted to swap out map with player.\n");
00160         return SAVE_ERROR_PLAYER;
00161     }
00162     remove_all_pets(); /* Give them a chance to follow */
00163 
00164     /* Update the reset time.  Only do this is STAND_STILL is not set */
00165     if (!map->fixed_resettime)
00166         set_map_reset_time(map);
00167 
00168     /* If it is immediate reset time, don't bother saving it - just get
00169      * rid of it right away.
00170      */
00171     if (map->reset_time <= seconds()) {
00172         mapstruct *oldmap = map;
00173 
00174         LOG(llevDebug, "Resetting map %s.\n", map->path);
00175         /* Lauwenmark : Here we handle the MAPRESET global event */
00176         execute_global_event(EVENT_MAPRESET, map);
00177         map = map->next;
00178         delete_map(oldmap);
00179         return SAVE_ERROR_OK;
00180     }
00181 
00182     if ((res = save_map(map, SAVE_MODE_NORMAL)) < 0) {
00183         LOG(llevError, "Failed to swap map %s.\n", map->path);
00184         /* This is sufficiently critical to mandate to warn all DMs. */
00185         draw_ext_info_format(NDI_ALL_DMS|NDI_UNIQUE|NDI_RED, -1, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOADSAVE,
00186                              "Failed to swap map %s!", NULL, map->path);
00187         /* Map is *not *swapped. */
00188         map->in_memory = MAP_IN_MEMORY;
00189         return res;
00190     }
00191 
00192     free_map(map);
00193 
00194     if (settings.recycle_tmp_maps == TRUE)
00195         write_map_log();
00196 
00197     return SAVE_ERROR_OK;
00198 }
00199 
00206 void check_active_maps(void) {
00207     mapstruct *map, *next;
00208 
00209     for (map = first_map; map != NULL; map = next) {
00210         next = map->next;
00211         if (map->in_memory != MAP_IN_MEMORY)
00212             continue;
00213         if (!map->timeout)
00214             continue;
00215         if (--(map->timeout) > 0)
00216             continue;
00217         /* If LWM is set, we only swap maps out when we run out of objects */
00218 #ifndef MAX_OBJECTS_LWM
00219         swap_map(map);
00220 #endif
00221     }
00222 }
00223 
00232 static mapstruct *map_least_timeout(const char *except_level) {
00233     mapstruct *map, *chosen = NULL;
00234     int timeout = MAP_MAXTIMEOUT+1;
00235 
00236     for (map = first_map; map != NULL; map = map->next)
00237         if (map->in_memory == MAP_IN_MEMORY
00238         && strcmp(map->path, except_level)
00239         && map->timeout
00240         && map->timeout < timeout)
00241             chosen = map, timeout = map->timeout;
00242     return chosen;
00243 }
00244 
00253 void swap_below_max(const char *except_level) {
00254     mapstruct *map;
00255 
00256     if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS)
00257         return;
00258     for (;;) {
00259 #ifdef MAX_OBJECTS_LWM
00260         if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS_LWM)
00261             return;
00262 #else
00263         if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS)
00264             return;
00265 #endif
00266         if ((map = map_least_timeout(except_level)) == NULL)
00267             return;
00268         LOG(llevDebug, "Trying to swap out %s before its time.\n", map->path);
00269         map->timeout = 0;
00270         swap_map(map);
00271     }
00272 }
00273 
00287 int players_on_map(mapstruct *m, int show_all) {
00288     player *pl;
00289     int nr = 0;
00290 
00291     for (pl = first_player; pl != NULL; pl = pl->next)
00292         if (pl->ob != NULL
00293         && !QUERY_FLAG(pl->ob, FLAG_REMOVED)
00294         && pl->ob->map == m
00295         && (show_all || !pl->hidden))
00296             nr++;
00297     return nr;
00298 }
00299 
00305 void flush_old_maps(void) {
00306     mapstruct *m, *oldmap;
00307     long sec;
00308     sec = seconds();
00309 
00310     m = first_map;
00311     while (m) {
00312         /* There can be cases (ie death) where a player leaves a map and the timeout
00313          * is not set so it isn't swapped out.
00314          */
00315         if ((m->in_memory == MAP_IN_MEMORY)
00316         && (m->timeout == 0)
00317         && !players_on_map(m, TRUE)) {
00318             set_map_timeout(m);
00319         }
00320 
00321         /* per player unique maps are never really reset.  However, we do want
00322          * to perdiocially remove the entries in the list of active maps - this
00323          * generates a cleaner listing if a player issues the map commands, and
00324          * keeping all those swapped out per player unique maps also has some
00325          * memory and cpu consumption.
00326          * We do the cleanup here because there are lots of places that call
00327          * swap map, and doing it within swap map may cause problems as
00328          * the functions calling it may not expect the map list to change
00329          * underneath them.
00330          */
00331         if ((m->unique || m->is_template) && m->in_memory == MAP_SWAPPED) {
00332             LOG(llevDebug, "Resetting map %s.\n", m->path);
00333             oldmap = m;
00334             m = m->next;
00335             delete_map(oldmap);
00336         } else if (m->in_memory != MAP_SWAPPED
00337         || m->tmpname == NULL
00338         || sec < m->reset_time) {
00339             m = m->next;
00340         } else {
00341             LOG(llevDebug, "Resetting map %s.\n", m->path);
00342             /* Lauwenmark : Here we handle the MAPRESET global event */
00343             execute_global_event(EVENT_MAPRESET, m);
00344             clean_tmp_map(m);
00345             oldmap = m;
00346             m = m->next;
00347             delete_map(oldmap);
00348         }
00349     }
00350 }