WindowMud

Communication.cpp Source Code

Download this source code in text format (Communication.cpp)

/***********************************************************
* WindowMud - Window Mud Server                            *
* File:   Communication.cpp                                *
* Usage:  Winsock tcp/ip telnet player communications      *
*                                                          *
* No warranty is given or implied.                         *
* There is no license associated with this code.           *
* There are no restrictions on the use of this code.       *
* This code may be used in any way the reader wishes.      *
* No credit or credits are given and none are required.    *
* The reader may take this code and claim they wrote it.   *
************************************************************/

/***********************************************************
* Includes                                                 *
************************************************************/

#include "stdafx.h"         // precompiled headers
#include "Communication.h"
// Server
#include "Descriptor.h"
#include "Dnode.h"
#include "Log.h"
#include "Player.h"
#include "Utility.h"

/***********************************************************
* Globals                                                  *
************************************************************/

extern CString   ErrorMsg;
extern bool      StateConnections;
extern bool      StateRunning;
extern bool      StateStopping;
extern bool      WinSockCleanUp;

Dnode           *pDnodeActor;
Dnode           *pDnodeOthers;
Dnode           *pDnodeSrc;
Dnode           *pDnodeTgt;
CString          CmdStr;
fd_set           ExcSet;
fd_set           InpSet;
int              ListenSocket;
CString          MudCmd;
fd_set           OutSet;
CStringArray     ValidCmds;

/***********************************************************
 * Communication class constructor                         *
 ***********************************************************/

Communication::Communication()
{
}

/***********************************************************
 * Communication class destructor                          *
 ***********************************************************/

Communication::~Communication()
{
}

////////////////////////////////////////////////////////////
// Public functions static                                //
////////////////////////////////////////////////////////////

/***********************************************************
* Send output to all players                               *
************************************************************/

void Communication::SendToAll(CString PlayerMsg, CString AllMsg)
{
  Descriptor::SetpDnodeCursorFirst();
  while (!Descriptor::EndOfDnodeList())
  { // Loop thru all connections
    pDnodeOthers = Descriptor::GetDnode();
    if (pDnodeActor == pDnodeOthers)
    { // Send to player
      pDnodeActor->PlayerOut += PlayerMsg;
    }
    else
    { // Send to everyone else
      if (pDnodeOthers->PlayerStatePlaying)
      { // Target player is in playing state
        pDnodeOthers->PlayerOut += AllMsg;
        pDnodeOthers->pPlayer->CreatePrompt();
        pDnodeOthers->PlayerOut += pDnodeOthers->pPlayer->GetOutput();
      }
    }
    Descriptor::SetpDnodeCursorNext();
  }
  // Re-position pDnodeCursor
  RepositionDnodeCursor();
}

/***********************************************************
 * Check for new connections                               *
 ***********************************************************/

void Communication::SockCheckForNewConnections()
{
  int                   SocketCount;
  static struct timeval TimeOut;

  TimeOut.tv_sec = 0;
  TimeOut.tv_usec = 1;
  FD_ZERO(&InpSet);
  FD_ZERO(&OutSet);
  FD_ZERO(&ExcSet);
  FD_SET(ListenSocket, &InpSet);
  // Check status of connections
  Descriptor::SetpDnodeCursorFirst();
  while (!Descriptor::EndOfDnodeList())
  { // Loop thru all connections
    pDnodeActor = Descriptor::GetDnode();
    FD_SET(pDnodeActor->DnodeFd, &InpSet);
    FD_SET(pDnodeActor->DnodeFd, &OutSet);
    FD_SET(pDnodeActor->DnodeFd, &ExcSet);
    Descriptor::SetpDnodeCursorNext();
  }
  // Detect socket state (pg 159-161)
  SocketCount = select(-1, &InpSet, &OutSet, &ExcSet, &TimeOut);
  if (SocketCount == -1)
  { // Select failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication::SockCheckForNewConnections: select: " + ErrorMsg;
    Utility::BlowUp();
  }
  if (FD_ISSET(ListenSocket, &InpSet))
  { // Process new connection
    SockNewConnection();
  }
}

/***********************************************************
 * Close port                                              *
 ***********************************************************/

void Communication::SockClosePort(int Port)
{ 
  CString LogBuf;
  int     Result;

  // Close the socket (pg 70)
  Result = ::closesocket(ListenSocket);
  if (Result!= 0)
  { // Sock error on close
    ErrorMsg = "Communication::SockClosePort - Error: closesocket";
    Utility::BlowUp();
  }
  LogBuf.Format("%d", Port);
  LogBuf = "Closed port " + LogBuf;
  Log::LogIt(LogBuf);
  // Free WinSock resources
  WinSockCleanUp = false;
  Result = WSACleanup();
  if (Result!= 0)
  { // WinSock cleanup failed
    ErrorMsg = "Communication::SockClosePort - Error: WSACleanup";
    Utility::BlowUp();
  }
}

