/
ScryMUD/mud/
ScryMUD/mud/grrmud/Boards/
ScryMUD/mud/grrmud/Help/
ScryMUD/mud/grrmud/Pfiles/
ScryMUD/mud/grrmud/PlayerSacks/
ScryMUD/mud/grrmud/PlayerShops/
ScryMUD/mud/grrmud/help_filter/
ScryMUD/mud/hegemon/
ScryMUD/mud/hegemon/data/
ScryMUD/mud/hegemon/data/help/battle/
ScryMUD/mud/hegemon/data/help/client/
ScryMUD/mud/hegemon/data/help/communications/
ScryMUD/mud/hegemon/data/help/skills/
ScryMUD/mud/hegemon/data/help/spells/
ScryMUD/mud/include/
ScryMUD/mud/lib/
ScryMUD/mud/lib/bitfield/
ScryMUD/mud/lib/log/
ScryMUD/mud/lib/string2/
// $Id: grrmud.cc,v 1.27.2.28 2000/06/28 02:07:32 aasen Exp $
// $Revision: 1.27.2.28 $  $Author: aasen $ $Date: 2000/06/28 02:07:32 $

//
//ScryMUD Server Code
//Copyright (C) 1998  Ben Greear
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.
//
//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//
// To contact the Author, Ben Greear:  greear@cyberhighway.net, (preferred)
//                                     greearb@agcs.com
//

///**********************  grrmud.cc  ***************************///
/* holds main function, drives the rest of the MUD */

#ifdef USEMYSQL
#include <mysql/mysql.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>

#include <unistd.h>

#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <signal.h>
#include <limits.h>
#include <iostream.h>
#include <fstream.h>
#include <strings.h>
#include <string2.h>
#include <list2.h>
#include <PtrArray.h>
#include "classes.h"
#include "misc.h"
#include "misc2.h"
#include "battle.h"
#include "commands.h"
#include "command2.h"
#include "command3.h"
#include "command4.h"
#include "spec_prc.h"
#include "const.h"
#include "grrmud.h"
#include "load_wld.h"
#include "login.h"
#include <LogStream.h>
#include "SkillSpell.h"
#include "BuildInfo.h"
#include "BugEntry.h"
#include "Filters.h"
#include "ServerConfig.h"
#include <new>

#define MAX_HOSTNAME    256

int initCmdsCollection(); //found in parse_gen.cc
int initCmdsArray();      //found in parse_gen.cc

/* local globals */
int     do_shutdown = FALSE;        /* clean shutdown */
int     grr_reboot;                 /* reboot status */
int     mother_desc = 0;            /* file desc of the mother connection */
int     avail_descs;                /* max descriptors available */
int     sockets_connected = 0;
int     First_Room = 0;
int     Last_Room = 100;
int     BOOT_TIME = 0;

//Global Variables!!

// These are used to track usage of the List<T> classes.
int __node_cnt = 0;
int __list_cnt = 0; 
int __cell_cnt = 0;

#ifdef USEMYSQL
MYSQL* database;
#endif

ServerConfig config;

LogStream            mudlog("./log/logfile.txt", ERROR | DIS | WRN | LSEC | DB | DBG);
//LogStream            mudlog("./log/logfile.txt", ERROR | DIS | WRN | LSEC | DB | DBG, LogStream::APPEND);
//LogStream            obj_ptr_log("./log/obj_ptr.log", ALL, LogStream::APPEND);
LogStream            obj_ptr_log("./log/obj_ptr.log", ERROR | DIS); // turn this off, for now.

List<String*>        banned_hosts;
class critter        mob_list[NUMBER_OF_MOBS + 1];
class object         obj_list[NUMBER_OF_ITEMS + 1];
class door_data      door_list[NUMBER_OF_DOORS + 1];
LazyPtrArray<room>   room_list;


BugCollection bl_ideas("./World/BC_IDEAS", "Idea Listing",
                       BugCollection::IDEAS);
BugCollection bl_bugs("./World/BC_BUGS", "Bug Listing",
                      BugCollection::BUGS);

List<critter*>        linkdead_list; 
List<critter*>        pc_list;  
List<critter*>        new_pc_list;  
List<room*>           embattled_rooms; 

List<door*>              affected_doors;
List<critter*>        affected_mobs; 
List<room*>           affected_rooms; 
List<object*>         affected_objects; 


   /* a list of mobs currently doing
      mob procs, will get a command 
      off of their queues untill queues are empty. */
PtrArray<critter>        proc_action_mobs;
PtrArray<room>           proc_action_rooms;
PtrArray<object>         proc_action_objs;


List<room*>           pulsed_proc_rooms; 
List<object*>         pulsed_proc_objects; 
List<object*>               obj_to_be_disolved_list;

int Cur_Max_Obj_Num = 0; //these are useful for OLC and saving SOBJs 
int Cur_Max_Crit_Num = 0;
int Cur_Max_Door_Num = 0;
int Cur_Max_Room_Num = 0; 

bitfield Obj_Wear_Flags_Mask;
//bitfield Obj_Wear_Procs_Mask;
//bitfield Obj_Remove_Procs_Mask;
bitfield Obj_Consume_Procs_Mask;
//bitfield Shop_Data_Buy_Procs_Mask;
//bitfield Shop_Data_Sell_Procs_Mask;
//bitfield Shop_Data_Offer_Procs_Mask;

/* these make certain functions constant time instead of linear or log(n) */

