// cque.cpp -- commands and functions for manipulating the command queue.
//
// $Id: cque.cpp,v 1.13 2000/08/03 21:42:18 sdennis Exp $
//
#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"
#include <signal.h>
#include "mudconf.h"
#include "db.h"
#include "htab.h"
#include "interface.h"
#include "match.h"
#include "attrs.h"
#include "flags.h"
#include "powers.h"
#include "command.h"
#include "alloc.h"
extern int FDECL(a_Queue, (dbref, int));
extern void FDECL(s_Queue, (dbref, int));
extern int FDECL(QueueMax, (dbref));
CLinearTimeDelta GetProcessorUsage(void)
{
CLinearTimeDelta ltd;
#ifdef WIN32
if (platform == VER_PLATFORM_WIN32_NT)
{
FILETIME ftCreate;
FILETIME ftExit;
FILETIME ftKernel;
FILETIME ftUser;
fpGetProcessTimes(hGameProcess, &ftCreate, &ftExit, &ftKernel, &ftUser);
ltd.Set100ns(*(INT64 *)(&ftUser));
return ltd;
}
// Win9x - We can really only report the system time with a
// high-resolution timer. This doesn't seperate the time from
// this process from other processes that are also running on
// the same machine, but it's the best we can do on Win9x.
//
// The scheduling in Win9x is totally screwed up, so even if
// Win9x did provide the information, it would certainly be
// wrong.
//
if (bQueryPerformanceAvailable)
{
// The QueryPerformanceFrequency call at game startup
// succeeded. The frequency number is the number of ticks
// per second.
//
INT64 li;
if (QueryPerformanceCounter((LARGE_INTEGER *)&li))
{
li = QP_A*li + (QP_B*li+QP_C)/QP_D;
ltd.Set100ns(li);
return ltd;
}
bQueryPerformanceAvailable = FALSE;
}
#endif // WIN32
#if !defined(WIN32) && defined(HAVE_GETRUSAGE)
struct rusage usage;
getrusage(RUSAGE_SELF, &usage);
ltd.SetTimeValueStruct(&usage.ru_utime);
return ltd;
#else // !WIN32 && HAVE_GETRUSAGE
// Either this Unix doesn't have getrusage or this is a
// fall-through case for Win32.
//
CLinearTimeAbsolute ltaNow;
ltaNow.GetLocal();
ltd = ltaNow - mudstate.start_time;
return ltd;
#endif // !WIN32 && HAVE_GETRUSAGE
}
/*
* ---------------------------------------------------------------------------
* * add_to: Adjust an object's queue or semaphore count.
*/
static int add_to(dbref player, int am, int attrnum)
{
int aflags;
dbref aowner;
char *atr_gotten = atr_get(player, attrnum, &aowner, &aflags);
int num = Tiny_atol(atr_gotten);
free_lbuf(atr_gotten);
num += am;
char buff[20];
int nlen = 0;
*buff = '\0';
if (num)
{
nlen = Tiny_ltoa(num, buff);
}
atr_add_raw_LEN(player, attrnum, buff, nlen);
return num;
}
// This Task assumes that pEntry is already unlinked from any lists it may have been related to.
//
void Task_RunQueueEntry(void *pEntry, int iUnused)
{
BQUE *point = (BQUE *)pEntry;
dbref player = point->player;
if ((player >= 0) && !Going(player))
{
giveto(player, mudconf.waitcost);
mudstate.curr_enactor = point->cause;
mudstate.curr_player = player;
a_Queue(Owner(player), -1);
point->player = NOTHING;
if (!Halted(player))
{
// Load scratch args.
//
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
{
if (point->scr[i])
{
int n = strlen(point->scr[i]);
memcpy(mudstate.global_regs[i], point->scr[i], n+1);
mudstate.glob_reg_len[i] = n;
}
else
{
mudstate.global_regs[i][0] = '\0';
mudstate.glob_reg_len[i] = 0;
}
}
char *command = point->comm;
while (command)
{
char *cp = parse_to(&command, ';', 0);
if (cp && *cp)
{
int numpipes = 0;
while ( command
&& (*command == '|')
&& (numpipes < mudconf.ntfy_nest_lim))
{
command++;
numpipes++;
mudstate.inpipe = 1;
mudstate.poutnew = alloc_lbuf("process_command.pipe");
mudstate.poutbufc = mudstate.poutnew;
mudstate.poutobj = player;
// No lag check on piped commands.
//
process_command(player, point->cause, 0, cp, point->env, point->nargs);
if (mudstate.pout)
{
free_lbuf(mudstate.pout);
mudstate.pout = NULL;
}
*mudstate.poutbufc = '\0';
mudstate.pout = mudstate.poutnew;
cp = parse_to(&command, ';', 0);
}
mudstate.inpipe = 0;
CLinearTimeAbsolute ltaBegin;
ltaBegin.GetUTC();
CLinearTimeDelta ltdUsageBegin = GetProcessorUsage();
process_command(player, point->cause, 0, cp, point->env, point->nargs);
CLinearTimeAbsolute ltaEnd;
ltaEnd.GetUTC();
CLinearTimeDelta ltdUsageEnd = GetProcessorUsage();
CLinearTimeDelta ltd = ltdUsageEnd - ltdUsageBegin;
db[player].cpu_time_used += ltd;
if (mudstate.pout)
{
free_lbuf(mudstate.pout);
mudstate.pout = NULL;
}
ltd = ltaEnd - ltaBegin;
if (ltd.ReturnSeconds() >= mudconf.max_cmdsecs)
{
STARTLOG(LOG_PROBLEMS, "CMD", "CPU");
log_name_and_loc(player);
char *logbuf = alloc_lbuf("do_top.LOG.cpu");
long ms = ltd.ReturnMilliseconds();
sprintf(logbuf, " queued command taking %d.%02d secs (enactor #%d): ", ms/100, ms%100, point->cause);
log_text(logbuf);
free_lbuf(logbuf);
//log_text(log_cmdbuf);
ENDLOG;
}
}
}
}
MEMFREE(point->text);
free_qentry(point);
}
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
{
mudstate.global_regs[i][0] = '\0';
mudstate.glob_reg_len[i] = 0;
}
}
/*
* ---------------------------------------------------------------------------
* * 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;
}
void Task_SemaphoreTimeout(void *pExpired, int iUnused)
{
// A semaphore has timed out.
//
BQUE *point = (BQUE *)pExpired;
add_to(point->sem, -1, point->attr);
point->sem = NOTHING;
Task_RunQueueEntry(point, 0);
}
dbref Halt_Player_Target;
dbref Halt_Object_Target;
int Halt_HaltedEntries;
int CallBack_HaltQueue(PTASK_RECORD p)
{
if (p->fpTask == Task_RunQueueEntry || p->fpTask == Task_SemaphoreTimeout)
{
// This is a @wait or timed semaphore task.
//
BQUE *point = (BQUE *)(p->arg_voidptr);
if (que_want(point, Halt_Player_Target, Halt_Object_Target))
{
Halt_HaltedEntries++;
if (p->fpTask == Task_SemaphoreTimeout)
{
add_to(point->sem, -1, point->attr);
}
MEMFREE(point->text);
free_qentry(point);
return IU_REMOVE_TASK;
}
}
return IU_NEXT_TASK;
}
/*
* ---------------------------------------------------------------------------
* * halt_que: Remove all queued commands from a certain player
*/
int halt_que(dbref player, dbref object)
{
Halt_Player_Target = player;
Halt_Object_Target = object;
Halt_HaltedEntries = 0;
// Process @wait, timed semaphores, and untimed semaphores.
//
scheduler.TraverseUnordered(CallBack_HaltQueue);
if (player == NOTHING)
{
player = Owner(object);
}
giveto(player, (mudconf.waitcost * Halt_HaltedEntries));
if (object == NOTHING)
{
s_Queue(player, 0);
}
else
{
a_Queue(player, -Halt_HaltedEntries);
}
return Halt_HaltedEntries;
}
/*
* ---------------------------------------------------------------------------
* * 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;
if ((key & HALT_ALL) && !(Can_Halt(player)))
{
notify(player, NOPERM_MESSAGE);
return;
}
// 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
{
if (Can_Halt(player))
obj_targ = match_thing(player, target);
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));
}
int Notify_Key;
int Notify_Num_Requested;
int Notify_Num_Done;
int Notify_Num_Max;
int Notify_Sem;
int Notify_Attr;
int CallBack_NotifySemaphore(PTASK_RECORD p)
{
// If we've notified enough, exit.
//
if ((Notify_Key == NFY_NFY) && (Notify_Num_Done >= Notify_Num_Max))
{
return IU_DONE;
}
if (p->fpTask == Task_SemaphoreTimeout)
{
// This represents a semaphore.
//
BQUE *point = (BQUE *)(p->arg_voidptr);
if ( (point->sem == Notify_Sem)
&& ((point->attr == Notify_Attr) || !Notify_Attr))
{
if (Notify_Key == NFY_DRAIN)
{
// Discard the command
//
giveto(point->player, mudconf.waitcost);
a_Queue(Owner(point->player), -1);
MEMFREE(point->text);
free_qentry(point);
return IU_REMOVE_TASK;
}
else
{
// Allow the command to run. The priority may have been
// PRIORITY_SUSPEND, so we need to change it.
//
if (Typeof(point->cause) == TYPE_PLAYER)
{
p->iPriority = PRIORITY_PLAYER;
}
else
{
p->iPriority = PRIORITY_OBJECT;
}
p->fpTask = Task_RunQueueEntry;
return IU_UPDATE_TASK;
}
}
}
return IU_NEXT_TASK;
}
/*
* ---------------------------------------------------------------------------
* * nfy_que: Notify commands from the queue and perform or discard them.
*/
int nfy_que(dbref sem, int attr, int key, int count)
{
int aflags;
dbref aowner;
char *str;
int Notify_Num_Requested = 1;
if (attr)
{
str = atr_get(sem, attr, &aowner, &aflags);
Notify_Num_Requested = Tiny_atol(str);
free_lbuf(str);
}
int Notify_Num_Done = 0;
if (Notify_Num_Requested > 0)
{
Notify_Key = key;
Notify_Sem = sem;
Notify_Attr = attr;
Notify_Num_Max = count;
scheduler.TraverseUnordered(CallBack_NotifySemaphore);
}
// Update the sem waiters count.
//
if (key == NFY_NFY)
{
add_to(sem, -count, attr);
}
else
{
atr_clr(sem, attr);
}
return Notify_Num_Done;
}
/*
* ---------------------------------------------------------------------------
* * do_notify: Command interface to nfy_que
*/
void do_notify(dbref player, dbref cause, int key, char *what, char *count)
{
dbref thing, aowner;
int loccount, attr = 0, aflags;
ATTR *ap;
char *obj;
obj = parse_to(&what, '/', 0);
init_match(player, obj, NOTYPE);
match_everything(0);
if ((thing = noisy_match_result()) < 0)
{
notify(player, "No match.");
}
else if (!controls(player, thing) && !Link_ok(thing))
{
notify(player, NOPERM_MESSAGE);
}
else
{
if (!what || !*what)
{
ap = NULL;
}
else
{
ap = atr_str(what);
}
if (!ap)
{
attr = A_SEMAPHORE;
}
else
{
/* Do they have permission to set this attribute? */
atr_pget_info(thing, ap->number, &aowner, &aflags);
if (Set_attr(player, thing, ap, aflags))
{
attr = ap->number;
}
else
{
notify_quiet(player, NOPERM_MESSAGE);
return;
}
}
if (count && *count)
loccount = Tiny_atol(count);
else
loccount = 1;
if (loccount > 0)
{
nfy_que(thing, attr, key, loccount);
if (!(Quiet(player) || Quiet(thing)))
{
if (key == NFY_DRAIN)
notify_quiet(player, "Drained.");
else
notify_quiet(player, "Notified.");
}
}
}
}
/*
* ---------------------------------------------------------------------------
* * setup_que: Set up a queue entry.
*/
static BQUE *setup_que(dbref player, dbref cause, char *command, char *args[], int nargs, char *sargs[])
{
int a;
BQUE *tmp;
char *tptr;
// Can we run commands at all?
//
if (Halted(player))
return NULL;
// Make sure player can afford to do it.
//
a = mudconf.waitcost;
if (mudconf.machinecost && RandomLong(0, mudconf.machinecost-1) == 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 = QueueMax(Owner(player));
if (a_Queue(Owner(player), 1) > 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_Halted(player);
return NULL;
}
// We passed all the tests.
//
// Calculate the length of the save string.
//
unsigned int tlen = 0;
static unsigned int nCommand;
static unsigned int nLenEnv[NUM_ENV_VARS];
static unsigned int nLenRegs[MAX_GLOBAL_REGS];
if (command)
{
nCommand = strlen(command) + 1;
tlen = nCommand;
}
if (nargs > NUM_ENV_VARS)
{
nargs = NUM_ENV_VARS;
}
for (a = 0; a < nargs; a++)
{
if (args[a])
{
nLenEnv[a] = strlen(args[a]) + 1;
tlen += nLenEnv[a];
}
}
if (sargs)
{
for (a = 0; a < MAX_GLOBAL_REGS; a++)
{
if (sargs[a])
{
nLenRegs[a] = strlen(sargs[a]) + 1;
tlen += nLenRegs[a];
}
}
}
// Create the qeue entry and load the save string.
//
tmp = alloc_qentry("setup_que.qblock");
tmp->comm = NULL;
tptr = tmp->text = (char *)MEMALLOC(tlen);
ISOUTOFMEMORY(tptr);
if (command)
{
memcpy(tptr, command, nCommand);
tmp->comm = tptr;
tptr += nCommand;
}
for (a = 0; a < nargs; a++)
{
if (args[a])
{
memcpy(tptr, args[a], nLenEnv[a]);
tmp->env[a] = tptr;
tptr += nLenEnv[a];
}
else
{
tmp->env[a] = NULL;
}
}
for ( ; a < NUM_ENV_VARS; a++)
{
tmp->env[a] = NULL;
}
for (a = 0; a < MAX_GLOBAL_REGS; a++)
{
tmp->scr[a] = NULL;
}
if (sargs)
{
for (a = 0; a < MAX_GLOBAL_REGS; a++)
{
if (sargs[a])
{
memcpy(tptr, sargs[a], nLenRegs[a]);
tmp->scr[a] = tptr;
tptr += nLenRegs[a];
}
}
}
// Load the rest of the queue block.
//
tmp->player = player;
tmp->IsTimed = FALSE;
tmp->sem = NOTHING;
tmp->attr = 0;
tmp->cause = cause;
tmp->nargs = nargs;
return tmp;
}
/*
* ---------------------------------------------------------------------------
* * wait_que: Add commands to the wait or semaphore queues.
*/
void wait_que(dbref player, dbref cause, int wait, dbref sem, int attr, char *command, char *args[], int nargs, char *sargs[])
{
if (!(mudconf.control_flags & CF_INTERP)) return;
BQUE *tmp = setup_que(player, cause, command, args, nargs, sargs);
if (!tmp) return;
int iPriority;
if (Typeof(tmp->cause) == TYPE_PLAYER)
{
iPriority = PRIORITY_PLAYER;
}
else
{
iPriority = PRIORITY_OBJECT;
}
if (wait == 0)
{
// This means that waitime is not used.
//
tmp->IsTimed = FALSE;
}
else
{
tmp->IsTimed = TRUE;
tmp->waittime.GetUTC();
CLinearTimeDelta ltdWait;
ltdWait.SetSeconds(wait);
tmp->waittime += ltdWait;
}
tmp->sem = sem;
tmp->attr = attr;
if (sem == NOTHING)
{
// Not a semaphore, so let it run it immediately or put it on
// the wait queue.
//
if (tmp->IsTimed)
{
scheduler.DeferTask(tmp->waittime, iPriority, Task_RunQueueEntry, tmp, 0);
}
else
{
scheduler.DeferImmediateTask(iPriority, Task_RunQueueEntry, tmp, 0);
}
}
else
{
if (!tmp->IsTimed)
{
// In this case, the timeout task below will never run,
// but it allows us to manage all semaphores together in
// the same data structure.
//
iPriority = PRIORITY_SUSPEND;
}
scheduler.DeferTask(tmp->waittime, iPriority, Task_SemaphoreTimeout, tmp, 0);
}
}
/*
* ---------------------------------------------------------------------------
* * 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, attr, aflags;
char *what;
ATTR *ap;
/*
* If arg1 is all numeric, do simple (non-sem) timed wait.
*/
if (is_number(event))
{
howlong = Tiny_atol(event);
wait_que(player, cause, howlong, NOTHING, 0, cmd, cargs, ncargs, mudstate.global_regs);
return;
}
/*
* Semaphore wait with optional timeout
*/
what = parse_to(&event, '/', 0);
init_match(player, what, NOTYPE);
match_everything(0);
thing = noisy_match_result();
if (!Good_obj(thing))
{
notify(player, "No match.");
}
else if (!controls(player, thing) && !Link_ok(thing))
{
notify(player, NOPERM_MESSAGE);
}
else
{
/*
* Get timeout, default 0
*/
if (event && *event && is_number(event))
{
attr = A_SEMAPHORE;
howlong = Tiny_atol(event);
}
else
{
attr = A_SEMAPHORE;
howlong = 0;
}
if (event && *event && !is_number(event))
{
ap = atr_str(event);
if (!ap)
{
attr = mkattr(event);
if (attr <= 0) {
notify_quiet(player, "Invalid attribute.");
return;
}
ap = atr_num(attr);
}
atr_pget_info(thing, ap->number, &aowner, &aflags);
if (attr && Set_attr(player, thing, ap, aflags))
{
attr = ap->number;
howlong = 0;
}
else
{
notify_quiet(player, NOPERM_MESSAGE);
return;
}
}
num = add_to(thing, 1, attr);
if (num <= 0)
{
/*
* thing over-notified, run the command immediately
*/
thing = NOTHING;
howlong = 0;
}
wait_que(player, cause, howlong, thing, attr, cmd, cargs, ncargs, mudstate.global_regs);
}
}
CLinearTimeAbsolute Show_lsaNow;
int Total_SystemTasks;
int Total_RunQueueEntry;
int Shown_RunQueueEntry;
int Total_SemaphoreTimeout;
int Shown_SemaphoreTimeout;
dbref Show_Player_Target;
dbref Show_Object_Target;
int Show_Key;
dbref Show_Player;
int Show_bFirstLine;
#ifdef WIN32
void Task_FreeDescriptor(void *arg_voidptr, int arg_Integer);
#endif
void dispatch_DatabaseDump(void *pUnused, int iUnused);
void dispatch_FreeListReconstruction(void *pUnused, int iUnused);
void dispatch_IdleCheck(void *pUnused, int iUnused);
void dispatch_CheckEvents(void *pUnused, int iUnused);
#ifndef MEMORY_BASED
void dispatch_CacheTick(void *pUnused, int iUnused);
#endif
int CallBack_ShowDispatches(PTASK_RECORD p)
{
Total_SystemTasks++;
CLinearTimeDelta ltd = p->ltaWhen - Show_lsaNow;
if (p->fpTask == dispatch_DatabaseDump)
{
notify(Show_Player, tprintf("[%d]auto-@dump", ltd.ReturnSeconds()));
}
else if (p->fpTask == dispatch_FreeListReconstruction)
{
notify(Show_Player, tprintf("[%d]auto-@dbck", ltd.ReturnSeconds()));
}
else if (p->fpTask == dispatch_IdleCheck)
{
notify(Show_Player, tprintf("[%d]Check for idle players", ltd.ReturnSeconds()));
}
else if (p->fpTask == dispatch_CheckEvents)
{
notify(Show_Player, tprintf("[%d]Test for @daily time", ltd.ReturnSeconds()));
}
#ifndef MEMORY_BASED
else if (p->fpTask == dispatch_CacheTick)
{
notify(Show_Player, tprintf("[%d]Database cache tick", ltd.ReturnSeconds()));
}
#endif // MEMORY_BASED
else if (p->fpTask == Task_ProcessCommand)
{
notify(Show_Player, tprintf("[%d]Further command quota", ltd.ReturnSeconds()));
}
#ifdef WIN32
else if (p->fpTask == Task_FreeDescriptor)
{
notify(Show_Player, tprintf("[%d]Delayed descriptor deallocation", ltd.ReturnSeconds()));
}
#endif // WIN32
else
{
Total_SystemTasks--;
}
return IU_NEXT_TASK;
}
void ShowPsLine(BQUE *tmp)
{
char *bufp = unparse_object(Show_Player, tmp->player, 0);
if (tmp->IsTimed && (Good_obj(tmp->sem)))
{
CLinearTimeDelta ltd = tmp->waittime - Show_lsaNow;
notify(Show_Player, tprintf("[#%d/%d]%s:%s", tmp->sem, ltd.ReturnSeconds(), bufp, tmp->comm));
}
else if (tmp->IsTimed)
{
CLinearTimeDelta ltd = tmp->waittime - Show_lsaNow;
notify(Show_Player, tprintf("[%d]%s:%s", ltd.ReturnSeconds(), bufp, tmp->comm));
}
else if (Good_obj(tmp->sem))
{
notify(Show_Player, tprintf("[#%d]%s:%s", tmp->sem, bufp, tmp->comm));
}
else
{
notify(Show_Player, tprintf("%s:%s", bufp, tmp->comm));
}
char *bp = bufp;
if (Show_Key == PS_LONG)
{
for (int i = 0; i < tmp->nargs; i++)
{
if (tmp->env[i] != NULL)
{
safe_str((char *)"; Arg", bufp, &bp);
safe_chr((char)(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(Show_Player, tmp->cause, 0);
notify(Show_Player, tprintf(" Enactor: %s%s", bp, bufp));
free_lbuf(bp);
}
free_lbuf(bufp);
}
int CallBack_ShowWait(PTASK_RECORD p)
{
if (p->fpTask != Task_RunQueueEntry)
{
return IU_NEXT_TASK;
}
Total_RunQueueEntry++;
BQUE *tmp = (BQUE *)(p->arg_voidptr);
if (que_want(tmp, Show_Player_Target, Show_Object_Target))
{
Shown_RunQueueEntry++;
if (Show_Key == PS_SUMM)
{
return IU_NEXT_TASK;
}
if (Show_bFirstLine)
{
notify(Show_Player, "----- Wait Queue -----");
Show_bFirstLine = FALSE;
}
ShowPsLine(tmp);
}
return IU_NEXT_TASK;
}
int CallBack_ShowSemaphore(PTASK_RECORD p)
{
if (p->fpTask != Task_SemaphoreTimeout)
{
return IU_NEXT_TASK;
}
Total_SemaphoreTimeout++;
BQUE *tmp = (BQUE *)(p->arg_voidptr);
if (que_want(tmp, Show_Player_Target, Show_Object_Target))
{
Shown_SemaphoreTimeout++;
if (Show_Key == PS_SUMM)
{
return IU_NEXT_TASK;
}
if (Show_bFirstLine)
{
notify(Show_Player, "----- Semaphore Queue -----");
Show_bFirstLine = FALSE;
}
ShowPsLine(tmp);
}
return IU_NEXT_TASK;
}
/*
* ---------------------------------------------------------------------------
* * do_ps: tell player what commands they have pending in the queue
*/
void do_ps(dbref player, dbref cause, int key, char *target)
{
char *bufp;
dbref player_targ, obj_targ;
// Figure out what to list the queue for.
//
if ((key & PS_ALL) && !(See_Queue(player)))
{
notify(player, NOPERM_MESSAGE);
return;
}
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;
}
Show_lsaNow.GetUTC();
Total_SystemTasks = 0;
Total_RunQueueEntry = 0;
Shown_RunQueueEntry = 0;
Total_SemaphoreTimeout = 0;
Shown_SemaphoreTimeout = 0;
Show_Player_Target = player_targ;
Show_Object_Target = obj_targ;
Show_Key = key;
Show_Player = player;
Show_bFirstLine = TRUE;
scheduler.TraverseOrdered(CallBack_ShowWait);
Show_bFirstLine = TRUE;
scheduler.TraverseOrdered(CallBack_ShowSemaphore);
if (Wizard(player))
{
notify(player, "----- System Queue -----");
scheduler.TraverseOrdered(CallBack_ShowDispatches);
}
// Display stats.
//
bufp = alloc_mbuf("do_ps");
sprintf(bufp, "Totals: Wait Queue...%d/%d Semaphores...%d/%d",
Shown_RunQueueEntry, Total_RunQueueEntry,
Shown_SemaphoreTimeout, Total_SemaphoreTimeout);
notify(player, bufp);
if (Wizard(player))
{
sprintf(bufp, " System Tasks.....%d", Total_SystemTasks);
notify(player, bufp);
}
free_mbuf(bufp);
}
CLinearTimeDelta ltdWarp;
int CallBack_Warp(PTASK_RECORD p)
{
if (p->fpTask == Task_RunQueueEntry || p->fpTask == Task_SemaphoreTimeout)
{
BQUE *point = (BQUE *)(p->arg_voidptr);
if (point->IsTimed)
{
point->waittime -= ltdWarp;
return IU_UPDATE_TASK;
}
}
return IU_NEXT_TASK;
}
/*
* ---------------------------------------------------------------------------
* * do_queue: Queue management
*/
void do_queue(dbref player, dbref cause, int key, char *arg)
{
int was_disabled;
was_disabled = 0;
if (key == QUEUE_KICK)
{
int i = Tiny_atol(arg);
int save_minPriority = scheduler.GetMinPriority();
if (save_minPriority <= PRIORITY_CF_DEQUEUE_DISABLED)
{
notify(player, "Warning: automatic dequeueing is disabled.");
scheduler.SetMinPriority(PRIORITY_CF_DEQUEUE_ENABLED);
}
CLinearTimeAbsolute lsaNow;
lsaNow.GetUTC();
scheduler.ReadyTasks(lsaNow);
int ncmds = scheduler.RunTasks(i);
scheduler.SetMinPriority(save_minPriority);
if (!Quiet(player))
{
notify(player, tprintf("%d commands processed.", ncmds));
}
}
else if (key == QUEUE_WARP)
{
int iWarp = Tiny_atol(arg);
CLinearTimeDelta ltdWarp;
ltdWarp.SetSeconds(iWarp);
if (scheduler.GetMinPriority() <= PRIORITY_CF_DEQUEUE_DISABLED)
{
notify(player, "Warning: automatic dequeueing is disabled.");
}
scheduler.TraverseUnordered(CallBack_Warp);
if (Quiet(player))
{
return;
}
if (iWarp > 0)
{
notify(player, tprintf("WaitQ timer advanced %d seconds.", iWarp));
}
else if (iWarp < 0)
{
notify(player, tprintf("WaitQ timer set back %d seconds.", iWarp));
}
else
{
notify(player, "Object queue appended to player queue.");
}
}
}