/***********************************************************
 * Open port                                               *
 ***********************************************************/

void Communication::SockOpenPort(int Port)
{
  unsigned long FionbioParm;
  struct        linger      ld;
  CString       LogBuf;
  int           OptionValue;
  int           Result;
  struct        sockaddr_in sa;
  WORD          VersionRequested;
  WSADATA       WsaData;

  FionbioParm       = 1;
  ld.l_onoff        = 0;
  ld.l_linger       = 0;
  OptionValue       = 1;
  VersionRequested  = MAKEWORD(1, 1);
  // Initialize WinSock API (pg 320)
  Result = WSAStartup(VersionRequested, &WsaData);
  if (Result != 0)
  { // WinSock is not available
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication::SockOpenPort - WinSock not available: " + ErrorMsg;
    Utility::BlowUp();
  }
  WinSockCleanUp = true;
  // Establish a streaming socket (pg 51)
  ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
  if (ListenSocket == SOCKET_ERROR)
  { // No socket, time to die
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication:SockOpenPort - Error: initializing socket: " + ErrorMsg;
    Utility::BlowUp();
  }
  // Enable reuse of local socket name (pg 305)
  Result = setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&OptionValue, sizeof(OptionValue));
  if (Result != 0)
  { // Set socket option failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication:SockOpenPort - Error: setsockopt: SOL_SOCKET SO_REUSEADDR" + ErrorMsg;
    Utility::BlowUp();
  }
  // Establish underlying TCP/IP buffer size (pg 305)
  Result = setsockopt(ListenSocket, SOL_SOCKET, SO_SNDBUF, (char *) &OptionValue, sizeof(OptionValue));
  if (Result != 0)
  { // Set socket option failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication:SockOpenPort - Error: setsockopt SNDBUF: " + ErrorMsg;
    Utility::BlowUp();
  }
  // Disable linger and set timeout to zero (pg 301)
  Result = setsockopt(ListenSocket, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
  if (Result != 0)
  { // Set socket option failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication:SockOpenPort - Error: setsockopt SO_LINGER: " + ErrorMsg;
    Utility::BlowUp();
  }
  // Initialize sockaddr structure (pg 53)
  memset((char *)&sa, 0, sizeof(sa));
  sa.sin_family       = AF_INET;
  sa.sin_addr.s_addr  = INADDR_ANY;
  sa.sin_port         = htons(Port);
  // Associate a local address with the socket (pg 56)
  Result = bind(ListenSocket, (struct sockaddr *)&sa, sizeof(sa));
  if (Result != 0)
  { // Bind socket failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication:SockOpenPort - Error: bind " + ErrorMsg;
    Utility::BlowUp();
  }
  // Make socket nonblocking (pg 286)
  Result = ioctlsocket(ListenSocket, FIONBIO, &FionbioParm);
  if (Result != 0)
  { // Failed to make socket nonblocking
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication:SockOpenPort ioctlsocket: " + ErrorMsg;
    Utility::BlowUp();
  }
  // Listen on port and limit pending connections (pg 60)
  Result = listen(ListenSocket, 20);
  if (Result != 0)
  { // Listen failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication:SockOpenPort - Error: listen " + ErrorMsg;
    Utility::BlowUp();
  }
  LogBuf.Format("%d", Port);
  LogBuf = "Listening on port " + LogBuf;
  Log::LogIt(LogBuf);
  // Load command array
  CommandArrayLoad();
}

/***********************************************************
 * Receive player input, check player status, send output  *
 ***********************************************************/

