melville/
melville/cmds/
melville/cmds/admin/
melville/data/
melville/data/mail/
melville/data/player/
melville/data/system/
melville/data/user/
melville/doc/functions/
melville/doc/help/
melville/inherit/
melville/log/
melville/obj/
melville/system/auto/
melville/system/player/
melville/system/user/
melville/users/
melville/users/mobydick/
melville/world/
/*
   Driver.c
   The driver object. All driver calls except those that go into users
   come into this object.
   Begun by Mobydick, 5-19-94.
   This object started as a copy of the Dworkin 2.4.5 simulation driver
   object, except that the 2.4.5 preload in initialize() was removed
   and lines to preload user.c and player.c were added, and some small
   changes to privileges were made.
   Since that time it has undergone some more changes, but not many:
   the influence of Dworkin is still strongly marked upon it.
*/

#include <config.h>
#include <options.h>

/*
 * NAME:	initialize
 * DESCRIPTION:	called once at game startup
 * This function loads four objects that need to be loaded right
   away, and starts the memory-cleaning call_out chain.
 */
static void initialize() {
    send_message("Loading user.c\n") ;
    compile_object(USER) ;
    send_message ("Loading player.c\n") ;
    compile_object(PLAYER) ;
    send_message ("Loading the user daemon\n") ;
    compile_object(USERS_D) ;
    send_message ("Loading the start room\n") ;
    compile_object(START) ;
    send_message("Setting up ipc.\n");
    call_out("swapswap", SWAPTIME);
}

/*
 * NAME:	swapswap()
 * DESCRIPTION:	swap out all objects
 */
static void swapswap() {
    call_out("swapswap", SWAPTIME);
    swapout();
    return ;
}

/*
 * NAME:	restored()
 * DESCRIPTION:	re-initialize the system after a restore
   I want this to get all user objects (which are non-interactive
   if we just rebooted) and delete them and the bodies attached
   to them. I want this because it doesn't seem Right to have a
   lot of linkdead bodies lying around if you've just rebooted.
   Depending on the application you have in mind you might not
   want to do this, or you might want to do something else.
 */
static void restored() {

    int i ;
    object body ;
    object *usrs ;

    usrs = USERS_D->query_users() ;
    for (i=0;i<sizeof(usrs);i++) {
	if (body=usrs[i]->query_player()) {
	    catch(body->quit()) ;
	} else {
	    catch(usrs[i]->quit());
	}
    }
    send_message("\nState restored.\nSetting up ipc.\n");
    return ;
}

/*
 * NAME:	path_read()
 * DESCRIPTION:	handle an editor read path
 */

string path_read(string path) {

/* We don't alter the path at all: instead we rely on the editor getting
   the full path name. We just check for permissions. */

    if (!previous_object()->valid_read(path)) return 0 ;
    return path ;
}

/*
 * NAME:	path_write()
 * DESCRIPTION:	handle an editor write path
 */

string path_write(string path) {

/* Same as above: rely on the full path going to the editor. */

    if (!previous_object()->valid_write(path)) return 0 ;
    return path ;
}

/*
 * NAME:	call_object()
 * DESCRIPTION:	translate an object path
 */

object call_object(string path) {

    int i;
    object obj ;

    if (sscanf(path, "../%*s") != 0 || sscanf(path, "%*s/../%*s") != 0) {
	error("Illegal path");
    }
    if ((i=strlen(path)) >= 2 && path[i - 2 ..] == ".c") {
	path = path[0 .. i - 3];
    }
    obj = find_object(path) ;
    if (!obj) obj = compile_object(path) ;
    return obj;
}

/*
 * NAME:	inherit_program()
 * DESCRIPTION:	translate an inherit path
*/

object inherit_program(string file, string path) {

    int i ;
    object tmp ;
    
    if (sscanf(path, "../%*s") != 0 || sscanf(path, "%*s/../%*s") != 0) {
	error("Illegal path");
    }
    if ((i=strlen(path)) >= 2 && path[i - 2 ..] == ".c") {
	path = path[0 .. i - 3];
    }
    tmp = find_object(path) ;
    if (!tmp) tmp = compile_object(path) ;
    return tmp;
}


/*
 * NAME:	path_include()
 * DESCRIPTION:	translate an include path
 */
string path_include(string file, string path) {
    if (path[0] != '/') {
	return file + "/../" + path;
    }
    return path;
}

/*
 * NAME:	compile_object()
 * DESCRIPTION:	(not) used for virtual objects
 */
static object compile_object(string file) {
    return 0;
}

/*
 * NAME:	recompile()
 * DESCRIPTION:	(not) used to recompile objects
 */
static void recompile(object obj) {
    return ;
}

/*
 * NAME:	telnet_connect()
 * DESCRIPTION:	return a user object for a new telnet connection.
 *              The open() function will be called in the returned object.
 */
static object telnet_connect() {
    object user, player, tmp ;

    tmp =find_object(USER);
    if (!tmp) {
        tmp = compile_object(USER);
    }
    user = clone_object(tmp);
    tmp =find_object(PLAYER);
    if (!tmp) {
        tmp = compile_object(PLAYER);
    }
    player = clone_object(tmp);
    user->set_player(player);
    player->set_user(user);

