MudOSa4DGD/
MudOSa4DGD/bin/
MudOSa4DGD/data/
MudOSa4DGD/doc/
MudOSa4DGD/doc/driver/
MudOSa4DGD/doc/efun/bitstrings/
MudOSa4DGD/doc/efun/command/
MudOSa4DGD/doc/efun/communication/
MudOSa4DGD/doc/efun/heart_beat/
MudOSa4DGD/doc/efun/interactive/
MudOSa4DGD/doc/efun/inventory/
MudOSa4DGD/doc/efun/living/
MudOSa4DGD/doc/efun/mappings/
MudOSa4DGD/doc/efun/strings/
MudOSa4DGD/doc/efun/uid/
MudOSa4DGD/doc/funs/
MudOSa4DGD/doc/language/
MudOSa4DGD/mudlib/dgd/doc/
MudOSa4DGD/mudlib/dgd/lib/include/dgd/
MudOSa4DGD/mudlib/dgd/lib/std/
MudOSa4DGD/mudlib/dgd/lib/sys/
MudOSa4DGD/mudlib/dgd/log/
MudOSa4DGD/mudlib/log/
MudOSa4DGD/mudlib/std/include/
MudOSa4DGD/mudlib/std/obj/
/*
 * global.c
 *
 * The global object holding global values and functions for the
 * DriverLib to access.
 *
 * (C) Frank Schmidt, Jesus@NorseMUD
 *
 */

#define __GLOBAL
#include <driver.h>

/* special care in this object */
#ifdef MUDOS_THIS_PLAYER_1
# undef this_player1
#endif



/*
 *  this_player
 */

/* the player giving the command, and the player acting it */
private object command_giver, command_actor;

/* set command actor */
int set_this_player(object player) {
  if (!DRIVER_PRIV()) {
    illegal();
    return 0;
  }
  command_actor = player;
  return 1;
}

/* set the command giver, the player behind it all */
int set_this_player1(object player) {
  if (!DRIVER_PRIV()) {
    illegal();
    return 0;
  }
  command_giver = player;
  return 1;
}

object this_player() {
  /*if (!AUTO_PRIV()) {
    illegal();
    return 0;
    }*/
  return command_actor;
}

object this_player1() {
  /*if (!AUTO_PRIV()) {
    illegal();
    return 0;
    }*/
  return command_giver;
}


/* call init in ob2 with ob1 as this_player() */
void do_init(object ob1, object ob2) {
  if (!AUTO_PRIV()) {
    illegal();
    return;
  }
  command_giver = ob1;
  catch(call_other(ob2, "__call_local", __INIT_FUNC));
}



/*
 *  query_verb
 */

/* The Verb (tm) */
private string current_verb;

int set_verb(string str) {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }
  current_verb = str;
  return 1;
}

string query_verb() {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }
  return current_verb;
}



/*
 *  genuine_command
 */

/* store if current command is genuine from user (security) */
private int genuine_comm;

int set_genuine_command(int i) {
  /* require auto/driver-access for setting it */
  if (DRIVER_PRIV()) {
    genuine_comm = i;
    return 1;
  }
  illegal();
}

int query_genuine_command() {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  return genuine_comm;
}



/*
 *  notify_fail
 */

/* notify_fail variable */
private string notify_string;

int notify_fail(string str) {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  notify_string = str;
  return 0;
}

string query_notify_fail() {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  return notify_string;
}



#ifdef MUDOS_HEART_BEAT

/*
 *  set_heart_beat
 */

/* info in hb structure */
#define HBI_OB         0
#define HBI_INTERVAL   1
#define HBI_COUNTER    2

/* handle of hb call_out & internal index in call_out */
private int hb_handle;
private int hb_index;
/* list of heart beat objects */
private mixed **hb_objects;


/* internal func to get index of an object in struct-array */
private int find_hb_index(object ob) {
  if (ob) {
    int i;
    for (i=a_sizeof(hb_objects); --i >= 0; ) {
      if (ob == hb_objects[i][HBI_OB]) {
	/* get out */
	break;
      }
    }
    return i;
  }
  return -1;
}


/* set the heart beat in any given object */
int set_heart_beat(object ob, int time) {
  int i;
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  i=find_hb_index(ob);
  /* Set the new heartbeat time */
  if (time > 0) {
    if (i >= 0)
      hb_objects[i] = ({ ob, time, time });
    else {
      /* hb_objects += ({ ... }) creates recursion in __do_heart_beat()
	 by set_heart_beat(0);set_heart_beat(1) during heart_beat() */
      hb_objects = ({ ({ ob, time, time }) }) + hb_objects;
    }
    return time;
  }
  else {
    /* delete heart beat instance */
    if (i >= 0) {
      hb_objects = array_delete(hb_objects, i);
      /* update handler's internal index if affected, to prevent bugs when
       an object changes its heart_beat settings during a heart_beat */
      if (hb_index >= i)
	--hb_index;
    }
    return 0;
  }
}


