/**************************************************************************/
// mob_q.cpp - Mob queue system - Kalahn
/***************************************************************************
* The Dawn of Time v1.69r (c)1997-2004 Michael Garratt *
* >> A number of people have contributed to the Dawn codebase, with the *
* majority of code written by Michael Garratt - www.dawnoftime.org *
* >> To use this source code, you must fully comply with the dawn license *
* in licenses.txt... In particular, you may not remove this copyright *
* notice. *
**************************************************************************/
#include "include.h"
int run_mpqueue_count=0;
char *mq_mpbug_details;
bool mq_running_queued_command=false;
/**************************************************************************/
#define MQFLAG_SEEALL (A)
/**************************************************************************/
enum mobqueuetype {MQT_UNDEFINED, MQT_COMMAND,
MQT_PRINT, MQT_ECHOAT, MQT_ECHOAROUND, MQT_ECHOROOM};
/**************************************************************************/
class mpqueue_type{
public:
char_data* ch;
time_t when; // when the prog will go off (when current_time equals this)
mobqueuetype type;
int flags;
char *text;
mpqueue_type *next; // next in hash bucket]
char *mpbug_details;
public: // public member functions
mpqueue_type();// default constructor
mpqueue_type(mobqueuetype t, time_t seconds, char_data * mob, const char *command);// custom constructor
~mpqueue_type();// deconstructor
};
#define MPQUEUE_HASH_SIZE (256)
/**************************************************************************/
// default constructor
mpqueue_type::mpqueue_type()
{
flags=0;
type=MQT_UNDEFINED;
ch=NULL;
text=str_dup("");
when=0;
next=NULL;
};
/**************************************************************************/
// default destructor
mpqueue_type::~mpqueue_type()
{
if(next){
bug("Warning: mpqueue_type::~mpqueue_type() called when next!=NULL!!!");
}
free_string(text);
};
/**************************************************************************/
// default constructor
mpqueue_type::mpqueue_type(mobqueuetype t, time_t seconds, char_data * mob, const char *argument)
{
flags=0;
type=t;
text=str_dup(argument);
when=current_time +seconds;
ch=mob;
ch->mpqueue_attached(when);
next=NULL;
};
/**************************************************************************/
int mpqueued_count=0;
int mpqueued_total=0;
mpqueue_type *mpqueue_hash_table[MPQUEUE_HASH_SIZE];
/**************************************************************************/
void mpqueue_dequeue_for(char_data *extracted_char)
{
char *tbuf;
if(run_mpqueue_count>0){
// can't remove nodes while in run_mpqueue()
// otherwise it creates crashes later on since run_mpqueue() performs some
// dequeueing itself (good bug find :) ) - Kal April 00
logf("mpqueue_dequeue_for('%s' %d) called with run_mpqueue() on call stack - NULLing node->ch", extracted_char->name, extracted_char->vnum());
mpqueue_type *node;
for(int h=0; h<MPQUEUE_HASH_SIZE; h++){
for(node=mpqueue_hash_table[h]; node; node=node->next){
if(node->ch==extracted_char){
tbuf=ctime( &node->when);
tbuf[str_len(tbuf)-6] = '\0';
logf("###############NULLed deque node->ch '%s' at %s, queue system will cleanup later", node->text, tbuf+4);
node->ch=NULL;
}
};
}
}else{
logf("mpqueue_dequeue_for('%s' %d)", extracted_char->name, extracted_char->vnum());
mpqueue_type *node, *node_next, *prev;
for(int h=0; h<MPQUEUE_HASH_SIZE; h++){
prev=NULL;
for(node=mpqueue_hash_table[h]; node; node=node_next){
node_next=node->next;
if(node->ch==extracted_char){
tbuf=ctime( &node->when);
tbuf[str_len(tbuf)-6] = '\0';
logf("###############deque node '%s' at %s", node->text, tbuf+4);
node->next=NULL;
delete node;
// remove node from the list
if(prev){
prev->next=node_next;
}else{
mpqueue_hash_table[h]=node_next;
}
mpqueued_count--; // stats stuff
}else{ // a node has been kept, therefore it is the previous node
prev=node;
}
};
}
}
}
/**************************************************************************/
void do_mpdequeueall(char_data *ch, char *)
{
mpqueue_dequeue_for(ch);
}
/**************************************************************************/
// execute a bucket in the hash table queue
void run_mpqueue(mpqueue_type *parent)
{
run_mpqueue_count++;
mpqueue_type *node=parent->next;
assertp(node);
if(node->when>current_time){ // only run once it is due to be run
run_mpqueue_count--;
return;
}
if(node->next){ // run nodes futher down
run_mpqueue(node);
}
if(node->ch){
switch(node->type){
case MQT_COMMAND:
interpret(node->ch, node->text);
break;
case MQT_PRINT:
node->ch->set_pdelay(0); // incase someone stuffed up and we would get an endless loop
node->ch->print(node->text);
break;
case MQT_ECHOAT:
act(node->text, node->ch, NULL, NULL, TO_CHAR);
break;
case MQT_ECHOAROUND:
act(node->text, node->ch, NULL, NULL, TO_ROOM);
break;
case MQT_ECHOROOM:
act(node->text, node->ch, NULL, NULL, TO_CHAR);
act(node->text, node->ch, NULL, NULL, TO_ROOM);
break;
default:
bug("run_mpqueue(mpqueue_type *parent): Unsupported recognised queued event type!");
break;
}
}else{ // NULL node->ch
logf("###############NULL node->ch found '%s', queue system deleting", node->text);
}
// dequeue it node from the list
parent->next=node->next;
node->next=NULL;
delete node;
// statistics stuff
mpqueued_count--;
run_mpqueue_count--;
}
/**************************************************************************/
// called every pulse - could be cleaner but who cares :)
void process_mpqueue()
{
// run optimization and table initialisation
static time_t last_run=0;
if(last_run==current_time){
return;
}
if(last_run==0){ // initialise the table (first time)
memset(&mpqueue_hash_table[0],0,sizeof(mpqueue_type*)*MPQUEUE_HASH_SIZE);
}
// find what we are running this second - if anything
last_run=current_time;
int hash=(int)current_time%MPQUEUE_HASH_SIZE;
if( !mpqueue_hash_table[hash] || mpqueue_hash_table[hash]->when>current_time){
return; // nothing to run this time
}
// run it
static mpqueue_type *parent=NULL;if(!parent){parent=new mpqueue_type;} // get a parent variable
parent->next=mpqueue_hash_table[hash];
run_mpqueue(parent);
mpqueue_hash_table[hash]=parent->next;
return;
}
/**************************************************************************/
// syntax: mob queue <seconds> command to run
void do_mpqueue(char_data *ch, char *argument)
{
char seconds_arg[MIL];
char fullarg[MIL];
strcpy(fullarg, argument);
argument=one_argument(argument,seconds_arg);
// validate arguments
if(IS_NULLSTR(argument)){ // not enough arguments
mpbug("do_mpqueue: not enough arguments, must have at least seconds and a command to queue.");
mpbug("syntax: mob queue <seconds> command to type )");
return;
}
if(!is_number(seconds_arg)){
mpbug("do_mpqemote: <seconds> ('%s') must be numeric.", seconds_arg);
mpbug("syntax: mob queue <seconds> command to type )");
return;
}
// START PROCESSING AND VALIDATING ARGUMENTS
// Number of seconds in which prog will be run
int seconds=atoi(seconds_arg);
if(seconds<0 || seconds>3000){
mpbug("do_mpqueue: seconds parameter must be between 1 and 3000.");
mpbug("syntax: mob queue <seconds> command to type )");
mpbug("buggy argument: '%s'", fullarg);
return;
}
if(!IS_VALID(ch)){
mpbug("A deleted mob is trying to queue '%s'.", fullarg);
return;
}
mpqueue_type *node=new mpqueue_type(MQT_COMMAND, seconds, ch, argument);
int hash=(int)node->when%MPQUEUE_HASH_SIZE;
// queue it into the hash table buckets
node->next=mpqueue_hash_table[hash];
mpqueue_hash_table[hash]=node;
// statistics stuff
mpqueued_count++;
mpqueued_total++;
}
/**************************************************************************/
void queue_print(int seconds, char_data *ch, const char *text)
{
mpqueue_type *node=new mpqueue_type(MQT_PRINT, seconds, ch, text);
int hash=(int)node->when%MPQUEUE_HASH_SIZE;
// queue it into the hash table buckets
node->next=mpqueue_hash_table[hash];
mpqueue_hash_table[hash]=node;
// statistics stuff
mpqueued_count++;
mpqueued_total++;
}
/**************************************************************************/