int
   ABSORB_BLOWS_SKILL_NUM,
   ACROBATICS_SKILL_NUM,
   ANCIENT_LANGUAGES_SKILL_NUM,
   ALCHEMY_SKILL_NUM,
   ARCHERY_SKILL_NUM,
   ARMOR_SKILL_NUM,
   BACKSTAB_SKILL_NUM,
   BALLISTICS_SKILL_NUM,
   BASH_DOOR_SKILL_NUM,
   BASH_SKILL_NUM,
   BERZERK_SKILL_NUM,
   BIND_WOUND_SKILL_NUM,
   BLACKSMITHING_SKILL_NUM,
   BLEND_SKILL_NUM,
   BLESS_SKILL_NUM,
   BLINDNESS_SKILL_NUM,
   BLOCK_SKILL_NUM,
   BLOCK_DOOR_SKILL_NUM,
   BODYSLAM_SKILL_NUM,
   BOW_SKILL_NUM,
   BRAWLING_SKILL_NUM,
   BREW_SKILL_NUM,
   BURNING_HANDS_SKILL_NUM,
   BUTCHER_SKILL_NUM,
   CALM_SKILL_NUM,
   CAMOUFLAGE_SKILL_NUM,
   CAMPING_SKILL_NUM,
   CAUSE_CRITICAL_SKILL_NUM,
   CAUSE_SICKNESS_SKILL_NUM,
   CHARM_SKILL_NUM,
   CHANNELLING_SKILL_NUM,
   CIRCLE_SKILL_NUM,
   CLAW_SKILL_NUM,
   CLIMBING_SKILL_NUM,
   COMMERCE_SKILL_NUM,
   CONSTRUCT_SKILL_NUM,
   CONJURING_SKILL_NUM,
   CONJURE_HORDE_SKILL_NUM,
   CONJURE_MINION_SKILL_NUM,
   CURE_SKILL_NUM,
   CREATE_GOLEM_SKILL_NUM,
   CREATE_LIGHT_SKILL_NUM,
   CREATE_FOOD_SKILL_NUM,
   CREATE_WATER_SKILL_NUM,
   CREATION_SKILL_NUM,
   CRITICAL_STRIKE_SKILL_NUM,
   CURE_BLINDNESS_SKILL_NUM,
   CURE_CRITICAL_SKILL_NUM,
   CURE_SERIOUS_SKILL_NUM,
   CURSE_SKILL_NUM,
   DAGGER_SKILL_NUM,
   DETECTION_SKILL_NUM,
   DETECT_ALIGNMENT_SKILL_NUM,
   DETECT_HIDDEN_SKILL_NUM, 
   DETECT_INVISIBILITY_SKILL_NUM,
   DETECT_MAGIC_SKILL_NUM,
   DETECT_POISON_SKILL_NUM,
   DETECT_RESISTANCES_SKILL_NUM,
   DISARM_SKILL_NUM,
   DISTORTION_WALL_SKILL_NUM,
   DISPEL_MAGIC_SKILL_NUM,
   DISPEL_GOOD_SKILL_NUM,
   DISPEL_EVIL_SKILL_NUM,
   DIVINE_PROTECTION_SKILL_NUM,
   DODGE_SKILL_NUM,
   DOD_SKILL_NUM,
   DOOR_BASH_SKILL_NUM,
   DUAL_WIELD_SKILL_NUM,
   EARTHMELD_SKILL_NUM,
   ELEMENTALISM_SKILL_NUM,
   ENCHANTMENT_SKILL_NUM,
   ENCHANT_ARMOR_SKILL_NUM,
   ENCHANT_WEAPON_SKILL_NUM,
   ENHANCED_DAMAGE_SKILL_NUM,
   ENTANGLE_SKILL_NUM,
   FAERIE_FIRE_SKILL_NUM,
   FENCING_SKILL_NUM,
   FIRE_BLADE_SKILL_NUM,
   FIREBALL_SKILL_NUM,
   FIREPROOF_SKILL_NUM,
   FIRESTORM_SKILL_NUM,
   FIREWALL_SKILL_NUM,
   FLAME_STRIKE_SKILL_NUM,
   FLESH_TO_STONE_SKILL_NUM,
   FLY_SKILL_NUM,
   FORESTRY_SKILL_NUM,
   FROST_BLADE_SKILL_NUM,
   GATE_SKILL_NUM,
   GROUP_HEAL_SKILL_NUM,
   GUARD_SKILL_NUM,
   HARM_SKILL_NUM,
   HARMING_SKILL_NUM,
   HASTE_SKILL_NUM,
   HEAL_SKILL_NUM,
   HEALING_SKILL_NUM,
   HERBALISM_SKILL_NUM,
   HEROS_FEAST_SKILL_NUM,
   HIDE_SKILL_NUM,
   HONOR_CODE_SKILL_NUM,
   HOLY_WORD_SKILL_NUM,
   HURL_SKILL_NUM,
   ICESTORM_SKILL_NUM,
   IDENTIFY_SKILL_NUM,
   ILLUMINATE_SKILL_NUM,
   ILLUSION_SKILL_NUM,
   INFRAVISION_SKILL_NUM,
   INVISIBILITY_SKILL_NUM,
   KICK_SKILL_NUM,
   LEADERSHIP_SKILL_NUM,
   LIGHTNING_SKILL_NUM,
   LIGHTNING_STORM_SKILL_NUM,
   LITERACY_SKILL_NUM,
   LOCATE_SKILL_NUM,
   LOGIC_SKILL_NUM,
   LORE_SKILL_NUM,
   MACE_SKILL_NUM,
   MAGIC_SHIELD_SKILL_NUM,
   MANA_SKILL_NUM,
   MARTIAL_ARTS_SKILL_NUM,
   MASS_CHARM_SKILL_NUM,
   MEDITATION_SKILL_NUM,
   METEORSTORM_SKILL_NUM,
   MIRROR_IMAGE_SKILL_NUM,
   NECROMANCY_SKILL_NUM,
   ORB_OF_POWER_SKILL_NUM,
   PARRY_SKILL_NUM,
   PASSDOOR_SKILL_NUM,
   PHYSICAL_ARTS_SKILL_NUM,
   PHYSIK_SKILL_NUM,
   PFE_SKILL_NUM,
   PFG_SKILL_NUM,
   PHILOSOPHY_SKILL_NUM,
   PICKLOCK_SKILL_NUM,
   PLAGUE_SKILL_NUM,
   POISON_SKILL_NUM,
   PORTAL_SKILL_NUM,
   PRISMATIC_GLOBE_SKILL_NUM,
   PROTECTION_SKILL_NUM,
   QUAKE_SKILL_NUM,
   QUICKFOOT_SKILL_NUM,
   RAINBOW_SKILL_NUM,
   RAISE_UNDEAD_SKILL_NUM,
   RECALL_SKILL_NUM,
   RECHARGE_SKILL_NUM,
   RELIGION_SKILL_NUM,
   REMOVE_CURSE_SKILL_NUM,
   REMOVE_POISON_SKILL_NUM,
   RESCUE_SKILL_NUM,
   RESTORE_SKILL_NUM,
   RUNE_EDGE_SKILL_NUM,
   SANCTUARY_SKILL_NUM,
   SCAN_SKILL_NUM,
   SCRIBE_SKILL_NUM,
   SCROLLS_SKILL_NUM,
   SCRYING_SKILL_NUM,
   SECOND_ATTACK_SKILL_NUM, 
   SHADOWS_BLESSING_SKILL_NUM,
   SHIELD_SKILL_NUM,
   SHOCKING_GRASP_SKILL_NUM,
   SKIN_SKILL_NUM,
   SLEEP_SKILL_NUM,
   SNEAK_SKILL_NUM,
   SOBER_SKILL_NUM,
   STEAL_SKILL_NUM,
   STRENGTH_SKILL_NUM,
   STRENGTH_CONDITIONING_SKILL_NUM,
   STONE_SKIN_SKILL_NUM,
   SUMMON_SKILL_NUM,
   SWORD_SKILL_NUM,
   SWORDBOND_SKILL_NUM,
   TAIL_SKILL_NUM,
   TAMMUZ_SKILL_NUM,
   TELEPORT_SKILL_NUM,
   THROWING_SKILL_NUM,
   TORNADO_SKILL_NUM,
   TRACK_SKILL_NUM,
   TRIP_SKILL_NUM,
   TYPHOON_SKILL_NUM,
   WEAKEN_SKILL_NUM,
   WEAPON_MASTERY_SKILL_NUM,
   WEB_SKILL_NUM,
   WHIP_SKILL_NUM,
   WIZARD_EYE_SKILL_NUM,
   WRESTLING_SKILL_NUM,
// New skills for the Avian/Dragon race
// [RJY] - Trice
   HOVER_SKILL_NUM,
   WING_POWER_SKILL_NUM,
   SECRET_FEATHER_SKILL_NUM,
   SHRIEK_SKILL_NUM,
   CARRY_SKILL_NUM,
   PECK_SKILL_NUM,
   TAILSWEEP_SKILL_NUM,
   VISION_SKILL_NUM,
   DIVE_SKILL_NUM,
   GLARE_SKILL_NUM,
   WINDZONE_SKILL_NUM,
   PLUCK_SKILL_NUM,
   BIRDSEYE_SKILL_NUM;



               /* end of global variables */



/* *********************************************************************
*  main game loop and related stuff                                    *
********************************************************************* */

char* extra_memory = NULL;

void scry_new_handler() {
   free(extra_memory);
   core_dump("scry_bad_alloc.");
}


