diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index 2f8775c0..acb94a39 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -240,6 +240,7 @@ enum ClientTriggerType FinishZoning = 0xC9, Teleport = 0xCA, + Examine = 0x12C, MarkPlayer = 0x12D, // Mark player, visible to party only SetTitleReq = 0x12E, TitleList = 0x12F, @@ -249,7 +250,7 @@ enum ClientTriggerType ClearWaymarks = 0x13A, CameraMode = 0x13B, // param12, 1 = camera mode enable, 0 = disable - + CharaNameReq = 0x13D, // requests character name by content id HuntingLogDetails = 0x194, Timers = 0x1AB, diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index a535f3dc..263fb39d 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -76,16 +76,20 @@ enum ServerZoneIpcType : SocialRequestResponse = 0x00BB, // updated 4.1 CancelAllianceForming = 0x00C6, // updated 4.2 + + Playtime = 0x00F5, // updated 4.3 Chat = 0x00F7, // updated 4.3 SocialList = 0x00FD, // updated 4.3 UpdateSearchInfo = 0x0100, // updated 4.3 InitSearchInfo = 0x0101, // updated 4.3 + ExamineSearchComment = 0x0102, // updated 4.1 ServerNotice = 0x0106, // updated 4.3 SetOnlineStatus = 0x0107, // updated 4.3 + CountdownInitiate = 0x0111, // updated 4.3 CountdownCancel = 0x0112, // updated 4.3 @@ -94,6 +98,8 @@ enum ServerZoneIpcType : LogMessage = 0x00D0, LinkshellList = 0x011C, // updated 4.3 + + ExamineFreeCompanyInfo = 0x013A, // updated 4.1 CharaFreeCompanyTag = 0x013B, // updated 4.3 FreeCompanyBoardMsg = 0x013C, // updated 4.3 FreeCompanyInfo = 0x013D, // updated 4.3 @@ -130,7 +136,8 @@ enum ServerZoneIpcType : PlayerStateFlags = 0x0184, // updated 4.3 PlayerClassInfo = 0x0185, // updated 4.3 ModelEquip = 0x0186, // updated 4.3 - + Examine = 0x0187, // updated 4.3 + CharaNameReq = 0x0189, // updated 4.3 UpdateClassInfo = 0x018A, // updated 4.3 ItemInfo = 0x0190, // updated 4.3 @@ -190,7 +197,7 @@ enum ServerZoneIpcType : // Unknown IPC types that still need to be sent // TODO: figure all these out properly - IPCTYPE_UNK_320 = 0x024C, // updated 4.3 + IPCTYPE_UNK_320 = 0x024C, // updated 4.3 IPCTYPE_UNK_322 = 0x024E, // updated 4.3 }; @@ -224,6 +231,8 @@ enum ClientZoneIpcType : SocialListHandler = 0x00DB, // updated 4.3 ReqSearchInfoHandler = 0x00E0, // updated 4.3 + ReqExamineSearchCommentHandler = 0x00E1, // updated 4.1 + SetSearchInfoHandler = 0x00DE, // updated 4.3 BlackListHandler = 0x00EC, // updated 4.3 @@ -232,6 +241,7 @@ enum ClientZoneIpcType : LinkshellListHandler = 0x00F4, // updated 4.3 SearchMarketboard = 0x0103, // updated 4.3 + ReqExamineFcInfo = 0x010F, // updated 4.1 FcInfoReqHandler = 0x011A, // updated 4.2 diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index 54f3cc11..83c639cf 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -140,6 +140,16 @@ struct FFXIVIpcInitSearchInfo : char padding[5]; }; +struct FFXIVIpcExamineSearchComment : + FFXIVIpcBasePacket< ExamineSearchComment > +{ + uint32_t charId; + // packet only has 196 bytes after the charid + // likely utf8 + char searchComment[195]; + char padding; +}; + /** * Structural representation of the packet sent by the server * to display a server notice message @@ -200,6 +210,25 @@ struct FFXIVIpcLinkshellList : } entry[8]; }; +struct FFXIVIpcExamineFreeCompanyInfo : + FFXIVIpcBasePacket< ExamineFreeCompanyInfo > +{ + char unknown[0x20]; // likely fc allegiance/icon/housing info etc + uint32_t charId; + uint32_t fcTimeCreated; + char unknown2[0x10]; + uint16_t unknown3; + char fcName[0x14]; // 20 char limit + uint16_t padding; + char fcTag[0x05]; // 5 char tag limit + uint16_t padding2; // null terminator? + char fcLeader[0x20]; // leader name (32 bytes) + char fcSlogan[192]; // source: https://ffxiv.gamerescape.com/wiki/Free_Company (packet cap confirms this size also) + char padding3; // null terminator? + char fcEstateProfile[20]; // todo: size needs confirmation + uint32_t padding4; +}; + struct FFXIVIpcStatusEffectList : FFXIVIpcBasePacket< StatusEffectList > { @@ -950,6 +979,54 @@ struct FFXIVIpcModelEquip : /* 003C */ uint32_t padding2; }; +struct FFXIVIpcExamine : + FFXIVIpcBasePacket< Examine > +{ + uint8_t unkFlag1; + uint8_t unkFlag2; + char classJob; + char level; + uint16_t padding; + uint16_t titleId; + char grandCompany; + char grandCompanyRank; + + char unknown[6]; + uint32_t u6_fromPSpawn; + uint32_t u7_fromPSpawn; + char padding1[8]; + uint64_t mainWeaponModel; + uint64_t secWeaponModel; + char unknown2[16]; + struct ItemData + { + uint32_t catalogId; + uint32_t appearanceCatalogId; + uint64_t crafterId; + uint8_t quality; + uint8_t unknown[3]; + struct Materia + { + uint16_t materiaId; + uint16_t tier; + } materia[5]; + } entries[14]; + char name[32]; + char padding2; + char unk3[16]; + char look[26]; + char padding3[5]; + uint32_t models[10]; + char unknown4[200]; +}; + +struct FFXIVIpcCharaNameReq : + FFXIVIpcBasePacket< CharaNameReq > +{ + uint64_t contentId; + char name[32]; +}; + /** * Structural representation of the packet sent by the server * to update a players appearance diff --git a/src/servers/sapphire_zone/Network/GameConnection.cpp b/src/servers/sapphire_zone/Network/GameConnection.cpp index 949c3b38..84f7b730 100644 --- a/src/servers/sapphire_zone/Network/GameConnection.cpp +++ b/src/servers/sapphire_zone/Network/GameConnection.cpp @@ -60,14 +60,15 @@ Core::Network::GameConnection::GameConnection( Core::Network::HivePtr pHive, &GameConnection::setSearchInfoHandler ); setZoneHandler( ClientZoneIpcType::ReqSearchInfoHandler, "ReqSearchInfoHandler", &GameConnection::reqSearchInfoHandler ); - + setZoneHandler( ClientZoneIpcType::ReqExamineSearchCommentHandler, "ReqExamineSearchCommentHandler", + &GameConnection::reqExamineSearchCommentHandler ); setZoneHandler( ClientZoneIpcType::BlackListHandler, "BlackListHandler", &GameConnection::blackListHandler ); setZoneHandler( ClientZoneIpcType::LinkshellListHandler, "LinkshellListHandler", &GameConnection::linkshellListHandler ); setZoneHandler( ClientZoneIpcType::FcInfoReqHandler, "FcInfoReqHandler", &GameConnection::fcInfoReqHandler ); - + setZoneHandler( ClientZoneIpcType::ReqExamineFcInfo, "ReqExamineFcInfo", &GameConnection::reqExamineFcInfo ); setZoneHandler( ClientZoneIpcType::ZoneLineHandler, "ZoneLineHandler", &GameConnection::zoneLineHandler ); setZoneHandler( ClientZoneIpcType::ClientTrigger, "ClientTrigger", &GameConnection::clientTriggerHandler ); diff --git a/src/servers/sapphire_zone/Network/GameConnection.h b/src/servers/sapphire_zone/Network/GameConnection.h index d837ad4e..1dbd362f 100644 --- a/src/servers/sapphire_zone/Network/GameConnection.h +++ b/src/servers/sapphire_zone/Network/GameConnection.h @@ -110,6 +110,10 @@ public: DECLARE_HANDLER( reqSearchInfoHandler ); + DECLARE_HANDLER( reqExamineSearchCommentHandler ); + + DECLARE_HANDLER( reqExamineFcInfo ); + DECLARE_HANDLER( updatePositionHandler ); DECLARE_HANDLER( chatHandler ); diff --git a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp index 14d2e611..374a6d64 100644 --- a/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/ClientTriggerHandler.cpp @@ -13,6 +13,8 @@ #include "Zone/ZonePosition.h" #include "Network/GameConnection.h" + +#include "Network/PacketWrappers/ExaminePacket.h" #include "Network/PacketWrappers/InitUIPacket.h" #include "Network/PacketWrappers/PingPacket.h" #include "Network/PacketWrappers/MoveActorPacket.h" @@ -27,6 +29,7 @@ #include "Action/Action.h" #include "Action/ActionTeleport.h" + #include "Session.h" #include "ServerZone.h" #include "Forwards.h" @@ -40,6 +43,21 @@ using namespace Core::Network::Packets; using namespace Core::Network::Packets::Server; using namespace Core::Network::ActorControl; +void examineHandler( Core::Entity::Player& player, uint32_t targetId ) +{ + using namespace Core; + + auto pSession = g_fw.get< Core::ServerZone >()->getSession( targetId ); + if( pSession ) + { + auto pTarget = pSession->getPlayer(); + if( pTarget ) + { + player.queuePacket( boost::make_shared< ExaminePacket >( player, pTarget ) ); + } + } +} + void Core::Network::GameConnection::clientTriggerHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player ) { @@ -118,6 +136,12 @@ void Core::Network::GameConnection::clientTriggerHandler( const Packets::FFXIVAR player.getCurrentAction()->setInterrupted(); break; } + case ClientTriggerType::Examine: + { + uint32_t targetId = param11; + examineHandler( player, targetId ); + break; + } case ClientTriggerType::MarkPlayer: // Mark player { break; @@ -138,6 +162,22 @@ void Core::Network::GameConnection::clientTriggerHandler( const Packets::FFXIVAR player.updateHowtosSeen( howToId ); break; } + case ClientTriggerType::CharaNameReq: + { + uint64_t targetContentId = param1; + // todo: look up player by content id + /* + auto packet = makeZonePacket< FFXIVIpcCharaNameReq >( player.getId() ); + packet->data().contentId = targetContentId; + + // lookup the name + + strcpy( packet->data().name, name ); + + player.queuePacket( packet ); + */ + break; + } case ClientTriggerType::EmoteReq: // emote { uint64_t targetId = player.getTargetId(); diff --git a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp index 5880e5ec..0efd32aa 100644 --- a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp @@ -105,6 +105,55 @@ void Core::Network::GameConnection::reqSearchInfoHandler( const Core::Network::P queueOutPacket( searchInfoPacket ); } +void Core::Network::GameConnection::reqExamineSearchCommentHandler( const Core::Network::Packets::FFXIVARR_PACKET_RAW& inPacket, + Entity::Player& player ) +{ + + auto targetId = *reinterpret_cast< const uint32_t* >( &inPacket.data[ 0x10 ] ); + auto pSession = g_fw.get< Core::ServerZone >()->getSession( targetId ); + + g_fw.get< Core::Logger >()->debug( std::to_string( targetId ) ); + + if( pSession ) + { + auto pPlayer = pSession->getPlayer(); + + if( pPlayer ) + { + // retail sends the requester's id as both (isForSelf) + auto searchInfoPacket = makeZonePacket< FFXIVIpcExamineSearchComment >( player.getId() ); + searchInfoPacket->data().charId = targetId; + strcpy( searchInfoPacket->data().searchComment, pPlayer->getSearchMessage() ); + player.queuePacket( searchInfoPacket ); + } + } +} + +void Core::Network::GameConnection::reqExamineFcInfo( const Core::Network::Packets::FFXIVARR_PACKET_RAW& inPacket, + Entity::Player& player ) +{ + + auto targetId = *reinterpret_cast< const uint32_t* >( &inPacket.data[ 0x18 ] ); + auto pSession = g_fw.get< Core::ServerZone >()->getSession( targetId ); + + g_fw.get< Core::Logger >()->debug( std::to_string( targetId ) ); + + if( pSession ) + { + auto pPlayer = pSession->getPlayer(); + + if( pPlayer ) + { + // retail sends the requester's id as both (isForSelf) + auto examineFcInfoPacket = makeZonePacket< FFXIVIpcExamineFreeCompanyInfo >( player.getId() ); + examineFcInfoPacket->data().charId = targetId; + // todo: populate with fc info + + player.queuePacket( examineFcInfoPacket ); + } + } +} + void Core::Network::GameConnection::linkshellListHandler( const Core::Network::Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player ) { diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/ExaminePacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/ExaminePacket.h new file mode 100644 index 00000000..c23cf0d5 --- /dev/null +++ b/src/servers/sapphire_zone/Network/PacketWrappers/ExaminePacket.h @@ -0,0 +1,78 @@ +#ifndef _CORE_NETWORK_PACKETS_EXAMINEPACKET_H +#define _CORE_NETWORK_PACKETS_EXAMINEPACKET_H + +#include +#include +#include +#include "Actor/Player.h" +#include "Forwards.h" +#include "Inventory/Item.h" +#include "StatusEffect/StatusEffect.h" + + +namespace Core { +namespace Network { +namespace Packets { +namespace Server { + +/** +* @brief The Examine response packet. +*/ +class ExaminePacket : + public ZoneChannelPacket< FFXIVIpcExamine > +{ +public: + ExaminePacket( Entity::Player& player, Entity::PlayerPtr pTarget ) : + ZoneChannelPacket< FFXIVIpcExamine >( pTarget->getId(), player.getId() ) + { + initialize( player, pTarget ); + }; + +private: + void initialize( Entity::Player& player, Entity::PlayerPtr pTarget ) + { + assert( pTarget ); + { + // todo: this packet needs mapping out + strcpy( m_data.name, pTarget->getName().c_str() ); + m_data.classJob = static_cast< uint8_t >( pTarget->getClass() ); + m_data.level = pTarget->getLevel(); + + m_data.unkFlag1 = 4; + m_data.unkFlag2 = 1; + + m_data.titleId = pTarget->getTitle(); + m_data.grandCompany = pTarget->getGc(); + m_data.grandCompanyRank = pTarget->getGcRankArray()[m_data.grandCompany]; + + m_data.mainWeaponModel = pTarget->getModelMainWeapon(); + m_data.secWeaponModel = pTarget->getModelSubWeapon(); + + memcpy( m_data.look, pTarget->getLookArray(), sizeof( m_data.look ) ); + for( auto i = 2; i < Common::GearSetSlot::SoulCrystal; ++i ) + m_data.models[ i - 2 ] = pTarget->getModelForSlot( static_cast< Common::GearSetSlot >( i ) ); + + // todo: main/sub/other stuff too + + for( auto i = 0; i < Common::GearSetSlot::SoulCrystal + 1; ++i ) + { + auto pItem = pTarget->getItemAt( Common::InventoryType::GearSet0, i ); + if( pItem ) + { + auto& entry = m_data.entries[i]; + entry.catalogId = pItem->getId(); + entry.quality = pItem->isHq(); + //entry.appearanceCatalogId = pItem->getGlamourId() + // todo: glamour/materia etc. + } + } + } + }; +}; + +} +} +} +} + +#endif /*_CORE_NETWORK_PACKETS_EXAMINEPACKET_H*/ diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h index 98ef4994..860faee8 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h @@ -53,11 +53,9 @@ private: m_data.mainWeaponModel = item->getModelId1(); m_data.secWeaponModel = player.getModelSubWeapon(); - m_data.models[ 0 ] = player.getModelForSlot( Common::GearSetSlot::Head ); - m_data.models[ 1 ] = player.getModelForSlot( Common::GearSetSlot::Body ); - m_data.models[ 2 ] = player.getModelForSlot( Common::GearSetSlot::Hands ); - m_data.models[ 3 ] = player.getModelForSlot( Common::GearSetSlot::Legs ); - m_data.models[ 4 ] = player.getModelForSlot( Common::GearSetSlot::Feet ); + for( auto i = 2; i < Common::GearSetSlot::SoulCrystal; ++i ) + m_data.models[ i - 2 ] = player.getModelForSlot( static_cast< Common::GearSetSlot >( i ) ); + strcpy( m_data.name, player.getName().c_str() ); m_data.pos.x = player.getPos().x;