#include <stdio.h> #include <ctype.h> #include <string.h> #include <sys/stat.h> #include "db.h" #include "credits.h" #include "externs.h" #include "softcode.h" extern char *swlm; extern int quiet_reboot; static int db_read_object P((FILE *)); static void putstring P((FILE *, char *)); static char *atr_fgets P((char *, int, FILE *)); static FILE *db_read_file = NULL; static int current_db_version; int loading_db = 0; OBJ *player_list = NULL; OBJ *thing_list = NULL; OBJ *room_list = NULL; OBJ *exit_list = NULL; OBJ **db_list = NULL; int db_top = 0; OBJ *getloc(OBJ *thing) { return(thing->location); } int Wizard(OBJ *thing) { if(Typeof(thing) == TYPE_PLAYER && thing->class == CLASS_DIR) return(1); return(0); } int Guest(OBJ *thing) { if(Typeof(thing) == TYPE_PLAYER && thing->class == CLASS_GUEST) return(1); return(0); } int Typeof(OBJ *thing) { return(thing->flags & TYPE_MASK); } /* This function will rightfully crash if reference_db() hasn't been */ /* called yet. There isn't any need for this function until after that */ OBJ *find_object(int dbref) { if(dbref >= db_top || dbref < 0) return(NULL); return(db_list[dbref]); } int get_free_dbref() { int i; for(i = 0;i < db_top;++i) if(!db_list[i]) break; return(i); } OBJ *new_object(int type, int need_dbref) { OBJ *o; int i = -1; o = (OBJ *)stack_alloc(sizeof(OBJ), 1, 0); o->name = NULL; o->flags = (unsigned long)type; o->flags2 = 0; o->attrlist = NULL; o->location = NULL; o->contents = NULL; o->next_con = NULL; o->pows = NULL; o->exits = NULL; o->next_exit = NULL; o->mail = NULL; o->link = NULL; o->owner = NULL; o->mod_time = 0; o->create_time = now; o->comm_aliases = NULL; o->program = NULL; /* Add the object to the proper list */ switch(type) { case TYPE_PLAYER: o->next = player_list; player_list = o; break; case TYPE_THING: o->next = thing_list; thing_list = o; break; case TYPE_ROOM: o->next = room_list; room_list = o; break; case TYPE_EXIT: o->next = exit_list; exit_list = o; break; default: log_error("new_object() was passed an unrecognized type!"); stack_free(o); return(NULL); } if(need_dbref) { i = get_free_dbref(); o->dbref = i; /* We only want to update db_list if they request a dbref */ /* Otherwise, the db is probably loading and it'll slow stuff down. */ if(i >= db_top) { db_top++; db_list = (OBJ **)stack_realloc(db_list, sizeof(OBJ *)*db_top); } db_list[i] = o; plugin_create(o); } return(o); } static void savelists(FILE *f, OBJ *o) { int num; OBJ *p; num = 0; if(Typeof(o) != TYPE_EXIT) { for(p = o->contents;p;p = p->next_con) num++; putref(f, num); for(p = o->contents;p;p = p->next_con) putref(f, p->dbref); num = 0; } if(Typeof(o) == TYPE_ROOM) { for(p = o->exits;p;p = p->next_exit) num++; putref(f, num); for(p = o->exits;p;p = p->next_exit) putref(f, p->dbref); num = 0; } } static void put_pows(FILE *f, OBJ *o) { int i; char buf[4096]; if(Typeof(o) != TYPE_PLAYER) return; for(i = 0;i < NUM_POWS;++i) { if(!i) sprintf(buf, "%d:%d", powers[i].num, o->pows[powers[i].num]); else sprintf(buf+strlen(buf), " %d:%d", powers[i].num, o->pows[powers[i].num]); } putstring(f, buf); } static void save_programs(FILE *f, OBJ *o) { PROG *p; PROGLINE *pline; int num_programs = 0, num_lines; for(p = o->program;p;p = p->next) num_programs++; fprintf(f, "%d\n", num_programs); for(p = o->program;p;p = p->next) { fprintf(f, "%s\n", p->name); fprintf(f, "%s\n", p->trigger); num_lines = 0; for(pline = p->program;pline;pline = pline->next) num_lines++; fprintf(f, "%d\n", num_lines); for(pline = p->program;pline;pline = pline->next) fprintf(f, "%d %s\n", pline->line, pline->code); } } static void save_atrs(FILE *f, OBJ *o) { ALIST *ptr; for(ptr = o->attrlist;ptr;ptr = ptr->next) { fputc('>', f); putref(f, ptr->attr->atrnum); putstring(f, ptr->value); } } static void save_comm_aliases(FILE *f, OBJ *o) { CALIAS *c; int ctr = 0; for(c = o->comm_aliases;c;c = c->next) ctr++; putref(f, ctr); for(c = o->comm_aliases;c;c = c->next) { fprintf(f, "%s\n", c->command->name); fprintf(f, "%s\n", c->alias); } } static int db_write_object(FILE *f, OBJ *o) { fprintf(f, "&%d\n", o->dbref); fprintf(f, "%ld\n", o->flags); fprintf(f, "%ld\n", o->flags2); putstring(f, o->name); if(Typeof(o) != TYPE_ROOM) { putref(f, o->location->dbref); if(!o->link) putref(f, -1); else putref(f, o->link->dbref); } if(Typeof(o) == TYPE_PLAYER) { put_pows(f, o); putref(f, o->class); } putref(f, o->owner->dbref); fprintf(f, "%ld\n", o->mod_time); fprintf(f, "%ld\n", o->create_time); savelists(f, o); save_programs(f, o); save_comm_aliases(f, o); putref(f, o->creator->dbref); save_atrs(f, o); fprintf(f, "<\n"); return(0); } void db_write(FILE *f) { OBJ *o; int num = 0; for(o = player_list;o;o = o->next) num++; for(o = thing_list;o;o = o->next) num++; for(o = room_list;o;o = o->next) num++; for(o = exit_list;o;o = o->next) num++; fprintf(f, "@MAZEv2-%d\n", DB_VERSION); fprintf(f, "~%d\n", num); fprintf(f, "%s\n", swlm); for(o = player_list;o;o = o->next) db_write_object(f, o); for(o = thing_list;o;o = o->next) db_write_object(f, o); for(o = room_list;o;o = o->next) db_write_object(f, o); for(o = exit_list;o;o = o->next) db_write_object(f, o); write_mail(); save_com_db(); save_maillists(); fflush(f); } static char *getstring_noalloc(FILE *f) { char buf[4096]; atr_fgets(buf, sizeof(buf), f); *last_char(buf) = '\0'; return(stack_string_alloc(buf, 0)); } void getstring(FILE *f, char **ptr) { char buf[4096]; strcpy(buf, getstring_noalloc(f)); *ptr = stack_string_alloc(buf, 1); } static void get_pows(FILE *f, OBJ *o) { char buf[4096]; char *b, *p, *s; if(Typeof(o) != TYPE_PLAYER) { o->pows = NULL; return; } /* Use calloc() so uninitialized powers are set to 0 */ o->pows = (int *)stack_alloc(sizeof(int)*NUM_POWS, 1, 1); strcpy(buf, getstring_noalloc(f)); b = buf; /* update_powers() will be called later to fix this */ if(current_db_version < 1) return; while((s = parse_up(&b, ' '))) { p = strchr(s, ':'); /* Don't bother checking failure. We're screwed anyway */ *p++ = '\0'; o->pows[atoi(s)] = atoi(p); } } static void getlists(FILE *f, OBJ *o) { int num, i; if(Typeof(o) != TYPE_EXIT) { num = getref(f); o->loadinfo->contents = (int *)stack_alloc(sizeof(int)*(num+1), 1, 0); for(i = 0;i < num;++i) o->loadinfo->contents[i] = getref(f); o->loadinfo->contents[num] = -1; } if(Typeof(o) == TYPE_ROOM) { num = getref(f); o->loadinfo->exits = (int *)stack_alloc(sizeof(int)*(num+1), 1, 0); for(i = 0;i < num;++i) o->loadinfo->exits[i] = getref(f); o->loadinfo->exits[num] = -1; } } void free_database() { struct object *o = NULL, *onext; /* Make all lists into one */ if(player_list) for(o = player_list;o->next;o = o->next); if(thing_list) { if(o) o->next = thing_list; for(o = thing_list;o->next;o = o->next); } if(room_list) { if(o) o->next = room_list; for(o = room_list;o->next;o = o->next); } if(exit_list) { if(o) o->next = exit_list; } /* Now player_list should point to the start of EVERYTHING */ for(o = player_list;o;o = onext) { onext = o->next; stack_free(o->name); free_progs(o); atr_free(o); free_mail(o); free_caliases(o); if(o->pows) stack_free(o->pows); stack_free(o); } stack_free(db_list); } /* Read attribute list */ static int get_list(FILE *f, OBJ *obj) { ATTR *atr; ALIST *newattr; char buf[4096]; int c; int atrnum; obj->attrlist = NULL; for(;;) { c = fgetc(f); switch(c) { case '>': /* Read # then string */ atrnum = getref(f); if((atr = find_attr_by_num(atrnum))) { strcpy(buf, getstring_noalloc(f)); atr_add_a(obj, atr, buf); /* This will happen if buf has no length, which really shouldn't happen */ if(!(newattr = find_attr_on_obj(obj, atr))) continue; } else /* Ignore bad attributes */ getstring_noalloc(f); break; case '<': /* End of list */ fgets(buf, 1024, f); return(1); default: log_error(tprintf("Bad character %c on object %d", c, obj->dbref)); return(0); } } } void db_set_read(FILE *f) { db_read_file = f; } static void run_startups() { } static void welcome_descriptors() { DDATA *d; char buf[128]; strcpy(buf, "MAZE reload complete. Welcome back!"); for(d = descriptor_list;d;d = d->next) if(check_state(d, STATE_RELOADCONNECT)) { set_state(d, STATE_CONNECTED); queue_string(d, buf, 1); } flush_all_output(); } static bool check_db_support(char *str) { char *p; if(strncmp(str, "MAZEv2", strlen("MAZEv2"))) return(0); if(!(p = strchr(str, '-'))) current_db_version = 0; else current_db_version = atoi(p+1); if(current_db_version > DB_VERSION) return(0); return(1); } void load_db() { int ctr; int total_db; char buf[4096], buf2[4096], db_version[4096]; int c; c = getc(db_read_file); /* Read DB version number */ if(c != '@') { log_error("Missing DB version!"); exit_nicely(1); } fgets(db_version, sizeof(db_version), db_read_file); if(*db_version) *(db_version+strlen(db_version)-1) = '\0'; if(!check_db_support(db_version)) { log_error("This version of TinyMAZE does not support this database version"); exit_nicely(1); } if(current_db_version != DB_VERSION) { log_important("Old database version. Attempting to convert it..."); log_important("*** START DB CONVERT MESSAGES ***"); } c = getc(db_read_file); if(c != '~') { log_error("Corrupt DB! Looking for '~'"); exit_nicely(1); } total_db = getref(db_read_file); fgets(buf, sizeof(buf), db_read_file); *(buf+strlen(buf)-1) = '\0'; swlm = stack_string_alloc(buf, 1); loading_db = 1; if(!quiet_reboot) { notify_all2("Loading database...", NULL); sprintf(buf2, "%d", total_db); strcpy(buf, comma(buf2)); sprintf(buf2, "%ld", file_size(config.db_name)); notify_all2(tprintf("%s total objects (%s bytes).", buf, comma(buf2)), NULL); flush_all_output(); } if(current_db_version < 1) log_important("All players' powers will be reset to the initial level for their class"); for(ctr = 0;ctr < total_db;++ctr) { if(!quiet_reboot && !(ctr%1000)) { sprintf(buf, "Loading object #%d\t(%3.0f%% Done)", ctr, 100*(float)ctr/(float)total_db); notify_all2(buf, NULL); flush_all_output(); } if(db_read_object(db_read_file) == -1) { log_error("Couldn't load database; shutting down the MAZE."); exit_nicely(136); break; } } if(current_db_version != DB_VERSION) log_important("*** END DB CONVERT MESSAGES ***"); if(!quiet_reboot) { notify_all2("Database object load complete.", NULL); flush_all_output(); } if(!quiet_reboot) { notify_all2("Referencing database...", NULL); flush_all_output(); } reference_db(); if(!quiet_reboot) { notify_all2("Loading mail database...", NULL); flush_all_output(); } read_mail(); load_com_db(); load_maillists(); load_todomotd(0); load_todomotd(1); load_sc_funcs(); if(!quiet_reboot) { notify_all2("Running startups.", NULL); flush_all_output(); } run_startups(); welcome_descriptors(); loading_db = 0; log_important("MAZE online."); } static void read_programs(FILE *f, OBJ *o) { PROG *p; int num_programs, num_lines; int line_num; char buf[4096]; char *b; o->program = NULL; num_programs = getref(f); while(num_programs-- > 0) { p = new_program(o); fgets(buf, sizeof(buf), f); *(buf+strlen(buf)-1) = '\0'; p->name = stack_string_alloc(buf, 1); fgets(buf, sizeof(buf), f); *(buf+strlen(buf)-1) = '\0'; p->trigger = stack_string_realloc(p->trigger, buf); num_lines = getref(f); while(num_lines--) { fgets(buf, sizeof(buf), f); *(buf+strlen(buf)-1) = '\0'; line_num = atoi(buf); if((b = strchr(buf, ' '))) add_prog_line(p, line_num, b+1); } } } static void read_comm_aliases(FILE *f, OBJ *o) { int num; char buf[4096], buf2[4096]; num = getref(f); while(num--) { strcpy(buf, getstring_noalloc(f)); strcpy(buf2, getstring_noalloc(f)); add_comm_alias(o, buf, buf2); } } static void update_powers(OBJ *o) { int i; for(i = 0;i < NUM_POWS;i++) set_pow(o, i, powers[i].init[class_to_list_pos(o->class)]); } static int db_read_object(FILE *f) { int c; int objnum; struct object *o; unsigned long flags; c = getc(f); switch(c) { case '&': objnum = getref(f); flags = atol(getstring_noalloc(f)); o = new_object(flags & TYPE_MASK, 0); if(!o) { log_error("Error creating new object!"); exit_nicely(1); } o->loadinfo = (struct db_load_info *)stack_alloc(sizeof(struct db_load_info), 1, 0); o->dbref = objnum; if(objnum >= db_top) db_top = objnum+1; o->flags = flags; o->flags2 = atol(getstring_noalloc(f)); getstring(f, &o->name); if(Typeof(o) != TYPE_ROOM) { o->loadinfo->location = getref(f); o->loadinfo->link = getref(f); } if(Typeof(o) == TYPE_PLAYER) { get_pows(f, o); o->class = getref(f); if(current_db_version < 1) update_powers(o); } o->loadinfo->owner = getref(f); o->mod_time = atol(getstring_noalloc(f)); o->create_time = atol(getstring_noalloc(f)); getlists(f, o); read_programs(f, o); read_comm_aliases(f, o); o->loadinfo->creator = getref(f); get_list(f, o); break; default: return(-1); } return(0); } /*****************************************************XXX BRG DB CODE XXX*/ #define DB_LOGICAL 0x15 /* atr_fputs - needed to support %r substitution * * does: outputs string <what> on stream <fp>, quoting newlines * * with a DB_LOGICAL (currently ctrl-U). * * added on 4/26/94 by Brian Gaeke (Roodler) */ void atr_fputs(char *what, FILE * fp) { while(*what) { if(*what == '\n') fputc(DB_LOGICAL, fp); fputc(*what++, fp); } } /* atr_fgets - needed to support %r substitution * * does: inputs a string, max <size> chars, from stream <fp>, * * into buffer <buffer>. if a DB_LOGICAL is encountered, * * the next character (possibly a \n) won't terminate the * * string, as it would in fgets. * * added on 4/26/94 by Brian Gaeke (Roodler) */ char *atr_fgets(char *buffer, int size, FILE *fp) { int num_read = 0; char ch, *mybuf = buffer; for(;;) { ch = getc(fp); if(ch == EOF) break; if(ch == DB_LOGICAL) { ch = getc(fp); *mybuf++ = ch; num_read++; continue; } if(ch == '\n') { *mybuf++ = ch; num_read++; break; } *mybuf++ = ch; num_read++; if(num_read > size) break; } *mybuf = '\0'; return(buffer); } /*****************************************************/ void putstring(FILE *f, char *s) { if(s) atr_fputs(s, f); fputc('\n', f); } void do_dump(OBJ *player) { if(!power(player, POW_DB)) { notify(player, perm_denied()); return; } notify_except_flag("|+W|** Saving **", NULL, QUIET); flush_all_output(); fork_and_dump(); notify(player, "Database @dumped."); } void info_db(OBJ *player) { notify(player, tprintf("|+B|DB Version|+W|: |+C|MAZEv2-%d", DB_VERSION)); notify(player, tprintf("|+B|db_top|+W| : |+C|%d", db_top)); notify(player, tprintf("|+B|First Free|+W|: |+C|#%d", get_free_dbref())); notify(player, tprintf("|+B|DB Size|+W| : |+C|%s bytes", comma(tprintf("%ld", file_size(config.db_name))))); notify(player, ""); do_stats(player, ""); }