1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-25 05:57:45 +00:00
sapphire/src/servers/sapphire_zone/Network/GameConnection.cpp

490 lines
17 KiB
C++
Raw Normal View History

#include <boost/format.hpp>
2018-03-06 22:22:19 +01:00
#include <Common.h>
#include <Network/CommonNetwork.h>
#include <Util/Util.h>
#include <Logging/Logger.h>
2018-06-28 00:07:07 +02:00
#include <utility>
2018-06-02 13:38:00 +02:00
#include <Network/Acceptor.h>
2018-03-06 22:22:19 +01:00
#include <Network/PacketContainer.h>
#include <Network/GamePacketParser.h>
2017-08-08 13:53:47 +02:00
#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"
2018-03-09 00:06:44 +01:00
extern Core::Framework g_fw;
2017-08-08 13:53:47 +02:00
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 )
2017-08-08 13:53:47 +02:00
{
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;
};
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::PingHandler, "PingHandler", &GameConnection::pingHandler );
setZoneHandler( ClientZoneIpcType::InitHandler, "InitHandler", &GameConnection::initHandler );
setZoneHandler( ClientZoneIpcType::ChatHandler, "ChatHandler", &GameConnection::chatHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::FinishLoadingHandler, "FinishLoadingHandler",
&GameConnection::finishLoadingHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::PlayTimeHandler, "PlayTimeHandler", &GameConnection::playTimeHandler );
setZoneHandler( ClientZoneIpcType::LogoutHandler, "LogoutHandler", &GameConnection::logoutHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::SocialListHandler, "SocialListHandler", &GameConnection::socialListHandler );
setZoneHandler( ClientZoneIpcType::SetSearchInfoHandler, "SetSearchInfoHandler",
&GameConnection::setSearchInfoHandler );
setZoneHandler( ClientZoneIpcType::ReqSearchInfoHandler, "ReqSearchInfoHandler",
&GameConnection::reqSearchInfoHandler );
setZoneHandler( ClientZoneIpcType::ReqExamineSearchCommentHandler, "ReqExamineSearchCommentHandler",
&GameConnection::reqExamineSearchCommentHandler );
setZoneHandler( ClientZoneIpcType::BlackListHandler, "BlackListHandler", &GameConnection::blackListHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::LinkshellListHandler, "LinkshellListHandler",
&GameConnection::linkshellListHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::FcInfoReqHandler, "FcInfoReqHandler", &GameConnection::fcInfoReqHandler );
setZoneHandler( ClientZoneIpcType::ReqExamineFcInfo, "ReqExamineFcInfo", &GameConnection::reqExamineFcInfo );
setZoneHandler( ClientZoneIpcType::ZoneLineHandler, "ZoneLineHandler", &GameConnection::zoneLineHandler );
setZoneHandler( ClientZoneIpcType::ClientTrigger, "ClientTrigger", &GameConnection::clientTriggerHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::DiscoveryHandler, "DiscoveryHandler", &GameConnection::discoveryHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::SkillHandler, "ActionHandler", &GameConnection::actionHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::GMCommand1, "GMCommand1", &GameConnection::gm1Handler );
setZoneHandler( ClientZoneIpcType::GMCommand2, "GMCommand2", &GameConnection::gm2Handler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::UpdatePositionHandler, "UpdatePositionHandler",
&GameConnection::updatePositionHandler );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::InventoryModifyHandler, "InventoryModifyHandler",
&GameConnection::inventoryModifyHandler );
2017-08-08 13:53:47 +02:00
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 );
2017-08-08 13:53:47 +02:00
setZoneHandler( ClientZoneIpcType::ReturnEventHandler, "EventHandlerReturn", &GameConnection::eventHandlerReturn );
setZoneHandler( ClientZoneIpcType::TradeReturnEventHandler, "EventHandlerReturn",
&GameConnection::eventHandlerReturn );
2017-08-23 23:58:14 +02:00
setZoneHandler( ClientZoneIpcType::LinkshellEventHandler, "LinkshellEventHandler",
&GameConnection::eventHandlerLinkshell );
setZoneHandler( ClientZoneIpcType::LinkshellEventHandler1, "LinkshellEventHandler1",
&GameConnection::eventHandlerLinkshell );
2017-08-12 23:20:26 +09:00
setZoneHandler( ClientZoneIpcType::CFDutyInfoHandler, "CFDutyInfoRequest", &GameConnection::cfDutyInfoRequest );
setZoneHandler( ClientZoneIpcType::CFRegisterDuty, "CFRegisterDuty", &GameConnection::cfRegisterDuty );
setZoneHandler( ClientZoneIpcType::CFRegisterRoulette, "CFRegisterRoulette", &GameConnection::cfRegisterRoulette );
setZoneHandler( ClientZoneIpcType::CFCommenceHandler, "CFDutyAccepted", &GameConnection::cfDutyAccepted );
2017-08-12 23:20:26 +09:00
setZoneHandler( ClientZoneIpcType::ReqEquipDisplayFlagsChange, "ReqEquipDisplayFlagsChange",
&GameConnection::reqEquipDisplayFlagsHandler );
2017-11-22 00:29:42 +09:00
setZoneHandler( ClientZoneIpcType::PerformNoteHandler, "PerformNoteHandler", &GameConnection::performNoteHandler );
setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler );
2017-08-08 13:53:47 +02:00
}
2017-11-28 17:29:06 +01:00
Core::Network::GameConnection::~GameConnection() = default;
2017-08-08 13:53:47 +02:00
// overwrite the parents onConnect for our game socket needs
void Core::Network::GameConnection::OnAccept( const std::string& host, uint16_t port )
2017-08-08 13:53:47 +02:00
{
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() );
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::OnDisconnect()
{
auto pLog = g_fw.get< Logger >();
pLog->debug( "GameConnection DISCONNECT" );
m_pSession = nullptr;
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::OnRecv( std::vector< uint8_t >& buffer )
2017-08-08 13:53:47 +02:00
{
// 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 );
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::OnError( const boost::system::error_code& error )
2017-08-08 13:53:47 +02:00
{
auto pLog = g_fw.get< Logger >();
pLog->debug( "GameConnection ERROR: " + error.message() );
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::queueInPacket( Core::Network::Packets::FFXIVARR_PACKET_RAW inPacket )
2017-08-08 13:53:47 +02:00
{
m_inQueue.push( inPacket );
2017-08-08 13:53:47 +02:00
}
2018-06-28 00:07:07 +02:00
void Core::Network::GameConnection::queueOutPacket( Core::Network::Packets::FFXIVPacketBasePtr outPacket )
2017-08-08 13:53:47 +02:00
{
m_outQueue.push( outPacket );
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::handleZonePacket( Core::Network::Packets::FFXIVARR_PACKET_RAW& pPacket )
2017-08-08 13:53:47 +02:00
{
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(
"Dump:\n" + Util::binaryToHexDump( const_cast< uint8_t* >( &pPacket.data[ 0 ] ), pPacket.segHdr.size ) );
}
}
2017-08-08 13:53:47 +02:00
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;
2018-08-23 23:19:25 +10:00
default:
break;
}
2017-08-08 13:53:47 +02:00
}
2017-11-28 17:29:06 +01:00
void Core::Network::GameConnection::sendPackets( Packets::PacketContainer* pPacket )
2017-08-08 13:53:47 +02:00
{
//g_log.Log(LoggingSeverity::info, pPacket->toString());
std::vector< uint8_t > sendBuffer;
2017-08-08 13:53:47 +02:00
pPacket->fillSendBuffer( sendBuffer );
Send( sendBuffer );
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::processInQueue()
{
// handle the incoming game packets
while( m_inQueue.size() )
{
auto pPacket = m_inQueue.pop();
handlePacket( pPacket );
}
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::processOutQueue()
{
auto pLog = g_fw.get< Logger >();
if( m_outQueue.size() < 1 )
return;
2017-08-08 13:53:47 +02:00
int32_t totalSize = 0;
2017-08-08 13:53:47 +02:00
// create a new packet container
PacketContainer pRP = PacketContainer( m_pSession->getId() );
2017-08-08 13:53:47 +02:00
// get next packet off the queue
while( auto pPacket = m_outQueue.pop() )
{
if( pPacket->getSize() == 0 )
{
pLog->debug( "end of packet set" );
break;
}
2017-08-08 13:53:47 +02:00
pRP.addPacket( pPacket );
totalSize += pPacket->getSize();
}
2017-08-08 13:53:47 +02:00
if( totalSize > 0 )
sendPackets( &pRP );
2017-08-08 13:53:47 +02:00
}
2018-06-28 00:07:07 +02:00
void Core::Network::GameConnection::sendSinglePacket( Core::Network::Packets::FFXIVPacketBasePtr pPacket )
2017-08-08 13:53:47 +02:00
{
PacketContainer pRP = PacketContainer();
pRP.addPacket( pPacket );
sendPackets( &pRP );
2017-08-08 13:53:47 +02:00
}
2018-01-08 21:42:44 +01:00
void Core::Network::GameConnection::injectPacket( const std::string& packetpath, Core::Entity::Player& player )
2017-08-08 13:53:47 +02:00
{
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;
2017-08-08 13:53:47 +02:00
queueOutPacket( FFXIVPacketBasePtr( new FFXIVRawPacket( packet + k, pSize ) ) );
2018-07-04 22:13:06 +10:00
k += ( pSize );
}
2017-08-08 13:53:47 +02:00
}
void Core::Network::GameConnection::handlePackets( const Core::Network::Packets::FFXIVARR_PACKET_HEADER& ipcHeader,
2017-11-28 00:09:36 +01:00
const std::vector< Core::Network::Packets::FFXIVARR_PACKET_RAW >& packetData )
2017-08-08 13:53:47 +02:00
{
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 SEGMENTTYPE_SESSIONINIT:
2017-08-08 13:53:47 +02:00
{
char* id = ( char* ) &( inPacket.data[ 4 ] );
2018-09-26 09:11:59 -04:00
uint32_t playerId = std::stoi( 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;
2017-08-08 13:53:47 +02:00
}
case SEGMENTTYPE_IPC: // game packet
2017-08-08 13:53:47 +02:00
{
queueInPacket( inPacket );
break;
2017-08-08 13:53:47 +02:00
}
case SEGMENTTYPE_KEEPALIVE: // keep alive
2017-08-08 13:53:47 +02:00
{
uint32_t id = *( uint32_t* ) &inPacket.data[ 0 ];
uint32_t timeStamp = *( uint32_t* ) &inPacket.data[ 4 ];
2017-08-08 13:53:47 +02:00
auto pe4 = boost::make_shared< FFXIVRawPacket >( 0x08, 0x18, 0, 0 );
*( unsigned int* ) ( &pe4->data()[ 0 ] ) = id;
*( unsigned int* ) ( &pe4->data()[ 4 ] ) = timeStamp;
sendSinglePacket( pe4 );
2017-08-08 13:53:47 +02:00
break;
2017-08-08 13:53:47 +02:00
}
case 8:
{
break;
2017-08-08 13:53:47 +02:00
}
}
2017-08-08 13:53:47 +02:00
}
2017-08-08 13:53:47 +02:00
}