From b0575d0f3f1eb2db9a19b5d17b23145df84f7806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Paulo?= Date: Mon, 14 Apr 2025 14:51:45 -0300 Subject: [PATCH] fix: validate amount before removing value from players bank --- src/game/game.cpp | 62 +++++++++++++++++++ src/game/game.hpp | 2 + .../functions/creatures/npc/npc_functions.cpp | 9 +++ 3 files changed, 73 insertions(+) diff --git a/src/game/game.cpp b/src/game/game.cpp index 37bbf91f8..1b9a3433c 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -2659,6 +2659,68 @@ std::shared_ptr Game::findItemOfType(const std::shared_ptr &cyli return nullptr; } +bool Game::validRemoveMoney(const std::shared_ptr &cylinder, uint64_t money, uint32_t flags /*= 0*/, bool useBalance /*= false*/) { + if (cylinder == nullptr) { + g_logger().error("[{}] cylinder is nullptr", __FUNCTION__); + return false; + } + if (money == 0) { + return true; + } + + std::vector> containers; + std::multimap> moneyMap; + uint64_t moneyCount = 0; + for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) { + const std::shared_ptr &thing = cylinder->getThing(i); + if (!thing) { + continue; + } + const auto &item = thing->getItem(); + if (!item) { + continue; + } + const std::shared_ptr &container = item->getContainer(); + if (container) { + containers.push_back(container); + } else { + const uint32_t worth = item->getWorth(); + if (worth != 0) { + moneyCount += worth; + moneyMap.emplace(worth, item); + } + } + } + size_t i = 0; + while (i < containers.size()) { + const std::shared_ptr &container = containers[i++]; + for (const std::shared_ptr &item : container->getItemList()) { + const std::shared_ptr &tmpContainer = item->getContainer(); + if (tmpContainer) { + containers.push_back(tmpContainer); + } else { + const uint32_t worth = item->getWorth(); + if (worth != 0) { + moneyCount += worth; + moneyMap.emplace(worth, item); + } + } + } + } + + const auto &player = useBalance ? std::dynamic_pointer_cast(cylinder) : nullptr; + uint64_t balance = 0; + if (useBalance && player) { + balance = player->getBankBalance(); + } + + if (moneyCount + balance < money) { + return false; + } + + return true; +} + bool Game::removeMoney(const std::shared_ptr &cylinder, uint64_t money, uint32_t flags /*= 0*/, bool useBalance /*= false*/) { if (cylinder == nullptr) { g_logger().error("[{}] cylinder is nullptr", __FUNCTION__); diff --git a/src/game/game.hpp b/src/game/game.hpp index 4eea6892e..44b1921b2 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -239,6 +239,8 @@ class Game { void createLuaItemsOnMap(); + bool validRemoveMoney(const std::shared_ptr &cylinder, uint64_t money, uint32_t flags = 0, bool useBank = false); + bool removeMoney(const std::shared_ptr &cylinder, uint64_t money, uint32_t flags = 0, bool useBank = false); void addMoney(const std::shared_ptr &cylinder, uint64_t money, uint32_t flags = 0); diff --git a/src/lua/functions/creatures/npc/npc_functions.cpp b/src/lua/functions/creatures/npc/npc_functions.cpp index b1ad6c654..4bee3f369 100644 --- a/src/lua/functions/creatures/npc/npc_functions.cpp +++ b/src/lua/functions/creatures/npc/npc_functions.cpp @@ -644,6 +644,15 @@ int NpcFunctions::luaNpcSellItem(lua_State* L) { } } + const auto totalCost = amount * pricePerUnit; + + if (!g_game().validRemoveMoney(player, totalCost, 0, true)) { + Lua::pushBoolean(L, false); + player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + g_logger().error("[NpcFunctions::luaNpcSellItem (validRemoveMoney)] - Player {} possibly tried to abuse a bug buying large amounts of {} on shop for npc {}", player->getName(), itemId, npc->getName()); + return 1; + } + const auto &[_, itemsPurchased, backpacksPurchased] = g_game().createItem(player, itemId, amount, subType, actionId, ignoreCap, inBackpacks ? ITEM_SHOPPING_BAG : 0); std::stringstream ss;