lpmoo-1.2/etc/
lpmoo-1.2/mudlib/
lpmoo-1.2/mudlib/etc/
lpmoo-1.2/mudlib/include/
lpmoo-1.2/mudlib/include/moo/
lpmoo-1.2/mudlib/lpc/
lpmoo-1.2/mudlib/std/auto/
lpmoo-1.2/mudlib/std/bfuns/
/*
 * NAME:	driver.c
 * DESCRIPTION:	driver/mudlib interface
 */

# define DEBUG  0

inherit "/std/auto";

# undef DGD_NET  /* define iff the DGD networking package is installed */

# include <objects.h>
# include <moo/config.h>
# include <moo/verb.h>
# include <dgd/status.h>
# include <dgd/type.h>

# define VERS_DGD	("DGD version " + status()[ST_VERSION])
# define VERS_LPMOO(x)	("LPMOO version " + (x))

# define panic(x)	do { write(x); shutdown(); return; } while (0)

int	swap_interval;		/* seconds between full swapouts */
int	swap_co_handle;		/* call_out() handle */

int	flags;			/* status flags */
int	dump_time;		/* time of last state dump */
object	global;			/* the global object */

int	ticks_second;		/* how many DGD ticks in one second */

# ifdef DGD_NET
mapping	ports;			/* current open ports */
# endif

# define F_DGD_NET	0x01	/* DGD networking installed? */
# define F_BACKDOOR	0x02	/* enabled backdoor eval? */
# define F_BOOTSTRAP	0x04	/* bootstrapping? */

/*
 * NAME:	write()
 * DESCRIPTION:	output a message
 */
static
void write(string msg)
{
  send_message("[" + ctime(time())[4 .. 15] + "] ** " + msg + "\n");
}

/*
 * NAME:	create()
 * DESCRIPTION:	called when object is created
 */
static
void create(void)
{
  write(VERS_DGD);

  global = load_object(GLOBAL);
}

/*
 * NAME:	notify()
 * DESCRIPTION:	proxy-user / debugging output
 */
void notify(string msg)
{
  send_message(msg + "\n");
}

/*
 * NAME:	log()
 * DESCRIPTION:	write a log message
 */
void log(string msg)
{
  write(msg);
}

/*
 * NAME:	checkpoint()
 * DESCRIPTION:	write a binary state dump
 */
void checkpoint(void)
{
  write("Writing state dump");

  dump_time = time();
  dump_state();
}

# ifdef DGD_NET
/*
 * NAME:	close_ports()
 * DESCRIPTION:	destroy all port objects
 */
static
void close_ports(void)
{
  if (ports)
    {
      object *objs;
      int i;

      objs = map_values(ports);
      for (i = sizeof(objs); i--; )
	if (objs[i])
	  destruct_object(objs[i]);
    }

  ports = ([ ]);
}
# endif

/*
 * NAME:	do_shutdown()
 * DESCRIPTION:	bring the mud down
 */
void do_shutdown(void)
{
# ifdef DGD_NET
  close_ports();
# endif

  checkpoint();

  write("Shutting down");

  shutdown();
}

/*
 * NAME:	initialize()
 * DESCRIPTION:	called once at startup
 */
static
void initialize(void)
{
  object config;

  if (catch(config = load_object(CONFIG)))
    panic("Error loading MOO configuration file; please check");

  write(VERS_LPMOO(config->query(CF_LPMOO_VERSION)));

  flags |= F_BOOTSTRAP;
  DBLOADER->main(config->query(CF_BOOTSTRAP_FILE));
}

/*
 * NAME:	bootstrap_finished()
 * DESCRIPTION:	called when bootstrap has completed
 */
void
bootstrap_finished(int success)
{
  destruct_object(find_object(DBLOADER));
  write(success ? "Bootstrap finished" : "Bootstrap failed");

  do_shutdown();
}

/*
 * NAME:	do_swap()
 * DESCRIPTION:	clean up memory
 */
static
void do_swap(void)
{
  swap_co_handle = call_out("do_swap", swap_interval);
  swapout();
}

/*
 * NAME:	query_net()
 * DESCRIPTION:	return true iff networking is enabled
 */
int query_net(void)
{
  return flags & F_DGD_NET;
}

/*
 * NAME:	query_ports()
 * DESCRIPTION:	return an array of current open ports
 */
int *query_ports(void)
{
# ifdef DGD_NET
  return CONFIG->query(CF_MPORT_LISTENING) ? map_indices(ports) : ({ });
# else
  return ({ });
# endif
}

