Crossfire Server, Trunk  R20513
swap.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "global.h"
20 
21 #include <string.h>
22 
23 #include "object.h"
24 #include "output_file.h"
25 #include "sproto.h"
26 
33 static void write_map_log(void) {
34  FILE *fp;
35  OutputFile of;
36  mapstruct *map;
37  char buf[MAX_BUF];
38  long current_time = time(NULL);
39 
40  snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
41  fp = of_open(&of, buf);
42  if (fp == NULL)
43  return;
44  for (map = first_map; map != NULL; map = map->next) {
45  /* If tmpname is null, it is probably a unique player map,
46  * so don't save information on it.
47  */
48  if (map->in_memory != MAP_IN_MEMORY
49  && (map->tmpname != NULL)
50  && (strncmp(map->path, "/random", 7))) {
51  /* the 0 written out is a leftover from the lock number for
52  * unique items and second one is from encounter maps.
53  * Keep using it so that old temp files continue
54  * to work.
55  */
56  fprintf(fp, "%s:%s:%ld:0:0:%d:0:%d\n", map->path, map->tmpname,
57  (map->reset_time == (uint32_t)-1 ? -1 : map->reset_time-current_time),
58  map->difficulty,
59  map->darkness);
60  }
61  }
62  of_close(&of);
63 }
64 
70 void read_map_log(void) {
71  FILE *fp;
72  mapstruct *map;
73  char buf[MAX_BUF];
74  int do_los, darkness, lock;
75  long sec = seconds();
76 
77  snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
78  if (!(fp = fopen(buf, "r"))) {
79  LOG(llevDebug, "Could not open %s for reading\n", buf);
80  return;
81  }
82  while (fgets(buf, MAX_BUF, fp) != NULL) {
83  char *tmp[3];
84 
85  map = get_linked_map();
86  /* scanf doesn't work all that great on strings, so we break
87  * out that manually. strdup is used for tmpname, since other
88  * routines will try to free that pointer.
89  */
90  if (split_string(buf, tmp, sizeof(tmp)/sizeof(*tmp), ':') != 3) {
91  LOG(llevDebug, "%s/temp.maps: ignoring invalid line: %s\n", settings.localdir, buf);
92  continue;
93  }
94 
95  safe_strncpy(map->path, tmp[0], sizeof(map->path));
96  map->tmpname = strdup_local(tmp[1]);
97 
98  /* Lock is left over from the lock items - we just toss it now.
99  * We use it twice - second one is from encounter, but as we
100  * don't care about the value, this works fine
101  */
102  sscanf(tmp[2], "%u:%d:%d:%hu:%d:%d\n", &map->reset_time, &lock, &lock, &map->difficulty, &do_los, &darkness);
103 
104  map->in_memory = MAP_SWAPPED;
105  map->darkness = darkness;
106  map->timeout = 0;
107 
108  /* When the reset time is saved out, it is adjusted so that
109  * the current time is subtracted (thus, it is saved as number
110  * of seconds from current time that it should reset). We need
111  * to add in the current seconds for this to work right.
112  * On metalforge, strange behavior was observed with really high
113  * reset times - I don't know how they got to that state,
114  * but easy enough to do some sanity checking here.
115  */
116  map->reset_time += sec;
117  if (map->reset_time > (sec+MAP_MAXRESET))
118  map->reset_time = 0;
119  }
120  fclose(fp);
121 }
122 
135 int swap_map(mapstruct *map) {
136  player *pl;
137  int res;
138 
139  if (map->in_memory != MAP_IN_MEMORY) {
140  LOG(llevError, "Tried to swap out map which was not in memory.\n");
142  }
143  for (pl = first_player; pl != NULL; pl = pl->next)
144  if (pl->ob == NULL || (!(QUERY_FLAG(pl->ob, FLAG_REMOVED)) && pl->ob->map == map))
145  break;
146 
147  if (pl != NULL) {
148  LOG(llevDebug, "Wanted to swap out map with player.\n");
149  return SAVE_ERROR_PLAYER;
150  }
151  pets_remove_all(); /* Give them a chance to follow */
152 
153  /* Update the reset time. Only do this is STAND_STILL is not set */
154  if (!map->fixed_resettime)
155  set_map_reset_time(map);
156 
157  /* If it is immediate reset time, don't bother saving it - just get
158  * rid of it right away.
159  */
160  if (map->reset_time <= seconds()) {
161  mapstruct *oldmap = map;
162 
163  LOG(llevDebug, "Resetting map %s.\n", map->path);
164  /* Lauwenmark : Here we handle the MAPRESET global event */
166  map = map->next;
167  delete_map(oldmap);
168  return SAVE_ERROR_OK;
169  }
170 
171  if ((res = save_map(map, SAVE_MODE_NORMAL)) < 0) {
172  LOG(llevError, "Failed to swap map %s.\n", map->path);
173  /* This is sufficiently critical to mandate to warn all DMs. */
175  "Failed to swap map %s!", map->path);
176  /* Map is *not *swapped. */
177  map->in_memory = MAP_IN_MEMORY;
178  return res;
179  }
180 
181  free_map(map);
182 
184  write_map_log();
185 
186  return SAVE_ERROR_OK;
187 }
188 
195 void check_active_maps(void) {
196  mapstruct *map, *next;
197 
198  for (map = first_map; map != NULL; map = next) {
199  next = map->next;
200  if (map->in_memory != MAP_IN_MEMORY)
201  continue;
202  if (!map->timeout)
203  continue;
204  if (--(map->timeout) > 0)
205  continue;
206  /* If LWM is set, we only swap maps out when we run out of objects */
207 #ifndef MAX_OBJECTS_LWM
208  swap_map(map);
209 #endif
210  }
211 }
212 
221 static mapstruct *map_least_timeout(const char *except_level) {
222  mapstruct *map, *chosen = NULL;
223  int timeout = MAP_MAXTIMEOUT+1;
224 
225  for (map = first_map; map != NULL; map = map->next)
226  if (map->in_memory == MAP_IN_MEMORY
227  && strcmp(map->path, except_level)
228  && map->timeout
229  && map->timeout < timeout)
230  chosen = map, timeout = map->timeout;
231  return chosen;
232 }
233 
242 void swap_below_max(const char *except_level) {
243  mapstruct *map;
244 
246  return;
247  for (;;) {
248 #ifdef MAX_OBJECTS_LWM
249  if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS_LWM)
250  return;
251 #else
253  return;
254 #endif
255  if ((map = map_least_timeout(except_level)) == NULL)
256  return;
257  LOG(llevDebug, "Trying to swap out %s before its time.\n", map->path);
258  map->timeout = 0;
259  swap_map(map);
260  }
261 }
262 
276 int players_on_map(mapstruct *m, int show_all) {
277  player *pl;
278  int nr = 0;
279 
280  for (pl = first_player; pl != NULL; pl = pl->next)
281  if (pl->ob != NULL
282  && !QUERY_FLAG(pl->ob, FLAG_REMOVED)
283  && pl->ob->map == m
284  && (show_all || !pl->hidden))
285  nr++;
286  return nr;
287 }
288 
294 void flush_old_maps(void) {
295  mapstruct *m, *oldmap;
296  long sec;
297  sec = seconds();
298 
299  m = first_map;
300  while (m) {
301  /* There can be cases (ie death) where a player leaves a map and the timeout
302  * is not set so it isn't swapped out.
303  */
304  if ((m->in_memory == MAP_IN_MEMORY)
305  && (m->timeout == 0)
306  && !players_on_map(m, TRUE)) {
307  set_map_timeout(m);
308  }
309 
310  /* per player unique maps are never really reset. However, we do want
311  * to perdiocially remove the entries in the list of active maps - this
312  * generates a cleaner listing if a player issues the map commands, and
313  * keeping all those swapped out per player unique maps also has some
314  * memory and cpu consumption.
315  * We do the cleanup here because there are lots of places that call
316  * swap map, and doing it within swap map may cause problems as
317  * the functions calling it may not expect the map list to change
318  * underneath them.
319  */
320  if ((m->unique || m->is_template) && m->in_memory == MAP_SWAPPED) {
321  LOG(llevDebug, "Resetting map %s.\n", m->path);
322  oldmap = m;
323  m = m->next;
324  delete_map(oldmap);
325  } else if (m->in_memory != MAP_SWAPPED
326  || m->tmpname == NULL
327  || sec < m->reset_time) {
328  m = m->next;
329  } else {
330  LOG(llevDebug, "Resetting map %s.\n", m->path);
331  /* Lauwenmark : Here we handle the MAPRESET global event */
333  clean_tmp_map(m);
334  oldmap = m;
335  m = m->next;
336  delete_map(oldmap);
337  }
338  }
339 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Sends message to player(s).
Definition: main.c:315
Error, serious thing.
Definition: logger.h:11
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:365
void set_map_timeout(mapstruct *oldmap)
Applies the map timeout.
Definition: main.c:307
One player.
Definition: player.h:92
mapstruct * get_linked_map(void)
Allocates, initialises, and returns a pointer to a mapstruct.
Definition: map.c:793
#define SAVE_ERROR_OK
No error.
Definition: map.h:143
void swap_below_max(const char *except_level)
Tries to swap out maps which are still in memory, because of MAP_TIMEOUT until used objects is below ...
Definition: swap.c:242
#define strdup_local
Definition: compat.h:25
uint32_t in_memory
Combination of IN_MEMORY_xxx flags.
Definition: map.h:345
#define MAP_MAXRESET
MAP_MAXRESET is the maximum time a map can have before being reset.
Definition: config.h:426
uint32_t is_template
If set, this is a template map.
Definition: map.h:338
uint32_t reset_time
When this map should reset.
Definition: map.h:332
static void write_map_log(void)
Writes out information on all the temporary maps.
Definition: swap.c:33
#define MSG_TYPE_ADMIN_LOADSAVE
load/save operations
Definition: newclient.h:477
void check_active_maps(void)
Finds maps in memory to reset.
Definition: swap.c:195
#define TRUE
Definition: compat.h:10
#define SAVE_MODE_NORMAL
No special handling.
Definition: map.h:120
Global type definitions and header inclusions.
void free_map(mapstruct *m)
Frees everything allocated by the given mapstructure.
Definition: map.c:1694
#define safe_strncpy
Definition: compat.h:23
uint32_t hidden
If True, player (DM) is hidden from view.
Definition: player.h:132
int players_on_map(mapstruct *m, int show_all)
Returns the count of players on a map, calculated from player list.
Definition: swap.c:276
static mapstruct * map_least_timeout(const char *except_level)
Returns the map with the lowest timeout variable (not 0).
Definition: swap.c:221
uint8_t recycle_tmp_maps
Re-use tmp maps.
Definition: global.h:270
#define FLAG_REMOVED
Object is not in any map or invenory.
Definition: define.h:232
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:130
#define NDI_RED
Definition: newclient.h:224
#define MAP_SWAPPED
Map spaces have been saved to disk.
Definition: map.h:131
uint32_t unique
If set, this is a per player unique map.
Definition: map.h:337
#define NDI_ALL_DMS
Inform all logged in DMs.
Definition: newclient.h:247
struct mapdef * map
Pointer to the map in which this object is present.
Definition: object.h:297
#define snprintf
Definition: win32.h:46
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.c:61
long seconds(void)
Return wall clock time in seconds.
Definition: time.c:344
void read_map_log(void)
Reads temporary maps information from disk.
Definition: swap.c:70
int execute_global_event(int eventcode,...)
Definition: main.c:369
#define MAP_MAXTIMEOUT
MAP_MAXTIMEOUT tells the maximum of ticks until a map is swapped out after a player has left it...
Definition: config.h:408
uint32_t fixed_resettime
If true, reset time is not affected by players entering/exiting map.
Definition: map.h:335
void pets_remove_all(void)
This function checks all pets so they try to follow their master around the world.
Definition: pets.c:257
int nrofallocobjects
How many OBs allocated (free + used)
Definition: object.c:57
uint8_t darkness
Indicates level of darkness of map.
Definition: map.h:346
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
#define MSG_TYPE_ADMIN
Definition: newclient.h:377
#define EVENT_MAPRESET
A map is resetting.
Definition: plugin.h:92
#define MAX_OBJECTS
MAX_OBJECTS is no hard limit.
Definition: config.h:443
uint16_t difficulty
What level the player should be to play here.
Definition: map.h:343
char * tmpname
Name of temporary file.
Definition: map.h:327
int save_map(mapstruct *m, int flag)
Saves a map to file.
Definition: map.c:1436
object * ob
The object representing the player.
Definition: player.h:158
int32_t timeout
Swapout is set to this.
Definition: map.h:341
unsigned int uint32_t
Definition: win32.h:162
void set_map_reset_time(mapstruct *map)
Updates the map&#39;s timeout.
Definition: map.c:2233
Object structure, the core of Crossfire.
void flush_old_maps(void)
Removes tmp-files of maps which are going to be reset next time they are visited. ...
Definition: swap.c:294
const char * localdir
Read/write data files.
Definition: global.h:245
Only for debugging purposes.
Definition: logger.h:13
int nroffreeobjects
How many OBs allocated and free (free)
Definition: object.c:56
struct Settings settings
Server settings.
Definition: init.c:40
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.c:30
int swap_map(mapstruct *map)
Swaps a map to disk.
Definition: swap.c:135
Functions for creating text output files.
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.c:1741
EXTERN player * first_player
First player.
Definition: global.h:117
struct pl * next
Pointer to next player, NULL if this is last.
Definition: player.h:93
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Splits a string delimited by passed in sep value into characters into an array of strings...
Definition: utils.c:499
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
struct mapdef * next
Next map, linked list.
Definition: map.h:326
This is a game-map.
Definition: map.h:325
#define SAVE_ERROR_PLAYER
Player on map to save.
Definition: map.h:152
void clean_tmp_map(mapstruct *m)
Removse the temporary file used by the map.
Definition: map.c:1954
EXTERN mapstruct * first_map
First map.
Definition: global.h:118
#define SAVE_ERROR_NOT_IN_MEMORY
Map to swap isn&#39;t in memory.
Definition: map.h:151