/* 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 (); } } }