/* cque.c */ #include <stdio.h> #include <ctype.h> #include <fcntl.h> #include <string.h> #ifdef WANT_ANSI #ifdef __STDC__ #include <stdlib.h> #endif /* __STDC */ #endif /* WANT_ANSI */ #ifdef XENIX #include <sys/signal.h> #else #include <signal.h> #endif /* xenix */ #include "mudconf.h" #include "config.h" #include "db.h" #include "htab.h" #include "interface.h" #include "match.h" #include "externs.h" #include "flags.h" void process_command(dbref player, dbref cause, int interactive, char *command, char *args[], int nargs); /* --------------------------------------------------------------------------- * clean_que: Free command and arguments passed to queue routines, if needed */ static void clean_que(int key, char *arg1, char *argv[], int nargs) { int i; if (key & RU_ARG1_TAKE) { if (arg1 != NULL) free_lbuf(arg1); } if (key & RU_ARG2_TAKE) { for (i=0; i<nargs; i++) { if (argv[i] != NULL) free_lbuf(argv[i]); } } } /* --------------------------------------------------------------------------- * add_to: Adjust an object's queue or semaphore count. */ static int add_to(dbref player, int am, int attrnum) { int num, aflags; dbref aowner; char buff[20]; char *atr_gotten; num = atoi(atr_gotten = atr_get(player, attrnum, &aowner, &aflags)); free_lbuf(atr_gotten); num += am; if (num) sprintf(buff, "%d", num); else *buff = '\0'; atr_add_raw(player, attrnum, buff); return (num); } /* --------------------------------------------------------------------------- * give_que: Thread a queue block onto the high or low priority queue */ static void give_que (BQUE *tmp) { tmp->next = NULL; tmp->left = -1; /* Thread the command into the correct queue */ if (Typeof(tmp->cause) == TYPE_PLAYER) { if (mudstate.qlast != NULL) { mudstate.qlast->next = tmp; mudstate.qlast = tmp; } else mudstate.qlast = mudstate.qfirst = tmp; } else { if (mudstate.qllast) { mudstate.qllast->next = tmp; mudstate.qllast = tmp; } else mudstate.qllast = mudstate.qlfirst = tmp; } } /* --------------------------------------------------------------------------- * que_want: Do we want this queue entry? */ static int que_want (BQUE *entry, dbref ptarg, dbref otarg) { if ((ptarg != NOTHING) && (ptarg != Owner(entry->player))) return 0; if ((otarg != NOTHING) && (otarg != entry->player)) return 0; return 1; } /* --------------------------------------------------------------------------- * halt_que: Remove all queued commands from a certain player */ int halt_que(dbref player, dbref object) { BQUE *trail, *point, *next; int numhalted; numhalted = 0; if ((player == NOTHING) && (object == NOTHING)) return 0; /* Player queue */ for (point = mudstate.qfirst; point; point = point->next) if (que_want(point, player, object)) { numhalted++; point->player = 0; } /* Object queue */ for (point = mudstate.qlfirst; point; point = point->next) if (que_want(point, player, object)) { numhalted++; point->player = 0; } /* Wait queue */ for (point=mudstate.qwait,trail=NULL; point; point = next) if (que_want(point, player, object)) { numhalted++; if (trail) trail->next = next = point->next; else mudstate.qwait = next = point->next; clean_que(RU_ARG1_TAKE|RU_ARG2_TAKE, point->comm, point->env, point->nargs); free_qentry(point); } else next = (trail = point)->next; /* Semaphore queue */ for (point=mudstate.qsemfirst,trail=NULL; point; point = next) if (que_want(point, player, object)) { numhalted++; if (trail) trail->next = next = point->next; else mudstate.qsemfirst = next = point->next; if (point == mudstate.qsemlast) mudstate.qsemlast = trail; add_to(point->sem, -1, A_SEMAPHORE); clean_que(RU_ARG1_TAKE|RU_ARG2_TAKE, point->comm, point->env, point->nargs); free_qentry(point); } else next = (trail = point)->next; if (player == NOTHING) player = Owner(object); giveto(player, (mudconf.waitcost * numhalted)); if (object == NOTHING) atr_clr(player, A_QUEUE); else add_to(player, -numhalted, A_QUEUE); return numhalted; } /* --------------------------------------------------------------------------- * do_halt: Command interface to halt_que. */ void do_halt (dbref player, dbref cause, int key, char *target) { dbref player_targ, obj_targ; int numhalted; /* Figure out what to halt */ if (!target || !*target) { obj_targ = NOTHING; if (key & HALT_ALL) { player_targ = NOTHING; } else { player_targ = Owner(player); if (Typeof(player) != TYPE_PLAYER) obj_targ = player; } } else { obj_targ = match_controlled(player, target); if (obj_targ == NOTHING) return; if (key & HALT_ALL) { notify(player, "Can't specify a target and /all"); return; } if (Typeof(obj_targ) == TYPE_PLAYER) { player_targ = obj_targ; obj_targ = NOTHING; } else { player_targ = NOTHING; } } numhalted = halt_que(player_targ, obj_targ); if (Quiet(player)) return; if (numhalted == 1) notify(Owner(player), "1 queue entries removed."); else notify(Owner(player), tprintf("%d queue entries removed.", numhalted)); } /* --------------------------------------------------------------------------- * nfy_que: Notify commands from the queue and perform or discard them. */ static int nfy_que(dbref sem, int key, int count) { BQUE *point, *trail, *next; int num, aflags; dbref aowner; char *str; str = atr_get(sem, A_SEMAPHORE, &aowner, &aflags); num = atoi(str); free_lbuf(str); if (num > 0) { num = 0; for (point=mudstate.qsemfirst,trail=NULL; point; point = next) { if (point->sem == sem) { num++; if (trail) trail->next = next = point->next; else mudstate.qsemfirst = next = point->next; if (point == mudstate.qsemlast) mudstate.qsemlast = trail; /* Either run or discard the command */ if (key != NFY_DRAIN) { give_que(point); } else { giveto(point->player, mudconf.waitcost); clean_que(RU_ARG1_TAKE|RU_ARG2_TAKE, point->comm, point->env, point->nargs); free_qentry(point); } } else { next = (trail = point)->next; } /* If we've notified enough, exit */ if ((key == NFY_NFY) && (num >= count)) next = NULL; } } else { num = 0; } /* Update the sem waiters count */ if (key == NFY_NFY) add_to(sem, -count, A_SEMAPHORE); else atr_clr(sem, A_SEMAPHORE); return num; } /* --------------------------------------------------------------------------- * do_notify: Command interface to nfy_que */ void do_notify (dbref player, dbref cause, int key, char *what, char *count) { dbref thing; int loccount; init_match(player, what, NOTYPE); match_exit(); match_neighbor(); match_possession(); match_me(); match_here(); match_absolute(); match_player(); if ((thing = noisy_match_result()) < 0) { notify(player, "No match."); } else if (!controls(player, thing) && !(Flags(thing) & LINK_OK)) { notify(player, "Permission denied."); } else { if (count && *count) loccount = atoi(count); else loccount = 1; if (loccount > 0) nfy_que(thing, key, loccount); } } /* --------------------------------------------------------------------------- * setup_que: Set up a queue entry. */ static BQUE *setup_que(dbref player, dbref cause, int key, char *command, char *args[], int nargs) { int a; BQUE *tmp; /* Can we run commands at all? */ if (Halted(player)) return NULL; /* make sure player can afford to do it */ a = mudconf.waitcost; if ((random() % mudconf.machinecost) == 0) a++; if (!payfor(player, a)) { notify(Owner(player), "Not enough money to queue command."); return NULL; } /* Wizards and their objs may queue up to db_top+1 cmds. Players are * limited to QUEUE_QUOTA. -mnp */ a = mudconf.queuemax; if (Wizard(player)) a += mudstate.db_top + 1; if (add_to(Owner(player), 1, A_QUEUE) > a) { notify(Owner(player), "Run away objects: too many commands queued. Halted."); halt_que(Owner(player), NOTHING); /* halt also means no command execution allowed */ s_Flags(player, Flags(player) | HALT); return NULL; } /* We passed all the tests. Create the queue entry. */ tmp = alloc_qentry("setup_que.qblock"); if (key & RU_ARG1_TAKE) { tmp->comm = command; /* save the lbuf */ } else { tmp->comm = alloc_lbuf("setup_que.arg1"); strcpy(tmp->comm, command); } tmp->player = player; tmp->left = -1; tmp->next = NULL; tmp->sem = NOTHING; tmp->cause = cause; /* Set up the environment */ if (nargs > NUM_ENV_VARS) { if (key & RU_ARG2_TAKE) { for (a=NUM_ENV_VARS; a<nargs; a++) { free_lbuf(args[a]); } } nargs = NUM_ENV_VARS; } for (a=0; a<nargs; a++) { if (args[a] != NULL) { if (key & (RU_ARG2_TAKE|RU_ARG2_TMPL)) { tmp->env[a] = args[a]; } else { tmp->env[a] = alloc_lbuf("setup_que.argv"); strcpy(tmp->env[a], args[a]); } } else { tmp->env[a] = NULL; } } tmp->nargs = nargs; return tmp; } /* --------------------------------------------------------------------------- * wait_que: Add commands to the wait or semaphore queues. */ void wait_que(dbref player, dbref cause, int key, int wait, dbref sem, char *command, char *args[], int nargs) { BQUE *tmp; if (mudconf.control_flags & CF_INTERP) tmp = setup_que(player, cause, key, command, args, nargs); else tmp = NULL; if (tmp == NULL) { clean_que(key, command, args, nargs); return; } tmp->left = wait; tmp->sem = sem; if (sem == NOTHING) { /* No semaphore, put on wait queue if wait value specified. * Otherwise put on the normal queue. */ if (wait <= 0) { give_que(tmp); } else { tmp->next = mudstate.qwait; mudstate.qwait = tmp; } } else { tmp->next = NULL; if (mudstate.qsemlast != NULL) mudstate.qsemlast->next = tmp; else mudstate.qsemfirst = tmp; mudstate.qsemlast = tmp; } } /* --------------------------------------------------------------------------- * do_wait: Command interface to wait_que */ void do_wait (dbref player, dbref cause, int key, char *event, char *cmd, char *cargs[], int ncargs) { dbref thing, aowner; int howlong, num, aflags; char *what; /* If arg1 is all numeric, do simple (non-sem) timed wait. */ if (is_number(event)) { howlong = atol(event); wait_que (player, cause, key, howlong, NOTHING, cmd, cargs, ncargs); return; } /* Semaphore wait with optional timeout */ what = parse_to(&event, '/', 0); init_match(player, what, NOTYPE); match_exit(); match_neighbor(); match_possession(); match_me(); match_here(); match_absolute(); match_player(); if ((thing = noisy_match_result()) < 0) { notify(player, "No match."); } else if (!controls(player, thing) && !(Flags(thing) & LINK_OK)) { notify(player, "Permission denied."); } else { /* Get timeout, default -1 */ if (event && *event) howlong = atol(event); else howlong = -1; add_to(thing, 1, A_SEMAPHORE); what = atr_get(thing, A_SEMAPHORE, &aowner, &aflags); num = atoi(what); free_lbuf(what); if (num <= 0) thing = NOTHING; wait_que(player, cause, key, howlong, thing, cmd, cargs, ncargs); } } /* --------------------------------------------------------------------------- * do_second: Check the wait and semaphore queues for commands to remove. */ void do_second(int nsecs) { BQUE *trail, *point, *next; char *cmdsave; /* move contents of low priority queue onto end of normal one * this helps to keep objects from getting out of control since * its affects on other objects happen only after one seconds * this should allow @halt to be type before getting blown away * by scrolling text */ if ((mudconf.control_flags & CF_DEQUEUE) == 0) return; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *)"< do_second >"; if (mudstate.qlfirst) { if (mudstate.qlast) mudstate.qlast->next = mudstate.qlfirst; else mudstate.qfirst = mudstate.qlfirst; mudstate.qlast = mudstate.qllast; mudstate.qllast = mudstate.qlfirst = NULL; } /* Note: the point->left test would be 0 except the command is being * put in the low priority queue to be done in one second anyways */ /* Do the wait queue */ for (point=mudstate.qwait,trail=NULL; point; point=next) { point->left -= nsecs; if (point->left <= 1) { if (trail != NULL) trail->next = next = point->next; else mudstate.qwait = next = point->next; give_que(point); } else next = (trail = point)->next; } /* Check the semaphore queue for expired timed-waits */ for (point=mudstate.qsemfirst,trail=NULL; point; point=next) { if (point->left < 0) { next = (trail = point)->next; continue; /* Skip if not timed-wait */ } point->left -= nsecs; if (point->left <= 1) { if (trail != NULL) trail->next = next = point->next; else mudstate.qsemfirst = next = point->next; if (point == mudstate.qsemlast) mudstate.qsemlast = trail; add_to(point->sem, -1, A_SEMAPHORE); point->sem = NOTHING; give_que(point); } else next = (trail = point)->next; } mudstate.debug_cmd = cmdsave; return; } /* --------------------------------------------------------------------------- * do_top: Execute the command at the top of the queue */ int do_top(int ncmds) { BQUE *tmp; dbref player; int count; char *command, *cp, *cmdsave; if ((mudconf.control_flags & CF_DEQUEUE) == 0) return 0; cmdsave = mudstate.debug_cmd; mudstate.debug_cmd = (char *)"< do_top >"; for (count=0; count<ncmds; count++) { if (!test_top()) { mudstate.debug_cmd = cmdsave; return count; } player = mudstate.qfirst->player; if ((player > 0) && !(Flags(player) & GOING)) { giveto(player, mudconf.waitcost); mudstate.curr_enactor = mudstate.qfirst->cause; mudstate.curr_player = player; add_to(Owner(player), -1, A_QUEUE); mudstate.qfirst->player = 0; if (!Halted(player)) { command = mudstate.qfirst->comm; while (command) { cp = parse_to(&command, ';', 0); if (cp && *cp) { process_command(player, mudstate.qfirst->cause, 0, cp, mudstate.qfirst->env, mudstate.qfirst->nargs); } } } } tmp = mudstate.qfirst->next; clean_que(RU_ARG1_TAKE|RU_ARG2_TAKE, mudstate.qfirst->comm, mudstate.qfirst->env, mudstate.qfirst->nargs); free_qentry(mudstate.qfirst); if (!(mudstate.qfirst = tmp)) mudstate.qlast = NULL; } mudstate.debug_cmd = cmdsave; return count; } /* --------------------------------------------------------------------------- * do_ps: tell player what commands they have pending in the queue */ static void show_que(dbref player, int key, BQUE *queue, int *qtot, int *qent, int *qdel, dbref player_targ, dbref obj_targ, const char *header) { BQUE *tmp; char *bp, *bufp; int i; *qtot = 0; *qent = 0; *qdel = 0; for (tmp = queue; tmp; tmp = tmp->next) { (*qtot)++; if (que_want(tmp, player_targ, obj_targ)) { (*qent)++; if (key == PS_SUMM) continue; if (*qent == 1) notify(player, tprintf("----- %s Queue -----", header)); bufp = unparse_object(player, tmp->player); if ((tmp->left >= 0) && (Good_obj(tmp->sem))) notify(player, tprintf("[#%d/%d]%s:%s", tmp->sem, tmp->left, bufp, tmp->comm)); else if (tmp->left >= 0) notify(player, tprintf("[%d]%s:%s", tmp->left, bufp, tmp->comm)); else if (Good_obj(tmp->sem)) notify(player, tprintf("[#%d]%s:%s", tmp->sem, bufp, tmp->comm)); else notify(player, tprintf("%s:%s", bufp, tmp->comm)); bp = bufp; if (key == PS_LONG) { for (i=0; i<(tmp->nargs); i++) { if (tmp->env[i] != NULL) { safe_str((char *)"; Arg", bufp, &bp); safe_chr(i+'0', bufp, &bp); safe_str((char *)"='", bufp, &bp); safe_str(tmp->env[i], bufp, &bp); safe_chr('\'', bufp, &bp); } } *bp = '\0'; bp = unparse_object(player, tmp->cause); notify(player, tprintf(" Enactor: %s%s", bp, bufp)); free_lbuf(bp); } free_lbuf(bufp); } else if (tmp->player == 0) { (*qdel)++; } } return; } void do_ps(dbref player, dbref cause, int key, char *target) { char *bufp; dbref player_targ, obj_targ; int pqent, pqtot, pqdel, oqent, oqtot, oqdel, wqent, wqtot, sqent, sqtot, i; /* Figure out what to list the queue for */ if (!target || !*target) { obj_targ = NOTHING; if (key & PS_ALL) { player_targ = NOTHING; } else { player_targ = Owner(player); if (Typeof(player) != TYPE_PLAYER) obj_targ = player; } } else { player_targ = Owner(player); obj_targ = match_controlled(player, target); if (obj_targ == NOTHING) return; if (key & PS_ALL) { notify(player, "Can't specify a target and /all"); return; } if (Typeof(obj_targ) == TYPE_PLAYER) { player_targ = obj_targ; obj_targ = NOTHING; } } key = key & ~PS_ALL; switch (key) { case PS_BRIEF: case PS_SUMM: case PS_LONG: break; default: notify(player, "Illegal combination of switches."); return; } /* Go do it */ show_que(player, key, mudstate.qfirst, &pqtot, &pqent, &pqdel, player_targ, obj_targ, "Player"); show_que(player, key, mudstate.qlfirst, &oqtot, &oqent, &oqdel, player_targ, obj_targ, "Object"); show_que(player, key, mudstate.qwait, &wqtot, &wqent, &i, player_targ, obj_targ, "Wait"); show_que(player, key, mudstate.qsemfirst, &sqtot, &sqent, &i, player_targ, obj_targ, "Semaphore"); /* Display stats */ bufp = alloc_mbuf("do_ps"); if (Wizard(player)) sprintf(bufp,"Totals: Player...%d/%d[%ddel] Object...%d/%d[%ddel] Wait...%d/%d Semaphore...%d/%d", pqent, pqtot, pqdel, oqent, oqtot, oqdel, wqent, wqtot, sqent, sqtot); else sprintf(bufp,"Totals: Player...%d/%d Object...%d/%d Wait...%d/%d Semaphore...%d/%d", pqent, pqtot, oqent, oqtot, wqent, wqtot, sqent, sqtot); notify(player, bufp); free_mbuf(bufp); } /* --------------------------------------------------------------------------- * do_queue: Queue management */ void do_queue (dbref player, dbref cause, int key, char *arg) { int i, ncmds, was_disabled; was_disabled = 0; if (key == QUEUE_KICK) { i = atol(arg); if ((mudconf.control_flags & CF_DEQUEUE) == 0) { was_disabled = 1; mudconf.control_flags |= CF_DEQUEUE; notify(player,"Warning: automatic dequeueing is disabled."); } ncmds = do_top(i); if (was_disabled) mudconf.control_flags &= ~CF_DEQUEUE; if (!Quiet(player)) notify(player, tprintf("%d commands processed.", ncmds)); } else if (key == QUEUE_WARP) { i = atol(arg); if ((mudconf.control_flags & CF_DEQUEUE) == 0) { was_disabled = 1; mudconf.control_flags |= CF_DEQUEUE; notify(player,"Warning: automatic dequeueing is disabled."); } do_second(i); if (was_disabled) mudconf.control_flags &= ~CF_DEQUEUE; if (Quiet(player)) return; if (i > 0) notify(player, tprintf("WaitQ timer advanced %d seconds.", i)); else if (i < 0) notify(player, tprintf("WaitQ timer set back %d seconds.", i)); else notify(player, "Object queue appended to player queue."); } }