/* game.c */ #include <stdio.h> #ifdef WANT_ANSI #ifdef __STDC__ #include <stddef.h> #include <stdlib.h> #include <unistd.h> #endif /* __STDC__ */ #endif /* WANT_ANSI */ #include <string.h> #include <ctype.h> #ifndef VMS #include <fcntl.h> #else #include <sys/fcntl.h> #endif #ifndef XENIX #include <signal.h> #ifndef VMS #include <sys/wait.h> #endif #else #include <sys/signal.h> #endif /* xenix */ #include <sys/types.h> #include <sys/stat.h> #include "mudconf.h" #include "config.h" #include "db.h" #include "interface.h" #include "match.h" #include "externs.h" #include "flags.h" #include "rwho_clilib.h" /* external prototypes */ #ifndef VMS extern int fork(void); extern int vfork(void); #endif extern void srandom(int seed); extern void init_attrtab(void); extern void init_cmdtab(void); extern void cf_init(void); extern int cf_read (const char *fn); extern void init_functab(void); extern void close_sockets(int emergency, char *message); extern void init_version(void); extern void init_logout_cmdtab(void); /* XXX Ansify these. */ extern void init_timer(); extern void raw_notify(); /* declarations */ int reserved; void fork_and_dump(int key); void dump_database(); /* * used to allocate storage for temporary stuff, cleared before command * execution */ void do_dump(dbref player, dbref cause, int key) { notify(player, "Dumping..."); fork_and_dump(key); } /* print out stuff into error file */ void report(void) { STARTLOG(LOG_BUGS,"BUG","INFO") log_text((char *)"Command: '"); log_text(mudstate.debug_cmd); log_text((char *)"'"); ENDLOG if (Good_obj(mudstate.curr_player)) { STARTLOG(LOG_BUGS,"BUG","INFO") log_text((char *)"Player: "); log_name_and_loc(mudstate.curr_player); if ((mudstate.curr_enactor != mudstate.curr_player) && Good_obj(mudstate.curr_enactor)) { log_text((char *)" Enactor: "); log_name_and_loc(mudstate.curr_enactor); } ENDLOG } } /* --------------------------------------------------------------------------- * notify_check: notifies the object #player of the message msg. The behavior * on puppets is as follows: If (!check_puppet), then the owner of the puppet * is informed. If (check_puppet), then the owner of the puppet is only * informed if the owner is in a different room. */ void notify_check(dbref player, dbref enactor, const char *msg, int check_puppet) { char *buff, *d, *tbuff; char *in; char *args[10]; dbref aowner; int i, nargs, aflags, doit, doit_set; if (!Good_obj(player)) return; mudstate.ntfy_nest_lev++; if (mudstate.ntfy_nest_lev >= mudconf.ntfy_nest_lim) { mudstate.ntfy_nest_lev--; return; } buff = alloc_lbuf("notify_check"); d = buff; if ((Flags(Owner(player)) & NOSPOOF) && (player != enactor) && (player != mudstate.curr_enactor) && (player != mudstate.curr_player)) { /* I'd really like to use tprintf here but I can't because * the caller may have. notify(player, tprintf(...)) is * quite common in the code. */ tbuff = alloc_sbuf("notify_check.nospoof"); safe_chr('[', buff, &d); safe_str(Name(enactor), buff, &d); sprintf(tbuff, "(#%d)", enactor); safe_str(tbuff, buff, &d); if (enactor != Owner(enactor)) { safe_chr('{', buff, &d); safe_str(Name(Owner(enactor)), buff, &d); safe_chr('}', buff, &d); } if (enactor != mudstate.curr_enactor) { sprintf(tbuff, "<-(#%d)", mudstate.curr_enactor); safe_str(tbuff, buff, &d); } safe_str((char *)"] ", buff, &d); free_sbuf(tbuff); } safe_str((char *)msg, buff, &d); *d = '\0'; doit_set = 0; doit = 0; switch (Typeof(player)) { case TYPE_PLAYER: raw_notify(player, buff); if (!mudconf.player_listen) break; case TYPE_THING: if ((Flags(player) & PUPPET) && (player != Owner(player)) && (!check_puppet || (Location(player) != Location(Owner(player))))) { tbuff = alloc_lbuf("notify_check.puppet"); d = tbuff; safe_str(Name(player), tbuff, &d); safe_str((char *)"> ", tbuff, &d); safe_str(buff, tbuff, &d); *d = '\0'; raw_notify(Owner(player), tbuff); free_lbuf(tbuff); } d = atr_pget(player, A_LISTEN, &aowner, &aflags); if (*d && wild_match(d, (char *)msg, args, 10)) { for (nargs=10; nargs && (!args[nargs-1] || !(*args[nargs-1])); nargs--) ; /* We have a match. Process AxHEAR attributes only * if the speaker passes the object's USE lock. */ doit = could_doit(enactor, player, A_LUSE); doit_set = 1; if (doit) { if (enactor != player) did_it(enactor, player, 0, NULL, 0, NULL, A_AHEAR, args, nargs); else did_it(enactor, player, 0, NULL, 0, NULL, A_AMHEAR, args, nargs); did_it(enactor, player, 0, NULL, 0, NULL, A_AAHEAR, args, nargs); for (i=0; i<10; i++) if (args[i]!=NULL) free_lbuf(args[i]); /* Also pass the message on to the contents * of the hearing object. Note: not telling * player protects against two forms of * recursion player doesn't tell itself (as * container) or as contents using teleport it * is possible to create a recursive loop but * this will be terminated when the depth * variable exceeds 30 */ if (!member(enactor,Contents(player)) && check_filter(player, enactor, A_INFILTER, msg)) { in = add_prefix(player, enactor, A_INPREFIX, msg, ""); notify_except(player, enactor, player, in, 0); free_lbuf(in); } } else { for (i=0; i<10; i++) if (args[i]!=NULL) free_lbuf(args[i]); } } free_lbuf(d); if ((player != enactor) && Monitor(player)) { if (!doit_set) doit = could_doit(enactor, player, A_LUSE); if (doit) (void)atr_match(player, enactor, AMATCH_LISTEN, (char *)msg, 0); } } free_lbuf(buff); mudstate.ntfy_nest_lev--; } void do_shutdown(dbref player, dbref cause, int extra, char *message) { STARTLOG(LOG_ALWAYS,"WIZ","SHTDN") log_text((char *)"Shutdown by "); log_name(player); ENDLOG STARTLOG(LOG_ALWAYS,"WIZ","SHTDN") log_text((char *)"Shutdown status: "); log_text(message); ENDLOG close(mudstate.reserved_fileid); mudstate.reserved_fileid = open(mudconf.status_file, O_RDWR|O_CREAT|O_TRUNC, 0); (void)write(mudstate.reserved_fileid, message, strlen(message)); (void)write(mudstate.reserved_fileid, "\n", 1); close(mudstate.reserved_fileid); mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); mudstate.shutdown_flag = 1; } static void dump_database_internal() { char tmpfile[256], outfn[256]; FILE *f; #ifdef VMS sprintf(tmpfile, "%s.-%d-", mudconf.outdb, mudstate.epoch - 1); unlink(tmpfile); /* nuke our predecessor */ sprintf(tmpfile, "%s.-%d-", mudconf.outdb, mudstate.epoch); #else sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch - 1); unlink(tmpfile); /* nuke our predecessor */ sprintf(tmpfile, "%s.#%d#", mudconf.outdb, mudstate.epoch); if(mudconf.compress_db) { sprintf(tmpfile, "%s.#%d#.Z", mudconf.outdb, mudstate.epoch -1); unlink (tmpfile); sprintf(tmpfile, "%s.#%d#.Z", mudconf.outdb, mudstate.epoch); strcpy(outfn, mudconf.outdb); strcat(outfn, ".Z"); if ((f = popen(tprintf("%s > %s",mudconf.compress, tmpfile),"w"))!=NULL) { db_write(f, F_MUSH, OUTPUT_VERSION|OUTPUT_FLAGS); pclose(f); if(rename(tmpfile, outfn) < 0) perror(tmpfile); return; } else perror(tmpfile); } #endif VMS if ((f = fopen(tmpfile, "w")) != NULL) { db_write(f, F_MUSH, OUTPUT_VERSION|OUTPUT_FLAGS); fclose(f); if (rename(tmpfile, mudconf.outdb) < 0) perror(tmpfile); } else perror(tmpfile); } void panic(const char *message, int dump) { FILE *f; int i; STARTLOG(LOG_ALWAYS,"SYS","PANIC") log_text((char *)message); ENDLOG report(); /* turn off signals */ for (i = 0; i < NSIG; i++) { if(!dump) signal(i, SIG_IGN); } /* shut down interface */ emergency_shutdown(); /* Close the gdbm db */ SYNC; CLOSE; /* dump panic file */ if ((f = fopen(mudconf.crashdb, "w")) == NULL) { STARTLOG(LOG_ALWAYS,"DMP","FAIL") perror(mudconf.crashdb); ENDLOG if(!dump) _exit(135); else abort(); } else { STARTLOG(LOG_ALWAYS,"DMP","PANIC") log_text((char *)"Panic dump: "); log_text(mudconf.crashdb); ENDLOG db_write(f, F_MUSH, OUTPUT_VERSION|OUTPUT_FLAGS); fclose(f); STARTLOG(LOG_ALWAYS,"DMP","DONE") log_text((char *)"Dump complete: "); log_text(mudconf.crashdb); ENDLOG if(!dump) _exit(136); else abort(); } } void dump_database() { char *buff; mudstate.epoch++; buff=alloc_mbuf("dump_database"); #ifndef VMS sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch); #else sprintf(buff, "%s.-%d-", mudconf.outdb, mudstate.epoch); #endif VMS sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch); STARTLOG(LOG_DBSAVES,"DMP","DUMP") log_text((char *)"Dumping: "); log_text(buff); ENDLOG SYNC; dump_database_internal(); STARTLOG(LOG_DBSAVES,"DMP","DONE") log_text((char *)"Dump complete: "); log_text(buff); ENDLOG free_mbuf(buff); } void fork_and_dump(int key) { int child; char *buff; if (*mudconf.dump_msg) raw_broadcast(0, "%s", mudconf.dump_msg); mudstate.epoch++; buff=alloc_mbuf("fork_and_dump"); #ifndef VMS sprintf(buff, "%s.#%d#", mudconf.outdb, mudstate.epoch); #else sprintf(buff, "%s.-%d-", mudconf.outdb, mudstate.epoch); #endif VMS STARTLOG(LOG_DBSAVES,"DMP","CHKPT") if (!key || (key & DUMP_TEXT)) { log_text((char *)"SYNCing"); if (!key || (key & DUMP_STRUCT)) log_text((char *)" and "); } if (!key || (key & DUMP_STRUCT)) { log_text((char *)"Checkpointing: "); log_text(buff); } ENDLOG free_mbuf(buff); al_store(); /* Save cached modified attribute list */ if (!key || (key & DUMP_TEXT)) SYNC; if (!key || (key & DUMP_STRUCT)) { #ifndef VMS if(mudconf.fork_dump) { if(mudconf.fork_vfork) { child = vfork(); } else { child = fork(); } } else { child = 0; } #else child = 0; #endif VMS if (child == 0) { close(mudstate.reserved_fileid); dump_database_internal(); #ifndef VMS if(mudconf.fork_dump) _exit(0); else mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); #else mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); #endif VMS } else if (child < 0) { perror("fork_and_dump: fork()"); } } } static void reaper() { #ifndef XENIX #ifndef VMS union wait stat; while (wait3(&stat, WNOHANG, 0) > 0); #else int status; wait(&status); #endif VMS #else int status; wait(&status); #endif XENIX } static int load_game(void) { FILE *f; int compressed; char infile[256]; struct stat statbuf; int db_format, db_version, db_flags; f = NULL; compressed = 0; #ifndef VMS if(mudconf.compress_db) { strcpy(infile, mudconf.indb); strcat(infile, ".Z"); if(stat(infile, &statbuf) == 0) { if ((f = popen(tprintf(" %s < %s", mudconf.uncompress, infile), "r")) == NULL) compressed = 1; } } #endif VMS if(compressed == 0) { strcpy(infile, mudconf.indb); if((f = fopen(mudconf.indb, "r")) == NULL) return -1; } /* ok, read it in */ STARTLOG(LOG_STARTUP,"INI","LOAD") log_text((char *)"Loading: "); log_text(infile); ENDLOG if (db_read(f, &db_format, &db_version, &db_flags) < 0) { STARTLOG(LOG_ALWAYS,"INI","FATAL") log_text((char *)"Error loading "); log_text(infile); ENDLOG return -1; } STARTLOG(LOG_STARTUP,"INI","LOAD") log_text((char *)"Load complete."); ENDLOG /* everything ok */ #ifndef VMS if(compressed) pclose(f); else fclose(f); #else fclose(f); #endif VMS return (0); } /* match a list of things */ int list_check(dbref thing, dbref player, char type, char *str, int check_parent) { int match = 0; while (thing != NOTHING) { if ((thing != player) && (atr_match(thing, player, type, str, check_parent) > 0)) match = 1; thing = Next(thing); } return (match); } /* * routine to check attribute list for wild card matches of certain type and * queue them */ static int atr_match1(dbref thing, dbref parent, dbref player, char type, char *str) { dbref aowner; int match, attr, aflags; char *buff, *s, *as; char *args[10]; ATTR *ap; /* See if we can do it. Silently fail if we can't. */ if (!could_doit(player, parent, A_LUSE)) return -1; match = 0; buff = alloc_lbuf("atr_match1"); atr_push(); for (attr=atr_head(parent,&as); attr; attr=atr_next(&as)) { ap = atr_num(attr); if (!ap || (ap->flags & AF_NOPROG)) continue; atr_get_str(buff, parent, attr, &aowner, &aflags); /* Make sure we can execute it */ if ((buff[0] != type) || (aflags & AF_NOPROG)) continue; /* decode it: search for first un escaped : */ for (s = buff + 1; *s && (*s != ':'); s++) ; if (!*s) continue; *s++ = 0; if (wild_match(buff + 1, str, args, 10)) { match = 1; wait_que(thing, player, RU_ARG1_COPY|RU_ARG2_TAKE, 0, NOTHING, s, args, 10); } } atr_pop(); free_lbuf(buff); return (match); } int atr_match(dbref thing, dbref player, char type, char *str, int check_parents) { int match, lev, result; dbref parent; match = 0; if (!check_parents) return atr_match1(thing, thing, player, type, str); for (lev=0, parent=thing; (Good_obj(parent) && (lev < mudconf.parent_nest_lim)); parent=Parent(parent), lev++) { result = atr_match1(thing, parent, player, type, str); if (result > 0) { match = 1; } else if (result < 0) { return -1; } } return match; } int Hearer(dbref thing) { char *as, *buff, *s; dbref aowner; int attr, aflags; ATTR *ap; if (IS(thing, TYPE_PLAYER, PLAYER_CONNECT) || (Flags(thing) & PUPPET)) return 1; if (Monitor(thing)) buff = alloc_lbuf("Hearer"); else buff = NULL; atr_push(); for (attr=atr_head(thing,&as); attr; attr=atr_next(&as)) { if (attr == A_LISTEN) { if (buff) free_lbuf(buff); atr_pop(); return 1; } if (Monitor(thing)) { ap = atr_num(attr); if (!ap || (ap->flags & AF_NOPROG)) continue; atr_get_str(buff, thing, attr, &aowner, &aflags); /* Make sure we can execute it */ if ((buff[0] != AMATCH_LISTEN) || (aflags & AF_NOPROG)) continue; /* Make sure there's a : in it */ for (s = buff + 1; *s && (*s != ':'); s++) ; if (s) { free_lbuf(buff); atr_pop(); return 1; } } } if (buff) free_lbuf(buff); atr_pop(); return 0; } #ifdef XENIX /* rename hack!!! */ rename(char *s1, char *s2) { char *buff; buff=alloc_mbuf("rename"); sprintf(buff, "mv %s %s", s1, s2); system(buff); free_mbuf(buff); } #endif void do_rwho (dbref player, dbref cause, int key) { #ifdef RWHO_IN_USE if (key == RWHO_START) { if (!mudstate.rwho_on) { rwhocli_setup(mudconf.rwho_host, mudconf.rwho_info_port, mudconf.rwho_pass, mudconf.mud_name, mudstate.short_ver); rwho_update(); if (!Quiet(player)) notify(player, "RWHO transmission started."); mudstate.rwho_on = 1; } else { notify(player, "RWHO transmission already on."); } } else if (key == RWHO_STOP) { if (mudstate.rwho_on) { rwhocli_shutdown(); if (!Quiet(player)) notify(player, "RWHO transmission stopped."); mudstate.rwho_on = 0; } else { notify(player, "RWHO transmission already off."); } } else { notify(player, "Illegal combination of switches."); } #else notify(player, "RWHO support has not been compiled in to the server."); #endif } void do_readcache (dbref player, dbref cause, int key) { helpindex_load(player); fcache_load(player); } #ifndef VMS void #endif VMS main(int argc, char *argv[]) { int mindb, lev; dbref thing, parent; if ((argc > 2) && (!strcmp(argv[1], "-s") && (argc > 3))) { fprintf(stderr, "Usage: %s [-s] [config-file]\n", argv[0]); exit(1); } fclose(stdin); fclose(stdout); mudstate.reserved_fileid = open(DEV_NULL, O_RDWR, 0); mindb = 0; /* Are we creating a new db? */ time(&mudstate.start_time); pool_init(&mudstate.lbuf_pool, LBUF_SIZE); pool_init(&mudstate.mbuf_pool, MBUF_SIZE); pool_init(&mudstate.sbuf_pool, SBUF_SIZE); pool_init(&mudstate.bool_pool, sizeof(struct boolexp)); pool_init(&mudstate.qentry_pool, sizeof(BQUE)); pool_init(&mudstate.desc_pool, sizeof(DESC)); cf_init(); init_cmdtab(); init_logout_cmdtab(); init_flagtab(); init_functab(); init_attrtab(); init_version(); hashinit(&mudstate.player_htab, 57); if (argc > 1 && !strcmp(argv[1], "-s")) { mindb = 1; if(argc == 3) cf_read(argv[2]); else cf_read(CONF_FILE); } else if (argc == 2) { cf_read(argv[1]); } else { cf_read(CONF_FILE); } fcache_init(); helpindex_init(); if(mindb) unlink(mudconf.gdbm); if (init_gdbm_db(mudconf.gdbm) < 0) { STARTLOG(LOG_ALWAYS,"INI","LOAD") log_text((char *)"Couldn't load text database: "); log_text(mudconf.gdbm); ENDLOG exit(2); } if (mindb) db_make_minimal(); else if (load_game() < 0) { STARTLOG(LOG_ALWAYS,"INI","LOAD") log_text((char *)"Couldn't load: "); log_text(mudconf.indb); ENDLOG exit(2); } srandom(getpid()); /* Do a consistency check and set up the freelist */ do_dbck(NOTHING, NOTHING, 0); /* set up dumper and reaper */ init_timer(); #ifndef VMS #ifndef XENIX signal(SIGCHLD, reaper); #else /* xenix */ signal(SIGCLD, reaper); #endif #endif VMS /* Reset all the hash stats */ hashreset(&mudstate.command_htab); hashreset(&mudstate.logout_cmd_htab); hashreset(&mudstate.func_htab); hashreset(&mudstate.p_flags_htab); hashreset(&mudstate.t_flags_htab); hashreset(&mudstate.r_flags_htab); hashreset(&mudstate.e_flags_htab); hashreset(&mudstate.attr_name_htab); nhashreset(&mudstate.attr_num_htab); hashreset(&mudstate.vattr_name_htab); nhashreset(&mudstate.vattr_num_htab); hashreset(&mudstate.player_htab); hashreset(&mudstate.news_htab); hashreset(&mudstate.help_htab); hashreset(&mudstate.wizhelp_htab); nhashreset(&mudstate.desc_htab); /* Run everyone's STARTUP attributes. We need to check parents too */ DO_WHOLE_DB(thing) { if (Flags(thing) & GOING) continue; for (lev=0, parent=thing; (Good_obj(parent) && (lev < mudconf.parent_nest_lim)); parent=Parent(parent), lev++) { if (Flags(thing) & STARTUP) { did_it(Owner(thing), thing, 0, NULL, 0, NULL, A_STARTUP, (char **)NULL, 0); break; } } } set_signals(); if (mudconf.rwho_transmit) do_rwho(NOTHING, NOTHING, RWHO_START); /* go do it */ shovechars(mudconf.port); close_sockets(0, (char *)"Going down - Bye"); dump_database(); CLOSE; exit(0); }