void Communication::SockRecv()
{
  int      ConnectionCount;
  int      DnodeFdSave;
  char     InpStr[MAX_INPUT_LENGTH];
  int      LineFeedPosition;
  CString  LogBuf;
  int      RecvByteCount;

  //************************
  //* Is Game is stopping? *
  //************************
  if (StateStopping)
  { // Game is stopping
    Descriptor::SetpDnodeCursorFirst();
    while (!Descriptor::EndOfDnodeList())
    { // Loop thru all connections
      pDnodeActor = Descriptor::GetDnode();
      if (!pDnodeActor->PlayerStateBye)
      { // If player is not already going Bye Bye
        pDnodeActor->PlayerStateBye = true;
        pDnodeActor->PlayerStateSendBanner = false;
        if (pDnodeActor->PlayerStatePlaying)
        { // Player is playing, but not any more
          pDnodeActor->PlayerStatePlaying = false;
          pDnodeActor->pPlayer->Save();
        }
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "Game is stopping ... Bye Bye!";
        pDnodeActor->PlayerOut += "\r\n";
        LogBuf  = pDnodeActor->PlayerName;
        LogBuf += " will be force disconnected";
        Log::LogIt(LogBuf);
      }
      Descriptor::SetpDnodeCursorNext();
    }
  }
  //***********************
  //* Service connections *
  //***********************
  Descriptor::SetpDnodeCursorFirst();
  while (!Descriptor::EndOfDnodeList())
  { // Loop thru all connections
    pDnodeActor = Descriptor::GetDnode();
    //***************************
    //* Check connection status *
    //***************************
    if (!pDnodeActor->PlayerStatePlaying)
    { // Player is logging on
      pDnodeActor->InputTick++;
      if (pDnodeActor->InputTick >= INPUT_TICK)
      { // No input, kick 'em out
        pDnodeActor->PlayerStateBye = true;
        LogBuf.Format("%d", pDnodeActor->DnodeFd);
        LogBuf = "Time out during logon on descriptor " + LogBuf;
        Log::LogIt(LogBuf);
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "No input ... closing connection";
      }
    }
    if (FD_ISSET(pDnodeActor->DnodeFd, &OutSet))
    { // This code don't do nothin', sample OutSet check
      int dummy = 0;
    }
    if (FD_ISSET(pDnodeActor->DnodeFd, &ExcSet))
    { // Kick out connections with exceptions
      pDnodeActor->PlayerStateBye = true;
      if (pDnodeActor->PlayerStatePlaying)
      { // Player is playing, but not any more
        pDnodeActor->PlayerStatePlaying = false;
        pDnodeActor->pPlayer->Save();
      }
    }
    else
    { // Good connection
      if (FD_ISSET(pDnodeActor->DnodeFd, &InpSet))
      { // Receive
        memset(InpStr, '\0', sizeof(InpStr));
        RecvByteCount = recv(pDnodeActor->DnodeFd, InpStr, MAX_INPUT_LENGTH-1, 0);
        if (RecvByteCount == 0)
        { // Should be input but there is none -- disconnected ??
          pDnodeActor->PlayerStateBye = true;
          if (pDnodeActor->PlayerStatePlaying)
          { // Player is playing, but not any more
            pDnodeActor->PlayerStatePlaying = false;
            pDnodeActor->pPlayer->Save();
          }
        }
        if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINTR)
        { // Nothing worth processing
         RecvByteCount = 0;
        }
        if (RecvByteCount > 0)
        { // Got something ... append it to player input
          pDnodeActor->PlayerInp += InpStr;
          pDnodeActor->InputTick = 0;
        }
      }
    }
    //*****************************
    //* Banner for new connection * 
    //*****************************
    if (pDnodeActor->PlayerStateSendBanner)
    { // New connection
      pDnodeActor->PlayerStateSendBanner = false;
      pDnodeActor->PlayerStateLoggingOn = true;
      pDnodeActor->PlayerStateWaitNewCharacter = true;
      // Send greeting  
      LogonGreeting();
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "Create a new character Y-N?";
      pDnodeActor->PlayerOut += "\r\n";
    }
    //**********************
    //* Send player output *
    //**********************
    if (pDnodeActor->PlayerOut.GetLength() > 0)
    { // Player has output, handle color codes
      // Color(); Add later
      // 'send' resquires a standard c string
      SockSend((LPCTSTR) pDnodeActor->PlayerOut);
    }
    //**********************
    //* Is player quiting? *
    //**********************
    if (pDnodeActor->PlayerStateBye)
    { // Player leaving the game ... disconnect them
      if (!pDnodeActor->PlayerStateReconnecting)
      { // Player is just quiting, not reconnecting
        delete pDnodeActor->pPlayer;
      }
      DnodeFdSave = pDnodeActor->DnodeFd;
      if (Descriptor::DeleteNode())
      { // When connection is deleted from list, log it
        LogBuf.Format("%d", DnodeFdSave);
        LogBuf = "Closed connection on descriptor " + LogBuf;
        Log::LogIt(LogBuf);
        ConnectionCount = Dnode::GetCount();
        if (ConnectionCount == 1)
        { // Connection count is one means no players are connected
          if (StateStopping)
          { // OMugs is stopping
            StateRunning = false;
          }
        }
      }
      // Skip to next dnode, this player's dnode has been deleted
      Descriptor::SetpDnodeCursorNext();
      continue;
    }
    //************************
    //* Process player input *
    //************************
    if (!StateStopping)
    { // Game is not shutting down
      LineFeedPosition = pDnodeActor->PlayerInp.FindOneOf("\r\n");
      if (LineFeedPosition > -1)
      { // Found a newline, parse the command
        if (pDnodeActor->PlayerName != "Admin Name Goes Here")
        { // Log player input
          LogBuf  = pDnodeActor->PlayerIpAddress;
          LogBuf += " ";
          LogBuf += pDnodeActor->PlayerInp;
          LogBuf.Replace("\r", " ");
          LogBuf.Replace("\n", " ");
          Log::LogIt(LogBuf);
        }
        CommandParse();
      }
    }
    // Get the next Dnode to process
    Descriptor::SetpDnodeCursorNext();
  }
}

