mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@hom.net 
MUD++ development mailing list    mudpp@van.ml.org
------------------------------------------------------------------------------
main.cc
*/


// The game loop is here.
// Also the main objects are created here: server, players, areas loaded 
// WARNING: This file is now a nightmare. I dont recommend even trying
//          to comprehend it until John Olson and I clean it up. Most
//			of the problem is bad organization on my part which didn't
//			handle platform specific additions.

#include "config.h"
#include "io.h" 
#include "string.h"
#include "server.h"
#include "room.h"
#include "pc.h"
#include "hash.h"
#include "llist.h"
#include "area.h"
#include "edit.h"
#include "help.h"
#include "env.h"
#include "cluster.h"
#include "global.h"
#include "properties.h"

#ifndef WIN32
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/signal.h>
#else
#include <io.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <direct.h>
#include <process.h>
#include <winsock.h>
#ifdef __SC__
#include <sys/unistd.h>
#endif // SC
#endif // WIN32

const char * version = "\n\r\
MUD++ Cyberspace Server version 0.34 (non-commercial)\n\r\
Copyright(C) 1995-1997 Melvin Smith\n\n\r";

extern "C"
{
#ifdef ultrix
int getrlimit( int, struct rlimit * );
int setrlimit( int, struct rlimit * );
#endif

#ifdef sun 
int getrlimit( int, struct rlimit * );
int setrlimit( int, const struct rlimit * );
#endif
}

// ****************************************
// Global variables

Server server;
String title = "Title screen failed to load. Check file.\n\r";

#if defined(CLIENT) || defined(SERVER)
Descriptor mmd_fd;
int mmd_messg_count;
#endif

char String::_argbuf[ 1024 ];
char * String::_argptr;
char * String::_argnext;
StringRep * String::repEmpty = new StringRep("");

LList<PC>		pcs;
LList<PC>		shellpcs;
LList<NPC>		npcs;
LList<Area>		areas;
LList<Object>		objects;
LList<Action>		actions;
LList<Help>		helps_ll;
HashTable<Help>	helps_ht;

bool DOWN = false;
bool REBOOT = false;
bool CONSOLE = false;

int pulse = PULSE_PER_SEC;
int action_pulse = ACTION_PULSE;
long tot_login = 0;
long cur_login = 0;
long max_login = 0;
bool reap_shells = false;

#ifdef WIN32
WSADATA WinsockInfo;  // contains available version info, max sockets...
#endif

// ****************************************

void loadAreas();
void loadSocials();
void loadTitle();
void buildWorld();
void loadPersistentData();
void savePersistentData();
void loadGuilds();
bool Nanny( PC *, const String & str = "" );
int startDaemon( const char *, const char * );
void IWannaBeADaemonToo();
void reboot();
void init_vm_type_table();
bool link_vmachine();
void mudppFinalizer();
void initProperties();

#ifndef WIN32
RETSIGTYPE sigHandler( int );
int installSigHandlers();
#endif


// Borland C++ Builder creates a main module with its own style, so call
// real_main() from there. May want to add other Win32 compilers
// which do similar things.
#ifndef __BORLANDC__
int real_main( int, char ** );

int main( int argc, char **argv )
{
	return real_main( argc, argv );
}
#endif


