/* Copyright 1989, 1990 by James Aspnes, David Applegate, and Bennet Yee */ /* See the file COPYING for distribution information */ #include <stdio.h> #include <ctype.h> #include <signal.h> #include <sys/wait.h> #include "db.h" #include "config.h" #include "interface.h" #include "globals.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, "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"); #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) { int child; epoch++; fprintf(stderr, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch); child = fork(); if(child == 0) { /* in the child */ close(0); /* get that file descriptor back */ dump_database_internal(); _exit(0); /* don't close anything up */ } else if(child < 0) { perror("fork_and_dump: fork()"); } /* in the parent */ /* reset checkpoint request */ please_checkpoint = 0; } static int reaper(void) { union wait stat; while(wait3(&stat, WNOHANG, 0) > 0); return 0; } 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, "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() + time(0)); /* set up dumper */ if(dumpfile) free((void *) dumpfile); dumpfile = alloc_string(outfile); signal(SIGCHLD, reaper); /* 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 %d in %d: %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(); } } }