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