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
------------------------------------------------------------------------------
server.cc
*/

#include <signal.h>
#include "config.h"
#include "server.h"
#include "io.h"

Server::Server()
:	masterport(4000), masterdesc(0),
	type( SOCK_STREAM ), nameserver(0), maxread( MAX_READ ),
	maxidle(0), descripts(0)
{
#if !defined(WIN32)
	maxdesc = sysconf( _SC_OPEN_MAX );
#else
    maxdesc = WinsockInfo.iMaxSockets;
#endif
}

Server::Server( int theport )
:	masterport( theport ), masterdesc(0),
	type( SOCK_STREAM ), nameserver(0), maxread( MAX_READ ),
	maxidle(0), descripts(0)
{
#ifndef WIN32
	maxdesc = sysconf( _SC_OPEN_MAX );
#else
	// no assurance that winsock has started yet
	// if it hasn't then maxdesc will be set to zero.
	maxdesc = WinsockInfo.iMaxSockets;
#endif /* WIN32 */
}

Server::~Server()
{
	if( master )
		master->close();
}


// This is for booting "into" a previous servers process
// space and inheriting the open master descriptor. Skip
// all the master struct creation and just assign the
// descriptor to sock and clean up a few other things.
// This really needs some serious error handling. Also need
// to save the sock struct info in the reboot.tab file
// and recreate it from that.
// --Fusion

int Server::boot( int oldport, int olddesc )
{
	// How lucky for us, WIN32 doesn't even support SIGPIPE
	// so why bother ignoring it? (generates an error anyway)
#ifndef WIN32
	::signal( SIGPIPE, SIG_IGN );
#endif

	master = new Socket( "", oldport, olddesc ); 
	master->nonBlock();
	masterport = oldport;
	masterdesc = olddesc;

#ifndef WIN32
	Cout << "SERVER: Ok, this OS supports " << maxdesc
		<< " open descriptors per process.\n";
#else
	Cout << "SERVER: Ok, this Winsock supports " << maxdesc
		<< " open descriptors per machine.\n";
#endif
	if( maxdesc > 256 )
		maxdesc = 256;
	bitwidth = maxdesc / 8;
	topdesc = olddesc;

	Cout << "SERVER: Rebooted succesfully - ready for connections.\n";
	Cout << "SERVER: Port [" << master->getPort() << "]\n";
#ifndef WIN32
	gettimeofday( &boot_time, 0 );
#else
			struct _timeb timebuffer;
			_ftime (& timebuffer);
			boot_time.tv_sec = timebuffer.time;
			boot_time.tv_usec = timebuffer.millitm * 1000;
#endif
	return 0;
}


int Server::boot( int tport )
{
#ifndef WIN32
	::signal( SIGPIPE, SIG_IGN );
#endif
	master = new Socket( SOCK_STREAM, tport );
	if( master->error() )
	{
		perror( "SERVER: Server::boot() socket not created" );
		mudpp_exit(0);
	}

	master->reuseAddr();
	master->noDelay();
	master->nonBlock();
	if( master->open() )
	{
		perror( "SERVER: Socket::open()" );
		mudpp_exit(0);
	}

	masterdesc = topdesc = master->getDescriptor();
	masterport = master->getPort();

#ifndef WIN32
	Cout << "SERVER: Ok, this OS supports " << maxdesc
		<< " open descriptors per process.\n";
#else
	Cout << "SERVER: Ok, this Winsock supports " << maxdesc
		<< " open descriptors per machine.\n";
#endif
	if( maxdesc > 256 )
		maxdesc = 256;
	bitwidth = maxdesc / 8;
	Cout << "SERVER: Booted succesfully - ready for connections.\n";
	Cout << "SERVER: Port [" << masterport << "]\n";
#ifndef WIN32
	gettimeofday( &boot_time, 0 );
#else
			struct _timeb timebuffer;
			_ftime (& timebuffer);
			boot_time.tv_sec = timebuffer.time;
			boot_time.tv_usec = timebuffer.millitm * 1000;
#endif
	return 0;
}


const timeval & Server::getUpTime()
{
	static timeval up;
	timeval now_time;
#ifndef WIN32
	gettimeofday( &now_time, 0 );
#else
			struct _timeb timebuffer;
			_ftime (& timebuffer);
			now_time.tv_sec = timebuffer.time;
			now_time.tv_usec = timebuffer.millitm * 1000;
#endif
	up.tv_sec = now_time.tv_sec - boot_time.tv_sec;
	return up;
}


