// game.cpp
//
// $Id: game.cpp,v 1.74 2005/10/16 20:48:14 sdennis Exp $
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include <sys/stat.h>
#include <signal.h>
#include "attrs.h"
#include "comsys.h"
#include "file_c.h"
#include "mguests.h"
#include "muxcli.h"
#include "pcre.h"
#include "powers.h"
#include "help.h"
#ifdef REALITY_LVLS
#include "levels.h"
#endif /* REALITY_LVLS */
extern void init_attrtab(void);
extern void init_cmdtab(void);
extern void cf_init(void);
extern void pcache_init(void);
extern int cf_read(void);
extern void ValidateConfigurationDbrefs(void);
extern void init_functab(void);
extern void close_sockets(bool emergency, char *message);
extern void build_version(void);
extern void init_version(void);
extern void init_logout_cmdtab(void);
extern void raw_notify(dbref, const char *);
extern void do_dbck(dbref executor, dbref caller, dbref enactor, int);
extern void boot_slave(dbref executor, dbref caller, dbref enactor, int key);
void fork_and_dump(int);
void pcache_sync(void);
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
static void init_rlimit(void);
#endif // HAVE_SETRLIMIT RLIMIT_NOFILE
#ifdef WIN32
extern CRITICAL_SECTION csDescriptorList; // for thread synchronisation
#endif // WIN32
void do_dump(dbref executor, dbref caller, dbref enactor, int key)
{
#ifndef WIN32
if (mudstate.dumping)
{
notify(executor, "Dumping in progress. Try again later.");
return;
}
#endif
notify(executor, "Dumping...");
fork_and_dump(key);
}
// print out stuff into error file
//
void report(void)
{
STARTLOG(LOG_BUGS, "BUG", "INFO");
log_text("Command: '");
log_text(mudstate.debug_cmd);
log_text("'");
ENDLOG;
if (Good_obj(mudstate.curr_executor))
{
STARTLOG(LOG_BUGS, "BUG", "INFO");
log_text("Player: ");
log_name_and_loc(mudstate.curr_executor);
if ( mudstate.curr_enactor != mudstate.curr_executor
&& Good_obj(mudstate.curr_enactor))
{
log_text(" Enactor: ");
log_name_and_loc(mudstate.curr_enactor);
}
ENDLOG;
}
}
/* ----------------------------------------------------------------------
* regexp_match: Load a regular expression match and insert it into
* registers.
*/
bool regexp_match
(
char *pattern,
char *str,
int case_opt,
char *args[],
int nargs
)
{
int matches;
int i;
const char *errptr;
int erroffset;
/*
* Load the regexp pattern. This allocates memory which must be
* later freed. A free() of the regexp does free all structures
* under it.
*/
pcre *re;
if ( MuxAlarm.bAlarmed
|| (re = pcre_compile(pattern, case_opt, &errptr, &erroffset, NULL)) == NULL)
{
/*
* This is a matching error. We have an error message in
* regexp_errbuf that we can ignore, since we're doing
* command-matching.
*/
return false;
}
// To capture N substrings, you need space for 3(N+1) offsets in the
// offset vector. We'll allow 2N-1 substrings and possibly ignore some.
//
const int ovecsize = 6 * nargs;
int *ovec = new int[ovecsize];
/*
* Now we try to match the pattern. The relevant fields will
* automatically be filled in by this.
*/
matches = pcre_exec(re, NULL, str, strlen(str), 0, 0, ovec, ovecsize);
if (matches < 0)
{
delete ovec;
MEMFREE(re);
return false;
}
if (matches == 0)
{
// There were too many substring matches. See docs for
// pcre_copy_substring().
//
matches = ovecsize / 3;
}
/*
* Now we fill in our args vector. Note that in regexp matching,
* 0 is the entire string matched, and the parenthesized strings
* go from 1 to 9. We DO PRESERVE THIS PARADIGM, for consistency
* with other languages.
*/
for (i = 0; i < nargs; ++i)
{
args[i] = alloc_lbuf("regexp_match");
if (pcre_copy_substring(str, ovec, matches, i,
args[i], LBUF_SIZE) < 0)
{
free_lbuf(args[i]);
args[i] = NULL;
}
}
delete ovec;
MEMFREE(re);
return true;
}
/* ----------------------------------------------------------------------
* atr_match: Check attribute list for wild card matches and queue them.
*/
static int atr_match1
(
dbref thing,
dbref parent,
dbref player,
char type,
char *str,
char *raw_str,
int check_exclude,
int hash_insert
)
{
// See if we can do it. Silently fail if we can't.
//
if (!could_doit(player, parent, A_LUSE))
{
return -1;
}
int match = 0;
if ( AMATCH_CMD == type
&& mudstate.bfNoCommands.IsSet(parent))
{
return match;
}
else if ( AMATCH_LISTEN == type
&& mudstate.bfNoListens.IsSet(parent))
{
return match;
}
bool bFoundCommands = false;
bool bFoundListens = false;
int atr;
char *as;
atr_push();
for (atr = atr_head(parent, &as); atr; atr = atr_next(&as))
{
ATTR *ap = atr_num(atr);
// Never check NOPROG attributes.
//
if ( !ap
|| (ap->flags & AF_NOPROG))
{
continue;
}
// We need to grab the attribute even before we know whether we'll use
// it or not in order to maintain cached knowledge about ^-Commands
// and $-Commands.
//
dbref aowner;
int aflags;
char buff[LBUF_SIZE];
atr_get_str(buff, parent, atr, &aowner, &aflags);
if (!(aflags & AF_NOPROG))
{
switch (buff[0])
{
case AMATCH_CMD:
bFoundCommands = true;
break;
case AMATCH_LISTEN:
bFoundListens = true;
break;
}
}
// If we aren't the bottom level check if we saw this attr
// before. Also exclude it if the attribute type is PRIVATE.
//
if ( check_exclude
&& ( (ap->flags & AF_PRIVATE)
|| (aflags & AF_PRIVATE)
|| hashfindLEN(&(ap->number), sizeof(ap->number), &mudstate.parent_htab)))
{
continue;
}
// If we aren't the top level remember this attr so we
// exclude it from now on.
//
if (hash_insert)
{
hashaddLEN(&(ap->number), sizeof(ap->number), &atr, &mudstate.parent_htab);
}
// Check for the leadin character after excluding the attrib.
// This lets non-command attribs on the child block commands
// on the parent.
//
if ( buff[0] != type
|| (aflags & AF_NOPROG))
{
continue;
}
// Decode it: search for first unescaped :
//
char *s = strchr(buff+1, ':');
if (!s)
{
continue;
}
*s++ = '\0';
char *args[NUM_ENV_VARS];
if ( ( 0 != (aflags & AF_REGEXP)
&& regexp_match(buff + 1, (aflags & AF_NOPARSE) ? raw_str : str,
((aflags & AF_CASE) ? 0 : PCRE_CASELESS), args, NUM_ENV_VARS))
|| ( 0 == (aflags & AF_REGEXP)
&& wild(buff + 1, (aflags & AF_NOPARSE) ? raw_str : str,
args, NUM_ENV_VARS)))
{
match = 1;
CLinearTimeAbsolute lta;
wait_que(thing, player, player, false, lta, NOTHING, 0, s,
args, NUM_ENV_VARS, mudstate.global_regs);
for (int i = 0; i < NUM_ENV_VARS; i++)
{
if (args[i])
{
free_lbuf(args[i]);
}
}
}
}
atr_pop();
if (bFoundCommands)
{
mudstate.bfNoCommands.Clear(parent);
mudstate.bfCommands.Set(parent);
}
else
{
mudstate.bfCommands.Clear(parent);
mudstate.bfNoCommands.Set(parent);
}
if (bFoundListens)
{
mudstate.bfNoListens.Clear(parent);
mudstate.bfListens.Set(parent);
}
else
{
mudstate.bfListens.Clear(parent);
mudstate.bfNoListens.Set(parent);
}
return match;
}
bool atr_match
(
dbref thing,
dbref player,
char type,
char *str,
char *raw_str,
bool check_parents
)
{
int lev, result;
bool exclude, insert;
dbref parent;
// If thing is halted or we are matching $-commands on a NO_COMMAND
// object, don't check anything
//
if ( Halted(thing)
|| ( AMATCH_CMD == type
&& No_Command(thing)))
{
return false;
}
// If we're matching ^-commands, strip ANSI
//
if (AMATCH_LISTEN == type)
{
// Remember, strip_ansi returns a pointer to a static buffer
// within itself.
//
size_t junk;
str = strip_ansi(str, &junk);
}
// If not checking parents, just check the thing
//
bool match = false;
if (!check_parents)
{
return (atr_match1(thing, thing, player, type, str, raw_str, false, false) > 0);
}
// Check parents, ignoring halted objects
//
exclude = false;
insert = true;
hashflush(&mudstate.parent_htab);
ITER_PARENTS(thing, parent, lev)
{
if (!Good_obj(Parent(parent)))
{
insert = false;
}
result = atr_match1(thing, parent, player, type, str, raw_str,
exclude, insert);
if (result > 0)
{
match = true;
}
else if (result < 0)
{
return match;
}
exclude = true;
}
return match;
}
/* ---------------------------------------------------------------------------
* notify_check: notifies the object #target of the message msg, and
* optionally notify the contents, neighbors, and location also.
*/
bool check_filter(dbref object, dbref player, int filter, const char *msg)
{
int aflags;
dbref aowner;
char *buf, *nbuf, *cp, *dp, *str;
buf = atr_pget(object, filter, &aowner, &aflags);
if (!*buf)
{
free_lbuf(buf);
return true;
}
char **preserve = NULL;
int *preserve_len = NULL;
preserve = PushPointers(MAX_GLOBAL_REGS);
preserve_len = PushIntegers(MAX_GLOBAL_REGS);
save_global_regs("check_filter_save", preserve, preserve_len);
nbuf = dp = alloc_lbuf("check_filter");
str = buf;
mux_exec(nbuf, &dp, object, player, player,
EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **)NULL, 0);
*dp = '\0';
dp = nbuf;
free_lbuf(buf);
restore_global_regs("check_filter_restore", preserve, preserve_len);
PopIntegers(preserve_len, MAX_GLOBAL_REGS);
PopPointers(preserve, MAX_GLOBAL_REGS);
if (!(aflags & AF_REGEXP))
{
do
{
cp = parse_to(&dp, ',', EV_STRIP_CURLY);
mudstate.wild_invk_ctr = 0;
if ( MuxAlarm.bAlarmed
|| quick_wild(cp, msg))
{
free_lbuf(nbuf);
return false;
}
} while (dp != NULL);
}
else
{
int case_opt = (aflags & AF_CASE) ? 0 : PCRE_CASELESS;
do
{
int erroffset;
const char *errptr;
cp = parse_to(&dp, ',', EV_STRIP_CURLY);
pcre *re;
if ( !MuxAlarm.bAlarmed
&& (re = pcre_compile(cp, case_opt, &errptr, &erroffset, NULL)) != NULL)
{
const int ovecsize = 33;
int ovec[ovecsize];
int matches = pcre_exec(re, NULL, msg, strlen(msg), 0, 0,
ovec, ovecsize);
if (0 <= matches)
{
MEMFREE(re);
free_lbuf(nbuf);
return false;
}
MEMFREE(re);
}
} while (dp != NULL);
}
free_lbuf(nbuf);
return true;
}
static char *add_prefix(dbref object, dbref player, int prefix,
const char *msg, const char *dflt)
{
int aflags;
dbref aowner;
char *buf, *nbuf, *cp, *str;
buf = atr_pget(object, prefix, &aowner, &aflags);
if (!*buf)
{
cp = buf;
safe_str(dflt, buf, &cp);
}
else
{
char **preserve = NULL;
int *preserve_len = NULL;
preserve = PushPointers(MAX_GLOBAL_REGS);
preserve_len = PushIntegers(MAX_GLOBAL_REGS);
save_global_regs("add_prefix_save", preserve, preserve_len);
nbuf = cp = alloc_lbuf("add_prefix");
str = buf;
mux_exec(nbuf, &cp, object, player, player,
EV_FIGNORE | EV_EVAL | EV_TOP, &str, (char **)NULL, 0);
free_lbuf(buf);
restore_global_regs("add_prefix_restore", preserve, preserve_len);
PopIntegers(preserve_len, MAX_GLOBAL_REGS);
PopPointers(preserve, MAX_GLOBAL_REGS);
buf = nbuf;
}
if (cp != buf)
{
safe_chr(' ', buf, &cp);
}
safe_str(msg, buf, &cp);
*cp = '\0';
return buf;
}
static char *dflt_from_msg(dbref sender, dbref sendloc)
{
char *tp, *tbuff;
tp = tbuff = alloc_lbuf("notify_check.fwdlist");
safe_str("From ", tbuff, &tp);
if (Good_obj(sendloc))
{
safe_str(Name(sendloc), tbuff, &tp);
}
else
{
safe_str(Name(sender), tbuff, &tp);
}
safe_chr(',', tbuff, &tp);
*tp = '\0';
return tbuff;
}
/* Do HTML escaping, converting < to <, etc. 'dest' needs to be
* allocated & freed by the caller.
*
* If you're using this to append to a string, you can pass in the
* safe_{str|chr} (char **) so we can just do the append directly,
* saving you an alloc_lbuf()...free_lbuf(). If you want us to append
* from the start of 'dest', just pass in a 0 for 'destp'.
*
* Returns 0 if the copy succeeded, 1 if it failed.
*/
bool html_escape(const char *src, char *dest, char **destp)
{
const char *msg_orig;
bool ret = false;
if (destp == 0)
{
char *temp = dest;
destp = &temp;
}
for (msg_orig = src; msg_orig && *msg_orig && !ret; msg_orig++)
{
char *p = *destp;
switch (*msg_orig)
{
case '<':
safe_str("<", dest, destp);
break;
case '>':
safe_str(">", dest, destp);
break;
case '&':
safe_str("&", dest, destp);
break;
case '\"':
safe_str(""", dest, destp);
break;
default:
safe_chr(*msg_orig, dest, destp);
break;
}
// For <>&\, this may cause an extra loop around before it figures out that we are
// out of buffer, but no harm is done in this, and the common case is a single character.
//
if (p == *destp)
{
ret = true;
}
}
**destp = 0;
return ret;
}
void notify_check(dbref target, dbref sender, const char *msg, int key)
{
// If speaker is invalid or message is empty, just exit.
//
if ( !Good_obj(target)
|| !msg
|| !*msg)
{
return;
}
#ifdef WOD_REALMS
if ((key & MSG_OOC) == 0)
{
if ((key & MSG_SAYPOSE) != 0)
{
if (REALM_DO_HIDDEN_FROM_YOU == DoThingToThingVisibility(target, sender, ACTION_IS_TALKING))
{
return;
}
}
else
{
if (REALM_DO_HIDDEN_FROM_YOU == DoThingToThingVisibility(target, sender, ACTION_IS_MOVING))
{
return;
}
}
}
#endif // WOD_REALMS
// Enforce a recursion limit
//
mudstate.ntfy_nest_lev++;
if (mudconf.ntfy_nest_lim <= mudstate.ntfy_nest_lev)
{
mudstate.ntfy_nest_lev--;
return;
}
char *msg_ns, *mp, *tbuff, *tp, *buff;
char *args[NUM_ENV_VARS];
dbref aowner, recip, obj;
int i, nargs, aflags;
FWDLIST *fp;
// If we want NOSPOOF output, generate it. It is only needed if we are
// sending the message to the target object.
//
if (key & MSG_ME)
{
mp = msg_ns = alloc_lbuf("notify_check");
if ( Nospoof(target)
&& target != sender
&& target != mudstate.curr_enactor
&& target != mudstate.curr_executor)
{
// I'd really like to use tprintf here but I can't because the
// caller may have. notify(target, tprintf(...)) is quite common
// in the code.
//
tbuff = alloc_sbuf("notify_check.nospoof");
safe_chr('[', msg_ns, &mp);
safe_str(Name(sender), msg_ns, &mp);
sprintf(tbuff, "(#%d)", sender);
safe_str(tbuff, msg_ns, &mp);
if (sender != Owner(sender))
{
safe_chr('{', msg_ns, &mp);
safe_str(Name(Owner(sender)), msg_ns, &mp);
safe_chr('}', msg_ns, &mp);
}
if (sender != mudstate.curr_enactor)
{
sprintf(tbuff, "<-(#%d)", mudstate.curr_enactor);
safe_str(tbuff, msg_ns, &mp);
}
safe_str("] ", msg_ns, &mp);
free_sbuf(tbuff);
}
safe_str(msg, msg_ns, &mp);
*mp = '\0';
}
else
{
msg_ns = NULL;
}
// msg contains the raw message, msg_ns contains the NOSPOOFed msg.
//
bool check_listens = !Halted(target);
switch (Typeof(target))
{
case TYPE_PLAYER:
if (key & MSG_ME)
{
if (key & MSG_HTML)
{
raw_notify_html(target, msg_ns);
}
else
{
if (Html(target))
{
char *msg_ns_escaped;
msg_ns_escaped = alloc_lbuf("notify_check_escape");
html_escape(msg_ns, msg_ns_escaped, 0);
raw_notify(target, msg_ns_escaped);
free_lbuf(msg_ns_escaped);
}
else
{
raw_notify(target, msg_ns);
}
}
}
if (!mudconf.player_listen)
{
check_listens = false;
}
// FALLTHROUGH
case TYPE_THING:
case TYPE_ROOM:
// If we're in a pipe, objects can receive raw_notify if
// they're not a player. (players were already notified
// above.
//
if ( mudstate.inpipe
&& !isPlayer(target))
{
raw_notify(target, msg_ns);
}
// Forward puppet message if it is for me.
//
bool has_neighbors = Has_location(target);
dbref targetloc = where_is(target);
bool is_audible = Audible(target);
if ( (key & MSG_ME)
&& Puppet(target)
&& (target != Owner(target))
&& ( (key & MSG_PUP_ALWAYS)
|| ( targetloc != Location(Owner(target))
&& targetloc != Owner(target))))
{
tp = tbuff = alloc_lbuf("notify_check.puppet");
safe_str(Name(target), tbuff, &tp);
safe_str("> ", tbuff, &tp);
safe_str(msg_ns, tbuff, &tp);
*tp = '\0';
raw_notify(Owner(target), tbuff);
free_lbuf(tbuff);
}
// Check for @Listen match if it will be useful.
//
bool pass_listen = false;
nargs = 0;
if ( check_listens
&& (key & (MSG_ME | MSG_INV_L))
&& H_Listen(target))
{
tp = atr_get(target, A_LISTEN, &aowner, &aflags);
if (*tp && wild(tp, (char *)msg, args, NUM_ENV_VARS))
{
for (nargs = NUM_ENV_VARS; nargs && (!args[nargs - 1] || !(*args[nargs - 1])); nargs--)
{
; // Nothing
}
pass_listen = true;
}
free_lbuf(tp);
}
// If we matched the @listen or are monitoring, check the
// USE lock.
//
bool pass_uselock = false;
if ( (key & MSG_ME)
&& check_listens
&& ( pass_listen
|| Monitor(target)))
{
pass_uselock = could_doit(sender, target, A_LUSE);
}
// Process AxHEAR if we pass LISTEN, USElock and it's for me.
//
if ( (key & MSG_ME)
&& pass_listen
&& pass_uselock
&& mudstate.nHearNest <= 2)
{
mudstate.nHearNest++;
if (sender != target)
{
did_it(sender, target, 0, NULL, 0, NULL, A_AHEAR, args, nargs);
}
else
{
did_it(sender, target, 0, NULL, 0, NULL, A_AMHEAR, args,
nargs);
}
did_it(sender, target, 0, NULL, 0, NULL, A_AAHEAR, args, nargs);
mudstate.nHearNest--;
}
// Get rid of match arguments. We don't need them anymore.
//
if (pass_listen)
{
for (i = 0; i < nargs; i++)
{
if (args[i] != NULL)
{
free_lbuf(args[i]);
}
}
}
// Process ^-listens if for me, MONITOR, and we pass USElock.
//
if ( (key & MSG_ME)
&& pass_uselock
&& sender != target
&& Monitor(target))
{
atr_match(target, sender, AMATCH_LISTEN, (char *)msg, (char *)msg,
false);
}
// Deliver message to forwardlist members.
//
if ( (key & MSG_FWDLIST)
&& is_audible
&& check_filter(target, sender, A_FILTER, msg))
{
tbuff = dflt_from_msg(sender, target);
buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
free_lbuf(tbuff);
fp = fwdlist_get(target);
if (fp)
{
for (i = 0; i < fp->count; i++)
{
recip = fp->data[i];
if ( !Good_obj(recip)
|| recip == target)
{
continue;
}
notify_check(recip, sender, buff,
MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE);
}
}
free_lbuf(buff);
}
// Deliver message through audible exits.
//
if (key & MSG_INV_EXITS)
{
DOLIST(obj, Exits(target))
{
recip = Location(obj);
if ( Audible(obj)
&& ( recip != target
&& check_filter(obj, sender, A_FILTER, msg)))
{
buff = add_prefix(obj, target, A_PREFIX, msg,
"From a distance,");
notify_check(recip, sender, buff,
MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE);
free_lbuf(buff);
}
}
}
// Deliver message through neighboring audible exits.
//
if ( has_neighbors
&& ( (key & MSG_NBR_EXITS)
|| ( (key & MSG_NBR_EXITS_A)
&& is_audible)))
{
// If from inside, we have to add the prefix string of
// the container.
//
if (key & MSG_S_INSIDE)
{
tbuff = dflt_from_msg(sender, target);
buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
free_lbuf(tbuff);
}
else
{
buff = (char *)msg;
}
DOLIST(obj, Exits(Location(target)))
{
recip = Location(obj);
if ( Good_obj(recip)
&& Audible(obj)
&& recip != targetloc
&& recip != target
&& check_filter(obj, sender, A_FILTER, msg))
{
tbuff = add_prefix(obj, target, A_PREFIX, buff,
"From a distance,");
notify_check(recip, sender, tbuff,
MSG_ME | MSG_F_UP | MSG_F_CONTENTS | MSG_S_INSIDE);
free_lbuf(tbuff);
}
}
if (key & MSG_S_INSIDE)
{
free_lbuf(buff);
}
}
// Deliver message to contents.
//
if ( ( (key & MSG_INV)
|| ( (key & MSG_INV_L)
&& pass_listen))
&& check_filter(target, sender, A_INFILTER, msg))
{
// Don't prefix the message if we were given the MSG_NOPREFIX key.
//
if (key & MSG_S_OUTSIDE)
{
buff = add_prefix(target, sender, A_INPREFIX, msg, "");
}
else
{
buff = (char *)msg;
}
DOLIST(obj, Contents(target))
{
if (obj != target)
{
notify_check(obj, sender, buff,
MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | key & MSG_HTML);
}
}
if (key & MSG_S_OUTSIDE)
{
free_lbuf(buff);
}
}
// Deliver message to neighbors.
//
if ( has_neighbors
&& ( (key & MSG_NBR)
|| ( (key & MSG_NBR_A)
&& is_audible
&& check_filter(target, sender, A_FILTER, msg))))
{
if (key & MSG_S_INSIDE)
{
tbuff = dflt_from_msg(sender, target);
buff = add_prefix(target, sender, A_PREFIX, msg, "");
free_lbuf(tbuff);
}
else
{
buff = (char *)msg;
}
DOLIST(obj, Contents(targetloc))
{
if ( obj != target
&& obj != targetloc)
{
notify_check(obj, sender, buff,
MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE);
}
}
if (key & MSG_S_INSIDE)
{
free_lbuf(buff);
}
}
// Deliver message to container.
//
if ( has_neighbors
&& ( (key & MSG_LOC)
|| ( (key & MSG_LOC_A)
&& is_audible
&& check_filter(target, sender, A_FILTER, msg))))
{
if (key & MSG_S_INSIDE)
{
tbuff = dflt_from_msg(sender, target);
buff = add_prefix(target, sender, A_PREFIX, msg, tbuff);
free_lbuf(tbuff);
}
else
{
buff = (char *)msg;
}
notify_check(targetloc, sender, buff,
MSG_ME | MSG_F_UP | MSG_S_INSIDE);
if (key & MSG_S_INSIDE)
{
free_lbuf(buff);
}
}
}
if (msg_ns)
{
free_lbuf(msg_ns);
}
mudstate.ntfy_nest_lev--;
}
void notify_except(dbref loc, dbref player, dbref exception, const char *msg, int key)
{
dbref first;
if (loc != exception)
{
notify_check(loc, player, msg, (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A | key));
}
DOLIST(first, Contents(loc))
{
if (first != exception)
{
notify_check(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE | key));
}
}
}
void notify_except2(dbref loc, dbref player, dbref exc1, dbref exc2, const char *msg)
{
dbref first;
if ( loc != exc1
&& loc != exc2)
{
notify_check(loc, player, msg, (MSG_ME_ALL | MSG_F_UP | MSG_S_INSIDE | MSG_NBR_EXITS_A));
}
DOLIST(first, Contents(loc))
{
if ( first != exc1
&& first != exc2)
{
notify_check(first, player, msg, (MSG_ME | MSG_F_DOWN | MSG_S_OUTSIDE));
}
}
}
/* ----------------------------------------------------------------------
* Reporting of CPU information.
*/
static void report_timecheck
(
dbref player,
bool yes_screen,
bool yes_log,
bool yes_clear
)
{
int thing, obj_counted;
CLinearTimeDelta ltdPeriod, ltdTotal;
CLinearTimeAbsolute ltaNow;
ltaNow.GetUTC();
ltdPeriod = ltaNow - mudstate.cpu_count_from;
if ( yes_log
&& (LOG_TIMEUSE & mudconf.log_options))
{
start_log("OBJ", "CPU");
log_name(player);
log_text(" checks object time use over ");
log_number(ltdPeriod.ReturnSeconds());
log_text(" seconds" ENDLINE);
}
else
{
yes_log = false;
STARTLOG(LOG_ALWAYS, "WIZ", "TIMECHECK");
log_name(player);
log_text(" checks object time use over ");
log_number(ltdPeriod.ReturnSeconds());
log_text(" seconds");
ENDLOG;
}
obj_counted = 0;
ltdTotal.Set100ns(0);
// Step through the db. Care only about the ones that are nonzero.
//
DO_WHOLE_DB(thing)
{
CLinearTimeDelta <d = db[thing].cpu_time_used;
if (ltd.Return100ns())
{
ltdTotal += ltd;
long used_msecs = ltd.ReturnMilliseconds();
obj_counted++;
if (yes_log)
{
Log.tinyprintf("#%d\t%ld" ENDLINE, thing, used_msecs);
}
if (yes_screen)
{
raw_notify(player, tprintf("#%d\t%ld", thing, used_msecs));
}
if (yes_clear)
{
ltd.Set100ns(0);
}
}
}
if (yes_screen)
{
raw_notify(player,
tprintf("Counted %d objects using %ld msecs over %d seconds.",
obj_counted, ltdTotal.ReturnMilliseconds(), ltdPeriod.ReturnSeconds()));
}
if (yes_log)
{
Log.tinyprintf("Counted %d objects using %ld msecs over %d seconds.",
obj_counted, ltdTotal.ReturnMilliseconds(), ltdPeriod.ReturnSeconds());
end_log();
}
if (yes_clear)
{
mudstate.cpu_count_from = ltaNow;
}
}
void do_timecheck(dbref executor, dbref caller, dbref enactor, int key)
{
bool yes_screen, yes_log, yes_clear;
yes_screen = yes_log = yes_clear = false;
if (key == 0)
{
// No switches, default to printing to screen and clearing counters.
//
yes_screen = true;
yes_clear = true;
}
else
{
if (key & TIMECHK_RESET)
{
yes_clear = true;
}
if (key & TIMECHK_SCREEN)
{
yes_screen = true;
}
if (key & TIMECHK_LOG)
{
yes_log = true;
}
}
report_timecheck(executor, yes_screen, yes_log, yes_clear);
}
void do_shutdown(dbref executor, dbref caller, dbref enactor, int key, char *message)
{
if (!Can_SiteAdmin(executor))
{
notify(executor, NOPERM_MESSAGE);
return;
}
raw_broadcast(0, "GAME: Shutdown by %s", Name(Owner(executor)));
STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN");
log_text("Shutdown by ");
log_name(executor);
ENDLOG;
STARTLOG(LOG_ALWAYS, "WIZ", "SHTDN");
log_text("Shutdown status: ");
log_text(message);
ENDLOG;
int fd = open(mudconf.status_file, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0600);
if (fd != -1)
{
write(fd, message, strlen(message));
write(fd, ENDLINE, sizeof(ENDLINE)-1);
DebugTotalFiles++;
if (close(fd) == 0)
{
DebugTotalFiles--;
}
}
// Do we perform a normal or an emergency shutdown? Normal
// shutdown is handled by exiting the main loop in shovechars,
// emergency shutdown is done here.
//
if (key & SHUTDN_PANIC)
{
// Close down the network interface.
//
emergency_shutdown();
// Close the attribute text db and dump the header db.
//
#ifndef MEMORY_BASED
// Save cached modified attribute list
//
al_store();
#endif // MEMORY_BASED
pcache_sync();
SYNC;
CLOSE;
STARTLOG(LOG_ALWAYS, "DMP", "PANIC");
log_text("Panic dump: ");
log_text(mudconf.crashdb);
ENDLOG;
dump_database_internal(DUMP_I_PANIC);
STARTLOG(LOG_ALWAYS, "DMP", "DONE");
log_text("Panic dump complete: ");
log_text(mudconf.crashdb);
ENDLOG;
}
// Set up for normal shutdown.
//
mudstate.shutdown_flag = true;
}
// There are several types of dumps:
//
// Type 0 - Normal mudstate.dumping controlled
// Type 1 - Panic uncontrolled but only one of these happening at a time.
// Type 2 - Restart mudstate.dumping controlled.
// Type 3 - FLAT mudstate.dumping controlled.
// Type 4 - signal uncontrolled and if we fault twice, the game ends --
// see check_panicking.
//
// When changing this function and to keep forking dumps safe, keep in mind
// that the following combinations can be occuring at the same time. Don't
// touch each other's files.
//
// Type 0 and 2 are allowed to touch each other's files. Type 1 and 4 should not
// touch files used in Type 0 or Type 2.
//
typedef struct
{
char **ppszOutputBase;
char szOutputSuffix[14];
bool bUseTemporary;
int fType;
char *pszErrorMessage;
} DUMP_PROCEDURE;
DUMP_PROCEDURE DumpProcedures[NUM_DUMP_TYPES] =
{
{ 0, "" , false, 0, "" }, // 0 -- Handled specially.
{ &mudconf.crashdb, "" , false, UNLOAD_VERSION | UNLOAD_FLAGS, "Opening crash file" }, // 1
{ &mudconf.indb, "" , true, OUTPUT_VERSION | OUTPUT_FLAGS, "Opening input file" }, // 2
{ &mudconf.indb, ".FLAT" , false, UNLOAD_VERSION | UNLOAD_FLAGS, "Opening flatfile" }, // 3
{ &mudconf.indb, ".SIG" , false, UNLOAD_VERSION | UNLOAD_FLAGS, "Opening signalled flatfile"} // 4
};
#ifdef WIN32
#define POPEN_READ_OP "rb"
#define POPEN_WRITE_OP "wb"
#else // WIN32
#define POPEN_READ_OP "r"
#define POPEN_WRITE_OP "w"
#endif // WIN32
void dump_database_internal(int dump_type)
{
char tmpfile[SIZEOF_PATHNAME+32];
char outfn[SIZEOF_PATHNAME+32];
char prevfile[SIZEOF_PATHNAME+32];
FILE *f;
if ( dump_type < 0
|| NUM_DUMP_TYPES <= dump_type)
{
return;
}
bool bPotentialConflicts = false;
#ifndef WIN32
// If we are already dumping for some reason, and suddenly get a type 1 or
// type 4 dump, basically don't touch mail and comsys files. The other
// dump will take care of them as well as can be expected for now, and if
// we try to, we'll just step on them.
//
if ( mudstate.dumping
&& ( dump_type == DUMP_I_PANIC
|| dump_type == DUMP_I_SIGNAL))
{
bPotentialConflicts = true;
}
#endif
// Call the local dump function only if another dump is not already
// in progress.
//
local_dump_database(dump_type);
if (0 < dump_type)
{
DUMP_PROCEDURE *dp = &DumpProcedures[dump_type];
sprintf(outfn, "%s%s", *(dp->ppszOutputBase), dp->szOutputSuffix);
if (dp->bUseTemporary)
{
sprintf(tmpfile, "%s.#%d#", outfn, mudstate.epoch);
RemoveFile(tmpfile);
f = fopen(tmpfile, "wb");
}
else
{
RemoveFile(outfn);
f = fopen(outfn, "wb");
}
if (f)
{
DebugTotalFiles++;
setvbuf(f, NULL, _IOFBF, 16384);
db_write(f, F_MUX, dp->fType);
if (fclose(f) == 0)
{
DebugTotalFiles--;
}
if (dp->bUseTemporary)
{
ReplaceFile(tmpfile, outfn);
}
}
else
{
log_perror("DMP", "FAIL", dp->pszErrorMessage, outfn);
}
if (!bPotentialConflicts)
{
if (mudconf.have_mailer)
{
f = fopen(mudconf.mail_db, "wb");
if (f)
{
DebugTotalFiles++;
dump_mail(f);
if (fclose(f) == 0)
{
DebugTotalFiles--;
}
}
}
if (mudconf.have_comsys)
{
save_comsys(mudconf.comsys_db);
}
}
return;
}
// Nuke our predecessor
//
if (mudconf.compress_db)
{
sprintf(prevfile, "%s.prev.gz", mudconf.outdb);
sprintf(tmpfile, "%s.#%d#.gz", mudconf.outdb, mudstate.epoch - 1);
RemoveFile(tmpfile);
sprintf(tmpfile, "%s.#%d#.gz", mudconf.outdb, mudstate.epoch);
strcpy(outfn, mudconf.outdb);
strcat(outfn, ".gz");
f = popen(tprintf("%s > %s", mudconf.compress, tmpfile), POPEN_WRITE_OP);
if (f)
{
DebugTotalFiles++;
setvbuf(f, NULL, _IOFBF, 16384);
db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS);
if (pclose(f) != -1)
{
DebugTotalFiles--;
}
ReplaceFile(outfn, prevfile);
if (ReplaceFile(tmpfile, outfn) < 0)
{
log_perror("SAV", "FAIL", "Renaming output file to DB file", tmpfile);
}
}
else
{
log_perror("SAV", "FAIL", "Opening", tmpfile);
}
}
else
{
sprintf(prevfile, "%s.prev", mudconf.outdb);
sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch - 1);
RemoveFile(tmpfile);
sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch);
f = fopen(tmpfile, "wb");
if (f)
{
DebugTotalFiles++;
setvbuf(f, NULL, _IOFBF, 16384);
db_write(f, F_MUX, OUTPUT_VERSION | OUTPUT_FLAGS);
if (fclose(f) == 0)
{
DebugTotalFiles--;
}
ReplaceFile(mudconf.outdb, prevfile);
if (ReplaceFile(tmpfile, mudconf.outdb) < 0)
{
log_perror("SAV", "FAIL", "Renaming output file to DB file", tmpfile);
}
}
else
{
log_perror("SAV", "FAIL", "Opening", tmpfile);
}
}
if (mudconf.have_mailer)
{
f = fopen(mudconf.mail_db, "wb");
if (f)
{
DebugTotalFiles++;
dump_mail(f);
if (fclose(f) == 0)
{
DebugTotalFiles--;
}
}
}
if (mudconf.have_comsys)
{
save_comsys(mudconf.comsys_db);
}
}
void dump_database(void)
{
char *buff;
mudstate.epoch++;
#ifndef WIN32
if (mudstate.dumping)
{
STARTLOG(LOG_DBSAVES, "DMP", "DUMP");
log_text("Waiting on previously-forked child before dumping... ");
ENDLOG;
while (mudstate.dumping)
{
// We have a forked dump in progress, so we will wait until the
// child exits.
//
MuxAlarm.Sleep(time_1s);
}
}
mudstate.dumping = true;
#endif
buff = alloc_mbuf("dump_database");
sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch);
STARTLOG(LOG_DBSAVES, "DMP", "DUMP");
log_text("Dumping: ");
log_text(buff);
ENDLOG;
#ifndef MEMORY_BASED
// Save cached modified attribute list
//
al_store();
#endif // MEMORY_BASED
pcache_sync();
dump_database_internal(DUMP_I_NORMAL);
SYNC;
STARTLOG(LOG_DBSAVES, "DMP", "DONE")
log_text("Dump complete: ");
log_text(buff);
ENDLOG;
free_mbuf(buff);
#ifndef WIN32
// This doesn't matter. We are about the stop the game. However,
// leave it in.
//
mudstate.dumping = false;
#endif
}
void fork_and_dump(int key)
{
#ifndef WIN32
static volatile bool bRequestAccepted = false;
// fork_and_dump is never called with mudstate.dumping true, but we'll
// ensure that assertion now.
//
if ( bRequestAccepted
|| mudstate.dumping)
{
return;
}
bRequestAccepted = true;
#endif
// If no options were given, then it means DUMP_TEXT+DUMP_STRUCT.
//
if (key == 0)
{
key = DUMP_TEXT+DUMP_STRUCT;
}
if (*mudconf.dump_msg)
{
raw_broadcast(0, "%s", mudconf.dump_msg);
}
check_mail_expiration();
char *buff = alloc_lbuf("fork_and_dump");
if (key & (DUMP_TEXT|DUMP_STRUCT))
{
STARTLOG(LOG_DBSAVES, "DMP", "CHKPT");
if (key & DUMP_TEXT)
{
log_text("SYNCing");
if (key & DUMP_STRUCT)
{
log_text(" and ");
}
}
if (key & DUMP_STRUCT)
{
mudstate.epoch++;
sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch);
log_text("Checkpointing: ");
log_text(buff);
}
ENDLOG;
}
if (key & DUMP_FLATFILE)
{
STARTLOG(LOG_DBSAVES, "DMP", "FLAT");
log_text("Creating flatfile: ");
sprintf(buff, "%s.FLAT", mudconf.outdb);
log_text(buff);
ENDLOG;
}
free_lbuf(buff);
#ifndef MEMORY_BASED
// Save cached modified attribute list
//
al_store();
#endif // MEMORY_BASED
pcache_sync();
SYNC;
#ifndef WIN32
mudstate.write_protect = true;
int child = 0;
bool bChildExists = false;
mudstate.dumping = true;
bool bAttemptFork = mudconf.fork_dump;
#if !defined(HAVE_PREAD) \
|| !defined(HAVE_PWRITE)
if (key & DUMP_FLATFILE)
{
// Don't attempt a fork()'ed @dump/flat without pread()/pwrite()
// support.
//
bAttemptFork = false;
}
#endif // !HAVE_PREAD !HAVE_PWRITE
#endif // WIN32
if (key & (DUMP_STRUCT|DUMP_FLATFILE))
{
#ifndef WIN32
if (bAttemptFork)
{
child = fork();
}
if (child == 0)
{
#endif
if (key & DUMP_STRUCT)
{
dump_database_internal(DUMP_I_NORMAL);
}
if (key & DUMP_FLATFILE)
{
dump_database_internal(DUMP_I_FLAT);
}
#ifndef WIN32
if (mudconf.fork_dump)
{
_exit(0);
}
}
else if (child < 0)
{
log_perror("DMP", "FORK", NULL, "fork()");
}
else if (child != mudstate.dumper)
{
mudstate.dumper = child;
bChildExists = true;
}
else if (child == mudstate.dumper)
{
// The child process executed and exited before fork() returned to
// the parent process. Without a process id, the parent's SIGCHLD
// handler could not be certain that the pid of the exiting
// process would match the pid of this child.
//
// However, at the this point, we can be sure. But, there's
// nothing much left to do.
//
// See SIGCHLD handler in bsd.cpp.
//
mudstate.dumper = 0;
// There is no child (bChildExists == false), we aren't dumping
// (mudstate.dumping == false) and there is no outstanding dumper
// process (mudstate.dumper == 0).
}
#endif
}
#ifndef WIN32
mudstate.write_protect = false;
if (!bChildExists)
{
// We have the ability to fork children, but we are not configured to
// use it; or, we tried to fork a child and failed; or, we didn't
// need to dump the structure or a flatfile; or, the child has finished
// dumping already.
//
mudstate.dumping = false;
mudstate.dumper = 0;
}
bRequestAccepted = false;
#endif
if (*mudconf.postdump_msg)
{
raw_broadcast(0, "%s", mudconf.postdump_msg);
}
}
#define LOAD_GAME_SUCCESS 0
#define LOAD_GAME_NO_INPUT_DB (-1)
#define LOAD_GAME_CANNOT_OPEN (-2)
#define LOAD_GAME_LOADING_PROBLEM (-3)
#ifdef MEMORY_BASED
static int load_game(void)
#else // MEMORY_BASED
static int load_game(int ccPageFile)
#endif // MEMORY_BASED
{
FILE *f = NULL;
char infile[SIZEOF_PATHNAME+8];
struct stat statbuf;
int db_format, db_version, db_flags;
bool compressed = false;
if (mudconf.compress_db)
{
strcpy(infile, mudconf.indb);
strcat(infile, ".gz");
if (stat(infile, &statbuf) == 0)
{
f = popen(tprintf(" %s < %s", mudconf.uncompress, infile), POPEN_READ_OP);
if (f != NULL)
{
DebugTotalFiles++;
compressed = true;
}
}
}
if (!compressed)
{
strcpy(infile, mudconf.indb);
if (stat(infile, &statbuf) != 0)
{
// Indicate that we couldn't load because the input db didn't
// exist.
//
return LOAD_GAME_NO_INPUT_DB;
}
if ((f = fopen(infile, "rb")) == NULL)
{
return LOAD_GAME_CANNOT_OPEN;
}
DebugTotalFiles++;
setvbuf(f, NULL, _IOFBF, 16384);
}
// Ok, read it in.
//
STARTLOG(LOG_STARTUP, "INI", "LOAD")
log_text("Loading: ");
log_text(infile);
ENDLOG
if (db_read(f, &db_format, &db_version, &db_flags) < 0)
{
// Everything is not ok.
//
if (compressed)
{
if (pclose(f) != -1)
{
DebugTotalFiles--;
}
}
else
{
if (fclose(f) == 0)
{
DebugTotalFiles--;
}
}
f = 0;
STARTLOG(LOG_ALWAYS, "INI", "FATAL")
log_text("Error loading ");
log_text(infile);
ENDLOG
return LOAD_GAME_LOADING_PROBLEM;
}
// Everything is ok.
//
if (compressed)
{
if (pclose(f) != -1)
{
DebugTotalFiles--;
}
}
else
{
if (fclose(f) == 0)
{
DebugTotalFiles--;
}
}
f = 0;
#ifndef MEMORY_BASED
if (db_flags & V_DATABASE)
{
// It loaded an output file.
//
if (ccPageFile == HF_OPEN_STATUS_NEW)
{
STARTLOG(LOG_STARTUP, "INI", "LOAD");
log_text("Attributes are not present in either the input file or the attribute database.");
ENDLOG;
}
}
else
{
// It loaded a flatfile.
//
if (ccPageFile == HF_OPEN_STATUS_OLD)
{
STARTLOG(LOG_STARTUP, "INI", "LOAD");
log_text("Attributes present in both the input file and the attribute database.");
ENDLOG;
}
}
#endif // !MEMORY_BASED
if (mudconf.have_comsys)
{
load_comsys(mudconf.comsys_db);
}
if (mudconf.have_mailer)
{
f = fopen(mudconf.mail_db, "rb");
if (f)
{
DebugTotalFiles++;
setvbuf(f, NULL, _IOFBF, 16384);
Log.tinyprintf("LOADING: %s" ENDLINE, mudconf.mail_db);
load_mail(f);
Log.tinyprintf("LOADING: %s (done)" ENDLINE, mudconf.mail_db);
if (fclose(f) == 0)
{
DebugTotalFiles--;
}
f = 0;
}
}
STARTLOG(LOG_STARTUP, "INI", "LOAD");
log_text("Load complete.");
ENDLOG;
return LOAD_GAME_SUCCESS;
}
/*
* match a list of things, using the no_command flag
*
* This seems to be always called with type == AMATCH_CMD...
* So the fact that ansi_strip is done within atr_match only
* brings about a if () performance hit...
*
*/
bool list_check
(
dbref thing,
dbref player,
char type,
char *str,
char *raw_str,
bool check_parent
)
{
bool bMatch = false;
int limit = mudstate.db_top;
while (NOTHING != thing)
{
#ifdef REALITY_LVLS
if ((thing != player)
&& (!(No_Command(thing)))
&& IsReal(thing, player))
#else
if ( thing != player
&& !No_Command(thing))
#endif /* REALITY_LVLS */
{
bMatch |= atr_match(thing, player, type, str, raw_str, check_parent);
}
// Non-authoritative test of circular reference.
//
dbref next;
if ( thing == (next = Next(thing))
|| --limit < 0
|| MuxAlarm.bAlarmed)
{
break;
}
thing = next;
}
return bMatch;
}
bool Hearer(dbref thing)
{
if ( mudstate.inpipe
&& thing == mudstate.poutobj)
{
return true;
}
if ( Connected(thing)
|| Puppet(thing)
|| H_Listen(thing))
{
return true;
}
if (mudstate.bfNoListens.IsSet(thing))
{
return false;
}
else if (mudstate.bfListens.IsSet(thing))
{
return true;
}
bool bFoundCommands = false;
bool bFoundListens = false;
if (Monitor(thing))
{
char *as, *buff, *s;
dbref aowner;
int atr, aflags;
ATTR *ap;
buff = alloc_lbuf("Hearer");
atr_push();
for (atr = atr_head(thing, &as); atr; atr = atr_next(&as))
{
ap = atr_num(atr);
if ( !ap
|| (ap->flags & AF_NOPROG))
{
continue;
}
atr_get_str(buff, thing, atr, &aowner, &aflags);
if (!(aflags & AF_NOPROG))
{
switch (buff[0])
{
case AMATCH_CMD:
bFoundCommands = true;
break;
case AMATCH_LISTEN:
bFoundListens = true;
break;
}
}
// Make sure we can execute it.
//
if ( buff[0] != AMATCH_LISTEN
|| (aflags & AF_NOPROG))
{
continue;
}
// Make sure there's a : in it.
//
for (s = buff + 1; *s && *s != ':'; s++)
{
; // Nothing
}
if (s)
{
free_lbuf(buff);
atr_pop();
mudstate.bfListens.Set(thing);
return true;
}
}
free_lbuf(buff);
atr_pop();
mudstate.bfNoListens.Set(thing);
if (bFoundCommands)
{
mudstate.bfNoCommands.Clear(thing);
mudstate.bfCommands.Set(thing);
}
else
{
mudstate.bfCommands.Clear(thing);
mudstate.bfNoCommands.Set(thing);
}
}
return false;
}
void do_readcache(dbref executor, dbref caller, dbref enactor, int key)
{
helpindex_load(executor);
fcache_load(executor);
}
static void process_preload(void)
{
dbref thing, parent, aowner;
int aflags, lev, i;
char *tstr;
FWDLIST *fp;
fp = (FWDLIST *) alloc_lbuf("process_preload.fwdlist");
tstr = alloc_lbuf("process_preload.string");
i = 0;
DO_WHOLE_DB(thing)
{
// Ignore GOING objects.
//
if (Going(thing))
{
continue;
}
scheduler.RunTasks(10);
// Look for a STARTUP attribute in parents.
//
if (mudconf.run_startup)
{
ITER_PARENTS(thing, parent, lev)
{
if (Flags(thing) & HAS_STARTUP)
{
did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_STARTUP,
(char **)NULL, 0);
// Process queue entries as we add them.
//
scheduler.RunTasks(10);
break;
}
}
}
// Look for a FORWARDLIST attribute.
//
if (H_Fwdlist(thing))
{
atr_get_str(tstr, thing, A_FORWARDLIST, &aowner, &aflags);
if (*tstr)
{
fwdlist_load(fp, GOD, tstr);
if (fp->count > 0)
{
fwdlist_set(thing, fp);
}
}
}
}
free_lbuf(fp);
free_lbuf(tstr);
}
#ifndef MEMORY_BASED
/*
* ---------------------------------------------------------------------------
* * info: display info about the file being read or written.
*/
void info(int fmt, int flags, int ver)
{
const char *cp;
if (fmt == F_MUX)
{
cp = "MUX";
}
else
{
cp = "*unknown*";
}
Log.tinyprintf("%s version %d:", cp, ver);
if ((flags & MANDFLAGS) != MANDFLAGS)
{
Log.WriteString(" Unsupported flags");
}
if (flags & V_DATABASE)
Log.WriteString(" Database");
if (flags & V_ATRNAME)
Log.WriteString(" AtrName");
if (flags & V_ATRKEY)
Log.WriteString(" AtrKey");
if (flags & V_ATRMONEY)
Log.WriteString(" AtrMoney");
Log.WriteString("\n");
}
char *standalone_infile = NULL;
char *standalone_outfile = NULL;
char *standalone_basename = NULL;
bool standalone_check = false;
bool standalone_load = false;
bool standalone_unload = false;
void dbconvert(void)
{
int setflags, clrflags, ver;
int db_ver, db_format, db_flags;
Log.SetBasename("-");
Log.StartLogging();
SeedRandomNumberGenerator();
pool_init(POOL_LBUF, LBUF_SIZE);
pool_init(POOL_MBUF, MBUF_SIZE);
pool_init(POOL_SBUF, SBUF_SIZE);
pool_init(POOL_BOOL, sizeof(struct boolexp));
cf_init();
// Decide what conversions to do and how to format the output file.
//
setflags = clrflags = ver = 0;
bool do_redirect = false;
bool do_write = true;
if (standalone_check)
{
do_write = false;
}
if (standalone_load)
{
clrflags = 0xffffffff;
setflags = OUTPUT_FLAGS;
ver = OUTPUT_VERSION;
do_redirect = true;
}
else if (standalone_unload)
{
clrflags = 0xffffffff;
setflags = UNLOAD_FLAGS;
ver = UNLOAD_VERSION;
}
// Open the database
//
init_attrtab();
char dirfile[SIZEOF_PATHNAME];
char pagfile[SIZEOF_PATHNAME];
strcpy(dirfile, standalone_basename);
strcat(dirfile, ".dir");
strcpy(pagfile, standalone_basename);
strcat(pagfile, ".pag");
int cc = init_dbfile(dirfile, pagfile, 650);
if (cc == HF_OPEN_STATUS_ERROR)
{
Log.tinyprintf("Can't open database in (%s, %s) files\n", dirfile, pagfile);
exit(1);
}
else if (cc == HF_OPEN_STATUS_OLD)
{
if (setflags == OUTPUT_FLAGS)
{
Log.tinyprintf("Would overwrite existing database (%s, %s)\n", dirfile, pagfile);
CLOSE;
exit(1);
}
}
else if (cc == HF_OPEN_STATUS_NEW)
{
if (setflags == UNLOAD_FLAGS)
{
Log.tinyprintf("Database (%s, %s) is empty.\n", dirfile, pagfile);
CLOSE;
exit(1);
}
}
FILE *fpIn = fopen(standalone_infile, "rb");
if (!fpIn)
{
exit(1);
}
// Go do it.
//
if (do_redirect)
{
extern void cache_redirect(void);
cache_redirect();
}
setvbuf(fpIn, NULL, _IOFBF, 16384);
db_read(fpIn, &db_format, &db_ver, &db_flags);
if (do_redirect)
{
extern void cache_pass2(void);
cache_pass2();
}
Log.WriteString("Input: ");
info(db_format, db_flags, db_ver);
if (standalone_check)
{
do_dbck(NOTHING, NOTHING, NOTHING, DBCK_FULL);
}
fclose(fpIn);
if (do_write)
{
FILE *fpOut = fopen(standalone_outfile, "wb");
if (!fpOut)
{
exit(1);
}
db_flags = (db_flags & ~clrflags) | setflags;
if (db_format != F_MUX)
{
db_ver = 3;
}
if (ver != 0)
{
db_ver = ver;
}
Log.WriteString("Output: ");
info(F_MUX, db_flags, db_ver);
setvbuf(fpOut, NULL, _IOFBF, 16384);
#ifndef MEMORY_BASED
// Save cached modified attribute list
//
al_store();
#endif // MEMORY_BASED
db_write(fpOut, F_MUX, db_ver | db_flags);
fclose(fpOut);
}
CLOSE;
db_free();
exit(0);
}
#endif // MEMORY_BASED
void write_pidfile(const char *pFilename)
{
FILE *fp = fopen(pFilename, "wb");
if (fp)
{
fprintf(fp, "%d" ENDLINE, game_pid);
fclose(fp);
}
else
{
STARTLOG(LOG_ALWAYS, "PID", "FAIL");
Log.tinyprintf("Failed to write pidfile %s\n", pFilename);
ENDLOG;
}
}
long DebugTotalFiles = 3;
long DebugTotalSockets = 0;
#ifdef WIN32
long DebugTotalThreads = 1;
long DebugTotalSemaphores = 0;
#endif
#ifdef MEMORY_ACCOUNTING
long DebugTotalMemory = 0;
#endif
#define CLI_DO_CONFIG_FILE CLI_USER+0
#define CLI_DO_MINIMAL CLI_USER+1
#define CLI_DO_VERSION CLI_USER+2
#define CLI_DO_USAGE CLI_USER+3
#define CLI_DO_INFILE CLI_USER+4
#define CLI_DO_OUTFILE CLI_USER+5
#define CLI_DO_CHECK CLI_USER+6
#define CLI_DO_LOAD CLI_USER+7
#define CLI_DO_UNLOAD CLI_USER+8
#define CLI_DO_BASENAME CLI_USER+9
#define CLI_DO_PID_FILE CLI_USER+10
#define CLI_DO_ERRORPATH CLI_USER+11
bool bMinDB = false;
bool bSyntaxError = false;
char *conffile = NULL;
bool bVersion = false;
char *pErrorBasename = "";
bool bServerOption = false;
#ifdef MEMORY_BASED
#define NUM_CLI_OPTIONS 6
#else
#define NUM_CLI_OPTIONS 12
#endif
CLI_OptionEntry OptionTable[NUM_CLI_OPTIONS] =
{
{ "c", CLI_REQUIRED, CLI_DO_CONFIG_FILE },
{ "s", CLI_NONE, CLI_DO_MINIMAL },
{ "v", CLI_NONE, CLI_DO_VERSION },
{ "h", CLI_NONE, CLI_DO_USAGE },
#ifndef MEMORY_BASED
{ "i", CLI_REQUIRED, CLI_DO_INFILE },
{ "o", CLI_REQUIRED, CLI_DO_OUTFILE },
{ "k", CLI_NONE, CLI_DO_CHECK },
{ "l", CLI_NONE, CLI_DO_LOAD },
{ "u", CLI_NONE, CLI_DO_UNLOAD },
{ "d", CLI_REQUIRED, CLI_DO_BASENAME },
#endif // MEMORY_BASED
{ "p", CLI_REQUIRED, CLI_DO_PID_FILE },
{ "e", CLI_REQUIRED, CLI_DO_ERRORPATH }
};
void CLI_CallBack(CLI_OptionEntry *p, char *pValue)
{
if (p)
{
switch (p->m_Unique)
{
case CLI_DO_PID_FILE:
bServerOption = true;
mudconf.pid_file = pValue;
break;
case CLI_DO_CONFIG_FILE:
bServerOption = true;
conffile = pValue;
break;
case CLI_DO_MINIMAL:
bServerOption = true;
bMinDB = true;
break;
case CLI_DO_VERSION:
bServerOption = true;
bVersion = true;
break;
case CLI_DO_ERRORPATH:
bServerOption = true;
pErrorBasename = pValue;
break;
#ifndef MEMORY_BASED
case CLI_DO_INFILE:
mudstate.bStandAlone = true;
standalone_infile = pValue;
break;
case CLI_DO_OUTFILE:
mudstate.bStandAlone = true;
standalone_outfile = pValue;
break;
case CLI_DO_CHECK:
mudstate.bStandAlone = true;
standalone_check = true;
break;
case CLI_DO_LOAD:
mudstate.bStandAlone = true;
standalone_load = true;
break;
case CLI_DO_UNLOAD:
mudstate.bStandAlone = true;
standalone_unload = true;
break;
case CLI_DO_BASENAME:
mudstate.bStandAlone = true;
standalone_basename = pValue;
break;
#endif
case CLI_DO_USAGE:
default:
bSyntaxError = true;
break;
}
}
else
{
bSyntaxError = true;
}
}
#if defined(__INTEL_COMPILER)
extern "C" unsigned int __intel_cpu_indicator;
#define CPU_FD_ID 0x00200000UL
#define CPUID_0 0
// GenuineIntel
//
#define INTEL_MFGSTR0 'uneG'
#define INTEL_MFGSTR1 'letn'
#define INTEL_MFGSTR2 'Ieni'
// AuthenticAMD
//
#define AMD_MFGSTR0 'htuA'
#define AMD_MFGSTR1 'DMAc'
#define AMD_MFGSTR2 'itne'
#define CPUID_1 1
#define CPU_STEPPING(x) ((x ) & 0x00000000F)
#define CPU_MODEL(x) ((x >> 4) & 0x00000000F)
#define CPU_FAMILY(x) ((x >> 8) & 0x00000000F)
#define CPU_TYPE(x) ((x >> 12) & 0x00000000F)
#define CPU_EXTMODEL(x) ((x >> 16) & 0x00000000F)
#define CPU_EXTFAMILY(x) ((x >> 20) & 0x00000000F)
#define CPU_FEATURE_MMX 0x00800000UL
#define CPU_FEATURE_FSXR 0x01000000UL
#define CPU_FEATURE_SSE 0x02000000UL
#define CPU_FEATURE_SSE2 0x04000000UL
#define CPU_MSR_SSE3 0x00000001UL
// Indicators.
//
// OLDOS tags indicate that the CPU supports SSE[n], but the operating system
// will throw an exception when they are used.
//
//
#define CPU_TYPE_UNSPECIALIZED 0x00000001UL
#define CPU_TYPE_FAMILY_5 0x00000002UL
#define CPU_TYPE_FAMILY_6 0x00000004UL
#define CPU_TYPE_FAMILY_5_MMX 0x00000008UL
#define CPU_TYPE_FAMILY_6_MMX 0x00000010UL
#define CPU_TYPE_FAMILY_6_MMX_FSXR 0x00000020UL
#define CPU_TYPE_FAMILY_6_MMX_FSXR_SSE_OLDOS 0x00000040UL
#define CPU_TYPE_FAMILY_6_MMX_FSXR_SSE 0x00000080UL
#define CPU_TYPE_FAMILY_F_SSE2_OLDOS 0x00000100UL
#define CPU_TYPE_FAMILY_F_SSE2 0x00000200UL
#define CPU_TYPE_FAMILY_6_MMX_FSXR_SSE2 0x00000400UL
#define CPU_TYPE_FAMILY_6_MMX_FSXR_SSE3 0x00000800UL
#define CPU_TYPE_FAMILY_F_SSE3 0x00000800UL
void cpu_init(void)
{
UINT32 dwCPUID;
// Determine whether CPUID instruction is supported.
//
__asm
{
// Obtain a copy of the flags register.
//
pushfd
pop eax
mov dwCPUID,eax
// Attempt to flip the ID bit.
//
xor eax,CPU_FD_ID
push eax
popfd
// Obtain a second copy of the flags register.
//
pushfd
pop eax
xor dwCPUID,eax
}
// If the ID bit didn't toggle, the CPUID instruction is not supported.
//
if (CPU_FD_ID != dwCPUID)
{
// CPUID instruction is not supported.
//
__intel_cpu_indicator = CPU_TYPE_UNSPECIALIZED;
return;
}
UINT32 dwHighest;
UINT32 dwMfgStr0;
UINT32 dwMfgStr1;
UINT32 dwMfgStr2;
// CPUID is supported.
//
__asm
{
mov eax,CPUID_0
cpuid
mov dwHighest,eax
mov dwMfgStr0,ebx
mov dwMfgStr1,ecx
mov dwMfgStr2,edx
}
if (0 == dwHighest)
{
// We can't decipher anything with only CPUID (EAX=$0) available.
//
__intel_cpu_indicator = CPU_TYPE_UNSPECIALIZED;
return;
}
typedef enum
{
Intel = 0,
AMD
} CPUMaker;
CPUMaker maker;
if ( INTEL_MFGSTR0 == dwMfgStr0
&& INTEL_MFGSTR1 == dwMfgStr1
&& INTEL_MFGSTR2 == dwMfgStr2)
{
maker = Intel;
}
else if ( AMD_MFGSTR0 == dwMfgStr0
&& AMD_MFGSTR1 == dwMfgStr1
&& AMD_MFGSTR2 == dwMfgStr2)
{
maker = AMD;
}
else
{
// It's not Intel or AMD.
//
__intel_cpu_indicator = CPU_TYPE_UNSPECIALIZED;
return;
}
UINT32 dwSignature;
UINT32 dwBrand;
UINT32 dwMSR;
UINT32 dwFeatures;
__asm
{
mov eax,CPUID_1
cpuid
mov dwSignature,eax
mov dwBrand,ebx
mov dwMSR,ecx
mov dwFeatures,edx
}
// Develop 'Effective' Family and Model.
//
UINT32 dwEffFamily;
if (CPU_FAMILY(dwSignature) == 0xF)
{
dwEffFamily = CPU_FAMILY(dwSignature) + CPU_EXTFAMILY(dwSignature);
}
else
{
dwEffFamily = CPU_FAMILY(dwSignature);
}
UINT32 dwEffModel;
if (CPU_MODEL(dwSignature) == 0xF)
{
dwEffModel = CPU_MODEL(dwSignature) + (CPU_EXTMODEL(dwSignature) << 4);
}
else
{
dwEffModel = CPU_MODEL(dwSignature);
}
#define ADVF_MMX 0x00000001UL
#define ADVF_FSXR 0x00000002UL
#define ADVF_SSE 0x00000004UL
#define ADVF_SSE2 0x00000008UL
#define ADVF_SSE3 0x00000010UL
UINT32 dwAdvFeatures = 0;
// Decode the features the chips claim to possess.
//
if (dwFeatures & CPU_FEATURE_MMX)
{
dwAdvFeatures |= ADVF_MMX;
}
if (dwFeatures & CPU_FEATURE_FSXR)
{
dwAdvFeatures |= ADVF_FSXR;
}
if (dwFeatures & CPU_FEATURE_SSE)
{
dwAdvFeatures |= ADVF_SSE;
}
if (dwFeatures & CPU_FEATURE_SSE2)
{
dwAdvFeatures |= ADVF_SSE2;
}
if ( dwEffFamily <= 5
&& dwMSR & CPU_MSR_SSE3)
{
dwAdvFeatures |= ADVF_SSE3;
}
// Test whether operating system will allow use of these extensions.
//
UINT32 dwUseable = dwAdvFeatures;
if (dwUseable & ADVF_MMX)
{
try
{
__asm
{
// Let's try a MMX instruction.
//
emms
}
}
catch (...)
{
dwUseable &= ~(ADVF_MMX|ADVF_SSE|ADVF_SSE2|ADVF_SSE3);
}
}
if (dwUseable & ADVF_SSE)
{
try
{
__asm
{
// Let's try a SSE instruction.
//
xorps xmm0, xmm0
}
}
catch (...)
{
dwUseable &= ~(ADVF_SSE|ADVF_SSE2|ADVF_SSE3);
}
}
if (dwUseable & ADVF_SSE2)
{
try
{
__asm
{
// Let's try a SSE2 instruction.
//
xorpd xmm0, xmm0
}
}
catch (...)
{
dwUseable &= ~(ADVF_SSE2|ADVF_SSE3);
}
}
if (dwUseable & ADVF_SSE3)
{
try
{
__asm
{
// Let's try a SSE3 instruction.
//
haddpd xmm1,xmm2
}
}
catch (...)
{
dwUseable &= ~(ADVF_SSE3);
}
}
// Map tested features to an indicator for CPU dispatching.
//
if (dwEffFamily <= 4)
{
__intel_cpu_indicator = CPU_TYPE_UNSPECIALIZED;
}
else if (5 == dwEffFamily)
{
if (dwUseable & ADVF_MMX)
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_5_MMX;
}
else
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_5;
}
}
else
{
if (dwUseable & ADVF_MMX)
{
if (dwUseable & ADVF_FSXR)
{
if (dwUseable & ADVF_SSE)
{
if (dwUseable & ADVF_SSE2)
{
if (dwUseable & ADVF_SSE3)
{
if (dwEffFamily < 15)
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_6_MMX_FSXR_SSE3;
}
else
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_F_SSE3;
}
}
else
{
if (dwEffFamily < 15)
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_6_MMX_FSXR_SSE2;
}
else
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_F_SSE2;
}
}
}
else
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_6_MMX_FSXR_SSE;
}
}
else
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_6_MMX_FSXR;
}
}
else
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_6_MMX;
}
}
else
{
__intel_cpu_indicator = CPU_TYPE_FAMILY_6;
}
}
// Report findings to the log.
//
fprintf(stderr, "cpu_init: %s, Family %d, Model %d, %s%s%s%s%s" ENDLINE,
(Intel == maker ? "Intel" : (AMD == maker ? "AMD" : "Unknown")),
dwEffFamily,
dwEffModel,
(dwUseable & ADVF_MMX) ? "MMX " : "",
(dwUseable & ADVF_FSXR) ? "FSXR ": "",
(dwUseable & ADVF_SSE) ? "SSE ": "",
(dwUseable & ADVF_SSE2) ? "SSE2 ": "",
(dwUseable & ADVF_SSE3) ? "SSE3 ": "");
if (dwUseable != dwAdvFeatures)
{
UINT32 dw = dwAdvFeatures & (~dwUseable);
fprintf(stderr, "cpu_init: %s%s%s%s%s unsupported by OS." ENDLINE,
(dw & ADVF_MMX) ? "MMX ": "",
(dw & ADVF_FSXR) ? "FSXR ": "",
(dw & ADVF_SSE) ? "SSE ": "",
(dw & ADVF_SSE2) ? "SSE2 ": "",
(dw & ADVF_SSE3) ? "SSE3 ": "");
}
}
#endif
#define DBCONVERT_NAME1 "dbconvert"
#define DBCONVERT_NAME2 "dbconvert.exe"
int DCL_CDECL main(int argc, char *argv[])
{
#if defined(__INTEL_COMPILER)
cpu_init();
#endif
build_version();
// Look for dbconvert[.exe] in the program name.
//
size_t nProg = strlen(argv[0]);
const char *pProg = argv[0] + nProg - 1;
while ( nProg
&& ( mux_isalpha(*pProg)
|| *pProg == '.'))
{
nProg--;
pProg--;
}
pProg++;
mudstate.bStandAlone = false;
if ( mux_stricmp(pProg, DBCONVERT_NAME1) == 0
|| mux_stricmp(pProg, DBCONVERT_NAME2) == 0)
{
mudstate.bStandAlone = true;
}
mudconf.pid_file = "netmux.pid";
// Parse the command line
//
CLI_Process(argc, argv, OptionTable,
sizeof(OptionTable)/sizeof(CLI_OptionEntry), CLI_CallBack);
#ifndef MEMORY_BASED
if (mudstate.bStandAlone)
{
int n = 0;
if (standalone_check)
{
n++;
}
if (standalone_load)
{
n++;
}
if (standalone_unload)
{
n++;
}
if ( !standalone_basename
|| !standalone_infile
|| !standalone_outfile
|| n != 1
|| bServerOption)
{
bSyntaxError = true;
}
else
{
dbconvert();
return 0;
}
}
else
#endif // MEMORY_BASED
if (bVersion)
{
fprintf(stderr, "Version: %s" ENDLINE, mudstate.version);
return 1;
}
if ( bSyntaxError
|| conffile == NULL
|| !bServerOption)
{
fprintf(stderr, "Version: %s" ENDLINE, mudstate.version);
if (mudstate.bStandAlone)
{
fprintf(stderr, "Usage: %s -d <dbname> -i <infile> [-o <outfile>] [-l|-u|-k]" ENDLINE, pProg);
fprintf(stderr, " -d Basename." ENDLINE);
fprintf(stderr, " -i Input file." ENDLINE);
fprintf(stderr, " -k Check." ENDLINE);
fprintf(stderr, " -l Load." ENDLINE);
fprintf(stderr, " -o Output file." ENDLINE);
fprintf(stderr, " -u Unload." ENDLINE);
}
else
{
fprintf(stderr, "Usage: %s [-c <filename>] [-p <filename>] [-h] [-s] [-v]" ENDLINE, pProg);
fprintf(stderr, " -c Specify configuration file." ENDLINE);
fprintf(stderr, " -e Specify logfile basename (or '-' for stderr)." ENDLINE);
fprintf(stderr, " -h Display this help." ENDLINE);
fprintf(stderr, " -p Specify process ID file." ENDLINE);
fprintf(stderr, " -s Start with a minimal database." ENDLINE);
fprintf(stderr, " -v Display version string." ENDLINE ENDLINE);
}
return 1;
}
mudstate.bStandAlone = false;
FLOAT_Initialize();
TIME_Initialize();
SeedRandomNumberGenerator();
Log.SetBasename(pErrorBasename);
Log.StartLogging();
game_pid = getpid();
write_pidfile(mudconf.pid_file);
BuildSignalNamesTable();
#ifdef MEMORY_ACCOUNTING
extern CHashFile hfAllocData;
extern CHashFile hfIdentData;
extern bool bMemAccountingInitialized;
hfAllocData.Open("svdptrs.dir", "svdptrs.pag", 40);
hfIdentData.Open("svdlines.dir", "svdlines.pag", 40);
bMemAccountingInitialized = true;
#endif
#ifdef WIN32
// Find which version of Windows we are using - Completion ports do
// not work with Windows 95/98
OSVERSIONINFO VersionInformation;
VersionInformation.dwOSVersionInfoSize = sizeof (VersionInformation);
GetVersionEx(&VersionInformation);
platform = VersionInformation.dwPlatformId;
hGameProcess = GetCurrentProcess();
if (platform == VER_PLATFORM_WIN32_NT)
{
Log.WriteString("Running under Windows NT" ENDLINE);
// Get a handle to the kernel32 DLL
//
HINSTANCE hInstKernel32 = LoadLibrary("kernel32");
if (!hInstKernel32)
{
Log.WriteString("LoadLibrary of kernel32 for a CancelIo entry point failed. Cannot continue." ENDLINE);
return 1;
}
// Find the entry point for CancelIO so we can use it. This is done
// dynamically because Windows 95/98 doesn't have a CancelIO entry
// point. If it were done at load time, it would always fail on
// Windows 95/98...even though we don't use it or depend on it in
// that case.
//
fpCancelIo = (FCANCELIO *)GetProcAddress(hInstKernel32, "CancelIo");
if (fpCancelIo == NULL)
{
Log.WriteString("GetProcAddress of _CancelIo failed. Cannot continue." ENDLINE);
return 1;
}
fpGetProcessTimes = (FGETPROCESSTIMES *)GetProcAddress(hInstKernel32, "GetProcessTimes");
if (fpGetProcessTimes == NULL)
{
Log.WriteString("GetProcAddress of GetProcessTimes failed. Cannot continue." ENDLINE);
return 1;
}
}
else
{
Log.WriteString("Running under Windows 95/98" ENDLINE);
}
// Initialize WinSock.
//
WORD wVersionRequested = MAKEWORD(2,2);
WSADATA wsaData;
if (WSAStartup(wVersionRequested, &wsaData) != 0)
{
Log.WriteString("ERROR: Could not initialize WinSock." ENDLINE);
return 101;
}
if ( LOBYTE(wsaData.wVersion) != 2
|| HIBYTE(wsaData.wVersion) != 2)
{
// We can't run on this version of WinSock.
//
Log.tinyprintf("INFO: WinSock v%d.%d instead of v2.2." ENDLINE,
LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
//WSACleanup();
//return 102;
}
if (!bCryptoAPI)
{
Log.WriteString("Crypto API unavailable.\r\n");
}
#endif // WIN32
mudstate.start_time.GetLocal();
mudstate.cpu_count_from.GetUTC();
pool_init(POOL_LBUF, LBUF_SIZE);
pool_init(POOL_MBUF, MBUF_SIZE);
pool_init(POOL_SBUF, SBUF_SIZE);
pool_init(POOL_BOOL, sizeof(struct boolexp));
pool_init(POOL_DESC, sizeof(DESC));
pool_init(POOL_QENTRY, sizeof(BQUE));
tcache_init();
pcache_init();
cf_init();
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
init_rlimit();
#endif // HAVE_SETRLIMIT RLIMIT_NOFILE
init_cmdtab();
init_logout_cmdtab();
init_flagtab();
init_powertab();
init_functab();
init_attrtab();
init_version();
mudconf.config_file = StringClone(conffile);
cf_read();
fcache_init();
helpindex_init();
#ifdef MEMORY_BASED
db_free();
#else // MEMORY_BASED
if (bMinDB)
{
RemoveFile(mudconf.game_dir);
RemoveFile(mudconf.game_pag);
}
int ccPageFile = init_dbfile(mudconf.game_dir, mudconf.game_pag, mudconf.cache_pages);
if (HF_OPEN_STATUS_ERROR == ccPageFile)
{
STARTLOG(LOG_ALWAYS, "INI", "LOAD");
log_text("Couldn't load text database: ");
log_text(mudconf.game_dir);
log_text(mudconf.game_pag);
ENDLOG;
return 2;
}
#endif // MEMORY_BASED
mudstate.record_players = 0;
if (bMinDB)
{
db_make_minimal();
}
else
{
#ifdef MEMORY_BASED
int ccInFile = load_game();
#else // MEMORY_BASED
int ccInFile = load_game(ccPageFile);
#endif // MEMORY_BASED
if (LOAD_GAME_NO_INPUT_DB == ccInFile)
{
// The input file didn't exist.
//
#ifndef MEMORY_BASED
if (HF_OPEN_STATUS_NEW == ccPageFile)
{
// Since the .db file didn't exist, and the .pag/.dir files
// were newly created, just create a minimal DB.
//
#endif // !MEMORY_BASED
db_make_minimal();
ccInFile = LOAD_GAME_SUCCESS;
#ifndef MEMORY_BASED
}
#endif // !MEMORY_BASED
}
if (ccInFile != LOAD_GAME_SUCCESS)
{
STARTLOG(LOG_ALWAYS, "INI", "LOAD")
log_text("Couldn't load: ");
log_text(mudconf.indb);
ENDLOG
return 2;
}
}
set_signals();
Guest.StartUp();
// Do a consistency check and set up the freelist
//
do_dbck(NOTHING, NOTHING, NOTHING, 0);
// Reset all the hash stats
//
hashreset(&mudstate.command_htab);
hashreset(&mudstate.channel_htab);
hashreset(&mudstate.mail_htab);
hashreset(&mudstate.logout_cmd_htab);
hashreset(&mudstate.func_htab);
hashreset(&mudstate.flags_htab);
hashreset(&mudstate.attr_name_htab);
hashreset(&mudstate.player_htab);
hashreset(&mudstate.fwdlist_htab);
hashreset(&mudstate.desc_htab);
int i;
for (i = 0; i < MAX_GLOBAL_REGS; i++)
{
mudstate.global_regs[i] = alloc_lbuf("main.global_reg");
mudstate.glob_reg_len[i] = 0;
}
ValidateConfigurationDbrefs();
process_preload();
#ifndef WIN32
load_restart_db();
if (!mudstate.restarting)
#endif // !WIN32
{
if (fclose(stdout) == 0)
{
DebugTotalFiles--;
}
if (fclose(stdin) == 0)
{
DebugTotalFiles--;
}
}
SetupPorts(&nMainGamePorts, aMainGamePorts, &mudconf.ports);
boot_slave(GOD, GOD, GOD, 0);
// All intialization should be complete, allow the local
// extensions to configure themselves.
//
local_startup();
init_timer();
#ifdef WIN32
if (platform == VER_PLATFORM_WIN32_NT)
{
process_output = process_outputNT;
shovecharsNT(nMainGamePorts, aMainGamePorts);
}
else
{
process_output = process_output9x;
shovechars9x(nMainGamePorts, aMainGamePorts);
}
#else // WIN32
shovechars(nMainGamePorts, aMainGamePorts);
#endif // WIN32
close_sockets(false, "Going down - Bye");
dump_database();
// All shutdown, barring logfiles, should be done, shutdown the
// local extensions.
//
local_shutdown();
CLOSE;
#ifndef WIN32
CleanUpSlaveSocket();
CleanUpSlaveProcess();
#endif
// Go ahead and explicitly free the memory for these things so
// that it's easy to spot unintentional memory leaks.
//
for (i = 0; i < mudstate.nHelpDesc; i++)
{
helpindex_clean(i);
}
db_free();
#ifdef WIN32
// critical section not needed any more
if (platform == VER_PLATFORM_WIN32_NT)
{
DeleteCriticalSection(&csDescriptorList);
}
WSACleanup();
#endif // WIN32
return 0;
}
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
static void init_rlimit(void)
{
struct rlimit *rlp;
rlp = (struct rlimit *)alloc_lbuf("rlimit");
if (getrlimit(RLIMIT_NOFILE, rlp))
{
log_perror("RLM", "FAIL", NULL, "getrlimit()");
free_lbuf(rlp);
return;
}
rlp->rlim_cur = rlp->rlim_max;
if (setrlimit(RLIMIT_NOFILE, rlp))
{
log_perror("RLM", "FAIL", NULL, "setrlimit()");
}
free_lbuf(rlp);
}
#endif // HAVE_SETRLIMIT RLIMIT_NOFILE