////////////////////////////////////////////////////////////
// Private functions static                               //
////////////////////////////////////////////////////////////

/***********************************************************
 * Load command array                                      *
 ***********************************************************/

void Communication::CommandArrayLoad()
{
  CString    LogBuf;
  CString    Stuff;
  int        Success;
  CStdioFile ValidCmdsFile;
  CString    ValidCmdsFileName;

  ValidCmdsFileName =  VALID_CMDS_DIR;
  ValidCmdsFileName += "ValidCommands.txt";
  Success = ValidCmdsFile.Open(ValidCmdsFileName,
                    CFile::modeRead |
                    CFile::typeText);
  if(!Success)
  { // Open failed
    ErrorMsg = "Communication::CommandArrayLoad - Open Valid Commands file failed (read)";
    Utility::BlowUp();
  }
  ValidCmds.RemoveAll();
  ValidCmdsFile.ReadString(Stuff);
  while (Stuff != "")
  { // Read all commands
    ValidCmds.Add(Stuff);
    ValidCmdsFile.ReadString(Stuff);
  }
  ValidCmdsFile.Close();
  LogBuf = "Command array loaded";
  Log::LogIt(LogBuf);
}

/***********************************************************
 * Check command authorization, level, and validity        *
 ***********************************************************/

CString Communication::CommandCheck(CString MudCmdChk)
{
  CString CommandCheckResult;
  CString ValCmd;
  CString ValCmdInfo;
  CString WhoCanDo;
  int     x;
  int     y;

  CommandCheckResult = "Not Found";
  y = ValidCmds.GetUpperBound();
  for (x = 0; x <= y ; x++)
  { // For each string in the ValidCmds CStringArray
    ValCmdInfo = ValidCmds.GetAt(x);
    ValCmd     = Utility::GetWord(ValCmdInfo, 1);
    WhoCanDo   = Utility::GetWord(ValCmdInfo, 2);
    if (MudCmd == ValCmd)
    { // Found the command
      if (WhoCanDo == "all")
      { // Anyone can do this command
				CommandCheckResult = "Ok";
        break;
      }
    }
  }
  if (CommandCheckResult == "")
  { // This should never be true
    ErrorMsg = "Communication::CommandCheck - Broke!";
    Utility::BlowUp();
  }
  return CommandCheckResult;
}

/***********************************************************
 * Command parsing                                         *
 ***********************************************************/

void Communication::CommandParse()
{
  CString  BadCommandMsg;
  int      CmdStrLength;
  CString  CommandCheckResult;
  CString  LogBuf;
  CString  MudCmdChk;
  bool     MudCmdOk;
  int      PositionOfNewline;
  int      RandomNumber;

  //**************************
  // Get next command string *
  //**************************
  CmdStr = pDnodeActor->PlayerInp;
  CmdStrLength = CmdStr.GetLength();
  PositionOfNewline = CmdStr.FindOneOf("\r\n");
  if (PositionOfNewline < 0)
  { // No newline found, skip out
    return;
  }
  CmdStr = CmdStr.Left(PositionOfNewline);
  pDnodeActor->PlayerInp = pDnodeActor->PlayerInp.Right(CmdStrLength - PositionOfNewline);
  pDnodeActor->PlayerInp.TrimLeft();
  if (CmdStr == "")
  { // Player hit enter without typing anything
    if (!pDnodeActor->PlayerStateLoggingOn)
    { // Player is not logging on
      pDnodeActor->pPlayer->CreatePrompt();
      pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput(); 
      return;
    }
  }
  //***************
  // Player logon *
  //***************
  if (pDnodeActor->PlayerStateLoggingOn)
  { // Player just connected and needs to logon
    DoLogon();
    return;
  }
  //*************
  // Get MudCmd *
  //*************
  MudCmd = Utility::GetWord(CmdStr, 1);
  MudCmd.MakeLower();
  //****************
  // Check command *
  //****************
  MudCmdOk = false;
  MudCmdChk = MudCmd;
  CommandCheckResult = CommandCheck(MudCmdChk);
  if (CommandCheckResult == "Ok")
  { // Mud command is Ok for this player
    MudCmdOk = true;
  }
  else
  if (Utility::GetWord(CommandCheckResult, 1) == "Level")
  { // Level restriction on command
    pDnodeActor->PlayerOut += "You must attain level ";
    pDnodeActor->PlayerOut += Utility::GetWord(CommandCheckResult, 2);
    pDnodeActor->PlayerOut += " before you can use that command.";
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->pPlayer->CreatePrompt();
    pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
    return; 
  }
  //**************
  // Bad command *
  //**************
  if (!MudCmdOk)
  { // Not a valid cmd and it is not a social
    RandomNumber = Utility::GetRandomNumber(5);
    switch(RandomNumber) 
    {
      case 1:
        BadCommandMsg = "How's that?";
        break;
      case 2:
        BadCommandMsg = "You try to give a command, but fail.";
        break;
      case 3:
        BadCommandMsg = "Hmmm, making up commands?";
        break;
      case 4:
        BadCommandMsg = "Ehh, what's that again?";
        break;
      case 5:
        BadCommandMsg = "Feeling creative?";
        break;
      default :
        BadCommandMsg = "Your command is not clear.";
    }
    pDnodeActor->PlayerOut += BadCommandMsg;
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->pPlayer->CreatePrompt();
    pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
    return;
  }

  //**********************
  //* Process the MudCmd *
  //**********************

  /* CHAT command */
  if (MudCmd == "chat")
  {
    DoChat();
    return;
  }

  pDnodeActor->PlayerOut += "Command is valid, but not implemented at this time.";
  pDnodeActor->PlayerOut += "\r\n";
  pDnodeActor->pPlayer->CreatePrompt();
  pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
  // Log it
  MudCmd  = Utility::MakeFirstUpper(MudCmd);
  LogBuf  = MudCmd;
  LogBuf += " is in command array, but Do";
  LogBuf += MudCmd;
  LogBuf += " is not coded.";
  Log::LogIt(LogBuf);
}

