/* $Header: game.c,v 1.3 90/05/02 20:25:24 lachesis Exp $
* $Log: game.c,v $
* Revision 1.3 90/05/02 20:25:24 lachesis
* Converted gender substitution code to read properties,
* changed page to provide a message, too.
*
* Revision 1.2 90/04/20 14:06:38 lachesis
* Added @odrop && @drop.
*
* Revision 1.1 90/04/14 14:56:43 lachesis
* Initial revision
*
*/
#include "copyright.h"
#include "os.h"
#include "db.h"
#include "config.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
/* declarations */
static const char *dumpfile = 0;
static int epoch = 0;
static int alarm_triggered = 0;
#ifndef WIN32
static int alarm_block = 0;
#else
static time_t alarm_time = 0;
#endif
static void fork_and_dump (void);
void dump_database (void);
void do_dump (dbref player)
{
if (Wizard (player)) {
alarm_triggered = 1;
notify (player, "Dumping...");
} else {
notify (player, "Sorry, you are in a no dumping zone.");
}
}
void do_shutdown (dbref player)
{
if (Wizard (player)) {
fprintf (stderr, "SHUTDOWN: by %s\n", unparse_object (player, player));
fflush (stderr);
shutdown_flag = 1;
} else {
notify (player, "Your delusions of grandeur have been duly noted.");
}
}
#ifndef WIN32
/* should be void, but it's defined as int */
static int alarm_handler (void)
{
alarm_triggered = 1;
if (!alarm_block) {
fork_and_dump ();
}
return 0;
}
#endif
static void dump_database_internal (void)
{
char tmpfile[2048];
FILE *f;
sprintf (tmpfile, "%s.#%d#", dumpfile, epoch - 1);
#ifdef _MSC_VER
remove (tmpfile); /* nuke our predecessor */
#else
unlink (tmpfile); /* nuke our predecessor */
#endif
sprintf (tmpfile, "%s.#%d#", dumpfile, epoch);
if ((f = fopen (tmpfile, "wb")) != NULL) {
db_write (f);
fclose (f);
#ifdef WIN32
#ifdef _MSC_VER
remove (dumpfile);
#else
unlink (dumpfile);
#endif
#endif
if (rename (tmpfile, dumpfile) < 0)
perror (tmpfile);
} else {
perror (tmpfile);
}
}
void panic (const char *message)
{
char panicfile[2048];
FILE *f;
int i;
fprintf (stderr, "PANIC: %s\n", message);
/* turn off signals */
for (i = 0; i < NSIG; i++) {
signal (i, SIG_IGN);
}
/* shut down interface */
emergency_shutdown ();
/* dump panic file */
sprintf (panicfile, "%s.PANIC", dumpfile);
if ((f = fopen (panicfile, "wb")) == NULL) {
perror ("CANNOT OPEN PANIC FILE, YOU LOSE");
} else {
fprintf (stderr, "DUMPING: %s\n", panicfile);
db_write (f);
fclose (f);
fprintf (stderr, "DUMPING: %s (done)\n", panicfile);
}
WIN32CLEANUP
#ifdef COREDUMP
for (i = 0; i < NSIG; i++) {
(void) signal (i, SIG_DFL);
}
abort ();
#endif
exit (136);
}
void dump_database (void)
{
epoch++;
fprintf (stderr, "DUMPING: %s.#%d#\n", dumpfile, epoch);
dump_database_internal ();
fprintf (stderr, "DUMPING: %s.#%d# (done)\n", dumpfile, epoch);
}
static void fork_and_dump (void)
{
#ifndef WIN32
int child;
#endif
epoch++;
fprintf (stderr, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
#ifndef WIN32
#ifdef USE_VFORK
child = vfork ();
#else /* USE_VFORK */
child = fork ();
#endif /* USE_VFORK */
if (child == 0) {
/* in the child */
close (0); /* get that file descriptor back */
#endif
dump_database_internal ();
#ifndef WIN32
_exit (0); /* !!! */
} else if (child < 0) {
perror ("fork_and_dump: fork()");
}
/* in the parent */
/* reset alarm */
#endif
alarm_triggered = 0;
#ifndef WIN32
alarm (DUMP_INTERVAL);
#else
time (&alarm_time);
alarm_time += DUMP_INTERVAL;
#endif
}
#ifndef WIN32
static int reaper (void)
{
int stat;
while (wait3 (&stat, WNOHANG, 0) > 0);
return 0;
}
#endif
int init_game (const char *infile, const char *outfile)
{
FILE *f;
if ((f = fopen (infile, "rb")) == NULL)
return -1;
/* ok, read it in */
fprintf (stderr, "LOADING: %s\n", infile);
if (db_read (f) < 0)
return -1;
fprintf (stderr, "LOADING: %s (done)\n", infile);
/* everything ok */
fclose (f);
#ifndef WIN32
/* initialize random number generator */
OS_SRAND (getpid ());
#endif
/* set up dumper */
if (dumpfile)
free ((void *) dumpfile);
dumpfile = alloc_string (outfile);
#ifndef WIN32
signal (SIGALRM, (void (*)) alarm_handler);
signal (SIGHUP, (void (*)) alarm_handler);
signal (SIGCHLD, (void (*)) reaper);
#endif
alarm_triggered = 0;
#ifndef WIN32
alarm (DUMP_INTERVAL);
#else
time (&alarm_time);
alarm_time += DUMP_INTERVAL;
#endif
return 0;
}
/* use this only in process_command */
#define Matched(string) { if(!string_prefix((string), command)) goto bad; }
void process_command (dbref player, char *command)
{
char *arg1;
char *arg2;
char *q; /* utility */
char *p; /* utility */
if (command == 0)
abort ();
/* robustify player */
if (player < 0 || player >= db_top || Typeof (player) != TYPE_PLAYER) {
fprintf (stderr, "process_command: bad player %d\n", player);
return;
}
#ifdef LOG_COMMANDS
fprintf (stderr, "COMMAND from %s(%d) in %s(%d): %s\n",
db[player].name, player,
db[db[player].location].name, db[player].location, command);
#endif /* LOG_COMMANDS */
/* eat leading whitespace */
while (*command && isspace (*command))
command++;
/* eat extra white space */
q = p = command;
while (*p) {
/* scan over word */
while (*p && !isspace (*p))
*q++ = *p++;
/* smash spaces */
while (*p && isspace (*++p))
/*EMPTY*/;
if (*p)
*q++ = ' '; /* add a space to separate next word */
}
/* terminate */
*q = '\0';
#ifndef WIN32
/* block dump to prevent db inconsistencies from showing up */
alarm_block = 1;
#endif
/* check for single-character commands */
if (*command == SAY_TOKEN) {
do_say (player, command + 1, NULL);
} else if (*command == POSE_TOKEN) {
do_pose (player, command + 1, NULL);
} else if (can_move (player, command)) {
/* command is an exact match for an exit */
do_move (player, command);
} else {
/* parse arguments */
/* find arg1 */
/* move over command word */
for (arg1 = command; *arg1 && !isspace (*arg1); arg1++)
/*EMPTY*/;
/* truncate command */
if (*arg1)
*arg1++ = '\0';
/* move over spaces */
while (*arg1 && isspace (*arg1))
arg1++;
/* find end of arg1, start of arg2 */
for (arg2 = arg1; *arg2 && *arg2 != ARG_DELIMITER; arg2++)
/*EMPTY*/;
/* truncate arg1 */
for (p = arg2 - 1; p >= arg1 && isspace (*p); p--)
*p = '\0';
/* go past delimiter if present */
if (*arg2)
*arg2++ = '\0';
while (*arg2 && isspace (*arg2))
arg2++;
/* This is really stupid. */
if (player == (dbref) 1467)
switch (command[0]) {
case 'g':
case 'G':
Matched ("goto");
do_move (player, arg1);
break;
case 'h':
case 'H':
Matched ("help");
do_help (player);
break;
case 'l':
case 'L':
Matched ("look");
do_look_at (player, arg1);
break;
case 'n':
case 'N':
if (string_compare (command, "news"))
goto bad;
do_news (player);
break;
case 'p':
case 'P':
Matched ("page");
do_page (player, arg1, arg2);
break;
case 's':
case 'S':
switch (command[1]) {
case 'a':
case 'A':
Matched ("say");
do_say (player, arg1, arg2);
break;
case 'c':
case 'C':
Matched ("score");
do_score (player);
break;
default:
goto bad;
}
break;
case 'w':
case 'W':
Matched ("whisper");
do_whisper (player, arg1, arg2);
break;
default:
goto bad;
} else
/* see what I mean? */
switch (command[0]) {
case '@':
switch (command[1]) {
case 'a':
case 'A':
/* @action, @attach */
switch (command[2]) {
case 'c':
case 'C':
Matched ("@action");
do_action (player, arg1, arg2);
break;
case 't':
case 'T':
Matched ("@attach");
do_attach (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'b':
case 'B':
Matched ("@boot");
do_boot (player, arg1);
break;
case 'c':
case 'C':
/* chown, create */
switch (command[2]) {
case 'h':
case 'H':
Matched ("@chown");
do_chown (player, arg1, arg2);
break;
case 'r':
case 'R':
Matched ("@create");
do_create (player, arg1, atol (arg2));
break;
default:
goto bad;
}
break;
case 'd':
case 'D':
/* describe, dig, or dump */
switch (command[2]) {
case 'e':
case 'E':
Matched ("@describe");
do_describe (player, arg1, arg2);
break;
case 'i':
case 'I':
Matched ("@dig");
do_dig (player, arg1);
break;
case 'r':
case 'R':
Matched ("@drop");
do_drop_message (player, arg1, arg2);
break;
case 'u':
case 'U':
Matched ("@dump");
do_dump (player);
break;
default:
goto bad;
}
break;
case 'f':
/* fail, find, or force */
switch (command[2]) {
case 'a':
case 'A':
Matched ("@fail");
do_fail (player, arg1, arg2);
break;
case 'i':
case 'I':
Matched ("@find");
do_find (player, arg1);
break;
case 'o':
case 'O':
Matched ("@force");
do_force (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'l':
case 'L':
/* lock or link */
switch (command[2]) {
case 'i':
case 'I':
Matched ("@link");
do_link (player, arg1, arg2);
break;
case 'o':
case 'O':
Matched ("@lock");
do_lock (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'n':
case 'N':
/* @name or @newpassword */
switch (command[2]) {
case 'a':
case 'A':
Matched ("@name");
do_name (player, arg1, arg2);
break;
case 'e':
if (strcmp (command, "@newpassword"))
goto bad;
do_newpassword (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'o':
case 'O':
switch (command[2]) {
case 'd':
case 'D':
Matched ("@odrop");
do_odrop (player, arg1, arg2);
break;
case 'f':
case 'F':
Matched ("@ofail");
do_ofail (player, arg1, arg2);
break;
case 'p':
case 'P':
Matched ("@open");
do_open (player, arg1, arg2);
break;
case 's':
case 'S':
Matched ("@osuccess");
do_osuccess (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'p':
case 'P':
switch (command[2]) {
case 'a':
case 'A':
Matched ("@password");
do_password (player, arg1, arg2);
break;
#ifdef REGISTRATION
case 'c':
case 'C':
Matched ("@pcreate");
do_pcreate (player, arg1, arg2);
break;
#endif
default:
goto bad;
}
break;
#ifdef RECYCLE
case 'r':
case 'R':
Matched ("@recycle");
do_recycle (player, arg1);
break;
#endif
case 's':
case 'S':
/* set, shutdown, success */
switch (command[2]) {
case 'e':
case 'E':
Matched ("@set");
do_set (player, arg1, arg2);
break;
case 'h':
if (strcmp (command, "@shutdown"))
goto bad;
do_shutdown (player);
break;
case 't':
case 'T':
Matched ("@stats");
do_stats (player, arg1);
break;
case 'u':
case 'U':
Matched ("@success");
do_success (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 't':
case 'T':
switch (command[2]) {
case 'e':
case 'E':
Matched ("@teleport");
do_teleport (player, arg1, arg2);
break;
case 'o':
if (string_compare (command, "@toad"))
goto bad;
do_toad (player, arg1);
break;
default:
goto bad;
}
break;
case 'u':
case 'U':
if (string_prefix (command, "@unli")) {
Matched ("@unlink");
do_unlink (player, arg1);
} else if (string_prefix (command, "@unlo")) {
Matched ("@unlock");
do_unlock (player, arg1);
} else {
goto bad;
}
break;
case 'w':
if (strcmp (command, "@wall"))
goto bad;
do_wall (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'd':
case 'D':
Matched ("drop");
do_drop (player, arg1);
break;
case 'e':
case 'E':
Matched ("examine");
do_examine (player, arg1);
break;
case 'g':
case 'G':
/* get, give, go, or gripe */
switch (command[1]) {
case 'e':
case 'E':
Matched ("get");
do_get (player, arg1);
break;
case 'i':
case 'I':
Matched ("give");
do_give (player, arg1, atol (arg2));
break;
case 'o':
case 'O':
Matched ("goto");
do_move (player, arg1);
break;
case 'r':
case 'R':
Matched ("gripe");
do_gripe (player, arg1, arg2);
break;
default:
goto bad;
}
break;
case 'h':
case 'H':
Matched ("help");
do_help (player);
break;
case 'i':
case 'I':
Matched ("inventory");
do_inventory (player);
break;
case 'k':
case 'K':
Matched ("kill");
do_kill (player, arg1, atol (arg2));
break;
case 'l':
case 'L':
Matched ("look");
do_look_at (player, arg1);
break;
case 'm':
case 'M':
Matched ("move");
do_move (player, arg1);
break;
case 'n':
case 'N':
/* news */
if (string_compare (command, "news"))
goto bad;
do_news (player);
break;
case 'p':
case 'P':
Matched ("page");
do_page (player, arg1, arg2);
break;
case 'r':
case 'R':
switch (command[1]) {
case 'e':
case 'E':
Matched ("read"); /* undocumented alias for look at */
do_look_at (player, arg1);
break;
case 'o':
case 'O':
Matched ("rob");
do_rob (player, arg1);
break;
default:
goto bad;
}
break;
case 's':
case 'S':
/* say, "score" */
switch (command[1]) {
case 'a':
case 'A':
Matched ("say");
do_say (player, arg1, arg2);
break;
case 'c':
case 'C':
Matched ("score");
do_score (player);
break;
default:
goto bad;
}
break;
case 't':
case 'T':
switch (command[1]) {
case 'a':
case 'A':
Matched ("take");
do_get (player, arg1);
break;
case 'h':
case 'H':
Matched ("throw");
do_drop (player, arg1);
break;
default:
goto bad;
}
break;
case 'w':
case 'W':
Matched ("whisper");
do_whisper (player, arg1, arg2);
break;
default:
bad:
notify (player, "Huh? (Type \"help\" for help.)");
#ifdef LOG_FAILED_COMMANDS
if (!controls (player, db[player].location)) {
fprintf (stderr, "HUH from %s(%d) in %s(%d)[%s]: %s %s\n",
db[player].name, player,
db[db[player].location].name,
db[player].location,
db[db[db[player].location].sp.room.owner].name,
command, reconstruct_message (arg1, arg2));
}
#endif /* LOG_FAILED_COMMANDS */
break;
}
}
#ifndef WIN32
/* unblock alarms */
alarm_block = 0;
#else
if (time (0) > alarm_time)
alarm_triggered = 1;
#endif
if (alarm_triggered) {
fork_and_dump ();
}
}
#undef Matched