/* $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 <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.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;
static int alarm_block = 0;
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.");
}
}
/* 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;
}
static void dump_database_internal(void)
{
char tmpfile[2048];
FILE *f;
sprintf(tmpfile, "%s.#%d#", dumpfile, epoch - 1);
unlink(tmpfile); /* nuke our predecessor */
sprintf(tmpfile, "%s.#%d#", dumpfile, epoch);
if((f = fopen(tmpfile, "w")) != NULL) {
db_write(f);
fclose(f);
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, "w")) == 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);
}
#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)
{
int child;
epoch++;
fprintf(stderr, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
#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 */
dump_database_internal();
_exit(0); /* !!! */
} else if(child < 0) {
perror("fork_and_dump: fork()");
}
/* in the parent */
/* reset alarm */
alarm_triggered = 0;
alarm(DUMP_INTERVAL);
}
static int reaper(void)
{
union wait stat;
while(wait3(&stat, WNOHANG, 0) > 0)
/*EMPTY*/
;
return 0;
}
int init_game(const char *infile, const char *outfile)
{
FILE *f;
if((f = fopen(infile, "r")) == 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);
/* initialize random number generator */
srandom(getpid());
/* set up dumper */
if(dumpfile) free((void *) dumpfile);
dumpfile = alloc_string(outfile);
signal(SIGALRM, alarm_handler);
signal(SIGHUP, alarm_handler);
signal(SIGCHLD, reaper);
alarm_triggered = 0;
alarm(DUMP_INTERVAL);
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 */
char *index(char *, char);
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';
/* block dump to prevent db inconsistencies from showing up */
alarm_block = 1;
/* 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;
}
}
/* unblock alarms */
alarm_block = 0;
if(alarm_triggered) {
fork_and_dump();
}
}
#undef Matched