From 3e73da646351fb892da35c2b08ccf1268bc8a093 Mon Sep 17 00:00:00 2001 From: Alice Ogeda Date: Wed, 1 Dec 2021 21:30:12 -0300 Subject: [PATCH] add initial friendlist impl; fix teri crash on login if commonlist is called; fix sql type and initial data on player creation; --- .../20211201205038_FixFriendlistKey.sql | 2 + src/api/PlayerMinimal.cpp | 10 ++ src/common/Common.h | 28 +++- src/common/Network/PacketDef/ClientIpcs.h | 1 + .../Network/PacketDef/Zone/ClientZoneDef.h | 12 ++ .../Network/PacketDef/Zone/ServerZoneDef.h | 17 +- src/world/Actor/Player.cpp | 9 ++ src/world/Actor/Player.h | 10 +- src/world/Actor/PlayerSql.cpp | 6 +- src/world/Manager/FriendListMgr.cpp | 153 ++++++++++++++++++ src/world/Manager/FriendListMgr.h | 25 +++ src/world/Network/GameConnection.cpp | 2 + src/world/Network/GameConnection.h | 2 + .../Network/Handlers/CommonListHandler.cpp | 61 +++---- .../Network/Handlers/FriendListHandlers.cpp | 63 ++++++++ src/world/Network/Handlers/InviteHandlers.cpp | 74 ++++++--- src/world/Network/Handlers/PartyHandlers.cpp | 2 - src/world/WorldServer.cpp | 3 + 18 files changed, 413 insertions(+), 67 deletions(-) create mode 100644 sql/migrations/20211201205038_FixFriendlistKey.sql create mode 100644 src/world/Manager/FriendListMgr.cpp create mode 100644 src/world/Manager/FriendListMgr.h create mode 100644 src/world/Network/Handlers/FriendListHandlers.cpp diff --git a/sql/migrations/20211201205038_FixFriendlistKey.sql b/sql/migrations/20211201205038_FixFriendlistKey.sql new file mode 100644 index 00000000..42a51fab --- /dev/null +++ b/sql/migrations/20211201205038_FixFriendlistKey.sql @@ -0,0 +1,2 @@ +ALTER TABLE `charainfofriendlist` + CHANGE COLUMN `CharacterId` `CharacterId` BIGINT NOT NULL DEFAULT 0 FIRST; \ No newline at end of file diff --git a/src/api/PlayerMinimal.cpp b/src/api/PlayerMinimal.cpp index f11dab46..2a82747b 100644 --- a/src/api/PlayerMinimal.cpp +++ b/src/api/PlayerMinimal.cpp @@ -268,6 +268,16 @@ void PlayerMinimal::saveAsNew() stmtSearchInfo->setUInt64( 1, m_characterId ); g_charaDb.directExecute( stmtSearchInfo ); + // Friend list related + auto stmtFriendList = g_charaDb.getPreparedStatement( Db::ZoneDbStatements::CHARA_FRIENDLIST_INS ); + std::vector< uint8_t > friendIds( 1600, 0 ); + std::vector< uint8_t > inviteIds( 1600, 0 ); + + stmtFriendList->setUInt64( 1, m_characterId ); + stmtFriendList->setBinary( 2, friendIds ); + stmtFriendList->setBinary( 3, inviteIds ); + g_charaDb.directExecute( stmtFriendList ); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// SET UP INVENTORIES createInvDbContainer( InventoryType::Bag0 ); diff --git a/src/common/Common.h b/src/common/Common.h index 888f0123..32f1488e 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -24,7 +24,7 @@ namespace Sapphire::Common const uint16_t MAX_PLAYER_LEVEL = 80; const uint8_t CURRENT_EXPANSION_ID = 3; - const uint8_t CLASSJOB_TOTAL = 38; + const uint8_t CLASSJOB_TOTAL = 33; const uint8_t CLASSJOB_SLOTS = 20; const uint8_t TOWN_COUNT = 6; @@ -971,7 +971,7 @@ namespace Sapphire::Common InvincibilityIgnoreDamage, }; - enum InviteType : int32_t + enum HierarchyType : uint8_t { NONE_2 = 0x0, PCPARTY = 0x1, @@ -984,6 +984,14 @@ namespace Sapphire::Common MAX_1 = 0xFF, }; + enum HierarchyStatus : uint8_t + { + Invalid = 0, + Added = 0x10, + SentRequest = 0x20, + ReceivedRequest = 0x30 + }; + enum LinkshellHierarchy : int32_t { NONE_1 = 0x0, @@ -994,13 +1002,17 @@ namespace Sapphire::Common MAX_0 = 0x7, }; - - enum GroupEntryStatus : uint8_t + union HierarchyData { - Invalid = 0, - Added = 0x10, - SentRequest = 0x20, - ReceivedRequest = 0x30 + uint64_t u64; + + struct HierarchyPackedData { + uint32_t dateAdded; + uint8_t status; + uint8_t type; + uint8_t group; + uint8_t unk; + } data; }; /* 61719 */ diff --git a/src/common/Network/PacketDef/ClientIpcs.h b/src/common/Network/PacketDef/ClientIpcs.h index f36fdd6e..a65035ff 100644 --- a/src/common/Network/PacketDef/ClientIpcs.h +++ b/src/common/Network/PacketDef/ClientIpcs.h @@ -83,6 +83,7 @@ namespace Sapphire::Network::Packets PcSearch = 0xEB, GetFcJoinRequestComment = 0xEC, InviteCancel = 0xED, + SetFriendlistGroup = 0xEE, LinkshellJoin = 0xF0, LinkshellJoinOfficial = 0xF1, LinkshellLeave = 0xF2, diff --git a/src/common/Network/PacketDef/Zone/ClientZoneDef.h b/src/common/Network/PacketDef/Zone/ClientZoneDef.h index b8ad1167..b08ec3d0 100644 --- a/src/common/Network/PacketDef/Zone/ClientZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ClientZoneDef.h @@ -579,6 +579,18 @@ struct FFXIVIpcGetCommonlistDetail : FFXIVIpcBasePacket< GetCommonlistDetail > uint8_t ListType; }; +struct FFXIVIpcFriendlistRemove : FFXIVIpcBasePacket< FriendlistRemove > +{ + uint64_t TargetCharacterID; + char TargetCharacterName[32]; +}; + +struct FFXIVIpcSetFriendlistGroup : FFXIVIpcBasePacket< SetFriendlistGroup > +{ + uint64_t TargetCharacterID; + uint8_t group; +}; + struct FFXIVIpcPcSearch : FFXIVIpcBasePacket< PcSearch > { uint64_t ClassID; diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index e5603ccd..3137f8e4 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -58,12 +58,17 @@ namespace Sapphire::Network::Packets::WorldPackets::Server /** * Structural representation of the packet sent by the server * with a list of players ( party list | friend list | search results ) + * 3.0 Update: 64bit TerritoryID -> 32bit Timestamp, 32bit TerritoryID */ struct PlayerEntry { uint64_t CharacterID; - uint64_t TerritoryID; - uint32_t HierarchyID; + uint32_t Timestamp; + uint32_t TerritoryID; + uint8_t HierarchyStatus; + uint8_t HierarchyType; + uint8_t HierarchyGroup; + uint8_t HierarchyUnk; uint16_t TerritoryType; uint8_t GrandCompanyID; uint8_t Region; @@ -144,6 +149,14 @@ namespace Sapphire::Network::Packets::WorldPackets::Server char InviteName[32]; }; + struct FFXIVIpcFriendlistRemoveResult : FFXIVIpcBasePacket< FriendlistRemoveResult > + { + uint64_t RemovedCharacterID; + uint32_t Result; + uint8_t Identity; + char RemovedCharacterName[32]; + }; + struct FFXIVIpcGetFcStatusResult : FFXIVIpcBasePacket< GetFcStatusResult > { uint64_t FreeCompanyID; diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index ca7eead9..0e881f39 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -2138,6 +2138,15 @@ void Sapphire::Entity::Player::setPartyId( uint64_t partyId ) m_partyId = partyId; } +Sapphire::Entity::Player::FriendListIDVec& Sapphire::Entity::Player::getFriendListID() +{ + return m_friendList; +} +Sapphire::Entity::Player::FriendListDataVec& Sapphire::Entity::Player::getFriendListData() +{ + return m_friendInviteList; +} + void Sapphire::Entity::Player::setLastPcSearchResult( std::vector< uint32_t > result ) { m_lastPcSearch = std::move( result ); diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 5f18edd6..7f978814 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -799,6 +799,9 @@ namespace Sapphire::Entity ////////////////////////////////////////////////////////////////////////////////////////////////////// + using FriendListIDVec = std::array< uint64_t, 200 >; + using FriendListDataVec = std::array< Common::HierarchyData, 200 >; + Common::HuntingLogEntry& getHuntingLogEntry( uint8_t index ); void sendHuntingLog(); @@ -808,6 +811,9 @@ namespace Sapphire::Entity uint64_t getPartyId() const; void setPartyId( uint64_t partyId ); + FriendListIDVec& getFriendListID(); + FriendListDataVec& getFriendListData(); + uint64_t m_lastMoveTime{}; uint8_t m_lastMoveflag{}; bool m_falling; @@ -963,8 +969,8 @@ namespace Sapphire::Entity std::array< Common::HuntingLogEntry, 12 > m_huntingLogEntries{}; - std::array< uint64_t, 200 > m_friendList{}; - std::array< uint64_t, 200 > m_friendInviteList{}; + FriendListIDVec m_friendList{}; + FriendListDataVec m_friendInviteList{}; uint64_t m_partyId; std::vector< uint32_t > m_lastPcSearch; diff --git a/src/world/Actor/PlayerSql.cpp b/src/world/Actor/PlayerSql.cpp index 37d439df..f1ffa8ff 100644 --- a/src/world/Actor/PlayerSql.cpp +++ b/src/world/Actor/PlayerSql.cpp @@ -462,12 +462,12 @@ void Sapphire::Entity::Player::updateDbFriendList() auto stmt = db.getPreparedStatement( Db::CHARA_FRIENDLIST_UP ); std::vector< uint8_t > friendIds( 1600 ); - std::vector< uint8_t > InviteIds( 1600 ); + std::vector< uint8_t > inviteIds( 1600 ); memcpy( friendIds.data(), m_friendList.data(), 1600 ); - memcpy( InviteIds.data(), m_friendInviteList.data(), 1600 ); + memcpy( inviteIds.data(), m_friendInviteList.data(), 1600 ); stmt->setBinary( 1, friendIds ); - stmt->setBinary( 2, InviteIds ); + stmt->setBinary( 2, inviteIds ); stmt->setUInt64( 3, m_characterId ); db.execute( stmt ); } diff --git a/src/world/Manager/FriendListMgr.cpp b/src/world/Manager/FriendListMgr.cpp new file mode 100644 index 00000000..e6cad69d --- /dev/null +++ b/src/world/Manager/FriendListMgr.cpp @@ -0,0 +1,153 @@ +#include +#include + +#include +#include +#include + +#include "Actor/Player.h" +#include "FriendListMgr.h" + +bool Sapphire::World::Manager::FriendListMgr::onInviteCreate( Entity::Player& source, Entity::Player& target ) +{ + auto& sourceFL = source.getFriendListID(); + auto& targetFL = target.getFriendListID(); + + // check if player already has been invited or friends + if( getEntryIndex( source, target.getCharacterId() ) != -1 ) + { + // already invited/friends + return false; + } + + // check if there are slots available to allocate and get idx + auto sourceIdx = getEntryIndex( source, 0 ); + auto targetIdx = getEntryIndex( target, 0 ); + + if( sourceIdx == -1 || targetIdx == -1 ) + { + // todo: complain about having too many friends + return false; + } + + auto& sourceFLData = source.getFriendListData(); + auto& targetFLData = target.getFriendListData(); + + // add ID and data to arrays of both players + sourceFL[sourceIdx] = target.getCharacterId(); + targetFL[targetIdx] = source.getCharacterId(); + + + Common::HierarchyData hierarchy; + hierarchy.data.dateAdded = Common::Util::getTimeSeconds(); + hierarchy.data.group = 0; + hierarchy.data.status = Common::HierarchyStatus::SentRequest; // set type for invite sender + hierarchy.data.type = Common::HierarchyType::FRIENDLIST; + + sourceFLData[sourceIdx] = hierarchy; + + // set type for invite receiver + hierarchy.data.status = Common::HierarchyStatus::ReceivedRequest; + targetFLData[targetIdx] = hierarchy; + + // force db update for friendlist + source.updateDbFriendList(); + target.updateDbFriendList(); + + return true; +} + +bool Sapphire::World::Manager::FriendListMgr::onInviteAccept( Entity::Player& source, Entity::Player& target ) +{ + // accept friend invite + auto sourceIdx = getEntryIndex( source, target.getCharacterId() ); + auto targetIdx = getEntryIndex( target, source.getCharacterId() ); + + if( sourceIdx == -1 || targetIdx == -1 ) + { + // currently not friends + return false; + } + + auto& sourceFLData = source.getFriendListData(); + auto& targetFLData = target.getFriendListData(); + + sourceFLData[sourceIdx].data.status = Common::HierarchyStatus::Added; + sourceFLData[sourceIdx].data.type = Common::HierarchyType::NONE_2; + targetFLData[targetIdx].data.status = Common::HierarchyStatus::Added; + targetFLData[targetIdx].data.type = Common::HierarchyType::NONE_2; + + source.updateDbFriendList(); + target.updateDbFriendList(); + return true; +} + +bool Sapphire::World::Manager::FriendListMgr::onInviteDecline( Entity::Player& source, Entity::Player& target ) +{ + // decline friend invite + auto sourceIdx = getEntryIndex( source, target.getCharacterId() ); + auto targetIdx = getEntryIndex( target, source.getCharacterId() ); + + if( sourceIdx == -1 || targetIdx == -1 ) + { + // currently not friends + return false; + } + + auto& sourceFL = source.getFriendListID(); + auto& targetFL = target.getFriendListID(); + + auto& sourceFLData = source.getFriendListData(); + auto& targetFLData = target.getFriendListData(); + + sourceFL[sourceIdx] = 0; + targetFL[targetIdx] = 0; + + sourceFLData[sourceIdx].u64 = 0; + targetFLData[targetIdx].u64 = 0; + + source.updateDbFriendList(); + target.updateDbFriendList(); + return true; +} + +bool Sapphire::World::Manager::FriendListMgr::onRemoveFriend( Entity::Player& source, Entity::Player& target ) +{ + // remove friend + // this not retail accurate - retail only removes source friendlist, but that also makes it more complicated for readding friend + // this will simply remove the entry from both players + + return onInviteDecline( source, target ); +} + +bool Sapphire::World::Manager::FriendListMgr::onAssignGroup( Entity::Player& source, Entity::Player& target, uint8_t group ) +{ + // assign group to friend entry (to source only) + auto sourceIdx = getEntryIndex( source, target.getCharacterId() ); + + if( sourceIdx == -1 ) + { + // currently not friends + return false; + } + + auto& sourceFLData = source.getFriendListData(); + + sourceFLData[sourceIdx].data.group = group; + + source.updateDbFriendList(); + + return true; +} + +ptrdiff_t Sapphire::World::Manager::FriendListMgr::getEntryIndex( Entity::Player& source, uint64_t characterId ) +{ + auto& sourceFL = source.getFriendListID(); + auto sourceInvIt = std::find( std::begin( sourceFL ), std::end( sourceFL ), characterId ); + + // not found + if( sourceInvIt == sourceFL.end() ) + return -1; + + return sourceInvIt - std::begin( sourceFL ); +} diff --git a/src/world/Manager/FriendListMgr.h b/src/world/Manager/FriendListMgr.h new file mode 100644 index 00000000..7bf32d40 --- /dev/null +++ b/src/world/Manager/FriendListMgr.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#include "ForwardsZone.h" + +namespace Sapphire::World::Manager +{ + class FriendListMgr + { + public: + FriendListMgr() = default; + + bool onInviteCreate( Entity::Player& source, Entity::Player& target ); + + bool onInviteAccept( Entity::Player& source, Entity::Player& target ); + bool onInviteDecline( Entity::Player& source, Entity::Player& target ); + + bool onRemoveFriend( Entity::Player& source, Entity::Player& target ); + bool onAssignGroup( Entity::Player& source, Entity::Player& target, uint8_t group ); + + private: + ptrdiff_t getEntryIndex( Entity::Player& source, uint64_t characterId ); + }; +} diff --git a/src/world/Network/GameConnection.cpp b/src/world/Network/GameConnection.cpp index 6cb2e40f..d566fb99 100644 --- a/src/world/Network/GameConnection.cpp +++ b/src/world/Network/GameConnection.cpp @@ -141,6 +141,8 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH setZoneHandler( PcPartyKick, "PcPartyKick", &GameConnection::pcPartyKickHandler ); setZoneHandler( PcPartyChangeLeader, "PcPartyChangeLeader", &GameConnection::pcPartyChangeLeaderHandler ); + setZoneHandler( FriendlistRemove, "FriendlistRemove", &GameConnection::friendlistRemoveHandler ); + setZoneHandler( SetFriendlistGroup, "SetFriendlistGroup", &GameConnection::setFriendlistGroupHandler ); } Sapphire::Network::GameConnection::~GameConnection() = default; diff --git a/src/world/Network/GameConnection.h b/src/world/Network/GameConnection.h index 3dc9fd82..e67b52b5 100644 --- a/src/world/Network/GameConnection.h +++ b/src/world/Network/GameConnection.h @@ -217,6 +217,8 @@ namespace Sapphire::Network DECLARE_HANDLER( pcPartyKickHandler ); DECLARE_HANDLER( pcPartyChangeLeaderHandler ); + DECLARE_HANDLER( friendlistRemoveHandler ); + DECLARE_HANDLER( setFriendlistGroupHandler ); }; } diff --git a/src/world/Network/Handlers/CommonListHandler.cpp b/src/world/Network/Handlers/CommonListHandler.cpp index 2e4fe12f..5d9bd0cc 100644 --- a/src/world/Network/Handlers/CommonListHandler.cpp +++ b/src/world/Network/Handlers/CommonListHandler.cpp @@ -58,10 +58,9 @@ void Sapphire::Network::GameConnection::getCommonlistDetailHandler( const Packet // serialize class data to packet - // todo: fix 28 (common::classjob count does not match packet size) auto classDataArr = pPlayer->getClassArray(); - for( size_t i = 0; i < 28; ++i ) + for( size_t i = 0; i < Common::CLASSJOB_TOTAL; ++i ) { resultPacket->data().ClassData[ i ].id = static_cast< uint16_t >( i ); resultPacket->data().ClassData[ i ].level = classDataArr[ i ]; @@ -76,7 +75,8 @@ void Sapphire::Network::GameConnection::getCommonlistHandler( const Packets::FFX // TODO: possibly move lambda func to util auto& server = Common::Service< World::WorldServer >::ref(); - auto generateEntries = [&]( const auto& idVec, size_t offset ) -> std::vector< PlayerEntry > + // this func paginates any commonlist entry, associating them with online player data and hierarchy ID (optional) + auto generateEntries = [&]( const auto& idVec, size_t offset, const std::vector< Common::HierarchyData >& hierarchyVec ) -> std::vector< PlayerEntry > { std::vector< PlayerEntry > entries; @@ -90,8 +90,8 @@ void Sapphire::Network::GameConnection::getCommonlistHandler( const Packets::FFX } auto id = idVec[ i ]; - auto pPlayer = server.getPlayer( id ); + if( !pPlayer ) continue; @@ -101,8 +101,14 @@ void Sapphire::Network::GameConnection::getCommonlistHandler( const Packets::FFX if( isConnected ) { - entry.TerritoryType = pPlayer->getCurrentTerritory()->getTerritoryTypeId(); - entry.TerritoryID = pPlayer->getCurrentTerritory()->getTerritoryTypeId(); + // todo: fix odd teri nullptr on login for friendlist etc + auto pTeri = pPlayer->getCurrentTerritory(); + if( pTeri ) + { + entry.TerritoryType = pPlayer->getCurrentTerritory()->getTerritoryTypeId(); + entry.TerritoryID = pPlayer->getCurrentTerritory()->getTerritoryTypeId(); + } + entry.CurrentClassID = static_cast< uint8_t >( pPlayer->getClass() ); entry.SelectClassID = static_cast< uint8_t >( pPlayer->getSearchSelectClass() ); @@ -122,10 +128,20 @@ void Sapphire::Network::GameConnection::getCommonlistHandler( const Packets::FFX entry.CharacterID = pPlayer->getCharacterId(); strcpy( entry.CharacterName, pPlayer->getName().c_str() ); + if( hierarchyVec.size() > 0 ) + { + auto hierarchy = hierarchyVec[ i ]; + + entry.Timestamp = hierarchy.data.dateAdded; + entry.HierarchyStatus = hierarchy.data.status; + entry.HierarchyType = hierarchy.data.type; + entry.HierarchyGroup = hierarchy.data.group; + entry.HierarchyUnk = hierarchy.data.unk; + } + entries.emplace_back( entry ); } - return entries; }; @@ -156,7 +172,7 @@ void Sapphire::Network::GameConnection::getCommonlistHandler( const Packets::FFX auto pParty = partyMgr.getParty( player.getPartyId() ); assert( pParty ); - page = generateEntries( pParty->MemberId, offset ); + page = generateEntries( pParty->MemberId, offset, {} ); // ensure first entry is the player requesting packet for( int i = 0; i < 8; ++i ) @@ -172,32 +188,17 @@ void Sapphire::Network::GameConnection::getCommonlistHandler( const Packets::FFX else { std::vector< uint32_t > soloParty = { player.getId() }; - page = generateEntries( soloParty, offset ); + page = generateEntries( soloParty, offset, {} ); } } else if( data.ListType == 0x0b ) { // friend list - /* listPacket->data().entries[ 0 ].TerritoryType = player.getCurrentTerritory()->getTerritoryTypeId(); - listPacket->data().entries[ 0 ].TerritoryID = player.getCurrentTerritory()->getTerritoryTypeId(); - listPacket->data().entries[ 0 ].CurrentClassID = static_cast< uint8_t >( player.getClass() ); - listPacket->data().entries[ 0 ].SelectClassID = static_cast< uint8_t >( player.getClass() ); - listPacket->data().entries[ 0 ].CharacterID = player.getContentId(); - listPacket->data().entries[ 0 ].CurrentLevel = player.getLevel(); - listPacket->data().entries[ 0 ].SelectLevel = player.getLevel(); - listPacket->data().entries[ 0 ].Identity = 1; + auto& friendList = player.getFriendListID(); + auto& friendListData = player.getFriendListData(); - strcpy( listPacket->data().entries[ 0 ].CharacterName, player.getName().c_str() ); + std::vector< Common::HierarchyData > hierarchyData( friendListData.begin(), friendListData.end() ); - // GC icon - listPacket->data().entries[ 0 ].GrandCompanyID = 2; - // client language J = 0, E = 1, D = 2, F = 3 - listPacket->data().entries[ 0 ].Region = 1; - // user language settings flag J = 1, E = 2, D = 4, F = 8 - listPacket->data().entries[ 0 ].SelectRegion = 1 + 2; - listPacket->data().entries[ 0 ].OnlineStatus = player.getOnlineStatusMask(); - listPacket->data().entries[ 0 ].HierarchyID = 0x20;*/ - offset = 0; - isLast = true; + page = generateEntries( friendList, offset, hierarchyData ); } else if( data.ListType == 0x0c ) { // linkshell @@ -213,13 +214,13 @@ void Sapphire::Network::GameConnection::getCommonlistHandler( const Packets::FFX std::vector< uint64_t > memberVec; std::copy( memberSet.begin(), memberSet.end(), std::back_inserter( memberVec ) ); - page = generateEntries( memberVec, offset ); + page = generateEntries( memberVec, offset, {} ); } } else if( data.ListType == 0x0e ) { // player search result auto queryPlayers = player.getLastPcSearchResult(); - page = generateEntries( queryPlayers, offset ); + page = generateEntries( queryPlayers, offset, {} ); } // if we didn't manually terminate pagination (party, etc), check if we need to do so diff --git a/src/world/Network/Handlers/FriendListHandlers.cpp b/src/world/Network/Handlers/FriendListHandlers.cpp new file mode 100644 index 00000000..72175287 --- /dev/null +++ b/src/world/Network/Handlers/FriendListHandlers.cpp @@ -0,0 +1,63 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "Manager/FriendListMgr.h" + +#include "Network/GameConnection.h" + +#include "Actor/Player.h" +#include "WorldServer.h" + +using namespace Sapphire::Common; +using namespace Sapphire::Network::Packets; +using namespace Sapphire::Network::Packets::WorldPackets; +using namespace Sapphire::World::Manager; + +void Sapphire::Network::GameConnection::friendlistRemoveHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, + Entity::Player& player ) +{ + const auto packet = ZoneChannelPacket< Client::FFXIVIpcFriendlistRemove >( inPacket ); + const auto& data = packet.data(); + + auto& server = Common::Service< Sapphire::World::WorldServer >::ref(); + auto& flMgr = Common::Service< Sapphire::World::Manager::FriendListMgr >::ref(); + + auto target = server.getPlayer( data.TargetCharacterID ); + if( !target ) + return; + + flMgr.onRemoveFriend( player, *target ); + + auto replyPacket = makeZonePacket< WorldPackets::Server::FFXIVIpcFriendlistRemoveResult >( player.getId() ); + auto& replyData = replyPacket->data(); + + replyData.RemovedCharacterID = target->getCharacterId(); + replyData.Identity = target->getGender(); + replyData.Result = 0; + strcpy( replyData.RemovedCharacterName, target->getName().c_str() ); + + server.queueForPlayer( player.getCharacterId(), replyPacket ); +} + +void Sapphire::Network::GameConnection::setFriendlistGroupHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, + Entity::Player& player ) +{ + const auto packet = ZoneChannelPacket< Client::FFXIVIpcSetFriendlistGroup >( inPacket ); + const auto& data = packet.data(); + + auto& server = Common::Service< Sapphire::World::WorldServer >::ref(); + auto& flMgr = Common::Service< Sapphire::World::Manager::FriendListMgr >::ref(); + + auto target = server.getPlayer( data.TargetCharacterID ); + if( !target ) + return; + + flMgr.onAssignGroup( player, *target, data.group ); +} \ No newline at end of file diff --git a/src/world/Network/Handlers/InviteHandlers.cpp b/src/world/Network/Handlers/InviteHandlers.cpp index 98a2f37f..b10ef3ba 100644 --- a/src/world/Network/Handlers/InviteHandlers.cpp +++ b/src/world/Network/Handlers/InviteHandlers.cpp @@ -20,6 +20,7 @@ #include "Network/PacketWrappers/PlayerSetupPacket.h" #include "Network/PacketWrappers/InviteUpdatePacket.h" +#include "Manager/FriendListMgr.h" #include "Manager/PartyMgr.h" #include "Manager/PlayerMgr.h" @@ -55,7 +56,7 @@ void Sapphire::Network::GameConnection::inviteHandler( const Sapphire::Network:: switch( packet.data().AuthType ) { - case InviteType::PCPARTY: + case HierarchyType::PCPARTY: { auto inviteResultPacket = makeZonePacket< WorldPackets::Server::FFXIVIpcInviteResult >( player.getId() ); auto& data = inviteResultPacket->data(); @@ -71,7 +72,7 @@ void Sapphire::Network::GameConnection::inviteHandler( const Sapphire::Network:: break; } - case InviteType::FRIENDLIST: + case HierarchyType::FRIENDLIST: { auto inviteResultPacket = makeZonePacket< WorldPackets::Server::FFXIVIpcInviteResult >( player.getId() ); auto& data = inviteResultPacket->data(); @@ -79,22 +80,31 @@ void Sapphire::Network::GameConnection::inviteHandler( const Sapphire::Network:: strcpy( data.TargetName, packet.data().TargetName ); server.queueForPlayer( player.getCharacterId(), inviteResultPacket ); - auto invitePacket = std::make_shared< InviteUpdatePacket >( player, Common::Util::getTimeSeconds() + 30, + auto invitePacket = std::make_shared< InviteUpdatePacket >( player, 0, packet.data().AuthType, 1, InviteUpdateType::NEW_INVITE ); pSession->getZoneConnection()->queueOutPacket( invitePacket ); + auto& flMgr = Common::Service< FriendListMgr >::ref(); + + // add support to adding offline players + auto target = server.getPlayer( data.TargetName ); + if( !target ) + return; + + flMgr.onInviteCreate( player, *target ); + break; } - case Common::InviteType::AUTOPARTY: + case HierarchyType::AUTOPARTY: break; - case Common::InviteType::FCCREATE: + case HierarchyType::FCCREATE: break; - case Common::InviteType::FREECOMPANY: + case HierarchyType::FREECOMPANY: break; - case Common::InviteType::FCJOINREQUEST: + case HierarchyType::FCJOINREQUEST: break; - case Common::InviteType::PARTYCANCEL: + case HierarchyType::PARTYCANCEL: break; } } @@ -108,19 +118,19 @@ void Sapphire::Network::GameConnection::inviteReplyHandler( const FFXIVARR_PACKE auto& server = Common::Service< Sapphire::World::WorldServer >::ref(); auto pSession = server.getSession( data.InviteCharacterID ); - auto& partyMgr = Common::Service< Sapphire::World::Manager::PartyMgr >::ref(); - if( !pSession ) return; + auto inviteReplyPacket = makeZonePacket< WorldPackets::Server::FFXIVIpcInviteReplyResult >( player.getId() ); + auto& inviteReplyData = inviteReplyPacket->data(); + inviteReplyData.Answer = data.Answer; + switch( data.AuthType ) { - case Common::InviteType::PCPARTY: + case HierarchyType::PCPARTY: { - auto inviteReplyPacket = makeZonePacket< WorldPackets::Server::FFXIVIpcInviteReplyResult >( player.getId() ); - auto& inviteReplyData = inviteReplyPacket->data(); + auto& partyMgr = Common::Service< Sapphire::World::Manager::PartyMgr >::ref(); - inviteReplyData.Answer = data.Answer; uint8_t result; if( data.Answer == InviteReplyType::ACCEPT ) { @@ -142,17 +152,41 @@ void Sapphire::Network::GameConnection::inviteReplyHandler( const FFXIVARR_PACKE break; } - case Common::InviteType::FRIENDLIST: + case HierarchyType::FRIENDLIST: + { + auto& flMgr = Common::Service< Sapphire::World::Manager::FriendListMgr >::ref(); + + uint8_t result; + if( data.Answer == InviteReplyType::ACCEPT ) + { + flMgr.onInviteAccept( player, *pSession->getPlayer() ); + result = InviteUpdateType::ACCEPT_INVITE; + } + else + { + flMgr.onInviteDecline( player, *pSession->getPlayer() ); + result = InviteUpdateType::REJECT_INVITE; + } + + auto inviteUpPacket = std::make_shared< InviteUpdatePacket >( player, 0, + data.AuthType, 1, result ); + pSession->getZoneConnection()->queueOutPacket( inviteUpPacket ); + + inviteReplyData.AuthType = data.AuthType; + strcpy( inviteReplyData.InviteCharacterName, pSession->getPlayer()->getName().c_str() ); + server.queueForPlayer( player.getCharacterId(), inviteReplyPacket ); + break; - case Common::InviteType::AUTOPARTY: + } + case HierarchyType::AUTOPARTY: break; - case Common::InviteType::FCCREATE: + case HierarchyType::FCCREATE: break; - case Common::InviteType::FREECOMPANY: + case HierarchyType::FREECOMPANY: break; - case Common::InviteType::FCJOINREQUEST: + case HierarchyType::FCJOINREQUEST: break; - case Common::InviteType::PARTYCANCEL: + case HierarchyType::PARTYCANCEL: break; } } diff --git a/src/world/Network/Handlers/PartyHandlers.cpp b/src/world/Network/Handlers/PartyHandlers.cpp index ff5d586b..82e126e0 100644 --- a/src/world/Network/Handlers/PartyHandlers.cpp +++ b/src/world/Network/Handlers/PartyHandlers.cpp @@ -9,8 +9,6 @@ #include "Manager/PartyMgr.h" -#include "Territory/InstanceContent.h" - #include "Network/GameConnection.h" #include "Session.h" diff --git a/src/world/WorldServer.cpp b/src/world/WorldServer.cpp index b7a9d310..18067c84 100644 --- a/src/world/WorldServer.cpp +++ b/src/world/WorldServer.cpp @@ -42,6 +42,7 @@ #include "Manager/ChatChannelMgr.h" #include "Manager/QuestMgr.h" #include "Manager/PartyMgr.h" +#include "Manager/FriendListMgr.h" #include "ContentFinder/ContentFinder.h" @@ -238,6 +239,7 @@ void Sapphire::World::WorldServer::run( int32_t argc, char* argv[] ) auto pRNGMgr = std::make_shared< Manager::RNGMgr >(); auto pQuestMgr = std::make_shared< Manager::QuestMgr >(); auto pPartyMgr = std::make_shared< Manager::PartyMgr >(); + auto pFriendMgr = std::make_shared< Manager::FriendListMgr >(); auto contentFinder = std::make_shared< ContentFinder >(); Common::Service< DebugCommandMgr >::set( pDebugCom ); @@ -249,6 +251,7 @@ void Sapphire::World::WorldServer::run( int32_t argc, char* argv[] ) Common::Service< Manager::RNGMgr >::set( pRNGMgr ); Common::Service< Manager::QuestMgr >::set( pQuestMgr ); Common::Service< Manager::PartyMgr >::set( pPartyMgr ); + Common::Service< Manager::FriendListMgr >::set( pFriendMgr ); Common::Service< ContentFinder >::set( contentFinder ); auto& exdData = Common::Service< Sapphire::Data::ExdData >::ref();