1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-25 11:07:45 +00:00

Merge branch 'party_and_gpose' into develop_5.25

This commit is contained in:
collett 2020-07-03 04:53:51 +09:00
commit d5f16e00dc
12 changed files with 765 additions and 54 deletions

View file

@ -367,7 +367,7 @@ namespace Sapphire::Network::ActorControl
AllotAttribute = 0x135,
ClearFieldMarkers = 0x13A,
CameraMode = 0x13B, // param12, 1 = camera mode enable, 0 = disable
CameraMode = 0x13B, // param11, 1 = enable, 0 = disable
CharaNameReq = 0x13D, // requests character name by content id
HuntingLogDetails = 0x194,

View file

@ -79,6 +79,7 @@ namespace Sapphire::Network::Packets
LogMessage = 0x00D0,
Chat = 0x0161, // updated 5.25
PartyChat = 0x0065, // updated 5.25
WorldVisitList = 0xF0FE, // added 4.5
@ -145,7 +146,7 @@ namespace Sapphire::Network::Packets
ActorCast = 0x00C4, // updated 5.25
SomeCustomiseChangePacketProbably = 0x00CD, // added 5.18
PartyList = 0x0287, // updated 5.18
PartyList = 0x01B7, // updated 5.25
HateRank = 0x012E, // updated 5.25
HateList = 0x02C4, // updated 5.25
ObjectSpawn = 0x00D2, // updated 5.25
@ -228,6 +229,13 @@ namespace Sapphire::Network::Packets
EquipDisplayFlags = 0x010D, // updated 5.25
ShopMessage = 0x00C1, // updated 5.25
LootMessage = 0x00B1, // updated 5.25
SocialInviteMessage = 0x02F0, // updated 5.25
SocialInviteMessage2 = 0x026E, // updated 5.25
SocialInviteResponseMessage = 0x0117, // updated 5.25
PartyMessage = 0x0284, // updated 5.25
/// Housing //////////////////////////////////////
LandSetInitialize = 0x0234, // updated 5.0
@ -304,6 +312,7 @@ namespace Sapphire::Network::Packets
CreateCrossWorldLS = 0x00AF, // updated 4.3
ChatHandler = 0x0189, // updated 5.25
PartyChatHandler = 0x0065, // update 5.25
SocialListHandler = 0x0371, // updated 5.25
SetSearchInfoHandler = 0x00FA, // updated 5.25
@ -380,8 +389,13 @@ namespace Sapphire::Network::Packets
PerformNoteHandler = 0x029B, // updated 4.3
WorldInteractionHandler = 0x0285, // updated 5.25
ShopMessage = 0x00C1, // updated 5.25
LootMessage = 0x00B1, // updated 5.25
SocialInviteHandler = 0x02BA, // updated 5.25
SocialInviteResponseHandler = 0x02C7, // updated 5.25
PartySetLeaderHandler = 0x02A5, // updated 5.25
LeavePartyHandler = 0x00C0, // updated 5.25
KickPartyMemberHandler = 0x0188, // update 5.25
DisbandPartyHandler = 0x0336, // update 5.25
};
////////////////////////////////////////////////////////////////////////////////

View file

@ -207,6 +207,13 @@ struct FFXIVIpcChatHandler :
/* 001A */ char message[1012];
};
struct FFXIVIpcPartyChatHandler :
FFXIVIpcBasePacket< ChatHandler >
{
uint64_t unknown;
char message[1024];
};
struct FFXIVIpcShopEventHandler :
FFXIVIpcBasePacket< ShopEventHandler >
{
@ -355,6 +362,61 @@ struct FFXIVIpcWorldInteractionHandler :
Common::FFXIVARR_POSITION3 position;
};
struct FFXIVIpcSocialInviteHandler :
FFXIVIpcBasePacket< SocialInviteHandler >
{
uint64_t unknown;
uint8_t p1;
uint8_t p2;
uint8_t socialType;
char name[32];
uint8_t padding[5];
};
struct FFXIVIpcSocialInviteResponseHandler :
FFXIVIpcBasePacket< SocialInviteResponseHandler >
{
uint64_t contentId;
uint8_t p1;
uint8_t p2;
uint8_t socialType;
uint8_t response;
uint32_t unknown;
};
struct FFXIVIpcPartySetLeaderHandler :
FFXIVIpcBasePacket< PartySetLeaderHandler >
{
uint64_t contentId;
uint8_t p1;
uint8_t p2;
char name[32];
uint8_t padding[6];
};
struct FFXIVIpcLeavePartyHandler :
FFXIVIpcBasePacket< LeavePartyHandler >
{
uint64_t empty;
};
struct FFXIVIpcKickPartyMemberHander :
FFXIVIpcBasePacket< KickPartyMemberHandler >
{
uint64_t contentId;
uint8_t p1;
uint8_t p2;
char name[32];
uint8_t padding[6];
};
struct FFXIVIpcDisbandPartyHandler :
FFXIVIpcBasePacket< DisbandPartyHandler >
{
uint64_t empty;
};
}
#endif //_CORE_NETWORK_PACKETS_ZONE_CLIENT_IPC_H

