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