/* * This file is part of UAS2. * * UAS2 is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * UAS2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with UASv1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * @file CharacterServer.cpp * Handles the CharacterServer. * * CharacterServer handles interactions with clients outside of the server game world. * Actions include account verification, providing the character list, the creation and deletion * of characters, and the connection and disconnection of clients. */ #include #include "Client.h" #include "CharacterServer.h" #include "MasterServer.h" #include "RecvPacket.h" #include "VersionNo.h" // Initialize static members //================================================ DWORD cCharacterServer::m_dwSendCRCSeed; DWORD cCharacterServer::m_dwRecvCRCSeed; WORD cCharacterServer::m_wLogicalID; SOCKET cCharacterServer::m_Socket; DWORD cCharacterServer::m_dwClientCount; //DWORD cCharacterServer::ReturnPadOffset( DWORD dwLength ); DWORD dwClientCount; //================================================ static BYTE PacketTwo[122] = { // Nov Patch 0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x67, 0x4A, 0x83, 0x6A, 0x0B, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00,0x00, //0x01, 0x00, 0x52, 0x5B, 0x95, 0x5B, 0xC9, 0x9A, 0x97, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x0C, 0x00, // Byte 41 0x00, 0x00, 0x32, 0x30, 0x30, 0x34, 0x2E, 0x30, 0x31, 0x2E, 0x30, 0x30, 0x31, 0x00, 0x00, 0x20, 0x00, 0x20, // 58 bytes // Keep below from Dec Client 0x3C, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x33, 0x08, 0x00, 0x00, 0x00, 0xFB, 0x63, 0xC5, 0x08, 0x16, 0xC7, 0x80, 0x31, 0xFF, 0x43, 0xE5, 0xFE, 0x8A, 0xF3, 0xE3, 0x69, 0x23, 0xCF, 0xF8, 0x82, 0xB9, 0xC4, 0x59, 0x80, 0x99, 0x30, 0xB6, 0x08, 0xE5, 0xC2, 0x80, 0xDD, 0xBB, 0xAD, 0xCD, 0xAD, 0x03, 0x00, 0x00, 0x00, 0xF8, 0xCA, 0xEF, 0x74, 0x54, 0x9E, 0xFC, 0x4B, 0x85, 0x48, 0xDF, 0xBB, // 122 bytes }; static BYTE PacketThree[250] = { 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0xD7, 0xA2, 0xCD, 0x6B, 0x0B, 0x00, 0x00, 0x00, //(____@______k____) 0xE6, 0x00, 0x00, 0x00, 0xE3, 0x5B, 0x4C, 0x6F, 0xE6, 0xD4, 0x8F, 0x41, 0x00, 0x00, 0x00, 0x00, //(_____[Lo___A____) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(________________) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //(__________) }; static BYTE UserName[44] = { // This is the tail of the Char list used in the June+ Clients 0xEF,0xBE,0xEF,0xBE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00, }; static char szClientVersion[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static char szBadClientVersion[12] = { 0x32, 0x30, 0x30, 0x34, 0x2E, 0x30, 0x31, 0x2E, 0x30, 0x30, 0x31, 0x00 }; static byte Invalid2[24] = { 0x19, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x5F, 0x72, 0x6B, 0xBD, 0x15, 0x00, 0x7A, 0x02, 0x04, 0x00, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, }; std::string aMonth[12] = { "January","February","March","April","May","June","July","August","September","October","November","December"}; /** * Processes Character Server packets * * This function is called when the client sends a general Character Server message. * These encompass the actions performed by a client when outside of the server game world. */ void cClient::ProcessPacket_CS( cRecvPacket *pcRecvPacket ) { m_dwLastRecvTime = timeGetTime( ); switch ( pcRecvPacket->GetFlags( ) ) { //Client logout case 0x00000020: { cMasterServer::DisconnectClient( this ); if(cMasterServer::m_dwNumUsers > 0) { cMasterServer::m_dwNumUsers--; } return ; } case 0x000000004: { DWORD dwLastGoodSeq; m_PacketPipe_CS.m_wTime = pcRecvPacket->GetTime( ); pcRecvPacket->CopyPayload( 0, &dwLastGoodSeq, 4 ); m_PacketPipe_CS.ClearSentPackets( dwLastGoodSeq ); return ; } case 0x00100000: case 0x00200200: case 0x00000200: { m_PacketPipe_CS.m_dwRecvSequence = pcRecvPacket->GetSequence( ); m_PacketPipe_CS.m_wTime = pcRecvPacket->GetTime( ); if ( pcRecvPacket->GetFlags( ) == 0x00100000 ) { m_PacketPipe_CS.ReturnPing( m_saSockAddr ); return ; } break; // case 0x00100000, case 0x00200200, and case 0x00000200 } } #ifdef _DEBUG char szPacketA[100]; sprintf( szPacketA, "Character Server Packet: %08x ",pcRecvPacket->GetFlags( )); cMasterServer::WriteToFile(szPacketA); //cMasterServer::ServerMessage( ColorGreen,this,(char *)szPacketA); #endif switch ( m_bCharState ) { case 1: { if ( pcRecvPacket->GetFlags( ) == 0x00000000 ) { m_bCharState = 2; bool bClientOk = false; CopyMemory( &szClientVersion[0], pcRecvPacket->GetPayload( 06 ), 12 ); // Connecting client version //If the client is of the correct version if((szClientVersion[0] == 0x32)&&(szClientVersion[1] == 0x30)&&(szClientVersion[2] == 0x30)&&(szClientVersion[3] == 0x34)) { bClientOk = true; } char szLoginInfo[45]; char szPasswordInfo[45]; WORD wLength; CopyMemory(&wLength, pcRecvPacket->GetPayload(32),2); if(szClientVersion[3] == '4') { // <-- Post 012704 Clients CopyMemory( szLoginInfo, pcRecvPacket->GetPayload( 34 ), wLength ); } else if(szClientVersion[0] == '5') { // Sentinel Client CopyMemory( szLoginInfo, pcRecvPacket->GetPayload( 76 ), 44 ); } else { //<-- Pre 012704 Clients CopyMemory( szLoginInfo, pcRecvPacket->GetPayload( 74 ), 44 ); // Dec-> (80), 44); June-> (74), 44); } szLoginInfo[wLength] = '\0'; char* pszSep = strchr(szLoginInfo, (int)':'); char szIP[32]; cMasterServer::FormatIP_Port( m_saSockAddr, szIP ); //Display console text information for a client login attempt m_wAccountNameLength = pszSep-szLoginInfo; CopyMemory( m_szAccountName, szLoginInfo,m_wAccountNameLength ); m_szAccountName [m_wAccountNameLength] = '\0'; UpdateConsole( " Client login attempt:\r\n" " User: %s\r\n" " IP: %s\r\n", m_szAccountName,szIP); //If no password is provided if(pszSep == NULL) { UpdateConsole( " Invalid login information from IP %s.\r\n ", szIP ); cClient::Hash_Remove( this ); break; } else { *pszSep = '\0'; ++pszSep; CopyMemory( &UserName[0], szLoginInfo, sizeof(szLoginInfo) ); // Added for June Client cMasterServer::m_dwNumUsers++; } if( bClientOk == true) { CopyMemory( szPasswordInfo, pszSep,wLength ); if ( !cDatabase::VerifyAccount( szLoginInfo, szPasswordInfo, m_dwAccountID, TRUE )) { UpdateConsole( " No matching account found.\r\n" ); bClientOk = false; //cClient::Hash_Remove( this ); //cMasterServer::DeleteClient( m_saSockAddr ); //break; // case 1 } else { UpdateConsole( " Account verified.\r\n" ); } } else { UpdateConsole( " Invalid client connection attempt.\r\n" ); } m_dwLastRecvTime = 0; //If the client is of the correct version if(bClientOk == true) { CopyMemory( &PacketTwo[42],szClientVersion,12); m_PacketPipe_CS.CalcCRC( PacketTwo, 102 ); sendto( m_PacketPipe_CS.m_Socket, (char *)PacketTwo, sizeof( PacketTwo ), NULL, (SOCKADDR *)&m_saSockAddr, sizeof( SOCKADDR ) ); } else { CopyMemory( &PacketTwo[42],szBadClientVersion,12); m_PacketPipe_CS.CalcCRC( PacketTwo, 102 ); sendto( m_PacketPipe_CS.m_Socket, (char *)PacketTwo, sizeof( PacketTwo ), NULL, (SOCKADDR *)&m_saSockAddr, sizeof( SOCKADDR ) ); cClient::Hash_Remove( this ); } } break; //case 1 } case 2: { if ( pcRecvPacket->GetFlags( ) == 0x00000080 ) { m_bCharState = 3; m_PacketPipe_CS.CalcCRC( PacketThree, 230 ); for ( int i = 0; i < 10; ++i ) sendto( m_PacketPipe_CS.m_Socket, (char *)PacketThree, sizeof( PacketThree ), NULL, (SOCKADDR *)&m_saSockAddr, sizeof( SOCKADDR ) ); } break; //case 2 } case 3: { if ( pcRecvPacket->GetFlags( ) == 0x00000040 ) { m_bCharState = 4; SendPacketF7B8( ); cDatabase::LoadAvatarList( m_dwAccountID, m_AvatarList ); SendAvatarList( ); SendMOTD( ); } break; //case 3 } case 4: { if ( pcRecvPacket->GetFlags( ) & 0x00000200 ) { DWORD dwMsgId; pcRecvPacket->CopyPayload( 16, &dwMsgId, sizeof( DWORD ) ); switch(dwMsgId) { case 0xF7C8L: SendPacketF7C7( ); break; //Avatar deletion case 0xF655: { //AvatarDeleteMessage *adm = (AvatarDeleteMessage*)pcRecvPacket->GetPayload( 16 ); AvatarDeleteMessage adm; pcRecvPacket->CopyPayload(16, &adm, 6 ); pcRecvPacket->CopyPayload( 16 + 6, adm.szAccountName, adm.wNameLength + 1); pcRecvPacket->CopyPayload( 16 + 6 + adm.wNameLength + cCharacterServer::ReturnPadOffset(adm.wNameLength), &adm.dwSlot, 8 ); cDatabase::DeleteAvatar( (m_AvatarList.begin() + adm.dwSlot)->m_dwGUID ); m_AvatarList.erase( m_AvatarList.begin() + adm.dwSlot ); AddPacket( CHAR_SERVER, (unsigned char*)&dwMsgId, sizeof( dwMsgId ), 4 ); SendDeleteAck( ); SendAvatarList( ); break; } //Character creation case 0xF656L: { CreateCharacterMessage ccm; pcRecvPacket->CopyPayload( 16, &ccm, 6 ); //ccm.szAccountName = new char[ccm.wLengthOfAccountName + 1]; pcRecvPacket->CopyPayload( 16 + 6, ccm.szAccountName, ccm.wLengthOfAccountName + 1); pcRecvPacket->CopyPayload( 16 + 6 + ccm.wLengthOfAccountName + cCharacterServer::ReturnPadOffset(ccm.wLengthOfAccountName), &ccm.dwValOne, 152 ); pcRecvPacket->CopyPayload( 16 + 6 + ccm.wLengthOfAccountName + cCharacterServer::ReturnPadOffset(ccm.wLengthOfAccountName) + 152, &ccm.dwNumSkills, 4 ); pcRecvPacket->CopyPayload( 16 + 6 + ccm.wLengthOfAccountName + cCharacterServer::ReturnPadOffset(ccm.wLengthOfAccountName) + 152 + 4, &ccm.dwSkillStatus, 4 * ccm.dwNumSkills ); pcRecvPacket->CopyPayload( 16 + 6 + ccm.wLengthOfAccountName + cCharacterServer::ReturnPadOffset(ccm.wLengthOfAccountName) + 152 + 4 + (4 * ccm.dwNumSkills), &ccm.wNameLength, 2 ); pcRecvPacket->CopyPayload( 16 + 6 + ccm.wLengthOfAccountName + cCharacterServer::ReturnPadOffset(ccm.wLengthOfAccountName) + 152 + 4 + (4 * ccm.dwNumSkills) + 2, ccm.szName, ccm.wNameLength); ccm.szName[ccm.wNameLength] = '\0'; //fix the name from name padding DWORD Test; Test = cCharacterServer::ReturnPadOffset(ccm.wLengthOfAccountName); //Cubem0j0: Test to see what is returned for character name as this does not appear to work. FILE* pFile = cMasterServer::pFile; pFile = fopen( "server.log", "wa" ); setbuf(pFile,NULL); cMasterServer::WriteToFile(ccm.szName); //End test pcRecvPacket->CopyPayload( ((cFragmentHeader*)(pcRecvPacket->m_bData+20))->m_wFragmentLength - 16, &ccm.dwUnk4, 16); cAvatarList cAL; if( cDatabase::CreateAvatar( m_dwAccountID, ccm, cAL.m_dwGUID ) ) //Create avatar { cMessage cMsg; cMsg << 0xF7B0L << 0L << 0L << 0xF643L << 1L << cAL.m_dwGUID << ccm.szName << 0L; AddPacket( CHAR_SERVER, cMsg, 4); cAL.m_strName = ccm.szName; m_AvatarList.push_back(cAL); SendAvatarList( ); } else { cMessage cMsg; cMsg << 0xF7B0L << 0L << 0L << 0xF643L << 3L << 0xBEEFBEEF << ccm.szName << 0L; AddPacket( CHAR_SERVER, cMsg, 4); } //SAFEDELETE_ARRAY( ccm.szAccountName ) //SAFEDELETE_ARRAY( ccm.szName ) break; } //Avatar creation case 0xF657L: cMasterServer::CreateNewAvatar( &m_pcAvatar, ((DWORD*)&(*pcRecvPacket)[0])[0x0A] ); m_pcAvatar->m_wNumLogins = 0x3C; m_pcAvatar->m_wModelSequenceType = 0xC6; m_pcAvatar->m_wModelSequence = 0x0; SendAddress( g_szLocalIP, g_nWorldPort ); m_bCharState = 6; break; } } break; // case 4 } case 6: { if ( pcRecvPacket->GetFlags( ) == 0x00020000 ) m_bCharState = 7; else SendAddress( g_szLocalIP, g_nWorldPort ); break; // case 6 } case 7: { break; // case 7 } //Client connection (to the server page) case 8: { m_bCharState = 4; SendAvatarList( ); //Send avatar list SendMOTD( ); //Send message of the day DWORD dwEnd3D = 0xF653; AddPacket( CHAR_SERVER, (BYTE *)&dwEnd3D, 4, 4 ); if(cCharacterServer::m_dwClientCount > 0) { cCharacterServer::m_dwClientCount--; } break; // case 8 } } } /** * Sends the client its list of avatars on the server * * This function is called whenever a client connects to a server * Returns a server message to the client. */ void cClient::SendAvatarList( ) { cMessage cMsg; cMsg << 0xF658L << 0x0000L << m_AvatarList.size(); //Iterate through the client's avatar list for( std::vector::iterator ical = m_AvatarList.begin(); ical != m_AvatarList.end(); ical++ ) { cMsg << ical->m_dwGUID << ical->m_strName.c_str( ); cMsg.pasteAlign(4); cMsg << 0L; } cMsg << 0L << 5L; //Dec Client ends here //June Client addition starts here for (int ib = 0; ib < 45; ib++) { if ((UserName[ib]) == 0) { break; } } cMsg << WORD(ib); cMsg.pasteData(UserName,ib); //for (int ia = 0; ia < (45 - ib); ia++) //{ // cMsg << BYTE(0x00); //} cMsg.pasteAlign(4); cMsg << DWORD(0x0L); //June Client ends here AddPacket( CHAR_SERVER, cMsg, 4 ); } void cClient::SendDeleteAck( ) { cMessage cAckMsg; cAckMsg << 0xF655L << 0x00108F04; AddPacket( CHAR_SERVER, cAckMsg, 4 ); } /** * Sends the client the server Message of the Day * * This function is called whenever a client connects to a server * Returns a server message to the client. */ void cClient::SendMOTD( ) { FILE *pcMOTD = fopen( "Server.motd","rt" ); std::stringstream strstrm; std::stringstream strstrm2; SYSTEMTIME st; GetSystemTime( &st ); char str[20] ={0,}; CopyMemory( &str[0], &STRFILEVER, sizeof(STRFILEVER) ); for(int a = 0;a For webspace to host our forum!\n"; strstrm2 << " k109 -> For project management assistance.\n"; strstrm2 << " Paaa -> For helping to test early releases.\n"; strstrm2 << " And all of the fans at http://forum.johnnyjpg.com!\n"; strstrm2 << "=================\n"; if ( pcMOTD ) { char buff[65536]; while ( !feof( pcMOTD ) ) { fgets( buff, 65535, pcMOTD ); strstrm << buff; } fclose( pcMOTD ); } cMessage cMsg; cMsg << 0xF65AL << strstrm.str( ).c_str( ) << strstrm2.str( ).c_str( ); AddPacket( CHAR_SERVER, cMsg, 4 ); } void cClient::SendAddress( char *szAddr, WORD wPort ) { BYTE pbSendBuffer[0x60]; SOCKADDR_IN *saSockAddr = (SOCKADDR_IN *)&pbSendBuffer[0x14]; cTransportHeader *pcTH = reinterpret_cast< cTransportHeader * >( pbSendBuffer ); pcTH->m_dwSequence = 0; pcTH->m_dwFlags = 0x00020000; pcTH->m_wLogicalID = m_PacketPipe_CS.m_wLogicalID; pcTH->m_wTotalSize = 0x10; pcTH->m_wTime = 0; pcTH->m_wTable = 0; saSockAddr->sin_family = AF_INET; saSockAddr->sin_port = htons( wPort ); saSockAddr->sin_addr.s_addr = inet_addr( szAddr ); ZeroMemory( &saSockAddr->sin_zero, 8 ); m_PacketPipe_CS.CalcCRC( pbSendBuffer, 16 ); sendto( m_PacketPipe_CS.m_Socket, (char *)pbSendBuffer, 36, NULL, (SOCKADDR *)&m_saSockAddr, sizeof( SOCKADDR ) ); } /** * Pads messages to the DWORD boundary. * * Used for variable-length (text/char[]) fields. * The client expects the next variable to start at the next whole DWORD (4 byte value) * The variable length field is zero-padded until this boundary is reached. * * @param dwLength - The length of the variable-length field. * * @return dwOffset - The BYTE value of padding required. */ DWORD cCharacterServer::ReturnPadOffset( DWORD dwLength ) { DWORD dwOffset; int intTest; intTest = ((dwLength + 2) % 4); switch(intTest) { case 0: { dwOffset = 0; break; } case 1: { dwOffset = 3; break; } case 2: { dwOffset = 2; break; } case 3: { dwOffset = 1; break; } default: { dwOffset = 0; break; } } return dwOffset; // Returns DWORD alignment padding amount }