From 2f1388084125742b8bc89b89c1a0be3ebbb4fd33 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sun, 24 Mar 2019 14:25:00 +1100 Subject: [PATCH 1/8] add a bunch of stat calc functions in --- src/world/Math/CalcStats.cpp | 132 ++++++++++++++++++++++++++++++++--- src/world/Math/CalcStats.h | 81 ++++++++++++++++++++- 2 files changed, 201 insertions(+), 12 deletions(-) diff --git a/src/world/Math/CalcStats.cpp b/src/world/Math/CalcStats.cpp index 3d26dd3f..3e51ae26 100644 --- a/src/world/Math/CalcStats.cpp +++ b/src/world/Math/CalcStats.cpp @@ -265,16 +265,16 @@ uint16_t CalcStats::calculateMpCost( const Sapphire::Entity::Chara& chara, uint1 return static_cast< uint16_t >( std::round( cost * baseCost ) ); } -float CalcStats::pBlk( const Chara& chara ) +float CalcStats::blockProbability( const Chara& chara ) { auto level = chara.getLevel(); - float blockRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::BlockRate ) ); - float levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + auto blockRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::BlockRate ) ); + auto levelVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); return std::floor( ( 30 * blockRate ) / levelVal + 10 ); } -float CalcStats::pDhr( const Chara& chara ) +float CalcStats::directHitProbability( const Chara& chara ) { const auto& baseStats = chara.getStats(); auto level = chara.getLevel(); @@ -282,13 +282,13 @@ float CalcStats::pDhr( const Chara& chara ) float dhRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::DirectHitRate ) ) + baseStats.accuracy; - float divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); - float subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); return std::floor( 550.f * ( dhRate - subVal ) / divVal ) / 10.f; } -float CalcStats::pChr( const Chara& chara ) +float CalcStats::criticalHitProbability( const Chara& chara ) { const auto& baseStats = chara.getStats(); auto level = chara.getLevel(); @@ -296,8 +296,122 @@ float CalcStats::pChr( const Chara& chara ) float chRate = static_cast< float >( chara.getBonusStat( Common::BaseParam::CriticalHit ) ) + baseStats.critHitRate; - float divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); - float subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); return std::floor( 200.f * ( chRate - subVal ) / divVal + 50.f ) / 10.f; } + + +float CalcStats::potency( uint16_t potency ) +{ + return potency / 100.f; +} + +float CalcStats::weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage, bool isMagicDamage ) +{ + const auto& baseStats = chara.getStats(); + auto level = chara.getLevel(); + + auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] ); + + float jobAttribute = 1.f; +} + +// todo: this is all retarded, needs to be per weapon and etcetc +//uint32_t CalcStats::getPrimaryClassJobAttribute( const Sapphire::Entity::Chara& chara ) +//{ +// +//} + +float CalcStats::calcAttackPower( uint32_t attackPower ) +{ + return std::floor( ( 125.f * ( attackPower - 292.f ) / 292.f ) + 100.f ) / 100.f; +} + +float CalcStats::magicAttackPower( const Sapphire::Entity::Chara& chara ) +{ + const auto& baseStats = chara.getStats(); + + return calcAttackPower( baseStats.attackPotMagic ); +} + +float CalcStats::healingMagicPower( const Sapphire::Entity::Chara& chara ) +{ + const auto& baseStats = chara.getStats(); + + return calcAttackPower( baseStats.healingPotMagic ); +} + +float CalcStats::attackPower( const Sapphire::Entity::Chara& chara ) +{ + const auto& baseStats = chara.getStats(); + + return calcAttackPower( baseStats.attack ); +} + +float CalcStats::determination( const Sapphire::Entity::Chara& chara ) +{ + auto level = chara.getLevel(); + const auto& baseStats = chara.getStats(); + + auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] ); + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + + return std::floor( 130.f * ( baseStats.determination - mainVal ) / divVal + 1000.f ) / 1000.f; +} + +float CalcStats::tenacity( const Sapphire::Entity::Chara& chara ) +{ + auto level = chara.getLevel(); + const auto& baseStats = chara.getStats(); + + auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + + return std::floor( 100.f * ( baseStats.tenacity - subVal ) / divVal + 1000.f ) / 1000.f; +} + +float CalcStats::speed( const Sapphire::Entity::Chara& chara ) +{ + auto level = chara.getLevel(); + const auto& baseStats = chara.getStats(); + + auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + + uint32_t speedVal = 0; + + // check whether we use spellspeed or skillspeed + // todo: this is kinda shitty though + switch( chara.getClass() ) + { + case Common::ClassJob::Arcanist: + case Common::ClassJob::Astrologian: + case Common::ClassJob::Whitemage: + case Common::ClassJob::Redmage: + case Common::ClassJob::Bluemage: + case Common::ClassJob::Blackmage: + case Common::ClassJob::Summoner: + case Common::ClassJob::Scholar: + case Common::ClassJob::Thaumaturge: + speedVal = baseStats.spellSpeed; + break; + + default: + speedVal = baseStats.skillSpeed; + } + + return std::floor( 130.f * ( speedVal - subVal ) / divVal + 1000.f ) / 1000.f; +} + +float CalcStats::criticalHitBonus( const Sapphire::Entity::Chara& chara ) +{ + auto level = chara.getLevel(); + const auto& baseStats = chara.getStats(); + + auto subVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::SUB ] ); + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + + return std::floor( 200.f * ( baseStats.critHitRate - subVal ) / divVal + 1400.f ) / 1000.f; +} \ No newline at end of file diff --git a/src/world/Math/CalcStats.h b/src/world/Math/CalcStats.h index e49b7b14..a7078f6c 100644 --- a/src/world/Math/CalcStats.h +++ b/src/world/Math/CalcStats.h @@ -28,22 +28,97 @@ namespace Sapphire::Math * @brief Calculates the probability of a block happening * @return */ - static float pBlk( const Sapphire::Entity::Chara& ); + static float blockProbability( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the probability of a direct hit happening * @return */ - static float pDhr( const Sapphire::Entity::Chara& ); + static float directHitProbability( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the probability of a critical hit happening * @return */ - static float pChr( const Sapphire::Entity::Chara& ); + static float criticalHitProbability( const Sapphire::Entity::Chara& chara ); + + /*! + * @brief Calculates the contribution of potency to damage output. + * @param potency The action potency + * @return + */ + static float potency( uint16_t potency ); + + /*! + * @brief Weapon damage is the contribution the weapon's damage rating + * @param chara The source/casting character. + * @param weaponDamage the weapons physical or magic damage + * @param isMagicDamage true if the damage is magical, otherwise it's treated as physical damage + * @return + */ + static float weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage, bool isMagicDamage ); + + /*! + * @brief Calculates the contribution of physical attack power to damage dealt + * @param chara The source/casting character. + * @return + */ + static float attackPower( const Sapphire::Entity::Chara& chara ); + + /*! + * @brief Calculates the contribution of magical attack power to damage dealt + * @param chara The source/casting character. + * @return + */ + static float magicAttackPower( const Sapphire::Entity::Chara& chara ); + + /*! + * @brief Calculates the contribution of healing magic power to healing dealt + * @param chara The source/casting character. + * @return + */ + static float healingMagicPower( const Sapphire::Entity::Chara& chara ); + + /*! + * @brief Calculates determinations contribution to damage and healing output. + * @param chara The source/casting character. + * @return Returns a rational number rounded to 3 decimal places. + */ + static float determination( const Sapphire::Entity::Chara& chara ); + + /*! + * @brief Calculates the tenacity contribution to damage, mitigation and healing. + * @param chara The source/casting character. + * @return Returns a rational number rounded to 3 decimal places. + */ + static float tenacity( const Sapphire::Entity::Chara& chara ); + + /*! + * @brief Calculates the bonus granted by either spell speed or skill speed depending on the casters classjob. + * @param chara The source/casting character. + * @return + */ + static float speed( const Sapphire::Entity::Chara& chara ); + + /*! + * @brief Calculates the amount of bonus damaged applied on a critical hit + * @param chara + * @return + */ + static float criticalHitBonus( const Sapphire::Entity::Chara& chara ); + + static float physicalDefense( const Sapphire::Entity::Chara& chara ); + + static float magicDefense( const Sapphire::Entity::Chara& chara ); + + static float blockStrength( const Sapphire::Entity::Chara& chara ); private: + static uint32_t getPrimaryClassJobAttribute( const Sapphire::Entity::Chara& chara ); + + static float calcAttackPower( uint32_t attackPower ); + }; } From 892069ed51b53b1494d53fdcc1de9800e92bba6d Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sun, 24 Mar 2019 16:04:04 +1100 Subject: [PATCH 2/8] more stat calc functions, some are missing still --- src/world/Math/CalcStats.cpp | 32 ++++++++++++++++++++ src/world/Math/CalcStats.h | 58 ++++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 13 deletions(-) diff --git a/src/world/Math/CalcStats.cpp b/src/world/Math/CalcStats.cpp index 3e51ae26..e66764a7 100644 --- a/src/world/Math/CalcStats.cpp +++ b/src/world/Math/CalcStats.cpp @@ -414,4 +414,36 @@ float CalcStats::criticalHitBonus( const Sapphire::Entity::Chara& chara ) auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); return std::floor( 200.f * ( baseStats.critHitRate - subVal ) / divVal + 1400.f ) / 1000.f; +} + +float CalcStats::physicalDefence( const Sapphire::Entity::Chara& chara ) +{ + auto level = chara.getLevel(); + const auto& baseStats = chara.getStats(); + + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + + return std::floor( 15.f * baseStats.defense ) / 100.f; +} + +float CalcStats::magicDefence( const Sapphire::Entity::Chara& chara ) +{ + auto level = chara.getLevel(); + const auto& baseStats = chara.getStats(); + + auto divVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::DIV ] ); + + return std::floor( 15.f * baseStats.magicDefense ) / 100.f; +} + +float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara ) +{ + +} + +float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara ) +{ + const auto& baseStats = chara.getStats(); + + return std::floor( 100.f * ( baseStats.healingPotMagic - 292.f ) / 264.f + 100.f ) / 100.f; } \ No newline at end of file diff --git a/src/world/Math/CalcStats.h b/src/world/Math/CalcStats.h index a7078f6c..6aab838d 100644 --- a/src/world/Math/CalcStats.h +++ b/src/world/Math/CalcStats.h @@ -18,6 +18,7 @@ namespace Sapphire::Math /*! * @brief Calculates the MP cost of a spell given its base cost + * * @param chara The Chara that is casting the action * @param baseCost The action cost * @return The total MP to be consumed by a successful cast @@ -26,61 +27,61 @@ namespace Sapphire::Math /*! * @brief Calculates the probability of a block happening - * @return */ static float blockProbability( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the probability of a direct hit happening - * @return */ static float directHitProbability( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the probability of a critical hit happening - * @return */ static float criticalHitProbability( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the contribution of potency to damage output. + * * @param potency The action potency - * @return */ static float potency( uint16_t potency ); /*! * @brief Weapon damage is the contribution the weapon's damage rating + * * @param chara The source/casting character. * @param weaponDamage the weapons physical or magic damage * @param isMagicDamage true if the damage is magical, otherwise it's treated as physical damage - * @return */ static float weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage, bool isMagicDamage ); /*! * @brief Calculates the contribution of physical attack power to damage dealt + * @todo Only works at level 70 + * * @param chara The source/casting character. - * @return */ static float attackPower( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the contribution of magical attack power to damage dealt + * @todo Only works at level 70 + * * @param chara The source/casting character. - * @return */ static float magicAttackPower( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the contribution of healing magic power to healing dealt + * * @param chara The source/casting character. - * @return */ static float healingMagicPower( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates determinations contribution to damage and healing output. + * * @param chara The source/casting character. * @return Returns a rational number rounded to 3 decimal places. */ @@ -88,6 +89,7 @@ namespace Sapphire::Math /*! * @brief Calculates the tenacity contribution to damage, mitigation and healing. + * * @param chara The source/casting character. * @return Returns a rational number rounded to 3 decimal places. */ @@ -95,28 +97,58 @@ namespace Sapphire::Math /*! * @brief Calculates the bonus granted by either spell speed or skill speed depending on the casters classjob. + * * @param chara The source/casting character. - * @return */ static float speed( const Sapphire::Entity::Chara& chara ); /*! * @brief Calculates the amount of bonus damaged applied on a critical hit - * @param chara - * @return + * @note Called Critical Hit Rate in the TJ document but I think that name is a bit too ambiguous - f(CHR) + * + * @param chara The source/casting character. */ static float criticalHitBonus( const Sapphire::Entity::Chara& chara ); - static float physicalDefense( const Sapphire::Entity::Chara& chara ); + /*! + * @brief Calculates how much damage you mitigate via physical defence + * + * @param chara The source/casting character. + */ + static float physicalDefence( const Sapphire::Entity::Chara& chara ); - static float magicDefense( const Sapphire::Entity::Chara& chara ); + /*! + * @brief Calculates how much damage you mitigate via magical defence + * + * @param chara The source/casting character. + */ + static float magicDefence( const Sapphire::Entity::Chara& chara ); + /*! + * @brief Calculates the percentage of damage that is mitigated on a successful block + * + * @param chara The source/casting character. + */ static float blockStrength( const Sapphire::Entity::Chara& chara ); + /*! + * @brief Calculates the multiplier that healing magic potency affects healing output + * + * @todo Only works for level 70 + * + * @param chara The source/casting character. + */ + static float healingMagicPotency( const Sapphire::Entity::Chara& chara ); + private: static uint32_t getPrimaryClassJobAttribute( const Sapphire::Entity::Chara& chara ); + /*! + * @brief Has the main attack power calculation allowing for de-duplication of functions. + * + * @param attackPower The magic/physical attack power value. + */ static float calcAttackPower( uint32_t attackPower ); }; From 68cc187cc6775a49d872417c8d34295d69e65dc8 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sun, 24 Mar 2019 16:32:20 +1100 Subject: [PATCH 3/8] validate the time since the last combo action and ignore any old combos --- src/common/Common.h | 7 +++++++ src/world/Actor/Chara.cpp | 9 +++++++++ src/world/Actor/Chara.h | 11 ++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/common/Common.h b/src/common/Common.h index 0cdb95bc..ce64a197 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -21,6 +21,13 @@ namespace Sapphire::Common const int32_t INVALID_GAME_OBJECT_ID = 0xE0000000; const uint64_t INVALID_GAME_OBJECT_ID64 = 0xE0000000; + /*! + * @brief The maximum length (in ms) of a combo before it is canceled/voided. + * + * The client has a combo timer of about 12 seconds, with a 0.5 second grace on top for latency considerations. + */ + const uint16_t MAX_COMBO_LENGTH = 12500; + struct FFXIVARR_POSITION3_U16 { uint16_t x; diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 68decd9e..46edf96e 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -662,9 +662,18 @@ int64_t Sapphire::Entity::Chara::getLastUpdateTime() const void Sapphire::Entity::Chara::setLastComboActionId( uint32_t actionId ) { m_lastComboActionId = actionId; + m_lastComboActionTime = Util::getTimeMs(); } uint32_t Sapphire::Entity::Chara::getLastComboActionId() const { + // initially check for the time passed first, if it's more than the threshold just return 0 for the combo + // we can hide the implementation detail this way and it just works:tm: for anything that uses it + + if( std::difftime( Util::getTimeMs(), m_lastComboActionTime ) > Common::MAX_COMBO_LENGTH ) + { + return 0; + } + return m_lastComboActionId; } diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index eb5e5b45..97acfa09 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -103,8 +103,17 @@ namespace Sapphire::Entity uint64_t m_targetId; /*! Ptr to a queued action */ Action::ActionPtr m_pCurrentAction; - /*! the id of the last combo action used (IgnoresCombo) */ + + /*! + * @brief the id of the last combo action used (IgnoresCombo) + */ uint32_t m_lastComboActionId; + + /*! + * @brief when the last combo action was used in ms + */ + uint64_t m_lastComboActionTime; + /*! Invincibility type */ Common::InvincibilityType m_invincibilityType; From 0488e713b889dc75286288a574627c62bdec2271 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sun, 24 Mar 2019 19:23:45 +1100 Subject: [PATCH 4/8] fix windows builds for now, missing a couple stat functions --- src/world/Math/CalcStats.cpp | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/world/Math/CalcStats.cpp b/src/world/Math/CalcStats.cpp index e66764a7..859cedec 100644 --- a/src/world/Math/CalcStats.cpp +++ b/src/world/Math/CalcStats.cpp @@ -308,15 +308,18 @@ float CalcStats::potency( uint16_t potency ) return potency / 100.f; } -float CalcStats::weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage, bool isMagicDamage ) -{ - const auto& baseStats = chara.getStats(); - auto level = chara.getLevel(); - - auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] ); - - float jobAttribute = 1.f; -} +//float CalcStats::weaponDamage( const Sapphire::Entity::Chara& chara, float weaponDamage, bool isMagicDamage ) +//{ +// const auto& baseStats = chara.getStats(); +// auto level = chara.getLevel(); +// +// auto mainVal = static_cast< float >( levelTable[ level ][ Common::LevelTableEntry::MAIN ] ); +// +// float jobAttribute = 1.f; +// +// // todo: fix this +// return 1.f +//} // todo: this is all retarded, needs to be per weapon and etcetc //uint32_t CalcStats::getPrimaryClassJobAttribute( const Sapphire::Entity::Chara& chara ) @@ -436,10 +439,10 @@ float CalcStats::magicDefence( const Sapphire::Entity::Chara& chara ) return std::floor( 15.f * baseStats.magicDefense ) / 100.f; } -float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara ) -{ - -} +//float CalcStats::blockStrength( const Sapphire::Entity::Chara& chara ) +//{ +// +//} float CalcStats::healingMagicPotency( const Sapphire::Entity::Chara& chara ) { From aac14164b8f54106772054257710033afdddccb4 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Sun, 24 Mar 2019 22:12:36 +1100 Subject: [PATCH 5/8] add malformed packet check back in and handle lobby connections better --- src/lobby/GameConnection.cpp | 19 ++++++------------- src/lobby/GameConnection.h | 1 + src/world/Network/GameConnection.cpp | 20 +++++++++++++++++--- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/lobby/GameConnection.cpp b/src/lobby/GameConnection.cpp index 8d6d479a..4f162d45 100644 --- a/src/lobby/GameConnection.cpp +++ b/src/lobby/GameConnection.cpp @@ -54,16 +54,13 @@ void Sapphire::Network::GameConnection::onDisconnect() void Sapphire::Network::GameConnection::onRecv( std::vector< uint8_t >& buffer ) { - Packets::FFXIVARR_PACKET_HEADER packetHeader; - const auto headerResult = Packets::getHeader( buffer, 0, packetHeader ); + m_packets.insert( std::end( m_packets ), std::begin( buffer ), std::end( buffer ) ); + // This is assumed packet always start with valid FFXIVARR_PACKET_HEADER for now. + Packets::FFXIVARR_PACKET_HEADER packetHeader{}; + const auto headerResult = Packets::getHeader( m_packets, 0, packetHeader ); if( headerResult == Incomplete ) - { - Logger::info( "Dropping connection due to incomplete packet header." ); - Logger::info( "FIXME: Packet message bounary is not implemented." ); - disconnect(); return; - } if( headerResult == Malformed ) { @@ -74,16 +71,11 @@ void Sapphire::Network::GameConnection::onRecv( std::vector< uint8_t >& buffer ) // Dissect packet list std::vector< Packets::FFXIVARR_PACKET_RAW > packetList; - const auto packetResult = Packets::getPackets( buffer, sizeof( struct FFXIVARR_PACKET_HEADER ), + const auto packetResult = Packets::getPackets( m_packets, sizeof( struct FFXIVARR_PACKET_HEADER ), packetHeader, packetList ); if( packetResult == Incomplete ) - { - Logger::info( "Dropping connection due to incomplete packets." ); - Logger::info( "FIXME: Packet message bounary is not implemented." ); - disconnect(); return; - } if( packetResult == Malformed ) { @@ -94,6 +86,7 @@ void Sapphire::Network::GameConnection::onRecv( std::vector< uint8_t >& buffer ) // Handle it handlePackets( packetHeader, packetList ); + m_packets.clear(); } diff --git a/src/lobby/GameConnection.h b/src/lobby/GameConnection.h index fc37756c..6bcb7122 100644 --- a/src/lobby/GameConnection.h +++ b/src/lobby/GameConnection.h @@ -38,6 +38,7 @@ namespace Sapphire::Network LockedQueue< Packets::GamePacketPtr > m_inQueue; LockedQueue< Packets::GamePacketPtr > m_outQueue; + std::vector< uint8_t > m_packets; public: GameConnection( HivePtr pHive, AcceptorPtr pAcceptor, FrameworkPtr pFw ); diff --git a/src/world/Network/GameConnection.cpp b/src/world/Network/GameConnection.cpp index 2559793d..103051f5 100644 --- a/src/world/Network/GameConnection.cpp +++ b/src/world/Network/GameConnection.cpp @@ -154,22 +154,36 @@ void Sapphire::Network::GameConnection::onDisconnect() void Sapphire::Network::GameConnection::onRecv( std::vector< uint8_t >& buffer ) { - m_packets.insert( std::end( m_packets ), std::begin( buffer ), std::end( buffer ) ); + m_packets.insert( std::end( m_packets ), std::begin( buffer ), std::end( buffer ) ); // This is assumed packet always start with valid FFXIVARR_PACKET_HEADER for now. Packets::FFXIVARR_PACKET_HEADER packetHeader{}; const auto headerResult = Packets::getHeader( m_packets, 0, packetHeader ); - if( ( headerResult == Incomplete ) || ( headerResult == Malformed ) ) + if( headerResult == Incomplete ) return; + if( headerResult == Malformed ) + { + Logger::info( "Dropping connection due to malformed packet header." ); + disconnect(); + return; + } + // Dissect packet list std::vector< Packets::FFXIVARR_PACKET_RAW > packetList; const auto packetResult = Packets::getPackets( m_packets, sizeof( struct FFXIVARR_PACKET_HEADER ), packetHeader, packetList ); - if( ( packetResult == Incomplete ) || ( packetResult == Malformed ) ) + if( packetResult == Incomplete ) return; + if( packetResult == Malformed ) + { + Logger::info( "Dropping connection due to malformed packets." ); + disconnect(); + return; + } + // Handle it handlePackets( packetHeader, packetList ); m_packets.clear(); From 7dc59eda598867e04415e093e59d9fd4c5699ad4 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Mon, 25 Mar 2019 00:09:01 +1100 Subject: [PATCH 6/8] code style --- src/common/Network/GamePacketParser.cpp | 252 ++++++++++++------------ 1 file changed, 126 insertions(+), 126 deletions(-) diff --git a/src/common/Network/GamePacketParser.cpp b/src/common/Network/GamePacketParser.cpp index 2e806d5f..6a18b718 100644 --- a/src/common/Network/GamePacketParser.cpp +++ b/src/common/Network/GamePacketParser.cpp @@ -1,126 +1,126 @@ -#include "CommonNetwork.h" -#include "GamePacketParser.h" - -#include // memcpy - -using namespace Sapphire::Network::Packets; - -PacketParseResult Sapphire::Network::Packets::getHeader( const std::vector< uint8_t >& buffer, - const uint32_t offset, - FFXIVARR_PACKET_HEADER& header ) -{ - const auto headerSize = sizeof( FFXIVARR_PACKET_HEADER ); - - // Check if we have enough bytes in the buffer. - auto remainingBytes = buffer.size() - offset; - if( remainingBytes < headerSize ) - return Incomplete; - - // Copy packet header. - memcpy( &header, buffer.data() + offset, headerSize ); - - if( !checkHeader( header ) ) - return Malformed; - - return Success; -} - -PacketParseResult Sapphire::Network::Packets::getSegmentHeader( const std::vector< uint8_t >& buffer, - const uint32_t offset, - FFXIVARR_PACKET_SEGMENT_HEADER& header ) -{ - const auto headerSize = sizeof( FFXIVARR_PACKET_SEGMENT_HEADER ); - - // Check if we have enough bytes in the buffer. - auto remainingBytes = buffer.size() - offset; - if( remainingBytes < headerSize ) - return Incomplete; - - // Copy segment header - memcpy( &header, buffer.data() + offset, headerSize ); - - return Success; -} - -PacketParseResult Sapphire::Network::Packets::getPackets( const std::vector< uint8_t >& buffer, - const uint32_t offset, - const FFXIVARR_PACKET_HEADER& packetHeader, - std::vector< FFXIVARR_PACKET_RAW >& packets ) -{ - // sanity check: check there's enough bytes in the buffer - const auto bytesExpected = packetHeader.size - sizeof( struct FFXIVARR_PACKET_HEADER ); - if( buffer.size() - offset < bytesExpected ) - return Incomplete; - - // Loop each message - uint32_t count = 0; - uint32_t bytesProcessed = 0; - while( count < packetHeader.count ) - { - FFXIVARR_PACKET_RAW rawPacket; - - // Copy ipc packet message - const auto packetResult = getPacket( buffer, offset + bytesProcessed, rawPacket ); - if( packetResult != Success ) - return packetResult; - - // NOTE: isn't rawPacket is allocated on stack? - // why is okay to do this? - packets.push_back( rawPacket ); - - // Add message size and count - bytesProcessed += rawPacket.segHdr.size; - count += 1; - } - - // sanity check: check if we processed all bytes. - // this check can fail if size of messages don't add up to size reported from packet header. - if( bytesExpected != bytesProcessed ) - return Malformed; - - return Success; -} - -PacketParseResult Sapphire::Network::Packets::getPacket( const std::vector< uint8_t >& buffer, const uint32_t offset, - FFXIVARR_PACKET_RAW& packet ) -{ - // Copy segment header - const auto headerResult = getSegmentHeader( buffer, offset, packet.segHdr ); - if( headerResult != Success ) - return headerResult; - - // Check header sanity and it's size - if( !checkSegmentHeader( packet.segHdr ) ) - return Malformed; - - const auto dataOffset = offset + sizeof( struct FFXIVARR_PACKET_SEGMENT_HEADER ); - const auto dataSize = packet.segHdr.size; - - // Allocate data buffer and copy - packet.data.resize( dataSize ); - memcpy( packet.data.data(), buffer.data() + dataOffset, dataSize ); - - return Success; -} - -bool Sapphire::Network::Packets::checkHeader( const FFXIVARR_PACKET_HEADER& header ) -{ - // Max size of the packet is capped at 1MB for now. - if( header.size > 1 * 1024 * 1024 ) - return false; - - // Max number of message is capped at 255 for now. - if( header.count > 255 ) - return false; - - return true; -} - -bool Sapphire::Network::Packets::checkSegmentHeader( const FFXIVARR_PACKET_SEGMENT_HEADER& header ) -{ - // Max size of individual message is capped at 256KB for now. - if( header.size > 256 * 1024 ) - return false; - - return true; -} +#include "CommonNetwork.h" +#include "GamePacketParser.h" + +#include // memcpy + +using namespace Sapphire::Network::Packets; + +PacketParseResult Sapphire::Network::Packets::getHeader( const std::vector< uint8_t >& buffer, + const uint32_t offset, + FFXIVARR_PACKET_HEADER& header ) +{ + const auto headerSize = sizeof( FFXIVARR_PACKET_HEADER ); + + // Check if we have enough bytes in the buffer. + auto remainingBytes = buffer.size() - offset; + if( remainingBytes < headerSize ) + return Incomplete; + + // Copy packet header. + memcpy( &header, buffer.data() + offset, headerSize ); + + if( !checkHeader( header ) ) + return Malformed; + + return Success; +} + +PacketParseResult Sapphire::Network::Packets::getSegmentHeader( const std::vector< uint8_t >& buffer, + const uint32_t offset, + FFXIVARR_PACKET_SEGMENT_HEADER& header ) +{ + const auto headerSize = sizeof( FFXIVARR_PACKET_SEGMENT_HEADER ); + + // Check if we have enough bytes in the buffer. + auto remainingBytes = buffer.size() - offset; + if( remainingBytes < headerSize ) + return Incomplete; + + // Copy segment header + memcpy( &header, buffer.data() + offset, headerSize ); + + return Success; +} + +PacketParseResult Sapphire::Network::Packets::getPackets( const std::vector< uint8_t >& buffer, + const uint32_t offset, + const FFXIVARR_PACKET_HEADER& packetHeader, + std::vector< FFXIVARR_PACKET_RAW >& packets ) +{ + // sanity check: check there's enough bytes in the buffer + const auto bytesExpected = packetHeader.size - sizeof( struct FFXIVARR_PACKET_HEADER ); + if( buffer.size() - offset < bytesExpected ) + return Incomplete; + + // Loop each message + uint32_t count = 0; + uint32_t bytesProcessed = 0; + while( count < packetHeader.count ) + { + FFXIVARR_PACKET_RAW rawPacket; + + // Copy ipc packet message + const auto packetResult = getPacket( buffer, offset + bytesProcessed, rawPacket ); + if( packetResult != Success ) + return packetResult; + + // NOTE: isn't rawPacket is allocated on stack? + // why is okay to do this? + packets.push_back( rawPacket ); + + // Add message size and count + bytesProcessed += rawPacket.segHdr.size; + count += 1; + } + + // sanity check: check if we processed all bytes. + // this check can fail if size of messages don't add up to size reported from packet header. + if( bytesExpected != bytesProcessed ) + return Malformed; + + return Success; +} + +PacketParseResult Sapphire::Network::Packets::getPacket( const std::vector< uint8_t >& buffer, const uint32_t offset, + FFXIVARR_PACKET_RAW& packet ) +{ + // Copy segment header + const auto headerResult = getSegmentHeader( buffer, offset, packet.segHdr ); + if( headerResult != Success ) + return headerResult; + + // Check header sanity and it's size + if( !checkSegmentHeader( packet.segHdr ) ) + return Malformed; + + const auto dataOffset = offset + sizeof( struct FFXIVARR_PACKET_SEGMENT_HEADER ); + const auto dataSize = packet.segHdr.size; + + // Allocate data buffer and copy + packet.data.resize( dataSize ); + memcpy( packet.data.data(), buffer.data() + dataOffset, dataSize ); + + return Success; +} + +bool Sapphire::Network::Packets::checkHeader( const FFXIVARR_PACKET_HEADER& header ) +{ + // Max size of the packet is capped at 1MB for now. + if( header.size > 1 * 1024 * 1024 ) + return false; + + // Max number of message is capped at 255 for now. + if( header.count > 255 ) + return false; + + return true; +} + +bool Sapphire::Network::Packets::checkSegmentHeader( const FFXIVARR_PACKET_SEGMENT_HEADER& header ) +{ + // Max size of individual message is capped at 256KB for now. + if( header.size > 256 * 1024 ) + return false; + + return true; +} From 142b5571b663e5f62efd5377bc3a90535045862f Mon Sep 17 00:00:00 2001 From: NotAdam Date: Mon, 25 Mar 2019 21:46:58 +1100 Subject: [PATCH 7/8] move m_bonusStats to std::array and zero it out on init --- src/world/Actor/Chara.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 46edf96e..0250e005 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -43,6 +43,8 @@ Sapphire::Entity::Chara::Chara( ObjKind type, FrameworkPtr pFw ) : m_lastTickTime = 0; m_lastUpdate = 0; + m_bonusStats.fill( 0 ); + // initialize the free slot queue for( uint8_t i = 0; i < MAX_STATUS_EFFECTS; i++ ) { From c4f443fa8d7b9206b277bea44386becf74cc0897 Mon Sep 17 00:00:00 2001 From: NotAdam Date: Tue, 26 Mar 2019 18:15:23 +1100 Subject: [PATCH 8/8] fix sessionid being read incorrectly from version packet, fix lobby key --- src/lobby/GameConnection.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lobby/GameConnection.cpp b/src/lobby/GameConnection.cpp index 4f162d45..93627743 100644 --- a/src/lobby/GameConnection.cpp +++ b/src/lobby/GameConnection.cpp @@ -236,15 +236,15 @@ void Sapphire::Network::GameConnection::enterWorld( FFXIVARR_PACKET_RAW& packet, bool Sapphire::Network::GameConnection::sendServiceAccountList( FFXIVARR_PACKET_RAW& packet, uint32_t tmpId ) { - LobbySessionPtr pSession = g_serverLobby.getSession( ( char* ) &packet.data[ 0 ] + 0x20 ); + LobbySessionPtr pSession = g_serverLobby.getSession( ( char* ) &packet.data[ 0 ] + 0x22 ); if( g_serverLobby.getConfig().allowNoSessionConnect && pSession == nullptr ) { auto session = make_LobbySession(); session->setAccountID( 0 ); - session->setSessionId( ( uint8_t* ) &packet.data[ 0 ] + 0x20 ); + session->setSessionId( ( uint8_t* ) &packet.data[ 0 ] + 0x22 ); pSession = session; - Logger::info( "Allowed connection with no session: {0}", std::string( ( char* ) &packet.data[ 0 ] + 0x20 ) ); + Logger::info( "Allowed connection with no session: {0}", std::string( ( char* ) &packet.data[ 0 ] + 0x22 ) ); } if( pSession != nullptr ) @@ -388,6 +388,8 @@ void Sapphire::Network::GameConnection::handleGamePacket( Packets::FFXIVARR_PACK Logger::info( "OpCode [{0}]", *reinterpret_cast< uint16_t* >( &packet.data[ 2 ] ) ); + Logger::info( Util::binaryToHexDump( packet.data.data(), packet.data.size() ) ); + switch( *reinterpret_cast< uint16_t* >( &packet.data[ 2 ] ) ) { case ClientVersionInfo: @@ -451,7 +453,7 @@ void Sapphire::Network::GameConnection::generateEncryptionKey( uint32_t key, con m_baseKey[ 2 ] = 0x34; m_baseKey[ 3 ] = 0x12; memcpy( m_baseKey + 0x04, &key, 4 ); - m_baseKey[ 8 ] = 0x30; + m_baseKey[ 8 ] = 0xC6; m_baseKey[ 9 ] = 0x11; memcpy( ( char* ) m_baseKey + 0x0C, keyPhrase.c_str(), keyPhrase.size() ); Sapphire::Util::md5( m_baseKey, m_encKey, 0x2C );