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