//hopefully trap the TERM signal and exit gracefully
void sig_term_handler(int signo) { 
   //reestablish signal handler
   if ((signo != SIGSEGV) && (signo != SIGBUS) && (signo != SIGIOT)) {
      cerr << "Got signal: " << signo << " NOT setting to default handler." << endl;
      signal(signo, (&sig_term_handler));
   }
   else {
      cerr << "Got signal: " << signo << " setting to default handler." << endl;
      signal(signo, SIG_DFL);
   }

   if (signo == SIGTERM) {
      mudlog << "Got SIGTERM, shutting down.\n";
      mudlog << flush;
      do_shutdown = 1;
   }
   else if (signo == SIGALRM) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGALRM, ignoring!!\n";
      mudlog << flush;
   }
   else if (signo == SIGHUP) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGHUP, ignoring!!\n";
      mudlog << flush;
   }
   else if (signo == SIGIO) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGIO, ignoring!!\n";
      mudlog << flush;
   }
   else if (signo == SIGPIPE) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGPIPE, ignoring!!\n";
      mudlog << flush;
   }
//   else if (signo == SIGPOLL) {
//      if (mudlog.ofLevel(DIS))
//         mudlog << "Got SIGPOLL, ignoring!!\n";
//      mudlog << flush;
//   }
   else if (signo == SIGSTOP) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGSTOP, shutting down..";
      mudlog << flush;
      do_shutdown = 1;
   }
   else if (signo == SIGINT) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGINT, shutting down..";
      mudlog << flush;
      do_shutdown = 1;
   }
   else if (signo == SIGTTIN) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGTTIN, ignoring!!\n";
      mudlog << flush;
   }
   else if (signo == SIGTTOU) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGTTOU, ignoring!!\n";
      mudlog << flush;
   }
   else if ((signo == SIGUSR1) || (signo == SIGUSR2)) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGUSR1 or SIGUSR2, ignoring!!\n";
      mudlog << flush;
   }
   else if (signo == SIGVTALRM) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGVTALRM, ignoring!!\n";
      mudlog << flush;
   }
   else if (signo == SIGXCPU) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGXCPU, shutting down..!!\n";
      mudlog << flush;
      do_shutdown = TRUE;
   }
   else if (signo == SIGXFSZ) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGXFSZ, shutting down..!!\n";
      mudlog << flush;
      do_shutdown = TRUE;
   }
   else if ((signo == SIGSEGV) || (signo == SIGIOT)) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGSEGV or SIGIOT (" << signo << "), shutting down..!!\n";
      mudlog << flush;
      char buf[100];
      sprintf(buf, "%s.CRASH.%i", mudlog.getFileName(), getpid());
      mudlog.flushToFile(buf);
      sprintf(buf, "%s.CRASH.%i", obj_ptr_log.getFileName(), getpid());
      obj_ptr_log.flushToFile(buf);
      abort();
      //do_shutdown = TRUE;
   }
   else if (signo == SIGBUS) {
      if (mudlog.ofLevel(DIS))
         mudlog << "Got SIGBUS, shutting down..!!\n";
      mudlog << flush;
      char buf[100];
      sprintf(buf, "%s.CRASH.%i", mudlog.getFileName(), getpid());
      mudlog.flushToFile(buf);
      sprintf(buf, "%s.CRASH.%i", obj_ptr_log.getFileName(), getpid());
      obj_ptr_log.flushToFile(buf);
      abort();
      //do_shutdown = TRUE;
   }
   else {
      mudlog << "Got unknown SIGNAL:  " << signo 
             << " shutting down..." << endl;
      mudlog << flush;
      do_shutdown = TRUE;
   }      
      
}//



int main(int argc, char** argv) {
   String dummy;
   dummy.setLogFile(&mudlog);
   //String::setLogFile(&mudlog);
   extra_memory = (char*)(malloc(20000));

   signal(SIGTERM, (&sig_term_handler));
   //signal(SIGABRT, (&sig_term_handler));
   signal(SIGALRM, (&sig_term_handler));
   signal(SIGFPE, (&sig_term_handler));
   signal(SIGHUP, (&sig_term_handler));
   signal(SIGILL, (&sig_term_handler));
   signal(SIGINT, (&sig_term_handler));
   signal(SIGIO, (&sig_term_handler));
   //signal(SIGIOT, (&sig_term_handler));
   signal(SIGPIPE, (&sig_term_handler));
   //signal(SIGPOLL, (&sig_term_handler));
   signal(SIGPROF, (&sig_term_handler));
   signal(SIGQUIT, (&sig_term_handler));
   signal(SIGSTOP, (&sig_term_handler));
   //signal(SIGSYS, (&sig_term_handler));
   signal(SIGTERM, (&sig_term_handler));
   
   // This is like: ctrl-z, no need to catch it.
   //signal(SIGTRAP, (&sig_term_handler));

   signal(SIGTSTP, (&sig_term_handler));
   signal(SIGTTIN, (&sig_term_handler));
   signal(SIGTTOU, (&sig_term_handler));
   signal(SIGUSR1, (&sig_term_handler));
   signal(SIGUSR2, (&sig_term_handler));
   signal(SIGVTALRM, (&sig_term_handler));
   signal(SIGXCPU, (&sig_term_handler));
   signal(SIGXFSZ, (&sig_term_handler));

   /* Trap a few beasties, but only once!! */
   signal(SIGSEGV, (&sig_term_handler));
   signal(SIGSEGV, (&sig_term_handler));

   set_new_handler(scry_new_handler);

   String buf(50);

   room_list.ensureCapacity(NUMBER_OF_ROOMS + 1);

   BOOT_TIME = time(NULL);

   Sprintf(buf, "echo \"%i\" > GRRMUD.PID", getpid());
   system("rm -f GRRMUD.PID");
   system(buf);
   system("chmod a+r GRRMUD.PID");

   cout << "ScryMUD version: " << BuildInfo::getVersion() << endl;
   cout << "Built on:        " << BuildInfo::getBuildDate() << endl;
   cout << "On machine:      " << BuildInfo::getBuildMachine() << endl;
   cout << "By:              " << BuildInfo::getBuilder() << endl << endl;

   cout << "Welcome to SryMUD server by Ben Greear.
This server and it's accompanying client, Hegemon, is distributed
under the GNU General Public License (GPL).  See the License file
for more information.

Be sure to check the log files in the ./log directory if you have
any strange occurances.

--Ben Greear (greear@cyberhighway.net, greearb@agcs.com)\n\n";

   srand(time(0));

   config.read("grrmud.cfg");
   config.readDynamic("dynamic.cfg");

   CmdLineInfo::init(argc, argv);

   #ifdef USEMYSQL
   if (config.useMySQL) {
      mysql_init(database);
      mysql_options(database, MYSQL_READ_DEFAULT_GROUP, "gmud");
      if (!mysql_real_connect(database, config.mySQLhost, config.mySQLuser,
                              config.mySQLpassword, config.mySQLdatabase,
                              config.mySQLport, NULL, 0)) {
         cout << "Couldn't connect to mySQL: " << mysql_error(database) << endl;
         cout << "Shutting down.\n";
         exit(0);
      }
   }
   #endif

   // run the auto-generated methods that load all of our commands
   // into the commands collection and arrays. (see parse_gen.cc)
   initCmdsCollection();
   initCmdsArray();

   readSiteBanned();

   // Drop into our endless loop.
   run_the_game(config.port);

   config.writeDynamic("dynamic.cfg");

   if (grr_reboot >= 1) {
      system("touch .reboot");
   }//if
   else {
      system("rm -f .reboot");
   }

   do_shutdown = TRUE;
   return (grr_reboot);
}


