#include <kernel/kernel.h>
#include <phantasmal/timed.h>
#include <phantasmal/log.h>
#include <phantasmal/lpc_names.h>
#include <type.h>
mapping* per_queue;
mixed* per_call_out;
/***********************************************************************
* TIMED handles call_outs on behalf of MUD mobiles and objects.
* There is a small maximum total number of call_outs available, and
* this uses them sparingly. TimeD also gives a heartbeat function
* interface, which is more familiar to long-time LPC users.
***********************************************************************/
private mixed* delay_tab;
/* Prototypes */
void upgraded(varargs int clone);
private void priv_start_call_out(int how_often);
private void priv_stop_call_out(int how_often);
static void create(void) {
upgraded();
}
void upgraded(varargs int clone) {
mixed *tmp_queue, *tmp_call_outs;
int size, ctr;
if(!SYSTEM() && !COMMON())
return;
/* Allocate or reallocate the queue of periodic
call_outs and the call_out numbers if necessary. */
if(!per_queue || sizeof(per_queue) != TIMED_HIGHEST) {
tmp_queue = allocate(TIMED_HIGHEST);
tmp_call_outs = allocate(TIMED_HIGHEST);
if(per_queue && (sizeof(per_queue) < TIMED_HIGHEST)) {
size = sizeof(per_queue);
} else if(per_queue) {
size = TIMED_HIGHEST;
} else {
size = 0;
}
for(ctr = 0; ctr < size; ctr++) {
tmp_queue[ctr] = per_queue[ctr];
tmp_call_outs[ctr] = per_call_out[ctr];
}
/* Initialize any extra slots as empty */
for(ctr = size; ctr < TIMED_HIGHEST; ctr++) {
tmp_queue[ctr] = ([ ]);
tmp_call_outs[ctr] = 0;
}
per_queue = tmp_queue;
per_call_out = tmp_call_outs;
}
/* Hardcode a MUD minute to 20 real seconds right now,
just to test. */
delay_tab = allocate(TIMED_HIGHEST);
delay_tab[TIMED_HALF_MINUTE] = 30;
delay_tab[TIMED_TEN_MINUTES] = 600;
delay_tab[TIMED_ONE_HOUR] = 3600;
delay_tab[TIMED_ONE_DAY] = 3600 * 24;
}
void set_heart_beat(int how_often, string funcname, mixed args...) {
if(!SYSTEM() && !COMMON() && !GAME())
return;
if((how_often >= TIMED_HIGHEST) || (how_often <= 0)) {
error("Illegal value for how_often in TIMED::periodic_call_out!");
}
if(per_queue[how_often][object_name(previous_object())]) {
error("Already have a heart_beat registered for "
+ object_name(previous_object()) + "!\n"
+ "Unregister it first!");
}
LOGD->write_syslog("Setting up periodic call_out in TIMED", LOG_VERBOSE);
per_queue[how_often][object_name(previous_object())]
= ({ funcname, args });
if(per_call_out[how_often] <= 0) {
priv_start_call_out(how_often);
}
}
private void stop_object_call_out(int how_often, string objname) {
per_queue[how_often][objname] = nil;
if(map_sizeof(per_queue[how_often]) == 0) {
priv_stop_call_out(how_often);
}
}
void stop_heart_beat(int how_often) {
if(!SYSTEM() && !COMMON() && !GAME())
return;
stop_object_call_out(how_often, object_name(previous_object()));
}
private void priv_start_call_out(int how_often) {
if(per_call_out[how_often] > 0) {
LOGD->write_syslog("Call_out #" + how_often
+ " is already started in TIMED::priv_start_call_out!",
LOG_WARNING);
return;
}
if(delay_tab[how_often] && delay_tab[how_often] > 0) {
per_call_out[how_often] = call_out("__priv_co_hook", delay_tab[how_often],
how_often);
} else {
LOGD->write_syslog("Trying to schedule zero-time callout!", LOG_ERR);
}
if(per_call_out[how_often] <= 0) {
LOGD->write_syslog("Can't schedule call_out # " + how_often
+ " in TIMED::priv_start_call_out!", LOG_ERROR);
per_call_out[how_often] = 0;
}
}
private void priv_stop_call_out(int how_often) {
int ret;
if(per_call_out[how_often] <= 0) {
LOGD->write_syslog("Call_out #" + how_often
+ " is already stopped in TIMED::priv_stop_call_out!",
LOG_WARNING);
return;
}
ret = remove_call_out(per_call_out[how_often]);
if(ret < 0) {
LOGD->write_syslog("Call_out #" + how_often + ", handle #"
+ per_call_out[how_often]
+ " doesn't exist in TIMED::priv_stop_call_out!",
LOG_WARNING);
}
per_call_out[how_often] = 0;
}
void __priv_co_hook(int how_often) {
int ctr;
mixed *keys, *tmp;
object call_obj;
if(!KERNEL()) {
error("TIMED::__priv_co_hook can be called only by KERNEL code!");
}
/* Schedule the next call */
per_call_out[how_often] = 0;
priv_start_call_out(how_often);
keys = map_indices(per_queue[how_often]);
for(ctr = 0; ctr < sizeof(keys); ctr++) {
tmp = per_queue[how_often][keys[ctr]];
call_obj = find_object(keys[ctr]);
if(call_obj) {
call_other(call_obj, tmp[0], tmp[1]...);
} else {
LOGD->write_syslog("Can't find object " + keys[ctr] + " to call!",
LOG_WARN);
stop_object_call_out(how_often, keys[ctr]);
}
}
}