/* * NAME: dbloader.c * DESCRIPTION: routines for loading a MOO db file */ # define DEBUG 0 inherit "/std/core"; inherit "/std/string"; inherit "/std/data"; # if DEBUG inherit "/std/vartext"; # else # define var2str(x) "" # endif # include <objects.h> # include <moo/data.h> # include <moo/config.h> # include <dgd/limits.h> # include <dgd/status.h> # include "dbloader.h" # define DELAY 1 # define CHUNK 300 # define SIZE (7 * MAX_STRING_SIZE / 8) # define MEMUSED() (status()[ST_DMEMUSED]) # define MEMFREE() (MEMUSED() < memthresh * 1024 * 1024) object driver; /* the driver object */ int memthresh; /* swapping threshold */ string dbfile; /* name of the db file */ string buffer; /* db file input buffer */ string *lines; /* db file line buffer */ int limit; /* number of lines in line buffer */ int filepos; /* file input position */ int linepos; /* next line pointer */ int filesize; /* total size of db file */ int line; /* current line number in file */ string *verbcode; /* verb code buffer */ int n_objs; /* total number of objects */ int n_progs; /* total number of programs */ int n_players; /* total number of players */ /* * NAME: create() * DESCRIPTION: initialize */ static void create(void) { ::create(); driver = load_object(DRIVER); memthresh = CONFIG->query(CF_MEMORY_THRESH); } /* * NAME: replenish() * DESCRIPTION: read more of the file */ private void replenish(void) { int i; buffer += read_file(dbfile, filepos, SIZE); filepos += SIZE; for (i = strlen(buffer); buffer[--i] != '\n'; ); lines = explode(buffer[.. i], "\n"); limit = sizeof(lines); buffer = buffer[i ..]; linepos = 0; } # define f_int() ((int) f_string()) /* * NAME: f_string() * DESCRIPTION: fetch a string from the db stream */ private string f_string(void) { if (linepos == limit) replenish(); ++line; return lines[linepos++]; } /* * NAME: f_code() * DESCRIPTION: fetch several lines of verb code from the stream */ private string f_code(void) { string line; int i; while ((line = f_string()) != ".") verbcode[i++] = line; return implode(verbcode[.. i - 1], "\n"); } /* * NAME: f_skip() * DESCRIPTION: skip over lines */ private void f_skip(int num) { while (num--) f_string(); } /* * NAME: read_value() * DESCRIPTION: read an arbitrary MOO value from the db stream */ private MOOVAL read_value(void) { int type, i, sz; MOOVAL *list, key, val; string buf, c; mapping map; switch (type = f_int()) { case T_STW: return STW(0); case T_STR: return STR(f_string()); case T_OBJ: return OBJ(f_int()); case T_ERR: return ERR(f_int()); case T_NUM: return NUM(f_int()); case T_LST: list = allocate(sz = f_int()); for (i = 0; i < sz; ++i) list[i] = read_value(); return LST(list); case T_FLT: return FLT(internal2flt(f_string())); case T_TBL: sz = f_int(); map = TNEW(); for (i = 0; i < sz; ++i) { key = read_value(); val = read_value(); TINSERT(map, key, val); } return TBL(map); case T_BUF: sz = f_int(); buf = ""; c = "x"; for (i = 0; i < sz; ++i) { c[0] = f_int(); buf += c; } return BUF(buf); default: error("Unknown value (line " + (string) line + ")"); } } /* * NAME: resume() * DESCRIPTION: continue bootstrapping (after a pause) */ static varargs void resume(string func, mixed args...) { with_lock(func, args...); } /* * NAME: swap() * DESCRIPTION: let DGD swapout */ private void swap(void) { driver->log("Swapping to disk (" + (string) (MEMUSED() / 1024) + "K dynamic memory in use)"); swapout(); } /* * NAME: progress() * DESCRIPTION: return a progress description */ private string progress(int cur, int max) { return (string) (cur * 100 / max) + "%"; } /* * NAME: read_verbs() * DESCRIPTION: load object verb code */ static void read_verbs(int next) { do { int id, vnum; object obj; string tag; mixed *ast; if (next == n_progs) { driver->bootstrap_finished(1); return; } if (sscanf(tag = f_string(), "#%d:%d", id, vnum) != 2) error("Bad verb header: " + tag); if (! (obj = MOOOBJ(id))) error("Verb programmed on nonexistent object: " + tag); debug("Verb " + tag); ast = parser->main(f_code()); if (! ast[0]) { string *msgs; int i; for (msgs = ast[1], i = sizeof(msgs); i--; ) driver->log(msgs[i]); error("Unparsable program " + tag); } obj->bootstrap_verb(vnum, global->compile_verb(ast[1], 1)); if (++next == n_progs || next % (CHUNK / 20) == 0) driver->log("Compiling verbs... " + progress(next, n_progs)); } while (MEMFREE()); call_out("resume", DELAY, "read_verbs", next); swap(); } /* * NAME: cleanup() * DESCRIPTION: last object traversal for cleaning up */ static void cleanup(int next) { do { object obj; if (next == n_objs) { driver->log("Loading " + (string) n_progs + " verb program" + (n_progs == 1 ? "..." : "s...")); call_out("resume", 0, "read_verbs", 0); return; } debug("Clean #" + (string) next); if (obj = MOOOBJ(next)) obj->bootstrap_cleanup(); if (++next == n_objs || next % (CHUNK / 2) == 0) driver->log("Cleaning... " + progress(next, n_objs)); } while (MEMFREE()); call_out("resume", DELAY, "cleanup", next); swap(); } /* * NAME: link_objects() * DESCRIPTION: traverse object hierarchies */ static void link_objects(int next) { do { object obj; if (next == n_objs) { driver->log("Cleaning up..."); call_out("resume", 0, "cleanup", 0); return; } debug("Link #" + (string) next); if (obj = MOOOBJ(next)) obj->bootstrap_links(); if (++next == n_objs || next % (CHUNK / 5) == 0) driver->log("Linking... " + progress(next, n_objs)); } while (MEMFREE()); call_out("resume", DELAY, "link_objects", next); swap(); } /* * NAME: read_objects() * DESCRIPTION: read object descriptions from the db stream */ static void read_objects(int next) { do { int id, i, sz; string rest; mixed *obj, *vdefs, *pdefs, *pvals; if (next == n_objs) { driver->log("Linking object hierarchies..."); call_out("resume", 0, "link_objects", 0); return; } if (sscanf(f_string(), "#%d%s", id, rest) != 2 || id != next) error("Bad object header (line " + (string) line + ", #" + (string) next + " expected)"); if (strlen(rest)) { debug("Recycled object #" + (string) id); global->bootstrap_recycled(id); ++next; continue; } obj = allocate(O__SIZE); obj[O_NAME] = f_string(); f_string(); debug("Object #" + (string) id + " (" + obj[O_NAME] + ")"); obj[O_FLAGS] = f_int(); obj[O_OWNER] = f_int(); obj[O_LOCATION] = f_int(); f_int(); /* contents */ f_int(); /* next */ obj[O_PARENT] = f_int(); f_int(); /* child */ f_int(); /* sibling */ vdefs = allocate(f_int()); for (i = 0, sz = sizeof(vdefs); i < sz; ++i) { vdefs[i] = allocate(V__SIZE); vdefs[i][V_NAME] = f_string(); vdefs[i][V_OWNER] = f_int(); vdefs[i][V_PERMS] = f_int(); vdefs[i][V_PREP] = f_int(); } obj[O_VERBDEFS] = vdefs; vdefs = 0; pdefs = allocate(f_int()); for (i = 0, sz = sizeof(pdefs); i < sz; ++i) pdefs[i] = f_string(); obj[O_PROPDEFS] = pdefs; pdefs = 0; pvals = allocate(f_int()); for (i = 0, sz = sizeof(pvals); i < sz; ++i) { MOOVAL value; pvals[i] = allocate(P__SIZE); value = read_value(); pvals[i][P_VALUE] = (STWP(value) ? value : moo_ext2int(value)); pvals[i][P_OWNER] = f_int(); pvals[i][P_PERMS] = f_int(); } obj[O_PROPVALS] = pvals; pvals = 0; load_object(MOOOBJ_NAME(id))->bootstrap(obj); obj = 0; if (++next == n_objs || next % (CHUNK / 10) == 0) driver->log("Reading objects... " + progress(next, n_objs)); } while (MEMFREE()); call_out("resume", DELAY, "read_objects", next); swap(); } /* * NAME: file_size() * DESCRIPTION: return the size of the file */ private int file_size(string file) { int *stats; stats = get_dir(file)[1]; if (sizeof(stats) != 1) return -1; return stats[0]; } /* * NAME: main() * DESCRIPTION: begin loading procedure */ void main(string file) { dbfile = file; filesize = file_size(dbfile); filepos = 0; buffer = ""; limit = 0; linepos = 0; verbcode = allocate(MAX_ARRAY_SIZE); driver->log("Reading text dump from " + file); if (filesize <= 0 || catch(n_objs = f_int(), n_progs = f_int(), f_int(), n_players = f_int()) != 0) { driver->log("Bad database file"); driver->bootstrap_finished(0); return; } global->bootstrap_max_object(n_objs - 1); driver->log("Loading " + (string) n_objs + " object definition" + (n_objs == 1 ? "..." : "s...")); f_skip(n_players); call_out("resume", 0, "read_objects", 0); }