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