View file

@ -46,6 +46,19 @@ namespace Sapphire::Network::Packets::Server
char msg[1012];
};
struct FFXIVIpcPartyChat : FFXIVIpcBasePacket< PartyChat >
{
uint64_t unknown;
uint64_t contentId;
uint32_t charaId;
uint8_t u1;
uint8_t u2;
uint8_t u3;
char name[32];
char message[1024];
uint8_t padding;
};
struct FFXIVIpcChatBanned : FFXIVIpcBasePacket< ChatBanned >
{
uint8_t padding[4]; // I was not sure reinterpreting ZST is valid behavior in C++.
@ -2078,6 +2091,83 @@ namespace Sapphire::Network::Packets::Server
uint32_t param7;
};
struct FFXIVIpcSocialInviteMessage : FFXIVIpcBasePacket< SocialInviteMessage >
{
uint64_t contentId;
uint32_t expireTime;
uint8_t p1;
uint8_t p2;
uint8_t socialType;
uint8_t padding;
uint8_t type;
uint8_t unknown4;
char name[32];
uint8_t padding2[6];
};
struct FFXIVIpcSocialInviteMessage2 : FFXIVIpcBasePacket< SocialInviteMessage2 >
{
uint64_t contentId;
uint32_t unknown3;
uint8_t p1;
uint8_t p2;
uint8_t socialType;
uint8_t padding;
char name[32];
};
struct FFXIVIpcSocialInviteResponseMessage : FFXIVIpcBasePacket< SocialInviteResponseMessage >
{
uint64_t contentId;
uint32_t unknown3;
uint8_t u1AlwaysOne;
uint8_t response;
uint8_t u2AlwaysOne;
char name[32];
uint8_t padding;
};
struct FFXIVIpcPartyList : FFXIVIpcBasePacket< PartyList >
{
struct
{
char name[32];
uint64_t contentId;
uint32_t charaId;
uint32_t u1;
uint32_t u2;
uint32_t hp;
uint32_t maxHp;
uint16_t mp;
uint16_t maxMp;
uint16_t u3;
uint16_t zoneId;
uint8_t gposeSelectable;
uint8_t classId;
uint8_t u5;
uint8_t level;
uint8_t otherData[368];
} member[8];
uint64_t someContentId1;
uint64_t someContentId2;
uint8_t leaderIndex;
uint8_t partySize;
uint16_t padding1;
uint32_t padding2;
};
struct FFXIVIpcPartyMessage : FFXIVIpcBasePacket< PartyMessage >
{
uint64_t leaderContentId;
uint64_t memberContentId;
uint8_t u1;
uint8_t u2;
uint16_t type;
uint8_t partySize; // ?
char leaderName[32];
char memberName[32];
uint8_t padding[3];
};
}
#endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/

View file