/* query the heart beat in any given object */
int query_heart_beat(object ob) {
  int i;
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  if ((i=find_hb_index(ob)) >= 0)
    return hb_objects[i][HBI_INTERVAL];
  return 0;
}


/* do the global heart beat */
static void __do_heart_beat() {
#if INFORM_DRIVER > 40
  DRIVER->notify("<HB>");
#endif

  /* NB: array of heartbeats might change during the loop!! */
  for (hb_index=0; hb_index < a_sizeof(hb_objects); ++hb_index) {
    /* do the beat in every object on the list */
    int e;
    mixed *elem;
    string ret;

    elem = hb_objects[hb_index];
    e = elem[HBI_COUNTER]-1;
    if (e <= 0) {
      /* Tick! */
      object ob;
      ob = elem[HBI_OB];
      if (ob) {
	/* set correct this_players */
	command_giver = 0;
#ifdef MUDOS_LIVING
	if (living(ob)) {
	  /* prevent any security access through heart beats */
	  command_actor = ob;
	}
	else
#endif
	  {
	    command_actor = 0;
	  }

	RLIMITS_ON();
	ret = catch(call_other(ob, "__call_local", __HEART_BEAT_FUNC));
	RLIMITS_OFF();

	/* reset this_player() */
	command_actor = 0;

	/* <hb_index> may have changed drastically, check if we still
	   have the same heart_beat */
	if (hb_index >= 0 && hb_objects[hb_index][HBI_OB] == ob) {
	  if (!ret) {
	    /* ok: reset counter */
	    hb_objects[hb_index][HBI_COUNTER] = elem[HBI_INTERVAL];
	  }
	  else {
	    /* turn off heartbeat due to error */
	    catch(error("Heartbeat turned off in object "+object_name(ob)+"."));
	    hb_objects = array_delete(hb_objects, hb_index);
	    --hb_index;
	  }
	}
      }
      else {
	/* delete 0-objects and update internal index */
	hb_objects = array_delete(hb_objects, hb_index);
	--hb_index;
      }
    }
    else {
      /* update counter */
      hb_objects[hb_index][HBI_COUNTER] = e;
    }
  }
  /* let's get another heart beat soon */
  hb_handle = dgd_call_out("__do_heart_beat", HEART_BEAT_INTERVAL);
}

#endif /* MUDOS_HEART_BEAT */



/*
 *  call_outs
 */

/* array indexes in call_out-info */
#define COI_OB     0
#define COI_FUNC   1
#define COI_TIME   2
#define COI_ARG    3
#define COI_TP     4

/* handle of call_out call_out & the sorted call_out table */
private int co_handle;
private mixed **co_objects;


/* internal func to get index of an object & func in struct-array */
private int find_co_index(object ob, string func) {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  if (ob) {
    int i;
    for (i=a_sizeof(co_objects); --i >= 0; ) {
      if (ob == co_objects[i][COI_OB] && 
	  func == co_objects[i][COI_FUNC])
	/* get out */
	break;
    }
    return i;
  }
  return -1;
}


/* issue a call_out */
varargs void call_out(object ob, string func, int delay, mixed args...) {
  if (!AUTO_PRIV()) {
    illegal();
    return;
  }

  /* add a new call_out */
  if (ob) {
    int i, time;
    if (delay < 0)
      delay = 0;
    time = time() + delay;
    for (i=a_sizeof(co_objects); --i >= 0; ) {
      if (time <= co_objects[i][COI_TIME])
	/* sorted */
	break;
    }
    co_objects = insert_array(co_objects, ({ ({ ob, func, time, args, this_player() }) }), i+1);
  }
}


/* find a call_out and return delay */
int find_call_out(object ob, string func) {
  int i;
  if (!AUTO_PRIV()) {
    illegal();
    return -1;
  }

  if (ob && (i=find_co_index(ob, func)) >= 0)
    return co_objects[i][COI_TIME]-time();
  return -1;
}


/* remove a call_out and return delay */
int remove_call_out(object ob, string func) {
  int i;
  if (!AUTO_PRIV()) {
    illegal();
    return -1;
  }

  if (ob && (i=find_co_index(ob, func)) >= 0) {
    /* do the removal */
    int delay;
    delay = co_objects[i][COI_TIME]-time();
    co_objects = array_delete(co_objects, i);
    return delay;
  }
  return -1;
}


