Crossfire Server, Branch 1.12
R12190
|
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 }