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->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 }
player::next
player * next
Pointer to next player, NULL if this is last.
Definition: player.h:108
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:276
llevError
@ llevError
Problems requiring server admin to fix.
Definition: logger.h:11
mapstruct::difficulty
uint16_t difficulty
What level the player should be to play here.
Definition: map.h:337
LOG
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:82
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:107
strdup_local
#define strdup_local
Definition: compat.h:29
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:371
time
same as sound ncom command like but with extra the client want tick commands so it knows animation timing the client wants to be informed of pickup mode changes Mode will be sent when the player successfully logs and afterward any time the value is but over time
Definition: protocol.txt:416
SAVE_ERROR_NOT_IN_MEMORY
#define SAVE_ERROR_NOT_IN_MEMORY
Map to swap isn't in memory.
Definition: map.h:152
mapstruct::tmpname
char * tmpname
Name of temporary file.
Definition: map.h:322
player::ob
object * ob
The object representing the player.
Definition: player.h:179
set_map_timeout
void set_map_timeout(mapstruct *oldmap)
Enable swapping for the given map.
Definition: main.cpp:312
MAP_IN_MEMORY
#define MAP_IN_MEMORY
Map is fully loaded.
Definition: map.h:131
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:58
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
player::hidden
uint32_t hidden
If True, player (DM) is hidden from view.
Definition: player.h:149
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:360
buf
StringBuffer * buf
Definition: readable.cpp:1564
SAVE_ERROR_PLAYER
#define SAVE_ERROR_PLAYER
Player on map to save.
Definition: map.h:153
FLAG_REMOVED
#define FLAG_REMOVED
Object is not in any map or invenory.
Definition: define.h:219
m
static event_registration m
Definition: citylife.cpp:424
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:478
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:1974
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:1650
mapstruct::reset_group
sstring reset_group
For reset purpose, all maps in the same group reset at the same time.
Definition: map.h:332
sproto.h
delete_map
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.cpp:1696
SAVE_MODE_NORMAL
#define SAVE_MODE_NORMAL
No special handling.
Definition: map.h:121
get_linked_map
mapstruct * get_linked_map(void)
Allocates, initialises, and returns a pointer to a mapstruct, linked through first_map.
Definition: map.cpp:763
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:2254
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
NDI_UNIQUE
#define NDI_UNIQUE
Print immediately, don't buffer.
Definition: newclient.h:266
mapstruct
This is a game-map.
Definition: map.h:320
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:339
MAP_SWAPPED
#define MAP_SWAPPED
Map spaces have been saved to disk.
Definition: map.h:132
mapstruct::timeout
int32_t timeout
Swapout is set to this.
Definition: map.h:336
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
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:1470
mapstruct::next
mapstruct * next
Next map, linked list.
Definition: map.h:321
mapstruct::reset_time
uint32_t reset_time
Server time when map gets reset, seconds since epoch.
Definition: map.h:327
TRUE
#define TRUE
Definition: compat.h:11
mapstruct::darkness
uint8_t darkness
Indicates level of darkness of map.
Definition: map.h:340
SAVE_ERROR_OK
#define SAVE_ERROR_OK
No error.
Definition: map.h:144
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:330
MSG_TYPE_ADMIN
#define MSG_TYPE_ADMIN
Definition: newclient.h:406
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:15
OutputFile
Definition: output_file.h:41
Settings::localdir
const char * localdir
Read/write data files.
Definition: global.h:254