/* do the global call out */
static void __do_call_out() {
  int sz, time, *tmplimits;
  string ret, obfile;
  mixed *elem;
  object ob;
  mapping rem_rlimits;

#if INFORM_DRIVER > 40
  DRIVER->notify("<CO>");
#endif

  /* static time throughout the loop */
  time = time();

  /* keep track of remaining rlimits on each call_out, limiting the
     next call_out to the same object */
  rem_rlimits = ([ ]);

  while ((sz=a_sizeof(co_objects)) &&
	 (elem=co_objects[sz-1])[COI_TIME] <= time) {
    /* get rid of old element */
    co_objects = array_delete(co_objects, sz-1);

    /* object not dested yet? */
    if (ob=elem[COI_OB]) {
      /* set correct this_players */
      command_giver = 0;
      command_actor = elem[COI_TP];

      /* get remaining rlimits on current object or default */
      if (!(tmplimits=rem_rlimits[obfile=object_name(ob)]))
	tmplimits = ({ DEFAULT_CALL_STACK, DEFAULT_TICK_LIMIT });


      /* call the call_out with correct rlimits for current object */
      NEW_RLIMITS_ON(tmplimits[0], tmplimits[1]);
      ret = catch(call_other(ob, "__call_local", 
			     elem[COI_FUNC], elem[COI_ARG]...));
      /* update values for remaining rlimits for <ob> */
      catch(rem_rlimits[obfile] = ({ status()[ST_STACKDEPTH], 
				     status()[ST_TICKS] }));
      NEW_RLIMITS_OFF();


      /* reset this_player() */
      command_actor = 0;

      if (ret) {
	/* error detected */
      }
    }
  }

  /* let's get another call_out check soon */
  co_handle = dgd_call_out("__do_call_out", CALL_OUT_INTERVAL);
}


/* return info about all pending call_outs */
mixed **call_out_info() {
  int i, sz, time;
  mixed **info;

  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  /* convert time to delay in whole table */
  info = allocate(sz=a_sizeof(co_objects));
  time = time();
  for (i=sz; --i >= 0; ) {
    mixed *tmp;
    tmp = co_objects[sz-i-1];
    /* get rid of this_player */
    tmp = tmp[0..3];
    tmp[COI_TIME] -= time;
    info[i] = tmp;
  }
  return info;
}



/* 
 * objects() 
 */

/* rate between each objects clean-up (to save CPU) */
#define OBS_CLEAN_UP_RATE 100

private object *object_a;
private int obs_clean_up;

object *objects() {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  /* clean-up and return result */
  return (object_a -= ({ 0 }));
}


void add_object(object ob) {
  if (!AUTO_PRIV()) {
    illegal();
    return;
  }

  /* clean up? */
  if (--obs_clean_up <= 0) {
    obs_clean_up = OBS_CLEAN_UP_RATE;
    object_a -= ({ 0 });
  }
  /* add new object */
#if 0
  object_a -= ({ ob });
#endif
  object_a += ({ ob });
}



/* 
 * calling reset() in all mudlib objects 
 */

#ifdef MUDOS_RESET

/* callout handle */
int rs_handle;

static void __do_reset() {
  object *obs, ob;
  string ret;
  int i;

  for (i=a_sizeof(obs=object_a); --i >= 0; ) {
    if (ob=obs[i]) {
      RLIMITS_ON();
      ret = catch(call_other(ob, "__call_local", __RESET_FUNC));
      RLIMITS_OFF();
      if (ret) {
	/* error detected */
      }
    }
  }

  /* do it again */
  rs_handle = dgd_call_out("__do_reset", RESET_INTERVAL);
}

#endif



/*
 *  players & livings
 */

/* mapping & array over livings */
private mapping living_m;
private object *living_a;

/* mapping & array over players */
private mapping player_m;
private object *player_a;


void add_user(object user) {
  if (!DRIVER_PRIV()) {
    illegal();
    return;
  }
  player_a -= ({ user });
  player_a += ({ user });
}

void remove_user(object user) {
  if (!DRIVER_PRIV()) {
    illegal();
    return;
  }
  player_a -= ({ user });
}


int userp(object ob) {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }
  return member_array(ob, player_a) != -1;
}


object *users() {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }
  return (player_a -= ({ 0 }));
}



#ifdef MUDOS_LIVING

varargs void set_living_name(string name, object ob, string oldname) {
  object *obs;
  if (!AUTO_PRIV()) {
    illegal();
    return;
  }

  /* remove old living name for <ob> */
  if (oldname) {
    if (arrayp(living_m[oldname]))
      living_m[oldname] -= ({ 0, ob });
    
#ifdef MUDOS_USER_LIVING_NAME
    /* remove user/player living oldname */
    if (arrayp(player_m[oldname]))
      player_m[oldname] -= ({ 0, ob });
#endif
  }


  if (!name) {
    /* delete living from arrays */
    living_a -= ({ ob });
    return;
  }

  /* add living name */
  if (arrayp(obs=living_m[name]) && a_sizeof(obs)) {
    /* clean-up */
    obs -= ({ 0, ob });
    obs += ({ ob });
    living_m[name] = obs;
  }
  else {
    /* adding a new living name */
    living_m[name] = ({ ob });
  }
  living_a -= ({ ob });
  living_a += ({ ob });

#ifdef MUDOS_USER_LIVING_NAME
  /* add user/player living name _only_ */
  if (interactive(ob)) {
    if (arrayp(obs=player_m[name]) && a_sizeof(obs)) {
      /* clean-up */
      obs -= ({ 0, ob });
      obs += ({ ob });
      player_m[name] = obs;
    }
    else {
      /* adding a new player name */
      player_m[name] = ({ ob });
    }
  }
#endif
}


