From f4a3c9493b45ec2970fe3f87cdfbfbe3f47302fb Mon Sep 17 00:00:00 2001 From: Lucy <44952533+Skyliegirl33@users.noreply.github.com> Date: Thu, 9 Mar 2023 17:17:27 +0100 Subject: [PATCH] Persistence for cross-class skills --- .../20230309164293_AddBorrowAction.sql | 1 + src/api/PlayerMinimal.cpp | 4 +++- src/common/Common.h | 1 + src/common/Database/ZoneDbConnection.cpp | 6 +++--- src/world/Actor/Player.cpp | 16 +++++++++++++++ src/world/Actor/Player.h | 7 +++++++ src/world/Actor/PlayerSql.cpp | 20 +++++++++++++++---- .../Network/Handlers/PacketCommandHandler.cpp | 5 +++++ src/world/Network/Util/PlayerUtil.cpp | 2 ++ 9 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 sql/migrations/20230309164293_AddBorrowAction.sql diff --git a/sql/migrations/20230309164293_AddBorrowAction.sql b/sql/migrations/20230309164293_AddBorrowAction.sql new file mode 100644 index 00000000..1cdc3f22 --- /dev/null +++ b/sql/migrations/20230309164293_AddBorrowAction.sql @@ -0,0 +1 @@ +ALTER TABLE `characlass` ADD `BorrowAction` binary(40) DEFAULT NULL NULL AFTER `Lvl`; \ No newline at end of file diff --git a/src/api/PlayerMinimal.cpp b/src/api/PlayerMinimal.cpp index c761ac04..f383a96d 100644 --- a/src/api/PlayerMinimal.cpp +++ b/src/api/PlayerMinimal.cpp @@ -256,12 +256,14 @@ void PlayerMinimal::saveAsNew() break; } - // CharacterId, ClassIdx, Exp, Lvl + // CharacterId, ClassIdx, Exp, Lvl, BorrowAction auto stmtClass = g_charaDb.getPreparedStatement( Db::ZoneDbStatements::CHARA_CLASS_INS ); stmtClass->setUInt64( 1, m_characterId ); stmtClass->setInt( 2, g_exdData.getRow< Excel::ClassJob >( m_class )->data().WorkIndex ); stmtClass->setInt( 3, 0 ); stmtClass->setInt( 4, 1 ); + std::vector< uint8_t > borrowActionVec( Common::ARRSIZE_BORROWACTION * 4 ); + stmtClass->setBinary( 5, borrowActionVec ); g_charaDb.directExecute( stmtClass ); auto stmtSearchInfo = g_charaDb.getPreparedStatement( Db::ZoneDbStatements::CHARA_SEARCHINFO_INS ); diff --git a/src/common/Common.h b/src/common/Common.h index 9f327162..2fbb197c 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -37,6 +37,7 @@ namespace Sapphire::Common const uint16_t ARRSIZE_UNLOCKS = 64u; const uint16_t ARRSIZE_ORCHESTRION = 40u; const uint16_t ARRSIZE_MONSTERNOTE = 12u; + const uint16_t ARRSIZE_BORROWACTION = 10u; const uint8_t TOWN_COUNT = 6; diff --git a/src/common/Database/ZoneDbConnection.cpp b/src/common/Database/ZoneDbConnection.cpp index 10f9f854..6a0f79a0 100644 --- a/src/common/Database/ZoneDbConnection.cpp +++ b/src/common/Database/ZoneDbConnection.cpp @@ -161,11 +161,11 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements() prepareStatement( CHARA_SEL_QUEST, "SELECT * FROM charaquest WHERE CharacterId = ?;", CONNECTION_SYNC ); /// CLASS INFO - prepareStatement( CHARA_CLASS_SEL, "SELECT ClassIdx, Exp, Lvl FROM characlass WHERE CharacterId = ?;", + prepareStatement( CHARA_CLASS_SEL, "SELECT ClassIdx, Exp, Lvl, BorrowAction FROM characlass WHERE CharacterId = ?;", CONNECTION_SYNC ); - prepareStatement( CHARA_CLASS_INS, "INSERT INTO characlass ( CharacterId, ClassIdx, Exp, Lvl ) VALUES( ?,?,?,? );", + prepareStatement( CHARA_CLASS_INS, "INSERT INTO characlass ( CharacterId, ClassIdx, Exp, Lvl, BorrowAction ) VALUES( ?,?,?,?,? );", CONNECTION_BOTH ); - prepareStatement( CHARA_CLASS_UP, "UPDATE characlass SET Exp = ?, Lvl = ? WHERE CharacterId = ? AND ClassIdx = ?;", + prepareStatement( CHARA_CLASS_UP, "UPDATE characlass SET Exp = ?, Lvl = ?, BorrowAction = ? WHERE CharacterId = ? AND ClassIdx = ?;", CONNECTION_ASYNC ); prepareStatement( CHARA_CLASS_DEL, "DELETE FROM characlass WHERE CharacterId = ?;", CONNECTION_ASYNC ); diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index 2ce6e2c3..2ad26bf2 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -563,6 +563,22 @@ void Player::setRewardFlag( Common::UnlockEntry unlockId ) Network::Util::Player::sendActorControlSelf( *this, SetRewardFlag, unlock, 1 ); } +void Player::setBorrowAction( uint8_t slot, uint32_t action ) +{ + if( slot > Common::ARRSIZE_BORROWACTION ) + return; + + auto& borrowAction = getBorrowAction(); + borrowAction[ slot ] = action; +} + +Player::BorrowAction& Player::getBorrowAction() +{ + auto& exdData = Common::Service< Data::ExdData >::ref(); + uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast( getClass() ) )->data().WorkIndex; + return m_borrowActions[ classJobIndex ]; +} + void Player::learnSong( uint8_t songId, uint32_t itemId ) { uint16_t index; diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 51425acc..e09a35ac 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -38,6 +38,7 @@ namespace Sapphire::Entity using ClassList = std::array< uint16_t, Common::ARRSIZE_CLASSJOB >; using ExpList = std::array< uint32_t, Common::ARRSIZE_CLASSJOB >; + using BorrowAction = std::array< uint32_t, Common::ARRSIZE_BORROWACTION >; struct AchievementData { std::array< uint8_t, 2048 / 8 > unlockList; @@ -446,6 +447,10 @@ namespace Sapphire::Entity /*! learn an action / update the unlock bitmask. */ void setRewardFlag( Common::UnlockEntry unlockId ); + void setBorrowAction( uint8_t slot, uint32_t action ); + + BorrowAction& getBorrowAction(); + /*! learn a song / update the unlock bitmask. */ void learnSong( uint8_t songId, uint32_t itemId ); @@ -966,6 +971,8 @@ namespace Sapphire::Entity std::array< Common::HuntingLogEntry, Common::ARRSIZE_MONSTERNOTE > m_huntingLogEntries{}; + std::array< BorrowAction, Common::ARRSIZE_CLASSJOB > m_borrowActions{}; + FriendListIDVec m_friendList{}; FriendListDataVec m_friendInviteList{}; diff --git a/src/world/Actor/PlayerSql.cpp b/src/world/Actor/PlayerSql.cpp index 58d3123c..4ee1b283 100644 --- a/src/world/Actor/PlayerSql.cpp +++ b/src/world/Actor/PlayerSql.cpp @@ -258,7 +258,7 @@ bool Sapphire::Entity::Player::loadAchievements() bool Sapphire::Entity::Player::loadClassData() { auto& db = Common::Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::ref(); - // ClassIdx, Exp, Lvl + // ClassIdx, Exp, Lvl, BorrowAction auto stmt = db.getPreparedStatement( Db::ZoneDbStatements::CHARA_CLASS_SEL ); stmt->setUInt64( 1, m_characterId ); auto res = db.query( stmt ); @@ -268,9 +268,11 @@ bool Sapphire::Entity::Player::loadClassData() auto index = res->getUInt16( 1 ); auto exp = res->getUInt( 2 ); auto lvl = res->getUInt8( 3 ); + auto borrowAction = res->getBlobVector( "BorrowAction" ); m_classArray[ index ] = lvl; m_expArray[ index ] = exp; + memcpy( m_borrowActions[ index ].data(), borrowAction.data(), borrowAction.size() ); } return true; @@ -487,13 +489,19 @@ void Sapphire::Entity::Player::updateDbClass() const auto& db = Common::Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::ref(); auto& exdData = Common::Service< Data::ExdData >::ref(); uint8_t classJobIndex = exdData.getRow< Excel::ClassJob >( static_cast( getClass() ) )->data().WorkIndex; + auto& borrowAction = m_borrowActions[ classJobIndex ]; - //Exp = ?, Lvl = ? WHERE CharacterId = ? AND ClassIdx = ? + //Exp = ?, Lvl = ?, BorrowAction = ? WHERE CharacterId = ? AND ClassIdx = ? auto stmtS = db.getPreparedStatement( Db::CHARA_CLASS_UP ); stmtS->setInt( 1, getExp() ); stmtS->setInt( 2, getLevel() ); - stmtS->setUInt64( 3, m_characterId ); - stmtS->setInt( 4, classJobIndex ); + + std::vector< uint8_t > borrowActionVec( borrowAction.size() * 4 ); + memcpy( borrowActionVec.data(), borrowAction.data(), borrowAction.size() * 4 ); + stmtS->setBinary( 3, borrowActionVec ); + + stmtS->setUInt64( 4, m_characterId ); + stmtS->setInt( 5, classJobIndex ); db.execute( stmtS ); } @@ -581,10 +589,14 @@ void Sapphire::Entity::Player::insertDbClass( const uint8_t classJobIndex, uint8 { auto& db = Common::Service< Db::DbWorkerPool< Db::ZoneDbConnection > >::ref(); auto stmtClass = db.getPreparedStatement( Db::CHARA_CLASS_INS ); + auto& borrowAction = m_borrowActions[ classJobIndex ]; + stmtClass->setUInt64( 1, m_characterId ); stmtClass->setInt( 2, classJobIndex ); stmtClass->setInt( 3, 0 ); stmtClass->setInt( 4, level ); + std::vector< uint8_t > borrowActionVec( borrowAction.size() ); + stmtClass->setBinary( 5, borrowActionVec ); db.directExecute( stmtClass ); } diff --git a/src/world/Network/Handlers/PacketCommandHandler.cpp b/src/world/Network/Handlers/PacketCommandHandler.cpp index 54acc210..b95c9703 100644 --- a/src/world/Network/Handlers/PacketCommandHandler.cpp +++ b/src/world/Network/Handlers/PacketCommandHandler.cpp @@ -497,6 +497,11 @@ void Sapphire::Network::GameConnection::commandHandler( const Packets::FFXIVARR_ Network::Util::Player::sendTitleList( player ); break; } + case PacketCommand::BORROW_ACTION: + { + player.setBorrowAction( static_cast< uint8_t >( data.Arg1 ), data.Arg2 ); + break; + } case PacketCommand::SET_HOWTO: // Update howtos seen { player.updateHowtosSeen( data.Arg0 ); diff --git a/src/world/Network/Util/PlayerUtil.cpp b/src/world/Network/Util/PlayerUtil.cpp index f9f3d611..45d05d51 100644 --- a/src/world/Network/Util/PlayerUtil.cpp +++ b/src/world/Network/Util/PlayerUtil.cpp @@ -291,10 +291,12 @@ void Util::Player::sendPlayerSetup( Entity::Player& player ) void Util::Player::sendChangeClass( Entity::Player& player ) { auto classInfo = makeZonePacket< FFXIVIpcChangeClass >( player.getId() ); + auto& borrowAction = player.getBorrowAction(); classInfo->data().ClassJob = static_cast< uint8_t >( player.getClass() ); classInfo->data().Lv = player.getLevel(); classInfo->data().Lv1 = player.getLevel(); classInfo->data().Login = player.isLogin() ? 1 : 0; + memcpy( &classInfo->data().BorrowAction[ 0 ], borrowAction.data(), borrowAction.size() * 4 ); server().queueForPlayer( player.getCharacterId(), classInfo ); }