/***********************************************************
 * Chat command                                            *
 ***********************************************************/

void Communication::DoChat()
{
  CString AllMsg;
  CString ChatMsg;
  CString PlayerMsg;

  //********************
  //* Validate command *
  //********************
  ChatMsg = Utility::GetWords(CmdStr, 2);
  if (ChatMsg.GetLength() < 1)
  { // Player did not enter any chat
    pDnodeActor->PlayerOut += "You start to chat, but, about what?";
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->pPlayer->CreatePrompt();
    pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
    return;
  }
  //*************
  //* Chat away *
  //*************
  PlayerMsg += "You Chat: ";
  PlayerMsg += ChatMsg;
  PlayerMsg += "\r\n";
  AllMsg  = "\r\n";
  AllMsg += pDnodeActor->PlayerName;
  AllMsg += " chats: ";
  AllMsg += ChatMsg;
  AllMsg += "\r\n";
  SendToAll(PlayerMsg, AllMsg);
  pDnodeActor->pPlayer->CreatePrompt();
  pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
}

/***********************************************************
 * Logon                                                   *
 ***********************************************************/

void Communication::DoLogon()
{
  if (pDnodeActor->PlayerStateWaitNewCharacter)
  { // New character 'y-n' prompt
    pDnodeActor->PlayerStateWaitNewCharacter = false;
    LogonWaitNewCharacter();
    return;
  }
  if (pDnodeActor->PlayerStateWaitName)
  { // Name prompt
    pDnodeActor->PlayerStateWaitName = false;
    LogonWaitName();
    return;
  }
  if (pDnodeActor->PlayerStateWaitNameConfirmation)
  { // Name confirmation prompt
    pDnodeActor->PlayerStateWaitNameConfirmation = false;
    LogonWaitNameConfirmation();
    return;
  }
  if (pDnodeActor->PlayerStateWaitPassword)
  { // Password prompt
    pDnodeActor->PlayerStateWaitPassword = false;
    LogonWaitPassword(); 
    return;
  }
  if (pDnodeActor->PlayerStateWaitMaleFemale)
  { // Sex prompt
    pDnodeActor->PlayerStateWaitMaleFemale = false;
    LogonWaitMaleFemale();
    return;
  }
}

/***********************************************************
 * Logon greeting                                          *
 ***********************************************************/

void Communication::LogonGreeting()
{
  CStdioFile GreetingFile;
  CString    GreetingFileName;
  CString    Stuff;
  int        Success;

  // Read greeting file
  GreetingFileName = GREETING_DIR;
  GreetingFileName += "Greeting";
  GreetingFileName += ".txt";
  Success = GreetingFile.Open(GreetingFileName,
                   CFile::modeRead |
                   CFile::typeText);
  if(!Success)
  {
    ErrorMsg = "Communication::LogonGreeting - Open Greeting file failed (read)";
    Utility::BlowUp();
  }
  GreetingFile.ReadString(Stuff);
  while (Stuff != "End of Greeting")
  { // Read all
    Stuff += "\r\n";
    pDnodeActor->PlayerOut += Stuff;
    GreetingFile.ReadString(Stuff);
  }
  GreetingFile.Close();
}

/***********************************************************
 * Logon wait male female                                  *
 ***********************************************************/

