/* (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