/* (c) Copyright by Anders Chrigstroem 1993, All rights reserved */
/* Permission is granted to use this source code and any executables
* created from this source code as part of the CD Gamedriver as long
* as it is not used in any way whatsoever for monetary gain. */
#include <setjmp.h>
#include <memory.h>
#include <signal.h>
#include <stdio.h>
#include "config.h"
#include "lint.h"
#include "interpret.h"
#include "exec.h"
#include "object.h"
#include "mudstat.h"
#define FUNC_NAME(ob, cop)\
(access_program(ob->prog->inherit[cop->inherit].prog),\
ob->prog->inherit[cop->inherit].prog->functions[cop->fun].name)
extern char *xalloc(), *string_copy();
extern jmp_buf error_recovery_context;
extern int error_recovery_context_exists, eval_cost, s_flag;
extern struct svalue *sp;
struct call
{
unsigned short fun;
unsigned short inherit;
int reload;
unsigned int when;
int id;
struct call *next;
struct object *command_giver;
struct vector *v;
};
static struct object *call_outs[NUM_SLOTS];
unsigned int now;
unsigned int last;
unsigned int is_now;
int num_call;
int call_out_size;
int call_id = 1;
static void
timer_alarm()
{
extern int interupted;
extern int current_time;
#ifdef USE_SWAP
static int acc_swap_out_prog[60];
static int acc_swap_out_obj[60];
static int acc_swap_in_prog[60];
static int acc_swap_in_obj[60];
static int acc2_swap_out_prog[60];
static int acc2_swap_out_obj[60];
static int acc2_swap_in_prog[60];
static int acc2_swap_in_obj[60];
extern int swap_out_obj_sec, swap_out_obj_min, swap_out_obj;
extern int swap_out_prog_sec, swap_out_prog_min, swap_out_prog;
extern int swap_in_obj_sec, swap_in_obj_min, swap_in_obj;
extern int swap_in_prog_sec, swap_in_prog_min, swap_in_prog;
extern int swap_out_obj_hour, swap_in_obj_hour;
extern int swap_out_prog_hour, swap_in_prog_hour;
if (now % TIME_RES == 0)
{
int sec = now / TIME_RES % 60;
swap_out_obj_min = swap_out_obj_min - acc_swap_out_obj[sec] +
(acc_swap_out_obj[sec] = swap_out_obj_sec = swap_out_obj);
swap_out_obj = 0;
swap_out_prog_min = swap_out_prog_min - acc_swap_out_prog[sec] +
(acc_swap_out_prog[sec] = swap_out_prog_sec = swap_out_prog);
swap_out_prog = 0;
swap_in_obj_min = swap_in_obj_min - acc_swap_in_obj[sec] +
(acc_swap_in_obj[sec] = swap_in_obj_sec = swap_in_obj);
swap_in_obj = 0;
swap_in_prog_min = swap_in_prog_min - acc_swap_in_prog[sec] +
(acc_swap_in_prog[sec] = swap_in_prog_sec = swap_in_prog);
swap_in_prog = 0;
if (now % (TIME_RES * 60) == 0)
{
int min = now / (TIME_RES * 60) % 60;
swap_out_obj_hour = swap_out_obj_hour - acc2_swap_out_obj[min] +
(acc2_swap_out_obj[min] = swap_out_obj_min);
swap_out_prog_hour = swap_out_prog_hour - acc2_swap_out_prog[min] +
(acc2_swap_out_prog[min] = swap_out_prog_min);
swap_in_obj_hour = swap_in_obj_hour - acc2_swap_in_obj[min] +
(acc2_swap_in_obj[min] = swap_in_obj_min);
swap_in_prog_hour = swap_in_prog_hour - acc2_swap_in_prog[min] +
(acc2_swap_in_prog[min] = swap_in_prog_min);
}
}
#endif
interupted = 1;
now++;
current_time = get_current_time();
}
void
init_call_out()
{
last = now = 0;
signal(SIGALRM, timer_alarm);
ualarm(1000000/TIME_RES, 1000000/TIME_RES);
}
int
num_call_outs(struct object *ob)
{
struct call *c;
int num_co;
for (num_co = 0, c = ob->call_outs; c;
num_co++, c = c->next) ;
return num_co;
}
void
call_out_swap_objects(struct object *ob1, struct object *ob2)
{
int slot;
struct object **obp;
struct call *tmp_co;
if (ob1->call_outs)
{
slot = ob1->call_outs->when & (NUM_SLOTS - 1);
for (obp = &(call_outs[slot]); *obp && *obp != ob1;
obp = &(*obp)->next_call_out) ;
if (!*obp)
fatal("Error in callout list.\n");
*obp = ob1->next_call_out;
}
if (ob2->call_outs)
{
slot = ob2->call_outs->when & (NUM_SLOTS - 1);
for (obp = &(call_outs[slot]); *obp && *obp != ob2;
obp = &(*obp)->next_call_out) ;
if (!*obp)
fatal("Error in callout list.\n");
*obp = ob2->next_call_out;
}
tmp_co = ob1->call_outs;
ob1->call_outs = ob2->call_outs;
ob2->call_outs = tmp_co;
if (ob1->call_outs)
{
slot = ob1->call_outs->when & (NUM_SLOTS - 1);
for (obp = &(call_outs[slot]); *obp &&
(*obp)->call_outs->when < ob1->call_outs->when;
obp = &(*obp)->next_call_out) ;
ob1->next_call_out = *obp;
*obp = ob1;
}
if (ob2->call_outs)
{
slot = ob2->call_outs->when & (NUM_SLOTS - 1);
for (obp = &(call_outs[slot]); *obp &&
(*obp)->call_outs->when < ob2->call_outs->when;
obp = &(*obp)->next_call_out) ;
ob2->next_call_out = *obp;
*obp = ob2;
}
}
static void
insert_call_out(struct object *ob, struct call *cop)
{
struct call **copp;
struct object **obp;
int slot;
/* Insert the callout */
for (copp = &ob->call_outs; *copp && (*copp)->when < cop->when;
copp = &(*copp)->next) ;
cop->next = *copp;
*copp = cop;
/* Are we inserting the callout first in the list? */
if (copp == &ob->call_outs)
{
/* Did the object allready have some callouts */
if (cop->next)
{
slot = cop->next->when & (NUM_SLOTS - 1);
for (obp = &(call_outs[slot]); *obp && *obp != ob;
obp = &(*obp)->next_call_out) ;
if (!*obp)
fatal("Error in callout list.\n");
*obp = ob->next_call_out;
}
/* Insert the object */
slot = ob->call_outs->when & (NUM_SLOTS - 1);
for (obp = &(call_outs[slot]);
*obp && (*obp)->call_outs->when < ob->call_outs->when;
obp = &(*obp)->next_call_out) ;
ob->next_call_out = *obp;
*obp = ob;
}
}
static void
free_call(struct call *cop)
{
free_vector(cop->v);
if (cop->command_giver)
free_object(cop->command_giver, "free_call");
free((char *)cop);
num_call--;
call_out_size -= sizeof(struct call);
}
int
new_call_out(struct object *ob, unsigned int func, unsigned int inh,
int delay, int reload, struct vector *arg)
{
struct call *cop;
if (delay <= 0)
delay = 1;
if (reload <= 0)
reload = 0;
cop = (struct call *)xalloc(sizeof(struct call));
num_call++;
call_out_size += sizeof(struct call);
cop->fun = func;
cop->inherit = inh;
cop->reload = reload;
cop->when = now + delay;
if (cop->when < last)
fatal("Error in callout!\n");
cop->id = call_id++;
cop->command_giver = command_giver;
if (command_giver)
command_giver->ref++;
if (arg)
{
cop->v = arg;
arg->ref++;
}
else
{
cop->v = allocate_array(1);
cop->v->item[0].type = T_NUMBER;
cop->v->item[0].u.number = cop->id;
}
insert_call_out(ob, cop);
return cop->id;
}
void
delete_call(struct object *ob, int call_id)
{
struct call **copp, *cop;
struct object **pob;
for(copp = &ob->call_outs; *copp && (*copp)->id != call_id;
copp = &(*copp)->next) ;
if (!*copp)
return;
cop = *copp;
*copp = (*copp)->next;
/* Is it the first callout in the list? */
if (copp == &ob->call_outs)
{
int slot;
slot = cop->when & (NUM_SLOTS - 1);
for (pob = &call_outs[slot]; *pob && *pob != ob;
pob = &(*pob)->next_call_out) ;
if (!*pob)
fatal("Corrupted callout list.\n");
*pob = ob->next_call_out;
/* Are there any callouts left? */
if (ob->call_outs)
{
slot = ob->call_outs->when & (NUM_SLOTS - 1);
for (pob = &call_outs[slot];
*pob && (*pob)->call_outs->when < ob->call_outs->when;
pob = &(*pob)->next_call_out) ;
ob->next_call_out = (*pob);
*pob = ob;
}
}
free_call(cop);
}
void
delete_all_calls(struct object *ob)
{
int slot;
struct object **pob;
struct call *next, *cop;
if (!(ob->call_outs))
return;
slot = ob->call_outs->when & (NUM_SLOTS - 1);
for (pob = &call_outs[slot]; *pob && *pob != ob;
pob = &(*pob)->next_call_out);
if (!*pob)
fatal("Corrupt callout list.\n");
*pob = ob->next_call_out;
for(cop = ob->call_outs; cop; cop = next)
{
next = cop->next;
free_call(cop);
}
ob->call_outs = 0;
}
/*
0 : call id;
1 : function;
2 : time left;
3 : reload time;
4 : argument;
*/
struct vector *
get_call(struct object *ob, int call_id)
{
struct vector *val;
struct call *cop;
for(cop = ob->call_outs; cop && cop->id != call_id;
cop = cop->next) ;
if (!cop)
return 0;
val = allocate_array(5);
val->item[0].type = T_NUMBER;
val->item[0].u.number = cop->id;
val->item[1].type = T_STRING;
val->item[1].string_type = STRING_MALLOC;
val->item[1].u.string = string_copy(FUNC_NAME(ob, cop));
val->item[2].type = T_FLOAT;
val->item[2].u.real = ((double)cop->when - now) / TIME_RES;
if (val->item[2].u.real < 0.0)
val->item[2].u.real = 0.0;
val->item[3].type = T_FLOAT;
val->item[3].u.real = ((double)cop->reload) / TIME_RES;
val->item[4].type = T_POINTER;
val->item[4].u.vec = cop->v;
cop->v->ref++;
return val;
}
/*
0 : call id;
1 : function;
2 : time left;
3 : reload time;
4 : argument;
*/
struct vector *
get_calls(struct object *ob)
{
struct call *cop;
int i;
struct vector *ret;
for(i = 0, cop = ob->call_outs; cop; i++, cop = cop->next) ;
ret = allocate_array(i);
for(i = 0, cop = ob->call_outs; cop; i++, cop = cop->next)
{
struct vector *val;
val = allocate_array(5);
val->item[0].type = T_NUMBER;
val->item[0].u.number = cop->id;
val->item[1].type = T_STRING;
val->item[1].string_type = STRING_MALLOC;
val->item[1].u.string = string_copy(FUNC_NAME(ob, cop));
val->item[2].type = T_FLOAT ;
val->item[2].u.real = ((double)cop->when - now) / TIME_RES;
if (val->item[2].u.real < 0.0)
val->item[2].u.real = 0.0;
val->item[3].type = T_FLOAT;
val->item[3].u.real = ((double)cop->reload) / TIME_RES;
val->item[4].type = T_POINTER;
val->item[4].u.vec = cop->v;
cop->v->ref++;
ret->item[i].type = T_POINTER;
ret->item[i].u.vec = val;
}
return ret;
}
int current_call_out_id;
struct object *current_call_out_object;
void
call_out()
{
static struct call *cop, **copp;
struct object *ob, **obp;
jmp_buf save_error_recovery_context;
int save_rec_exists, i;
extern struct object *command_giver;
extern struct object *current_interactive;
int sum_eval = 0;
int sum_time = 0;
int sum_ptime = 0;
int num_done = 0;
char caodesc[100];
int num_args;
if (last >= now)
return;
memcpy((char *) save_error_recovery_context,
(char *) error_recovery_context,
sizeof error_recovery_context);
save_rec_exists = error_recovery_context_exists;
error_recovery_context_exists = 1;
current_interactive = 0;
is_now = now;
for (;last <= is_now; last++)
{
int slot, nslot;
extern void call_function(struct object *,
int, unsigned int, int);
int inh, fun;
slot = last & (NUM_SLOTS - 1);
while(call_outs[slot] &&
call_outs[slot]->call_outs->when <= last)
{
/* Extract the object and the callout */
ob = call_outs[slot];
call_outs[slot] = ob->next_call_out;
cop = ob->call_outs;
ob->call_outs = cop->next;
if (cop->reload > 0) /* Reschedule the callout */
{
current_call_out_id = cop->id;
cop->when = now + cop->reload;
if (cop->when < last)
fatal("Error in callouts!\n");
for (copp = &ob->call_outs;
*copp && (*copp)->when < cop->when;
copp = &(*copp)->next) ;
cop->next = *copp;
*copp = cop;
}
else
current_call_out_id = 0;
if (ob->call_outs) /* Reinsert the object */
{
nslot = ob->call_outs->when & (NUM_SLOTS - 1);
for (obp = &call_outs[nslot];
*obp && (*obp)->call_outs->when < ob->call_outs->when;
obp = &(*obp)->next_call_out) ;
ob->next_call_out = *obp;
*obp = ob;
}
/* do the call */
if (cop->command_giver &&
cop->command_giver->flags & O_DESTRUCTED)
{
free_object(cop->command_giver, "call_out");
cop->command_giver = 0;
}
if (cop->command_giver &&
cop->command_giver->flags & O_ENABLE_COMMANDS)
command_giver = cop->command_giver;
else if (ob->flags & O_ENABLE_COMMANDS)
command_giver = ob;
else
command_giver = 0;
if (s_flag)
reset_mudstatus();
eval_cost = 0;
num_args = cop->v->size;
for (i = 0; i < num_args; i++)
{
if (cop->v->item[i].type == T_OBJECT &&
cop->v->item[i].u.ob->flags & O_DESTRUCTED)
free_svalue(&cop->v->item[i]);
push_svalue(&cop->v->item[i]);
}
current_call_out_object = current_object = ob;
current_call_out_object->ref++;
inh = cop->inherit;
fun = cop->fun;
if (cop->reload <= 0)
{
free_call(cop);
}
if (setjmp(error_recovery_context))
{
extern void clear_state();
clear_state();
debug_message("Error in call out.\n");
if (current_call_out_id)
{
access_program(ob->prog->inherit[inh].prog);
debug_message("Call out %s turned off in %s.\n",
ob->prog->inherit[inh].prog->
functions[fun].name,
current_call_out_object->name);
delete_call(current_call_out_object, current_call_out_id);
}
free_object(current_call_out_object, "call_out");
}
else
{
call_function(ob, inh, fun, num_args);
pop_stack();
if (s_flag)
{
num_done++;
sum_eval += eval_cost;
sum_time += get_millitime();
sum_ptime += get_processtime();
access_program(ob->prog->inherit[inh].prog);
sprintf(caodesc,"CAO:%s(%s)", ob->name,
ob->prog->inherit[inh].prog->functions[fun].name);
print_mudstatus(caodesc, eval_cost,
get_millitime(), get_processtime());
}
free_object(current_call_out_object, "call_out");
}
}
}
memcpy((char *) error_recovery_context,
(char *) save_error_recovery_context,
sizeof error_recovery_context);
error_recovery_context_exists = save_rec_exists;
if (s_flag && num_done)
{
reset_mudstatus();
sprintf(caodesc,"Call_out (%d)", num_done);
print_mudstatus(caodesc, sum_eval, sum_time, sum_ptime);
}
}
#ifdef DEBUG
void
count_ref_from_call_outs()
{
int i;
struct object *ob;
struct call *cop;
for(i = 0; i < NUM_SLOTS; i++)
for(ob = call_outs[i]; ob; ob = ob->next_call_out)
for(cop = ob->call_outs; cop; cop->next)
{
switch(cop->v.type)
{
case T_POINTER:
cop->v.u.vec->extra_ref++;
break;
case T_OBJECT:
cop->v.u.ob->extra_ref++;
break;
}
if (cop->command_giver)
cop->command_giver->extra_ref++;
}
}
#endif