Crossfire Server, Trunk  R20513
style.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 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 
21 #include "global.h"
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #ifndef WIN32 /* ---win32 exclude headers */
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include "../include/autoconf.h"
31 #endif /* win32 */
32 
33 #include "random_map.h"
34 
43 static int pointer_strcmp(const void *p1, const void *p2)
44 {
45  const char *s1 = *(const char * const *)p1;
46  const char *s2 = *(const char * const *)p2;
47 
48  return(strcmp(s1, s2));
49 }
50 
74 int load_dir(const char *dir, char ***namelist, int skip_dirs)
75 {
76  DIR *dp;
77  struct dirent *d;
78  int entries = 0, entry_size = 0;
79  char name[NAME_MAX+1], **rn = NULL;
80  struct stat sb;
81 
82  dp = opendir(dir);
83  if (dp == NULL) {
84  return -1;
85  }
86 
87  while ((d = readdir(dp)) != NULL) {
88  if (skip_dirs) {
89  snprintf(name, sizeof(name), "%s/%s", dir, d->d_name);
90  stat(name, &sb);
91  if (S_ISDIR(sb.st_mode)) {
92  continue;
93  }
94  }
95 
96  if (entries == entry_size) {
97  entry_size += 10;
98  rn = realloc(rn, sizeof(char *)*entry_size);
99  }
100  rn[entries] = strdup_local(d->d_name);
101  entries++;
102  }
103  (void)closedir(dp);
104 
105  /* We don't return -1 for this case here as directory isn't invalid as such.
106  * Most likely directory was empty. In theory it could also be due to
107  * realloc failing to allocate.
108  */
109  if (rn == NULL) {
110  return 0;
111  }
112 
113  qsort(rn, entries, sizeof(char *), pointer_strcmp);
114 
115  *namelist = rn;
116  return entries;
117 }
118 
123 
131 mapstruct *load_style_map(char *style_name)
132 {
133  mapstruct *style_map;
134 
135  /* Given a file. See if its in memory */
136  for (style_map = styles; style_map != NULL; style_map = style_map->next) {
137  if (!strcmp(style_name, style_map->path)) {
138  return style_map;
139  }
140  }
141  style_map = mapfile_load(style_name, MAP_STYLE);
142  /* Remove it from global list, put it on our local list */
143  if (style_map) {
144  mapstruct *tmp;
145 
146  if (style_map == first_map) {
147  first_map = style_map->next;
148  } else {
149  for (tmp = first_map; tmp && tmp->next != style_map; tmp = tmp->next)
150  ;
151  if (tmp) {
152  tmp->next = style_map->next;
153  }
154  }
155  style_map->next = styles;
156  styles = style_map;
157  }
158  return style_map;
159 }
160 
180 mapstruct *find_style(const char *dirname, const char *stylename, int difficulty)
181 {
182  char style_file_path[256];
183  char style_file_full_path[256];
184  mapstruct *style_map = NULL;
185  struct stat file_stat;
186  int i, only_subdirs = 0;
187 
188  /* if stylename exists, set style_file_path to that file.*/
189  if (stylename && strlen(stylename) > 0) {
190  snprintf(style_file_path, sizeof(style_file_path), "%s/%s", dirname, stylename);
191  } else { /* otherwise, just use the dirname. We'll pick a random stylefile.*/
192  snprintf(style_file_path, sizeof(style_file_path), "%s", dirname);
193  }
194 
195  /* is what we were given a directory, or a file? */
196  snprintf(style_file_full_path, sizeof(style_file_full_path), "%s/maps%s", settings.datadir, style_file_path);
197  if (stat(style_file_full_path, &file_stat) == 0
198  && !S_ISDIR(file_stat.st_mode)) {
199  style_map = load_style_map(style_file_path);
200  }
201  if (style_map == NULL) { /* maybe we were given a directory! */
202  char **namelist;
203  int n;
204  char style_dir_full_path[256];
205 
206  /* get the names of all the files in that directory */
207  snprintf(style_dir_full_path, sizeof(style_dir_full_path), "%s/maps%s", settings.datadir, style_file_path);
208 
209  /* First, skip subdirectories. If we don't find anything, then try again
210  * without skipping subdirs.
211  */
212  n = load_dir(style_dir_full_path, &namelist, 1);
213  if (n <= 0) {
214  n = load_dir(style_dir_full_path, &namelist, 0);
215  only_subdirs = 1;
216  }
217 
218  if (n <= 0) {
219  return NULL; /* nothing to load. Bye. */
220  }
221 
222  /* Picks a random map. Note that if this is all directories,
223  * we know it won't be able to load, so save a few ticks.
224  * the door handling checks for this failure and handles
225  * it properly.
226  */
227  if (difficulty == -1) { /* pick a random style from this dir. */
228  if (only_subdirs) {
229  style_map = NULL;
230  } else {
231  char *p;
232 
233  p = strchr(style_file_path, '\0');
234  snprintf(p, style_file_path+sizeof(style_file_path)-p, "/%s", namelist[RANDOM()%n]);
235  style_map = load_style_map(style_file_path);
236  }
237  } else { /* find the map closest in difficulty */
238  int min_dist = 32000, min_index = -1;
239  char *p;
240 
241  for (i = 0; i < n; i++) {
242  int dist;
243  char *mfile_name = strrchr(namelist[i], '_')+1;
244 
245  if ((mfile_name-1) == '\0') { /* since there isn't a sequence, */
246  int q;
247 
248  /*pick one at random to recurse */
249  style_map = find_style(style_file_path, namelist[RANDOM()%n], difficulty);
250  for (q = 0; q < n; q++) {
251  free(namelist[q]);
252  }
253  free(namelist);
254  return style_map;
255  } else {
256  dist = abs(difficulty-atoi(mfile_name));
257  if (dist < min_dist) {
258  min_dist = dist;
259  min_index = i;
260  }
261  }
262  }
263  /* presumably now we've found the "best" match for the
264  difficulty. */
265  p = strchr(style_file_path, '\0');
266  snprintf(p, style_file_path+sizeof(style_file_path)-p, "/%s", namelist[min_index]);
267  style_map = load_style_map(style_file_path);
268  }
269  for (i = 0; i < n; i++) {
270  free(namelist[i]);
271  }
272  free(namelist);
273  }
274  return style_map;
275 }
276 
287 {
288  int x, y, limit = 0;
289  object *new_obj;
290 
291  /* while returning a null object will result in a crash, that
292  * is actually preferable to an infinite loop. That is because
293  * most servers will automatically restart in case of crash.
294  * Change the logic on getting the random space - shouldn't make
295  * any difference, but this seems clearer to me.
296  */
297  do {
298  limit++;
299  x = RANDOM()%MAP_WIDTH(style);
300  y = RANDOM()%MAP_HEIGHT(style);
301  new_obj = GET_MAP_OB(style, x, y);
302  } while (new_obj == NULL && limit < 1000);
303  return HEAD(new_obj);
304 }
305 
309 void free_style_maps(void)
310 {
311  mapstruct *next;
312  int style_maps = 0;
313 
314  /* delete_map will try to free it from the linked list,
315  * but won't find it, so we need to do it ourselves
316  */
317  while (styles) {
318  next = styles->next;
319  delete_map(styles);
320  styles = next;
321  style_maps++;
322  }
323  LOG(llevDebug, "free_style_maps: Freed %d maps\n", style_maps);
324 }
char path[HUGE_BUF]
Filename of the map.
Definition: map.h:365
mapstruct * mapfile_load(const char *map, int flags)
Opens the file "filename" and reads information about the map from the given file, and stores it in a newly allocated mapstruct.
Definition: map.c:1235
#define MAP_STYLE
Active objects shouldn&#39;t be put on active list.
Definition: map.h:98
#define strdup_local
Definition: compat.h:25
Random map related variables.
#define MAP_HEIGHT(m)
Map height.
Definition: map.h:80
DIR * opendir(const char *)
Opens a directory for reading.
Definition: win32.c:37
Definition: win32.h:110
Global type definitions and header inclusions.
static int pointer_strcmp(const void *p1, const void *p2)
Char comparison for sorting purposes.
Definition: style.c:43
Definition: win32.h:120
mapstruct * styles
Loaded styles maps cache, to avoid having to load all the time.
Definition: style.c:122
mapstruct * find_style(const char *dirname, const char *stylename, int difficulty)
Loads and returns the map requested.
Definition: style.c:180
int load_dir(const char *dir, char ***namelist, int skip_dirs)
This is our own version of scandir/select_regular_files/sort.
Definition: style.c:74
#define snprintf
Definition: win32.h:46
char d_name[_MAX_FNAME+1]
Definition: win32.h:114
#define HEAD(op)
Returns the head part of an object.
Definition: object.h:594
object * pick_random_object(mapstruct *style)
Picks a random object from a style map.
Definition: style.c:286
mapstruct * load_style_map(char *style_name)
Loads specified map (or take it from cache list).
Definition: style.c:131
const char * datadir
Read only data files.
Definition: global.h:244
#define RANDOM()
Definition: define.h:679
#define MAP_WIDTH(m)
Map width.
Definition: map.h:78
Only for debugging purposes.
Definition: logger.h:13
struct Settings settings
Server settings.
Definition: init.c:40
struct dirent * readdir(DIR *)
Returns the next file/directory for specified directory handle, obtained through a call to opendir()...
Definition: win32.c:75
void delete_map(mapstruct *m)
Frees the map, including the mapstruct.
Definition: map.c:1741
#define GET_MAP_OB(M, X, Y)
Gets the bottom object on a map.
Definition: map.h:172
int closedir(DIR *)
Dispose of a directory handle.
Definition: win32.c:108
#define S_ISDIR(x)
Definition: win32.h:69
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
EXTERN mapstruct * first_map
First map.
Definition: global.h:118
void free_style_maps(void)
Frees cached style maps.
Definition: style.c:309
#define NAME_MAX
NAME_MAX used by random maps may not be defined on pure ansi systems.
Definition: define.h:30