/////////////////////////////////////////////////////////// ///////////////// 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; }