/* Primitives Package */ #include "copyright.h" #include "config.h" #include <sys/types.h> #include <stdio.h> #include <time.h> #include <ctype.h> #include "db.h" #include "tune.h" #include "inst.h" #include "externs.h" #include "match.h" #include "interface.h" #include "params.h" #include "fbstrings.h" #include "interp.h" #include "mufevent.h" #include "array.h" struct mufevent_process { struct mufevent_process *prev, *next; dbref player; dbref prog; int filtercount; char** filters; struct frame *fr; } *mufevent_processes; /* static void muf_event_process_free(struct mufevent* ptr) * Frees up a mufevent_process once you are done with it. * This shouldn't be used outside this module. */ static void muf_event_process_free(struct mufevent_process *ptr) { int i; if (ptr->next) { ptr->next->prev = ptr->prev; } if (ptr->prev) { ptr->prev->next = ptr->next; } else { mufevent_processes = ptr->next; } ptr->prev = NULL; ptr->next = NULL; ptr->player = NOTHING; ptr->prog = NOTHING; if (ptr->fr) { prog_clean(ptr->fr); ptr->fr = NULL; } if (ptr->filters) { for (i = 0; i < ptr->filtercount; i++) { if (ptr->filters[i]) { free(ptr->filters[i]); ptr->filters[i] = NULL; } } free(ptr->filters); ptr->filters = NULL; ptr->filtercount = 0; } free(ptr); } /* void muf_event_register_specific(dbref player, dbref prog, struct frame* fr, int eventcount, char** eventids) * Called when a MUF program enters EVENT_WAITFOR, to register that * the program is ready to process MUF events of the given ID type. * This duplicates the eventids list for itself, so the caller is * responsible for freeing the original eventids list passed. */ void muf_event_register_specific(dbref player, dbref prog, struct frame *fr, int eventcount, char** eventids) { struct mufevent_process *newproc; struct mufevent_process *ptr; int i; newproc = (struct mufevent_process *) malloc(sizeof(struct mufevent_process)); newproc->prev = NULL; newproc->next = NULL; newproc->player = player; newproc->prog = prog; newproc->fr = fr; newproc->filtercount = eventcount; if (eventcount > 0) { newproc->filters = (char**) malloc(eventcount * sizeof(char**)); for (i = 0; i < eventcount; i++) { newproc->filters[i] = string_dup(eventids[i]); } } else { newproc->filters = NULL; } ptr = mufevent_processes; while (ptr && ptr->next) { ptr = ptr->next; } if (!ptr) { mufevent_processes = newproc; } else { ptr->next = newproc; newproc->prev = ptr; } } /* void muf_event_register(dbref player, dbref prog, struct frame* fr) * Called when a MUF program enters EVENT_WAIT, to register that * the program is ready to process any type of MUF events. */ void muf_event_register(dbref player, dbref prog, struct frame *fr) { muf_event_register_specific(player, prog, fr, 0, NULL); } /* int muf_event_read_notify(int descr, dbref player) * Sends a "READ" event to the first foreground or preempt muf process * that is owned by the given player. Returns 1 if an event was sent, * 0 otherwise. */ int muf_event_read_notify(int descr, dbref player, const char* cmd) { struct mufevent_process *ptr; ptr = mufevent_processes; while (ptr) { if (ptr->player == player) { if (ptr->fr && ptr->fr->multitask != BACKGROUND) { if (*cmd || ptr->fr->wantsblanks) { struct inst temp; temp.type = PROG_INTEGER; temp.data.number = descr; muf_event_add(ptr->fr, "READ", &temp, 1); return 1; } } } ptr = ptr->next; } return 0; } /* int muf_event_dequeue_pid(int pid) * removes the MUF program with the given PID from the EVENT_WAIT queue. */ int muf_event_dequeue_pid(int pid) { struct mufevent_process *proc, *tmp; int count = 0; proc = mufevent_processes; while (proc) { tmp = proc; proc = proc->next; if (tmp->fr->pid == pid) { if (!tmp->fr->been_background) PLAYER_SET_BLOCK(tmp->player, 0); muf_event_purge(tmp->fr); muf_event_process_free(tmp); count++; } } return count; } /* static int event_has_refs(dbref program, struct mufevent_process *proc) * Checks the MUF event queue for address references on the stack or * dbref references on the callstack */ static int event_has_refs(dbref program, struct mufevent_process *proc) { int loop; struct frame* fr = proc->fr; if (!fr) { return 0; } for (loop = 1; loop < fr->caller.top; loop++) { if (fr->caller.st[loop] == program) { return 1; } } for (loop = 0; loop < fr->argument.top; loop++) { if (fr->argument.st[loop].type == PROG_ADD && fr->argument.st[loop].data.addr->progref == program) { return 1; } } return 0; } /* int muf_event_dequeue(dbref prog, int killmode) * Deregisters a program from any instances of it in the EVENT_WAIT queue. * killmode values: * 0: kill all matching processes (Equivalent to 1) * 1: kill all matching MUF processes * 2: kill all matching foreground MUF processes */ int muf_event_dequeue(dbref prog, int killmode) { struct mufevent_process *proc, *tmp; int count = 0; proc = mufevent_processes; while (proc) { tmp = proc; proc = proc->next; if (tmp->prog != prog && !event_has_refs(prog, tmp) && tmp->player != prog) { continue; } if (killmode == 2) { if (tmp->fr && tmp->fr->multitask == BACKGROUND) { continue; } } else if (killmode == 1) { if (!tmp->fr) { continue; } } if (!tmp->fr->been_background) PLAYER_SET_BLOCK(tmp->player, 0); muf_event_purge(tmp->fr); muf_event_process_free(tmp); count++; } return count; } struct frame* muf_event_pid_frame(int pid) { struct mufevent_process *ptr = mufevent_processes; while (ptr) { if (ptr->fr && ptr->fr->pid == pid) return ptr->fr; ptr = ptr->next; } return NULL; } /* int muf_event_controls(dbref player, int pid) * Returns true if the given player controls the given PID. */ int muf_event_controls(dbref player, int pid) { struct mufevent_process *proc = mufevent_processes; while (proc && pid != proc->fr->pid) { proc = proc->next; } if (!proc) { return 0; } if (!controls(player, proc->prog) && player != proc->player) { return 0; } return 1; } /* int muf_event_list(dbref player, char* pat) * List all processes in the EVENT_WAIT queue that the given player controls. * This is used by the @ps command. */ int muf_event_list(dbref player, const char *pat) { char buf[BUFFER_LEN]; char pidstr[BUFFER_LEN]; char inststr[BUFFER_LEN]; char cpustr[BUFFER_LEN]; char progstr[BUFFER_LEN]; char prognamestr[BUFFER_LEN]; int count = 0; time_t rtime = time((time_t *) NULL); time_t etime; double pcnt; struct mufevent_process *proc = mufevent_processes; while (proc) { if (proc->fr) { etime = rtime - proc->fr->started; if (etime > 0) { pcnt = proc->fr->totaltime.tv_sec; pcnt += proc->fr->totaltime.tv_usec / 1000000; pcnt = pcnt * 100 / etime; if (pcnt > 99.9) { pcnt = 99.9; } } else { pcnt = 0.0; } } snprintf(pidstr, sizeof(pidstr), "%d", proc->fr->pid); snprintf(inststr, sizeof(inststr), "%d", (proc->fr->instcnt / 1000)); snprintf(cpustr, sizeof(cpustr), "%4.1f", pcnt); if (proc->fr) { snprintf(progstr, sizeof(progstr), "#%d", proc->fr->caller.st[1]); snprintf(prognamestr, sizeof(prognamestr), "%s", NAME(proc->fr->caller.st[1])); } else { snprintf(progstr, sizeof(progstr), "#%d", proc->prog); snprintf(prognamestr, sizeof(prognamestr), "%s", NAME(proc->prog)); } snprintf(buf, sizeof(buf), pat, pidstr, "--", time_format_2((long) (rtime - proc->fr->started)), inststr, cpustr, progstr, prognamestr, NAME(proc->player), "EVENT_WAITFOR"); if (Wizard(OWNER(player)) || (OWNER(proc->prog) == OWNER(player)) || (proc->player == player)) notify_nolisten(player, buf, 1); count++; proc = proc->next; } return count; } /* stk_array *get_mufevent_pids(stk_array *nw, dbref ref) * Given a muf list array, appends pids to it where ref * matches the trigger, program, or player. If ref is #-1 * then all processes waiting for mufevents are added. */ stk_array * get_mufevent_pids(stk_array *nw, dbref ref) { struct inst temp1, temp2; int count = 0; struct mufevent_process *proc = mufevent_processes; while (proc) { if (proc->player == ref || proc->prog == ref || proc->fr->trig == ref || ref < 0) { temp1.type = PROG_INTEGER; temp1.data.number = count++; temp2.type = PROG_INTEGER; temp2.data.number = proc->fr->pid; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); } proc = proc->next; } return nw; } stk_array * get_mufevent_pidinfo(stk_array* nw, int pid) { struct inst temp1, temp2; stk_array* arr; time_t rtime = time(NULL); time_t etime = 0; double pcnt = 0.0; int i; struct mufevent_process *proc = mufevent_processes; while (proc && (proc->fr->pid != pid)) { proc = proc->next; } if (proc && (proc->fr->pid == pid)) { if (proc->fr) { etime = rtime - proc->fr->started; if (etime > 0) { pcnt = proc->fr->totaltime.tv_sec; pcnt += proc->fr->totaltime.tv_usec / 1000000; pcnt = pcnt * 100 / etime; if (pcnt > 100.0) { pcnt = 100.0; } } else { pcnt = 0.0; } } temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("PID"); temp2.type = PROG_INTEGER; temp2.data.number = proc->fr->pid; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("CALLED_PROG"); temp2.type = PROG_OBJECT; temp2.data.objref = proc->prog; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("TRIG"); temp2.type = PROG_OBJECT; temp2.data.objref = proc->fr->trig; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("PLAYER"); temp2.type = PROG_OBJECT; temp2.data.objref = proc->player; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("CALLED_DATA"); temp2.type = PROG_STRING; temp2.data.string = alloc_prog_string("EVENT_WAITFOR"); array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("INSTCNT"); temp2.type = PROG_INTEGER; temp2.data.number = proc->fr->instcnt; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("DESCR"); temp2.type = PROG_INTEGER; temp2.data.number = proc->fr->descr; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("CPU"); temp2.type = PROG_FLOAT; temp2.data.fnumber = pcnt; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("NEXTRUN"); temp2.type = PROG_INTEGER; temp2.data.number = -1; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("STARTED"); temp2.type = PROG_INTEGER; temp2.data.number = (int) proc->fr->started; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("TYPE"); temp2.type = PROG_STRING; temp2.data.string = alloc_prog_string("MUFEVENT"); array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("SUBTYPE"); temp2.type = PROG_STRING; temp2.data.string = alloc_prog_string(""); array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); temp1.type = PROG_STRING; temp1.data.string = alloc_prog_string("FILTERS"); arr = new_array_packed(0); for (i = 0; i < proc->filtercount; i++) { array_set_intkey_strval(&arr, i, proc->filters[i]); } temp2.type = PROG_ARRAY; temp2.data.array = arr; array_setitem(&nw, &temp1, &temp2); CLEAR(&temp1); CLEAR(&temp2); } return nw; } /* int muf_event_count(struct frame* fr) * Returns how many events are waiting to be processed. */ int muf_event_count(struct frame* fr) { struct mufevent *ptr; int count = 0; for (ptr = fr->events; ptr; ptr = ptr->next) count++; return count; } /* int muf_event_exists(struct frame* fr, const char* eventid) * Returns how many events of the given event type are waiting to be processed. * The eventid passed can be an smatch string. */ int muf_event_exists(struct frame* fr, const char* eventid) { struct mufevent *ptr; int count = 0; char pattern[BUFFER_LEN]; strcpy(pattern, eventid); for (ptr = fr->events; ptr; ptr = ptr->next) if (equalstr(pattern, ptr->event)) count++; return count; } /* static void muf_event_free(struct mufevent* ptr) * Frees up a MUF event once you are done with it. This shouldn't be used * outside this module. */ static void muf_event_free(struct mufevent *ptr) { CLEAR(&ptr->data); free(ptr->event); ptr->event = NULL; ptr->next = NULL; free(ptr); } /* void muf_event_add(struct frame* fr, char* event, struct inst* val, int exclusive) * Adds a MUF event to the event queue for the given program instance. * If the exclusive flag is true, and if an item of the same event type * already exists in the queue, the new one will NOT be added. */ void muf_event_add(struct frame *fr, char *event, struct inst *val, int exclusive) { struct mufevent *newevent; struct mufevent *ptr; ptr = fr->events; while (ptr && ptr->next) { if (exclusive && !strcmp(event, ptr->event)) { return; } ptr = ptr->next; } if (exclusive && ptr && !strcmp(event, ptr->event)) { return; } newevent = (struct mufevent *) malloc(sizeof(struct mufevent)); newevent->event = string_dup(event); copyinst(val, &newevent->data); newevent->next = NULL; if (!ptr) { fr->events = newevent; } else { ptr->next = newevent; } } /* struct mufevent* muf_event_pop_specific(struct frame* fr, int eventcount, const char** events) * Removes the first event of one of the specified types from the event queue * of the given program instance. * Returns a pointer to the removed event to the caller. * Returns NULL if no matching events are found. * You will need to call muf_event_free() on the returned data when you * are done with it and wish to free it from memory. */ struct mufevent* muf_event_pop_specific(struct frame *fr, int eventcount, char **events) { struct mufevent *tmp = NULL; struct mufevent *ptr = NULL; int i; for (i = 0; i < eventcount; i++) { if (fr->events && equalstr(events[i], fr->events->event)) { tmp = fr->events; fr->events = tmp->next; return tmp; } } ptr = fr->events; while (ptr && ptr->next) { for (i = 0; i < eventcount; i++) { if (equalstr(events[i], ptr->next->event)) { tmp = ptr->next; ptr->next = tmp->next; return tmp; } } ptr = ptr->next; } return NULL; } /* void muf_event_remove(struct frame* fr, char* event, int doall) * Removes a given MUF event type from the event queue of the given * program instance. If which is MUFEVENT_ALL, all instances are removed. * If which is MUFEVENT_FIRST, only the first instance is removed. * If which is MUFEVENT_LAST, only the last instance is removed. */ void muf_event_remove(struct frame *fr, char *event, int which) { struct mufevent *tmp = NULL; struct mufevent *ptr = NULL; while (fr->events && !strcmp(event, fr->events->event)) { if (which == MUFEVENT_LAST) { tmp = fr->events; break; } else { tmp = fr->events; fr->events = tmp->next; muf_event_free(tmp); if (which == MUFEVENT_FIRST) { return; } } } ptr = fr->events; while (ptr && ptr->next) { if (!strcmp(event, ptr->next->event)) { if (which == MUFEVENT_LAST) { tmp = ptr; ptr = ptr->next; } else { tmp = ptr->next; ptr->next = tmp->next; muf_event_free(tmp); if (which == MUFEVENT_FIRST) { return; } } } else { ptr = ptr->next; } } } /* static struct mufevent* muf_event_peek(struct frame* fr) * This returns a pointer to the top muf event of the given * program instance's event queue. The event is not removed * from the queue. */ /* * Commented out by Winged for non-reference. If you need it, de-comment. * static struct mufevent * muf_event_peek(struct frame *fr) { return fr->events; } */ /* static struct mufevent* muf_event_pop(struct frame* fr) * This pops the top muf event off of the given program instance's * event queue, and returns it to the caller. The caller should * call muf_event_free() on the data when it is done with it. */ static struct mufevent * muf_event_pop(struct frame *fr) { struct mufevent *ptr = NULL; if (fr->events) { ptr = fr->events; fr->events = fr->events->next; } return ptr; } /* void muf_event_purge(struct frame* fr) * purges all muf events from the given program instance's event queue. */ void muf_event_purge(struct frame *fr) { while (fr->events) { muf_event_free(muf_event_pop(fr)); } } /* void muf_event_process() * For all program instances who are in the EVENT_WAIT queue, * check to see if they have any items in their event queue. * If so, then process one each. Up to ten programs can have * events processed at a time. */ void muf_event_process(void) { int limit = 10; struct mufevent_process **prev, *proc; struct mufevent *ev; dbref current_program; int block, is_fg; prev = &mufevent_processes; while ((proc = *prev) != NULL && limit > 0) { if (proc->fr) { if (proc->filtercount > 0) { /* Search prog's event list for the apropriate event type. */ /* HACK: This is probably inefficient to be walking this * queue over and over. Hopefully it's usually a short list. */ ev = muf_event_pop_specific(proc->fr, proc->filtercount, proc->filters); } else { /* Pop first event off of prog's event queue. */ ev = muf_event_pop(proc->fr); } if (ev) { --limit; *prev = (*prev)->next; if (proc->fr->argument.top + 1 >= STACK_SIZE) { /* Uh oh! That MUF program's stack is full! * Print an error, free the frame, and exit. */ notify_nolisten(proc->player, "Program stack overflow.", 1); prog_clean(proc->fr); } else { current_program = PLAYER_CURR_PROG(proc->player); block = PLAYER_BLOCK(proc->player); is_fg = (proc->fr->multitask != BACKGROUND); copyinst(&ev->data, &(proc->fr->argument.st[proc->fr->argument.top])); proc->fr->argument.top++; push(proc->fr->argument.st, &(proc->fr->argument.top), PROG_STRING, MIPSCAST alloc_prog_string(ev->event)); interp_loop(proc->player, proc->prog, proc->fr, 0); if (!is_fg) { PLAYER_SET_BLOCK(proc->player, block); PLAYER_SET_CURR_PROG(proc->player, current_program); } } muf_event_free(ev); proc->fr = NULL; /* We do NOT want to free this program after every EVENT_WAIT. */ muf_event_process_free(proc); continue; /* Current item was changed, so prev remains same. */ } } prev = &((*prev)->next); } }