1478 lines
49 KiB
C
1478 lines
49 KiB
C
/*
|
|
* static char *rcsid_init_c =
|
|
* "$Id: init.c 11578 2009-02-23 22:02:27Z lalo $";
|
|
*/
|
|
|
|
/*
|
|
CrossFire, A Multiplayer game for X-windows
|
|
|
|
Copyright (C) 2002 Mark Wedel & Crossfire Development Team
|
|
Copyright (C) 1992 Frank Tore Johansen
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
The authors can be reached via e-mail at crossfire-devel@real-time.com
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Server initialisation, settings loading, command-line handling and such.
|
|
*/
|
|
|
|
#include <global.h>
|
|
#include <loader.h>
|
|
#include <version.h>
|
|
#ifndef __CEXTRACT__
|
|
#include <sproto.h>
|
|
#endif
|
|
|
|
/* Needed for strcasecmp(). */
|
|
#include <strings.h>
|
|
|
|
static void help(void);
|
|
static void usage(void);
|
|
static void init_beforeplay(void);
|
|
static void init_startup(void);
|
|
static void compile_info(void);
|
|
static void init_signals(void);
|
|
static void init_races(void);
|
|
static void dump_races(void);
|
|
static void add_to_racelist(const char *race_name, object *op);
|
|
static racelink *get_racelist(void);
|
|
static void fatal_signal(int make_core);
|
|
|
|
/** Filename when in daemon mode. */
|
|
static char default_daemon_log[] = "logfile";
|
|
|
|
static void set_logfile(char *val) {
|
|
settings.logfilename = val;
|
|
}
|
|
|
|
static void call_version(void) {
|
|
version(NULL);
|
|
exit(0);
|
|
}
|
|
|
|
static void showscores(void) {
|
|
display_high_score(NULL, 9999, NULL);
|
|
exit(0);
|
|
}
|
|
|
|
static void set_debug(void) {
|
|
settings.debug = llevDebug;
|
|
}
|
|
|
|
static void unset_debug(void) {
|
|
settings.debug = llevInfo;
|
|
}
|
|
|
|
static void set_mondebug(void) {
|
|
settings.debug = llevMonster;
|
|
}
|
|
|
|
static void set_dumpmon1(void) {
|
|
settings.dumpvalues = 1;
|
|
}
|
|
|
|
static void set_dumpmon2(void) {
|
|
settings.dumpvalues = 2;
|
|
}
|
|
|
|
static void set_dumpmon3(void) {
|
|
settings.dumpvalues = 3;
|
|
}
|
|
|
|
static void set_dumpmon4(void) {
|
|
settings.dumpvalues = 4;
|
|
}
|
|
|
|
static void set_dumpmon5(void) {
|
|
settings.dumpvalues = 5;
|
|
}
|
|
|
|
static void set_dumpmon6(void) {
|
|
settings.dumpvalues = 6;
|
|
}
|
|
|
|
static void set_dumpmon7(void) {
|
|
settings.dumpvalues = 7;
|
|
}
|
|
|
|
static void set_dumpmon8(void) {
|
|
settings.dumpvalues = 8;
|
|
}
|
|
|
|
static void set_dumpmon9(void) {
|
|
settings.dumpvalues = 9;
|
|
}
|
|
|
|
static void set_dumpmont(const char *name) {
|
|
settings.dumpvalues = 10;
|
|
settings.dumparg = name;
|
|
}
|
|
|
|
static void set_daemon(void) {
|
|
settings.daemonmode = 1;
|
|
if (settings.logfilename[0] == '\0') {
|
|
settings.logfilename = default_daemon_log;
|
|
}
|
|
}
|
|
|
|
static void set_datadir(const char *path) {
|
|
settings.datadir = path;
|
|
}
|
|
|
|
static void set_confdir(const char *path) {
|
|
settings.confdir = path;
|
|
}
|
|
|
|
static void set_localdir(const char *path) {
|
|
settings.localdir = path;
|
|
}
|
|
|
|
static void set_mapdir(const char *path) {
|
|
settings.mapdir = path;
|
|
}
|
|
|
|
static void set_archetypes(const char *path) {
|
|
settings.archetypes = path;
|
|
}
|
|
|
|
static void set_regions(const char *path) {
|
|
settings.regions = path;
|
|
}
|
|
|
|
static void set_treasures(const char *path) {
|
|
settings.treasures = path;
|
|
}
|
|
|
|
static void set_uniquedir(const char *path) {
|
|
settings.uniquedir = path;
|
|
}
|
|
|
|
static void set_templatedir(const char *path) {
|
|
settings.templatedir = path;
|
|
}
|
|
|
|
static void set_playerdir(const char *path) {
|
|
settings.playerdir = path;
|
|
}
|
|
|
|
static void set_tmpdir(const char *path) {
|
|
settings.tmpdir = path;
|
|
}
|
|
|
|
static void free_races(void);
|
|
|
|
static void free_materials(void);
|
|
|
|
static void showscoresparm(const char *data) {
|
|
display_high_score(NULL, 9999, data);
|
|
exit(0);
|
|
}
|
|
|
|
/**
|
|
* Change the server's port. Will exit() if invalid value.
|
|
*
|
|
* @param val
|
|
* port to use. Must be a valid one, between 1 and 32765 inclusive.
|
|
*/
|
|
static void set_csport(const char *val) {
|
|
settings.csport = atoi(val);
|
|
#ifndef WIN32 /* ***win32: set_csport: we remove csport error secure check here, do this later */
|
|
if (settings.csport <= 0
|
|
|| settings.csport > 32765
|
|
|| (settings.csport < 1024 && getuid() != 0)) {
|
|
LOG(llevError, "%d is an invalid csport number.\n", settings.csport);
|
|
exit(1);
|
|
}
|
|
#endif /* win32 */
|
|
}
|
|
|
|
/** Typedefs used when calling option handlers. */
|
|
/*@{*/
|
|
typedef void (*cmdlinefunc_args0)(void);
|
|
typedef void (*cmdlinefunc_args1)(const char* arg1);
|
|
typedef void (*cmdlinefunc_args2)(const char* arg1, const char* arg2);
|
|
/*@}*/
|
|
|
|
/**
|
|
* One command line option definition.
|
|
* Most of this is shamelessly stolen from XSysStats. But since that is
|
|
* also my program, no problem.
|
|
*/
|
|
struct Command_Line_Options {
|
|
const char *cmd_option; /**< How it is called on the command line. */
|
|
uint8 num_args; /**< Number or args it takes. */
|
|
uint8 pass; /**< What pass this should be processed on. @todo describe passes :) */
|
|
void (*func)(); /**< function to call when we match this.
|
|
* if num_args is true, than that gets passed
|
|
* to the function, otherwise nothing is passed
|
|
*/
|
|
};
|
|
|
|
/**
|
|
* Actual valid command line options.
|
|
* The way this system works is pretty simple - parse_args takes
|
|
* the options passed to the program and a pass number. If an option
|
|
* matches both in name and in pass (and we have enough options),
|
|
* we call the associated function. This makes writing a multi
|
|
* pass system very easy, and it is very easy to add in new options.
|
|
*/
|
|
static struct Command_Line_Options options[] = {
|
|
/** Pass 1 functions - Stuff that can/should be called before we actually
|
|
* initialize any data.
|
|
*/
|
|
{ "-h", 0, 1, help },
|
|
/* Honor -help also, since it is somewhat common */
|
|
{ "-help", 0, 1, help },
|
|
{ "-v", 0, 1, call_version },
|
|
{ "-d", 0, 1, set_debug },
|
|
{ "+d", 0, 1, unset_debug },
|
|
{ "-mon", 0, 1, set_mondebug },
|
|
{ "-data", 1, 1, set_datadir },
|
|
{ "-conf", 1, 1, set_confdir },
|
|
{ "-local", 1, 1, set_localdir },
|
|
{ "-maps", 1, 1, set_mapdir },
|
|
{ "-arch", 1, 1, set_archetypes },
|
|
{ "-regions", 1, 1, set_regions },
|
|
{ "-playerdir", 1, 1, set_playerdir },
|
|
{ "-treasures", 1, 1, set_treasures },
|
|
{ "-uniquedir", 1, 1, set_uniquedir },
|
|
{ "-templatedir", 1, 1, set_templatedir },
|
|
{ "-tmpdir", 1, 1, set_tmpdir },
|
|
{ "-log", 1, 1, set_logfile },
|
|
{ "-detach", 0, 1, set_daemon },
|
|
|
|
#ifdef WIN32
|
|
/* Windows service stuff */
|
|
{ "-regsrv", 0, 1, service_register },
|
|
{ "-unregsrv", 0, 1, service_unregister },
|
|
{ "-srv", 0, 1, service_handle },
|
|
#endif
|
|
|
|
/** Pass 2 functions. Most of these could probably be in pass 1,
|
|
* as they don't require much of anything to bet set up.
|
|
*/
|
|
{ "-csport", 1, 2, set_csport },
|
|
|
|
/** Start of pass 3 information. In theory, by pass 3, all data paths
|
|
* and defaults should have been set up.
|
|
*/
|
|
{ "-o", 0, 3, compile_info },
|
|
{ "-m", 0, 3, set_dumpmon1 },
|
|
{ "-m2", 0, 3, set_dumpmon2 },
|
|
{ "-m3", 0, 3, set_dumpmon3 },
|
|
{ "-m4", 0, 3, set_dumpmon4 },
|
|
{ "-m5", 0, 3, set_dumpmon5 },
|
|
{ "-m6", 0, 3, set_dumpmon6 },
|
|
{ "-m7", 0, 3, set_dumpmon7 },
|
|
{ "-m8", 0, 3, set_dumpmon8 },
|
|
{ "-m9", 0, 3, set_dumpmon9 },
|
|
{ "-mt", 1, 3, set_dumpmont },
|
|
{ "-mexp", 0, 3, dump_experience },
|
|
{ "-s", 0, 3, showscores },
|
|
{ "-score", 1, 3, showscoresparm }
|
|
};
|
|
|
|
/**
|
|
* Parse command line arguments.
|
|
*
|
|
* Note since this may be called before the library has been set up,
|
|
* we don't use any of crossfires built in logging functions.
|
|
*
|
|
* @param argc
|
|
* length of argv.
|
|
* @param argv
|
|
* arguments.
|
|
* @param pass
|
|
* initialization pass arguments to use.
|
|
* @todo describe pass.
|
|
*/
|
|
static void parse_args(int argc, char *argv[], int pass) {
|
|
size_t i;
|
|
int on_arg = 1;
|
|
|
|
while (on_arg < argc) {
|
|
for (i = 0; i < sizeof(options)/sizeof(struct Command_Line_Options); i++) {
|
|
if (!strcmp(options[i].cmd_option, argv[on_arg])) {
|
|
/* Found a matching option, but should not be processed on
|
|
* this pass. Just skip over it
|
|
*/
|
|
if (options[i].pass != pass) {
|
|
on_arg += options[i].num_args+1;
|
|
break;
|
|
}
|
|
if (options[i].num_args) {
|
|
if ((on_arg+options[i].num_args) >= argc) {
|
|
fprintf(stderr, "%s requires an argument.\n", options[i].cmd_option);
|
|
exit(1);
|
|
} else {
|
|
if (options[i].num_args == 1)
|
|
((cmdlinefunc_args1)options[i].func)(argv[on_arg+1]);
|
|
if (options[i].num_args == 2)
|
|
((cmdlinefunc_args2)options[i].func)(argv[on_arg+1], argv[on_arg+2]);
|
|
on_arg += options[i].num_args+1;
|
|
}
|
|
} else { /* takes no args */
|
|
((cmdlinefunc_args0)options[i].func)();
|
|
on_arg++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (i == sizeof(options)/sizeof(struct Command_Line_Options)) {
|
|
fprintf(stderr, "Unknown option: %s\n", argv[on_arg]);
|
|
usage();
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Material types. */
|
|
materialtype_t *materialt;
|
|
|
|
/**
|
|
* Creates an empty materialtype_t structure.
|
|
*
|
|
* @return
|
|
* new blanked structure.
|
|
* @note
|
|
* will fatal() instead of returning NULL.
|
|
*/
|
|
static materialtype_t *get_empty_mat(void) {
|
|
materialtype_t *mt;
|
|
int i;
|
|
|
|
mt = (materialtype_t *)malloc(sizeof(materialtype_t));
|
|
if (mt == NULL)
|
|
fatal(OUT_OF_MEMORY);
|
|
mt->name = NULL;
|
|
mt->description = NULL;
|
|
for (i = 0; i < NROFATTACKS; i++) {
|
|
mt->save[i] = 0;
|
|
mt->mod[i] = 0;
|
|
}
|
|
mt->chance = 0;
|
|
mt->difficulty = 0;
|
|
mt->magic = 0;
|
|
mt->damage = 0;
|
|
mt->wc = 0;
|
|
mt->ac = 0;
|
|
mt->sp = 0;
|
|
mt->weight = 100;
|
|
mt->value = 100;
|
|
mt->next = NULL;
|
|
return mt;
|
|
}
|
|
|
|
/**
|
|
* Loads the materials.
|
|
* @todo describe materials and such.
|
|
*/
|
|
static void load_materials(void) {
|
|
char buf[MAX_BUF], filename[MAX_BUF], *cp, *next;
|
|
FILE *fp;
|
|
materialtype_t *mt;
|
|
int i, value;
|
|
|
|
snprintf(filename, sizeof(filename), "%s/materials", settings.datadir);
|
|
LOG(llevDebug, "Reading material type data from %s...\n", filename);
|
|
if ((fp = fopen(filename, "r")) == NULL) {
|
|
LOG(llevError, "Cannot open %s for reading\n", filename);
|
|
mt = get_empty_mat();
|
|
mt->next = NULL;
|
|
materialt = mt;
|
|
return;
|
|
}
|
|
mt = get_empty_mat();
|
|
materialt = mt;
|
|
while (fgets(buf, MAX_BUF, fp) != NULL) {
|
|
if (*buf == '#')
|
|
continue;
|
|
if ((cp = strchr(buf, '\n')) != NULL)
|
|
*cp = '\0';
|
|
cp = buf;
|
|
while (*cp == ' ') /* Skip blanks */
|
|
cp++;
|
|
if (!strncmp(cp, "name", 4)) {
|
|
/* clean up the previous entry */
|
|
if (mt->next != NULL) {
|
|
if (mt->description == NULL)
|
|
mt->description = add_string(mt->name);
|
|
mt = mt->next;
|
|
}
|
|
mt->next = get_empty_mat();
|
|
mt->name = add_string(strchr(cp, ' ')+1);
|
|
} else if (!strncmp(cp, "description", 11)) {
|
|
mt->description = add_string(strchr(cp, ' ')+1);
|
|
} else if (sscanf(cp, "material %d", &value)) {
|
|
mt->material = value;
|
|
} else if (!strncmp(cp, "saves", 5)) {
|
|
cp = strchr(cp, ' ')+1;
|
|
for (i = 0; i < NROFATTACKS; i++) {
|
|
if (cp == NULL) {
|
|
mt->save[i] = 0;
|
|
continue;
|
|
}
|
|
if ((next = strchr(cp, ',')) != NULL)
|
|
*(next++) = '\0';
|
|
sscanf(cp, "%d", &value);
|
|
mt->save[i] = (sint8)value;
|
|
cp = next;
|
|
}
|
|
} else if (!strncmp(cp, "mods", 4)) {
|
|
cp = strchr(cp, ' ')+1;
|
|
for (i = 0; i < NROFATTACKS; i++) {
|
|
if (cp == NULL) {
|
|
mt->save[i] = 0;
|
|
continue;
|
|
}
|
|
if ((next = strchr(cp, ',')) != NULL)
|
|
*(next++) = '\0';
|
|
sscanf(cp, "%d", &value);
|
|
mt->mod[i] = (sint8)value;
|
|
cp = next;
|
|
}
|
|
} else if (sscanf(cp, "chance %d\n", &value)) {
|
|
mt->chance = (sint8)value;
|
|
} else if (sscanf(cp, "diff %d\n", &value)) {
|
|
mt->difficulty = (sint8)value;
|
|
} else if (sscanf(cp, "magic %d\n", &value)) {
|
|
mt->magic = (sint8)value;
|
|
} else if (sscanf(cp, "damage %d\n", &value)) {
|
|
mt->damage = (sint8)value;
|
|
} else if (sscanf(cp, "wc %d\n", &value)) {
|
|
mt->wc = (sint8)value;
|
|
} else if (sscanf(cp, "ac %d\n", &value)) {
|
|
mt->ac = (sint8)value;
|
|
} else if (sscanf(cp, "sp %d\n", &value)) {
|
|
mt->sp = (sint8)value;
|
|
} else if (sscanf(cp, "weight %d\n", &value)) {
|
|
mt->weight = value;
|
|
} else if (sscanf(cp, "value %d\n", &value)) {
|
|
mt->value = value;
|
|
}
|
|
}
|
|
if (mt->next) {
|
|
free(mt->next);
|
|
mt->next = NULL;
|
|
}
|
|
LOG(llevDebug, "Done.\n");
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
/**
|
|
* Frees all memory allocated to materials.
|
|
*/
|
|
static void free_materials(void) {
|
|
materialtype_t *next;
|
|
|
|
while (materialt) {
|
|
next = materialt->next;
|
|
free(materialt);
|
|
materialt = next;
|
|
}
|
|
materialt = NULL;
|
|
}
|
|
|
|
/**
|
|
* This loads the settings file. There could be debate whether this should
|
|
* be here or in the common directory - but since only the server needs this
|
|
* information, having it here probably makes more sense.
|
|
*/
|
|
static void load_settings(void) {
|
|
char buf[MAX_BUF], *cp;
|
|
int has_val, comp;
|
|
FILE *fp;
|
|
|
|
snprintf(buf, sizeof(buf), "%s/settings", settings.confdir);
|
|
|
|
/* We don't require a settings file at current time, but down the road,
|
|
* there will probably be so many values that not having a settings file
|
|
* will not be a good thing.
|
|
*/
|
|
if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
|
|
LOG(llevError, "Warning: No settings file found\n");
|
|
return;
|
|
}
|
|
while (fgets(buf, MAX_BUF-1, fp) != NULL) {
|
|
if (buf[0] == '#')
|
|
continue;
|
|
/* eliminate newline */
|
|
if ((cp = strrchr(buf, '\n')) != NULL)
|
|
*cp = '\0';
|
|
|
|
/* Skip over empty lines */
|
|
if (buf[0] == 0)
|
|
continue;
|
|
|
|
/* Skip all the spaces and set them to nulls. If not space,
|
|
* set cp to "" to make strcpy's and the like easier down below.
|
|
*/
|
|
if ((cp = strchr(buf, ' ')) != NULL) {
|
|
while (*cp == ' ')
|
|
*cp++ = 0;
|
|
has_val = 1;
|
|
} else {
|
|
cp = "";
|
|
has_val = 0;
|
|
}
|
|
|
|
if (!strcasecmp(buf, "metaserver_notification")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.meta_on = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.meta_on = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for metaserver_notification: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "metaserver_server")) {
|
|
if (has_val)
|
|
strcpy(settings.meta_server, cp);
|
|
else
|
|
LOG(llevError, "load_settings: metaserver_server must have a value.\n");
|
|
} else if (!strcasecmp(buf, "motd")) {
|
|
if (has_val)
|
|
strcpy(settings.motd, cp);
|
|
else
|
|
LOG(llevError, "load_settings: motd must have a value.\n");
|
|
} else if (!strcasecmp(buf, "dm_mail")) {
|
|
if (has_val)
|
|
strcpy(settings.dm_mail, cp);
|
|
else
|
|
LOG(llevError, "load_settings: dm_mail must have a value.\n");
|
|
} else if (!strcasecmp(buf, "metaserver_host")) {
|
|
if (has_val)
|
|
strcpy(settings.meta_host, cp);
|
|
else
|
|
LOG(llevError, "load_settings: metaserver_host must have a value.\n");
|
|
} else if (!strcasecmp(buf, "port")) {
|
|
set_csport(cp);
|
|
} else if (!strcasecmp(buf, "metaserver_port")) {
|
|
int port = atoi(cp);
|
|
|
|
if (port < 1 || port > 65535)
|
|
LOG(llevError, "load_settings: metaserver_port must be between 1 and 65535, %d is invalid\n", port);
|
|
else
|
|
settings.meta_port = port;
|
|
} else if (!strcasecmp(buf, "metaserver_comment")) {
|
|
strcpy(settings.meta_comment, cp);
|
|
} else if (!strcasecmp(buf, "worldmapstartx")) {
|
|
int size = atoi(cp);
|
|
|
|
if (size < 0)
|
|
LOG(llevError, "load_settings: worldmapstartx must be at least 0, %d is invalid\n", size);
|
|
else
|
|
settings.worldmapstartx = size;
|
|
} else if (!strcasecmp(buf, "worldmapstarty")) {
|
|
int size = atoi(cp);
|
|
|
|
if (size < 0)
|
|
LOG(llevError, "load_settings: worldmapstarty must be at least 0, %d is invalid\n", size);
|
|
else
|
|
settings.worldmapstarty = size;
|
|
} else if (!strcasecmp(buf, "worldmaptilesx")) {
|
|
int size = atoi(cp);
|
|
|
|
if (size < 1)
|
|
LOG(llevError, "load_settings: worldmaptilesx must be greater than 1, %d is invalid\n", size);
|
|
else
|
|
settings.worldmaptilesx = size;
|
|
} else if (!strcasecmp(buf, "worldmaptilesy")) {
|
|
int size = atoi(cp);
|
|
|
|
if (size < 1)
|
|
LOG(llevError, "load_settings: worldmaptilesy must be greater than 1, %d is invalid\n", size);
|
|
else
|
|
settings.worldmaptilesy = size;
|
|
} else if (!strcasecmp(buf, "worldmaptilesizex")) {
|
|
int size = atoi(cp);
|
|
|
|
if (size < 1)
|
|
LOG(llevError, "load_settings: worldmaptilesizex must be greater than 1, %d is invalid\n", size);
|
|
else
|
|
settings.worldmaptilesizex = size;
|
|
} else if (!strcasecmp(buf, "worldmaptilesizey")) {
|
|
int size = atoi(cp);
|
|
|
|
if (size < 1)
|
|
LOG(llevError, "load_settings: worldmaptilesizey must be greater than 1, %d is invalid\n", size);
|
|
else
|
|
settings.worldmaptilesizey = size;
|
|
} else if (!strcasecmp(buf, "fastclock")) {
|
|
int lev = atoi(cp);
|
|
|
|
if (lev < 0)
|
|
LOG(llevError, "load_settings: fastclock must be at least 0, %d is invalid\n", lev);
|
|
else
|
|
settings.fastclock = lev;
|
|
} else if (!strcasecmp(buf, "not_permadeth")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.not_permadeth = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.not_permadeth = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for not_permadeth: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "resurrection")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.resurrection = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.resurrection = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for resurrection: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "set_title")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.set_title = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.set_title = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for set_title: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "search_items")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.search_items = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.search_items = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for search_items: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "spell_encumbrance")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.spell_encumbrance = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.spell_encumbrance = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for spell_encumbrance: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "spell_failure_effects")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.spell_failure_effects = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.spell_failure_effects = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for spell_failure_effects: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "casting_time")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.casting_time = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.casting_time = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for casting_time: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "real_wiz")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.real_wiz = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.real_wiz = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for real_wiz: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "recycle_tmp_maps")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.recycle_tmp_maps = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.recycle_tmp_maps = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for recycle_tmp_maps: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "explore_mode")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.explore_mode = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.explore_mode = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for explore_mode: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "who_format")) {
|
|
if (has_val)
|
|
strcpy(settings.who_format, cp);
|
|
} else if (!strcasecmp(buf, "who_wiz_format")) {
|
|
if (has_val)
|
|
strcpy(settings.who_wiz_format, cp);
|
|
} else if (!strcasecmp(buf, "spellpoint_level_depend")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.spellpoint_level_depend = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.spellpoint_level_depend = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for spellpoint_level_depend: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "stat_loss_on_death")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.stat_loss_on_death = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.stat_loss_on_death = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for stat_loss_on_death: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "use_permanent_experience")) {
|
|
LOG(llevError, "use_permanent_experience is deprecated, usepermenent_experience_percentage instead\n");
|
|
} else if (!strcasecmp(buf, "permanent_experience_percentage")) {
|
|
int val = atoi(cp);
|
|
if (val < 0 || val > 100)
|
|
LOG(llevError, "load_settings: permenent_experience_percentage must be between 0 and 100, %d is invalid\n", val);
|
|
else
|
|
settings.permanent_exp_ratio = val;
|
|
} else if (!strcasecmp(buf, "death_penalty_percentage")) {
|
|
int val = atoi(cp);
|
|
if (val < 0 || val > 100)
|
|
LOG(llevError, "load_settings: death_penalty_percentage must be between 0 and 100, %d is invalid\n", val);
|
|
else
|
|
settings.death_penalty_ratio = val;
|
|
} else if (!strcasecmp(buf, "death_penalty_levels")) {
|
|
int val = atoi(cp);
|
|
if (val < 0 || val > 255)
|
|
LOG(llevError, "load_settings: death_penalty_levels can not be negative, %d is invalid\n", val);
|
|
else
|
|
settings.death_penalty_level = val;
|
|
} else if (!strcasecmp(buf, "balanced_stat_loss")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.balanced_stat_loss = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.balanced_stat_loss = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for balanced_stat_loss: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "simple_exp")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.simple_exp = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.simple_exp = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: Unknown value for simple_exp: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "item_power_factor")) {
|
|
float tmp = atof(cp);
|
|
if (tmp < 0)
|
|
LOG(llevError, "load_settings: item_power_factor must be a positive number (%f < 0)\n", tmp);
|
|
else
|
|
settings.item_power_factor = tmp;
|
|
} else if (!strcasecmp(buf, "pk_luck_penalty")) {
|
|
sint16 val = atoi(cp);
|
|
|
|
if (val < -100 || val > 100)
|
|
LOG(llevError, "load_settings: pk_luck_penalty must be between -100 and 100, %d is invalid\n", val);
|
|
else
|
|
settings.pk_luck_penalty = val;
|
|
} else if (!strcasecmp(buf, "set_friendly_fire")) {
|
|
int val = atoi(cp);
|
|
|
|
if (val < 1 || val > 100)
|
|
LOG(llevError, "load_settings: set_friendly_fire must be between 1 an 100, %d is invalid\n", val);
|
|
else
|
|
settings.set_friendly_fire = val;
|
|
} else if (!strcasecmp(buf, "armor_max_enchant")) {
|
|
int max_e = atoi(cp);
|
|
if (max_e <= 0)
|
|
LOG(llevError, "load_settings: armor_max_enchant is %d\n", max_e);
|
|
else
|
|
settings.armor_max_enchant = max_e;
|
|
} else if (!strcasecmp(buf, "armor_weight_reduction")) {
|
|
int wr = atoi(cp);
|
|
if (wr < 0)
|
|
LOG(llevError, "load_settings: armor_weight_reduction is %d\n", wr);
|
|
else
|
|
settings.armor_weight_reduction = wr;
|
|
} else if (!strcasecmp(buf, "armor_weight_linear")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.armor_weight_linear = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.armor_weight_linear = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for armor_weight_linear: %s\n", cp);
|
|
}
|
|
|
|
} else if (!strcasecmp(buf, "armor_speed_improvement")) {
|
|
int wr = atoi(cp);
|
|
if (wr < 0)
|
|
LOG(llevError, "load_settings: armor_speed_improvement is %d\n", wr);
|
|
else
|
|
settings.armor_speed_improvement = wr;
|
|
} else if (!strcasecmp(buf, "armor_speed_linear")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.armor_speed_linear = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.armor_speed_linear = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for armor_speed_linear: %s\n", cp);
|
|
}
|
|
|
|
} else if (!strcasecmp(buf, "no_player_stealing")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.no_player_stealing = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.no_player_stealing = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for no_player_stealing: %s\n", cp);
|
|
}
|
|
|
|
} else if (!strcasecmp(buf, "create_home_portals")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.create_home_portals = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.create_home_portals = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for create_home_portals: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "personalized_blessings")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.personalized_blessings = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.personalized_blessings = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for personalized_blessings: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "pk_max_experience")) {
|
|
sint64 pkme = atoll(cp);
|
|
if (pkme < 0)
|
|
pkme = -1;
|
|
settings.pk_max_experience = pkme;
|
|
} else if (!strcasecmp(buf, "pk_max_experience_percent")) {
|
|
int pkmep = atoi(cp);
|
|
if (pkmep < 0) {
|
|
LOG(llevError, "load_settings: pk_max_experience_percent should be positive or zero\n", cp);
|
|
} else
|
|
settings.pk_max_experience_percent = pkmep;
|
|
} else if (!strcasecmp(buf, "allow_denied_spells_writing")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.allow_denied_spells_writing = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.allow_denied_spells_writing = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for allow_denied_spells_writing: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "allow_broken_converters")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.allow_broken_converters = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.allow_broken_converters = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for allow_broken_converters: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "log_timestamp")) {
|
|
if (!strcasecmp(cp, "on") || !strcasecmp(cp, "true")) {
|
|
settings.log_timestamp = TRUE;
|
|
} else if (!strcasecmp(cp, "off") || !strcasecmp(cp, "false")) {
|
|
settings.log_timestamp = FALSE;
|
|
} else {
|
|
LOG(llevError, "load_settings: unknown value for log_timestamp: %s\n", cp);
|
|
}
|
|
} else if (!strcasecmp(buf, "log_timestamp_format")) {
|
|
free(settings.log_timestamp_format);
|
|
settings.log_timestamp_format = strdup_local(cp);
|
|
} else {
|
|
LOG(llevError, "Unknown value in settings file: %s\n", buf);
|
|
}
|
|
}
|
|
close_and_delete(fp, comp);
|
|
if (settings.log_timestamp_format == NULL)
|
|
settings.log_timestamp_format = strdup_local("%y/%m/%d %H:%M:%S");
|
|
|
|
/*
|
|
* The who formats are defined in config to be blank. They should have been
|
|
* overridden by the settings file, if there are no entries however, it will
|
|
* have stayed blank. Since this probably isn't what is wanted, we will check if
|
|
* new formats have been specified, and if not we will use the old defaults.
|
|
*/
|
|
if (!strcmp(settings.who_format, ""))
|
|
strcpy(settings.who_format, "%N_%T%t%h%d%b%n<%m>");
|
|
if (!strcmp(settings.who_wiz_format, ""))
|
|
strcpy(settings.who_wiz_format, "%N_%T%t%h%d%b%nLevel %l <%m>(@%i)(%c)");
|
|
}
|
|
|
|
/**
|
|
* This is the main server initialisation function.
|
|
*
|
|
* Called only once, when starting the program.
|
|
*/
|
|
void init(int argc, char **argv) {
|
|
|
|
init_done = 0; /* Must be done before init_signal() */
|
|
logfile = stderr;
|
|
parse_args(argc, argv, 1); /* First arg pass - right now it does
|
|
* nothing, but in future specifying the
|
|
* LibDir in this pass would be reasonable*/
|
|
|
|
init_library(); /* Must be called early */
|
|
load_settings(); /* Load the settings file */
|
|
load_materials();
|
|
parse_args(argc, argv, 2);
|
|
fprintf(logfile, "Welcome to CrossFire, v%s\n", FULL_VERSION);
|
|
fprintf(logfile, "Copyright (C) 1994 Mark Wedel.\n");
|
|
fprintf(logfile, "Copyright (C) 1992 Frank Tore Johansen.\n");
|
|
|
|
if (strcmp(settings.dm_mail, "") != 0) {
|
|
fprintf(logfile, "Maintained locally by: %s\n", settings.dm_mail);
|
|
fprintf(logfile, "Questions and bugs should be mailed to above address.\n");
|
|
}
|
|
SRANDOM(time(NULL));
|
|
|
|
init_startup(); /* Write (C), check shutdown/forbid files */
|
|
init_signals(); /* Sets up signal interceptions */
|
|
init_commands(); /* Sort command tables */
|
|
read_map_log(); /* Load up the old temp map files */
|
|
init_skills();
|
|
init_ob_methods();
|
|
cftimer_init();
|
|
|
|
parse_args(argc, argv, 3);
|
|
|
|
#ifndef WIN32 /* ***win32: no become_daemon in windows */
|
|
if (settings.daemonmode)
|
|
become_daemon();
|
|
#endif
|
|
|
|
init_beforeplay();
|
|
init_server();
|
|
metaserver_init();
|
|
metaserver2_init();
|
|
reset_sleep();
|
|
init_done = 1;
|
|
}
|
|
|
|
/**
|
|
* Frees all memory allocated around here:
|
|
* - materials
|
|
* - races
|
|
*/
|
|
void free_server(void) {
|
|
free_materials();
|
|
free_races();
|
|
}
|
|
|
|
static void usage(void) {
|
|
(void)fprintf(logfile, "Usage: crossfire [-h] [-<flags>]...\n");
|
|
}
|
|
|
|
static void help(void) {
|
|
/* The information in usage is redundant with what is given below, so why call it? */
|
|
/* usage();*/
|
|
printf("Flags:\n");
|
|
printf(" -csport <port> Specifies the port to use for the new client/server code.\n");
|
|
printf(" -d Turns on some debugging.\n");
|
|
printf(" +d Turns off debugging (useful if server compiled with debugging\n");
|
|
printf(" as default).\n");
|
|
printf(" -detach The server will go in the background, closing all\n");
|
|
printf(" connections to the tty.\n");
|
|
printf(" -h Display this information.\n");
|
|
printf(" -log <file> Specifies which file to send output to.\n");
|
|
printf(" Only has meaning if -detach is specified.\n");
|
|
printf(" -mon Turns on monster debugging.\n");
|
|
printf(" -o Prints out info on what was defined at compile time.\n");
|
|
printf(" -s Display the high-score list.\n");
|
|
printf(" -score <name or class> Displays all high scores with matching name/class.\n");
|
|
printf(" -v Print version and contributors.\n");
|
|
printf(" -conf Sets the configuration dir (settings, motd, etc.)\n");
|
|
printf(" -data Sets the lib dir (archetypes, treasures, etc.)\n");
|
|
printf(" -local Read/write local data (hiscore, unique items, etc.)\n");
|
|
printf(" -maps Sets the directory for maps.\n");
|
|
printf(" -arch Sets the archetype file to use.\n");
|
|
printf(" -regions Sets the regions file to use.\n");
|
|
printf(" -playerdir Sets the directory for the player files.\n");
|
|
printf(" -templatedir Sets the directory for template generate maps.\n");
|
|
printf(" -treasures Sets the treasures file to use.\n");
|
|
printf(" -uniquedir Sets the unique items/maps directory.\n");
|
|
printf(" -tmpdir Sets the directory for temporary files (mostly maps.)\n");
|
|
printf(" -m Lists out suggested experience for all monsters.\n");
|
|
printf(" -m2 Dumps out abilities.\n");
|
|
printf(" -m3 Dumps out artifact information.\n");
|
|
printf(" -m4 Dumps out spell information.\n");
|
|
printf(" -m5 Dumps out skill information.\n");
|
|
printf(" -m6 Dumps out race information.\n");
|
|
printf(" -m7 Dumps out alchemy information.\n");
|
|
printf(" -m8 Dumps out gods information.\n");
|
|
printf(" -m9 Dumps out more alchemy information (formula checking).\n");
|
|
printf(" -mt <name> Dumps out list of treasures for a monster.\n");
|
|
exit(0);
|
|
}
|
|
|
|
static void init_beforeplay(void) {
|
|
init_archetypes(); /* If not called before, reads all archetypes from file */
|
|
init_artifacts(); /* If not called before, reads all artifacts from file */
|
|
check_spells(); /* If not called before, links archtypes used by spells */
|
|
init_regions(); /* If not called before, reads all regions from file */
|
|
init_archetype_pointers(); /* Setup global pointers to archetypes */
|
|
init_races(); /* overwrite race designations using entries in lib/races file */
|
|
init_gods(); /* init linked list of gods from archs*/
|
|
init_readable(); /* inits useful arrays for readable texts */
|
|
init_formulae(); /* If not called before, reads formulae from file */
|
|
|
|
switch (settings.dumpvalues) {
|
|
case 1:
|
|
print_monsters();
|
|
cleanup();
|
|
|
|
case 2:
|
|
dump_abilities();
|
|
cleanup();
|
|
|
|
case 3:
|
|
dump_artifacts();
|
|
cleanup();
|
|
|
|
case 4:
|
|
dump_spells();
|
|
cleanup();
|
|
|
|
case 5:
|
|
cleanup();
|
|
|
|
case 6:
|
|
dump_races();
|
|
cleanup();
|
|
|
|
case 7:
|
|
dump_alchemy();
|
|
cleanup();
|
|
|
|
case 8:
|
|
dump_gods();
|
|
cleanup();
|
|
|
|
case 9:
|
|
dump_alchemy_costs();
|
|
cleanup();
|
|
|
|
case 10:
|
|
dump_monster_treasure(settings.dumparg);
|
|
cleanup();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if starting the server is allowed.
|
|
*
|
|
* @todo describe forbid_play() and such restrictions.
|
|
*/
|
|
static void init_startup(void) {
|
|
char buf[MAX_BUF];
|
|
FILE *fp;
|
|
int comp;
|
|
|
|
#ifdef SHUTDOWN_FILE
|
|
snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, SHUTDOWN_FILE);
|
|
if ((fp = open_and_uncompress(buf, 0, &comp)) != NULL) {
|
|
while (fgets(buf, MAX_BUF-1, fp) != NULL)
|
|
printf("%s", buf);
|
|
close_and_delete(fp, comp);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
if (forbid_play()) { /* Maybe showing highscore should be allowed? */
|
|
LOG(llevError, "CrossFire: Playing not allowed.\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dump compilation information, activated with the -o flag.
|
|
*
|
|
* It writes out information on how Imakefile and config.h was configured
|
|
* at compile time.
|
|
*/
|
|
static void compile_info(void) {
|
|
int i = 0;
|
|
char err[MAX_BUF];
|
|
|
|
printf("Non-standard include files:\n");
|
|
#if !defined(__STRICT_ANSI__) || defined(__sun__)
|
|
#if !defined(Mips)
|
|
printf("<stdlib.h>\n");
|
|
i = 1;
|
|
#endif
|
|
#if !defined(MACH) && !defined(sony)
|
|
printf("<malloc.h>\n");
|
|
i = 1;
|
|
#endif
|
|
#endif
|
|
#ifndef __STRICT_ANSI__
|
|
#ifndef MACH
|
|
printf("<memory.h\n");
|
|
i = 1;
|
|
#endif
|
|
#endif
|
|
#ifndef sgi
|
|
printf("<sys/timeb.h>\n");
|
|
i = 1;
|
|
#endif
|
|
if (!i)
|
|
printf("(none)\n");
|
|
printf("Datadir:\t\t%s\n", settings.datadir);
|
|
printf("Localdir:\t\t%s\n", settings.localdir);
|
|
#ifdef PERM_FILE
|
|
printf("Perm file:\t<ETC>/%s\n", PERM_FILE);
|
|
#endif
|
|
#ifdef SHUTDOWN_FILE
|
|
printf("Shutdown file:\t<ETC>/%s\n", SHUTDOWN_FILE);
|
|
#endif
|
|
printf("Save player:\t<true>\n");
|
|
printf("Save mode:\t%4.4o\n", SAVE_MODE);
|
|
printf("Playerdir:\t<VAR>/%s\n", settings.playerdir);
|
|
printf("Itemsdir:\t<VAR>/%s\n", settings.uniquedir);
|
|
printf("Tmpdir:\t\t%s\n", settings.tmpdir);
|
|
printf("Map max timeout:\t%d\n", MAP_MAXTIMEOUT);
|
|
printf("Max objects:\t%d\n", MAX_OBJECTS);
|
|
#ifdef USE_CALLOC
|
|
printf("Use_calloc:\t<true>\n");
|
|
#else
|
|
printf("Use_calloc:\t<false>\n");
|
|
#endif
|
|
|
|
printf("Max_time:\t%d\n", MAX_TIME);
|
|
|
|
#ifdef WIN32 /* ***win32 compile_info(): remove execl... */
|
|
printf("Logfilename:\t%s\n", settings.logfilename);
|
|
exit(0);
|
|
#else
|
|
execl("/bin/uname", "uname", "-a", NULL);
|
|
LOG(llevError, "Oops, shouldn't have gotten here: execl(/bin/uname) failed: %s\n", strerror_local(errno, err, sizeof(err)));
|
|
exit(-1);
|
|
#endif
|
|
}
|
|
|
|
/* Signal handlers: */
|
|
|
|
/**
|
|
* SIGSERV handler.
|
|
* @param i
|
|
* unused.
|
|
*/
|
|
static void rec_sigsegv(int i) {
|
|
LOG(llevError, "\nSIGSEGV received.\n");
|
|
fatal_signal(1);
|
|
}
|
|
|
|
/**
|
|
* SIGINT handler.
|
|
* @param i
|
|
* unused.
|
|
*/
|
|
static void rec_sigint(int i) {
|
|
LOG(llevInfo, "\nSIGINT received.\n");
|
|
fatal_signal(0);
|
|
}
|
|
|
|
/**
|
|
* SIGHUP handler.
|
|
* SIGHUP handlers on daemons typically make them reread the config
|
|
* files and reinitialize itself. This behaviour is better left for
|
|
* an explicit shutdown and restart with Crossfire, as there is just
|
|
* too much persistent runtime state. However, another function of
|
|
* SIGHUP handlers is to reopen the log file for logrotate's benefit.
|
|
* We can do that here.
|
|
*
|
|
* @param i
|
|
* unused.
|
|
*/
|
|
static void rec_sighup(int i) {
|
|
/* Don't call LOG(). It calls non-reentrant functions. The other
|
|
* signal handlers shouldn't really call LOG() either. */
|
|
if (logfile != stderr) {
|
|
reopen_logfile = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SIGQUIT handler.
|
|
*
|
|
* @param i
|
|
* unused.
|
|
*/
|
|
static void rec_sigquit(int i) {
|
|
LOG(llevInfo, "\nSIGQUIT received\n");
|
|
fatal_signal(1);
|
|
}
|
|
|
|
/**
|
|
* SIGPIPE handler.
|
|
*
|
|
* Keep running if we receive a sigpipe. Crossfire should really be able
|
|
* to handle this signal (at least at some point in the future if not
|
|
* right now). By causing a dump right when it is received, it is not
|
|
* doing much good. However, if it core dumps later on, at least it can
|
|
* be looked at later on, and maybe fix the problem that caused it to
|
|
* dump core. There is no reason that SIGPIPES should be fatal
|
|
*
|
|
* @param i
|
|
* unused.
|
|
*/
|
|
static void rec_sigpipe(int i) {
|
|
LOG(llevError, "\nSIGPIPE--------------\n------------\n--------\n---\n");
|
|
#if 1 && !defined(WIN32) /* ***win32: we don't want send SIGPIPE */
|
|
LOG(llevInfo, "\nReceived SIGPIPE, ignoring...\n");
|
|
signal(SIGPIPE, rec_sigpipe);/* hocky-pux clears signal handlers */
|
|
#else
|
|
LOG(llevError, "\nSIGPIPE received, not ignoring...\n");
|
|
fatal_signal(1); /*Might consider to uncomment this line */
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* SIGBUS handler.
|
|
*
|
|
* @param i
|
|
* unused.
|
|
*/
|
|
static void rec_sigbus(int i) {
|
|
#ifdef SIGBUS
|
|
LOG(llevError, "\nSIGBUS received\n");
|
|
fatal_signal(1);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* SIGTERM handler.
|
|
*
|
|
* @param i
|
|
* unused.
|
|
*/
|
|
static void rec_sigterm(int i) {
|
|
LOG(llevInfo, "\nSIGTERM received\n");
|
|
fatal_signal(0);
|
|
}
|
|
|
|
/**
|
|
* General signal handling. Will exit() in any case.
|
|
*
|
|
* @param make_core
|
|
* if set abort() instead of exit() to generate a core dump.
|
|
*/
|
|
static void fatal_signal(int make_core) {
|
|
if (init_done) {
|
|
emergency_save(0);
|
|
clean_tmp_files();
|
|
}
|
|
if (make_core)
|
|
abort();
|
|
exit(0);
|
|
}
|
|
|
|
/**
|
|
* Setup our signal handlers.
|
|
*/
|
|
static void init_signals(void) {
|
|
#ifndef WIN32 /* init_signals() remove signals */
|
|
struct sigaction sa;
|
|
|
|
sa.sa_sigaction = NULL;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
sa.sa_handler = rec_sighup;
|
|
sigaction(SIGHUP, &sa, NULL);
|
|
signal(SIGINT, rec_sigint);
|
|
#ifndef DEBUG
|
|
signal(SIGQUIT, rec_sigquit);
|
|
signal(SIGSEGV, rec_sigsegv);
|
|
LOG(llevInfo, "\n---------registering SIGPIPE\n");
|
|
signal(SIGPIPE, rec_sigpipe);
|
|
#ifdef SIGBUS
|
|
signal(SIGBUS, rec_sigbus);
|
|
#endif
|
|
signal(SIGTERM, rec_sigterm);
|
|
#endif
|
|
#endif /* win32 */
|
|
}
|
|
|
|
/**
|
|
* Reads the races file in the lib/ directory, then
|
|
* overwrites old 'race' entries. This routine allow us to quickly
|
|
* re-configure the 'alignment' of monsters, objects. Useful for
|
|
* putting together lists of creatures, etc that belong to gods.
|
|
*/
|
|
static void init_races(void) {
|
|
FILE *file;
|
|
char race[MAX_BUF], fname[MAX_BUF], buf[MAX_BUF], *cp, variable[MAX_BUF];
|
|
archetype *mon = NULL;
|
|
static int init_done = 0;
|
|
|
|
if (init_done)
|
|
return;
|
|
init_done = 1;
|
|
first_race = NULL;
|
|
|
|
snprintf(fname, sizeof(fname), "%s/races", settings.datadir);
|
|
LOG(llevDebug, "Reading races from %s...\n", fname);
|
|
if (!(file = fopen(fname, "r"))) {
|
|
LOG(llevError, "Cannot open races file %s: %s\n", fname, strerror_local(errno, buf, sizeof(buf)));
|
|
return;
|
|
}
|
|
|
|
while (fgets(buf, MAX_BUF, file) != NULL) {
|
|
int set_race = 1, set_list = 1;
|
|
if (*buf == '#')
|
|
continue;
|
|
if ((cp = strchr(buf, '\n')) != NULL)
|
|
*cp = '\0';
|
|
cp = buf;
|
|
while (*cp == ' ' || *cp == '!' || *cp == '@') {
|
|
if (*cp == '!')
|
|
set_race = 0;
|
|
if (*cp == '@')
|
|
set_list = 0;
|
|
cp++;
|
|
}
|
|
if (sscanf(cp, "RACE %s", variable)) { /* set new race value */
|
|
strcpy(race, variable);
|
|
} else {
|
|
char *cp1;
|
|
|
|
/* Take out beginning spaces */
|
|
for (cp1 = cp; *cp1 == ' '; cp1++)
|
|
;
|
|
/* Remove newline and trailing spaces */
|
|
for (cp1 = cp+strlen(cp)-1; *cp1 == '\n' || *cp1 == ' '; cp1--) {
|
|
*cp1 = '\0';
|
|
if (cp == cp1)
|
|
break;
|
|
}
|
|
|
|
if (cp[strlen(cp)-1] == '\n')
|
|
cp[strlen(cp)-1] = '\0';
|
|
/* set creature race to race value */
|
|
if ((mon = find_archetype(cp)) == NULL)
|
|
LOG(llevError, "Creature %s in race file lacks archetype\n", cp);
|
|
else {
|
|
if (set_race && (!mon->clone.race || strcmp(mon->clone.race, race))) {
|
|
if (mon->clone.race) {
|
|
LOG(llevDebug, " Resetting race to %s from %s for archetype %s\n", race, mon->clone.race, mon->name);
|
|
free_string(mon->clone.race);
|
|
}
|
|
mon->clone.race = add_string(race);
|
|
}
|
|
/* if the arch is a monster, add it to the race list */
|
|
if (set_list && QUERY_FLAG(&mon->clone, FLAG_MONSTER))
|
|
add_to_racelist(race, &mon->clone);
|
|
}
|
|
}
|
|
}
|
|
fclose(file);
|
|
LOG(llevDebug, "done races.\n");
|
|
}
|
|
|
|
/**
|
|
* Dumps all race information to stderr.
|
|
*/
|
|
static void dump_races(void) {
|
|
racelink *list;
|
|
objectlink *tmp;
|
|
|
|
for (list = first_race; list; list = list->next) {
|
|
fprintf(stderr, "\nRACE %s:\t", list->name);
|
|
for (tmp = list->member; tmp; tmp = tmp->next)
|
|
fprintf(stderr, "%s(%d), ", tmp->ob->arch->name, tmp->ob->level);
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
/**
|
|
* Frees all race-related information.
|
|
*/
|
|
static void free_races(void) {
|
|
racelink *race;
|
|
objectlink *link;
|
|
|
|
LOG(llevDebug, "Freeing race information.\n");
|
|
while (first_race) {
|
|
race = first_race->next;
|
|
while (first_race->member) {
|
|
link = first_race->member->next;
|
|
free(first_race->member);
|
|
first_race->member = link;
|
|
}
|
|
free_string(first_race->name);
|
|
free(first_race);
|
|
first_race = race;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an object to the racelist.
|
|
*
|
|
* @param race_name
|
|
* race name.
|
|
* @param op
|
|
* what object to add to the race.
|
|
*/
|
|
static void add_to_racelist(const char *race_name, object *op) {
|
|
racelink *race;
|
|
|
|
if (!op || !race_name)
|
|
return;
|
|
race = find_racelink(race_name);
|
|
|
|
if (!race) { /* add in a new race list */
|
|
race = get_racelist();
|
|
race->next = first_race;
|
|
first_race = race;
|
|
race->name = add_string(race_name);
|
|
}
|
|
|
|
if (race->member->ob) {
|
|
objectlink *tmp = get_objectlink();
|
|
|
|
tmp->next = race->member;
|
|
race->member = tmp;
|
|
}
|
|
race->nrof++;
|
|
race->member->ob = op;
|
|
}
|
|
|
|
/**
|
|
* Create a new ::racelink structure.
|
|
*
|
|
* @note
|
|
* will call fatal() in case of memory allocation failure.
|
|
* @return
|
|
* empty structure.
|
|
*/
|
|
static racelink *get_racelist(void) {
|
|
racelink *list;
|
|
|
|
list = (racelink *)malloc(sizeof(racelink));
|
|
if (!list)
|
|
fatal(OUT_OF_MEMORY);
|
|
list->name = NULL;
|
|
list->nrof = 0;
|
|
list->member = get_objectlink();
|
|
list->next = NULL;
|
|
|
|
return list;
|
|
}
|
|
|
|
/**
|
|
* Find the race information for the specified name.
|
|
*
|
|
* @param name
|
|
* race to search for.
|
|
* @return
|
|
* race structure, NULL if not found.
|
|
*/
|
|
racelink *find_racelink(const char *name) {
|
|
racelink *test = NULL;
|
|
|
|
if (name && first_race)
|
|
for (test = first_race; test && test != test->next; test = test->next)
|
|
if (!test->name || !strcmp(name, test->name))
|
|
break;
|
|
|
|
return test;
|
|
}
|