Crossfire Server, Trunk  R20513
region.c
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 
21 #include "global.h"
22 
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #ifndef WIN32 /* ---win32 exclude header */
28 #include <unistd.h>
29 #endif /* win32 */
30 
31 static void parse_regions(FILE *fp);
32 static void assign_region_parents(void);
33 
46 region *get_region_by_name(const char *region_name) {
47  region *reg;
48 
49  for (reg = first_region; reg != NULL; reg = reg->next)
50  if (!strcmp(reg->name, region_name))
51  return reg;
52 
53  for (reg = first_region; reg != NULL; reg = reg->next) {
54  if (reg->fallback) {
55  LOG(llevDebug, "region called %s requested, but not found, fallback used.\n", region_name);
56  return reg;
57  }
58  }
59  LOG(llevInfo, "Got no region or fallback for region %s.\n", region_name);
60  return NULL;
61 }
62 
76 }
77 
92 const char *get_name_of_region_for_map(const mapstruct *m) {
93  region *reg;
94 
95  if (m->region != NULL)
96  return m->region->name;
97  for (reg = first_region; reg != NULL; reg = reg->next) {
98  if (reg->fallback)
99  return reg->name;
100  }
101  LOG(llevInfo, "map %s had no region and I couldn't find a fallback to use.\n", m->name);
102  return "unknown";
103 }
104 
121 region *get_region_from_string(const char *name) {
122  region *reg;
123  char *substr;
124  char *p;
125 
126  if (first_region == NULL) {
127  return NULL;
128  }
129 
130  if (*name == '\0') {
131  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
132  ;
133  return reg;
134  }
135  p = strchr(name, '\n');
136  if (p)
137  *p = '\0';
138  for (reg = first_region; reg != NULL; reg = reg->next)
139  if (!strcasecmp(reg->name, name))
140  return reg;
141 
142  for (reg = first_region; reg != NULL; reg = reg->next)
143  if (reg->longname != NULL) {
144  if (!strcasecmp(reg->longname, name))
145  return reg;
146  }
147 
148  substr = NULL;
149  for (reg = first_region; reg != NULL; reg = reg->next)
150  if (reg->longname != NULL) {
151  substr = strstr(reg->longname, name);
152  if (substr != NULL)
153  return reg;
154  }
155  for (reg = first_region; reg != NULL; reg = reg->next)
156  if (reg->longname != NULL) {
157  /*
158  * This is not a bug, we want the region that is most identifiably a discrete
159  * area in the game, eg if we have 'scor', we want to return 'scorn' and not
160  * 'scornarena', regardless of their order on the list so we only look at those
161  * regions with a longname set.
162  */
163  substr = strstr(reg->name, name);
164  if (substr != NULL)
165  return reg;
166  }
167  for (reg = first_region; reg != NULL; reg = reg->next) {
168  substr = strstr(reg->name, name);
169  if (substr != NULL)
170  return reg;
171  }
172  /* if we are still here, we are going to have to give up, and give the top level region */
173  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
174  ;
175  return reg;
176 }
177 
190 int region_is_child_of_region(const region *child, const region *r) {
191  if (r == NULL)
192  return -1;
193  if (child == NULL)
194  return 0;
195  if (!strcmp(child->name, r->name))
196  return 1;
197  else if (child->parent != NULL)
198  return region_is_child_of_region(child->parent, r);
199  else
200  return 0;
201 }
202 
217 const char *get_region_longname(const region *r) {
218  if (r->longname != NULL)
219  return r->longname;
220  else if (r->parent != NULL)
221  return get_region_longname(r->parent);
222  else {
223  LOG(llevDebug, "NOTICE region %s has no parent and no longname.\n", r->name);
224  return "no name can be found for the current region";
225  }
226 }
227 
238 const char *get_region_msg(const region *r) {
239  if (r->msg != NULL)
240  return r->msg;
241  else if (r->parent != NULL)
242  return get_region_msg(r->parent);
243  else {
244  LOG(llevDebug, "NOTICE region %s has no parent and no msg.\n", r->name);
245  return "no description can be found for the current region";
246  }
247 }
248 
260 object *get_jail_exit(object *op) {
261  region *reg;
262  object *exit;
263 
264  if (op->type != PLAYER) {
265  LOG(llevError, "region.c: get_jail_exit called against non-player object.\n");
266  return NULL;
267  }
268 
269  reg = get_region_by_map(op->map);
270  while (reg != NULL) {
271  if (reg->jailmap) {
272  exit = object_new();
273  EXIT_PATH(exit) = add_string(reg->jailmap);
274  /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
275  SET_FLAG(exit, FLAG_DAMNED);
276  EXIT_X(exit) = reg->jailx;
277  EXIT_Y(exit) = reg->jaily;
278  return exit;
279  } else
280  reg = reg->parent;
281  }
282  LOG(llevDebug, "No suitable jailmap for region %s was found.\n", reg->name);
283  return NULL;
284 }
285 
292 int init_regions(void) {
293  FILE *fp;
294  char filename[MAX_BUF];
295 
296  if (first_region != NULL) /* Only do this once */
297  return 0;
298 
299  snprintf(filename, sizeof(filename), "%s/%s/%s", settings.datadir, settings.mapdir, settings.regions);
300  if ((fp = fopen(filename, "r")) == NULL) {
301  LOG(llevError, "Couldn't read regions file from \"%s\".\n", filename);
302  return 1;
303  }
304  parse_regions(fp);
306 
307  fclose(fp);
308  return 0;
309 }
310 
326  region *new = (region *)calloc(1, sizeof(region));
327  if (new == NULL)
329 
330  return new;
331 }
332 
340 static void parse_regions(FILE *fp) {
341  region *new;
342  region *reg;
343 
344  char buf[HUGE_BUF], msgbuf[HUGE_BUF], *key = NULL, *value, *end;
345  int msgpos = 0;
346 
347  new = NULL;
348  while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
349  buf[HUGE_BUF-1] = 0;
350  key = buf;
351  while (isspace(*key))
352  key++;
353  if (*key == 0)
354  continue; /* empty line */
355  value = strchr(key, ' ');
356  if (!value) {
357  end = strchr(key, '\n');
358  *end = 0;
359  } else {
360  *value = 0;
361  value++;
362  /* isspace() includes newline. To avoid crash on empty line further
363  * down we must check for it here.
364  */
365  while (isspace(*value) && *value != '\n')
366  value++;
367  end = strchr(value, '\n');
368  }
369 
370  /*
371  * This is a bizzare mutated form of the map and archetype parser
372  * rolled into one. Key is the field name, value is what it should
373  * be set to.
374  * We've already done the work to null terminate key,
375  * and strip off any leading spaces for both of these.
376  * We have not touched the newline at the end of the line -
377  * these might be needed for some values. the end pointer
378  * points to the first of the newlines.
379  * value could be NULL! It would be easy enough to just point
380  * this to "" to prevent cores, but that would let more errors slide
381  * through.
382  */
383  if (!strcmp(key, "region")) {
384  *end = 0;
385  new = get_region_struct();
386  new->name = strdup_local(value);
387  } else if (!strcmp(key, "parent")) {
388  /*
389  * Note that this is in the initialisation code, so we don't actually
390  * assign the pointer to the parent yet, because it might not have been
391  * parsed.
392  */
393  *end = 0;
394 
395  if (!new) {
396  LOG(llevError, "region.c: malformated regions file: \"parent\" before \"region\".\n");
398  }
399  if (!value) {
400  LOG(llevError, "region.c: malformated regions file: No value given for \"parent\" key.\n");
402  }
403  new->parent_name = strdup_local(value);
404  } else if (!strcmp(key, "longname")) {
405  *end = 0;
406  if (!new) {
407  LOG(llevError, "region.c: malformated regions file: \"longname\" before \"region\".\n");
409  }
410  if (!value) {
411  LOG(llevError, "region.c: malformated regions file: No value given for \"longname\" key.\n");
413  }
414  new->longname = strdup_local(value);
415  } else if (!strcmp(key, "jail")) {
416  /* jail entries are of the form: /path/to/map x y */
417  char path[MAX_BUF];
418  int x, y;
419 
420  if (!new) {
421  LOG(llevError, "region.c: malformated regions file: \"jail\" before \"region\".\n");
423  }
424  if (!value) {
425  LOG(llevError, "region.c: malformated regions file: No value given for \"jail\" key.\n");
427  }
428 
429  if (sscanf(value, "%[^ ] %d %d\n", path, &x, &y) != 3) {
430  LOG(llevError, "region.c: malformated regions entry: jail %s\n", value);
431  continue;
432  }
433  new->jailmap = strdup_local(path);
434  new->jailx = x;
435  new->jaily = y;
436  } else if (!strcmp(key, "msg")) {
437  if (!new) {
438  LOG(llevError, "region.c: malformated regions file: \"msg\" before \"region\".\n");
440  }
441  while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
442  key = buf;
443  while (isspace(*key))
444  key++;
445  if (key && strcmp(key, "endmsg\n") == 0)
446  break;
447  else {
448  strcpy(msgbuf+msgpos, key);
449  msgpos += strlen(key);
450  }
451  }
452  /*
453  * There may be regions with empty messages (eg, msg/endmsg
454  * with nothing between). When maps are loaded, this is done
455  * so better do it here too...
456  */
457  if (msgpos != 0)
458  new->msg = strdup_local(msgbuf);
459 
460  /* we have to reset msgpos, or the next region will store both msg blocks.*/
461  msgpos = 0;
462  } else if (!strcmp(key, "fallback")) {
463  *end = 0;
464  if (!new) {
465  LOG(llevError, "region.c: malformated regions file: \"fallback\" before \"region\".\n");
467  }
468  if (!value) {
469  LOG(llevError, "region.c: malformated regions file: No value given for \"fallback\" key.\n");
471  }
472  new->fallback = atoi(value);
473  } else if (!strcmp(key, "end")) {
474  if (!new) {
475  LOG(llevError, "region.c: Ignoring spurious \"end\" between regions.\n");
476  continue;
477  }
478  /* Place this new region last on the list, if the list is empty put it first */
479  for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next)
480  ;
481 
482  if (reg == NULL)
483  first_region = new;
484  else
485  reg->next = new;
486  new = NULL;
487  } else if (!strcmp(key, "nomore")) {
488  if (new) {
489  LOG(llevError, "region.c: Last region not properly closed.\n");
490  free(new);
491  }
492  /* we have reached the end of the region specs....*/
493  break;
494  } else {
495  /* we should never get here, if we have, then something is wrong */
496  LOG(llevError, "Got unknown value in region file: %s %s\n", key, value);
497  }
498  }
499  if (!key || strcmp(key, "nomore")) {
500  LOG(llevError, "Got premature eof on regions file!\n");
501  free(new);
502  }
503 }
504 
508 static void assign_region_parents(void) {
509  region *reg;
510  uint32_t parent_count = 0;
512 
513  for (reg = first_region; reg != NULL; reg = reg->next) {
514  if (reg->parent_name != NULL) {
516  if (reg->parent == NULL) {
517  LOG(llevError, "Couldn't find parent %s for region %s\n", reg->name, reg->parent_name);
518  }
519  parent_count++;
520  }
521  region_count++;
522  }
523  LOG(llevDebug, "regions: loaded %u with %u parents\n",
524  region_count, parent_count);
525 }
Error, serious thing.
Definition: logger.h:11
#define FLAG_DAMNED
The object is very cursed.
Definition: define.h:318
int init_regions(void)
Initialises regions from the regions file.
Definition: region.c:292
int16_t jaily
The coodinates in jailmap to which the player should be sent.
Definition: map.h:298
Information.
Definition: logger.h:12
#define SET_FLAG(xyz, p)
Definition: define.h:223
region * get_region_by_name(const char *region_name)
Gets a region by name.
Definition: region.c:46
#define strdup_local
Definition: compat.h:25
int region_is_child_of_region(const region *child, const region *r)
Checks if a region is a child of another.
Definition: region.c:190
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.c:596
#define HUGE_BUF
Used for messages - some can be quite long.
Definition: define.h:37
const char * regions
Name of the regions file - libdir is prepended.
Definition: global.h:249
int8_t fallback
Whether, in the event of a region not existing, this should be the one we fall back on as the default...
Definition: map.h:295
region * get_region_by_map(mapstruct *m)
Gets a region from a map.
Definition: region.c:74
Global type definitions and header inclusions.
object * get_jail_exit(object *op)
Returns an object which is an exit through which the player represented by op should be sent in order...
Definition: region.c:260
region * get_region_from_string(const char *name)
Tries to find a region that &#39;name&#39; corresponds to.
Definition: region.c:121
const char * get_region_longname(const region *r)
Gets the longname of a region.
Definition: region.c:217
char * name
Name of map as given by its creator.
Definition: map.h:328
char * name
Shortend name of the region as maps refer to it.
Definition: map.h:278
object * object_new(void)
Grabs an object from the list of unused objects, makes sure it is initialised, and returns it...
Definition: object.c:1037
static int region_count
Count of regions.
Definition: mapper.c:460
struct mapdef * map
Pointer to the map in which this object is present.
Definition: object.h:297
struct regiondef * next
Pointer to next region, NULL for the last one.
Definition: map.h:277
This is a game region.
Definition: map.h:276
#define snprintf
Definition: win32.h:46
region * get_region_struct(void)
Allocates and zeros a region struct, this isn&#39;t free()&#39;d anywhere, so might be a memory leak...
Definition: region.c:325
char * parent_name
So that parent and child regions can be defined in any order, we keep hold of the parent_name during ...
Definition: map.h:279
#define EXIT_PATH(xyz)
Definition: define.h:455
#define EXIT_X(xyz)
Definition: define.h:457
struct regiondef * parent
Pointer to the region that is a parent of the current region, if a value isn&#39;t defined in the current...
Definition: map.h:286
#define EXIT_Y(xyz)
Definition: define.h:458
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
int16_t jailx
Definition: map.h:298
unsigned int uint32_t
Definition: win32.h:162
See Player.
Definition: object.h:107
const char * datadir
Read only data files.
Definition: global.h:244
char * jailmap
Where a player that is arrested in this region should be imprisoned.
Definition: map.h:297
Only for debugging purposes.
Definition: logger.h:13
const char * mapdir
Where the map files are.
Definition: global.h:247
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
struct Settings settings
Server settings.
Definition: init.c:40
sstring add_string(const char *str)
This will add &#39;str&#39; to the hash table.
Definition: shstr.c:124
const char * get_name_of_region_for_map(const mapstruct *m)
Gets the name of a region for a map.
Definition: region.c:92
int strcasecmp(const char *s1, const char *s2)
Case-insensitive comparaison of strings.
Definition: porting.c:256
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
char * msg
The description of the region.
Definition: map.h:293
EXTERN region * first_region
First region.
Definition: global.h:119
static void assign_region_parents(void)
Links child with their parent from the parent_name field.
Definition: region.c:508
This is a game-map.
Definition: map.h:325
static void parse_regions(FILE *fp)
Reads/parses the region file, and copies into a linked list of region structs.
Definition: region.c:340
struct regiondef * region
What jurisdiction in the game world this map is ruled by points to the struct containing all the prop...
Definition: map.h:329
char * longname
Official title of the region, this might be defined to be the same as name.
Definition: map.h:291
const char * get_region_msg(const region *r)
Gets a message for a region.
Definition: region.c:238