Crossfire Server, Trunk  1.75.0
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 
136 int swap_map(mapstruct *map) {
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)
157  set_map_reset_time(map);
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 }
output_file.h
global.h
first_player
player * first_player
First player.
Definition: init.cpp:106
settings
struct Settings settings
Global settings.
Definition: init.cpp:139
write_map_log
static void write_map_log(void)
Writes out information on all the temporary maps.
Definition: swap.cpp:34
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
Settings::recycle_tmp_maps
uint8_t recycle_tmp_maps
Re-use tmp maps.
Definition: global.h:273
llevError
@ llevError
Error, serious thing.
Definition: logger.h:11
mapstruct::difficulty
uint16_t difficulty
What level the player should be to play here.
Definition: map.h:333
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
of_close
int of_close(OutputFile *of)
Closes an output file.
Definition: output_file.cpp:61
of_open
FILE * of_open(OutputFile *of, const char *fname)
Opens an output file.
Definition: output_file.cpp:30
player
One 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
Map to swap isn't in memory.
Definition: map.h:147
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
mapstruct::tmpname
char * tmpname
Name of temporary file.
Definition: map.h:317
set_map_timeout
void set_map_timeout(mapstruct *oldmap)
Enable swapping for the given map.
Definition: main.cpp:304
object::map
struct mapstruct * map
Pointer to the map in which this object is present.
Definition: object.h:305
maps_swapped_total
int maps_swapped_total
Definition: logger.cpp:41
time
non standard information is not specified or uptime this means how long since the executable has been started A particular host may have been running a server for quite a long time
Definition: arch-handbook.txt:206
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
Inform all logged in DMs.
Definition: newclient.h:268
NDI_RED
#define NDI_RED
Definition: newclient.h:249
rndm
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.cpp:162
mapstruct::path
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:355
buf
StringBuffer * buf
Definition: readable.cpp:1565
m
static event_registration m
Definition: citylife.cpp:424
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:126
map_can_reset
bool map_can_reset(const mapstruct *map, long current_time)
Returns whether a map can be reset, including all other maps in the same reset group.
Definition: swap.cpp:266
swap_map
int swap_map(mapstruct *map)
Swaps a map to disk.
Definition: swap.cpp:136
read_map_log
void read_map_log(void)
Reads temporary maps information from disk.
Definition: swap.cpp:71
stats.h
split_string
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.cpp:473
first_map
mapstruct * first_map
First map.
Definition: init.cpp:107
check_active_maps
void check_active_maps(void)
Finds maps in memory to swap.
Definition: swap.cpp:201
clean_tmp_map
void clean_tmp_map(mapstruct *m)
Removse the temporary file used by the map.
Definition: map.cpp:1983
EVENT_MAPRESET
#define EVENT_MAPRESET
A map is resetting.
Definition: events.h:63
free_map
void free_map(mapstruct *m)
Frees everything allocated by the given mapstructure.
Definition: map.cpp:1660
mapstruct::reset_group
sstring reset_group
For reset purpose, all maps in the same group reset at the same time.
Definition: map.h:327
sproto.h
delete_map
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.cpp:1706
get_linked_map
mapstruct * get_linked_map(void)
Allocates, initialises, and returns a pointer to a mapstruct, linked through first_map.
Definition: map.cpp:786
seconds
long seconds(void)
Return wall clock time in seconds.
Definition: time.cpp:348
MAX_BUF
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
MSG_TYPE_ADMIN_LOADSAVE
#define MSG_TYPE_ADMIN_LOADSAVE
load/save operations
Definition: newclient.h:503
set_map_reset_time
void set_map_reset_time(mapstruct *map)
Updates the map's timeout.
Definition: map.cpp:2263
SAVE_MODE_NORMAL
#define SAVE_MODE_NORMAL
No special handling.
Definition: map.h:116
pets_attempt_follow
void pets_attempt_follow(object *for_owner, int force)
Check pets so they try to follow their master around the world.
Definition: pets.cpp:262
FLAG_REMOVED
#define FLAG_REMOVED
Object is not in any map or invenory.
Definition: define.h:232
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
mapstruct
This is a game-map.
Definition: map.h:315
flush_old_maps
void flush_old_maps(void)
Reset maps that need to, remove their swap file.
Definition: swap.cpp:291
mapstruct::in_memory
uint32_t in_memory
Combination of IN_MEMORY_xxx flags.
Definition: map.h:335
unpaid_count::pl
object * pl
Definition: shop.cpp:683
MAP_SWAPPED
#define MAP_SWAPPED
Map spaces have been saved to disk.
Definition: map.h:127
mapstruct::timeout
int32_t timeout
Swapout is set to this.
Definition: map.h:332
map_can_reset_no_group
static bool map_can_reset_no_group(const mapstruct *map, long current_time)
Returns whether a map can be reset, without taking its reset group into account.
Definition: swap.cpp:253
SAVE_ERROR_OK
#define SAVE_ERROR_OK
No error.
Definition: map.h:139
players_on_map
int players_on_map(mapstruct *m, int show_all)
Returns the count of players on a map, calculated from player list.
Definition: swap.cpp:234
save_map
int save_map(mapstruct *m, int flag)
Saves a map to file.
Definition: map.cpp:1405
mapstruct::next
mapstruct * next
Next map, linked list.
Definition: map.h:316
mapstruct::reset_time
uint32_t reset_time
Server time when map gets reset, seconds since epoch.
Definition: map.h:322
TRUE
#define TRUE
Definition: compat.h:11
mapstruct::darkness
uint8_t darkness
Indicates level of darkness of map.
Definition: map.h:336
MAP_MAXRESET
#define MAP_MAXRESET
MAP_MAXRESET is the maximum time a map can have before being reset.
Definition: config.h:425
mapstruct::fixed_resettime
uint32_t fixed_resettime
If true, reset time is not affected by players entering/exiting map.
Definition: map.h:325
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:406
object::next
object * next
Pointer to the next object in the free/used list.
Definition: object.h:285
SAVE_ERROR_PLAYER
#define SAVE_ERROR_PLAYER
Player on map to save.
Definition: map.h:148
object.h
events_execute_global_event
void events_execute_global_event(int eventcode,...)
Execute a global event.
Definition: events.cpp:30
llevDebug
@ llevDebug
Only for debugging purposes.
Definition: logger.h:13
OutputFile
Definition: output_file.h:41
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:250