/*
 * NAME:	path_ed_read()
 * DESCRIPTION:	handle an editor read path
 */
string path_ed_read(string path)
{
  return path;
}

/*
 * NAME:	path_ed_write()
 * DESCRIPTION:	handle an editor write path
 */
string path_ed_write(string path)
{
  return path;
}

/*
 * NAME:	path_object()
 * DESCRIPTION:	translate an object path
 */
string path_object(string path)
{
  return path;
}

/*
 * NAME:	path_inherit()
 * DESCRIPTION:	translate an inherit path
 */
string path_inherit(string file, string path)
{
  return path;
}

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

/*
 * NAME:	compile_object()
 * DESCRIPTION:	used for virtual objects
 */
static
object compile_object(string file)
{
  return MOOOBJ_NAMEP(file) ? global->fetch_object() : 0;
}

/*
 * NAME:	recompile()
 * DESCRIPTION:	used to recompile objects
 */
static
void recompile(object obj)
{
  if (! sscanf(object_name(obj), "/lib/%*s"))
    destruct_object(obj);
}

# ifdef DGD_NET

/*
 * NAME:	connect()
 * DESCRIPTION:	called to initiate an outbound TCP/IP connection
 */
void connect(object obj, string ipaddr, int port, string protocol)
{
  ::connect(obj, ipaddr, port, protocol);
}

/*
 * NAME:	listen()
 * DESCRIPTION:	open a port and attach it to an object
 */
int listen(object listener, int port, string protocol)
{
  object obj;

  if (ports[port] != 0)
    return E_INVARG;

  obj = clone_object(LISTENER);
  obj->configure(protocol == "telnet" ? TELNET : BINARY, listener);

  if (catch(open_port(obj, port, protocol)))
    {
      destruct_object(obj);
      return E_QUOTA;
    }

  ports[port] = obj;

  write("Listening on port " + (string) port + " for " +
	(protocol == "tcp" ? "binary" : protocol) + " connections");

  return E_NONE;
}

/*
 * NAME:	unlisten()
 * DESCRIPTION:	close a port
 */
int unlisten(int port)
{
  object obj;

  obj = ports[port];
  if (obj == 0)
    return E_INVARG;

  destruct_object(obj);
  ports[port] = 0;

  write("Stopped listening on port " + (string) port);

  return E_NONE;
}

# endif

/*
 * NAME:	calibrate_ticks()
 * DESCRIPTION:	determine how many DGD ticks in one second
 */
private
void calibrate_ticks(void)
{
  int timer;

  timer = time();
  while (timer == time())
    ;

  ticks_second = get_exec_cost();

  timer = time() + 2;
  while (timer > time())
    ;

  ticks_second -= get_exec_cost();
  ticks_second /= 2;
}

/*
 * NAME:	get_ticks_second()
 * DESCRIPTION:	return the number of calculated DGD ticks per second
 */
int get_ticks_second(void)
{
  return ticks_second;
}

/*
 * NAME:	restored()
 * DESCRIPTION:	re-initialize the system after a restore
 */
static
void restored(void)
{
  object config;

  flags = 0;

  write(VERS_DGD);
  write("State restored from dump file (" + ctime(dump_time) +")");

  if (config = find_object(CONFIG))
    destruct_object(config);

  if (catch(config = load_object(CONFIG)))
    panic("Error loading MOO configuration file; please check");

  write(VERS_LPMOO(config->query(CF_LPMOO_VERSION)));

  remove_call_out(swap_co_handle);
  if (swap_interval = config->query(CF_SWAP_INTERVAL))
    {
      swap_co_handle = call_out("do_swap", swap_interval);
      write("Swapping out every " + (swap_interval / 60) + " minutes");
    }

  calibrate_ticks();
  write("Calibrated " + (string) ticks_second + " DGD ticks per second");

  if (config->query(CF_EVAL_BACKDOOR))
    {
      flags |= F_BACKDOOR;
      write("Enabled built-in eval backdoor for first connection");
    }

# ifdef DGD_NET

  flags |= F_DGD_NET;

  if (config->query(CF_OUTBOUND_NET))
    write("Outbound networking enabled");

  close_ports();

  if (config->query(CF_MPORT_LISTENING))
    write("Multiple-port listening enabled");
  else
    {
      if (listen(MOOOBJ(0), config->query(CF_TELNET_PORT), "telnet") != E_NONE)
	write("Unable to open telnet port");

      if (listen(MOOOBJ(0), config->query(CF_BINARY_PORT), "tcp") != E_NONE)
	write("Unable to open binary port");
    }

# else

  write("Built-in networking only");

# endif

  catch(global->restart(dump_time));
  write("Multi-user mode");
}