// Real main program
int real_main( int argc , char ** argv)
{
#ifndef WIN32
	pid_t pid;
#endif
	Cout << version;
	atexit(mudppFinalizer);

	// Parse command line arguments
	if( argc > 1 )
	{
		bool err = false;
		argv++;
		while( *argv )
		{
			char * arg = *argv;
			if( *arg != '-' )
			{
				err = true;
				Cout << "Invalid switch: " << arg << endl;
			}

			if( !strcmp( arg+1, "console" ) )
				CONSOLE = true;
			argv++;
		}

		if( err )
		{
			Cout << "Usage:  mud++ [-console]\n";
			exit(0);
		}
	}

	// We need to do it early, to allow daemon/not daemon property
	Cout << "Loading properties ... ";
	initProperties();

	// WIN32 sockets are BSD 4.3 flavor with certain windows requirements
	// such as code to kickoff the winsock.dll and version checking.
#ifdef WIN32
	int	iWStartError;  // if 0 then no error and winsock is ok

	// 0x0101 = version 1.1
	iWStartError = WSAStartup( 0x0101, &WinsockInfo);
	if (iWStartError != 0)
		switch (iWStartError)
		{
			case WSASYSNOTREADY:
			{
				Cout << "WSAStartup failed:System not ready for network communications.\n\n\r";
				return (0);
			}
			case WSAVERNOTSUPPORTED:
			case WSAEINVAL:
			{
				Cout << "WSAStartup failed:Version requested is not available in this winsock.dll.\n\n\r";
				return(0);
			}
		}
	else
		/* right now this spits our version info that is difficult to read, but info is correct */
	{
		Cout << "Starting Winsock with version " << WinsockInfo.wVersion << ".\n\r";
		Cout << "Winsock Manuf Data: " << WinsockInfo.szDescription << "\n\r";
		server.setmaxdesc(WinsockInfo.iMaxSockets);
	}
#endif

#ifndef WIN32
	chdir( AREA_DIR );
	umask( ~( S_IRUSR | S_IWUSR ) );
#else
	_chdir( AREA_DIR );
	_umask( ~( _S_IREAD | _S_IWRITE ) );
#endif

	// Raise the soft limit on coredump size to the max (hard limit)
	// if it isn't already. This isn't POSIX but it is SYSV and 4.3+BSD
	// so it should compile. If it doesn't, delete these lines and
	// invoke mud++ through a csh or ksh after giving the command
	// 'unlimit coredumpsize', otherwise you may not get a core for debugging.

#ifndef WIN32
#if !defined(__CYGWIN32__)
	struct rlimit rl;
	
	getrlimit( RLIMIT_CORE, &rl );
	rl.rlim_cur = rl.rlim_max;
	setrlimit( RLIMIT_CORE, &rl );

	static Property * demonize =0;
	UseProperty( &demonize, "fork_at_startup", PROP_BOOL, "true", NULL, false);
	if ( (!CONSOLE) && demonize->getBool() )
	{
		// Zap off into daemon land.
		if( ( pid = fork() ) < 0 )
		{
			perror( "main:fork" );
			mudpp_exit( 0 );
		}
		else if( pid != 0 ) // parent exits
			mudpp_exit(0);
	}
	else 
		Cout << "Running in console mode.\n";

	// Get our pid now
	if( ( pid = getpid() ) < 0 )
		perror( "getpid" );
	// Create a new session. 
	setsid();
	if( installSigHandlers() < 0 )
	{
		perror( "installing signal handlers" );
		mudpp_exit(0);
		}
#else // CYGWIN32
	if ( ( pid = getpid() ) < 0 )
		perror( "getpid" );
#endif // CYGWIN32
#endif // WIN32

	Cout << "Looking for reboot.tab\n";
	InputFile tab( "./reboot.tab" );
	// This is important!
	// If we are already a daemon we DON'T want to go through the
	// process of becoming a daemon again or stuff gets screwed.
	// For example, the first time through we close stdin, so it
	// is usually recycled to the Server desc port, so we don't
	// want to close it again in case of a reboot. This was a bug
	// in early versions ( 0.10 and back )
#ifndef WIN32
	if( !CONSOLE && !tab )
		IWannaBeADaemonToo();
	Cout << "Main server pid [" << pid << "]\n";
#else
	Cout << "Main server is this application instance.\n";
#endif


#if 0
#if defined(CLIENT) || defined(SERVER)
#if defined(CLIENT)
	mmd_fd = startDaemon( "mmdaemon", "-c" );
#else
	mmd_fd = startDaemon( "mmdaemon", "-s" );
#endif
	if( mmd_fd < 0 )
	{
		Cout << "Could not start mmdaemon.\n";
		mudpp_exit(0);
	}

	Cout << "mmdaemon started.\n";
#endif
#endif

	// Init table type - temporary hack, check vm/types.cc
	init_vm_type_table();

	// Read ../etc/HOSTS even if we aren't a cluster.
	// To change the port for the server edit this file and
	// change the 'local' entry.
	if( loadHostsFile() < 0 )
	{
		Cout << "Error loading hosts file.\n";
		mudpp_exit(0);
	}

	// See if we have been spawned by another instance.
	// If so, inherit all open descriptors.

	PC *pc;
	String str;

	Cout << "Initialization of Garbage Collector.\n";
	GC_init();
	Cout << "VMachine data linking...";
	if( link_vmachine( ) )
		Cout << "linked successfully.\n";
	else
		Cout << "error occured. VMachine disabled.\n";
	loadTitle();
	Cout << "Loading help Index.\n";
	loadHelps();
	Cout << "Loading socials database.\n";
	loadSocials();
	Cout << "Loading guilds.\n";
	loadGuilds();
	Cout << "Loading areas.\n";
	loadAreas();
	Cout << "Linking world.\n";
	buildWorld();
	Cout << "Parsing hints file ... loaded " << loadHints() << " entries.\n";
	Cout << "Loading persistent data.\n";
	loadPersistentData();


	if( !tab )
	{
		Cout << "No reboot.tab, this is a fresh boot.\n\r";
		server.boot( local.getPlayerPort() );
	}
	else
	{
		char name[ 256 ];
		char host[ 256 ];
		int desc;
		int port;

		Cout << "Live reboot, loading descriptor data.\n\r";

		// For now I assume that first entry is the socket control
		// entry so I dont examine it.
		tab.getstring( name ); // name
		tab.getstring( host ); // hostname ignored for this entry 
		port = tab.getnum();
		desc = tab.getnum();
		server.boot( port, desc );
		Cout << "Reloading server on TCP port " << port << ':' << desc << endl;
		Cout << "Restoring old connections.\n";
		for( ; ; )
		{
			if( tab.eof() )
				break;

			tab.getstring( name );
			if( tab.eof() )
				break;

			tab.getstring( host );
			if( tab.eof() )
				break;

			port = tab.getnum();
			desc = tab.getnum();
			Cout << name << ':' << host << ':' << port << ':' << desc << endl;
			
			Socket *newSock = new Socket( host, port, desc );
			server.addSock( newSock );

			pc = new PC( &server, newSock, name );
			pcs.add( pc );
			newSock->write( "\n\rSuccessful Reboot.\n\r" );

			// Here I got lazy. The potential is here to go ahead
			// and load the player -and- the mob he was fighting
			// if there was one, along with mobs old hp/mana
			// For now this is still better than closing connection.
			pc->setState( STATE_GET_NAME );
			str = name;
			Nanny( pc, str );
		}

		Cout << "reboot.tab loaded.\n";
		tab.close();
		remove( "./reboot.tab" );
	}

	server.useNameServer();



	String commd;
#ifdef WIN32
	struct _timeb timelast;
	struct _timeb timenow;
	unsigned short millidiff;
	time_t secdiff;
	_ftime(&timelast);  // get the time for comparisons
#endif // WIN32

	// OK here goes the game loop
	while( !DOWN )
	{
		// High precision timer to conserve CPU
#ifndef WIN32
		server.sleep( PULSE );
#else
		// sleeping is a waste of the multithreading feature of WIN32 OS
		_ftime(&timenow);  // get the time for this game loop
		secdiff = timenow.time - timelast.time;  // difference in seconds 
		millidiff = timenow.millitm - timelast.millitm;  // diff in milliseconds
		if ( (secdiff != 0) || (millidiff > (1000 * PULSE)) )
		{
			_ftime(&timelast);
#endif /* WIN32 */			

		// Pulse
		if( !pulse-- )
		{
			if( reap_shells )
			{
				Cout << "DEBUG: Reaping shell.\n";

 				// Careful with the reap_shells var, there is
				// some juggling here. If the SIGCHLD handler toggles
				// reap_shells but the pipe isn't ready yet, it will
				// skip over it. What I'm doing now is while there
				// are PCs in the shell list, once 1 has exited I
				// I try to reap shells every pulse until all are gone.
				// There are other ways around this race condition.
				shellpcs.reset();
				if( !shellpcs.peek() )
					reap_shells = false;
				else while( ( pc = shellpcs.peek() ) )
				{
					if( pc->getPipeIn()->canRead() )
					{
						pc->closePipeIn();
						shellpcs.remove( pc );
						pcs.addTop( pc );
						shellpcs.reset();
						if( !shellpcs.peek() )
							reap_shells = false;
						pc->getSocket()->write( "\n\rMUD IO resumed.\n\r" );
						if( pc->getEditor() )
							pc->getEditor()->command( "" );
						continue;
					}
					shellpcs.next();
				}
			}
			heartbeat();
			pulse = PULSE_PER_SEC;
		}

		// Actions need to be updated sooner than game "ticks"
		if( !action_pulse-- )
		{
			action_heartbeat();
			action_pulse = ACTION_PULSE;
		}
#ifdef WIN32
		}  // end of code only called once per main PULSE
#endif /* WIN32 */

		// Check for new connections
		if( server.newConnection() )
		{   
			Socket * sock = server.accept();
			if( sock )
			{
				sock->nonBlock();
				pc = new PC( &server, sock, "" );
				Cout << "New connection from " << sock->getHostName() << endl;
				pc->setState( STATE_INIT );
				sock->write( title.chars() );
				pcs.addTop( pc );

				// If Nanny returns false, pc pointer is no longer valid
				if( Nanny( pc ) )
					pc->flush();
				else
					Cout << "Nanny terminated new player.\n";
			} 
		}
 
		// Poll all active descriptors 
		server.poll();

		// Set the current pointer back to head of player list
		pcs.reset();

		while( ( pc = pcs.peek() ) )
		{
			// Keep this at top of loop, since continue is used
			pcs.next();

			// Check for guys to boot.
			if( pc->getState() == STATE_BOOT )
			{
				// Make sure he gets any pending text
				// This could be a disconnect, slay, etc. so inform him
				if( pc->getSocket() )
				{
					pc->flush();
					server.remove( pc->getSocket() );
					pc->getSocket()->close();
					Cout << "PLR_BOOT:Closing link to " << pc->getName() << "\n";
				}

				if( pc->inRoom() )
				{
					pc->inRoom()->rmCharInv( pc );
					cur_login--;
				}

				pcs.remove( pc );
				pc->fordelete();
				continue;
			}

			if( !pc->getSocket() )
				continue;

			// Boot sockets with exceptional condition
			// Need to add handler instead of booting.
			if( server.error( pc->getSocket() ) )
			{
				str.clr();
				str << "SOCK_ERR:Closing link to " << pc->getName();
				wizLog( str, pc ); 
				server.remove( pc->getSocket() );
				pc->getSocket()->close();
				pc->setSocket( 0 ); // NULL out the socket pointer
				continue;
			}

			// Read socket if ready
			if( server.canRead( pc->getSocket() ) )
			{
				pc->readInput();
				if( pc->getSocket()->eof() )
				{
					if( (bool)pc->getName() )
					{
						str.clr();
						str << "Closing link to " << pc->getName();
						wizLog( str, pc );
					}			
					else pc->setState( STATE_BOOT );
					server.remove( pc->getSocket() );
					pc->getSocket()->close();
					pc->setSocket( 0 );
					continue;
				}
			}

			if( !pc->inBuf() && !pc->outBuf() )
				continue;
			else if( pc->pagePending() )
			{
				if( pc->outBuf() )
				{
					pc->flush();
					pc->putPrompt();
					continue;
				}

				if( !pc->getNextCommand() )
					continue;

				pc->page( pc->getCommand() );
				pc->putPrompt();
				pc->clrIdle();
			}
			else if( pc->getEditor() )
			{
				if( !pc->getNextCommand() )
					continue;

				pc->clrIdle();
				if( (pc->getCommand() == "quit") || (pc->getCommand() == "exit")  )
				{
					pc->quitEditor();
				}
				else
				{
					// Need to check return status and pass command on
					// to regular interpreter if not recognized
					pc->getEditor()->command( pc->getCommand() );
				}

				pc->flush();
				pc->putPrompt();
				continue;
			}
			else if( pc->inBuf() )
			{
				if( pc->getState() >= STATE_INIT 
						&& pc->getState() < STATE_PLAYING )
				{
					if( !pc->getNextCommand() )
						continue;

					pc->clrIdle();

					if( !Nanny( pc, pc->getCommand() ) )
						continue;

					if( pc->pagePending( ) )
						pc->page( "" );
				}
				else
				{
					if( !pc->getNextCommand() )
						continue;

					pc->command();

					if( pc->outBuf() )
						pc->flush( );

					if( pc->pagePending( ) )
						pc->page( "" );

					pc->putPrompt( );
					pc->clrIdle();
				}
			}

			// Process some buffered output
			if( pc->outBuf() )
			{
				pc->flush();
				pc->putPrompt();
			}
		}
	}
	// Bottom of game loop

	savePersistentData();

	if( REBOOT )
		reboot();  // in WIN32 reboot will _exitthread

	Cout << "MUD++ daemon shutdown normally.\n";

	mudpp_exit(0);


	// shouldn't get here
	return 0;

}  // end of main for UN*X, end of daemonThread for WIN32


