/* Copyright 1989, 1990 by James Aspnes, David Applegate, and Bennet Yee */
/* See the file COPYING for distribution information */
#include "os.h"
#include "db.h"
#include "config.h"
#include "interface.h"
#include "globals.h"
#include "externs.h"
extern const char *alloc_string (const char *);
/* globals */
datum me = NOTHING;
datum you = NOTHING;
datum text = NOTHING;
datum mtext = NOTHING;
/* declarations */
static const char *dumpfile = 0;
static int epoch = 0;
/* syscalls set these */
int please_gc = 0;
int please_checkpoint = 0;
static void fork_and_dump (void);
void dump_database (void);
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, "wb")) != NULL) {
db_write (f);
fclose (f);
#ifdef WIN32
unlink (dumpfile);
#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");
#ifdef DUMP_CORE
abort ();
#endif
_exit (135);
} else {
fprintf (stderr, "DUMPING: %s\n", panicfile);
db_write (f);
fclose (f);
fprintf (stderr, "DUMPING: %s (done)\n", panicfile);
#ifdef DUMP_CORE
abort ();
#endif
_exit (136);
}
}
void dump_database (void)
{
epoch++;
/* no garbage, it's the real thing */
full_gc ();
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
child = fork ();
if (child == 0) {
/* in the child */
close (0); /* get that file descriptor back */
#endif
dump_database_internal ();
#ifndef WIN32
_exit (0); /* don't close anything up */
} else if (child < 0) {
perror ("fork_and_dump: fork()");
}
#endif
/* in the parent */
/* reset checkpoint request */
please_checkpoint = 0;
}
#ifndef WIN32
static void reaper (int code)
{
int stat;
while (wait3 (&stat, WNOHANG, 0) > 0);
}
#endif
static void init_one_object (datum x)
{
do_action (x, x, STARTUP_ACTION);
}
static void init_objects (void)
{
process_command (TOP_OBJECT, TOP_INIT_COMMAND);
/* don't need gc after this since no new strings can be created */
foreach_object (init_one_object);
}
datum connect_player (const char *name, const char *password)
{
datum iname;
struct object *top;
datum value;
set s;
datum player;
const char *crypted; /* encrypted password */
datum victim;
struct object *v;
if ((top = object (TOP_OBJECT)) == 0
|| !assoc (top->sets, PLAYER_SET_NAME, &value)) {
panic ("Player list is gone!");
return NOTHING;
} else if ((iname = intern_soft (name)) == NOTHING) {
return NOTHING;
}
/* else */
/* find iname in the s */
s = (set) value;
victim = NOTHING;
SET_FOREACH_MATCH (s, iname, player) {
if ((crypted = string (lookup (player, PASSWORD_NAME))) != 0
&& check_password (password, crypted)) {
/* we have a winner */
if (victim != NOTHING)
return NOTHING; /* we have two winners?!? */
else
victim = player;
}
}
END_SET_FOREACH;
/* bail out if they don't really exist */
if ((v = object (victim)) == 0)
return 0;
/* can only be connected once */
if (oflag_set (v, F_CONNECTED))
return NOTHING;
/* everything ok, do the connection */
v->flags |= F_CONNECTED;
PUSH_GLOBALS {
me = TOP_OBJECT;
add_to (TOP_OBJECT, CONNECTED_SET_NAME, victim);
}
POP_GLOBALS;
/* tell the victim they're awake */
do_action (victim, victim, CONNECT_ACTION);
return victim;
}
void disconnect_player (datum player)
{
struct object *p;
/* tell the victim they are asleep */
do_action (player, player, DISCONNECT_ACTION);
/* nuke its connected bit */
if ((p = object (player)) != 0) {
p->flags &= ~F_CONNECTED;
}
/* and remove it from the list */
PUSH_GLOBALS {
me = TOP_OBJECT;
take_from (TOP_OBJECT, CONNECTED_SET_NAME, player);
}
POP_GLOBALS;
}
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);
/* initialize random number generator */
#ifdef WIN32
OS_SRAND (time (0));
#else
OS_SRAND (getpid () + time (0));
#endif
/* set up dumper */
if (dumpfile)
free ((void *) dumpfile);
dumpfile = alloc_string (outfile);
#ifndef WIN32
signal (SIGCHLD, reaper);
#endif
/* do programmable initialization */
init_objects ();
return 0;
}
static void check_requests (void)
{
if (please_gc) {
/* do the whole thing */
full_gc ();
please_gc = 0;
} else {
/* do an incremental pass */
incremental_gc ();
}
if (please_checkpoint) {
fork_and_dump ();
}
}
int process_command (datum player, const char *command)
{
if (command == 0)
abort ();
#ifdef LOG_COMMANDS
fprintf (stderr, "COMMAND from %ld in %ld: %s\n",
player, safe_get (player, location), command);
#endif /* LOG_COMMANDS */
parse_command (player, command);
check_requests ();
return 1;
}
void check_events (void)
{
datum thing;
int i;
unsigned long t;
t = time (0);
/* run event queue */
for (i = 0; i < TICKS_PER_SLICE; i++) {
if ((thing = undelay (t)) == NOTHING) {
break;
} else {
/* block delay inside a tick */
no_delays++;
do_action (thing, thing, TICK_ACTION);
no_delays--;
check_requests ();
}
}
}