/*
 * NAME:	init_connection()
 * DESCRIPTION:	prepare a connection object
 */
object init_connection(object conn, object listener, int port)
{
  if (flags & F_BOOTSTRAP)
    conn->set_bootstrap(1);

  if (flags & F_BACKDOOR)
    {
      flags &= ~F_BACKDOOR;
      conn->set_backdoor(1);
    }

  conn->set_listener(listener);
  conn->set_port_number(port);

  return conn;
}

/*
 * NAME:	telnet_connect()
 * DESCRIPTION:	called when a user connects; return a player object
 */
object telnet_connect(void)
{
  return init_connection(clone_object(TELNET), MOOOBJ(0), 0);
}

/*
 * NAME:	binary_connect()
 * DESCRIPTION:	handle binary connections
 */
object binary_connect(void)
{
  return init_connection(clone_object(BINARY), MOOOBJ(0), 0);
}

/*
 * NAME:	compile_log()
 * DESCRIPTION:	(don't) return the name of a compile time error log
 */
static
string compile_log(string file)
{
  return 0;
}

/*
 * NAME:	log_error()
 * DESCRIPTION:	log a runtime error
 */
static
void log_error(string error, int caught)
{
  mixed **trace, *frame;
  int i, sz, line;
  string obj, prog, func;
  object user;

  user = this_user();

  if (! DEBUG && error[0 .. 1] == "K!")		/* killed or suspended */
    {
      if (user)
	user->finish();
      return;
    }

  if (! DEBUG && caught)
    return;

  sz = sizeof(trace = call_trace()) - 1;

  /* locate a MOO call frame */
  for (i = sz - 1, frame = trace[i];
       i-- && (sizeof(frame) < 7 ||
	       typeof(frame[6]) != T_ARRAY ||
	       sizeof(frame[6]) != I_LINK + 1);
       frame = trace[i]);

  if (i >= 0 && ! caught)
    {
      mixed *info;
      int do_traceback;

      info = frame[6];

      if (info[I_VERBOBJ])
	info[I_LINENO] = info[I_VERBOBJ]->get_lineno();
      else
	info[I_LINENO] = -1;

      global->remove_refs(info);

      if (error[0 .. 1] == "E!")
	{
	  error = error[2 ..];
	  do_traceback = 1;
	}
      else if (strlen(error) > 11 && error[0 .. 11] == "Maximum exec")
	{
	  error = "Task ran out of ticks";
	  do_traceback = 1;
	}
      else if (error == "Array too large")
	{
	  error = "List too long";
	  do_traceback = 1;
	}
      else if (strlen(error) > 16 && error[0 .. 16] == "Mapping too large")
	{
	  error = "Table too large";
	  do_traceback = 1;
	}
      else if (error == "Too many callouts")
	{
	  error = "Too many forked tasks";
	  do_traceback = 1;
	}
      else if (error == "String too long")
	do_traceback = 1;

      if (do_traceback)
	{
	  global->traceback(info, error);
	  if (user)
	    user->finish();
	  return;
	}
    }

  if ((flags & F_BOOTSTRAP) &&
      strlen(error) > 11 && error[0 .. 11] == "Maximum exec")
    return;

  write(error + (caught ? " (caught)" : ""));

  for (i = 0; i < sz; ++i)
    {
      string str;
      int len;

      frame = trace[i];

      obj   = frame[0];
      prog  = frame[1];
      func  = frame[2];
      line  = frame[3];

      if (line == 0)
	str = "    ";
      else
	{
	  str = "    " + line;
	  str = str[strlen(str) - 4 ..];
	}

      str += " " + func + " ";
      len = strlen(func);
      if (len < 17)
	str += "                 "[len ..];

      str += prog;
      if (prog != obj)
	{
	  len = strlen(prog);
	  if (len < strlen(obj) && prog == obj[.. len - 1])
	    str += " (" + obj[len ..] + ")";
	  else
	    str += " (" + obj + ")";
	}

      send_message(str + "\n");
    }

  if (flags & F_BOOTSTRAP)
    bootstrap_finished(0);

  if (user && ! caught)
    {
      user->notify("** Sorry, an internal error occurred: " + error);
      user->notify("** Object: " + obj + ", program: " + prog +
		   (line == 0 ? ", function \"" + func + "\"" :
		    ", line " + line));
      user->finish();
    }
}