void reboot()
{
	PC *	pc;
	OutputFile	out( "reboot.tab" );

	// Write the control socket descriptor so the Server object can
	// pick it up on way back in after execl()

	Cout << "Saving server on TCP port " << server.getPort() << ':'
										<< server.getDescriptor() << endl;
	out << "mudpp.1~" << "localhost~"
		<< server.getPort() << " "
		<< server.getDescriptor();

	// Write each player name and descriptor so mud++ can reload
	// the player files after execl()

	pcs.reset();
	while( ( pc = pcs.peek() ) )
	{
		pcs.next();

		if( !pc->getSocket() )
			continue;

		out << "\n"
			<< pc->getName() << '~'
			<< pc->getSocket()->getHostName() << '~'
			<< pc->getSocket()->getPort() << " "
			<< pc->getSocket()->getDescriptor();
	}
		
	out.close();
	execl( "../src/mud++", "mud++", (char*)0 );
	perror( "execl" );
	mudpp_exit(0);
}


void loadAreas()
{
	String areaKey;
	char filename[256];	
	char buf[256];
	Area *area;

	InputFile in( AREA_FILE );

	while( !in.eof() )
	{
		in.getline( filename );
		Cout << "Area-[" << filename << "]" << endl;
		if( !*filename )
			break;

		InputFile inArea( filename );
		if( inArea )
		{
			inArea.getword( buf );
			if( *buf == 'A' )
			{
				areaKey = inArea.getword( buf );
				if( !isalpha( areaKey[0] ) )
					inArea.error( "Invalid or missing Area index scope" );

				area = new Area( areaKey );
				area->readFrom( inArea );
				areas.addTop( area );
			}
			else
			{
				Cout << "Area file [" << filename << "] has no area header.\n";
				mudpp_exit(0);
			}
		}
		else
		{
			Cout << "Error opening area file [" << filename << "]\n";
			mudpp_exit(0);
		}
		in.skipwhite();
	}

	Cout <<"\nDatabase booted sucessfully.\n"; 
}