int Server::poll()
{
	memcpy( &R_SET, &MASTER_SET, bitwidth );
	memcpy( &W_SET, &MASTER_SET, bitwidth );
	memcpy( &E_SET, &MASTER_SET, bitwidth );
	FD_SET( masterdesc, &R_SET );
	// Linux modifies timeval * in select so reset every time.
	null_time.tv_sec = 0;
	null_time.tv_usec = 0;
	while( select( topdesc+1, &R_SET, &W_SET, &E_SET, &null_time ) < 0 )
	{
		if( errno == EINTR )
			continue;
		Cout << "SERVER: select() error in Poll() - exiting.\n";
		mudpp_exit( errno );
	}
	return FD_ISSET( masterdesc, &R_SET );
}

// I high resolution sleep function
// Best to inline this to remove the overhead of the function call

void Server::sleep( long microsecs )
{
	static struct timeval sleep_time;
	sleep_time.tv_sec  = 0;
	sleep_time.tv_usec = microsecs; 
	while( select( 0, NULL, NULL, NULL, &sleep_time ) < 0 )
	{
#ifndef WIN32
		if( errno == EINTR )
			continue;
		Cout << "SERVER: Error select() in Sleep()\n";
		mudpp_exit( errno );
#else
		if( WSAGetLastError() == WSAEINTR )
			continue;
		Cout << "SERVER::sleep: Error in select()\n";
		mudpp_exit(0);
#endif
	}
}


int Server::newConnection()
{
	FD_ZERO( &R_SET );
	FD_SET( masterdesc, &R_SET );
	// Linux modifies timeval * in select so reset every time.
	null_time.tv_sec  = 0;
	null_time.tv_usec = 0; 
	while( select( masterdesc + 1, &R_SET, 0, 0, &null_time ) < 0 )
	{
		if( errno == EINTR )
			continue;
		perror( "SERVER: select() in newConnection() - fatal" );
		mudpp_exit( errno );
	}
	return FD_ISSET( masterdesc, &R_SET );
}


Socket * Server::accept()
{
	int desc;
	Socket *newSock = master->accept();

	if( !newSock )
	{
		if( errno == EAGAIN || errno == EWOULDBLOCK )
			return 0;
		return 0;
	}

	newSock->noDelay();
	newSock->getPeerName();
	newSock->resolveIP();
	desc = newSock->getDescriptor();

	if( desc > topdesc )
		topdesc = desc;

	FD_SET( desc, &MASTER_SET );
	descripts++;

	Cout << "accept: new desc = " << desc << endl;
	return newSock;
}


void Server::addSock( Socket * sock )
{
	if( FD_ISSET( sock->getDescriptor(), &MASTER_SET ) )
	{
		Cout << "SERVER: Error. Attempt to add existing descriptor.\n";
		mudpp_exit(0);
	}

	FD_SET( sock->getDescriptor(), &MASTER_SET );
	if( sock->getDescriptor() > topdesc )
		topdesc = sock->getDescriptor();
	descripts++;
}


void Server::remove( Socket * sock )
{
	// I exit on the following two errors because if this happens
	// you need to find out why.

	int desc = sock->getDescriptor();
	if( desc < 0 || desc >= maxdesc ) 
	{
		Cout << "SERVER: Close() - desc [" << desc << "] out of bounds.\n";
		abort();
	}

	if( ! FD_ISSET( desc, &MASTER_SET ) )
	{
		Cout << "SERVER: Invalid descriptor\n";
		abort();
	}

	FD_CLR( desc, &MASTER_SET );

	if( !--descripts )
	{
		topdesc = masterdesc;
		return;
	}

	if( desc == topdesc )
	{
		for( desc = topdesc-1; !FD_ISSET( desc, &MASTER_SET ); desc-- )
			;
		topdesc = desc;
	}
}


int Server::error( Socket * sock )
{
	int desc = sock->getDescriptor();

	if( FD_ISSET( desc, &E_SET ) )
	{
		FD_CLR( desc, &R_SET );
		FD_CLR( desc, &W_SET );
		return FD_ISSET( desc, &E_SET );
	}

	return 0;
}