/* Init sockets, run game, and cleanup sockets */

void run_the_game(int port) {
   int  s;

   if (mudlog.ofLevel(DIS))
      mudlog << "Opening mother connection.\n";

   mother_desc = s = init_socket(port);
   
   system("rm -f .reboot"); //if it crashes in this next bit stay down,
                         //the world files are corrupt probably

   config.currentLoadModifier = config.bootLoadModifier;
   load_wld(); //read in universe

   ZoneList::instance().readSelf();
   ZoneList::instance().execute(); //modify zones as needed

   // Read in the bug and idea list.
   bl_bugs.read();
   bl_ideas.read();

   config.currentLoadModifier = config.regularLoadModifier;

   system("touch .reboot");

   if (config.convertWorldFromDev) {
      config.convertWorldFromDev = false;
      write_all_zones();

      // Write out un-directed graphs for all zones.
      // This is currently broken. --Ben
      //ZoneCollection::instance().createNeatoFiles();

      ofstream ps_dot_script("./World/ss_ps.dot");
      ofstream gif_dot_script("./World/ss_gif.dot");
   
      if (ps_dot_script) {
         ps_dot_script << SSCollection::instance().generatePsDotScript();
      }
      else {
         mudlog << "ERROR: could not open ./World/ss_ps.dot" << endl;
      }

      if (gif_dot_script) {
         gif_dot_script << SSCollection::instance().generateGifDotScript();
      }
      else {
         mudlog << "ERROR: could not open ./World/ss_gif.dot" << endl;
      }
      
      ofstream ss_html("./World/ss_html.html");
      
      if (ss_html) {
         ss_html << SSCollection::instance().generateHtml();
      }
      else {
         mudlog << "ERROR: could not open ./World/ss_html.html" << endl;
      }

      grr_reboot = 1;
      return;
   }//if


   init_masks();   //set all the masks for miscelaneous bitfields

   mudlog.log(DBG, "Entering game loop.\n");

   game_loop(s);

   close_sockets(s);
   mudlog.log(DIS, "Normal termination of game.\n");
}//run_the_game



/* Accept new connects, relay commands, and call 'heartbeat-functs' */