    return user;
}

/* Binary connections are not yet supported. You could use one for
   lots of things; intermud services, FTP, a variety of things.
   But Melville doesn't. */

static object binary_connect() {
    return 0 ;
}

/*
 * NAME:	runtime_error()
 * DESCRIPTION:	log a runtime error
Dworkin's version of this logged the full trace to stderr, and dumped a
one-line description to the user's screen. I prefer the reverse: I'd like
the wizard in question to get all the information possible, and keep the
logs shorter. But you can choose what you want: the two strings long_err
and short_err store the full trace and the one-liner respectively. Store
them and send them where you will...
 */

static void runtime_error(string error, int caught, int ticks) {

    mixed **trace;
    string progname, objname, function, str;
    string long_err, short_err ;
    int i, sz, line, len;
    object player;

    if (caught) {
	return;
    }
    short_err = error + "\n" ;
    long_err = error + "\n" ;
    trace = call_trace();
    if ((sz=sizeof(trace) - 1) != 0) {
	for (i = 0; i < sz; i++) {
	    progname = trace[i][1];
	    function = trace[i][2];
/* I'm not sure what this is catching: bad object to call_other maybe? */
	    if (progname == AUTO && strlen(function) > 3) {
		switch (function[0 .. 2]) {
		case "bad":
		    progname = trace[i - 1][1];
		    function = trace[i - 1][2];
		case "_F_":
                case "_Q_":
		    continue ;
		default:
		    break;
		}
	    }

	    objname  = trace[i][0];
	    line     = trace[i][3];

	    if (line == 0) {
		str = "    ";
	    } else {
		str = "    " + line;
		str = str[strlen(str) - 4 ..];
	    }
	    str += " " + function + " ";
	    len = strlen(function);
	    if (len < 17) {
		str += "                 "[len ..];
	    }
	    str += " " + progname;
	    if (progname != objname) {
		len = strlen(progname);
		if (len < strlen(objname) && progname == objname[.. len - 1]) {
		    str += " (" + objname[len ..] + ")";
		} else {
		    str += " (" + objname + ")";
		}
	    }
	    long_err += str+"\n" ;
	}

	short_err += "Object: " + objname + ", program: " + progname + 
		", line " +line + "\n";

/* Send short_err to the driver log. */
        send_message(short_err) ;

/* If this player is a wizard, send longerr: if not, send the bogus message. */
	player = this_user() ;
	if (player!=0) {
	    player->catch_tell("You suddenly notice a strange distortion in the fabric of space.\n") ;
	}
	if (player != 0 && player->query_wizard()) {
	    player->catch_tell(long_err) ;
	}
    }
/* It better be loaded. It might be better to use get_object(), but
   that causes compile-time problems since auto isn't available yet.
   For now I'm leaving it this way... it can be worked around very
   easily if it turns out to be a problem. */
    find_object(USERS_D)->set_this_player(0) ;
}

/*
   compile_error() is called by the driver when an object fails
   to compile. It passes the name of the file, the line with the
   problem, and a (sometimes opaque) description of the problem.
   compile_error() writes it to the appropriate user and log file.
*/
void compile_error (string file, int line, string err) {

    string *path;
    string logfile ;

    path = explode(file, "/");
    if (path[0] == "users") {
        logfile = "/users/" + path[1] + "/log" ;
    }
    logfile = "/log/log";
    write_file(logfile,"In line "+line+" of "+file+" occurred:\n"+err+"\n") ;
    return ;
}

/*
 * NAME:         interrupt() ;
 * DESCRIPTION:  handle a crash or process kill
 * Log the event, get rid of the users, dump the state if you're into
   that sort of thing, and bail out.
*/

static void interrupt() {

    int i ;
    object *goons ;

    write_file (SHUTDOWN_LOG,MUD_NAME+" crashed at "+ctime(time())+"!\n") ;
    goons = USERS_D->query_users() ;
    for (i=0;i<sizeof(goons);i++) {
        goons[i]->catch_tell("Game driver crashing!\n") ;
        goons[i]->query_player()->quit() ;
    }
#ifdef DUMP_STATE_AT_SHUTDOWN
    dump_state() ;
#endif
    shutdown() ;
    return ;
}

/*
 * NAME:         remove_program() ;
 * DESCRIPTION:  Called when the master program of an object is
                 removed (that is, when the master object and all
		 of its clones have been destroyed)
*/

static void remove_program() {
/* This is here so the mudlib can take action when an object's code is
   removed from memory/disk, usually some kind of resource tracking
   housecleaning. I don't do that, so it does nothing. */
    return ;
}

/* compile_rlimits() and runtime_rlimits() are security that are
   invoked at compile time and runtime, respectively, whenever an
   object invokes rlimits to change the stack depth and tick limit
   used in code. Returning 1 means the change is legal, returning 0
   means it's not. If compile_rlimits returns 0, then runtime doesn't
   get checked.
   For the moment, I'm letting everything be legal cause it's easier.
   These are not static so that another object can call into the
   driver to see if something would be permitted. I don't see much
   use for that but there's no reason to stop it either.
*/

int compile_rlimits (string objname) {
    return 1 ;
}

int runtime_rlimits (object obj, int depth, int ticks) {
    return 1 ;
}