2021-11-27 00:53:57 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <iterator>
|
|
|
|
|
2021-12-03 22:21:18 +01:00
|
|
|
#include <Common.h>
|
|
|
|
#include <Exd/ExdData.h>
|
|
|
|
#include <Util/Util.h>
|
|
|
|
#include <Service.h>
|
|
|
|
|
2018-03-06 22:22:19 +01:00
|
|
|
#include <Logging/Logger.h>
|
|
|
|
#include <Database/DatabaseDef.h>
|
2020-03-01 01:00:57 +11:00
|
|
|
#include <Service.h>
|
2021-11-27 00:53:57 +01:00
|
|
|
#include <Manager/ChatChannelMgr.h>
|
2021-12-03 22:21:18 +01:00
|
|
|
#include <Network/GamePacket.h>
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2018-12-02 02:01:41 +01:00
|
|
|
#include "Linkshell/Linkshell.h"
|
2018-03-02 07:22:25 -03:00
|
|
|
#include "LinkshellMgr.h"
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
#include "Actor/Player.h"
|
|
|
|
|
2021-12-03 22:21:18 +01:00
|
|
|
#include "WorldServer.h"
|
|
|
|
|
|
|
|
#include <Network/GameConnection.h>
|
|
|
|
#include <Network/GamePacket.h>
|
|
|
|
#include <Network/PacketDef/Zone/ServerZoneDef.h>
|
2021-12-07 00:02:41 +01:00
|
|
|
#include <Network/PacketWrappers/LinkshellResultPacket.h>
|
|
|
|
#include <Network/PacketDef/ClientIpcs.h>
|
2021-12-03 22:21:18 +01:00
|
|
|
|
2021-12-04 21:53:04 +01:00
|
|
|
#include "Session.h"
|
|
|
|
|
2021-12-03 22:21:18 +01:00
|
|
|
using namespace Sapphire::Common;
|
|
|
|
using namespace Sapphire::Network::Packets;
|
|
|
|
using namespace Sapphire::Network::Packets::WorldPackets::Server;
|
|
|
|
using namespace Sapphire::World::Manager;
|
|
|
|
|
2018-12-02 02:01:41 +01:00
|
|
|
bool Sapphire::World::Manager::LinkshellMgr::loadLinkshells()
|
2017-08-28 18:36:51 +02:00
|
|
|
{
|
2020-03-01 01:00:57 +11:00
|
|
|
auto& db = Common::Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::ref();
|
2021-11-27 00:53:57 +01:00
|
|
|
auto& chatChannelMgr = Common::Service< Manager::ChatChannelMgr >::ref();
|
2020-03-01 01:00:57 +11:00
|
|
|
|
2021-12-04 12:29:54 +01:00
|
|
|
auto query = db.getPreparedStatement( Db::LINKSHELL_SEL_ALL );
|
|
|
|
auto res = db.query( query );
|
2018-08-29 21:40:59 +02:00
|
|
|
|
|
|
|
while( res->next() )
|
|
|
|
{
|
|
|
|
uint64_t linkshellId = res->getUInt64( 1 );
|
2021-11-27 00:53:57 +01:00
|
|
|
uint64_t masterId = res->getUInt64( 2 );
|
2018-08-29 21:40:59 +02:00
|
|
|
std::string name = res->getString( 4 );
|
|
|
|
|
|
|
|
auto func = []( std::set< uint64_t >& outList, std::vector< char >& inData )
|
|
|
|
{
|
|
|
|
if( inData.size() )
|
2017-08-28 23:19:35 +02:00
|
|
|
{
|
2021-11-27 00:53:57 +01:00
|
|
|
size_t entryCount = inData.size() / 8;
|
|
|
|
std::vector< uint64_t > list( entryCount );
|
|
|
|
|
|
|
|
for( int i = 0; i < entryCount; ++i )
|
|
|
|
{
|
|
|
|
auto val = *reinterpret_cast< const uint64_t* >( &inData[ i * 8 ] );
|
|
|
|
list[ i ] = val;
|
|
|
|
}
|
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
outList.insert( list.begin(), list.end() );
|
|
|
|
}
|
|
|
|
};
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
std::set< uint64_t > members;
|
|
|
|
std::vector< char > membersBin;
|
|
|
|
membersBin = res->getBlobVector( 3 );
|
|
|
|
func( members, membersBin );
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
std::set< uint64_t > leaders;
|
|
|
|
std::vector< char > leadersBin;
|
|
|
|
leadersBin = res->getBlobVector( 5 );
|
2021-11-27 00:53:57 +01:00
|
|
|
func( leaders, leadersBin );
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
std::set< uint64_t > invites;
|
|
|
|
std::vector< char > invitesBin;
|
|
|
|
invitesBin = res->getBlobVector( 6 );
|
2021-11-27 00:53:57 +01:00
|
|
|
func( invites, invitesBin );
|
|
|
|
|
|
|
|
auto chatChannelId = chatChannelMgr.createChatChannel( Common::ChatChannelType::LinkshellChat );
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
// TODO: remove shared_ptr, pass references instead
|
|
|
|
auto lsPtr = std::make_shared< Linkshell >( linkshellId, name, chatChannelId, masterId, members, leaders, invites );
|
2018-08-29 21:40:59 +02:00
|
|
|
m_linkshellIdMap[ linkshellId ] = lsPtr;
|
|
|
|
m_linkshellNameMap[ name ] = lsPtr;
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
}
|
2017-08-28 18:36:51 +02:00
|
|
|
|
2018-08-29 21:40:59 +02:00
|
|
|
return true;
|
2017-08-28 18:36:51 +02:00
|
|
|
}
|
2017-08-28 23:43:52 +02:00
|
|
|
|
2021-12-04 12:29:54 +01:00
|
|
|
void LinkshellMgr::writeLinkshell( uint64_t lsId )
|
|
|
|
{
|
|
|
|
auto& db = Common::Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::ref();
|
|
|
|
|
|
|
|
auto ls = getLinkshellById( lsId );
|
|
|
|
|
|
|
|
if( !ls )
|
|
|
|
{
|
|
|
|
Logger::error( "Linkshell {} not found for write!", lsId );
|
|
|
|
}
|
|
|
|
|
|
|
|
auto query = db.getPreparedStatement( Db::LINKSHELL_UP );
|
|
|
|
|
|
|
|
auto& members = ls->getMemberIdList();
|
|
|
|
auto& leaders = ls->getLeaderIdList();
|
|
|
|
auto& invites = ls->getInviteIdList();
|
|
|
|
std::vector< uint64_t > memberVec;
|
|
|
|
std::vector< uint64_t > leaderVec;
|
|
|
|
std::vector< uint64_t > inviteVec;
|
|
|
|
|
|
|
|
std::copy( members.begin(), members.end(), std::back_inserter( memberVec ) );
|
|
|
|
std::copy( leaders.begin(), leaders.end(), std::back_inserter( leaderVec ) );
|
|
|
|
std::copy( invites.begin(), invites.end(), std::back_inserter( inviteVec ) );
|
|
|
|
|
|
|
|
std::vector< uint8_t > memberBin( memberVec.size() * 8 );
|
|
|
|
memcpy( memberBin.data(), memberVec.data(), memberVec.size() * 8 );
|
|
|
|
|
|
|
|
std::vector< uint8_t > leaderBin( leaderVec.size() * 8 );
|
|
|
|
memcpy( leaderBin.data(), leaderVec.data(), leaderVec.size() * 8 );
|
|
|
|
|
|
|
|
std::vector< uint8_t > inviteBin( inviteVec.size() * 8 );
|
|
|
|
memcpy( inviteBin.data(), inviteVec.data(), inviteVec.size() * 8 );
|
|
|
|
|
|
|
|
query->setBinary( 1, memberBin );
|
|
|
|
query->setString( 2, ls->getName() );
|
|
|
|
query->setBinary( 3, leaderBin );
|
|
|
|
query->setBinary( 4, inviteBin );
|
|
|
|
query->setInt64( 5, lsId );
|
|
|
|
db.execute( query );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-12-02 02:01:41 +01:00
|
|
|
Sapphire::LinkshellPtr Sapphire::World::Manager::LinkshellMgr::getLinkshellByName( const std::string& name )
|
2017-08-28 23:43:52 +02:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
auto it = m_linkshellNameMap.find( name );
|
|
|
|
if( it == m_linkshellNameMap.end() )
|
|
|
|
return nullptr;
|
|
|
|
else
|
|
|
|
return it->second;
|
2017-08-28 23:43:52 +02:00
|
|
|
}
|
|
|
|
|
2018-12-02 02:01:41 +01:00
|
|
|
Sapphire::LinkshellPtr Sapphire::World::Manager::LinkshellMgr::getLinkshellById( uint64_t lsId )
|
2017-08-28 23:43:52 +02:00
|
|
|
{
|
2018-08-29 21:40:59 +02:00
|
|
|
auto it = m_linkshellIdMap.find( lsId );
|
|
|
|
if( it == m_linkshellIdMap.end() )
|
|
|
|
return nullptr;
|
|
|
|
else
|
|
|
|
return it->second;
|
2017-08-28 23:43:52 +02:00
|
|
|
}
|
2021-11-27 00:53:57 +01:00
|
|
|
|
|
|
|
Sapphire::LinkshellPtr Sapphire::World::Manager::LinkshellMgr::createLinkshell( const std::string& name, Entity::Player& player )
|
|
|
|
{
|
|
|
|
uint64_t linkshellId = 1;
|
|
|
|
|
|
|
|
if( !m_linkshellIdMap.empty() )
|
|
|
|
{
|
|
|
|
auto lastIdx = ( --m_linkshellIdMap.end() )->first;
|
|
|
|
linkshellId = lastIdx + 1;
|
|
|
|
}
|
|
|
|
|
2021-12-07 14:27:43 +01:00
|
|
|
// check if a linkshell with the same name already exists
|
|
|
|
auto lsIt = m_linkshellNameMap.find( name );
|
|
|
|
if( lsIt != m_linkshellNameMap.end() )
|
|
|
|
return nullptr;
|
|
|
|
|
2021-12-09 22:10:14 +01:00
|
|
|
auto& chatChannelMgr = Common::Service< Manager::ChatChannelMgr >::ref();
|
|
|
|
auto chatChannelId = chatChannelMgr.createChatChannel( Common::ChatChannelType::LinkshellChat );
|
|
|
|
chatChannelMgr.addPlayerToChannel( chatChannelId, player );
|
|
|
|
|
2021-11-27 00:53:57 +01:00
|
|
|
uint64_t masterId = player.getCharacterId();
|
|
|
|
|
|
|
|
// TODO: remove this messy set
|
|
|
|
std::set< uint64_t > memberSet;
|
|
|
|
std::set< uint64_t > leaderSet;
|
|
|
|
std::set< uint64_t > inviteSet;
|
|
|
|
|
|
|
|
// we add linkshell owner to the list of members
|
|
|
|
memberSet.insert( masterId );
|
|
|
|
|
|
|
|
auto lsPtr = std::make_shared< Linkshell >( linkshellId, name, chatChannelId, masterId, memberSet, leaderSet, inviteSet );
|
|
|
|
m_linkshellIdMap[ linkshellId ] = lsPtr;
|
|
|
|
m_linkshellNameMap[ name ] = lsPtr;
|
|
|
|
|
|
|
|
// TODO: generalize SQL update
|
|
|
|
// TODO: handle player pkt
|
|
|
|
|
|
|
|
// prepare binary data for SQL
|
|
|
|
std::vector< uint64_t > members( 128, 0 );
|
|
|
|
std::vector< uint64_t > leaders( 128, 0 );
|
|
|
|
std::vector< uint64_t > invites( 128, 0 );
|
|
|
|
|
|
|
|
// add player entry
|
|
|
|
members[ 0 ] = masterId;
|
|
|
|
|
|
|
|
std::vector< uint8_t > memVec( sizeof( members ) );
|
|
|
|
memcpy( memVec.data(), members.data(), sizeof( members ) );
|
|
|
|
|
|
|
|
std::vector< uint8_t > leadVec( sizeof( leaders ) );
|
|
|
|
memcpy( leadVec.data(), leaders.data(), sizeof( leaders ) );
|
|
|
|
|
|
|
|
std::vector< uint8_t > invVec( sizeof( invites ) );
|
|
|
|
memcpy( invVec.data(), invites.data(), sizeof( invites ) );
|
|
|
|
|
|
|
|
auto& db = Common::Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::ref();
|
|
|
|
auto stmt = db.getPreparedStatement( Db::ZoneDbStatements::CHARA_LINKSHELL_INS );
|
|
|
|
stmt->setUInt64( 1, linkshellId );
|
|
|
|
stmt->setUInt64( 2, masterId );
|
|
|
|
stmt->setBinary( 3, memVec );
|
|
|
|
stmt->setString( 4, std::string( name ) );
|
|
|
|
stmt->setBinary( 5, leadVec );
|
|
|
|
stmt->setBinary( 6, invVec );
|
|
|
|
|
|
|
|
db.directExecute( stmt );
|
|
|
|
|
|
|
|
return lsPtr;
|
|
|
|
}
|
2021-12-09 22:10:14 +01:00
|
|
|
|
2021-12-03 22:21:18 +01:00
|
|
|
void Sapphire::World::Manager::LinkshellMgr::finishLinkshellCreation( const std::string& name, uint32_t result, Entity::Player& player )
|
|
|
|
{
|
|
|
|
auto& server = Common::Service< World::WorldServer >::ref();
|
|
|
|
|
2021-12-09 22:10:14 +01:00
|
|
|
auto linkshellResult = makeLinkshellResult( player, 0, 0, 1, result, 0, name, "" );
|
2021-12-03 22:21:18 +01:00
|
|
|
server.queueForPlayer( player.getCharacterId(), linkshellResult );
|
|
|
|
|
|
|
|
}
|
2021-11-27 00:53:57 +01:00
|
|
|
|
|
|
|
const std::vector< Sapphire::LinkshellPtr > Sapphire::World::Manager::LinkshellMgr::getPlayerLinkshells( Entity::Player& player ) const
|
|
|
|
{
|
|
|
|
std::vector< Sapphire::LinkshellPtr > lsVec;
|
|
|
|
|
2021-12-09 22:10:14 +01:00
|
|
|
for( const auto &[ key, value ] : m_linkshellIdMap )
|
2021-11-27 00:53:57 +01:00
|
|
|
{
|
2021-12-09 22:10:14 +01:00
|
|
|
auto& memberList = value->getMemberIdList();
|
|
|
|
auto& inviteList = value->getInviteIdList();
|
2021-11-27 00:53:57 +01:00
|
|
|
|
2021-12-09 22:10:14 +01:00
|
|
|
// find player id in LS member or invite list
|
|
|
|
if( memberList.count( player.getCharacterId() ) ||
|
|
|
|
inviteList.count( player.getCharacterId() ) )
|
|
|
|
{
|
|
|
|
lsVec.emplace_back( value );
|
2021-11-27 00:53:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return lsVec;
|
2021-12-04 12:29:54 +01:00
|
|
|
}
|
|
|
|
|
2021-12-07 00:02:41 +01:00
|
|
|
void LinkshellMgr::invitePlayer( Entity::Player& sourcePlayer, Entity::Player& invitedPlayer, uint64_t linkshellId )
|
2021-12-04 21:53:04 +01:00
|
|
|
{
|
|
|
|
auto& server = Common::Service< World::WorldServer >::ref();
|
|
|
|
|
|
|
|
auto lsPtr = getLinkshellById( linkshellId );
|
|
|
|
|
2021-12-07 00:02:41 +01:00
|
|
|
if( !lsPtr )
|
|
|
|
return Logger::warn( "Failed to invite player to linkshell - linkshell not found!" );
|
2021-12-04 21:53:04 +01:00
|
|
|
|
2021-12-07 00:02:41 +01:00
|
|
|
lsPtr->addInvite( invitedPlayer.getCharacterId() );
|
2021-12-04 21:53:04 +01:00
|
|
|
writeLinkshell( lsPtr->getId() );
|
2021-12-07 00:02:41 +01:00
|
|
|
sendLinkshellList( invitedPlayer );
|
|
|
|
|
|
|
|
auto linkshellInviteResult = makeLinkshellResult( invitedPlayer, 0, 0,
|
|
|
|
WorldPackets::Client::LinkshellJoin, 0,
|
|
|
|
LinkshellResultPacket::UpdateStatus::Target,
|
|
|
|
lsPtr->getName(), sourcePlayer.getName() );
|
|
|
|
|
|
|
|
server.queueForPlayer( invitedPlayer.getCharacterId(), linkshellInviteResult );
|
|
|
|
|
|
|
|
auto linkshellInviteResult1 = makeLinkshellResult( sourcePlayer, 0, 0,
|
|
|
|
WorldPackets::Client::LinkshellJoin, 0,
|
|
|
|
LinkshellResultPacket::UpdateStatus::Execute,
|
|
|
|
lsPtr->getName(), invitedPlayer.getName() );
|
|
|
|
|
|
|
|
server.queueForPlayer( sourcePlayer.getCharacterId(), linkshellInviteResult1 );
|
2021-12-04 21:53:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void LinkshellMgr::sendLinkshellList( Entity::Player& player )
|
|
|
|
{
|
|
|
|
auto& server = Common::Service< World::WorldServer >::ref();
|
|
|
|
|
|
|
|
auto linkshellListPacket = makeZonePacket< FFXIVIpcGetLinkshellListResult >( player.getId() );
|
|
|
|
|
|
|
|
auto lsVec = getPlayerLinkshells( player );
|
|
|
|
|
2021-12-05 21:16:36 +01:00
|
|
|
uint32_t chatFlag = player.isLogin() ? 0 : 1ul << 11;
|
|
|
|
|
2021-12-04 21:53:04 +01:00
|
|
|
for( int i = 0; i < lsVec.size(); ++i )
|
|
|
|
{
|
|
|
|
auto pLs = lsVec[ i ];
|
|
|
|
uint32_t hierarchy = 0;
|
|
|
|
|
|
|
|
if( pLs->getMasterId() == player.getCharacterId() )
|
2021-12-05 21:16:36 +01:00
|
|
|
hierarchy = Ls::LinkshellHierarchy::Master << 8;
|
2021-12-04 21:53:04 +01:00
|
|
|
else if( pLs->getLeaderIdList().count( player.getCharacterId() ) )
|
2021-12-05 21:16:36 +01:00
|
|
|
hierarchy = Ls::LinkshellHierarchy::Leader << 8;
|
2021-12-04 21:53:04 +01:00
|
|
|
else if( pLs->getInviteIdList().count( player.getCharacterId() ) )
|
2021-12-05 21:16:36 +01:00
|
|
|
hierarchy = Ls::LinkshellHierarchy::Invite << 8;
|
2021-12-04 21:53:04 +01:00
|
|
|
else
|
2021-12-05 21:16:36 +01:00
|
|
|
hierarchy = Ls::LinkshellHierarchy::Member << 8;
|
|
|
|
|
|
|
|
hierarchy |= chatFlag;
|
2021-12-04 21:53:04 +01:00
|
|
|
|
|
|
|
linkshellListPacket->data().LinkshellList[ i ].LinkshellID = pLs->getId();
|
|
|
|
linkshellListPacket->data().LinkshellList[ i ].ChannelID = pLs->getChatChannel();
|
|
|
|
linkshellListPacket->data().LinkshellList[ i ].HierarchyID = hierarchy;
|
|
|
|
strcpy( linkshellListPacket->data().LinkshellList[ i ].LinkshellName, pLs->getName().c_str() );
|
|
|
|
}
|
|
|
|
|
|
|
|
server.queueForPlayer( player.getCharacterId(), linkshellListPacket );
|
|
|
|
}
|
|
|
|
|
2021-12-05 00:37:52 +01:00
|
|
|
void LinkshellMgr::leaveLinkshell( uint64_t lsId, uint64_t characterId )
|
|
|
|
{
|
|
|
|
auto& server = Common::Service< World::WorldServer >::ref();
|
2021-12-05 17:21:37 +01:00
|
|
|
auto& chatChannelMgr = Common::Service< Manager::ChatChannelMgr >::ref();
|
2021-12-05 00:37:52 +01:00
|
|
|
auto leavingPlayer = server.getPlayer( characterId );
|
|
|
|
auto lsPtr = getLinkshellById( lsId );
|
|
|
|
if( !leavingPlayer || !lsPtr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
lsPtr->removeMember( characterId );
|
|
|
|
writeLinkshell( lsId );
|
2021-12-04 12:29:54 +01:00
|
|
|
|
2021-12-05 17:21:37 +01:00
|
|
|
chatChannelMgr.removePlayerFromChannel( lsPtr->getChatChannel(), *leavingPlayer );
|
2021-12-05 00:37:52 +01:00
|
|
|
sendLinkshellList( *leavingPlayer );
|
|
|
|
}
|
|
|
|
|
|
|
|
void LinkshellMgr::joinLinkshell( uint64_t lsId, uint64_t characterId )
|
|
|
|
{
|
|
|
|
auto &server = Common::Service< World::WorldServer >::ref();
|
2021-12-05 17:21:37 +01:00
|
|
|
auto& chatChannelMgr = Common::Service< Manager::ChatChannelMgr >::ref();
|
2021-12-05 00:37:52 +01:00
|
|
|
auto joiningPlayer = server.getPlayer( characterId );
|
|
|
|
auto lsPtr = getLinkshellById( lsId );
|
|
|
|
if( !joiningPlayer || !lsPtr )
|
|
|
|
return;
|
|
|
|
|
|
|
|
lsPtr->addMember( characterId );
|
|
|
|
lsPtr->removeInvite( characterId );
|
|
|
|
writeLinkshell( lsId );
|
|
|
|
|
2021-12-05 17:21:37 +01:00
|
|
|
chatChannelMgr.addPlayerToChannel( lsPtr->getChatChannel(), *joiningPlayer );
|
|
|
|
|
2021-12-05 00:37:52 +01:00
|
|
|
sendLinkshellList( *joiningPlayer );
|
|
|
|
}
|