void game_loop(int s)  {
   fd_set input_set, output_set, exc_set;
   struct timeval last_time, now, timespent, timeout, null_time;
   struct timeval time_since_pulse;
   struct timezone init;
   static struct timeval opt_time;
   Cell<critter*> pc_cell;
   critter* pc_ptr;
   critter* tmp_ptr;
   int i, pulse = 1;
   int maxdesc = s;
   short did_pulse = FALSE;
   String buf(100);
   String prompt(100);

//   log("Beginning of game_loop\n");

   /* initialize the timezone tz */

   //init.tz_minuteswest = 6*60;
   //init.tz_dsttime = DST_USA;

   null_time.tv_sec = 0;
   null_time.tv_usec = 0;

   time_since_pulse.tv_sec = 0;
   time_since_pulse.tv_usec = 0;

   opt_time.tv_usec = config.optUsec;  /* Init time values */
   opt_time.tv_sec = 0;
   
   gettimeofday(&last_time, NULL);  // was &init

   avail_descs = config.maxPlayers;

   /* Main loop */
   while (!do_shutdown) {

      /* add new pc's to game */
      new_pc_list.head(pc_cell);
      pc_ptr = pc_cell.next();
      while (pc_ptr) {
         pc_list.gainData(pc_ptr);
         pc_ptr = new_pc_list.lose(pc_cell);
      }//while

      FD_ZERO(&input_set);
      FD_ZERO(&output_set);
      FD_ZERO(&exc_set);
      FD_SET(s, &input_set);

      maxdesc = s;
      pc_list.head(pc_cell);
      int desc;
      while ((pc_ptr = pc_cell.next())) {
         if ((pc_ptr->MODE == MODE_GO_LINKDEAD_PLEASE) ||
             (pc_ptr->MODE == MODE_LOGOFF_NEWBIE_PLEASE) ||
             (pc_ptr->MODE == MODE_QUIT_ME_PLEASE)) {
            continue;
         }

         desc = pc_ptr->getDescriptor();
         if ((desc < 0) || (desc > config.maxPlayers)) {
            mudlog << "ERROR:  descriptor out of range:  " << desc
                   << " on critter:  " << *(pc_ptr->getName())
                   << endl;
         }
         else {
            if (desc > maxdesc) {
               maxdesc = desc;
            }
            FD_SET(desc, &input_set);
            FD_SET(desc, &exc_set);
            FD_SET(desc, &output_set);
         }//else
      }//while

      //log("check out the time.\n");

      gettimeofday(&now, &init);
      timespent = timediff(&now, &last_time);
      timeout = timediff(&opt_time, &timespent);
      last_time.tv_sec = now.tv_sec + timeout.tv_sec;
      last_time.tv_usec = now.tv_usec + timeout.tv_usec;
      if (last_time.tv_usec >= 1000000) {
         last_time.tv_usec -= 1000000;
         last_time.tv_sec++;
      }//if

      time_since_pulse.tv_usec += last_time.tv_usec;
      time_since_pulse.tv_sec += last_time.tv_sec;
      if (time_since_pulse.tv_usec >= 1000000) {
         time_since_pulse.tv_usec -= 1000000;
         time_since_pulse.tv_sec++;
      }//if

      if (time_since_pulse.tv_sec >= 1) {
         pulse++;
         time_since_pulse.tv_usec = 0;
         time_since_pulse.tv_sec = 0;
         did_pulse = TRUE;
      }//if

      //log("Doing log-offs.\n");

                       /* do log_offs of all types */

      pc_list.head(pc_cell);
      pc_ptr = pc_cell.next();
      while (pc_ptr) {
         if (pc_ptr->getMode() == MODE_GO_LINKDEAD_PLEASE) {
            mudlog << "Logging off via MODE_GO_LINKDEAD_PLEASE." << endl;
            tmp_ptr = pc_ptr;
            pc_ptr = pc_list.lose(pc_cell);
            tmp_ptr->doGoLinkdead(); //do not delete the critter!
         }//if
         else if (pc_ptr->getMode() == MODE_LOGOFF_NEWBIE_PLEASE) {
            mudlog << "Logging off via MODE_LOGOFF_NEWBIE_PLEASE." << endl;
            tmp_ptr = pc_ptr;
            pc_ptr = pc_list.lose(pc_cell);
            tmp_ptr->doLogOffNewLogin();
            delete tmp_ptr;
         }//if
         else if (pc_ptr->getMode() == MODE_QUIT_ME_PLEASE)  {
            mudlog << "Logging off via MODE_QUIT_ME_PLEASE." << endl;
            tmp_ptr = pc_ptr;
            pc_ptr = pc_list.lose(pc_cell);
            if (tmp_ptr->pc->link_condition == CON_LOGGING_IN) { //special case
               tmp_ptr->doLogOffNewLogin();
            }
            else {
               tmp_ptr->doLogOffActive();
            }
            delete tmp_ptr;
         }//else
         else {
            pc_ptr = pc_cell.next();
         }//else
      }//while pc_ptr

      linkdead_list.head(pc_cell);
      pc_ptr = pc_cell.next();
      while (pc_ptr) {
         if ((pc_ptr->getMode() == MODE_LOG_OFF_LINKDEAD_PLEASE) ||
             (pc_ptr->getMode() == MODE_QUIT_ME_PLEASE)) {
            mudlog << "Logging off via MODE_LOG_OFF_LINKDEAD_PLEASE." << endl;
            tmp_ptr = pc_ptr;
            pc_ptr = linkdead_list.lose(pc_cell);
            if (tmp_ptr->pc->link_condition == CON_LOGGING_IN) { //special case
               tmp_ptr->doLogOffNewLogin();
            }
            else {
               tmp_ptr->doLogOffInactive();
            }
            delete tmp_ptr;
         }//else
         else {
            pc_ptr = pc_cell.next();
         }//else
      }//while pc_ptr

      //log("Doing pulsed procs.\n");
      /* pulsed processes */

      if (did_pulse) {
         if ((pulse % 11) == 0) {
            if (!embattled_rooms.isEmpty()) { 
               do_battle();
            }
            do_mini_tick(); //decrements pause ect.
         }//if

         // Will try to pulse the entire MUD ever 20 seconds.
         // We will pulse one-tenth at a time (room wise).
         if ((pulse % 10) == 0)
           do_pulsed_spec_procs(First_Room, Last_Room);
         if ((pulse % 379) == 0) {
            do_tick(); //takes care of zones too
         }
         
         if (((pulse + 1) % 1000) == 0) {
           save_all();  //save all, about every 5 ticks
         }//if

         // trim the log files to the last 1000 lines, start adding from there.
         if (((pulse + 1) % 10000) == 0) {
            //mudlog << "WARNING:  About to truncate.\n" << endl;
            mudlog.truncate();
         }
      }//if
      
       //log("Calling select, poll\n");
      
      null_time.tv_sec = 0;
      null_time.tv_usec = 0;

      if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time)
           < 0) {
         if (errno == EBADF) {
            mudlog << "ERROR:  bad file desc. given in a set to select.\n";
            do_shutdown = TRUE;
            exit(100);
         }//if
         else if (errno == EINTR) {
            mudlog.log(DIS, "ERROR:  A non blocked signal was caught.\n");
         }//if
         else {
               if (mudlog.ofLevel(DIS)) {
               mudlog << "ERROR:  select, poll:  " << strerror(errno) << endl;
            }
            do_shutdown = TRUE;
            exit(100);
         }//else
      }//if

       //log("calling select, sleep\n");

      if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) {
         if (errno == EBADF) {
            mudlog << "ERROR:  bad file desc. given in a set to select.\n";
            do_shutdown = TRUE;
            exit(100);
         }//if
         else if (errno == EINTR) {
            mudlog.log(DIS, "ERROR:  A non blocked signal was caught.\n");
         }//if
         else {
               if (mudlog.ofLevel(DIS)) {
               mudlog << "ERROR:  select, sleep:  " << strerror(errno) << endl;
            }
            do_shutdown = TRUE;
            exit(100);
         }//else
      }//if


      /* New connection? */
      if (FD_ISSET(s, &input_set)) {
         if (critter::createWithDescriptor(s) < 0) { //set password and descriptor
            mudlog << "ERROR:  new connection:  " << strerror(errno) << endl;
         }//if
      }//if

         //log("Losing link freaks.\n");

                       /* kick out link freaks */
      pc_list.head(pc_cell);
      while ((pc_ptr = pc_cell.next())) {
         if (FD_ISSET(pc_ptr->getDescriptor(), &exc_set)) {
            FD_CLR(pc_ptr->getDescriptor(), &input_set);
            FD_CLR(pc_ptr->getDescriptor(), &output_set);
            pc_ptr->setMode(MODE_GO_LINKDEAD_PLEASE);
         }//if
      }//while

        //log("Get input.\n");
                        /* get input */
      pc_list.head(pc_cell);
      while ((pc_ptr = pc_cell.next())) {
         if ((pc_ptr->MODE != MODE_GO_LINKDEAD_PLEASE) &&
             (pc_ptr->MODE != MODE_LOGOFF_NEWBIE_PLEASE) &&
             (pc_ptr->MODE != MODE_QUIT_ME_PLEASE)) {
              if (FD_ISSET(pc_ptr->getDescriptor(), &input_set)) {
               if (pc_ptr->readInput() < 0) { 
                  mudlog.log(DBG, "Kicked out by negative get_input.\n");
                  if (pc_ptr->pc->link_condition == CON_LOGGING_IN) {
                     pc_ptr->setMode(MODE_LOGOFF_NEWBIE_PLEASE);
                  }
                  else {
                     pc_ptr->setMode(MODE_GO_LINKDEAD_PLEASE);
                  }
               }//if
            }//if
         }//if
      }//while

      int len;
                        /* process input */
      pc_list.head(pc_cell);
      while ((pc_ptr = pc_cell.next())) {
         if ((pc_ptr->MODE != MODE_GO_LINKDEAD_PLEASE) && 
             (pc_ptr->MODE != MODE_LOGOFF_NEWBIE_PLEASE) &&
             (pc_ptr->MODE != MODE_QUIT_ME_PLEASE)) {
            len = pc_ptr->getInput()->Strlen();
               if (len && pc_ptr->getInput()->charAt(len - 1) == '\n') {

               if (mudlog.ofLevel(DBG)) {
                  mudlog << "processing input for: " << *(pc_ptr->getName())
                         << ",  cmd -:" << *(pc_ptr->getInput()) << ":- " << endl;
               }

               critter* snooper;
               if ((snooper = pc_ptr->SNOOPED_BY)) {
                  mudlog.log(TRC, "Within snoop if\n");
                  String buf2(100);
                  Sprintf(buf2, "SNOOP_IN: %S:  %S\n",
                          pc_ptr->getName(snooper->SEE_BIT), pc_ptr->getInput());
                  snooper->show(buf2);
               }//if snoop

               if (pc_ptr->possessing) {
                  String cmd1 = pc_ptr->getInput()->Look_Command();
                  cmd1.Strip();
                  if (mudlog.ofLevel(DBG)) {
                     mudlog << "Is possessing, cmd1: " << cmd1 << endl;
                  }
                  if (strncasecmp(cmd1, "unpossess", strlen("unpossess")) == 0) {
                     if (mudlog.ofLevel(DBG)) {
                        mudlog << "Unpossessing..." << endl;
                     }
                     pc_ptr->unPossess();
                     pc_ptr->show("You have stopped possessing that poor soul.\n");
                     short eos, term_by_period;
                     pc_ptr->getInput()->Get_Command(eos, term_by_period);
                  }//if
                  else {
                     pc_ptr->possessing->processInput(*(pc_ptr->getInput()),
                                                      FALSE, TRUE);
                     if (mudlog.ofLevel(DBG)) {
                        mudlog << "input after processInput: " << *(pc_ptr->getInput())
                               << endl;
                     }//if
                     //pc_ptr->setDoPrompt(TRUE);
                  }
               }//if possessing
               else {
                  pc_ptr->processInput(*(pc_ptr->getInput()), TRUE, FALSE);
               }
            }//else
         }//if
      }//while

       //log("Doing Prompts.\n");
                                     /* do prompts */
      pc_list.head(pc_cell);
      while ((pc_ptr = pc_cell.next())) {
         if (pc_ptr->getMode() == MODE_NORMAL) {
            if (pc_ptr->shouldDoPrompt()) {
               pc_ptr->doPrompt();

                  if (pc_ptr->shouldDoTank()) { //tank graph
                  critter* tank, *victim;
                  if ((victim = pc_ptr->getFirstFighting())) {
                     tank = victim->getFirstFighting();
                     if (tank) {
                        prompt = "\n";
                        int num_of_stars = (int)(((float)
                                                  (tank->getHP())/
                                                  (float)(tank->getHP_MAX())) *
                                                 40.0);
                        for (i = 0; i < num_of_stars; i++) {
                           prompt += "*";
                        }//for
                        prompt += "\n";
                        pc_ptr->show(prompt);
                     }// if tank
                  }//if victim
               }//if tank graph
               pc_ptr->setDoPrompt(FALSE);
            }//if should do prompt
         }//if in MODE_NORMAL
      }//while

      //log("Doing output.\n");
                                    /* output */
      pc_list.head(pc_cell);
      while ((pc_ptr = pc_cell.next())) {
         if ((pc_ptr->MODE != MODE_GO_LINKDEAD_PLEASE) &&
             (pc_ptr->MODE != MODE_LOGOFF_NEWBIE_PLEASE) &&
             (pc_ptr->MODE != MODE_QUIT_ME_PLEASE)) {
            if (pc_ptr->writeOutput() < 0) { 
               mudlog.log(DBG, "Kicked out by negative write_output.\n");
               pc_ptr->setMode(MODE_GO_LINKDEAD_PLEASE);
            }//if
         }//if
      }//while

      /*  Mob action procs.  */
      int obj_was_deleted = FALSE;
      ScriptCmd* cmd_ptr;
      for (int cnt = 0; cnt < proc_action_mobs.getCurLen(); cnt++) {
         if (!(pc_ptr = proc_action_mobs.elementAt(cnt))) {
            continue;
         }//if
         else {
            if (pc_ptr->getPause() > 0) {
               continue;
            }

            ScriptCmd* tmp_cmd_ptr;
            // Cast away const'ness, but still use it as if it's const.
            if ((tmp_cmd_ptr = (ScriptCmd*)(pc_ptr->getNextScriptCmd()))) {
               cmd_ptr = new ScriptCmd(*tmp_cmd_ptr);

               // pc_ptr is the script owner.
               MobScript::parseScriptCommand(*cmd_ptr, *pc_ptr, obj_was_deleted);
               delete cmd_ptr;
            }
            else {
               pc_ptr->finishedMobProc();

               if (!pc_ptr->isInProcNow()) {
                  Sprintf(buf, "Done with script..taking off proc_action_mobs, idx: %i\n",
                          cnt);
                  pc_ptr->show(buf);
                  proc_action_mobs.set((critter*)NULL, cnt);
               }
            }
         }//else
      }//for

      /* Room action procs */
      room* rm_ptr;
      for (int cnt = 0; cnt < proc_action_rooms.getCurLen(); cnt++) {
         if (!(rm_ptr = proc_action_rooms.elementAt(cnt))) {
            continue;
         }//if
         else {
            if (rm_ptr->getPause() > 0) {
               if (mudlog.ofLevel(SCRIPT)) {
                  mudlog << "room_script execute: pause is > 0:  " << rm_ptr->getPause()
                         << "  for room: " << rm_ptr->getIdNum() << endl;
               }
               continue;
            }
            else {
               if (mudlog.ofLevel(SCRIPT)) {
                  mudlog << "room_script execute: attempting to get script cmd for room: "
                         << rm_ptr->getIdNum() << endl;
               }
            }//else

            ScriptCmd* tmp_cmd_ptr;
            // Cast away const'ness, but still use it as if it's const.
            if ((tmp_cmd_ptr = (ScriptCmd*)(rm_ptr->getNextScriptCmd()))) {

               if (mudlog.ofLevel(SCRIPT)) {
                  mudlog << "grrmud.cc:  got a room script command -:"
                         << tmp_cmd_ptr->getCommand() << ":-" << endl;
               }
               cmd_ptr = new ScriptCmd(*tmp_cmd_ptr);

               // rm_ptr is the script owner.
               int script_ret_val;
               if ((script_ret_val = RoomScript::parseScriptCommand(*cmd_ptr, *rm_ptr, obj_was_deleted)) < 0) {
                  if (mudlog.ofLevel(WRN)) {
                     mudlog << "RoomScript command: target -:" 
                            << cmd_ptr->getTarget() << ":-  cmd -:"
                            << cmd_ptr->getCommand() << " for room# "
                            << rm_ptr->getIdNum() << " returned negative value: "
                            << script_ret_val << endl;
                  }
               }
               delete cmd_ptr;
            }
            else {
               rm_ptr->finishedRoomProc();

               // it may have started another one
               if (!rm_ptr->isInProcNow()) {
                  proc_action_rooms.set((room*)NULL, cnt);
               }
            }
         }//else
      }//for


      /* Object action procs */
      object* o_ptr;
      for (int cnt = 0; cnt < proc_action_objs.getCurLen(); cnt++) {
         //mudlog << "Searching for proc objs, cnt: " << cnt << endl;
         if (!(o_ptr = proc_action_objs.elementAt(cnt))) {
            //mudlog << "Was null.\n";
            continue;
         }//if
         else {
            //mudlog << "Found one: " << o_ptr->getIdNum() << endl;
            if (o_ptr->getPause() > 0) {
               continue;
            }

            ScriptCmd* tmp_cmd_ptr;
            // Cast away const'ness, but still use it as if it's const.
            if ((tmp_cmd_ptr = (ScriptCmd*)(o_ptr->getNextScriptCmd()))) {

               if (mudlog.ofLevel(DBG)) {
                  mudlog << "grrmud.cc:  got an object script command -:"
                         << tmp_cmd_ptr->getCommand() << ":-" << endl;
               }
               cmd_ptr = new ScriptCmd(*tmp_cmd_ptr);

               // rm_ptr is the script owner.
               int script_ret_val;
               if ((script_ret_val = ObjectScript::parseScriptCommand(*cmd_ptr, *o_ptr,
                                                                      obj_was_deleted)) < 0) {
                  if (!obj_was_deleted) {
                     if (mudlog.ofLevel(WRN)) {
                        mudlog << "WARNING:  ObjectScript command: target -:" 
                               << cmd_ptr->getTarget() << ":-  cmd -:"
                               << cmd_ptr->getCommand() << " for object# "
                               << o_ptr->getIdNum() << " returned negative value: "
                               << script_ret_val << endl;
                     }
                  }
               }
               delete cmd_ptr;
            }
            else {
               mudlog << "Could not get another command, must be done."
                      << endl;
               o_ptr->finishedObjProc();

               // it may have started another one
               if (!o_ptr->isInProcNow()) {
                  proc_action_objs.set((object*)NULL, cnt);
               }
            }
         }//else
      }//for

      //log("At end of while loop.\n");
   }//while
}// game_loop


