#include "config.h" /* PLEASE go read the copyright notice contained in mush.h --Howard */ /* Ported to DaemonMUCK by Howard on March 23, 1993. */ #ifdef MUSH #include <ctype.h> #include <fcntl.h> #include <string.h> #include "copyright.h" #include "params.h" #include "externs.h" #include "db.h" #include "match.h" #include "mush.h" #include "money.h" #include "interface.h" extern int atoi(); typedef struct bque BQUE; struct bque { BQUE *next; dbref player; /* player who will do command */ dbref cause; /* player causing command (for %N) */ dbref sem; /* semaphore object to block on */ int left; /* seconds left until execution */ char *env[10]; /* environment, from wild match */ char *comm; /* command to be executed */ }; static BQUE *qfirst = NULL, *qlast = NULL, *qwait = NULL; static BQUE *qlfirst = NULL, *qllast = NULL; static BQUE *qsemfirst = NULL, *qsemlast = NULL; void burst_object_command() { int i = 0; while( i < MAX_MUSH_QUEUE && (do_top() > 0)) i++; } void parse_que(dbref player, char *command, dbref cause) { char buff[BUFFER_LEN], *s, *r; void big_que(); s = buff; strcpy(buff, command); while ((r = parse_to(&s, ';', 0)) != NULL) { if(payfor(OWNER(player), Q_COST)) big_que(player, r, cause); else notify(player, player, "Not enough money to queue command"); } } static int add_to_generic(dbref player, int am, int key) { int num = 0; char *a; char *atrname; char buff[MAX_COMMAND_LEN]; if (key == 0) { atrname = "QUEUE"; player = OWNER(player); } else { atrname = "SEMAPHORE"; } a = atr_get_noparent(player, atrname); /* don't get from the parent! */ if (a) num = atoi(a); num += am; if (num) sprintf(buff, "%d", num); else *buff = '\0'; (void) atr_add(player, atrname, buff); return (num); } static int add_to(dbref player, int am) { return (add_to_generic(player, am, 0)); } static int add_to_sem(dbref player, int am) { return (add_to_generic(player, am, 1)); } static int queue_limit(dbref player) { /* returns 1 if player has exceeded his queue limit */ if (HugeQueue(player)) { if (add_to(player, 1) > (2 * QUEUE_QUOTA)) return 1; else return 0; } else { if (add_to(player, 1) > QUEUE_QUOTA) return 1; else return 0; } return 0; /* NOTREACHED */ } void free_qentry(point) BQUE *point; { int a; for (a = 0; a < 10; a++) if (point->env[a]) { free((char *)point->env[a]); } if (point->comm) free((char *)point->comm); free((char *)point); } void big_que(dbref player, char *command, dbref cause) { int a; BQUE *tmp; if ((Typeof(player) != TYPE_PLAYER) && (Halted(player))) return; if (queue_limit(player)) { notify(OWNER(player), OWNER(player), tprintf("Runaway object: %s(#%d). Commands halted.", unparse_name(player), player)); /* wipe out that object's queue and set it HAVEN */ do_halt(OWNER(player), "", player); FLAGS(player) |= HAVEN; DBDIRTY(player); return; } tmp = (BQUE *) malloc (sizeof(BQUE)); strcpy((tmp->comm = (char *) malloc(strlen(command)+1)), command); tmp->player = player; tmp->next = NULL; tmp->left = 0; tmp->cause = cause; for (a = 0; a < 10; a++) if (!wptr[a]) tmp->env[a] = NULL; else { strcpy((tmp->env[a] = (char *)malloc(strlen(wptr[a])+1)), wptr[a]); } if (Typeof(cause) == TYPE_PLAYER) { if (qlast) { qlast->next = tmp; qlast = tmp; } else qlast = qfirst = tmp; } else { if (qllast) { qllast->next = tmp; qllast = tmp; } else qllast = qlfirst = tmp; } } void wait_que(dbref player, int wait, char *command, dbref cause, dbref sem) { BQUE *tmp; int a; /* make sure player can afford to do it */ tmp = (BQUE *)malloc(sizeof(BQUE)); strcpy((tmp->comm = (char *)malloc(strlen(command)+1)), command); tmp->player = player; tmp->cause = cause; tmp->sem = sem; tmp->left = wait; for (a = 0; a < 10; a++) { if (!wptr[a]) tmp->env[a] = NULL; else { strcpy((tmp->env[a] = (char *)malloc(strlen(wptr[a])+1)), wptr[a]); } } if (sem == NOTHING) { /* No semaphore, put on normal wait queue */ tmp->next = qwait; qwait = tmp; } else { /* Put it on the semaphore queue */ tmp->next = NULL; if (qsemlast != NULL) qsemlast->next = tmp; else qsemfirst = tmp; qsemlast = tmp; } } void do_second() { /* call every second to check for wait queue commands */ BQUE *trail = NULL, *point, *next; int a; /* move contents of low priority queue onto end of normal one * this helps to keep objects from getting out of control since * its effects on other objects happen only after one second * this should allow @halt to be typed before getting blown away * by scrolling text. */ if (qlfirst) { if (qlast) qlast->next = qlfirst; else qfirst = qlfirst; qlast = qllast; qllast = qlfirst = NULL; } /* check regular wait queue */ for (point = qwait, trail = NULL; point; point = next) { /* * Note: this would be 0 except the command is being put in the low * priority queue to be done in one second anyways */ if (point->left-- <= 1) { if (trail != NULL) trail->next = next = point->next; else qwait = next = point->next; for (a = 0; a < 10; a++) { wptr[a] = point->env[a]; } parse_que(point->player, point->comm, point->cause); free_qentry(point); } else next = (trail = point)->next; } /* check for semaphore timeouts */ for (point = qsemfirst, trail = NULL; point; point = next) { if (point->left < 0) { next = (trail = point)->next; continue; /* skip non-timed waits */ } if (point->left-- <= 1) { if (trail != NULL) trail->next = next = point->next; else qsemfirst = next = point->next; if (point == qsemlast) qsemlast = trail; add_to_sem(point->sem, -1); point->sem = NOTHING; for (a = 0; a < 10; a++) { wptr[a] = point->env[a]; } parse_que(point->player, point->comm, point->cause); free_qentry(point); } else next = (trail = point)->next; } } int test_top() { return (qfirst ? 1 : 0); } /* execute one command off the top of the queue */ int do_top() { int a; int i = 0; BQUE *tmp; dbref player; char tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN]; char tbuf3[BUFFER_LEN], *s; if (!qfirst) return (0); if (qfirst->player && GoodObject(qfirst->player)) { /* cplr = qfirst->player; strcpy(ccom, qfirst->comm); */ add_to(player = qfirst->player, -1); qfirst->player = 0; if ((Typeof(player) == TYPE_PLAYER || !Halted(player)) && !(FLAGS(player) & INTERACTIVE)) { /* if ((Typeof(player) == TYPE_PLAYER && !(FLAGS(player) & INTERACTIVE)) || !Halted(player)) { */ for (a = 0; a < 10; a++) { wptr[a] = qfirst->env[a]; } strcpy(tbuf2, qfirst->comm); tbuf3[0] = '\0'; for (s = tbuf2; *s; tbuf3[i++] = toupper(*s++)) ; tbuf3[i++] = '\0'; strcpy(tbuf1, tbuf2); mush_interp = 1; process_command(player, tbuf1, qfirst->cause); mush_interp = 0; } } tmp = qfirst->next; free_qentry(qfirst); if (!(qfirst = tmp)) qlast = NULL; return (1); } int nfy_que(sem, key, count) dbref sem; int key; /* 0 is normal, 1 is all, 2 is drain */ int count; { BQUE *point, *trail, *next; int num; char *a; int x; a = atr_get_noparent(sem, "SEMAPHORE"); if (a) num = atoi(a); else num = 0; if (num > 0) { num = 0; for (point = qsemfirst, trail = NULL; point; point = next) { if (point->sem == sem) { num++; if (trail) trail->next = next = point->next; else qsemfirst = next = point->next; if (point == qsemlast) qsemlast = trail; /* run or discard the command */ for (x = 0; x < 10; x++) wptr[x] = point->env[x]; if (key != 2) parse_que(point->player, point->comm, point->cause); else free_qentry(point); } else { next = (trail = point)->next; } /* if we've notified enough, exit. Note that we don't have to check * for the case of @notify/all, since we don't break out of this * loop "manually" unless the key is standard @notify. */ if ((key == 0) && (num >= count)) next = NULL; } } else { num = 0; } /* update the semaphore waiter's count */ if (key == 0) add_to_sem(sem, -count); else atr_add(sem, "SEMAPHORE", ""); return num; } void do_notify(player, cause, key, what, count) dbref player; dbref cause; int key; /* 0 is normal, 1 is all, 2 is drain */ char *what; char *count; { dbref thing; match_data md; int i; /* find it */ init_match(player, what, NOTYPE, &md); match_everything(&md); if(Wizard(player)) match_absolute(&md); /* Addition by Howard */ if ((thing = noisy_match_result(&md)) == NOTHING) return; /* must control something or have it link_ok in order to use it as * as a semaphore. */ if (!controls(player, thing) && !LinkOk(thing)) { notify(player, player, "Permission denied."); return; } /* find how many times to notify */ if (count && *count) i = atoi(count); else i = 1; if (i > 0) nfy_que(thing, key, i); if (key != 2) { quiet_notify(player, "Notified."); } else { quiet_notify(player, "Drained."); } } void do_wait(dbref player, dbref cause, char *arg1, char *cmd) { dbref thing; char *tcount; match_data md; int waitfor, num; char *a; char *arg2; arg2 = strip_braces(cmd); if (is_number(arg1)) { /* normal wait */ wait_que(player, atoi(arg1), arg2, cause, NOTHING); free(arg2); return; } /* semaphore wait with optional timeout */ /* find the thing to wait on */ tcount = (char *) index(arg1, '/'); if (tcount) *tcount++ = '\0'; init_match(player, arg1, NOTYPE, &md); if(Wizard(player)) match_absolute(&md); /* Addition by Howard */ match_everything(&md); if ((thing = noisy_match_result(&md)) == NOTHING) { free(arg2); return; } if (!controls(player, thing) && !LinkOk(thing)) { notify(player, player, "Permission denied."); free(arg2); return; } /* get timeout, default of -1 */ if (tcount && *tcount) waitfor = atol(tcount); else waitfor = -1; add_to_sem(thing, 1); a = atr_get_noparent(thing, "SEMAPHORE"); if (a) num = atoi(a); else num = 0; if (num <= 0) thing = NOTHING; wait_que(player, waitfor, arg2, cause, thing); free(arg2); } void do_queue(dbref player, char *what) { /* tell player what commands they have pending in the queue (@ps) */ BQUE *tmp; dbref victim = NOTHING; int everything = 0; int quick = 0; int pq = 0, oq = 0, wq = 0, sq = 0; int tpq = 0, toq = 0, twq = 0, tsq = 0; match_data md; if (!strcasecmp(what, "count")) { victim = player; everything = 1; quick = 1; } else if (LookQueue(player)) { if(!what || !*what) victim = player; else if(!strcasecmp(what, "all")) { /* do the whole thing */ victim = player; everything = 1; } else { init_match(player, what, TYPE_PLAYER, &md); match_player(&md); match_absolute(&md); match_me(&md); victim = match_result(&md); } } else { victim = player; } switch(victim) { case NOTHING: notify(player, player, "I couldn't find that player."); break; case AMBIGUOUS: notify(player, player, "I don't know who you mean!"); break; default: if(everything == 1) notify(player, player, "Queue for : all"); else notify(player, player, tprintf("Queue for : %s", unparse_name(victim))); if (!quick) notify(player,player, "Player Queue:"); for (tmp = qfirst; tmp; tmp = tmp->next) { tpq++; if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) { pq++; if (!quick) notify(player, player, tprintf("%s:%s", unparse_object(player, tmp->player), tmp->comm)); } } if (!quick) notify(player,player, "Object Queue:"); for (tmp = qlfirst; tmp; tmp = tmp->next) { toq++; if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) { oq++; if (!quick) notify(player, player, tprintf("%s:%s", unparse_object(player, tmp->player), tmp->comm)); } } if (!quick) notify(player, player, "Wait Queue:"); for (tmp = qwait; tmp; tmp = tmp->next) { twq++; if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) { wq++; if (!quick) notify(player, player, tprintf("[%d]%s:%s", tmp->left, unparse_object(player, tmp->player), tmp->comm)); } } if (!quick) notify(player, player, "Semaphore Queue:"); for (tmp = qsemfirst; tmp; tmp = tmp->next) { tsq++; if ((OWNER(tmp->player) == OWNER(victim)) || (everything)) { sq++; if (!quick) { if (tmp->left != -1) notify(player, player, tprintf("[#%d/%d]%s:%s", tmp->sem, tmp->left, unparse_object(player, tmp->player), tmp->comm)); else notify(player, player, tprintf("[#%d]%s:%s", tmp->sem, unparse_object(player, tmp->player), tmp->comm)); } } } notify(player,player, "------------ Queue Done ------------"); notify(player,player, tprintf( "Totals: Player...%d/%d Object...%d/%d Wait...%d/%d Semaphore...%d/%d", pq, tpq, oq, toq, wq, twq, sq, tsq)); } } void do_halt(dbref owner, char *ncom, dbref victim) { BQUE *tmp, *trail = NULL, *point, *next; int num = 0; dbref player; if (victim == NOTHING) player = owner; else player = victim; quiet_notify(OWNER(player), "Halted."); for (tmp = qfirst; tmp; tmp = tmp->next) if ((tmp->player == player) || (OWNER(tmp->player) == player)) { num--; tmp->player = 0; } for (tmp = qlfirst; tmp; tmp = tmp->next) if (((tmp->player == player) || (OWNER(tmp->player) == player))) { num--; tmp->player = 0; } /* remove wait q stuff */ for (point = qwait; point; point = next) { if (((point->player == player) || (OWNER(point->player) == player))) { if (trail) trail->next = next = point->next; else qwait = next = point->next; free_qentry(point); } else next = (trail = point)->next; } /* clear semaphore queue */ for (point = qsemfirst, trail = NULL; point; point = next) { if (((point->player == player) || (OWNER(point->player) == player))) { if (trail) trail->next = next = point->next; else qsemfirst = next = point->next; if (point == qsemlast) qsemlast = trail; add_to_sem(point->sem, -1); free_qentry(point); } else next = (trail = point)->next; } if (OWNER(player) == player) (void) atr_add(player, "QUEUE", ""); else add_to(player, num); if (*ncom) parse_que(player, ncom, player); } void do_halt1(dbref player, char *arg1, char *arg2) { dbref victim; match_data md; if (*arg1 == '\0') do_halt(player, "", player); else { init_match(player, arg1, NOTYPE, &md); match_neighbor(&md); match_possession(&md); match_me(&md); match_absolute(&md); match_player(&md); if ((victim = noisy_match_result(&md)) == NOTHING) return; if (!controls(player, victim)) { notify(player, player, "Permission denied."); return; } /* if not player and no new command provided, set it HAVEN */ if ((Typeof(victim) != TYPE_PLAYER) && (*arg2 == '\0')) { FLAGS(victim) |= HAVEN; DBDIRTY(player); } if (OWNER(victim) != player) { if (Typeof(victim) == TYPE_PLAYER) { notify(player, player, tprintf("All objects for %s have been halted.", unparse_name(victim))); notify(victim, player, tprintf("All of your objects have been halted by %s.", unparse_name(player))); } else { notify(player, player, tprintf("Halted: %s's %s(#%d)", unparse_name(OWNER(victim)), unparse_name(victim), victim)); notify (OWNER(victim), OWNER(victim), tprintf("Halted: %s(#%d), by %s", unparse_name(victim), victim, unparse_name(player))); } } else { if (victim == player) notify(player, player, "All of your objects have been halted."); else notify(player, player, tprintf("Halted: %s(#%d)", unparse_name(victim), victim)); } do_halt(player, arg2, victim); } } void do_allhalt(dbref player) { dbref victim; if (!Wizard(player)) { notify(player, player, "You do not have the power to bring the world to a halt."); return; } for(victim = 0;victim <db_top; victim++) { if(Typeof(victim) == TYPE_PLAYER) { notify(victim,player, tprintf("Your objects have been globally halted by %s", unparse_name(player))); do_halt(victim, "", victim); } } } void atr_add(dbref player, char *atrname, char *buff) { if(*atrname && *buff && GoodObject(player)) add_property(player, atrname, buff, default_perms(atrname), ACCESS_WI); else if(*atrname && GoodObject(player)) remove_property(player, atrname, ACCESS_WI); } void do_kick(dbref player, char *arg1) { int i=0, n; /* executes <num> commands off the queue immediately */ if (!Wizard(player)) { notify(player, player, "Permission denied."); return; } if (!arg1 || !*arg1) { notify(player, player, "How many commands do you want to execute?"); return; } n = atoi(arg1); if ((n <= 0) || (n > COMMAND_BURST_SIZE * 2)) { notify(player, player, "Number out of range."); return; } while (i < n && do_top() > 0) i++; notify(player, player, tprintf("%d commands executed.", i)); } /* this is a hack which just strips a level of braces. It malloc()s memory * which must be free()d later. */ char *strip_braces(char *line1) { char *buff, *tstr, *tbuf; char *bufc, *str = line1; buff = (char *) malloc(BUFFER_LEN + 1); bufc = buff; while (isspace(*str)) /* eat spaces at the beginning */ str++; switch (*str) { case '{': tstr = str++; tbuf = parse_to(&str, '}', 0); if (str == NULL) { str = tstr; safe_str(tstr, buff, &bufc); *bufc = '\0'; return buff; } else { safe_str(tbuf, buff, &bufc); str--; *bufc = '\0'; return buff; } break; /* NOT REACHED */ default: strcpy(buff, str); return buff; } } char *atr_get_noparent(dbref thing, char *atr) { char tmp[BUFFER_LEN]; propdir *ptr; if(!GoodObject(thing) || !atr || !*atr) return NULL; for(ptr = DBFETCHPROP(thing); ptr; ptr = ptr->next) { if (ptr && ptr->data && !strcasecmp(ptr->name, atr)) { strcpy(tmp, uncompress(ptr->data)); return tmp; } } return (char *) NULL; } #endif