diff --git a/changelog.txt b/changelog.txt index a1a4e264c..33bf2de1a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,7 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- -== 21/01/2016 == -Uleat: Disabled RoF+ clients from augmentation items not in their possessions slots (0-29, 9999, 251-330) to abate an exploit in the current code +== 12/03/2016 == +Uleat: Added hack detection to trade code + - If illegal items are found in trade slots when the 'trade' button is clicked, the trade is cancelled and a message is sent to the offending player + - Future revisions will, at a minimum, log the player as a hacker once the quirks have been worked out + +== 12/01/2016 == +Uleat: Disabled RoF+ clients from augmenting items not in their possessions slots (0-29, 9999, 251-330) to abate an exploit in the current code == 10/17/2016 == Uleat: Moved namespace ItemField from item_instance.h to shareddb.cpp - the only place it is used diff --git a/common/item_instance.cpp b/common/item_instance.cpp index 268440dc8..d8d23064e 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -819,6 +819,32 @@ bool EQEmu::ItemInstance::IsSlotAllowed(int16 slot_id) const { else { return false; } } +bool EQEmu::ItemInstance::IsDroppable(bool recurse) const +{ + if (!m_item) + return false; + /*if (m_ornamentidfile) // not implemented + return false;*/ + if (m_attuned) + return false; + /*if (m_item->FVNoDrop != 0) // not implemented + return false;*/ + if (m_item->NoDrop == 0) + return false; + + if (recurse) { + for (auto iter : m_contents) { + if (!iter.second) + continue; + + if (!iter.second->IsDroppable(recurse)) + return false; + } + } + + return true; +} + void EQEmu::ItemInstance::Initialize(SharedDatabase *db) { // if there's no actual item, don't do anything if (!m_item) diff --git a/common/item_instance.h b/common/item_instance.h index 90dd1403f..6566fd77e 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -188,6 +188,8 @@ namespace EQEmu bool IsSlotAllowed(int16 slot_id) const; + bool IsDroppable(bool recurse = true) const; + bool IsScaling() const { return m_scaling; } bool IsEvolving() const { return (m_evolveLvl >= 1); } uint32 GetExp() const { return m_exp; } diff --git a/zone/client.h b/zone/client.h index 5b2c0a939..8bb996bdf 100644 --- a/zone/client.h +++ b/zone/client.h @@ -855,6 +855,7 @@ public: void SetConsumption(int32 in_hunger, int32 in_thirst); bool CheckTradeLoreConflict(Client* other); + bool CheckTradeNonDroppable(); void LinkDead(); void Insight(uint32 t_id); bool CheckDoubleAttack(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0d4294132..0ba79d15f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -13287,7 +13287,6 @@ void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) other->trade->state = TradeCompleting; trade->state = TradeCompleting; - // should we do this for NoDrop items as well? if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { Message_StringID(13, TRADE_CANCEL_LORE); other->Message_StringID(13, TRADE_CANCEL_LORE); @@ -13296,6 +13295,26 @@ void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) other->trade->Reset(); trade->Reset(); } + else if (CheckTradeNonDroppable()) { + Message_StringID(13, TRADE_HAS_BEEN_CANCELLED); + other->Message_StringID(13, TRADE_HAS_BEEN_CANCELLED); + this->FinishTrade(this); + other->FinishTrade(other); + other->trade->Reset(); + trade->Reset(); + Message(15, "Hacking activity detected in trade transaction."); + // TODO: query (this) as a hacker + } + else if (other->CheckTradeNonDroppable()) { + Message_StringID(13, TRADE_HAS_BEEN_CANCELLED); + other->Message_StringID(13, TRADE_HAS_BEEN_CANCELLED); + this->FinishTrade(this); + other->FinishTrade(other); + other->trade->Reset(); + trade->Reset(); + other->Message(15, "Hacking activity detected in trade transaction."); + // TODO: query (other) as a hacker + } else { // Audit trade to database for both trade streams other->trade->LogTrade(); diff --git a/zone/string_ids.h b/zone/string_ids.h index e1fb4de7c..b9eba66df 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -255,6 +255,7 @@ #define MEMBER_OF_YOUR_GUILD 1429 #define OFFICER_OF_YOUR_GUILD 1430 #define LEADER_OF_YOUR_GUILD 1431 +#define TRADE_HAS_BEEN_CANCELLED 1449 #define RECEIVED_PLATINUM 1452 //You receive %1 Platinum from %2. #define RECEIVED_GOLD 1453 //You receive %1 Gold from %2. #define RECEIVED_SILVER 1454 //You receive %1 Silver from %2. diff --git a/zone/trading.cpp b/zone/trading.cpp index e150ceb12..85ed824b2 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -967,23 +967,37 @@ bool Client::CheckTradeLoreConflict(Client* other) { if (!other) return true; - // Move each trade slot into free inventory slot - for (int16 i = EQEmu::legacy::TRADE_BEGIN; i <= EQEmu::legacy::TRADE_END; i++){ - const EQEmu::ItemInstance* inst = m_inv[i]; - if (inst && inst->GetItem()) { - if (other->CheckLoreConflict(inst->GetItem())) - return true; - } + for (int16 index = EQEmu::legacy::TRADE_BEGIN; index <= EQEmu::legacy::TRADE_END; ++index) { + const EQEmu::ItemInstance* inst = m_inv[index]; + if (!inst || !inst->GetItem()) + continue; + + if (other->CheckLoreConflict(inst->GetItem())) + return true; } - for (int16 i = EQEmu::legacy::TRADE_BAGS_BEGIN; i <= EQEmu::legacy::TRADE_BAGS_END; i++){ - const EQEmu::ItemInstance* inst = m_inv[i]; + for (int16 index = EQEmu::legacy::TRADE_BAGS_BEGIN; index <= EQEmu::legacy::TRADE_BAGS_END; ++index) { + const EQEmu::ItemInstance* inst = m_inv[index]; + if (!inst || !inst->GetItem()) + continue; - if (inst && inst->GetItem()) { - if (other->CheckLoreConflict(inst->GetItem())) - return true; - } + if (other->CheckLoreConflict(inst->GetItem())) + return true; + } + + return false; +} + +bool Client::CheckTradeNonDroppable() +{ + for (int16 index = EQEmu::legacy::TRADE_BEGIN; index <= EQEmu::legacy::TRADE_END; ++index){ + const EQEmu::ItemInstance* inst = m_inv[index]; + if (!inst) + continue; + + if (!inst->IsDroppable()) + return true; } return false;