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 
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 
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  map->timeout = 0;
150  return SAVE_ERROR_PLAYER;
151  }
152  pets_attempt_follow(0, 0); /* Give them a chance to follow */
153 
154  /* Update the reset time. Only do this is STAND_STILL is not set */
155  if (!map->fixed_resettime)
157 
158  /* If it is immediate reset time and not in a reset group, don't bother
159  * saving it - just get rid of it right away.
160  * If it is part of a reset group, then swap it, and handle reset in
161  * flush_old_maps().
162  */
163  if (map->reset_time <= seconds() && map->reset_group == NULL) {
164  mapstruct *oldmap = map;
165 
166  LOG(llevDebug, "Resetting map %s instead of swapping it.\n", map->path);
168  map = map->next;
169  delete_map(oldmap);
170  return SAVE_ERROR_OK;
171  }
172 
173  LOG(llevDebug, "Swapping map %s.\n", map->path);
174  if ((res = save_map(map, SAVE_MODE_NORMAL)) < 0) {
175  LOG(llevError, "Failed to swap map %s.\n", map->path);
176  /* This is sufficiently critical to mandate to warn all DMs. */
178  "Failed to swap map %s!", map->path);
179  /* Map is *not *swapped. */
180  map->in_memory = MAP_IN_MEMORY;
181 
182  // Add random delay to avoid repeated failing attempts at swapping.
183  map->timeout = 60 + rndm(0, 30);
184  return res;
185  }
186 
187  map->timeout = 0;
188  free_map(map);
189 
191  write_map_log();
192 
193  return SAVE_ERROR_OK;
194 }
195 
199 void check_active_maps(void) {
200  /* Swapping can take many tens of milliseconds. Swapping too many maps in
201  * one tick can cause enough latency for the server to skip time. */
202  int num_to_swap = 1;
203 
204  mapstruct *map, *next;
205  for (map = first_map; map != NULL; map = next) {
206  next = map->next;
207  if (map->in_memory == MAP_IN_MEMORY) {
208  // map->timeout == 0 means to never swap
209  if (map->timeout > 1) {
210  map->timeout -= 1;
211  } else if (num_to_swap > 0 && map->timeout == 1) {
212  swap_map(map);
213  num_to_swap -= 1;
214  }
215  }
216  }
217 }
218 
232 int players_on_map(mapstruct *m, int show_all) {
233  player *pl;
234  int nr = 0;
235 
236  for (pl = first_player; pl != NULL; pl = pl->next)
237  if (pl->ob != NULL
239  && pl->ob->map == m
240  && (show_all || !pl->hidden))
241  nr++;
242  return nr;
243 }
244 
251 static bool map_can_reset_no_group(const mapstruct *map, long current_time) {
252  return (map->in_memory == MAP_SWAPPED)
253  && (map->tmpname != NULL)
254  && (current_time >= map->reset_time);
255 }
256 
264 bool map_can_reset(const mapstruct *map, long current_time) {
265  if (!map_can_reset_no_group(map, current_time)) {
266  return 0;
267  }
268 
269  if (map->reset_group == NULL) {
270  return 1;
271  }
272 
273  mapstruct *other = first_map;
274  while (other) {
275  if (other != map && other->reset_group == map->reset_group && !map_can_reset_no_group(other, current_time)) {
276  /* Another map isn't ready to reset, so don't reset this map either */
277  return 0;
278  }
279  other = other->next;
280  }
281 
282  return 1;
283 }
284 
289 void flush_old_maps(void) {
290  mapstruct *m, *oldmap;
291  long sec;
292  sec = seconds();
293 
294  m = first_map;
295  while (m) {
296  /* There can be cases (ie death) where a player leaves a map and the timeout
297  * is not set so it isn't swapped out.
298  */
299  if ((m->in_memory == MAP_IN_MEMORY)
300  && (m->timeout == 0)
301  && !players_on_map(m, TRUE)) {
303  }
304 
305  /* per player unique maps are never really reset. However, we do want
306  * to perdiocially remove the entries in the list of active maps - this
307  * generates a cleaner listing if a player issues the map commands, and
308  * keeping all those swapped out per player unique maps also has some
309  * memory and cpu consumption.
310  * We do the cleanup here because there are lots of places that call
311  * swap map, and doing it within swap map may cause problems as
312  * the functions calling it may not expect the map list to change
313  * underneath them.
314  */
315  if ((m->unique || m->is_template) && m->in_memory == MAP_SWAPPED) {
316  LOG(llevDebug, "Resetting unique or template map %s.\n", m->path);
317  oldmap = m;
318  m = m->next;
319  delete_map(oldmap);
320  } else if (!map_can_reset(m, sec)) {
321  m = m->next;
322  } else {
323  LOG(llevDebug, "Resetting map %s.\n", m->path);
325  clean_tmp_map(m);
326  oldmap = m;
327  m = m->next;
328  delete_map(oldmap);
329  }
330  }
331 }
mapdef::reset_group
sstring reset_group
Definition: map.h:329
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:33
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:51
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
strdup_local
#define strdup_local
Definition: compat.h:29
SAVE_ERROR_NOT_IN_MEMORY
#define SAVE_ERROR_NOT_IN_MEMORY
Definition: map.h:152
obj::map
struct mapdef * map
Definition: object.h:303
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
pl
Definition: player.h:105
set_map_timeout
void set_map_timeout(mapstruct *oldmap)
Definition: main.cpp:304
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
pl::ob
object * ob
Definition: player.h:176
NDI_ALL_DMS
#define NDI_ALL_DMS
Definition: newclient.h:264
Ice.tmp
int tmp
Definition: Ice.py:207
NDI_RED
#define NDI_RED
Definition: newclient.h:245
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
buf
StringBuffer * buf
Definition: readable.cpp:1611
m
static event_registration m
Definition: citylife.cpp:425
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Definition: map.h:131
map_can_reset
bool map_can_reset(const mapstruct *map, long current_time)
Definition: swap.cpp:264
swap_map
int swap_map(mapstruct *map)
Definition: swap.cpp:135
pl::next
struct pl * next
Definition: player.h:106
disinfect.map
map
Definition: disinfect.py:4
read_map_log
void read_map_log(void)
Definition: swap.cpp:70
split_string
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.cpp:483
first_map
mapstruct * first_map
Definition: init.cpp:107
check_active_maps
void check_active_maps(void)
Definition: swap.cpp:199
clean_tmp_map
void clean_tmp_map(mapstruct *m)
Definition: map.cpp:1976
EVENT_MAPRESET
#define EVENT_MAPRESET
Definition: events.h:50
free_map
void free_map(mapstruct *m)
Definition: map.cpp:1684
sproto.h
mapdef
Definition: map.h:317
delete_map
void delete_map(mapstruct *m)
Definition: map.cpp:1730
get_linked_map
mapstruct * get_linked_map(void)
Definition: map.cpp:788
seconds
long seconds(void)
Definition: time.cpp:344
MAX_BUF
#define MAX_BUF
Definition: define.h:35
MSG_TYPE_ADMIN_LOADSAVE
#define MSG_TYPE_ADMIN_LOADSAVE
Definition: newclient.h:499
set_map_reset_time
void set_map_reset_time(mapstruct *map)
Definition: map.cpp:2255
SAVE_MODE_NORMAL
#define SAVE_MODE_NORMAL
Definition: map.h:121
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:262
flush_old_maps
void flush_old_maps(void)
Definition: swap.cpp:289
MAP_SWAPPED
#define MAP_SWAPPED
Definition: map.h:132
pl::hidden
uint32_t hidden
Definition: player.h:147
map_can_reset_no_group
static bool map_can_reset_no_group(const mapstruct *map, long current_time)
Definition: swap.cpp:251
SAVE_ERROR_OK
#define SAVE_ERROR_OK
Definition: map.h:144
players_on_map
int players_on_map(mapstruct *m, int show_all)
Definition: swap.cpp:232
save_map
int save_map(mapstruct *m, int flag)
Definition: map.cpp:1428
TRUE
#define TRUE
Definition: compat.h:11
MAP_MAXRESET
#define MAP_MAXRESET
Definition: config.h:417
altar_valkyrie.pl
pl
Definition: altar_valkyrie.py:28
mapdef::next
struct mapdef * next
Definition: map.h:318
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:402
SAVE_ERROR_PLAYER
#define SAVE_ERROR_PLAYER
Definition: map.h:153
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:27
llevDebug
@ llevDebug
Definition: logger.h:13
OutputFile
Definition: output_file.h:41
Settings::localdir
const char * localdir
Definition: global.h:249