@ -234,6 +234,10 @@ Sapphire::Common::OnlineStatus Sapphire::Entity::Player::getOnlineStatus() const
void Sapphire::Entity::Player::setOnlineStatusMask( uint64_t status )
{
m_onlineStatus = status;
sendToInRangeSet( makeActorControl( getId(), SetStatusIcon, static_cast< uint8_t >( getOnlineStatus() ) ), true );
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( getId() );
statusPacket->data().onlineStatusFlags = status;
queuePacket( statusPacket );
}
uint64_t Sapphire::Entity::Player::getOnlineStatusMask() const
@ -1043,7 +1047,6 @@ bool Sapphire::Entity::Player::hasStateFlag( Common::PlayerStateFlag flag ) cons
void Sapphire::Entity::Player::setStateFlag( Common::PlayerStateFlag flag )
{
auto prevOnlineStatus = getOnlineStatus();
int32_t iFlag = static_cast< uint32_t >( flag );
uint16_t index;
@ -1052,13 +1055,6 @@ void Sapphire::Entity::Player::setStateFlag( Common::PlayerStateFlag flag )
m_stateFlags[ index ] |= value;
sendStateFlags();
auto newOnlineStatus = getOnlineStatus();
if( prevOnlineStatus != newOnlineStatus )
sendToInRangeSet( makeActorControl( getId(), SetStatusIcon,
static_cast< uint8_t >( getOnlineStatus() ) ), true );
}
void Sapphire::Entity::Player::setStateFlags( std::vector< Common::PlayerStateFlag > flags )
@ -1079,8 +1075,6 @@ void Sapphire::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag )
if( !hasStateFlag( flag ) )
return;
auto prevOnlineStatus = getOnlineStatus();
int32_t iFlag = static_cast< uint32_t >( flag );
uint16_t index;
@ -1089,11 +1083,6 @@ void Sapphire::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag )
m_stateFlags[ index ] ^= value;
sendStateFlags();
auto newOnlineStatus = getOnlineStatus();
if( prevOnlineStatus != newOnlineStatus )
sendToInRangeSet( makeActorControl( getId(), SetStatusIcon, static_cast< uint8_t >( getOnlineStatus() ) ), true );
}
void Sapphire::Entity::Player::update( uint64_t tickCount )
@ -2297,6 +2286,218 @@ void Sapphire::Entity::Player::clearBuyBackMap()
m_shopBuyBackMap.clear();
}
bool Sapphire::Entity::Player::isPartyLeader()
{
if( !m_partyLeader )
return false;
return m_partyLeader->getId() == getId();
}
bool Sapphire::Entity::Player::isInParty()
{
return m_partyLeader != nullptr;
}
Sapphire::Entity::PlayerPtr Sapphire::Entity::Player::getPartyLeader()
{
return m_partyLeader;
}
bool Sapphire::Entity::Player::createEmptyParty()
{
if( m_partyLeader )
return false;
m_partyLeader = getAsPlayer();
m_partyMemberList.clear();
m_partyMemberList.push_back( m_partyLeader );
setOnlineStatusMask( 0x0000803000000000ui64 );
return true;
}
void Sapphire::Entity::Player::disbandParty()
{
if( isPartyLeader() )
{
for( auto member : m_partyMemberList )
{
if( member->getId() == getId() )
continue;
member->m_partyLeader = nullptr;
member->setOnlineStatusMask( 0x0000800000000000ui64 );
member->clearPartyList();
}
m_partyLeader = nullptr;
clearPartyList();
}
}
Sapphire::Entity::PlayerPtr Sapphire::Entity::Player::getPartyInvitationSender()
{
return m_partyInvitationSender;
}
void Sapphire::Entity::Player::setPartyInvitationSender( PlayerPtr sender )
{
m_partyInvitationSender = sender;
}
bool Sapphire::Entity::Player::addPartyMember( PlayerPtr member )
{
if( !isPartyLeader() || member->isInParty() || getPartySize() >= 8 )
return false;
m_partyMemberList.push_back( member );
member->m_partyLeader = m_partyLeader;
member->setOnlineStatusMask( 0x0000802000000000ui64 );
sendPartyListToParty();
return true;
}
bool Sapphire::Entity::Player::removePartyMember( PlayerPtr member )
{
if( member->isInParty() && member->getPartyLeader()->getId() == getId() )
{
if( member->getId() == member->getPartyLeader()->getId() )
{
Entity::PlayerPtr nextLeader = nullptr;
for( auto it = m_partyMemberList.begin(); it != m_partyMemberList.end(); it++ )
{
if( !( *it )->isPartyLeader() )
{
nextLeader = *it;
break;
}
}
if( !nextLeader )
{
disbandParty();
return true;
}
changePartyLeader( nextLeader );
return nextLeader->removePartyMember( member );
}
else
{
bool flag = false;
for( auto it = m_partyMemberList.begin(); it != m_partyMemberList.end(); it++ )
{
if( ( *it )->getId() == member->getId() )
{
m_partyMemberList.erase( it );
member->m_partyLeader = nullptr;
member->setOnlineStatusMask( 0x0000800000000000ui64 );
member->clearPartyList();
flag = true;
break;
}
}
if( flag )
{
sendPartyListToParty();
if( getPartySize() == 1 )
{
m_partyLeader = nullptr;
m_partyMemberList.clear();
}
return true;
}
}
}
return false;
}
bool Sapphire::Entity::Player::changePartyLeader( PlayerPtr newLeader )
{
if( !isPartyLeader() || !newLeader->isInParty() || newLeader->getPartyLeader()->getId() != getId() )
return false;
for( auto it = m_partyMemberList.begin(); it != m_partyMemberList.end(); it++ )
{
if( ( *it )->getId() == newLeader->getId() )
{
newLeader->m_partyMemberList = m_partyMemberList;
m_partyMemberList.clear();
for( auto m : newLeader->m_partyMemberList )
{
m->m_partyLeader = newLeader;
}
newLeader->setOnlineStatusMask( 0x0000803000000000ui64 );
setOnlineStatusMask( 0x0000802000000000ui64 );
newLeader->sendPartyListToParty();
return true;
}
}
return false;
}
uint8_t Sapphire::Entity::Player::getPartySize()
{
if( isInParty() )
{
return m_partyMemberList.size();
}
return 0;
}
void Sapphire::Entity::Player::sendPartyListToParty()
{
assert( isPartyLeader() );
FFXIVIpcPartyList partyList = {};
int i = 0;
int leaderIndex = 0;
for( auto member : m_partyMemberList )
{
assert( i < 8 );
memcpy( partyList.member[ i ].name, member->getName().c_str(), member->getName().length() + 1 );
partyList.member[ i ].contentId = member->getContentId();
partyList.member[ i ].charaId = member->getId();
partyList.member[ i ].u1 = 0xE0000000;
partyList.member[ i ].u2 = 0xE0000000;
partyList.member[ i ].hp = member->getHp();
partyList.member[ i ].maxHp = member->getMaxHp();
partyList.member[ i ].mp = member->getMp();
partyList.member[ i ].maxMp = 10000;
partyList.member[ i ].u3 = 0x44;
partyList.member[ i ].zoneId = member->getCurrentTerritory()->getTerritoryTypeId();
partyList.member[ i ].gposeSelectable = 1;
partyList.member[ i ].classId = static_cast< uint8_t >( member->getClass() );
partyList.member[ i ].level = member->getLevel();
if( member->isPartyLeader() )
leaderIndex = i;
i++;
}
partyList.someContentId1 = 0x0044000000000001ui64;
partyList.someContentId2 = 0x0044000100000001ui64;
partyList.leaderIndex = leaderIndex;
partyList.partySize = getPartySize();
for( auto member : m_partyMemberList )
{
auto packet = makeZonePacket< FFXIVIpcPartyList >( member->getId() );
memcpy( &packet->data().member[ 0 ], &partyList, sizeof( partyList ) );
member->queuePacket( packet );
}
}
void Sapphire::Entity::Player::clearPartyList()
{
assert( !isInParty() );
auto packet = makeZonePacket< FFXIVIpcPartyList >( getId() );
queuePacket( packet );
m_partyMemberList.clear();
}
void Sapphire::Entity::Player::foreachPartyMember( std::function< void( PlayerPtr member ) > callback )
{
if( !isPartyLeader() || !callback )
return;
for( auto member : m_partyMemberList )
{
callback( member );
}
}
void Sapphire::Entity::Player::gaugeClear()
{
std::memset( &m_gauge, 0, sizeof( m_gauge ) );

View file

@ -1024,8 +1024,29 @@ namespace Sapphire::Entity
Common::SamSen gaugeSamGetSenRaw();
bool gaugeSamHasAnySen();
// party
//////////////////////////////////////////////////////////////////////////////////////////////////////
private:
PlayerPtr m_partyLeader;
PlayerPtr m_partyInvitationSender;
std::vector< PlayerPtr > m_partyMemberList;
void clearPartyList();
void sendPartyListToParty();
public:
bool isPartyLeader();
bool isInParty();
bool createEmptyParty();
void disbandParty();
PlayerPtr getPartyLeader();
PlayerPtr getPartyInvitationSender();
void setPartyInvitationSender( PlayerPtr sender );
bool addPartyMember( PlayerPtr member );
bool removePartyMember( PlayerPtr member );
bool changePartyLeader( PlayerPtr newLeader );
uint8_t getPartySize();
void foreachPartyMember( std::function< void( PlayerPtr member ) > callback);
//////////////////////////////////////////////////////////////////////////////////////////////////////
std::unordered_map< uint32_t, TerritoryPtr > m_privateInstanceMap;
TerritoryPtr getOrCreatePrivateInstance( uint32_t zoneId );
bool enterPredefinedPrivateInstance( uint32_t zoneId );

View file

@ -134,7 +134,14 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
setZoneHandler( ClientZoneIpcType::WorldInteractionHandler, "WorldInteractionHandler", &GameConnection::worldInteractionhandler );
setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler );
setChatHandler( ClientZoneIpcType::PartyChatHandler, "PartyChatHandler", &GameConnection::partyChatHandler );
setZoneHandler( ClientZoneIpcType::SocialInviteHandler, "SocialInviteHandler", &GameConnection::socialInviteHandler );
setZoneHandler( ClientZoneIpcType::SocialInviteResponseHandler, "SocialInviteResponseHandler", &GameConnection::socialInviteResponseHandler );
setZoneHandler( ClientZoneIpcType::PartySetLeaderHandler, "PartySetLeaderHandler", &GameConnection::partySetLeaderHandler );
setZoneHandler( ClientZoneIpcType::LeavePartyHandler, "LeavePartyHandler", &GameConnection::leavePartyHandler );
setZoneHandler( ClientZoneIpcType::KickPartyMemberHandler, "KickPartyMemberHandler", &GameConnection::kickPartyMemberHandler );
setZoneHandler( ClientZoneIpcType::DisbandPartyHandler, "DisbandPartyHandler", &GameConnection::disbandPartyHandler );
}
Sapphire::Network::GameConnection::~GameConnection() = default;

