///////////////////////////////////////////////////////////
///////////////// Have an itch? Scratch it! ///////////////
///////////////////////// SCRATCH /////////////////////////
///////////////////// A MUD Server ////////////////////
///////////////////// By: Jared Devall ////////////////////
///////////////////// Thanks: ////////////////////
///////////////////// DIKU/Merc/ROM ////////////////////
///////////////////// Aetas/Deus Gang ////////////////////
///////////////////// Beej ////////////////////
///////////////////////////////////////////////////////////
#include <string>
#include <iostream>
#include <fstream>
#include <time.h>
#include <cstdio>
#include <signal.h>
#include <list>
#include "stringutil.h"
#include "world.h"
#include "timestamp.h"
using namespace std;
// Holds the "infinite" loop that runs the mud
void World::Exist( const string &startMessage, int port, bool copyover ) {
string buf;
if ( copyover == false ) {
SocketServer::Instance().Start();
} else {
CopyoverRecovery();
}
buf << "\n\t" << startMessage << " on port " << port;
buf << "\n\tTime: " << Timestamp::Instance().GetDateTime();
cout << buf << endl;
_worldExists = true; // This is used so we can have a Character Shutdown the Server.
// _worldExists can be set to false by shutdown; otherwise run
while( _worldExists ) {
Monitor();
}
cout << "Shutdown successfully." << endl;
return;
}
// Saves connections and spawns a new instance of the mud
void World::Copyover( ) {
list< Avatar *>::iterator a_it;
Avatar * dead;
ofstream fp("file.cpy");
if ( !fp ) {
cout << "Copyover file could not be opened." << endl;
return;
}
for ( a_it = _avatarList.begin(); a_it != _avatarList.end(); ) {
// These people aren't logged in. Kill them!
if ( (*a_it)->GetStatus() != CONNECTED ) {
dead = (*a_it);
a_it = _avatarList.erase( a_it );
delete dead;
continue;
}
(*a_it)->Save();
fp << (*a_it)->GetSocket()->GetDescriptor() << " " <<
(*a_it)->Get( "name" ) << " " <<
(*a_it)->GetSocket()->GetSocketIP() << endl;
a_it++;
}
fp << "-1";
fp.close();
string controlSock;
controlSock << SocketServer::Instance().GetHostSocket();
execl("./scratch", "2950", "-copyover", controlSock.c_str(), NULL );
}
// Sets up the connections after a copyover
void World::CopyoverRecovery( ) {
Avatar * avatar;
ifstream fp("file.cpy");
int desc;
std::string name;
std::string host;
std::string lhost;
if ( !fp ) {
cout << "Copyover file could not be opened." << endl;
exit(1);
}
for(;;) {
fp >> desc >> name >> host;
if (desc == -1)
break;
avatar = new Avatar;
avatar->GetSocket()->SetDescriptor( desc );
avatar->GetSocket()->SetSocketIP( string(host) );
avatar->Set( "name", name );
avatar->Load();
avatar->SetStatus( CONNECTED );
// Add user to socket and avatar lists
SocketServer::Instance()._socketList.push_back( avatar->GetSocket() );
_avatarList.push_back( avatar );
// We're all done. Let them know. :)
avatar->Send( "Reboot Successful.\n\r" );
// Stick them defaultly in InputHandler
avatar->StackHandler( new InputHandler );
}
fp.close();
remove( "file.cpy" ); // Delete the temp file
cout << "Copyover Recovery is now Complete." << endl;
}
Avatar * World::FindAvatar( const string &target ) {
list< Avatar * >::iterator it;
for ( it = _avatarList.begin(); it != _avatarList.end(); ++it ) {
if ( str_cmp( (*it)->Get( "name" ), target ) )
return (*it);
}
return NULL;
}
// Sends a message to all avatars meeting broadcast_type
void World::Broadcast( const string &message, BROADCAST_TYPE type ) {
list< Avatar * >::iterator avatar;
if ( type == ALL ) {
for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
(*avatar)->Send( message );
}
} else if ( type == WCONNECTED ) {
for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
if ( (*avatar)->GetStatus() == CONNECTED )
(*avatar)->Send( message );
}
}
}
// This is the 'shutdown' of the MUD.
void World::Die( ) {
_worldExists = false;
}
// This is where we process everything going on
void World::Monitor( ) {
bool newConnection = false;
FlushOutput();
if ( ( newConnection = SocketServer::Instance().Monitor() ) == true ) {
Avatar * avatar = new Avatar;
avatar->SetSocket( SocketServer::Instance()._socketList.back() );
_avatarList.push_back( avatar );
avatar->StackHandler( new GetNameHandler );
newConnection = false;
avatar->Send( " ============================================= \n\r" );
avatar->Send( " === {WScratch MUD{x === \n\r" );
avatar->Send( " ============================================= \n\r\n\r" );
avatar->Send( " {RIn Early Development.{x\n\r" );
avatar->Send( " {W- Rendelven -{x\n\r\n\r" );
avatar->Send( " {R*{WNote: All passwords changed to 'password'\n\r" );
avatar->Send( " Change Once Connected{x\n\r" );
}
UpdateTimers();
UpdateAvatars();
HandleInput();
}
// Fixes the prompts for everyone
void World::FlushOutput( ) {
list< Avatar * >::iterator avatar;
for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
(*avatar)->FlushOutput();
}
}
// Checks to see if any avatars have disconnected or got input
void World::UpdateAvatars( ) {
list< Avatar * >::iterator avatar;
for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
if ( (*avatar)->GetSocket()->GotInput() == true ) {
(*avatar)->SetGotInput( true );
}
if ( (*avatar)->GetSocket()->IsDisconnected() )
(*avatar)->SetDisconnected( true );
}
KillFlaggedAvatars();
}
// Deletes the avatars from the list and disconnects them
void World::KillFlaggedAvatars( ) {
list< Avatar * >::iterator avatar;
Avatar * avatar2;
for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ) {
avatar2 = (*avatar);
if ( avatar2->IsDisconnected() ) {
avatar = _avatarList.erase( avatar );
delete avatar2;
} else {
++avatar;
}
}
}
// Handles all avatar input
void World::HandleInput( ) {
std::list< Avatar * >::iterator avatar;
// This is for handling input.
// Loops through avatars, checks to see if there's input to play with,
// then goes on that.
if ( _avatarList.empty() != true ) {
for ( avatar = _avatarList.begin(); avatar != _avatarList.end(); ++avatar ) {
// Don't mess with input until delayed text is finished
if ( (*avatar)->GotInput() == true /*&& (*avatar)->waiting == false*/ ) { // We need to make sure there's actually some input to handle
(*avatar)->HandleInput( (*avatar)->GetSocket()->GetInput() ); // This sends user input to right handler.
(*avatar)->GetSocket()->FlushInput( ); // We need to clean the inbuf or it'll loop using the same stuff.
}
}
}
UpdateAvatars();
}
// Updates our timers to see if they need to fire, etc
void World::UpdateTimers () {
if ( _timers.empty() )
return;
time_t now = time(NULL);
Timer *timer;
for ( timer = _timers.top(); timer->When() <= now; timer = _timers.top() ) {
_timers.pop();
if ( !timer->_enabled || !timer->Fire() ) {
delete timer;
if ( _timers.empty() )
break;
} else {
_timers.push( timer );
}
}
}
// Schedule a new timer event
void World::Schedule( Timer *timer ) {
_timers.push( timer );
}
// disable a new timer event
void World::Unschedule( Timer *timer ) {
timer->_enabled = false;
}