Crossfire Server, Branches 1.12  R18729
swap.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_swap_c =
3  * "$Id: swap.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2002 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The author can be reached via e-mail to crossfire-devel@real-time.com
27 */
28 
34 #include <global.h>
35 #ifndef __CEXTRACT__
36 #include <sproto.h>
37 #endif
38 #include <object.h>
39 
46 static void write_map_log(void) {
47  FILE *fp;
48  mapstruct *map;
49  char buf[MAX_BUF];
50  long current_time = time(NULL);
51 
52  snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
53  if (!(fp = fopen(buf, "w"))) {
54  LOG(llevError, "Could not open %s for writing\n", buf);
55  return;
56  }
57  for (map = first_map; map != NULL; map = map->next) {
58  /* If tmpname is null, it is probably a unique player map,
59  * so don't save information on it.
60  */
61  if (map->in_memory != MAP_IN_MEMORY
62  && (map->tmpname != NULL)
63  && (strncmp(map->path, "/random", 7))) {
64  /* the 0 written out is a leftover from the lock number for
65  * unique items and second one is from encounter maps.
66  * Keep using it so that old temp files continue
67  * to work.
68  */
69  fprintf(fp, "%s:%s:%ld:0:0:%d:0:%d\n", map->path, map->tmpname,
70  (map->reset_time == -1 ? -1 : map->reset_time-current_time),
71  map->difficulty,
72  map->darkness);
73  }
74  }
75  fclose(fp);
76 }
77 
83 void read_map_log(void) {
84  FILE *fp;
85  mapstruct *map;
86  char buf[MAX_BUF], *cp, *cp1;
87  int do_los, darkness, lock;
88  long sec = seconds();
89 
90  snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
91  if (!(fp = fopen(buf, "r"))) {
92  LOG(llevDebug, "Could not open %s for reading\n", buf);
93  return;
94  }
95  while (fgets(buf, MAX_BUF, fp) != NULL) {
96  map = get_linked_map();
97  /* scanf doesn't work all that great on strings, so we break
98  * out that manually. strdup is used for tmpname, since other
99  * routines will try to free that pointer.
100  */
101  cp = strchr(buf, ':');
102  *cp++ = '\0';
103  strcpy(map->path, buf);
104  cp1 = strchr(cp, ':');
105  *cp1++ = '\0';
106  map->tmpname = strdup_local(cp);
107 
108  /* Lock is left over from the lock items - we just toss it now.
109  * We use it twice - second one is from encounter, but as we
110  * don't care about the value, this works fine
111  */
112  sscanf(cp1, "%u:%d:%d:%hu:%d:%d\n", &map->reset_time, &lock, &lock, &map->difficulty, &do_los, &darkness);
113 
114  map->in_memory = MAP_SWAPPED;
115  map->darkness = darkness;
116  map->timeout = 0;
117 
118  /* When the reset time is saved out, it is adjusted so that
119  * the current time is subtracted (thus, it is saved as number
120  * of seconds from current time that it should reset). We need
121  * to add in the current seconds for this to work right.
122  * On metalforge, strange behavior was observed with really high
123  * reset times - I don't know how they got to that state,
124  * but easy enough to do some sanity checking here.
125  */
126  map->reset_time += sec;
127  if (map->reset_time > (sec+MAP_MAXRESET))
128  map->reset_time = 0;
129 
130  }
131  fclose(fp);
132 }
133 
146 int swap_map(mapstruct *map) {
147  player *pl;
148  int res;
149 
150  if (map->in_memory != MAP_IN_MEMORY) {
151  LOG(llevError, "Tried to swap out map which was not in memory.\n");
153  }
154  for (pl = first_player; pl != NULL; pl = pl->next)
155  if (pl->ob == NULL || (!(QUERY_FLAG(pl->ob, FLAG_REMOVED)) && pl->ob->map == map))
156  break;
157 
158  if (pl != NULL) {
159  LOG(llevDebug, "Wanted to swap out map with player.\n");
160  return SAVE_ERROR_PLAYER;
161  }
162  remove_all_pets(); /* Give them a chance to follow */
163 
164  /* Update the reset time. Only do this is STAND_STILL is not set */
165  if (!map->fixed_resettime)
166  set_map_reset_time(map);
167 
168  /* If it is immediate reset time, don't bother saving it - just get
169  * rid of it right away.
170  */
171  if (map->reset_time <= seconds()) {
172  mapstruct *oldmap = map;
173 
174  LOG(llevDebug, "Resetting map %s.\n", map->path);
175  /* Lauwenmark : Here we handle the MAPRESET global event */
177  map = map->next;
178  delete_map(oldmap);
179  return SAVE_ERROR_OK;
180  }
181 
182  if ((res = save_map(map, SAVE_MODE_NORMAL)) < 0) {
183  LOG(llevError, "Failed to swap map %s.\n", map->path);
184  /* This is sufficiently critical to mandate to warn all DMs. */
186  "Failed to swap map %s!", NULL, map->path);
187  /* Map is *not *swapped. */
188  map->in_memory = MAP_IN_MEMORY;
189  return res;
190  }
191 
192  free_map(map);
193 
195  write_map_log();
196 
197  return SAVE_ERROR_OK;
198 }
199 
206 void check_active_maps(void) {
207  mapstruct *map, *next;
208 
209  for (map = first_map; map != NULL; map = next) {
210  next = map->next;
211  if (map->in_memory != MAP_IN_MEMORY)
212  continue;
213  if (!map->timeout)
214  continue;
215  if (--(map->timeout) > 0)
216  continue;
217  /* If LWM is set, we only swap maps out when we run out of objects */
218 #ifndef MAX_OBJECTS_LWM
219  swap_map(map);
220 #endif
221  }
222 }
223 
232 static mapstruct *map_least_timeout(const char *except_level) {
233  mapstruct *map, *chosen = NULL;
234  int timeout = MAP_MAXTIMEOUT+1;
235 
236  for (map = first_map; map != NULL; map = map->next)
237  if (map->in_memory == MAP_IN_MEMORY
238  && strcmp(map->path, except_level)
239  && map->timeout
240  && map->timeout < timeout)
241  chosen = map, timeout = map->timeout;
242  return chosen;
243 }
244 
253 void swap_below_max(const char *except_level) {
254  mapstruct *map;
255 
257  return;
258  for (;;) {
259 #ifdef MAX_OBJECTS_LWM
260  if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS_LWM)
261  return;
262 #else
264  return;
265 #endif
266  if ((map = map_least_timeout(except_level)) == NULL)
267  return;
268  LOG(llevDebug, "Trying to swap out %s before its time.\n", map->path);
269  map->timeout = 0;
270  swap_map(map);
271  }
272 }
273 
287 int players_on_map(mapstruct *m, int show_all) {
288  player *pl;
289  int nr = 0;
290 
291  for (pl = first_player; pl != NULL; pl = pl->next)
292  if (pl->ob != NULL
293  && !QUERY_FLAG(pl->ob, FLAG_REMOVED)
294  && pl->ob->map == m
295  && (show_all || !pl->hidden))
296  nr++;
297  return nr;
298 }
299 
305 void flush_old_maps(void) {
306  mapstruct *m, *oldmap;
307  long sec;
308  sec = seconds();
309 
310  m = first_map;
311  while (m) {
312  /* There can be cases (ie death) where a player leaves a map and the timeout
313  * is not set so it isn't swapped out.
314  */
315  if ((m->in_memory == MAP_IN_MEMORY)
316  && (m->timeout == 0)
317  && !players_on_map(m, TRUE)) {
318  set_map_timeout(m);
319  }
320 
321  /* per player unique maps are never really reset. However, we do want
322  * to perdiocially remove the entries in the list of active maps - this
323  * generates a cleaner listing if a player issues the map commands, and
324  * keeping all those swapped out per player unique maps also has some
325  * memory and cpu consumption.
326  * We do the cleanup here because there are lots of places that call
327  * swap map, and doing it within swap map may cause problems as
328  * the functions calling it may not expect the map list to change
329  * underneath them.
330  */
331  if ((m->unique || m->is_template) && m->in_memory == MAP_SWAPPED) {
332  LOG(llevDebug, "Resetting map %s.\n", m->path);
333  oldmap = m;
334  m = m->next;
335  delete_map(oldmap);
336  } else if (m->in_memory != MAP_SWAPPED
337  || m->tmpname == NULL
338  || sec < m->reset_time) {
339  m = m->next;
340  } else {
341  LOG(llevDebug, "Resetting map %s.\n", m->path);
342  /* Lauwenmark : Here we handle the MAPRESET global event */
344  clean_tmp_map(m);
345  oldmap = m;
346  m = m->next;
347  delete_map(oldmap);
348  }
349  }
350 }
char path[HUGE_BUF]
Definition: map.h:384
void set_map_timeout(mapstruct *oldmap)
Definition: standalone.c:77
Definition: player.h:146
mapstruct * get_linked_map(void)
Definition: map.c:816
#define SAVE_ERROR_OK
Definition: map.h:164
void swap_below_max(const char *except_level)
Definition: swap.c:253
uint8 recycle_tmp_maps
Definition: global.h:360
#define MAP_MAXRESET
Definition: config.h:430
static void write_map_log(void)
Definition: swap.c:46
uint32 in_memory
Definition: map.h:366
void check_active_maps(void)
Definition: swap.c:206
#define SAVE_MODE_NORMAL
Definition: map.h:141
void free_map(mapstruct *m)
Definition: map.c:1707
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
int players_on_map(mapstruct *m, int show_all)
Definition: swap.c:287
static mapstruct * map_least_timeout(const char *except_level)
Definition: swap.c:232
#define FLAG_REMOVED
Definition: define.h:528
sint32 timeout
Definition: map.h:362
#define MAP_IN_MEMORY
Definition: map.h:151
#define NDI_RED
Definition: newclient.h:198
#define MAP_SWAPPED
Definition: map.h:152
uint32 hidden
Definition: player.h:186
#define NDI_ALL_DMS
Definition: newclient.h:221
struct mapdef * map
Definition: object.h:155
long seconds(void)
Definition: time.c:417
void read_map_log(void)
Definition: swap.c:83
int execute_global_event(int eventcode,...)
Definition: standalone.c:229
uint16 difficulty
Definition: map.h:364
#define TRUE
Definition: exp.c:41
#define MAP_MAXTIMEOUT
Definition: config.h:412
int nrofallocobjects
Definition: object.c:67
void remove_all_pets(void)
Definition: pets.c:273
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
char * strdup_local(const char *str)
Definition: porting.c:310
#define MAX_BUF
Definition: define.h:81
#define MSG_TYPE_ADMIN
Definition: newclient.h:324
#define EVENT_MAPRESET
Definition: plugin.h:88
#define MAX_OBJECTS
Definition: config.h:447
char * tmpname
Definition: map.h:348
int save_map(mapstruct *m, int flag)
Definition: map.c:1453
uint8 darkness
Definition: map.h:369
object * ob
Definition: player.h:207
uint32 unique
Definition: map.h:358
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
void set_map_reset_time(mapstruct *map)
Definition: map.c:2239
uint32 is_template
Definition: map.h:359
void flush_old_maps(void)
Definition: swap.c:305
const char * localdir
Definition: global.h:335
uint32 reset_time
Definition: map.h:353
int nroffreeobjects
Definition: object.c:66
uint32 fixed_resettime
Definition: map.h:356
struct Settings settings
Definition: init.c:48
int swap_map(mapstruct *map)
Definition: swap.c:146
void delete_map(mapstruct *m)
Definition: map.c:1745
EXTERN player * first_player
Definition: global.h:190
struct pl * next
Definition: player.h:147
#define NDI_UNIQUE
Definition: newclient.h:219
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
struct mapdef * next
Definition: map.h:347
#define MSG_TYPE_ADMIN_LOADSAVE
Definition: newclient.h:417
Definition: map.h:346
#define SAVE_ERROR_PLAYER
Definition: map.h:173
void clean_tmp_map(mapstruct *m)
Definition: map.c:1959
EXTERN mapstruct * first_map
Definition: global.h:191
#define SAVE_ERROR_NOT_IN_MEMORY
Definition: map.h:172