struct timeval timediff(struct timeval *a, struct timeval *b)  {
   struct timeval rslt, tmp;

   //log("In timediff.\n");

   tmp = *a;

   if ((rslt.tv_usec = tmp.tv_usec - b->tv_usec) < 0) {
      rslt.tv_usec += 1000000;
      --(tmp.tv_sec);
   }//if
   if ((rslt.tv_sec = tmp.tv_sec - b->tv_sec) < 0) {
      rslt.tv_usec = 0;
      rslt.tv_sec = 0;
   }//if
   return(rslt);
}//timediff()



/* ******************************************************************
*  socket handling                                                  *
****************************************************************** */

int     init_socket(int port)   {
   int  s;
   char *opt;
   char hostname[MAX_HOSTNAME+1];
   struct sockaddr_in sa;
   struct hostent *hp;

   mudlog.log(TRC, "in init_socket\n");
   memset((char*)(&sa), 0, sizeof(struct sockaddr_in ));

   if (gethostname(hostname, MAX_HOSTNAME) < 0) {
      mudlog << "ERROR: gethostname:  " << strerror(errno) << endl;
      //strcpy(hostname, "mogul");
   }//if
   hp = gethostbyname(hostname);
   if (hp == NULL) {
      mudlog << "ERROR:  gethostbyname:  " << strerror(errno) << endl;
      do_shutdown = TRUE;
      exit(100);
   }//if
   sa.sin_family = hp->h_addrtype;
   sa.sin_port   = htons(port);

   if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      mudlog << "ERROR:  socket:  " << strerror(errno) << endl;
      do_shutdown = TRUE;
      exit(100);
   }//if

   if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof (opt))
          < 0) {
      mudlog << "ERROR:  setsockopt:  " << strerror(errno) << endl;
      do_shutdown = TRUE;
      exit(100);
   }//if

   int sanity = 0;
   while (TRUE) {
      if (sanity++ > 25) {
         mudlog << "ERROR:  bind sanity check kicked in.." << endl;
         close(s);
         do_shutdown = TRUE;
         exit(0); //Give up..something is wrong
      }//if
         
      if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
         mudlog << "ERROR:  trying to bind:  " << strerror(errno) << endl;
         sleep(10);
      }//if
      else {
         break;
      }
   }//while

   listen(s, 5);
   return(s);
}//init_socket, for the mother descriptor



