diff --git a/sql/update.sql b/sql/update.sql index c0f2ed69..67fdb72e 100644 --- a/sql/update.sql +++ b/sql/update.sql @@ -23,5 +23,9 @@ -- ALTER TABLE `charadetail` CHANGE `OpeningSequence` `OpeningSequence` INT(3) NULL DEFAULT '0'; -- ------------------------------------------- -- update.sql Before Merge into Other SQL's 30/08/2017 +-- ------------------------------------------- +-- ALTER TABLE `charadetail` ADD `EquipDisplayFlags` int(3) DEFAULT '0' AFTER `GMRank`; +-- ------------------------------------------- +-- update.sql before titles added 09/10/2017 -ALTER TABLE `charadetail` ADD `EquipDisplayFlags` int(3) DEFAULT '0' AFTER `GMRank`; \ No newline at end of file +ALTER TABLE `charadetail` CHANGE `TitleList` `Titlelist` BINARY(48) NULL DEFAULT NULL; \ No newline at end of file diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h index 12b97937..07889117 100644 --- a/src/servers/Server_Common/Common.h +++ b/src/servers/Server_Common/Common.h @@ -288,6 +288,7 @@ namespace Core { Aetherytes = 0x00000080, // Attuned aetherytes HomePoint = 0x00000100, // Current homepoint HowTo = 0x00000200, + Title = 0x00000400, HpMp = 0x00000800, QuestTracker = 0x00001000, diff --git a/src/servers/Server_Common/Network/PacketDef/Ipcs.h b/src/servers/Server_Common/Network/PacketDef/Ipcs.h index 3ee418d8..16243669 100644 --- a/src/servers/Server_Common/Network/PacketDef/Ipcs.h +++ b/src/servers/Server_Common/Network/PacketDef/Ipcs.h @@ -114,6 +114,7 @@ namespace Packets { ActorFreeSpawn = 0x0191, // unchanged for sb InitZone = 0x019A, // unchanged for sb WeatherChange = 0x01AF, // updated for sb + PlayerTitleList = 0x01B1, // updated for 4.06 Discovery = 0x01B2, // updated for sb EorzeaTimeOffset = 0x01B4, diff --git a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h index 9726addf..778f9a08 100644 --- a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h @@ -639,6 +639,14 @@ struct FFXIVIpcUpdateClassInfo : FFXIVIpcBasePacket uint32_t restedExp; }; +/** + * Structural representation of the packet sent by the server + * to send the titles available to the player + */ +struct FFXIVIpcPlayerTitleList : FFXIVIpcBasePacket +{ + uint8_t titleList[48]; +}; /** * Structural representation of the packet sent by the server diff --git a/src/servers/Server_REST/PlayerMinimal.cpp b/src/servers/Server_REST/PlayerMinimal.cpp index 9643a35b..f8c7e1fe 100644 --- a/src/servers/Server_REST/PlayerMinimal.cpp +++ b/src/servers/Server_REST/PlayerMinimal.cpp @@ -175,6 +175,9 @@ namespace Core { char unlocks[64]; memset( unlocks, 0, 64 ); + char titleList[48]; + memset( titleList, 0, 48 ); + int16_t questTracking[5] = { -1, -1, -1, -1, -1 }; uint16_t size = static_cast< uint16_t >( m_lookMap.size() ); @@ -270,6 +273,7 @@ namespace Core { " unlocks, " " QuestTracking, " " Aetheryte, " + " TitleList, " " GMRank, " " UPDATE_DATE ) " " VALUES (" + std::to_string( m_iD ) + ", " @@ -285,6 +289,7 @@ namespace Core { + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)unlocks, 64 ) ) + "'), " + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)questTracking, 10 ) ) + "'), " + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)aetherytes, 12 ) ) + "')," + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)titleList, 48 ) ) + "')," + std::to_string( m_gmRank ) + ", NOW());" ); diff --git a/src/servers/Server_Zone/Actor/Player.cpp b/src/servers/Server_Zone/Actor/Player.cpp index e407b214..10c4cf2f 100644 --- a/src/servers/Server_Zone/Actor/Player.cpp +++ b/src/servers/Server_Zone/Actor/Player.cpp @@ -1420,10 +1420,39 @@ void Core::Entity::Player::setIsLogin( bool bIsLogin ) m_bIsLogin = bIsLogin; } +uint8_t * Core::Entity::Player::getTitleList() +{ + return m_titleList; +} + +uint16_t Core::Entity::Player::getTitle() const +{ + return m_title; +} + +void Core::Entity::Player::addTitle( uint16_t titleId ) +{ + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( titleId, value, index ); + + m_titleList[index] |= value; + setSyncFlag( PlayerSyncFlags::Title ); +} + void Core::Entity::Player::setTitle( uint16_t titleId ) { + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( titleId, value, index ); + + if ( ( m_titleList[index] & value ) == 0 ) // Player doesn't have title - bail + return; + m_title = titleId; + sendToInRangeSet( ActorControlPacket142( getId(), SetTitle, titleId ), true ); + setSyncFlag( PlayerSyncFlags::Title ); } void Core::Entity::Player::setEquipDisplayFlags( uint8_t state ) diff --git a/src/servers/Server_Zone/Actor/Player.h b/src/servers/Server_Zone/Actor/Player.h index 3f0a3451..4f63533b 100644 --- a/src/servers/Server_Zone/Actor/Player.h +++ b/src/servers/Server_Zone/Actor/Player.h @@ -328,7 +328,13 @@ public: void teleport( uint16_t aetheryteId, uint8_t type = 1 ); /*! prepares zoning / fades out the screen */ void prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadoutTime = 0, uint16_t animation = 0 ); - /*! change player's title */ + /*! get player's title list (available titles) */ + uint8_t * getTitleList(); + /*! get player's active title */ + uint16_t getTitle() const; + /*! add title to player title list */ + void addTitle( uint16_t titleId ); + /*! change player's active title */ void setTitle( uint16_t titleId ); /*! change gear param state */ void setEquipDisplayFlags( uint8_t state ); @@ -572,7 +578,7 @@ private: } m_retainerInfo[8]; uint16_t m_title; - uint16_t m_titleList[32]; + uint8_t m_titleList[48]; uint8_t m_achievement[16]; uint8_t m_howTo[33]; uint8_t m_homePoint; diff --git a/src/servers/Server_Zone/Actor/PlayerSql.cpp b/src/servers/Server_Zone/Actor/PlayerSql.cpp index d8cf4194..32141d70 100644 --- a/src/servers/Server_Zone/Actor/PlayerSql.cpp +++ b/src/servers/Server_Zone/Actor/PlayerSql.cpp @@ -82,7 +82,9 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) "cd.CFPenaltyUntil, " "cd.OpeningSequence, " "cd.GMRank, " - "cd.EquipDisplayFlags " + "cd.EquipDisplayFlags, " + "cd.ActiveTitle, " + "cd.TitleList " // 40 "FROM charabase AS c " " INNER JOIN charadetail AS cd " " ON c.CharacterId = cd.CharacterId " @@ -176,6 +178,9 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) m_gmRank = field[37].get< uint8_t >(); m_equipDisplayFlags = field[38].get< uint8_t >(); + m_title = field[39].get< uint8_t >(); + field[40].getBinary( reinterpret_cast< char* >( m_titleList ), sizeof( m_titleList ) ); + m_pCell = nullptr; if( !loadActiveQuests() || !loadClassData() || !loadSearchInfo() ) @@ -316,6 +321,12 @@ void Core::Entity::Player::createUpdateSql() if( m_updateFlags & PlayerSyncFlags::HowTo ) charaDetailSet.insert( " HowTo = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_howTo ), sizeof( m_howTo ) ) ) + "')" ); + if ( m_updateFlags & PlayerSyncFlags::Title ) + { + charaDetailSet.insert( " ActiveTitle = " + std::to_string( m_title ) ); + charaDetailSet.insert( " TitleList = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_titleList ), sizeof( m_titleList ) ) ) + "')" ); + } + if( m_updateFlags & PlayerSyncFlags::Aetherytes ) charaDetailSet.insert( " Aetheryte = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_aetheryte ), sizeof( m_aetheryte ) ) ) + "')" ); diff --git a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp index acfbad2c..1c352c61 100644 --- a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp +++ b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp @@ -257,13 +257,6 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye pPlayer->sendModel(); pPlayer->sendDebug( "Model updated" ); } - else if ( subCommand == "title" ) - { - uint32_t titleId; - sscanf( params.c_str(), "%d", &titleId ); - - pPlayer->setTitle( titleId ); - } else { pPlayer->sendUrgent( subCommand + " is not a valid SET command." ); @@ -311,6 +304,14 @@ void Core::DebugCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlaye pPlayer->addStatusEffect( effect ); } + else if ( subCommand == "title" ) + { + uint32_t titleId; + sscanf( params.c_str(), "%d", &titleId ); + + pPlayer->addTitle( titleId ); + pPlayer->sendNotice( "Added title (ID: " + std::to_string( titleId ) + ")" ); + } else if( subCommand == "spawn" ) { int32_t model, name; @@ -333,7 +334,6 @@ void Core::DebugCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlaye pPlayer->queuePacket( pPe ); } else if( subCommand == "actrl" ) - { // temporary research packet diff --git a/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp b/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp index 09d76e94..9012ef65 100644 --- a/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp +++ b/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp @@ -118,6 +118,19 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in pPlayer->getCurrentAction()->setInterrupted(); break; } + case 0x12E: // Set player title + { + pPlayer->setTitle( param1 ); + break; + } + case 0x12F: // Get title list + { + GamePacketNew< FFXIVIpcPlayerTitleList, ServerZoneIpcType > titleListPacket( pPlayer->getId() ); + memcpy( titleListPacket.data().titleList, pPlayer->getTitleList(), sizeof( titleListPacket.data().titleList ) ); + + pPlayer->queuePacket( titleListPacket ); + break; + } case 0x133: // Update howtos seen { uint32_t howToId = static_cast< uint32_t >( param1 ); diff --git a/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h b/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h index 00540d78..a2ded446 100644 --- a/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h +++ b/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h @@ -36,35 +36,41 @@ namespace Server { // TODO: temporary gm rank //m_data.gmRank = 0xff; - - m_data.currentMount = 0; m_data.classJob = pPlayer->getClass(); //m_data.status = static_cast< uint8_t >( pPlayer->getStatus() ); + m_data.hPCurr = pPlayer->getHp(); m_data.mPCurr = pPlayer->getMp(); m_data.tPCurr = pPlayer->getTp(); m_data.hPMax = pPlayer->getMaxHp(); m_data.mPMax = pPlayer->getMaxMp(); - m_data.gmRank = pPlayer->getGmRank(); + //m_data.tPMax = 3000; m_data.level = pPlayer->getLevel(); + m_data.gmRank = pPlayer->getGmRank(); memcpy( m_data.look, pPlayer->getLookArray(), 26 ); + auto item = pPlayer->getInventory()->getItemAt( Inventory::GearSet0, Inventory::EquipSlot::MainHand ); if( item ) m_data.mainWeaponModel = item->getModelId1(); m_data.secWeaponModel = pPlayer->getModelSubWeapon(); + m_data.models[0] = pPlayer->getModelForSlot( Inventory::EquipSlot::Head ); m_data.models[1] = pPlayer->getModelForSlot( Inventory::EquipSlot::Body ); m_data.models[2] = pPlayer->getModelForSlot( Inventory::EquipSlot::Hands ); m_data.models[3] = pPlayer->getModelForSlot( Inventory::EquipSlot::Legs ); m_data.models[4] = pPlayer->getModelForSlot( Inventory::EquipSlot::Feet ); strcpy( m_data.name, pPlayer->getName().c_str() ); + m_data.pos.x = pPlayer->getPos().x; m_data.pos.y = pPlayer->getPos().y; m_data.pos.z = pPlayer->getPos().z; - m_data.voice = pPlayer->getVoiceId(); - m_data.rotation = Math::Util::floatToUInt16Rot( pPlayer->getRotation() ); + + + m_data.title = pPlayer->getTitle(); + m_data.voice = pPlayer->getVoiceId(); + m_data.currentMount = 0; m_data.onlineStatus = static_cast< uint8_t >( pPlayer->getOnlineStatus() );