void loadTitle()
{
	InputFile in( "../etc/title" );
	if( !in )
		return;
	title = in.getBuf();
	in.close();
}


// Link rooms, do initial repop

void buildWorld()
{
	Area *area;

	for_each( areas, area )
	{
		area->hardLink();
	}

	// 2 seperate loops in case I decide to do something funky with doors.

	for_each( areas, area )
	{
		area->repop();
	}
}


// Close terminal and open the log like a good daemon
void IWannaBeADaemonToo()
{
	const char * fail = "Failed to redirect STDOUT/STDERR to log.\n\r";
	Cout << "Logging started.\n";

	static Property * logToFile =0;
	UseProperty( &logToFile, "log_to_file", PROP_BOOL, "true", NULL, false );
	if ( !logToFile->getBool() )
		return;

	int logfd = open( "../log/server.log", O_WRONLY|O_CREAT|O_APPEND, 0777 );
	if( logfd < 0 )
	{
		perror( "startLogging:open" );
		mudpp_exit(0);
	}

	close( STDOUT_FILENO );
	close( STDERR_FILENO );
	if( dup( logfd ) != STDOUT_FILENO || dup( logfd ) != STDERR_FILENO )
	{
		write( logfd, fail, strlen( fail ) );
		mudpp_exit(0);
	}

	// Now all Cout << and perror() will go into the log.
	close( logfd );
	close( STDIN_FILENO ); // We don't need it anymore
}