int new_connection(int s)  {
   struct sockaddr_in isa;
   unsigned int  i;  //GLIBC
   // int i;
   int t;
 
   mudlog.log(TRC, "in new_connection\n");

   i = sizeof(isa);
   if (getsockname(s, (struct sockaddr *) & isa, &i) < 0) {
      mudlog << "ERROR:  new_connection, getsockname:  " 
             << strerror(errno) << endl;
      return (-1);
   }//if

   if ((t = accept(s, (struct sockaddr *)(&isa), &i)) < 0) {
      mudlog << "ERROR:  accept:  " << strerror(errno) << endl;
      return(-1);
   }//if
   nonblock(t);
   return(t);
}//new_connection()


// Static method, create a critter using the initialized socket s
int critter::createWithDescriptor(int s) {
   int  desc;
   critter *newd = new critter;
   unsigned int size;  //EGCS
   //int size;
   int i;
   struct sockaddr_in sock;
   struct hostent *from;
   String buf2(100);

   newd->pc = new pc_data;
   newd->CRITTER_TYPE = 0; //change default of MOB to PC

   mudlog.log(TRC, "In createWithDescriptor\n");

   if ((desc = new_connection(s)) < 0) {
      delete newd;
      return -1;
   }//if

   newd->setDescriptor(desc);
   
   if (sockets_connected > avail_descs) {
      char* str = "Sorry, ScryMUD is full, try again later!\n\r";
      write(desc, str, strlen(str)); 
      mudlog.log(WRN, "WARNING:  the mud was full.\n");
      close(desc);
      delete newd;
      return -1;
   }//if

   mudlog.log(TRC, "Finding info.\n");

   /* find info */
   size = sizeof(sock);
   if (getpeername(desc, (struct sockaddr *) & sock, &size) < 0) {
      mudlog << "ERROR:  getpeername:  " << strerror(errno) << endl;
   }// if 
   else {
      // BEN:  Turning this off, might be crashing us...can just look up IP's
      // with nslookup
      if (TRUE || !(from = gethostbyaddr((char *)&sock.sin_addr,
               sizeof(sock.sin_addr), AF_INET))) { //begin of if
         //mudlog << "WARNING:  gethostbyaddr:  " << strerror(errno) << endl;
         i = sock.sin_addr.s_addr;
         Sprintf(newd->pc->host, "%i.%i.%i.%i", (i & 0x000000FF),
              ((i & 0x0000FF00) >> 8), ((i & 0x00FF0000) >> 16), 
              ((i & 0xFF000000) >> 24));
      } //if
      else {
         newd->pc->host = from->h_name;
      }//else
   }//else
   
   if (mudlog.ofLevel(DBG))
      mudlog << "Got host:  " << newd->pc->host << endl;

   if (is_banned(newd->pc->host)) {
      char* str =
"Your site has been banned.  There are three possible causes for this:\n\n\
You did something that is very strictly against the rules.\n\n\
Someone from your site did something that is very strictly against the rules,\n\
and you are now connecting from the same machine or IP address as they.\n\n\
Someone from your site did something that is very strictly against the rules,\n\
and tried to persist in that action.  In this case, the whole site has been\n\
banned instead of just one user.\n\n\
If you feel that you or your site has been wrongly sitebanned and would like\n\
the ban removed, please contact greear@cyberhighway.net to discuss the matter.";

      write(desc, str, strlen(str)); 
      close(desc);
      Sprintf(buf2, "WARNING:  Connection attempt denied from [%S]", 
              &(newd->pc->host));
      mudlog.log(WRN, buf2);
      delete newd;
      return -1;
   }//if

   newd->PC_FLAGS.turn_on(16); //insert the \r that DOS loves so much *puke*
   pc_list.prepend(newd);
   sockets_connected++;

         //will read it in, or create it from input
   newd->startLogin(); 
   
   mudlog.log(TRC, "At end of new_descriptor.\n");         
   return TRUE;
}//createWithSocket



int critter::writeOutput() {
   int ret = 0;

   if (isInPageBreak()) { //in pause mode (ie page_break)
      return 0;
   }

   int len = pc->output.Strlen();
   // log("in write_output\n");
   if (len) { //no need to write zip
      if (PC_FLAGS.get(16)) { //if do carriage return
         pc->output.parse_show(PC_FLAGS.get(15)); //CR behind or not
         len = pc->output.Strlen();
      }//if
      if (PC_FLAGS.get(22)) { //if do page breaks
         int len = pc->output.Strlen();
         int nl_cnt = 0;
         int i = 0;
         const char* buf = (const char*)(pc->output);
         for (i = 0; i<len; i++) {
            if (buf[i] == '\n') {
               nl_cnt++;
               if (nl_cnt == max(getLinesOnPage(), 8)) {
                  break;
               }
            }//if
         }//for

         const char* pr = "******[ HIT RETURN TO CONTINUE ] ******";
         if (nl_cnt == max(getLinesOnPage(), 8)) {
            PC_FLAGS.turn_on(23);
            pc->output.insertAfter(i, pr);
            if (mudlog.ofLevel(XMT)) {
               mudlog << "About to write:  -:" << pc->output << ":-" << endl;
            }
            ret = pc->output.Write(pc->descriptor, i + strlen(pr));
            if (mudlog.ofLevel(XMT)) {
               mudlog << "After write:  -:" << pc->output << ":-" << endl;
            }
         }//if
         else {
            ret = pc->output.Write(pc->descriptor, len);
         }
      }//if
      else {
         ret = pc->output.Write(pc->descriptor, len);
      }
   }//if
   return ret;
}//write_output



#if defined(SVR4) || defined(LINUX)

void nonblock(int s) {
   int  flags;

   mudlog.log(TRC, "in nonblock\n");

   flags = fcntl(s, F_GETFL);
   flags |= O_NONBLOCK;
   if (fcntl(s, F_SETFL, flags) < 0) {
      mudlog << "ERROR:  fcntl, executing nonblock:  " << strerror(errno) 
             << endl;
      do_shutdown = TRUE;
      exit(100);
   }
}//nonblock