void Communication::LogonWaitMaleFemale()
{
  CString  AllMsg;
  CString  LogBuf;
  CString  PlayerMsg;

  CmdStr.MakeUpper();
  if (!(CmdStr.FindOneOf("MF") == 0 && CmdStr.GetLength() == 1))
  { // Not M or F
    pDnodeActor->PlayerStateWaitMaleFemale = true;
    pDnodeActor->PlayerOut += "You must enter a M or F.";
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "Sex of this character M-F?";
    pDnodeActor->PlayerOut += "\r\n";
  }
  else
  { // M or F entered, save them, let them play
    pDnodeActor->pPlayer = new Player();
    pDnodeActor->pPlayer->Name = pDnodeActor->PlayerName;
    pDnodeActor->pPlayer->Password = pDnodeActor->PlayerPassword;
    pDnodeActor->pPlayer->Sex = CmdStr;
    pDnodeActor->PlayerStateLoggingOn = false;
    pDnodeActor->PlayerStatePlaying = true;
    pDnodeActor->pPlayer->Save();
    PlayerMsg  = "\r\n";
    PlayerMsg += "May your travels be safe.";
    PlayerMsg += "\r\n";
    PlayerMsg += "\r\n";
    AllMsg  = "\r\n";
    AllMsg += "Please welcome new player ";
    AllMsg += pDnodeActor->PlayerName;
    AllMsg += ".";
    AllMsg += "\r\n";
    SendToAll(PlayerMsg, AllMsg);
    pDnodeActor->pPlayer->CreatePrompt();
    pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
    LogBuf  = "New player ";
    LogBuf += pDnodeActor->PlayerName;
    Log::LogIt(LogBuf);
    pDnodeActor->pPlayer->Save();
  }
}

/***********************************************************
 * Logon wait name                                         *
 ***********************************************************/

void Communication::LogonWaitName()
{
  // Fix name so first letter is upper case, rest are lower case
  pDnodeActor->PlayerName = Utility::MakeFirstUpper(CmdStr);
  if (pDnodeActor->PlayerNewCharacter == "Y")
  { // New player
    pDnodeActor->PlayerStateWaitNameConfirmation = true;
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "You wish to be known as ";
    pDnodeActor->PlayerOut += pDnodeActor->PlayerName;
    pDnodeActor->PlayerOut += "? Y-N";
    pDnodeActor->PlayerOut += "\r\n";
  }
  else
  { // Returning player
    if (!Player::IsPlayer(pDnodeActor->PlayerName))
    { // Name not found on file
      pDnodeActor->PlayerStateWaitName = true;
      pDnodeActor->PlayerOut += pDnodeActor->PlayerName;
      pDnodeActor->PlayerOut += " is not a citizen of this realm.";
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "Name?";
      pDnodeActor->PlayerOut += "\r\n";
    }
    else
    { // Name found on file
      pDnodeActor->PlayerStateWaitPassword = true;
      if (pDnodeActor->PlayerStateWaitPassword)
      { // Prompt for password
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "Password?";
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->pPlayer = new Player(); 
        pDnodeActor->pPlayer->Name = pDnodeActor->PlayerName;
        pDnodeActor->pPlayer->ParsePlayerStuff();
        pDnodeActor->PlayerPassword = pDnodeActor->pPlayer->Password;
      }
    }
  }
}

/***********************************************************
 * Logon wait name confirmation                            *
 ***********************************************************/

void Communication::LogonWaitNameConfirmation()
{
  CmdStr.MakeUpper();
  if (!(CmdStr.FindOneOf("YN") == 0 && CmdStr.GetLength() == 1))
  { // Not Y or N ... try again
    pDnodeActor->PlayerStateWaitNameConfirmation = true;
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "You must enter a Y or N.";
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "You wish to be known as ";
    pDnodeActor->PlayerOut += pDnodeActor->PlayerName;
    pDnodeActor->PlayerOut += "? Y-N";
    pDnodeActor->PlayerOut += "\r\n";
  }
  else
  { // Y or N entered
    if (CmdStr == "N")
    { // N ... changed their mind ... try again
      pDnodeActor->PlayerStateWaitName = true;
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "Ok then, Try again.";
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "Name?";
      pDnodeActor->PlayerOut += "\r\n";
    }
    else
    { // Y entered ... they like the name
      if (!Player::IsNameValid(pDnodeActor->PlayerName))
      { // Name is invalid ... try again
        pDnodeActor->PlayerStateWaitName = true;
        pDnodeActor->PlayerOut += pDnodeActor->PlayerName;
        pDnodeActor->PlayerOut += " is not an acceptable name in this realm\r\n";
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "Name?";
        pDnodeActor->PlayerOut += "\r\n";
      }
      else
      { // Name is valid
        if (Player::IsPlayer(pDnodeActor->PlayerName))
        { // Name aleady used ... try again
          pDnodeActor->PlayerStateWaitName = true;
          pDnodeActor->PlayerOut += pDnodeActor->PlayerName;
          pDnodeActor->PlayerOut += " belongs to an exiting character";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "Try a different name";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "Name?";
          pDnodeActor->PlayerOut += "\r\n";
        }
        else
        { // New player with valid name that has not been used
          pDnodeActor->PlayerStateWaitPassword = true;
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "Password?";
          pDnodeActor->PlayerOut += "\r\n";
        }
      }
    }
  }
}

