/* ....[@@@..[@@@..............[@.................. 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@falcon.mercer.peachnet.edu MUD++ development mailing list mudpp-list@spice.com ------------------------------------------------------------------------------ main.cc */ // The game loop is here. // Also the main objects are created here: server, players, mobs, objs #include "io.h" #include "string.h" #include "server.h" #include "room.h" #include "indexable.h" #include "llist.h" #include "area.h" #include "edit.h" #include "help.h" #include "env.h" #include "global.h" #include <sys/time.h> #include <sys/resource.h> const char * version = "\n\r\ MUD++ Cyberspace Server version 0.1\n\r\ Copyright(C) 1995, 1996 Melvin Smith\n\n\r"; #if defined( ultrix ) extern "C" { int getrlimit( int, struct rlimit * ); int setrlimit( int, struct rlimit * ); } #endif Server server( 4000 ); LList<PC> pcs; LList<NPC> npcs; LList<Area> areas; LList<Object> objects; void loadAreas(); void buildWorld(); void Nanny( PC *, String & str = "" ); int DOWN; int REBOOT; int pulse = PULSE_PER_SEC; int main( int argc, char *argv[] ) { // pid_t pid; Cout << version; int thePort = 4000; if( argc > 1 ) { thePort = atoi( argv[1] ); if( thePort <= 0 ) thePort = 4000; } chdir( AREA_DIR ); umask( ~( S_IRUSR | S_IWUSR ) ); // 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. rlimit rl; getrlimit( RLIMIT_CORE, &rl ); rl.rlim_cur = rl.rlim_max; setrlimit( RLIMIT_CORE, &rl ); /* Zap off into daemon land. if( ( pid = fork() ) < 0 ) { perror( "fork" ); exit( 0 ); } else if( pid != 0 ) { // parent exit(0); } */ // Create a new session. // setsid(); // See if we have been spawned by another instance. // If so, inherit all open descriptors. PC *pc; String str; Cout << "Looking for reboot.tab\n"; InFile tab( "./reboot.tab" ); if( !tab ) { Cout << "No reboot.tab, this is a fresh boot.\n\r"; server.boot( thePort ); } 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 ); for( ; ; ) { if( tab.eof() ) break; Cout << "Restoring old connection.\n"; tab.getstring( name ); if( tab.eof() ) break; tab.getstring( host ); if( tab.eof() ) break; port = tab.getnum(); desc = tab.getnum(); Cout << "\n" << 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(); Cout << "Loading help Index.\n"; loadHelps(); Cout << "Loading areas.\n"; loadAreas(); Cout << "Linking world.\n"; buildWorld(); // OK here goes the game loop while( !DOWN ) { // High precision timer server.sleep( PULSE ); // Pulse if( !pulse-- ) { heartbeat(); pulse = PULSE_PER_SEC; } // Check for new connections if( server.newConnection() ) { Socket * sock = server.accept(); if( sock ) { pc = new PC( &server, sock, "" ); printf("New connection from %s\n", sock->getHostName() ); pc->setState( STATE_INIT ); pc->view( "./title" ); pcs.addTop( pc ); Nanny( pc ); pc->flush(); } } // Poll all active descriptors server.poll(); // Set the current pointer back to head of player list pcs.reset(); while( ( pc = pcs.peek() ) ) { String commd( 256 ); // 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 pc->flush(); server.remove( pc->getSock() ); pc->getSock()->close(); if( pc->inRoom() ) pc->inRoom()->rmCharInv( pc ); pcs.remove( pc ); delete pc; continue; } // Boot sockets with exceptional condition // Need to add handler instead of booting. if( server.error( pc->getSock() ) ) { Cout << "Closing link to " << pc->getName() <<endl; server.remove( pc->getSock() ); pc->getSock()->close(); if( pc->inRoom() ) pc->inRoom()->rmCharInv( pc ); pcs.remove( pc ); delete pc; continue; } // Read socket if ready if( server.canRead( pc->getSock() ) ) { pc->readInput(); if( pc->getSock()->eof() ) { pc->setState( STATE_BOOT ); Cout << "EOF Read: Closing link to " << pc->getName() << endl; server.remove( pc->getSock() ); pc->getSock()->close(); pcs.remove( pc ); delete pc; continue; } } if( !pc->inBuf() && !pc->outBuf() ) continue; else if( pc->pagePending() ) pc->page( "" ); else if( pc->getEditor() ) { if( !pc->getNextCommand() ) continue; commd = pc->getCommand(); if( commd == "quit" ) { pc->quitEditor(); } else { // Need to check return status and pass command on // to regular interpreter if not recognized pc->getEditor()->command( commd ); } pc->flush(); pc->putPrompt(); continue; } else if( pc->inBuf() ) { if( pc->getState() >= STATE_INIT && pc->getState() < STATE_PLAYING ) { if( !pc->getNextCommand() ) continue; commd = pc->getCommand(); Nanny( pc, commd ); } else if( pc->inBuf() && pc->getState() > STATE_PLAYING ) { switch( pc->getState() ) { default : pc->setState( STATE_PLAYING ); break; //case STATE_EMAIL : pc->do_mail( buf ); break; //case STATE_EDIT_TEXT : pc->Edit( buf ); break; } // pc->flush(); // pc->putPrompt(); // continue; } else { pc->getNextCommand(); pc->command(); if( pc->outBuf() ) pc->flush( ); if( pc->pagePending( ) ) pc->page( "" ); else pc->putPrompt( ); } } // Process some buffered output if( pc->outBuf( ) ) { pc->flush(); if( pc->getState() == STATE_PLAYING ) pc->putPrompt(); } } } // Bottom of game loop if( REBOOT ) { OutFile out( "reboot.tab" ); // Write the control socket descriptor so the Server object can // pick it up on way back in after execl() out << "mudpp.1~" << "localhost~" << server.getPort() << " " << server.getDesc(); // Write each player name and descriptor so mud++ can reload // the player files after execl() pcs.reset(); while( ( pc = pcs.peek() ) ) { out << "\n" << pc->getName() << '~' << pc->getSock()->getHostName() << '~' << pc->getSock()->getPort() << " " << pc->getSock()->getDesc(); pcs.next(); } out.close(); execl( "../src/mud++", "mud++", (char*)0 ); } exit( 0 ); } void loadAreas() { String areaKey; char filename[256]; char buf[256]; Area *area; InFile in( AREA_FILE ); while( in ) { in.getline( filename ); Cout << "Area-[" << filename << "]" << endl; if( !*filename ) break; InFile inArea( filename ); if( inArea ) { inArea.getword( buf ); if( *buf == 'A' ) { areaKey = inArea.getword( buf ); area = new Area( areaKey ); area->readFrom( inArea ); areas.addTop( area ); } else { Cout << "Area file [" << filename << "] has no area header.\n"; exit(0); } } else { Cout << "Error opening area file [" << filename << "]\n"; exit(0); } } Cout <<"\nDatabase booted sucessfully.\n"; } // Link rooms, do initial repop void buildWorld() { Area *area; areas.reset(); while( ( area = areas.peek() ) ) { areas.next(); area->hardLink(); } // 2 seperate loops in case I decide to do something funky with doors. areas.reset(); while( ( area = areas.peek() ) ) { areas.next(); area->repop(); } }