// MUD++ daemons communicate with UNIX DOMAIN sockets which are generally
// very fast, slightly more than the cost of 2 memcpys which is what we
// want here. Will change to shared memory later.

// TODO WIN32
// if this subroutine is to be used then it could be the daemonThread
// starter function, in any event if it is implemented under WIN32 as
// a thread it does not need pipes.
#if !defined(WIN32) && !defined(__CYGWIN32__)
int startDaemon( const char * name, const char * arg )
{
	char path[256];
	int pair[2];
	pid_t pid;

	if( socketpair( AF_UNIX, SOCK_STREAM, 0, pair ) < 0 )
	{
		perror( "startDaemon:socketpair" );
		return -1;
	}

	pid = fork();
	if( pid > 0 )
	{
		close( pair[1] );
		return pair[0];
	}
	else if( pid == 0 )
	{
		close( pair[0] );
		if( dup2( pair[1], 0 ) < 0 || dup2( pair[1], 1 ) < 0
				|| dup2( pair[1], 2 ) < 0 )
			mudpp_exit(0);

		strcpy( path, "../src/" );
		strcat( path, name );
		execl( path, name, arg, (char*)0 );
		Cout << "ERR: exec daemon failed.\n";
		mudpp_exit(0);
	}
	return -1;
}
#endif /* WIN32 && CYGWIN32 */