View file

@ -121,6 +121,8 @@ namespace Sapphire::Network
DECLARE_HANDLER( chatHandler );
DECLARE_HANDLER( partyChatHandler );
DECLARE_HANDLER( zoneLineHandler );
DECLARE_HANDLER( clientTriggerHandler );
@ -189,6 +191,17 @@ namespace Sapphire::Network
DECLARE_HANDLER( worldInteractionhandler );
DECLARE_HANDLER( socialInviteHandler );
DECLARE_HANDLER( socialInviteResponseHandler );
DECLARE_HANDLER( partySetLeaderHandler );
DECLARE_HANDLER( leavePartyHandler );
DECLARE_HANDLER( kickPartyMemberHandler );
DECLARE_HANDLER( disbandPartyHandler );
};
}

View file

@ -494,6 +494,18 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
player.sendDebug( "event battle level sync: {0}, ilevel sync?: {1}", param12, param2 );
break;
}
case ClientTriggerType::CameraMode:
{
if( param11 == 1 )
{
player.setOnlineStatusMask( player.getOnlineStatusMask() | 0x0000000000040000ui64 );
}
else
{
player.setOnlineStatusMask( player.getOnlineStatusMask() & 0xFFFFFFFFFFFBFFFFui64 );
}
break;
}
case ClientTriggerType::Trigger612:
{
player.sendStateFlags();

View file

@ -256,19 +256,12 @@ void Sapphire::Network::GameConnection::gm1Handler( const Packets::FFXIVARR_PACK
{
targetPlayer->setOnlineStatusMask( param1 );
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( player.getId() );
statusPacket->data().onlineStatusFlags = param1;
queueOutPacket( statusPacket );
auto searchInfoPacket = makeZonePacket< FFXIVIpcSetSearchInfo >( player.getId() );
searchInfoPacket->data().onlineStatusFlags = param1;
searchInfoPacket->data().selectRegion = targetPlayer->getSearchSelectRegion();
strcpy( searchInfoPacket->data().searchMessage, targetPlayer->getSearchMessage() );
targetPlayer->queuePacket( searchInfoPacket );
targetPlayer->sendToInRangeSet( makeActorControl( player.getId(), SetStatusIcon,
static_cast< uint8_t >( player.getOnlineStatus() ) ),
true );
player.sendNotice( "Icon for {0} was set to {1}", targetPlayer->getName(), param1 );
break;
}

View file

@ -87,18 +87,11 @@ void Sapphire::Network::GameConnection::setSearchInfoHandler( const Packets::FFX
// mark player as new adventurer
player.setNewAdventurer( true );
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( player.getId() );
statusPacket->data().onlineStatusFlags = status;
queueOutPacket( statusPacket );
auto searchInfoPacket = makeZonePacket< FFXIVIpcSetSearchInfo >( player.getId() );
searchInfoPacket->data().onlineStatusFlags = status;
searchInfoPacket->data().selectRegion = player.getSearchSelectRegion();
strcpy( searchInfoPacket->data().searchMessage, player.getSearchMessage() );
queueOutPacket( searchInfoPacket );
player.sendToInRangeSet( makeActorControl( player.getId(), SetStatusIcon,
static_cast< uint8_t >( player.getOnlineStatus() ) ), true );
}
void Sapphire::Network::GameConnection::reqSearchInfoHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
@ -437,31 +430,47 @@ void Sapphire::Network::GameConnection::socialListHandler( const Packets::FFXIVA
int32_t entrysizes = sizeof( listPacket->data().entries );
memset( listPacket->data().entries, 0, sizeof( listPacket->data().entries ) );
listPacket->data().entries[ 0 ].bytes[ 2 ] = player.getCurrentTerritory()->getTerritoryTypeId();
listPacket->data().entries[ 0 ].bytes[ 3 ] = 0x80;
listPacket->data().entries[ 0 ].bytes[ 4 ] = 0x02;
listPacket->data().entries[ 0 ].bytes[ 6 ] = 0x3B;
listPacket->data().entries[ 0 ].bytes[ 11 ] = 0x10;
listPacket->data().entries[ 0 ].classJob = static_cast< uint8_t >( player.getClass() );
listPacket->data().entries[ 0 ].contentId = player.getContentId();
listPacket->data().entries[ 0 ].level = player.getLevel();
listPacket->data().entries[ 0 ].zoneId = player.getCurrentTerritory()->getTerritoryTypeId();
listPacket->data().entries[ 0 ].zoneId1 = 0x0100;
// TODO: no idea what this does
//listPacket.data().entries[0].one = 1;
auto fillEntryAt = [ &listPacket ]( int i, Entity::PlayerPtr nextPlayer )
{
listPacket->data().entries[ i ].bytes[ 2 ] = nextPlayer->getCurrentTerritory()->getTerritoryTypeId();
listPacket->data().entries[ i ].bytes[ 3 ] = 0x80;
listPacket->data().entries[ i ].bytes[ 4 ] = 0x02;
listPacket->data().entries[ i ].bytes[ 6 ] = 0x3B;
listPacket->data().entries[ i ].bytes[ 8 ] = nextPlayer->isPartyLeader() ? 1 : 0;
listPacket->data().entries[ i ].bytes[ 11 ] = 0x10;
listPacket->data().entries[ i ].classJob = static_cast< uint8_t >( nextPlayer->getClass() );
listPacket->data().entries[ i ].contentId = nextPlayer->getContentId();
listPacket->data().entries[ i ].level = nextPlayer->getLevel();
listPacket->data().entries[ i ].zoneId = nextPlayer->getCurrentTerritory()->getTerritoryTypeId();
listPacket->data().entries[ i ].zoneId1 = 0x0100;
// TODO: no idea what this does
//listPacket.data().entries[0].one = 1;
memcpy( listPacket->data().entries[ 0 ].name, player.getName().c_str(), strlen( player.getName().c_str() ) );
memcpy( listPacket->data().entries[ i ].name, nextPlayer->getName().c_str(), strlen( nextPlayer->getName().c_str() ) );
// GC icon
listPacket->data().entries[ 0 ].bytes1[ 0 ] = 2;
// client language J = 0, E = 1, D = 2, F = 3
listPacket->data().entries[ 0 ].bytes1[ 1 ] = 1;
// user language settings flag J = 1, E = 2, D = 4, F = 8
listPacket->data().entries[ 0 ].bytes1[ 2 ] = 1 + 2;
listPacket->data().entries[ 0 ].onlineStatusMask = player.getOnlineStatusMask();
// GC icon
listPacket->data().entries[ i ].bytes1[ 0 ] = 2;
// client language J = 0, E = 1, D = 2, F = 3
listPacket->data().entries[ i ].bytes1[ 1 ] = 1;
// user language settings flag J = 1, E = 2, D = 4, F = 8
listPacket->data().entries[ i ].bytes1[ 2 ] = 1 + 2;
listPacket->data().entries[ i ].onlineStatusMask = nextPlayer->getOnlineStatusMask();
};
auto nextPlayer = player.getAsPlayer();
fillEntryAt( 0, nextPlayer );
if( player.isInParty() )
{
int i = 1;
player.getPartyLeader()->foreachPartyMember( [ &player, &fillEntryAt, &i ]( auto m )
{
if( m->getId() == player.getId() )
return;
fillEntryAt( i, m );
i++;
} );
}
queueOutPacket( listPacket );
}
else if( type == 2 )
{ // friend list
@ -478,6 +487,34 @@ void Sapphire::Network::GameConnection::socialListHandler( const Packets::FFXIVA
}
void Sapphire::Network::GameConnection::partyChatHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
// proper party chat packet not working, <se.x> won't work with this workaround
if( !player.isInParty() )
return;
const auto packet = ZoneChannelPacket< Client::FFXIVIpcPartyChatHandler >( inPacket );
//FFXIVIpcPartyChat chat = {};
//chat.unknown = packet.data().unknown;
//chat.contentId = player.getContentId();
//chat.charaId = player.getId();
//chat.u1 = 0x44;
//memcpy( chat.name, player.getName().c_str(), player.getName().length() + 1 );
//memcpy( chat.message, packet.data().message, sizeof( chat.message ) );
auto leader = player.getPartyLeader();
leader->foreachPartyMember( [ &packet, &player/*, chat*/ ]( auto m )
{
if( player.getId() == m->getId() )
return;
//auto pChat = makeZonePacket< FFXIVIpcPartyChat >( m->getId() );
//memcpy( &pChat->data(), &chat, sizeof( chat ) );
//m->queuePacket( pChat );
/* workaround */
auto chatPacket = std::make_shared< Server::ChatPacket >( player, ChatType::Party, packet.data().message );
m->queuePacket( chatPacket );
} );
}
void Sapphire::Network::GameConnection::chatHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player )
{
@ -838,4 +875,261 @@ void Sapphire::Network::GameConnection::worldInteractionhandler( const Packets::
player.sendDebug( "Unknown dwarf house." );
}
}
}
void Sapphire::Network::GameConnection::socialInviteHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
const auto packetIn = ZoneChannelPacket< Client::FFXIVIpcSocialInviteHandler >( inPacket );
switch( packetIn.data().socialType )
{
case 1:
{
if( player.isInParty() && !player.isPartyLeader() )
return;
std::string name( packetIn.data().name );
auto& serverMgr = Common::Service< Sapphire::World::ServerMgr >::ref();
auto session = serverMgr.getSession( name );
if( session )
{
auto targetPlayer = session->getPlayer();
if( targetPlayer->isInParty() )
return;
auto packet1 = makeZonePacket< FFXIVIpcSocialInviteMessage2 >( player.getId() );
packet1->data().contentId = targetPlayer->getContentId();
packet1->data().p1 = packetIn.data().p1;
packet1->data().p2 = packetIn.data().p2;
packet1->data().socialType = packetIn.data().socialType;
memcpy( packet1->data().name, targetPlayer->getName().c_str(), targetPlayer->getName().length() + 1 );
player.queuePacket( packet1 );
auto packet2 = makeZonePacket< FFXIVIpcSocialInviteMessage >( targetPlayer->getId() );
packet2->data().contentId = player.getContentId();
packet2->data().expireTime = Common::Util::getTimeSeconds() + 30;
packet2->data().p1 = packetIn.data().p1;
packet2->data().p2 = packetIn.data().p2;
packet2->data().socialType = packetIn.data().socialType;
packet2->data().type = 1;
packet2->data().unknown4 = 1;
memcpy( packet2->data().name, player.getName().c_str(), player.getName().length() + 1 );
targetPlayer->queuePacket( packet2 );
targetPlayer->setPartyInvitationSender( player.getAsPlayer() );
}
return;
}
}
}
void Sapphire::Network::GameConnection::socialInviteResponseHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
const auto packetIn = ZoneChannelPacket< Client::FFXIVIpcSocialInviteResponseHandler >( inPacket );
switch( packetIn.data().socialType )
{
case 1:
{
if( player.isInParty() || !player.getPartyInvitationSender() )
return;
auto response = packetIn.data().response;
auto sender = player.getPartyInvitationSender();
player.setPartyInvitationSender( nullptr );
auto packet1 = makeZonePacket< FFXIVIpcSocialInviteResponseMessage >( player.getId() );
packet1->data().contentId = sender->getContentId();
packet1->data().u1AlwaysOne = 1;
packet1->data().response = response;
packet1->data().u2AlwaysOne = 1;
memcpy( packet1->data().name, sender->getName().c_str(), sender->getName().length() + 1 );
player.queuePacket( packet1 );
if( response == 1 && ( !sender->isInParty() || sender->getPartyLeader()->getId() == sender->getId() ) )
{
if( !sender->isInParty() )
{
if( !sender->createEmptyParty() )
return;
}
auto member = player.getAsPlayer();
if( sender->addPartyMember( member ) )
{
FFXIVIpcPartyMessage msg = {};
msg.leaderContentId = sender->getContentId();
msg.memberContentId = player.getContentId();
msg.u1 = 1;
msg.type = 1;
msg.partySize = sender->getPartySize();
memcpy( msg.leaderName, sender->getName().c_str(), sender->getName().length() + 1 );
memcpy( msg.memberName, player.getName().c_str(), player.getName().length() + 1 );
sender->foreachPartyMember( [ msg ]( auto m )
{
auto packetMsg = makeZonePacket< FFXIVIpcPartyMessage >( m->getId() );
memcpy( &packetMsg->data(), &msg, sizeof( msg ) );
m->queuePacket( packetMsg );
} );
auto packet2 = makeZonePacket< FFXIVIpcSocialInviteMessage >( sender->getId() );
packet2->data().contentId = player.getContentId();
packet2->data().expireTime = Common::Util::getTimeSeconds() + 30;
packet2->data().p1 = packetIn.data().p1;
packet2->data().p2 = packetIn.data().p2;
packet2->data().socialType = packetIn.data().socialType;
packet2->data().type = 4;
memcpy( packet2->data().name, player.getName().c_str(), player.getName().length() + 1 );
sender->queuePacket( packet2 );
}
}
else
{
auto packet2 = makeZonePacket< FFXIVIpcSocialInviteMessage >( sender->getId() );
packet2->data().contentId = player.getContentId();
packet2->data().expireTime = Common::Util::getTimeSeconds() + 30;
packet2->data().p1 = packetIn.data().p1;
packet2->data().p2 = packetIn.data().p2;
packet2->data().socialType = packetIn.data().socialType;
packet2->data().type = 5;
memcpy( packet2->data().name, player.getName().c_str(), player.getName().length() + 1 );
sender->queuePacket( packet2 );
}
return;
}
}
}
void Sapphire::Network::GameConnection::partySetLeaderHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
if( !player.isPartyLeader() )
return;
const auto packetIn = ZoneChannelPacket< Client::FFXIVIpcPartySetLeaderHandler >( inPacket );
Sapphire::Entity::PlayerPtr newLeader = nullptr;
player.foreachPartyMember( [ &packetIn, &newLeader ]( auto m )
{
if( m->getContentId() == packetIn.data().contentId )
{
newLeader = m;
}
} );
if( newLeader && player.changePartyLeader( newLeader ) )
{
FFXIVIpcPartyMessage msg = {};
msg.leaderContentId = player.getContentId();
msg.memberContentId = newLeader->getContentId();
msg.type = 2;
msg.u1 = 1;
msg.partySize = newLeader->getPartySize();
memcpy( msg.leaderName, player.getName().c_str(), player.getName().length() + 1 );
memcpy( msg.memberName, newLeader->getName().c_str(), newLeader->getName().length() + 1 );
newLeader->foreachPartyMember( [ msg ] ( auto m )
{
auto packetMsg = makeZonePacket< FFXIVIpcPartyMessage >( m->getId() );
memcpy( &packetMsg->data(), &msg, sizeof( msg ) );
m->queuePacket( packetMsg );
} );
}
}
void Sapphire::Network::GameConnection::leavePartyHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
if( !player.isInParty() )
return;
auto leader = player.getPartyLeader();
if( leader->getPartySize() <= 2 )
{
FFXIVIpcPartyMessage msg = {};
msg.leaderContentId = leader->getContentId();
msg.type = 3;
leader->foreachPartyMember( [ msg ] ( auto m )
{
auto packetMsg = makeZonePacket< FFXIVIpcPartyMessage >( m->getId() );
memcpy( &packetMsg->data(), &msg, sizeof( msg ) );
m->queuePacket( packetMsg );
} );
leader->disbandParty();
}
else
{
FFXIVIpcPartyMessage msg = {};
msg.memberContentId = player.getContentId();
leader->foreachPartyMember( [ &player, &leader, &msg ] ( auto m )
{
if( m->getId() == player.getId() )
{
msg.leaderContentId = leader->getContentId();
msg.type = 5;
}
else
{
msg.leaderContentId = 0;
msg.type = 4;
}
auto packetMsg = makeZonePacket< FFXIVIpcPartyMessage >( m->getId() );
memcpy( &packetMsg->data(), &msg, sizeof( msg ) );
m->queuePacket( packetMsg );
} );
leader->removePartyMember( player.getAsPlayer() );
}
}
void Sapphire::Network::GameConnection::kickPartyMemberHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
if( !player.isPartyLeader() )
return;
const auto packetIn = ZoneChannelPacket< Client::FFXIVIpcKickPartyMemberHander >( inPacket );
Sapphire::Entity::PlayerPtr toKick = nullptr;
player.foreachPartyMember( [ &packetIn, &toKick ]( auto m )
{
if( m->getContentId() == packetIn.data().contentId )
{
toKick = m;
}
} );
if( toKick )
{
if( player.getPartySize() <= 2 )
{
FFXIVIpcPartyMessage msg = {};
msg.leaderContentId = player.getContentId();
msg.type = 3;
player.foreachPartyMember( [ msg ] ( auto m )
{
auto packetMsg = makeZonePacket< FFXIVIpcPartyMessage >( m->getId() );
memcpy( &packetMsg->data(), &msg, sizeof( msg ) );
m->queuePacket( packetMsg );
} );
player.disbandParty();
}
else
{
FFXIVIpcPartyMessage msg = {};
msg.leaderContentId = player.getContentId();
msg.memberContentId = toKick->getContentId();
player.foreachPartyMember( [ &toKick, &msg ] ( auto m )
{
if( m->getId() == toKick->getId() )
{
msg.type = 5;
}
else
{
msg.type = 4;
}
auto packetMsg = makeZonePacket< FFXIVIpcPartyMessage >( m->getId() );
memcpy( &packetMsg->data(), &msg, sizeof( msg ) );
m->queuePacket( packetMsg );
} );
player.removePartyMember( toKick );
}
}
}
void Sapphire::Network::GameConnection::disbandPartyHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player )
{
if( !player.isPartyLeader() )
return;
FFXIVIpcPartyMessage msg = {};
msg.leaderContentId = player.getContentId();
msg.type = 3;
player.foreachPartyMember( [ msg ] ( auto m )
{
auto packetMsg = makeZonePacket< FFXIVIpcPartyMessage >( m->getId() );
memcpy( &packetMsg->data(), &msg, sizeof( msg ) );
m->queuePacket( packetMsg );
} );
player.disbandParty();
}

View file

@ -73,6 +73,10 @@ void Sapphire::World::Session::close()
if( m_pPlayer )
{
m_pPlayer->clearBuyBackMap();
if( m_pPlayer->isInParty() )
{
m_pPlayer->getPartyLeader()->removePartyMember( m_pPlayer );
}
// do one last update to db
m_pPlayer->updateSql();
// reset the zone, so the zone handler knows to remove the actor