varargs object find_player(string name, int i) {
  object *obs;
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  if (arrayp(player_m[name])) {
    if (a_sizeof(obs=(player_m[name]-=({0})))) {
      /* find ith player by name */
      object obi;
      while (i < a_sizeof(obs)) {
	obi = obs[i];
#if 0
	if (member_array(obi, player_a) >= 0)
#else
        if (interactive(obi))
#endif
	  return obi;
	else
	  /* clean-up old non-players */
	  player_m[name] = obs = array_delete(obs, i);
      }
      return 0;
    }
    else
      player_m = map_delete(player_m, name);
  }
  return 0;
}


varargs object find_living(string name, int i) {
  object *obs;
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }

  if (arrayp(living_m[name])) {
    if (a_sizeof(obs=(living_m[name]-=({0})))) {
      /* find ith living by name */
      object obi;
      while (i < a_sizeof(obs)) {
	obi = obs[i];
#if 0
	if (member_array(obi, living_a) >= 0)
#else
        if (living(obi))
#endif
	  return obi;
	else
	  /* clean-up old non-livings */
	  living_m[name] = obs = array_delete(obs, i);
      }
      return 0;
    }
    else
      living_m = map_delete(living_m, name);
  }
  return 0;
}


object *livings() {
  if (!AUTO_PRIV()) {
    illegal();
    return 0;
  }
  return (living_a -= ({ 0 }));
}

#endif



/*
 *  filter_all_present() - does all_present()
 */

#ifdef all_inventory

/* return array of all matching <id> in <env> */
varargs object *filter_all_present(string id, object env) {
  return filter_array(all_inventory(env), "_all_present", this_object(), id);
}

#endif



/*
 *  global sort techniques
 */

int sort_alpha_asc(string str1, string str2) {
  return strcmp(str1, str2);
}

int sort_alpha_des(string str1, string str2) {
  return strcmp(str2, str1);
}

int sort_num_asc(int int1, int int2) {
  return int1-int2;
}

int sort_num_des(int int1, int int2) {
  return int2-int1;
}

int sort_float_asc(float f1, float f2) {
  return (int)(f1-f2);
}

int sort_float_des(float f1, float f2) {
  return (int)(f2-f1);
}


/* filter_array func for all_present */
int _all_present(object ob, string theid) {
  return (int)call_other(ob, __ID_FUNC, theid);
}


#ifdef MUDOS_LIVING

/* filter for livings for move() */
int isliving(object ob) {
  return living(ob);
}

#endif



/*
 *  Initialize all "local" global vars
 */

static void __CREATE_DEF() {
  if (object_name(this_object()) != GLOBAL) {
    /* self-destruct */
    illegal();
    __DESTROY_DEF();
    return;
  }

  ::__CREATE_DEF();
#ifdef MUDOS_USER_ID
  seteuid(getuid());
#endif

#ifdef MUDOS_HEART_BEAT  
  /* heart beat data and turn on heart beat + internal index */
  hb_objects = ({ });
  hb_handle = dgd_call_out("__do_heart_beat", HEART_BEAT_INTERVAL);
  hb_index = -1;
#endif
  
  /* call_out data */
  co_objects = ({ });
  co_handle = dgd_call_out("__do_call_out", CALL_OUT_INTERVAL);

#ifdef MUDOS_RESET
  /* reset() callout */
  rs_handle = dgd_call_out("__do_reset", RESET_INTERVAL);
#endif
  
  /* objects, player & living tables */
  object_a = ({ });
  player_m = ([ ]);
  player_a = ({ });
  living_m = ([ ]);
  living_a = ({ });
  
  /* restart strings */
  notify_string = "<Original notify fail>";
  current_verb = "<Original verb>";
  genuine_comm = 0;
}


/* unmovable */
varargs int __MOVE_DEF(object dest) {
  illegal();
}


static void __DESTROY_DEF() {
  /* prevent destruction if no access */
  if (!DRIVER_PRIV()) {
    illegal();
    return;
  }
  ::__DESTROY_DEF();
}



/* prevent cloning of global object! */
int __QUERY_PREVENT_CLONE_DEF() {
  return 1;
}