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

Implement party just enough to gpose.

This commit is contained in:
collett 2020-07-03 04:22:45 +09:00
parent d60b68cfa2
commit e80d51ff72
11 changed files with 761 additions and 55 deletions

View file

@ -367,7 +367,7 @@ namespace Sapphire::Network::ActorControl
AllotAttribute = 0x135, AllotAttribute = 0x135,
ClearFieldMarkers = 0x13A, 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 CharaNameReq = 0x13D, // requests character name by content id
HuntingLogDetails = 0x194, HuntingLogDetails = 0x194,

View file

@ -79,6 +79,7 @@ namespace Sapphire::Network::Packets
LogMessage = 0x00D0, LogMessage = 0x00D0,
Chat = 0x0161, // updated 5.25 Chat = 0x0161, // updated 5.25
PartyChat = 0x0065, // updated 5.25
WorldVisitList = 0xF0FE, // added 4.5 WorldVisitList = 0xF0FE, // added 4.5
@ -145,7 +146,7 @@ namespace Sapphire::Network::Packets
ActorCast = 0x00C4, // updated 5.25 ActorCast = 0x00C4, // updated 5.25
SomeCustomiseChangePacketProbably = 0x00CD, // added 5.18 SomeCustomiseChangePacketProbably = 0x00CD, // added 5.18
PartyList = 0x0287, // updated 5.18 PartyList = 0x01B7, // updated 5.25
HateRank = 0x012E, // updated 5.25 HateRank = 0x012E, // updated 5.25
HateList = 0x02C4, // updated 5.25 HateList = 0x02C4, // updated 5.25
ObjectSpawn = 0x00D2, // updated 5.25 ObjectSpawn = 0x00D2, // updated 5.25
@ -228,6 +229,13 @@ namespace Sapphire::Network::Packets
EquipDisplayFlags = 0x010D, // updated 5.25 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 ////////////////////////////////////// /// Housing //////////////////////////////////////
LandSetInitialize = 0x0234, // updated 5.0 LandSetInitialize = 0x0234, // updated 5.0
@ -304,6 +312,7 @@ namespace Sapphire::Network::Packets
CreateCrossWorldLS = 0x00AF, // updated 4.3 CreateCrossWorldLS = 0x00AF, // updated 4.3
ChatHandler = 0x0189, // updated 5.25 ChatHandler = 0x0189, // updated 5.25
PartyChatHandler = 0x0065, // update 5.25
SocialListHandler = 0x0371, // updated 5.25 SocialListHandler = 0x0371, // updated 5.25
SetSearchInfoHandler = 0x00FA, // updated 5.25 SetSearchInfoHandler = 0x00FA, // updated 5.25
@ -380,8 +389,13 @@ namespace Sapphire::Network::Packets
PerformNoteHandler = 0x029B, // updated 4.3 PerformNoteHandler = 0x029B, // updated 4.3
WorldInteractionHandler = 0x0285, // updated 5.25 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]; /* 001A */ char message[1012];
}; };
struct FFXIVIpcPartyChatHandler :
FFXIVIpcBasePacket< ChatHandler >
{
uint64_t unknown;
char message[1024];
};
struct FFXIVIpcShopEventHandler : struct FFXIVIpcShopEventHandler :
FFXIVIpcBasePacket< ShopEventHandler > FFXIVIpcBasePacket< ShopEventHandler >
{ {
@ -355,6 +362,61 @@ struct FFXIVIpcWorldInteractionHandler :
Common::FFXIVARR_POSITION3 position; 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 #endif //_CORE_NETWORK_PACKETS_ZONE_CLIENT_IPC_H

View file

@ -46,6 +46,19 @@ namespace Sapphire::Network::Packets::Server
char msg[1012]; 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 > struct FFXIVIpcChatBanned : FFXIVIpcBasePacket< ChatBanned >
{ {
uint8_t padding[4]; // I was not sure reinterpreting ZST is valid behavior in C++. 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; 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*/ #endif /*_CORE_NETWORK_PACKETS_SERVER_IPC_H*/

View file

@ -232,6 +232,10 @@ Sapphire::Common::OnlineStatus Sapphire::Entity::Player::getOnlineStatus() const
void Sapphire::Entity::Player::setOnlineStatusMask( uint64_t status ) void Sapphire::Entity::Player::setOnlineStatusMask( uint64_t status )
{ {
m_onlineStatus = 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 uint64_t Sapphire::Entity::Player::getOnlineStatusMask() const
@ -1047,7 +1051,6 @@ bool Sapphire::Entity::Player::hasStateFlag( Common::PlayerStateFlag flag ) cons
void Sapphire::Entity::Player::setStateFlag( Common::PlayerStateFlag flag ) void Sapphire::Entity::Player::setStateFlag( Common::PlayerStateFlag flag )
{ {
auto prevOnlineStatus = getOnlineStatus();
int32_t iFlag = static_cast< uint32_t >( flag ); int32_t iFlag = static_cast< uint32_t >( flag );
uint16_t index; uint16_t index;
@ -1056,13 +1059,6 @@ void Sapphire::Entity::Player::setStateFlag( Common::PlayerStateFlag flag )
m_stateFlags[ index ] |= value; m_stateFlags[ index ] |= value;
sendStateFlags(); 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 ) void Sapphire::Entity::Player::setStateFlags( std::vector< Common::PlayerStateFlag > flags )
@ -1083,8 +1079,6 @@ void Sapphire::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag )
if( !hasStateFlag( flag ) ) if( !hasStateFlag( flag ) )
return; return;
auto prevOnlineStatus = getOnlineStatus();
int32_t iFlag = static_cast< uint32_t >( flag ); int32_t iFlag = static_cast< uint32_t >( flag );
uint16_t index; uint16_t index;
@ -1093,11 +1087,6 @@ void Sapphire::Entity::Player::unsetStateFlag( Common::PlayerStateFlag flag )
m_stateFlags[ index ] ^= value; m_stateFlags[ index ] ^= value;
sendStateFlags(); 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 ) void Sapphire::Entity::Player::update( uint64_t tickCount )
@ -2269,6 +2258,218 @@ void Sapphire::Entity::Player::clearBuyBackMap()
m_shopBuyBackMap.clear(); 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() void Sapphire::Entity::Player::gaugeClear()
{ {
std::memset( &m_gauge, 0, sizeof( m_gauge ) ); std::memset( &m_gauge, 0, sizeof( m_gauge ) );

View file

@ -990,8 +990,29 @@ namespace Sapphire::Entity
void sendActorGauge(); void sendActorGauge();
void gaugeSetRaw( uint8_t* pData ); void gaugeSetRaw( uint8_t* pData );
// 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);
//////////////////////////////////////////////////////////////////////////////////////////////////////
Common::HuntingLogEntry& getHuntingLogEntry( uint8_t index ); Common::HuntingLogEntry& getHuntingLogEntry( uint8_t index );
void sendHuntingLog(); void sendHuntingLog();

View file

@ -134,7 +134,14 @@ Sapphire::Network::GameConnection::GameConnection( Sapphire::Network::HivePtr pH
setZoneHandler( ClientZoneIpcType::WorldInteractionHandler, "WorldInteractionHandler", &GameConnection::worldInteractionhandler ); setZoneHandler( ClientZoneIpcType::WorldInteractionHandler, "WorldInteractionHandler", &GameConnection::worldInteractionhandler );
setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler ); 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; Sapphire::Network::GameConnection::~GameConnection() = default;

View file

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

View file

@ -494,7 +494,18 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX
player.sendDebug( "event battle level sync: {0}, ilevel sync?: {1}", param12, param2 ); player.sendDebug( "event battle level sync: {0}, ilevel sync?: {1}", param12, param2 );
break; break;
} }
case ClientTriggerType::CameraMode:
{
if( param11 == 1 )
{
player.setOnlineStatusMask( player.getOnlineStatusMask() | 0x0000000000040000ui64 );
}
else
{
player.setOnlineStatusMask( player.getOnlineStatusMask() & 0xFFFFFFFFFFFBFFFFui64 );
}
break;
}
default: default:
{ {
Logger::debug( "[{0}] Unhandled action: {1:04X}", m_pSession->getId(), commandId ); Logger::debug( "[{0}] Unhandled action: {1:04X}", m_pSession->getId(), commandId );

View file

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

View file

@ -87,18 +87,11 @@ void Sapphire::Network::GameConnection::setSearchInfoHandler( const Packets::FFX
// mark player as new adventurer // mark player as new adventurer
player.setNewAdventurer( true ); player.setNewAdventurer( true );
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( player.getId() );
statusPacket->data().onlineStatusFlags = status;
queueOutPacket( statusPacket );
auto searchInfoPacket = makeZonePacket< FFXIVIpcSetSearchInfo >( player.getId() ); auto searchInfoPacket = makeZonePacket< FFXIVIpcSetSearchInfo >( player.getId() );
searchInfoPacket->data().onlineStatusFlags = status; searchInfoPacket->data().onlineStatusFlags = status;
searchInfoPacket->data().selectRegion = player.getSearchSelectRegion(); searchInfoPacket->data().selectRegion = player.getSearchSelectRegion();
strcpy( searchInfoPacket->data().searchMessage, player.getSearchMessage() ); strcpy( searchInfoPacket->data().searchMessage, player.getSearchMessage() );
queueOutPacket( searchInfoPacket ); 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, 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 ); int32_t entrysizes = sizeof( listPacket->data().entries );
memset( listPacket->data().entries, 0, sizeof( listPacket->data().entries ) ); memset( listPacket->data().entries, 0, sizeof( listPacket->data().entries ) );
listPacket->data().entries[ 0 ].bytes[ 2 ] = player.getCurrentTerritory()->getTerritoryTypeId(); auto fillEntryAt = [ &listPacket ]( int i, Entity::PlayerPtr nextPlayer )
listPacket->data().entries[ 0 ].bytes[ 3 ] = 0x80; {
listPacket->data().entries[ 0 ].bytes[ 4 ] = 0x02; listPacket->data().entries[ i ].bytes[ 2 ] = nextPlayer->getCurrentTerritory()->getTerritoryTypeId();
listPacket->data().entries[ 0 ].bytes[ 6 ] = 0x3B; listPacket->data().entries[ i ].bytes[ 3 ] = 0x80;
listPacket->data().entries[ 0 ].bytes[ 11 ] = 0x10; listPacket->data().entries[ i ].bytes[ 4 ] = 0x02;
listPacket->data().entries[ 0 ].classJob = static_cast< uint8_t >( player.getClass() ); listPacket->data().entries[ i ].bytes[ 6 ] = 0x3B;
listPacket->data().entries[ 0 ].contentId = player.getContentId(); listPacket->data().entries[ i ].bytes[ 8 ] = nextPlayer->isPartyLeader() ? 1 : 0;
listPacket->data().entries[ 0 ].level = player.getLevel(); listPacket->data().entries[ i ].bytes[ 11 ] = 0x10;
listPacket->data().entries[ 0 ].zoneId = player.getCurrentTerritory()->getTerritoryTypeId(); listPacket->data().entries[ i ].classJob = static_cast< uint8_t >( nextPlayer->getClass() );
listPacket->data().entries[ 0 ].zoneId1 = 0x0100; 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 // TODO: no idea what this does
//listPacket.data().entries[0].one = 1; //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 // GC icon
listPacket->data().entries[ 0 ].bytes1[ 0 ] = 2; listPacket->data().entries[ i ].bytes1[ 0 ] = 2;
// client language J = 0, E = 1, D = 2, F = 3 // client language J = 0, E = 1, D = 2, F = 3
listPacket->data().entries[ 0 ].bytes1[ 1 ] = 1; listPacket->data().entries[ i ].bytes1[ 1 ] = 1;
// user language settings flag J = 1, E = 2, D = 4, F = 8 // user language settings flag J = 1, E = 2, D = 4, F = 8
listPacket->data().entries[ 0 ].bytes1[ 2 ] = 1 + 2; listPacket->data().entries[ i ].bytes1[ 2 ] = 1 + 2;
listPacket->data().entries[ 0 ].onlineStatusMask = player.getOnlineStatusMask(); 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 ); queueOutPacket( listPacket );
} }
else if( type == 2 ) else if( type == 2 )
{ // friend list { // 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, void Sapphire::Network::GameConnection::chatHandler( const Packets::FFXIVARR_PACKET_RAW& inPacket,
Entity::Player& player ) Entity::Player& player )
{ {
@ -793,3 +830,260 @@ void Sapphire::Network::GameConnection::worldInteractionhandler( const Packets::
} }
} }
} }
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();
}