Crossfire Server, Trunk
swap.cpp
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 #include "stats.h"
27 
34 static void write_map_log(void) {
35  FILE *fp;
36  OutputFile of;
37  mapstruct *map;
38  char buf[MAX_BUF];
39  long current_time = time(NULL);
40 
41  snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
42  fp = of_open(&of, buf);
43  if (fp == NULL)
44  return;
45  for (map = first_map; map != NULL; map = map->next) {
46  /* If tmpname is null, it is probably a unique player map,
47  * so don't save information on it.
48  */
49  if (map->in_memory != MAP_IN_MEMORY
50  && (map->tmpname != NULL)
51  && (strncmp(map->path, "/random", 7))) {
52  /* the 0 written out is a leftover from the lock number for
53  * unique items and second one is from encounter maps.
54  * Keep using it so that old temp files continue
55  * to work.
56  */
57  fprintf(fp, "%s:%s:%ld:0:0:%d:0:%d\n", map->path, map->tmpname,
58  (map->reset_time == (uint32_t)-1 ? -1 : map->reset_time-current_time),
59  map->difficulty,
60  map->darkness);
61  }
62  }
63  of_close(&of);
64 }
65 
71 void read_map_log(void) {
72  FILE *fp;
73  mapstruct *map;
74  char buf[MAX_BUF];
75  int do_los, darkness, lock;
76  long sec = seconds();
77 
78  snprintf(buf, sizeof(buf), "%s/temp.maps", settings.localdir);
79  if (!(fp = fopen(buf, "r"))) {
80  LOG(llevDebug, "Could not open %s for reading\n", buf);
81  return;
82  }
83  while (fgets(buf, MAX_BUF, fp) != NULL) {
84  char *tmp[3];
85 
86  map = get_linked_map();
87  /* scanf doesn't work all that great on strings, so we break
88  * out that manually. strdup is used for tmpname, since other
89  * routines will try to free that pointer.
90  */
91  if (split_string(buf, tmp, sizeof(tmp)/sizeof(*tmp), ':') != 3) {
92  LOG(llevDebug, "%s/temp.maps: ignoring invalid line: %s\n", settings.localdir, buf);
93  continue;
94  }
95 
96  safe_strncpy(map->path, tmp[0], sizeof(map->path));
97  map->tmpname = strdup_local(tmp[1]);
98 
99  /* Lock is left over from the lock items - we just toss it now.
100  * We use it twice - second one is from encounter, but as we
101  * don't care about the value, this works fine
102  */
103  sscanf(tmp[2], "%u:%d:%d:%hu:%d:%d\n", &map->reset_time, &lock, &lock, &map->difficulty, &do_los, &darkness);
104 
105  map->in_memory = MAP_SWAPPED;
106  map->darkness = darkness;
107  map->timeout = 0;
108 
109  /* When the reset time is saved out, it is adjusted so that
110  * the current time is subtracted (thus, it is saved as number
111  * of seconds from current time that it should reset). We need
112  * to add in the current seconds for this to work right.
113  * On metalforge, strange behavior was observed with really high
114  * reset times - I don't know how they got to that state,
115  * but easy enough to do some sanity checking here.
116  */
117  map->reset_time += sec;
118  if (map->reset_time > (sec+MAP_MAXRESET))
119  map->reset_time = 0;
120  }
121  fclose(fp);
122 }
123 
137  player *pl;
138  int res;
139 
140  if (map->in_memory != MAP_IN_MEMORY) {
141  LOG(llevError, "Tried to swap out map which was not in memory.\n");
143  }
144  for (pl = first_player; pl != NULL; pl = pl->next)
145  if (pl->ob == NULL || (!(QUERY_FLAG(pl->ob, FLAG_REMOVED)) && pl->ob->map == map))
146  break;
147 
148  if (pl != NULL) {
149  LOG(llevDebug, "Wanted to swap out map with player.\n");
150  map->timeout = 0;
151  return SAVE_ERROR_PLAYER;
152  }
153  pets_attempt_follow(0, 0); /* Give them a chance to follow */
154 
155  /* Update the reset time. Only do this is STAND_STILL is not set */
156  if (!map->fixed_resettime)
158 
159  /* If it is immediate reset time and not in a reset group, don't bother
160  * saving it - just get rid of it right away.
161  * If it is part of a reset group, then swap it, and handle reset in
162  * flush_old_maps().
163  */
164  if (map->reset_time <= seconds() && map->reset_group == NULL) {
165  mapstruct *oldmap = map;
166 
167  LOG(llevDebug, "Resetting map %s instead of swapping it.\n", map->path);
169  map = map->next;
170  delete_map(oldmap);
171  return SAVE_ERROR_OK;
172  }
173 
174  LOG(llevDebug, "Swapping map %s.\n", map->path);
175  if ((res = save_map(map, SAVE_MODE_NORMAL)) < 0) {
176  LOG(llevError, "Failed to swap map %s.\n", map->path);
177  /* This is sufficiently critical to mandate to warn all DMs. */
179  "Failed to swap map %s!", map->path);
180  /* Map is *not *swapped. */
181  map->in_memory = MAP_IN_MEMORY;
182 
183  // Add random delay to avoid repeated failing attempts at swapping.
184  map->timeout = 60 + rndm(0, 30);
185  return res;
186  }
187 
189  map->timeout = 0;
190  free_map(map);
191 
193  write_map_log();
194 
195  return SAVE_ERROR_OK;
196 }
197 
201 void check_active_maps(void) {
202  /* Swapping can take many tens of milliseconds. Swapping too many maps in
203  * one tick can cause enough latency for the server to skip time. */
204  int num_to_swap = 1;
205 
206  mapstruct *map, *next;
207  for (map = first_map; map != NULL; map = next) {
208  next = map->next;
209  if (map->in_memory == MAP_IN_MEMORY) {
210  // map->timeout == 0 means to never swap
211  if (map->timeout > 1) {
212  map->timeout -= 1;
213  } else if (num_to_swap > 0 && map->timeout == 1) {
214  swap_map(map);
215  num_to_swap -= 1;
216  }
217  }
218  }
219 }
220 
234 int players_on_map(mapstruct *m, int show_all) {
235  player *pl;
236  int nr = 0;
237 
238  for (pl = first_player; pl != NULL; pl = pl->next)
239  if (pl->ob != NULL
240  && !QUERY_FLAG(pl->ob, FLAG_REMOVED)
241  && pl->ob->map == m
242  && (show_all || !pl->hidden))
243  nr++;
244  return nr;
245 }
246 
253 static bool map_can_reset_no_group(const mapstruct *map, long current_time) {
254  return (map->in_memory == MAP_SWAPPED)
255  && (map->tmpname != NULL)
256  && (current_time >= map->reset_time);
257 }
258 
266 bool map_can_reset(const mapstruct *map, long current_time) {
267  if (!map_can_reset_no_group(map, current_time)) {
268  return 0;
269  }
270 
271  if (map->reset_group == NULL) {
272  return 1;
273  }
274 
275  mapstruct *other = first_map;
276  while (other) {
277  if (other != map && other->reset_group == map->reset_group && !map_can_reset_no_group(other, current_time)) {
278  /* Another map isn't ready to reset, so don't reset this map either */
279  return 0;
280  }
281  other = other->next;
282  }
283 
284  return 1;
285 }
286 
291 void flush_old_maps(void) {
292  mapstruct *m, *oldmap;
293  long sec;
294  sec = seconds();
295 
296  m = first_map;
297  while (m) {
298  /* There can be cases (ie death) where a player leaves a map and the timeout
299  * is not set so it isn't swapped out.
300  */
301  if ((m->in_memory == MAP_IN_MEMORY)
302  && (m->timeout == 0)
303  && !players_on_map(m, TRUE)) {
305  }
306 
307  /* per player unique maps are never really reset. However, we do want
308  * to perdiocially remove the entries in the list of active maps - this
309  * generates a cleaner listing if a player issues the map commands, and
310  * keeping all those swapped out per player unique maps also has some
311  * memory and cpu consumption.
312  * We do the cleanup here because there are lots of places that call
313  * swap map, and doing it within swap map may cause problems as
314  * the functions calling it may not expect the map list to change
315  * underneath them.
316  */
317  if ((m->unique || m->is_template) && m->in_memory == MAP_SWAPPED) {
318  LOG(llevDebug, "Resetting unique or template map %s.\n", m->path);
319  oldmap = m;
320  m = m->next;
321  delete_map(oldmap);
322  } else if (!map_can_reset(m, sec)) {
323  m = m->next;
324  } else {
325  LOG(llevDebug, "Resetting map %s.\n", m->path);
327  clean_tmp_map(m);
328  oldmap = m;
329  m = m->next;
330  delete_map(oldmap);
331  }
332  }
333 }
give.next
def next
Definition: give.py:44
output_file.h
global.h
first_player
player * first_player
Definition: init.cpp:106
settings
struct Settings settings
Definition: init.cpp:139
write_map_log
static void write_map_log(void)
Definition: swap.cpp:34
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
Settings::recycle_tmp_maps
uint8_t recycle_tmp_maps
Definition: global.h:272
llevError
@ llevError
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
of_close
int of_close(OutputFile *of)
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Definition: output_file.cpp:30
player
Definition: player.h:105
strdup_local
#define strdup_local
Definition: compat.h:29
SAVE_ERROR_NOT_IN_MEMORY
#define SAVE_ERROR_NOT_IN_MEMORY
Definition: map.h:147
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
set_map_timeout
void set_map_timeout(mapstruct *oldmap)
Definition: main.cpp:304
maps_swapped_total
int maps_swapped_total
Definition: logger.cpp:41
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
NDI_ALL_DMS
#define NDI_ALL_DMS
Definition: newclient.h:253
Ice.tmp
int tmp
Definition: Ice.py:207
NDI_RED
#define NDI_RED
Definition: newclient.h:234
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
buf
StringBuffer * buf
Definition: readable.cpp:1565
m
static event_registration m
Definition: citylife.cpp:425
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:126
map_can_reset
bool map_can_reset(const mapstruct *map, long current_time)
Definition: swap.cpp:266
swap_map
int swap_map(mapstruct *map)
Definition: swap.cpp:136
disinfect.map
map
Definition: disinfect.py:4
read_map_log
void read_map_log(void)
Definition: swap.cpp:71
stats.h
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:473
first_map
mapstruct * first_map
Definition: init.cpp:107
check_active_maps
void check_active_maps(void)
Definition: swap.cpp:201
clean_tmp_map
void clean_tmp_map(mapstruct *m)
Definition: map.cpp:1945
EVENT_MAPRESET
#define EVENT_MAPRESET
Definition: events.h:50
free_map
void free_map(mapstruct *m)
Definition: map.cpp:1653
mapstruct::reset_group
sstring reset_group
Definition: map.h:325
sproto.h
delete_map
void delete_map(mapstruct *m)
Definition: map.cpp:1699
get_linked_map
mapstruct * get_linked_map(void)
Definition: map.cpp:776
seconds
long seconds(void)
Definition: time.cpp:348
MAX_BUF
#define MAX_BUF
Definition: define.h:35
MSG_TYPE_ADMIN_LOADSAVE
#define MSG_TYPE_ADMIN_LOADSAVE
Definition: newclient.h:488
set_map_reset_time
void set_map_reset_time(mapstruct *map)
Definition: map.cpp:2224
SAVE_MODE_NORMAL
#define SAVE_MODE_NORMAL
Definition: map.h:116
pets_attempt_follow
void pets_attempt_follow(object *for_owner, int force)
Definition: pets.cpp:249
FLAG_REMOVED
#define FLAG_REMOVED
Definition: define.h:232
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
mapstruct
Definition: map.h:313
flush_old_maps
void flush_old_maps(void)
Definition: swap.cpp:291
MAP_SWAPPED
#define MAP_SWAPPED
Definition: map.h:127
map_can_reset_no_group
static bool map_can_reset_no_group(const mapstruct *map, long current_time)
Definition: swap.cpp:253
SAVE_ERROR_OK
#define SAVE_ERROR_OK
Definition: map.h:139
players_on_map
int players_on_map(mapstruct *m, int show_all)
Definition: swap.cpp:234
save_map
int save_map(mapstruct *m, int flag)
Definition: map.cpp:1396
mapstruct::next
mapstruct * next
Definition: map.h:314
TRUE
#define TRUE
Definition: compat.h:11
MAP_MAXRESET
#define MAP_MAXRESET
Definition: config.h:425
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:391
SAVE_ERROR_PLAYER
#define SAVE_ERROR_PLAYER
Definition: map.h:148
altar_valkyrie.res
int res
Definition: altar_valkyrie.py:74
object.h
events_execute_global_event
void events_execute_global_event(int eventcode,...)
Definition: events.cpp:32
llevDebug
@ llevDebug
Definition: logger.h:13
OutputFile
Definition: output_file.h:41
Settings::localdir
const char * localdir
Definition: global.h:249