/***********************************************************
 * Logon wait new character                                *
 ***********************************************************/

void Communication::LogonWaitNewCharacter()
{
  CmdStr.MakeUpper();
  if (!(CmdStr.FindOneOf("YN") == 0 && CmdStr.GetLength() == 1))
  { // Not Y or N ... try again
    pDnodeActor->PlayerStateWaitNewCharacter = true;
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "You must enter a Y or N.";
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "Create a new character Y-N?";
    pDnodeActor->PlayerOut += "\r\n";
  }
  else
  { // Y or N entered ... store response for use later
    pDnodeActor->PlayerStateWaitName = true;
    pDnodeActor->PlayerNewCharacter = CmdStr;
    pDnodeActor->PlayerOut += "\r\n";
    pDnodeActor->PlayerOut += "Name?";
    pDnodeActor->PlayerOut += "\r\n";
  }
}

/***********************************************************
 * Logon wait password                                     *
 ***********************************************************/

void Communication::LogonWaitPassword()
{
  CString  AllMsg;
  CString  LogBuf;
  CString  PlayerMsg;

  if (pDnodeActor->PlayerPassword == CmdStr)
  { // Password matches
    if (pDnodeActor->PlayerNewCharacter == "Y")
    { // Password matches and a new player, get sex
      pDnodeActor->PlayerStateWaitMaleFemale = true;
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut +=  pDnodeActor->PlayerName;
      pDnodeActor->PlayerOut += ", remember your password.";
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "You must know it log in again.";
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "\r\n";
      pDnodeActor->PlayerOut += "Sex of this character M-F?";
      pDnodeActor->PlayerOut += "\r\n";
    }
    else
    { // Password matches and returning player, let them play
      // Reconnecting?
      Descriptor::SetpDnodeCursorFirst();
      while (!Descriptor::EndOfDnodeList())
      { // Loop thru all connections
        pDnodeOthers = Descriptor::GetDnode();
        if (pDnodeActor != pDnodeOthers)
        { // Check other connections
          if (pDnodeActor->PlayerName == pDnodeOthers->PlayerName)
          { // Reconnect character 
            pDnodeActor->PlayerStateLoggingOn = false;
            pDnodeActor->PlayerStatePlaying = true;
            pDnodeActor->pPlayer = pDnodeOthers->pPlayer;
            pDnodeActor->PlayerOut += "\r\n";
            pDnodeActor->PlayerOut += "You take control of ";
            pDnodeActor->PlayerOut += pDnodeOthers->PlayerName;
            pDnodeActor->PlayerOut += ".";
            pDnodeActor->PlayerOut += "\r\n";
            pDnodeActor->PlayerOut += "Reconnection successful.";
            pDnodeActor->PlayerOut += "\r\n";
            pDnodeActor->pPlayer->CreatePrompt();
            pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
            LogBuf  = "Reconnecting player ";
            LogBuf += pDnodeActor->PlayerName;
            Log::LogIt(LogBuf);
            pDnodeActor->pPlayer->Save();
            // Clean up old connection
            pDnodeOthers->PlayerStateBye = true;
            pDnodeOthers->PlayerStateReconnecting = true;
            pDnodeOthers->PlayerOut += "\r\n";
            pDnodeOthers->PlayerOut += "Disconnecting - multiple login dectected";
            pDnodeOthers->PlayerOut += "\r\n ";
          }
        }
        Descriptor::SetpDnodeCursorNext();
      }
      // Re-position pDnodeCursor
      RepositionDnodeCursor();
      if (pDnodeActor->PlayerStateLoggingOn)
      { // Not reconnecting
        pDnodeActor->PlayerStateLoggingOn = false;
        pDnodeActor->PlayerStatePlaying = true;
        PlayerMsg  = "\r\n";
        PlayerMsg += "May your travels be safe.";
        PlayerMsg += "\r\n";
        PlayerMsg += "\r\n";
        AllMsg  = "\r\n";
        AllMsg += pDnodeActor->PlayerName;
        AllMsg += " has entered the game.";
        AllMsg += "\r\n";
        SendToAll(PlayerMsg, AllMsg);
        pDnodeActor->pPlayer->CreatePrompt();
        pDnodeActor->PlayerOut += pDnodeActor->pPlayer->GetOutput();
        LogBuf  = "Returning player ";
        LogBuf += pDnodeActor->PlayerName;
        Log::LogIt(LogBuf);
        pDnodeActor->pPlayer->Save();
      }
    }
  }
  else
  { // Password does not match
    if (pDnodeActor->PlayerNewCharacter == "Y")
    { // New player
      pDnodeActor->PlayerWrongPasswordCount++;
      pDnodeActor->PlayerStateWaitPassword = true;
      if (pDnodeActor->PlayerWrongPasswordCount == 1)
      { // First password entered
        pDnodeActor->PlayerPassword = CmdStr;
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "Retype Password:";
        pDnodeActor->PlayerOut += "\r\n";
      }
      else
      {
        if (pDnodeActor->PlayerWrongPasswordCount < 4)
        { // Can't seem to type the same password ... doh!
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "Retyped Password does not match";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "Try again";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "Password?";
          pDnodeActor->PlayerOut += "\r\n";
        }
        else
        { // Mis-matched password entered 3 times, boot them off!
          pDnodeActor->PlayerStateBye = true;
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "Wrong password entered 3 times";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "\r\n";
          pDnodeActor->PlayerOut += "You have been disconnected.";
          pDnodeActor->PlayerOut += "\r\n";
        }
      }
    }
    else
    { // Returning player
      pDnodeActor->PlayerWrongPasswordCount++;
      if (pDnodeActor->PlayerWrongPasswordCount < 3)
      { // Tries < 3
        pDnodeActor->PlayerStateWaitPassword = true;
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "Wrong password.";
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "Password?";
        pDnodeActor->PlayerOut += "\r\n";
      }
      else
      { // Wrong password entered 3 times, log it, boot them off!
        pDnodeActor->PlayerStateBye = true;
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "Wrong password entered 3 times";
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "\r\n";
        pDnodeActor->PlayerOut += "You have been disconnected";
        pDnodeActor->PlayerOut += "\r\n";
        LogBuf = "Password failure for ";
        LogBuf += pDnodeActor->PlayerName;
        Log::LogIt(LogBuf);
      }
    }
  }
}