#else

void nonblock(int s) {
   if (fcntl(s, F_SETFL, O_NDELAY) == -1) {
      mudlog << "ERROR:  fcntl (!LINUX), executing nonblock:  " 
             << strerror(errno) << endl;
      do_shutdown = TRUE;
      exit(100);
   }//if
}//nonblock

#endif



int critter::readInput()  {
   if (!pc)
      return -1;

   int ret;
   ret = pc->input.Read(pc->descriptor, MAX_INPUT_LEN);

   mudlog.log(TRC, "End of get_input, here it is:");
   mudlog.log(TRC, pc->input);

   return ret;  //valid input added to input buffer, can go parse it now.
}//get_input


void close_sockets(int mother) {
   Cell<critter*> cell;
   pc_list.head(cell);
   critter* crit_ptr;

   mudlog.log(DIS, "Closing all sockets now.\n");

   while ((crit_ptr = cell.next())) {
      if (crit_ptr->doLogOffActive() < 0)
         mudlog << "ERROR, close socket failed.\n";
   }

   close(mother);
}//close_sockets


// delete self after this!
int critter::doLogOffActive() {
   String buf(100);

   if (!isPc()) {
      mudlog.log(ERROR, "ERROR, doLoseLink on npc.\n");
      return -1;
   }

   if (mudlog.ofLevel(DBG)) {
      mudlog << "In doLoseLink, critter name:  " <<  *(getName())
             << "  room:  " << getCurRoomNum() << "  addr:  " << this
             << endl << flush;
      mudlog << " link condition:  " << pc->link_condition
             << endl;
   }

   if (pc->link_condition != CON_PLAYING) {
      mudlog.log(ERROR, "ERROR, doLoseLink, link condition !CON_PLAYING\n");
      return -1;
   }

   doGoLinkdead(); //first step

   doLogOffInactive();

   linkdead_list.loseData(this); //take em off the link dead list.

   return 1;
}//doLogOffActive



int critter::doLogOffInactive() {
   String buf(100);
   //remove from linkdead list in calling code!

   if (mudlog.ofLevel(DBG)) {
      mudlog << "doLogOffInactive, critter:  " << *(getName())
             << "  size of PETS:  " << PETS.size() << endl;
   }

   doUngroup(1, &NULL_STRING); //take em out of their group, if in it.

   Cell<critter*> pet_cll(PETS);
   critter* pet_ptr;
   while ((pet_ptr = pet_cll.next())) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "PETS:  Has a pet:  " <<  *(pet_ptr->getName())
                << "  ptr_address: " << pet_ptr << endl;
      }
   }//while

   List<critter*> tmp_list;
   tmp_list = PETS; //shallow copy

   tmp_list.head(pet_cll);
   while ((pet_ptr = pet_cll.next())) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "Tmp_List:  Has a pet:  " <<  *(pet_ptr->getName())
                << "  ptr_address: " << pet_ptr << endl;
      }
   }//while

   if (mudlog.ofLevel(DBG)) {
      mudlog << "  size of tmp_list:  " << tmp_list.size() << endl;
   }

   tmp_list.head(pet_cll);
   while ((pet_ptr = pet_cll.next())) {
      if (mudlog.ofLevel(DBG)) {
         mudlog << "Found a pet:  " <<  *(pet_ptr->getName())
                << endl << flush;
      }
      pet_ptr->show("Your master has left the game.\n");
      pet_ptr->doBecomeNonPet();
   }//while
   tmp_list.clear();

   if (PETS.size() > 0) {
      mudlog << "ERROR:  PETS.size() > 0 in doLogOffInactive:  "
             << PETS.size() << endl << flush;
      PETS.clear();
   }

   // Take care of their followers, if they still have any      
   tmp_list = FOLLOWERS;
   tmp_list.head(pet_cll);
   while ((pet_ptr = pet_cll.next())) {
      pet_ptr->doFollow(*pet_ptr, TRUE);
   }
   FOLLOWERS.clear();
   tmp_list.clear();

   if (pc->w_eye_obj) {
      pc->w_eye_obj->obj_proc->w_eye_owner = NULL;
      pc->w_eye_obj = NULL;
   }//if

   if (possessed_by) {
      possessed_by->unPossess();
      possessed_by = NULL;
   }

   if (SNOOPED_BY) {
      SNOOPED_BY->show("(SNOOPEE) has left the game.\n");
      SNOOPED_BY->SNOOPING = NULL;
   }

   doShowList(this, Selectors::instance().CC_gets_info_allow,
              Selectors::instance().CC_none, pc_list,
              CS_PLAYER_OFF_LD_LIST_INFO,
              getName());

   if (MASTER) {
      doBecomeNonPet();
   }
   
   emote("has left the game.");
   
   doLeaveRoom();
   
   recursive_init_unload(*this); //take eq out of circulation

   return 1;
}//doLogOffInActive


//very little changes, but lots of messages are sent.
// just close the descriptor if it's open, and set the vis
// bit accordingly.  Add to link_dead list.
int critter::doGoLinkdead() {
   String buf(100);

   if (!isPc()) {
      mudlog.log(ERROR, "ERROR, doGoLinkDead on npc.\n");
      return -1;
   }

   if (mudlog.ofLevel(DBG)) {
      mudlog << "In doGoLinkDead, critter name:  " <<  *(getName())
             << "  room:  " << getCurRoomNum() << "  addr:  " << this
             << endl << flush;
      mudlog << " link condition:  " << pc->link_condition
             << endl;
   }

   if (SNOOPED_BY) {
      Sprintf(buf, "%S (SNOOPEE) has lost link.\n", getName());
      buf.Cap();
      SNOOPED_BY->show(buf);
   }//if

   if (possessed_by) {
      possessed_by->unPossess();
      possessed_by = NULL;
   }

   doShowList(this, Selectors::instance().CC_gets_info_allow,
              Selectors::instance().CC_none, pc_list,
              CS_PLAYER_LOST_CON_INFO,
              getName());
   
   if (MASTER) {
      Sprintf(buf, "%S has lost link.\n", getName());
      buf.Cap();
      MASTER->show(buf);
   }//if

   emote("has lost link.");

   if (pc->descriptor != -1) {
      writeOutput();
      if (close(pc->descriptor) < 0) {
         mudlog << "ERROR: close_socket, close:  " << strerror(errno) 
                << endl;
      }//if
      else {
         sockets_connected--;
      }
      
      pc->descriptor = -1;
   }//if

   VIS_BIT |= 32;     
   linkdead_list.gainData(this);  //so one might re-login easily

   return 1;
}//doGoLinkDead


int critter::doLogOffNewLogin() {
   mudlog.log(TRC, "In CON_LOGGING_In\n");
   String buf(100);

   if (!isPc()) {
      mudlog.log(ERROR, "ERROR, doLogOffNewLogin on npc.\n");
      return -1;
   }

   if (mudlog.ofLevel(DBG)) {
      mudlog << "In doLogOffNewLogin, critter name:  " <<  *(getName())
             << "  room:  " << getCurRoomNum() << "  addr:  " << this
             << endl << flush;
      mudlog << " link condition:  " << pc->link_condition
             << endl;
   }

   if (pc->descriptor != -1) {
      writeOutput(); //try to flush the buffer if possible.
      if (close(pc->descriptor) < 0) {
         mudlog << "ERROR: log_off: close() " << strerror(errno) << endl;
      }//if
      else
         sockets_connected--;
   }//if
   return 1;
}//doLogOffNewLogin