dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// 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++;
}
/**************************************************************************/