Crossfire Server, Trunk
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  /* Don't allow escaping from the styles directory. */
189  if (strstr(dirname, "..") != NULL || (stylename != NULL && strstr(stylename, "..") != NULL))
190  return NULL;
191 
192  /* if stylename exists, set style_file_path to that file.*/
193  if (stylename && strlen(stylename) > 0) {
194  snprintf(style_file_path, sizeof(style_file_path), "%s/%s", dirname, stylename);
195  } else { /* otherwise, just use the dirname. We'll pick a random stylefile.*/
196  strlcpy(style_file_path, dirname, sizeof(style_file_path));
197  }
198 
199  /* is what we were given a directory, or a file? */
200  snprintf(style_file_full_path, sizeof(style_file_full_path), "%s/maps%s", settings.datadir, style_file_path);
201  if (stat(style_file_full_path, &file_stat) == 0
202  && !S_ISDIR(file_stat.st_mode)) {
203  style_map = load_style_map(style_file_path);
204  }
205  if (style_map == NULL) { /* maybe we were given a directory! */
206  char **namelist;
207  int n;
208  char style_dir_full_path[256];
209 
210  /* get the names of all the files in that directory */
211  snprintf(style_dir_full_path, sizeof(style_dir_full_path), "%s/maps%s", settings.datadir, style_file_path);
212 
213  /* First, skip subdirectories. If we don't find anything, then try again
214  * without skipping subdirs.
215  */
216  n = load_dir(style_dir_full_path, &namelist, 1);
217  if (n <= 0) {
218  n = load_dir(style_dir_full_path, &namelist, 0);
219  only_subdirs = 1;
220  }
221 
222  if (n <= 0) {
223  return NULL; /* nothing to load. Bye. */
224  }
225 
226  /* Picks a random map. Note that if this is all directories,
227  * we know it won't be able to load, so save a few ticks.
228  * the door handling checks for this failure and handles
229  * it properly.
230  */
231  if (difficulty == -1) { /* pick a random style from this dir. */
232  if (only_subdirs) {
233  style_map = NULL;
234  } else {
235  char *p;
236 
237  p = strchr(style_file_path, '\0');
238  snprintf(p, style_file_path+sizeof(style_file_path)-p, "/%s", namelist[RANDOM()%n]);
239  style_map = load_style_map(style_file_path);
240  }
241  } else { /* find the map closest in difficulty */
242  int min_dist = 32000, min_index = -1;
243  char *p;
244 
245  for (i = 0; i < n; i++) {
246  int dist;
247  char *mfile_name = strrchr(namelist[i], '_');
248  if (mfile_name == NULL) { /* since there isn't a sequence, */
249  int q;
250 
251  /*pick one at random to recurse */
252  style_map = find_style(style_file_path, namelist[RANDOM()%n], difficulty);
253  for (q = 0; q < n; q++) {
254  free(namelist[q]);
255  }
256  free(namelist);
257  return style_map;
258  } else {
259  dist = abs(difficulty - atoi(mfile_name + 1));
260  if (dist < min_dist) {
261  min_dist = dist;
262  min_index = i;
263  }
264  }
265  }
266  /* presumably now we've found the "best" match for the
267  difficulty. */
268  p = strchr(style_file_path, '\0');
269  snprintf(p, style_file_path+sizeof(style_file_path)-p, "/%s", namelist[min_index]);
270  style_map = load_style_map(style_file_path);
271  }
272  for (i = 0; i < n; i++) {
273  free(namelist[i]);
274  }
275  free(namelist);
276  }
277  return style_map;
278 }
279 
290 {
291  int x, y, limit = 0;
292  object *new_obj;
293 
294  /* while returning a null object will result in a crash, that
295  * is actually preferable to an infinite loop. That is because
296  * most servers will automatically restart in case of crash.
297  * Change the logic on getting the random space - shouldn't make
298  * any difference, but this seems clearer to me.
299  */
300  do {
301  limit++;
302  x = RANDOM()%MAP_WIDTH(style);
303  y = RANDOM()%MAP_HEIGHT(style);
304  new_obj = GET_MAP_OB(style, x, y);
305  } while (new_obj == NULL && limit < 1000);
306  return HEAD(new_obj);
307 }
308 
312 void free_style_maps(void)
313 {
314  mapstruct *next;
315  int style_maps = 0;
316 
317  /* delete_map will try to free it from the linked list,
318  * but won't find it, so we need to do it ourselves
319  */
320  while (styles) {
321  next = styles->next;
323  styles = next;
324  style_maps++;
325  }
326  LOG(llevDebug, "free_style_maps: Freed %d maps\n", style_maps);
327 }
give.next
def next
Definition: give.py:44
GET_MAP_OB
#define GET_MAP_OB(M, X, Y)
Definition: map.h:173
global.h
random_map.h
strdup_local
#define strdup_local
Definition: compat.h:29
diamondslots.x
x
Definition: diamondslots.py:15
Settings::datadir
const char * datadir
Definition: global.h:243
pick_random_object
object * pick_random_object(mapstruct *style)
Definition: style.c:289
Ice.tmp
int tmp
Definition: Ice.py:207
NAME_MAX
#define NAME_MAX
Definition: define.h:30
load_style_map
mapstruct * load_style_map(char *style_name)
Definition: style.c:131
first_map
EXTERN mapstruct * first_map
Definition: global.h:116
MAP_STYLE
#define MAP_STYLE
Definition: map.h:99
settings
struct Settings settings
Definition: init.c:39
item.q
q
Definition: item.py:32
opendir
DIR * opendir(const char *)
find_style
mapstruct * find_style(const char *dirname, const char *stylename, int difficulty)
Definition: style.c:180
CFInsulter.style
style
Definition: CFInsulter.py:69
HEAD
#define HEAD(op)
Definition: object.h:593
readdir
struct dirent * readdir(DIR *)
styles
mapstruct * styles
Definition: style.c:122
pointer_strcmp
static int pointer_strcmp(const void *p1, const void *p2)
Definition: style.c:43
mapdef
Definition: map.h:317
delete_map
void delete_map(mapstruct *m)
Definition: map.c:1722
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
MAP_WIDTH
#define MAP_WIDTH(m)
Definition: map.h:78
strlcpy
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: porting.c:220
RANDOM
#define RANDOM()
Definition: define.h:644
dirent
#define dirent
Definition: xdir.h:12
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
load_dir
int load_dir(const char *dir, char ***namelist, int skip_dirs)
Definition: style.c:74
diamondslots.y
y
Definition: diamondslots.py:16
MAP_HEIGHT
#define MAP_HEIGHT(m)
Definition: map.h:80
free_style_maps
void free_style_maps(void)
Definition: style.c:312
mapdef::path
char path[HUGE_BUF]
Definition: map.h:358
closedir
int closedir(DIR *)
mapdef::next
struct mapdef * next
Definition: map.h:318
llevDebug
@ llevDebug
Definition: logger.h:13
mapfile_load
mapstruct * mapfile_load(const char *map, int flags)
Definition: map.c:1214
give.name
name
Definition: give.py:27