/***********************************************************
 * Reposition Dnode cursor                                 *
 ***********************************************************/

void Communication::RepositionDnodeCursor()
{
  // Dnode cursor must be repositioned after each 'looping thru all connections'
  Descriptor::SetpDnodeCursorFirst();
  while (!Descriptor::EndOfDnodeList())
  { // Loop thru all connections
    pDnodeOthers = Descriptor::GetDnode();
    if (pDnodeActor == pDnodeOthers)
    { // pDnodeCursor is now correctly positioned
      break;
    }
    Descriptor::SetpDnodeCursorNext();
  }
}

/***********************************************************
 * New connection                                          *
 ***********************************************************/

void Communication::SockNewConnection()
{
  unsigned long       FionbioParm;
  CString             LogBuf;
  int                 Result;
  struct sockaddr_in  Sock;
  int                 SocketHandle;
  int                 SocketSize;
  CString             IpAddress;
  CString             TmpStr;
    
  FionbioParm = 1;
  SocketSize  = sizeof(Sock);
  // Return a new socket for a newly created connection (pg 63)
  SocketHandle = accept(ListenSocket, (struct sockaddr *)&Sock, &SocketSize);
  if (!SocketHandle)
  { // Accept failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication::SockNewConnection - Error: accept: " + ErrorMsg;
    Utility::BlowUp();
  }
  IpAddress = inet_ntoa(Sock.sin_addr);
  // Make socket nonblocking (pg 286)
  Result = ioctlsocket(SocketHandle, FIONBIO, &FionbioParm);
  if (Result != 0)
  { // Failed to make socket nonblocking
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication::SockNewConnection - Error: ioctlsocket " + ErrorMsg;
    Utility::BlowUp();
  }
  TmpStr.Format("%d", SocketHandle);
  LogBuf  = "New connection with socket handle ";
  LogBuf += TmpStr;
  LogBuf += " and address ";
  LogBuf += IpAddress;
  Log::LogIt(LogBuf);
  pDnodeActor = new Dnode(SocketHandle, IpAddress);
  Descriptor::AppendIt();
  StateConnections = true;
}

/***********************************************************
 * Send message                                            *
 ***********************************************************/

void Communication::SockSend(const char *SendBuffer)
{
  int Length;
  int Written;

  if (!SendBuffer || SendBuffer[0] == '\0')
  { // Nothing to send
    return;
  }
  Length = strlen(SendBuffer);
  Written = send(pDnodeActor->DnodeFd, SendBuffer, Length, 0);
  if (Written == SOCKET_ERROR)
  { // Send failed
    ErrorMsg.Format("%s", strerror(errno));
    ErrorMsg = "Communication::SockSend - Error: send: " + ErrorMsg;
    Utility::BlowUp();
  } 
  if (Written == Length)
  { // Everything was sent
    pDnodeActor->PlayerOut = "";
  }
  else
  { // Some was not sent
    pDnodeActor->PlayerOut = pDnodeActor->PlayerOut.Right(Length-Written);
  }
}