#ifndef WIN32
RETSIGTYPE defaultSigHandler( int sig )
{
	pid_t child_pid;
	if( sig == SIGCHLD )
	{
		int status;
		if( ( child_pid = wait( &status ) ) < 0 )
		{
			return;
			// To get system("gzip -d") working I had to comment this
			// is it needed for some serious debugging ??
			//perror( "wait" );
			//mudpp_exit(0);
		}
#ifdef DEBUG
		Cout << "Child [" << child_pid << "] exited.\n";
#endif
		reap_shells = true;
		return;
	}
	else if( sig == SIGINT )
	{
		Cout << "Caught SIGINT. Shutting down.\n";
		exit(0);
	}
	else if( sig == SIGHUP )
	{
		Cout << "Received SIGHUP. Rebooting.\n";
		reboot();
	}
	else abort();
}


int installSigHandlers()
{
#ifndef SA_RESTART
#define SA_RESTART	0
#endif
	static struct sigaction sa;
	sigemptyset( &sa.sa_mask );
	sa.sa_flags = SA_RESTART;
	sa.sa_handler = defaultSigHandler;

	if( sigaction( SIGINT, &sa, 0 ) < 0 )
		return -1;
	if( sigaction( SIGTERM, &sa, 0 ) < 0 )
		return -1;
#if !defined(CLIENT)
	if( sigaction( SIGCHLD, &sa, 0 ) < 0 )
		return -1;
#endif

	return 0;
}
#endif /* WIN32 */


void mudppFinalizer()
{
	Cout.flush();
}