diff --git a/src/common/Database/CharaDbConnection.cpp b/src/common/Database/CharaDbConnection.cpp index 5dfcd831..bfbc0dc8 100644 --- a/src/common/Database/CharaDbConnection.cpp +++ b/src/common/Database/CharaDbConnection.cpp @@ -133,7 +133,7 @@ void Core::Db::CharaDbConnection::doPrepareStatements() /// ITEM GLOBAL prepareStatement( CHARA_ITEMGLOBAL_INS, "INSERT INTO charaglobalitem ( CharacterId, ItemId, catalogId, UPDATE_DATE ) VALUES ( ?, ?, ?, NOW() );", CONNECTION_BOTH ); + // SOCIAL GROUPS - - + prepareStatement( CHARA_SOCIAL_FRIENDS_INS, "INSERT INTO charainfofriendlist ( CharacterId, CharacterIdList, InviteDataList, UPDATE_DATE ) VALUES ( ?, ?, ?, NOW() );", CONNECTION_SYNC ); // todo: maybe not sync } diff --git a/src/common/Database/CharaDbConnection.h b/src/common/Database/CharaDbConnection.h index 420bc7f7..02773cde 100644 --- a/src/common/Database/CharaDbConnection.h +++ b/src/common/Database/CharaDbConnection.h @@ -75,6 +75,8 @@ enum CharaDbStatements : uint32_t CHARA_ITEMGLOBAL_INS, + CHARA_SOCIAL_FRIENDS_INS, + MAX_STATEMENTS }; diff --git a/src/common/Network/PacketDef/Ipcs.h b/src/common/Network/PacketDef/Ipcs.h index 0c565c60..1614df6c 100644 --- a/src/common/Network/PacketDef/Ipcs.h +++ b/src/common/Network/PacketDef/Ipcs.h @@ -198,7 +198,7 @@ namespace Packets { CFDutyInfoHandler = 0x0078, // updated 4.2 - SocialReqSendHandler = 0x00AE, // updated 4.1 + SocialReqSendHandler = 0x00CA, // updated 4.2 ChatHandler = 0x00C7, // updated 4.2 diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index c86bbccf..d15a5db6 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -137,6 +137,7 @@ struct FFXIVIpcSocialRequestResponse : FFXIVIpcBasePacket uint32_t unknown; Common::SocialCategory category; // Common::SocialCategory Common::SocialRequestResponse response; // Common::SocialRequestResponse + uint8_t unknown2; // possibly padding char name[0x20]; uint16_t padding; }; diff --git a/src/servers/sapphire_api/PlayerMinimal.cpp b/src/servers/sapphire_api/PlayerMinimal.cpp index d3a1c6b9..7e1d6086 100644 --- a/src/servers/sapphire_api/PlayerMinimal.cpp +++ b/src/servers/sapphire_api/PlayerMinimal.cpp @@ -290,6 +290,11 @@ namespace Core { createInvDbContainer( InventoryType::Currency ); createInvDbContainer( InventoryType::Crystal ); + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// SET UP SOCIAL GROUPS + + createFriendsListContainer( m_id ); + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// SETUP EQUIPMENT / STARTING GEAR auto classJobInfo = g_exdDataGen.get< Core::Data::ClassJob >( m_class ); @@ -364,6 +369,21 @@ namespace Core { g_charaDb.directExecute( stmtCreateInv ); } + void PlayerMinimal::createFriendsListContainer( uint32_t characterId ) const + { + // todo: check if size is a-ok + std::vector< uint8_t > friendsList( 800 ); + std::vector< uint8_t > inviteDateList( 800 ); + + auto stmtCreateFrnList = g_charaDb.getPreparedStatement( Db::CHARA_SOCIAL_FRIENDS_INS ); + stmtCreateFrnList->setInt( 1, characterId ); + stmtCreateFrnList->setBinary( 2, friendsList ); + stmtCreateFrnList->setBinary( 3, inviteDateList ); + + g_charaDb.directExecute( stmtCreateFrnList ); + + } + uint64_t PlayerMinimal::getNextUId64() const { g_charaDb.directExecute( std::string( "INSERT INTO uniqueiddata( IdName ) VALUES( 'NOT_SET' );" ) ); diff --git a/src/servers/sapphire_api/PlayerMinimal.h b/src/servers/sapphire_api/PlayerMinimal.h index 38019e10..e0fe114d 100644 --- a/src/servers/sapphire_api/PlayerMinimal.h +++ b/src/servers/sapphire_api/PlayerMinimal.h @@ -152,6 +152,8 @@ namespace Core { void createInvDbContainer( uint16_t slot ) const; + void createFriendsListContainer( uint32_t characterId ) const; + uint32_t m_modelEquip[10]; uint64_t getNextUId64() const; diff --git a/src/servers/sapphire_zone/Actor/PlayerSql.cpp b/src/servers/sapphire_zone/Actor/PlayerSql.cpp index 745bb49c..7862165c 100644 --- a/src/servers/sapphire_zone/Actor/PlayerSql.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerSql.cpp @@ -26,6 +26,8 @@ #include "Forwards.h" #include "Framework.h" +#include "Social/Manager/SocialMgr.h" + extern Core::Framework g_fw; using namespace Core::Common; @@ -205,6 +207,9 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) calculateStats(); + m_friendsListId = g_fw.get< Social::SocialMgr < Social::FriendList > >()->loadFriendsList( m_id ); + pLog->debug( std::to_string( m_id ) + " ID, has group ID: " + std::to_string( m_friendsListId ) ); + // first login, run the script event if( m_bNewGame ) { diff --git a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp index 945ee886..2ae2704e 100644 --- a/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp +++ b/src/servers/sapphire_zone/Network/Handlers/PacketHandlers.cpp @@ -489,14 +489,15 @@ void Core::Network::GameConnection::socialListHandler( const Packets::GamePacket auto playerFriendsList = g_fw.get< Social::SocialMgr< Social::FriendList > >()->findGroupById( player.getFriendsListId() ); // todo: move this garbage else fucking where - for ( auto member : playerFriendsList.getMembers() ) + for ( auto member : playerFriendsList->getMembers() ) { // more elegant way to break over list entries pls if ( i == 10 ) break; - g_fw.get< Logger >()->debug( "aaa" + std::to_string( i ) + ": " + member.second.name ); - listPacket.data().entries[i] = Core::Social::Group::generatePlayerEntry( member.second ); + g_fw.get< Logger >()->debug( "aaa" + std::to_string( i ) ); + // todo: replace this with call for generating the entire vector + listPacket.data().entries[i] = Core::Social::FriendList::generatePlayerEntry( member, false ); i++; } @@ -554,7 +555,7 @@ void Core::Network::GameConnection::socialReqResponseHandler( const Packets::Gam if( pSession ) { - g_fw.get< Logger >()->debug( std::to_string(static_cast(action)) ); + g_fw.get< Logger >()->debug( std::to_string( static_cast< uint8_t >( action ) ) ); } response.data().response = Common::SocialRequestResponse::Accept; memcpy( &( response.data().name ), name.c_str(), 32 ); @@ -584,7 +585,7 @@ void Core::Network::GameConnection::socialReqSendHandler( const Packets::GamePac bool successful = false; Entity::PlayerPtr pRecipient = pSession->getPlayer(); - std::array typeVar{ "", "PartyInvite", "FriendInvite", "FreeCompanyPetition", "FreeCompanyInvite" }; + std::array< std::string, 5 > typeVar{ "", "PartyInvite", "FriendInvite", "FreeCompanyPetition", "FreeCompanyInvite" }; // todo: proper handling of invites already sent // todo: move this to world server @@ -647,7 +648,9 @@ void Core::Network::GameConnection::socialReqSendHandler( const Packets::GamePac { // todo: check if already on friends list or invite pending /* - if( pPlayer->getFriendList()->find(name) ) + auto playerFriendsList = g_fw.get< Social::SocialMgr< Social::FriendList > >()->findGroupById( pPlayer->getgetFriendsListId() ); + + if( playerFriendsList ) { response.data().messageId = 312; // That player is already a friend or has been sent a request. } @@ -672,11 +675,11 @@ void Core::Network::GameConnection::socialReqSendHandler( const Packets::GamePac { ZoneChannelPacket< FFXIVIpcSocialRequestReceive > packet( player.getId(), pRecipient->getId() ); - std::array typeMessage{ 0, + std::array< uint16_t, 5 > typeMessage{ 0, 1, // You invite to a party. - 10, // You send a friend request to . - 1884, // You invite to your free company. - 3044, // Free company petition signature request sent to + 2, // You send a friend request to . + 4, // Free company petition signature request sent to + 5, // You invite to your free company. }; // TODO: confirm the timers on retail @@ -690,7 +693,7 @@ void Core::Network::GameConnection::socialReqSendHandler( const Packets::GamePac packet.data().actorId = player.getId(); packet.data().category = category; packet.data().action = Core::Common::SocialRequestAction::Invite; - packet.data().unknown3 = 80; + packet.data().unknown3 = 80; // these seem like bitmasks! packet.data().unknown = 46; packet.data().unknown2 = 64; memcpy( &( packet.data().name ), player.getName().c_str(), 32 ); @@ -700,11 +703,21 @@ void Core::Network::GameConnection::socialReqSendHandler( const Packets::GamePac auto recipientFriendsList = g_fw.get< Social::SocialMgr< Social::FriendList > >()->findGroupById( pRecipient->getFriendsListId() ); - auto senderResultPacket = recipientFriendsList.inviteMember( player.getAsPlayer(), pRecipient, player.getId(), pRecipient->getId() ); + auto senderResultPacketResult = recipientFriendsList->addInvite( pRecipient->getId() ); + recipientFriendsList->addInvite( player.getId() ); + + auto senderResultPacket = GamePacketNew< Server::FFXIVIpcSocialRequestResponse, ServerZoneIpcType >( pRecipient->getId(), player.getId() ); + senderResultPacket.data().contentId = pRecipient->getId(); + senderResultPacket.data().category = Common::SocialCategory::Friends; + senderResultPacket.data().response = Common::SocialRequestResponse::Cancel; + + memcpy( &( senderResultPacket.data().name ), pRecipient->getName().c_str(), 32 ); + + //todo: build packet from packetresult here player.queuePacket( senderResultPacket ); - if ( recipientFriendsList.isFriendList() ) + if( recipientFriendsList->isFriendList() ) { g_fw.get< Logger >()->debug( "he HAA HAAA" ); } diff --git a/src/servers/sapphire_zone/ServerZone.cpp b/src/servers/sapphire_zone/ServerZone.cpp index adf350b6..7739e1a5 100644 --- a/src/servers/sapphire_zone/ServerZone.cpp +++ b/src/servers/sapphire_zone/ServerZone.cpp @@ -167,6 +167,7 @@ void Core::ServerZone::run( int32_t argc, char* argv[] ) auto pScript = g_fw.get< Scripting::ScriptMgr >(); auto pLsMgr = g_fw.get< LinkshellMgr >(); auto pTeriMgr = g_fw.get< TerritoryMgr >(); + auto pFriendListMgr = g_fw.get< Social::SocialMgr< Social::FriendList > >(); printBanner(); @@ -175,6 +176,14 @@ void Core::ServerZone::run( int32_t argc, char* argv[] ) pLog->fatal( "Unable to load settings!" ); return; } + + pLog->info( "Initializing social groups" ); + + if( !pFriendListMgr->init() ) + { + pLog->fatal( "Unable to initialize friend list manager!" ); + return; + } pLog->info( "LinkshellMgr: Caching linkshells" ); if( !pLsMgr->loadLinkshells() ) diff --git a/src/servers/sapphire_zone/Social/FriendList.cpp b/src/servers/sapphire_zone/Social/FriendList.cpp index 4ced6876..03485375 100644 --- a/src/servers/sapphire_zone/Social/FriendList.cpp +++ b/src/servers/sapphire_zone/Social/FriendList.cpp @@ -10,26 +10,130 @@ #include #include +#include #include #include "Group.h" #include "FriendList.h" +#include "Framework.h" + +extern Core::Framework g_fw; + using namespace Core::Network::Packets; using namespace Core::Network::Packets::Server; +using namespace Core::Social; + std::vector< PlayerEntry > Core::Social::FriendList::getFriendListEntries( uint16_t entryAmount ) { std::vector< PlayerEntry > entryList = {}; uint16_t limit = 0; - for ( const auto& member : m_members ) + for ( const auto& member : m_groupMembers ) { if ( limit == entryAmount ) break; - entryList.push_back( generatePlayerEntry( member.second ) ); + entryList.push_back( generatePlayerEntry( member, false ) ); + limit++; + } + + for ( const auto& invite : m_groupInvites ) + { + if ( limit == entryAmount ) + break; + + entryList.push_back( generatePlayerEntry( invite, true ) ); limit++; } return entryList; +} + + +//todo: generalize this for linkshell etc +Core::Network::Packets::Server::PlayerEntry FriendList::generatePlayerEntry( uint64_t characterId, bool isInvite ) +{ + // We check if player is online. If so, we can pull data from existing session in memory + // Otherwise, we pull from SQL. We can optimize this later, there are quite a few choices here + + auto pSession = g_fw.get< ServerZone >()->getSession( characterId ); + + // todo: set as offline in one of the unknown values, if session does not exist + Core::Network::Packets::Server::PlayerEntry entry = {}; + + entry.contentId = characterId; + entry.timestamp = 1512799339; + + // todo: if invite change these + entry.status = 2; + entry.unknown = 0; + //entry.entryIcon = 0xf; + entry.unavailable = 0; // unavailable (other world) + entry.one = 0; + + if ( pSession ) + { + auto pPlayer = pSession->getPlayer(); + //entry.contentId = pPlayer->getContentId(); + //entry.bytes[2] = pPlayer->getCurrentZone()->getId(); + + memcpy( entry.name, pPlayer->getName().c_str(), strlen( pPlayer->getName().c_str() ) ); + + entry.classJob = pPlayer->getClass(); + + entry.level = pPlayer->getLevel(); + entry.zoneId = pPlayer->getCurrentZone()->getGuId(); + entry.grandCompany = pPlayer->getGc(); + memcpy( &entry.fcTag[0], "Meme", 4 ); + entry.clientLanguage = 2; + entry.knownLanguages = 0x0F; + entry.onlineStatusMask = pPlayer->getOnlineStatusMask(); + + g_fw.get< Logger >()->debug( std::to_string( pPlayer->getContentId() ) ); + } + else + { + //todo: lets grab it from the db + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + + //const std::string char_id_str = std::to_string( characterId ); + + auto stmt = pDb->getPreparedStatement( Db::CharaDbStatements::CHARA_SEL ); + + stmt->setUInt( 1, characterId ); + auto res = pDb->query( stmt ); + + // todo: Is this correct? Seems so judging from retail + if ( !res->next() ) + { + entry.unavailable = 1; + } + else + { + // Todo: Can we make PlayerSQL a static function and use it for this? + + + auto name = res->getString( "Name" ); + strcpy( entry.name, name.c_str() ); + //memcpy( entry.name, res->getString( "Name" ), strlen( pPlayer->getName().c_str() ) ); + + //todo: pull actual FC name + memcpy( &entry.fcTag[0], "Ao", 4 ); + entry.clientLanguage = res->getUInt8( "Language" ); + entry.knownLanguages = 0x0F; + //entry.onlineStatusMask = pPlayer->getOnlineStatusMask(); + } + + + } + + // TODO: no idea what this does - me neither + //listPacket.data().entries[0].one = 1; + + g_fw.get< Logger >()->debug( std::to_string( characterId ) ); + + //g_fw.get< Logger >()->debug( std::to_string( entry.contentId ) ); + + return entry; } \ No newline at end of file diff --git a/src/servers/sapphire_zone/Social/FriendList.h b/src/servers/sapphire_zone/Social/FriendList.h index 58e27d5a..6a1608b2 100644 --- a/src/servers/sapphire_zone/Social/FriendList.h +++ b/src/servers/sapphire_zone/Social/FriendList.h @@ -25,6 +25,8 @@ public: std::vector< Network::Packets::Server::PlayerEntry > getFriendListEntries( uint16_t entryAmount ); + static Core::Network::Packets::Server::PlayerEntry generatePlayerEntry( uint64_t characterId, bool isInvite ); + protected: uint64_t m_id{ 0 }; uint64_t m_ownerId{ 0 }; diff --git a/src/servers/sapphire_zone/Social/Group.cpp b/src/servers/sapphire_zone/Social/Group.cpp index 06318636..22e46ae0 100644 --- a/src/servers/sapphire_zone/Social/Group.cpp +++ b/src/servers/sapphire_zone/Social/Group.cpp @@ -22,7 +22,64 @@ using namespace Core::Network; // todo: invite map in g_serverZone.getGroupMgr(GroupType) and look up -Core::Network::Packets::GamePacketPtr Group::addMember( Core::Entity::PlayerPtr pSender, Core::Entity::PlayerPtr pRecipient, uint64_t senderId, uint64_t recipientId ) + + +uint32_t Group::addMember( uint64_t characterId ) +{ + assert( characterId != 0 ); + + uint32_t logMessage = 0; + + m_groupMembers.insert( characterId ); + + return 0; +} + +uint32_t Group::addInvite( uint64_t characterId ) +{ + assert( characterId != 0 ); + + uint32_t logMessage = 0; + + m_groupInvites.insert( characterId ); + + return 0; +} + +Core::Network::Packets::GamePacketPtr Group::processInvite( uint64_t recipientId, uint64_t senderId ) +{ + constexpr uint32_t logMessages[] = { + 0, // + 1 + }; + assert( senderId != 0 ); + + using namespace Core::Network::Packets; + + uint64_t recipientContentId = 0; + + auto packet = GamePacketNew< Server::FFXIVIpcSocialRequestResponse, ServerZoneIpcType >( recipientId, senderId ); + packet.data().contentId = recipientContentId; + packet.data().category = Common::SocialCategory::Friends; + packet.data().response = Common::SocialRequestResponse::Accept; + //packet.data(). + + if ( m_members.size() < m_maxCapacity ) + { + // todo: broadcast join message + + m_groupInvites.erase( recipientId ); + + m_groupMembers.emplace( recipientId ); + } + else + { + } + + return packet; +} + +Core::Network::Packets::GamePacketPtr Group::addMember2( Core::Entity::PlayerPtr pSender, Core::Entity::PlayerPtr pRecipient, uint64_t senderId, uint64_t recipientId ) { constexpr uint32_t logMessages[] = { 0, // @@ -55,10 +112,10 @@ Core::Network::Packets::GamePacketPtr Group::addMember( Core::Entity::PlayerPtr m_invites.erase( m_invites.find( recipientId ), m_invites.end() ); GroupMember member; - member.inviterId = senderId; + //member.inviterId = senderId; member.role = 0; - member.contentId = recipientId; - member.name = pSender->getName(); + //member.contentId = recipientId; + //member.name = pSender->getName(); m_members.emplace( recipientId, member ); } else @@ -68,7 +125,7 @@ Core::Network::Packets::GamePacketPtr Group::addMember( Core::Entity::PlayerPtr return packet; } -Packets::GamePacketPtr Group::inviteMember( Core::Entity::PlayerPtr pSender, Core::Entity::PlayerPtr pRecipient, uint64_t senderId, uint64_t recipientId ) +Packets::GamePacketPtr Group::inviteMember2( Core::Entity::PlayerPtr pSender, Core::Entity::PlayerPtr pRecipient, uint64_t senderId, uint64_t recipientId ) { assert( pSender != nullptr || senderId != 0 ); @@ -77,13 +134,13 @@ Packets::GamePacketPtr Group::inviteMember( Core::Entity::PlayerPtr pSender, Cor packet.data().contentId = recipientId; packet.data().category = Common::SocialCategory::Friends; - if ( m_invites.size() < m_maxCapacity ) + if( m_invites.size() < m_maxCapacity ) { GroupMember member; - member.inviterId = senderId; + //member.inviterId = senderId; member.role = 0; - member.contentId = recipientId; - member.name = pSender->getName(); + //member.contentId = recipientId; + //member.name = pSender->getName(); m_invites.emplace( recipientId, member ); } @@ -92,7 +149,7 @@ Packets::GamePacketPtr Group::inviteMember( Core::Entity::PlayerPtr pSender, Cor } // todo: fix -Core::Network::Packets::GamePacketPtr Group::removeMember( Core::Entity::PlayerPtr pSender, Core::Entity::PlayerPtr pRecipient, uint64_t senderId, uint64_t recipientId ) +Core::Network::Packets::GamePacketPtr Group::removeMember2( Core::Entity::PlayerPtr pSender, Core::Entity::PlayerPtr pRecipient, uint64_t senderId, uint64_t recipientId ) { assert( pSender != nullptr || senderId != 0 ); @@ -108,7 +165,7 @@ void Group::sendPacketToMembers( Core::Network::Packets::GamePacketPtr pPacket, assert( pPacket ); for( const auto& member : m_members ) { - auto pSession = g_fw.get< ServerZone >()->getSession( member.second.name ); + auto pSession = g_fw.get< ServerZone >()->getSession( member.second.characterId ); if( pSession ) { pSession->getPlayer()->queuePacket( pPacket ); @@ -116,57 +173,16 @@ void Group::sendPacketToMembers( Core::Network::Packets::GamePacketPtr pPacket, } } -Core::Network::Packets::Server::PlayerEntry Group::generatePlayerEntry( GroupMember groupMember ) + + +std::set< uint64_t >& Group::getMembers() { - Core::Network::Packets::Server::PlayerEntry entry = {}; - - memcpy( entry.name, groupMember.name.c_str(), strlen( groupMember.name.c_str() ) ); - entry.contentId = groupMember.contentId; - - // We check if player is online. If so, we can pull more data - otherwise just name - // todo: set as offline in one of the unknown values, if session does not exist - - auto pSession = g_fw.get< ServerZone >()->getSession( groupMember.name ); // todo: aa i don't like this. maybe just store their ID instead of contentID??? - - entry.timestamp = 1512799339; - entry.status = 2; - entry.unknown = 0; - //entry.entryIcon = 0xf; - entry.unavailable = 0; // unavailable (other world) - entry.one = 0; - - if( pSession ) - { - auto pPlayer = pSession->getPlayer(); - entry.contentId = pPlayer->getContentId(); - //entry.bytes[2] = pPlayer->getCurrentZone()->getId(); - - entry.classJob = pPlayer->getClass(); - - entry.level = pPlayer->getLevel(); - entry.zoneId = pPlayer->getCurrentZone()->getGuId(); - entry.grandCompany = pPlayer->getGc(); - memcpy( &entry.fcTag[0], "Meme", 4 ); - entry.clientLanguage = 2; - entry.knownLanguages = 0x0F; - entry.onlineStatusMask = pPlayer->getOnlineStatusMask(); - - g_fw.get< Logger >()->debug( std::to_string( pPlayer->getContentId() ) ); - } - - // TODO: no idea what this does - me neither - //listPacket.data().entries[0].one = 1; - - g_fw.get< Logger >()->debug( std::to_string(groupMember.contentId) ); - - g_fw.get< Logger >()->debug( std::to_string( entry.contentId ) ); - - return entry; + return m_groupMembers; } -std::map< uint64_t, GroupMember > Group::getMembers() const +std::set< uint64_t >& Group::getInvites() { - return m_members; + return m_groupInvites; } uint32_t Group::getCapacity() const @@ -174,6 +190,11 @@ uint32_t Group::getCapacity() const return m_maxCapacity; } +uint32_t Group::getTotalSize() const +{ + return m_groupMembers.size() + m_groupInvites.size(); +} + bool Group::isParty() const { return m_type == GroupType::Party; diff --git a/src/servers/sapphire_zone/Social/Group.h b/src/servers/sapphire_zone/Social/Group.h index a664fd17..45f5fa16 100644 --- a/src/servers/sapphire_zone/Social/Group.h +++ b/src/servers/sapphire_zone/Social/Group.h @@ -16,9 +16,9 @@ namespace Social { struct GroupMember { - uint64_t inviterId; - uint64_t contentId; // todo: maybe just use id.. - std::string name; + //uint64_t inviterId; + uint64_t characterId; // todo: maybe just use id.. + //std::string name; uint32_t role; }; @@ -48,23 +48,38 @@ public: bool isBlacklist() const; bool isContentGroup() const; - virtual Core::Network::Packets::GamePacketPtr addMember( Entity::PlayerPtr pSender, Entity::PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); + // New group system: return error code for logmessage + //TODO: change the member models!!!!!!! + + virtual uint32_t addMember( uint64_t characterId ); + virtual uint32_t addInvite( uint64_t characterId ); - virtual Core::Network::Packets::GamePacketPtr inviteMember( Entity::PlayerPtr pSender, Entity::PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); - virtual Core::Network::Packets::GamePacketPtr removeMember( Entity::PlayerPtr pSender, Entity::PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); + virtual Core::Network::Packets::GamePacketPtr processInvite( uint64_t recipientId, uint64_t senderId ); + + virtual Core::Network::Packets::GamePacketPtr addMember2( Entity::PlayerPtr pSender, Entity::PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); + + virtual Core::Network::Packets::GamePacketPtr inviteMember2( Entity::PlayerPtr pSender, Entity::PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); + virtual Core::Network::Packets::GamePacketPtr removeMember2( Entity::PlayerPtr pSender, Entity::PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); //virtual Core::Network::Packets::GamePacketPtr kickMember( PlayerPtr pSender, PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); virtual void sendPacketToMembers( Core::Network::Packets::GamePacketPtr pPacket, bool invitesToo = false ); + //virtual void populateGroupMembers(); + /*! generates a player entry used for lists (social, etc) */ - static Core::Network::Packets::Server::PlayerEntry generatePlayerEntry( GroupMember groupMember ); + // - /*! access GroupMember vector */ + /*! access member vector */ + std::set< uint64_t >& getMembers(); - std::map< uint64_t, GroupMember > getMembers() const; + /*! access invite vector */ + std::set< uint64_t >& getInvites(); /*! get container limit */ uint32_t getCapacity() const; + /*! get total size of group (members + invites) */ + uint32_t Group::getTotalSize() const; + protected: GroupType m_type{ GroupType::None }; uint64_t m_id{ 0 }; @@ -72,6 +87,10 @@ protected: uint32_t m_maxCapacity{ 250 }; uint32_t m_maxRoles{ 50 }; std::chrono::steady_clock::time_point m_createTime{ std::chrono::steady_clock::now() }; + + std::set< uint64_t > m_groupMembers; + std::set< uint64_t > m_groupInvites; + std::map< uint64_t, GroupMember > m_members; std::map< uint64_t, GroupMember > m_invites; // diff --git a/src/servers/sapphire_zone/Social/Manager/SocialMgr.cpp b/src/servers/sapphire_zone/Social/Manager/SocialMgr.cpp index 83491513..6f58d310 100644 --- a/src/servers/sapphire_zone/Social/Manager/SocialMgr.cpp +++ b/src/servers/sapphire_zone/Social/Manager/SocialMgr.cpp @@ -5,57 +5,3 @@ #include #include "Forwards.h" - -template< class T > -Core::Social::SocialMgr< T >::SocialMgr() - : m_groupCount( 0 ), - m_lastGroupId( 0 ), - m_groups{} -{ -} - -template< class T > -Core::Social::SocialMgr< T >::~SocialMgr() -{ - -} - - -template< class T > -T Core::Social::SocialMgr< T >::findGroupByInviteIdForPlayer( uint64_t playerId ) const -{ - auto it = m_invites.find( playerId ); - if( it != m_invites.end() ) - { - return findGroupById( it->second ); - } - return nullptr; -} -template< class T > -T Core::Social::SocialMgr< T >::findGroupById( uint64_t groupId ) const -{ - auto it = m_groups.find( groupId ); - if( it != m_groups.end() ) - { - return it->second; - } - return nullptr; -} - -template< class T > -uint64_t Core::Social::SocialMgr< T >::generateGroupId() -{ - m_lastGroupId++; - return m_lastGroupId; -} - -template< class T > -bool Core::Social::SocialMgr< T >::hasInvite( uint64_t playerId ) const -{ - auto it = m_invites.find( playerId ); - if ( it != m_invites.end() ) - { - return true; - } - return false; -} diff --git a/src/servers/sapphire_zone/Social/Manager/SocialMgr.h b/src/servers/sapphire_zone/Social/Manager/SocialMgr.h index a0a0e1df..5e75c20e 100644 --- a/src/servers/sapphire_zone/Social/Manager/SocialMgr.h +++ b/src/servers/sapphire_zone/Social/Manager/SocialMgr.h @@ -7,10 +7,17 @@ #include #include +#include #include #include #include "Forwards.h" +#include +#include + +#include "Framework.h" + +extern Core::Framework g_fw; namespace Core { namespace Social { @@ -20,14 +27,32 @@ class SocialMgr { public: - SocialMgr(); - ~SocialMgr(); + SocialMgr() : m_groupCount( 0 ), + m_lastGroupId( 0 ), + m_groups{} + { - T findGroupByInviteIdForPlayer( uint64_t playerId ) const; - T findGroupById( uint64_t groupId ) const; - /* + } - T findGroup( uint64_t groupId ) + ~SocialMgr() + { + + } + + bool init(); + //bool< FriendList > init(); + + boost::shared_ptr< T > findGroupByInviteIdForPlayer( uint64_t playerId ) const + { + auto it = m_invites.find( playerId ); + if ( it != m_invites.end() ) + { + return findGroupById( it->second ); + } + return nullptr; + } + + boost::shared_ptr< T > findGroupById( uint64_t groupId ) const { auto it = m_groups.find( groupId ); if ( it != m_groups.end() ) @@ -35,37 +60,37 @@ public: return it->second; } return nullptr; - }*/ + } - bool hasInvite( uint64_t playerId ) const; + bool hasInvite( uint64_t playerId ) const + { + auto it = m_invites.find( playerId ); + if ( it != m_invites.end() ) + { + return true; + } + return false; + } + + uint64_t loadFriendsList( uint32_t characterId ); protected: // those would be implemented in T, so you'd have T.m_type and T.m_maxEntries // GroupType m_type{ GroupType::None }; // uint32_t m_maxEntries{ 0xFFFFFFFF }; + uint64_t generateGroupId() + { + m_lastGroupId++; + return m_lastGroupId; + } + uint64_t m_groupCount; std::map< uint64_t, uint64_t > m_invites; uint64_t m_lastGroupId; - // < recipient, groupid > - //virtual GroupPtr createGroup( PlayerPtr pOwner ) = 0; - - /* - friend virtual Core::Network::Packets::GamePacketPtr Core::Entity::Group::Group::addMember( PlayerPtr pSender, PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); - friend virtual Core::Network::Packets::GamePacketPtr Core::Entity::Group::Group::inviteMember( PlayerPtr pSender, PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); - friend virtual Core::Network::Packets::GamePacketPtr Core::Entity::Group::Group::removeMember( PlayerPtr pSender, PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); - friend virtual Core::Network::Packets::GamePacketPtr Core::Entity::Group::Group::kickMember( PlayerPtr pSender, PlayerPtr pRecipient, uint64_t senderId = 0, uint64_t recipientId = 0 ); - friend virtual void sendPacketToMembers( Core::Network::Packets::GamePacketPtr pPacket, bool invitesToo = false ); - - friend virtual void load(); - friend virtual void update(); - friend virtual void disband(); - */ - - uint64_t generateGroupId(); - std::map< uint64_t, T > m_groups; + std::map< uint64_t, boost::shared_ptr< T > > m_groups; private: @@ -74,4 +99,56 @@ private: } }; + +// Specialization + +template< class T > +bool Core::Social::SocialMgr< T >::init() +{ + return true; +} + +template<> inline +uint64_t Core::Social::SocialMgr< Core::Social::FriendList >::loadFriendsList( uint32_t characterId ) +{ + + auto pDb = g_fw.get< Db::DbWorkerPool< Db::CharaDbConnection > >(); + auto res = pDb->query( "SELECT CharacterId, CharacterIdList, InviteDataList " + "FROM charainfofriendlist " + "WHERE CharacterId = " + std::to_string( characterId ) ); + + + if ( !res->next() ) + return false; + + uint64_t ownerId = res->getUInt64( 1 ); + + auto groupID = generateGroupId(); + auto friendsList = Core::Social::FriendList( groupID, ownerId ); + + auto func = []( std::set< uint64_t >& outList, std::vector< char >& inData ) + { + if ( inData.size() ) + { + std::vector< uint64_t > list( inData.size() / 8 ); + outList.insert( list.begin(), list.end() ); + } + }; + + std::vector< char > friends; + friends = res->getBlobVector( 2 ); + func( friendsList.getMembers(), friends ); + + std::vector< char > friendInvites; + friendInvites = res->getBlobVector( 3 ); + func( friendsList.getInvites(), friendInvites ); + + auto friendListPtr = boost::make_shared< Core::Social::FriendList >( friendsList ); + + m_groups[groupID] = friendListPtr; + + return groupID; +} + + #endif /* ! _SOCIALMGR_H */