#include #include #include #include #include #include #include #include #include #include "Zone/Zone.h" #include "Network/PacketWrappers/InitUIPacket.h" #include "DebugCommand/DebugCommandHandler.h" #include "GameConnection.h" #include "ServerZone.h" #include "Session.h" #include "Framework.h" #include "Forwards.h" extern Core::Framework g_fw; using namespace Core::Common; using namespace Core::Network::Packets; using namespace Core::Network::Packets::Server; Core::Network::GameConnection::GameConnection( Core::Network::HivePtr pHive, Core::Network::AcceptorPtr pAcceptor ) : Connection( pHive ) , m_pAcceptor( pAcceptor ) , m_conType( ConnectionType::None ) { auto setZoneHandler = [=]( uint16_t opcode, std::string handlerName, GameConnection::Handler pHandler ) { m_zoneHandlerMap[opcode] = pHandler; m_zoneHandlerStrMap[opcode] = handlerName; }; auto setChatHandler = [=]( uint16_t opcode, std::string handlerName, GameConnection::Handler pHandler ) { m_chatHandlerMap[opcode] = pHandler; m_chatHandlerStrMap[opcode] = handlerName; }; setZoneHandler( ClientZoneIpcType::PingHandler, "PingHandler", &GameConnection::pingHandler ); setZoneHandler( ClientZoneIpcType::InitHandler, "InitHandler", &GameConnection::initHandler ); setZoneHandler( ClientZoneIpcType::ChatHandler, "ChatHandler", &GameConnection::chatHandler ); setZoneHandler( ClientZoneIpcType::FinishLoadingHandler, "FinishLoadingHandler", &GameConnection::finishLoadingHandler ); setZoneHandler( ClientZoneIpcType::PlayTimeHandler, "PlayTimeHandler", &GameConnection::playTimeHandler ); setZoneHandler( ClientZoneIpcType::LogoutHandler, "LogoutHandler", &GameConnection::logoutHandler ); setZoneHandler( ClientZoneIpcType::SocialListHandler, "SocialListHandler", &GameConnection::socialListHandler ); setZoneHandler( ClientZoneIpcType::SetSearchInfoHandler, "SetSearchInfoHandler", &GameConnection::setSearchInfoHandler ); setZoneHandler( ClientZoneIpcType::ReqSearchInfoHandler, "ReqSearchInfoHandler", &GameConnection::reqSearchInfoHandler ); setZoneHandler( ClientZoneIpcType::BlackListHandler, "BlackListHandler", &GameConnection::blackListHandler ); setZoneHandler( ClientZoneIpcType::LinkshellListHandler, "LinkshellListHandler", &GameConnection::linkshellListHandler ); setZoneHandler( ClientZoneIpcType::FcInfoReqHandler, "FcInfoReqHandler", &GameConnection::fcInfoReqHandler ); setZoneHandler( ClientZoneIpcType::ZoneLineHandler, "ZoneLineHandler", &GameConnection::zoneLineHandler ); setZoneHandler( ClientZoneIpcType::ClientTrigger, "ClientTrigger", &GameConnection::clientTriggerHandler ); setZoneHandler( ClientZoneIpcType::DiscoveryHandler, "DiscoveryHandler", &GameConnection::discoveryHandler ); setZoneHandler( ClientZoneIpcType::SkillHandler, "SkillHandler", &GameConnection::skillHandler ); setZoneHandler( ClientZoneIpcType::GMCommand1, "GMCommand1", &GameConnection::gm1Handler ); setZoneHandler( ClientZoneIpcType::GMCommand2, "GMCommand2", &GameConnection::gm2Handler ); setZoneHandler( ClientZoneIpcType::UpdatePositionHandler,"UpdatePositionHandler", &GameConnection::updatePositionHandler ); setZoneHandler( ClientZoneIpcType::InventoryModifyHandler,"InventoryModifyHandler", &GameConnection::inventoryModifyHandler ); setZoneHandler( ClientZoneIpcType::TalkEventHandler, "EventHandlerTalk", &GameConnection::eventHandlerTalk ); setZoneHandler( ClientZoneIpcType::EmoteEventHandler, "EventHandlerEmote", &GameConnection::eventHandlerEmote ); setZoneHandler( ClientZoneIpcType::WithinRangeEventHandler, "EventHandlerWithinRange", &GameConnection::eventHandlerWithinRange ); setZoneHandler( ClientZoneIpcType::OutOfRangeEventHandler, "EventHandlerOutsideRange", &GameConnection::eventHandlerOutsideRange ); setZoneHandler( ClientZoneIpcType::EnterTeriEventHandler, "EventHandlerEnterTeri", &GameConnection::eventHandlerEnterTerritory ); setZoneHandler( ClientZoneIpcType::ReturnEventHandler, "EventHandlerReturn", &GameConnection::eventHandlerReturn ); setZoneHandler( ClientZoneIpcType::TradeReturnEventHandler, "EventHandlerReturn", &GameConnection::eventHandlerReturn ); setZoneHandler( ClientZoneIpcType::LinkshellEventHandler, "LinkshellEventHandler", &GameConnection::eventHandlerLinkshell ); setZoneHandler( ClientZoneIpcType::LinkshellEventHandler1, "LinkshellEventHandler1", &GameConnection::eventHandlerLinkshell ); setZoneHandler( ClientZoneIpcType::CFDutyInfoHandler, "CFDutyInfoRequest", &GameConnection::cfDutyInfoRequest ); setZoneHandler( ClientZoneIpcType::CFRegisterDuty, "CFRegisterDuty", &GameConnection::cfRegisterDuty ); setZoneHandler( ClientZoneIpcType::CFRegisterRoulette, "CFRegisterRoulette", &GameConnection::cfRegisterRoulette ); setZoneHandler( ClientZoneIpcType::CFCommenceHandler, "CFDutyAccepted", &GameConnection::cfDutyAccepted); setZoneHandler( ClientZoneIpcType::ReqEquipDisplayFlagsChange, "ReqEquipDisplayFlagsChange", &GameConnection::reqEquipDisplayFlagsHandler ); setZoneHandler( ClientZoneIpcType::PerformNoteHandler, "PerformNoteHandler", &GameConnection::performNoteHandler ); setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler); } Core::Network::GameConnection::~GameConnection() = default; // overwrite the parents onConnect for our game socket needs void Core::Network::GameConnection::OnAccept( const std::string & host, uint16_t port ) { GameConnectionPtr connection( new GameConnection( m_hive, m_pAcceptor ) ); m_pAcceptor->Accept( connection ); auto pLog = g_fw.get< Logger >(); pLog->info( "Connect from " + m_socket.remote_endpoint().address().to_string() ); } void Core::Network::GameConnection::OnDisconnect() { auto pLog = g_fw.get< Logger >(); pLog->debug( "GameConnection DISCONNECT" ); m_pSession = nullptr; } void Core::Network::GameConnection::OnRecv( std::vector< uint8_t > & buffer ) { // This is assumed packet always start with valid FFXIVARR_PACKET_HEADER for now. auto pLog = g_fw.get< Logger >(); Packets::FFXIVARR_PACKET_HEADER packetHeader{}; const auto headerResult = Packets::getHeader( buffer, 0, packetHeader ); if( headerResult == Incomplete ) { pLog->info( "Dropping connection due to incomplete packet header." ); pLog->info( "FIXME: Packet message bounary is not implemented." ); Disconnect(); return; } else if( headerResult == Malformed ) { pLog->info( "Dropping connection due to malformed packet header." ); Disconnect(); return; } // Dissect packet list std::vector< Packets::FFXIVARR_PACKET_RAW > packetList; const auto packetResult = Packets::getPackets( buffer, sizeof( struct FFXIVARR_PACKET_HEADER ), packetHeader, packetList ); if( packetResult == Incomplete ) { pLog->info( "Dropping connection due to incomplete packets." ); pLog->info( "FIXME: Packet message bounary is not implemented." ); Disconnect(); return; } else if( packetResult == Malformed ) { pLog->info( "Dropping connection due to malformed packets." ); Disconnect(); return; } // Handle it handlePackets( packetHeader, packetList ); } void Core::Network::GameConnection::OnError( const boost::system::error_code & error ) { auto pLog = g_fw.get< Logger >(); pLog->debug( "GameConnection ERROR: " + error.message() ); } void Core::Network::GameConnection::queueInPacket( Core::Network::Packets::FFXIVARR_PACKET_RAW inPacket ) { m_inQueue.push( inPacket ); } void Core::Network::GameConnection::queueOutPacket( Core::Network::Packets::FFXIVPacketBasePtr outPacket ) { m_outQueue.push( outPacket ); } void Core::Network::GameConnection::handleZonePacket( Core::Network::Packets::FFXIVARR_PACKET_RAW& pPacket ) { auto pLog = g_fw.get< Logger >(); uint16_t opcode = *reinterpret_cast< uint16_t* >( &pPacket.data[0x02] ); auto it = m_zoneHandlerMap.find( opcode ); std::string sessionStr = "[" + std::to_string( m_pSession->getId() ) + "]"; if( it != m_zoneHandlerMap.end() ) { auto itStr = m_zoneHandlerStrMap.find( opcode ); std::string name = itStr != m_zoneHandlerStrMap.end() ? itStr->second : "unknown"; // dont display packet notification if it is a ping or pos update, don't want the spam if( opcode != PingHandler && opcode != UpdatePositionHandler ) pLog->debug( sessionStr + " Handling Zone IPC : " + name + "( " + boost::str( boost::format( "%|04X|" ) % static_cast< uint32_t >( opcode ) ) + " )" ); ( this->*( it->second ) )( pPacket, *m_pSession->getPlayer() ); } else { pLog->debug( sessionStr + " Undefined Zone IPC : Unknown ( " + boost::str( boost::format( "%|04X|" ) % static_cast< uint32_t >( opcode ) ) + " )" ); //pLog->debug( "\n" + pPacket.toString() ); } } void Core::Network::GameConnection::handleChatPacket( Core::Network::Packets::FFXIVARR_PACKET_RAW& pPacket ) { auto pLog = g_fw.get< Logger >(); uint16_t opcode = *reinterpret_cast< uint16_t* >( &pPacket.data[0x02] ); auto it = m_chatHandlerMap.find( opcode ); std::string sessionStr = "[" + std::to_string( m_pSession->getId() ) + "]"; if( it != m_chatHandlerMap.end() ) { auto itStr = m_chatHandlerStrMap.find( opcode ); std::string name = itStr != m_chatHandlerStrMap.end() ? itStr->second : "unknown"; // dont display packet notification if it is a ping or pos update, don't want the spam pLog->debug( sessionStr + " Handling Chat IPC : " + name + "( " + boost::str( boost::format( "%|04X|" ) % static_cast< uint32_t >( opcode ) ) + " )" ); ( this->*( it->second ) )( pPacket, *m_pSession->getPlayer() ); } else { pLog->debug( sessionStr + " Undefined Chat IPC : Unknown ( " + boost::str( boost::format( "%|04X|" ) % static_cast< uint32_t >( opcode ) ) + " )" ); //pLog->debug( pPacket.toString() ); } } void Core::Network::GameConnection::handlePacket( Core::Network::Packets::FFXIVARR_PACKET_RAW& pPacket ) { if( !m_pSession ) return; switch( m_conType ) { case Network::ConnectionType::Zone: handleZonePacket( pPacket ); break; case Network::ConnectionType::Chat: handleChatPacket( pPacket ); break; } } void Core::Network::GameConnection::sendPackets( Packets::PacketContainer* pPacket ) { //g_log.Log(LoggingSeverity::info, pPacket->toString()); std::vector< uint8_t > sendBuffer; pPacket->fillSendBuffer( sendBuffer ); Send( sendBuffer ); } void Core::Network::GameConnection::processInQueue() { // handle the incoming game packets while( m_inQueue.size() ) { auto pPacket = m_inQueue.pop(); handlePacket( pPacket ); } } void Core::Network::GameConnection::processOutQueue() { auto pLog = g_fw.get< Logger >(); if( m_outQueue.size() < 1 ) return; int32_t totalSize = 0; // create a new packet container PacketContainer pRP = PacketContainer( m_pSession->getId() ); // get next packet off the queue while( auto pPacket = m_outQueue.pop() ) { if( pPacket->getSize() == 0 ) { pLog->debug( "end of packet set" ); break; } pRP.addPacket( pPacket ); totalSize += pPacket->getSize(); } if( totalSize > 0 ) sendPackets( &pRP ); } void Core::Network::GameConnection::sendSinglePacket( Core::Network::Packets::FFXIVPacketBasePtr pPacket ) { PacketContainer pRP = PacketContainer(); pRP.addPacket( pPacket ); sendPackets( &pRP ); } void Core::Network::GameConnection::injectPacket( const std::string& packetpath, Core::Entity::Player& player ) { char packet[0x11570]; memset( packet, 0, 0x11570 ); // get the packet name / path from the command arguments FILE *fp = nullptr; fp = fopen( packetpath.c_str(), "rb" ); if( fp == nullptr ) { player.sendDebug( "Packet " + packetpath + " not found!" ); return; } // read the packet into the buffer fseek( fp, 0, SEEK_END ); int32_t size = ftell( fp ); rewind( fp ); if ( fread( packet, sizeof( char ), size, fp ) != size ) { player.sendDebug( "Packet " + packetpath + " did not read full size: " + std::to_string( size ) ); return; } fclose( fp ); // cycle through the packet entries and queue each one for( int32_t k = 0x18; k < size; ) { uint32_t tmpId = player.getId(); // replace ids in the entryheader if needed if( !memcmp( packet + k + 0x04, packet + k + 0x08, 4 ) ) { memcpy( packet + k + 0x04, &tmpId, 4 ); memcpy( packet + k + 0x08, &tmpId, 4 ); } else memcpy( packet + k + 0x08, &tmpId, 4 ); uint16_t pSize = *reinterpret_cast< uint16_t* >( packet + k ); // queue packet to the session if( pSize == 0 ) return; queueOutPacket( FFXIVPacketBasePtr( new FFXIVRawPacket( packet + k, pSize ) ) ); k += ( pSize ); } } void Core::Network::GameConnection::handlePackets( const Core::Network::Packets::FFXIVARR_PACKET_HEADER& ipcHeader, const std::vector< Core::Network::Packets::FFXIVARR_PACKET_RAW >& packetData ) { auto pLog = g_fw.get< Logger >(); auto pServerZone = g_fw.get< ServerZone >(); // if a session is set, update the last time it recieved a game packet if( m_pSession ) m_pSession->updateLastDataTime(); for( auto inPacket : packetData ) { switch( inPacket.segHdr.type ) { case 1: { char* id = ( char* ) &( inPacket.data[4] ); uint32_t playerId = boost::lexical_cast< uint32_t >( id ); auto pCon = boost::static_pointer_cast< GameConnection, Connection >( shared_from_this() ); // try to retrieve the session for this id auto session = pServerZone->getSession( playerId ); if( !session ) { pLog->info( "[" + std::string( id ) + "] Session not registered, creating" ); // return; if( !pServerZone->createSession( playerId ) ) { Disconnect(); return; } session = pServerZone->getSession( playerId ); } //TODO: Catch more things in lobby and send real errors else if( !session->isValid() || ( session->getPlayer() && session->getPlayer()->getLastPing() != 0 ) ) { pLog->error( "[" + std::string(id) + "] Session INVALID, disconnecting" ); Disconnect(); return; } // if not set, set the session for this connection if( !m_pSession && session ) m_pSession = session; auto pe = boost::make_shared< FFXIVRawPacket >( 0x07, 0x18, 0, 0 ); *(unsigned int*)(&pe->data()[0]) = 0xE0037603; *(unsigned int*)(&pe->data()[4]) = static_cast< uint32_t >( time( nullptr ) ); sendSinglePacket( pe ); // main connection, assinging it to the session if( ipcHeader.connectionType == ConnectionType::Zone ) { auto pe1 = boost::make_shared< FFXIVRawPacket >( 0x02, 0x38, 0, 0 ); *(unsigned int*)(&pe1->data()[0]) = playerId; sendSinglePacket( pe1 ); pLog->info( "[" + std::string( id ) + "] Setting session for zone connection" ); session->setZoneConnection( pCon ); } // chat connection, assinging it to the session else if( ipcHeader.connectionType == ConnectionType::Chat ) { auto pe2 = boost::make_shared< FFXIVRawPacket >( 0x02, 0x38, 0, 0 ); *(unsigned int*)(&pe2->data()[0]) = playerId; sendSinglePacket( pe2 ); auto pe3 = boost::make_shared< FFXIVRawPacket >( 0x03, 0x28, playerId, playerId ); *(unsigned short*)(&pe3->data()[2]) = 0x02; sendSinglePacket( pe3 ); pLog->info( "[" + std::string( id ) + "] Setting session for chat connection" ); session->setChatConnection( pCon ); } break; } case 3: // game packet { queueInPacket( inPacket ); break; } case 7: // keep alive { uint32_t id = *( uint32_t* ) &inPacket.data[0]; uint32_t timeStamp = *( uint32_t* ) &inPacket.data[4]; auto pe4 = boost::make_shared< FFXIVRawPacket >( 0x08, 0x18, 0, 0 ); *(unsigned int*)(&pe4->data()[0]) = id; *(unsigned int*)(&pe4->data()[4]) = timeStamp; sendSinglePacket( pe4 ); break; } case 8: { break; } } } }