From dfe864e477235e522047d92bd57f61370b001baa Mon Sep 17 00:00:00 2001 From: luis Date: Sun, 15 Mar 2026 21:18:19 -0300 Subject: [PATCH] trading post try fix crash --- .../game/Handlers/PerkProgramHandler.cpp | 9 +- .../game/PerkProgram/PerkProgramData.cpp | 19 ++++- .../game/PerkProgram/PerkProgramMgr.cpp | 83 +++++++++++++++++-- .../Server/Packets/PerksProgramPackets.cpp | 6 ++ .../game/Server/Packets/PerksProgramPackets.h | 10 +++ src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldSession.h | 2 +- 7 files changed, 117 insertions(+), 14 deletions(-) diff --git a/src/server/game/Handlers/PerkProgramHandler.cpp b/src/server/game/Handlers/PerkProgramHandler.cpp index 098f5448f..666d70af1 100644 --- a/src/server/game/Handlers/PerkProgramHandler.cpp +++ b/src/server/game/Handlers/PerkProgramHandler.cpp @@ -19,6 +19,7 @@ #include "PerkProgramData.h" #include "PerkProgramMgr.h" #include "PerksProgramPacketsCommon.h" +#include "PerksProgramPackets.h" #include "Config.h" #include "DatabaseEnv.h" #include "ObjectMgr.h" @@ -39,17 +40,21 @@ void WorldSession::HandlePerkProgramRequestPurchase(WorldPackets::PerksProgram:: if (!_player) return; - //GetPerkProgramMgr()->BuyItem(packet.PerksVendorItemID, packet.vendorGuid); + GetPerkProgramMgr()->BuyItem(packet.VendorItemID, ObjectGuid::Empty); } -void WorldSession::HandlePerkProgamSetForzenVendorItem(WorldPackets::PerksProgram::PerksProgramSetFrozenVendorItem& packet) +void WorldSession::HandlePerkProgramSetFrozenVendorItem(WorldPackets::PerksProgram::PerksProgramSetFrozenVendorItem& packet) { if (!_player) return; + + GetPerkProgramMgr()->SetFrozenItem(packet.VendorItemID, packet.VendorItemID == -1); } void WorldSession::HandlePerkProgramPendingReward(WorldPackets::PerksProgram::PerksProgramPendingReward& packet) { if (!_player) return; + + GetPerkProgramMgr()->SendPendingRewards(); } diff --git a/src/server/game/PerkProgram/PerkProgramData.cpp b/src/server/game/PerkProgram/PerkProgramData.cpp index f1e4405ab..f8773ecae 100644 --- a/src/server/game/PerkProgram/PerkProgramData.cpp +++ b/src/server/game/PerkProgram/PerkProgramData.cpp @@ -13,6 +13,8 @@ #include "DB2Stores.h" #include #include "GameTime.h" +#include +#include PerkProgramDataStoreMgr::PerkProgramDataStoreMgr() {}; @@ -47,11 +49,19 @@ void PerkProgramDataStoreMgr::LoadPerkVendorItem() return; } + uint32 validCount = 0; for (PerksVendorItemEntry const* itemVendor : sPerksVendorItemStore) { if (!itemVendor) continue; + // Validate item data + if (itemVendor->Cost > 1000000) // Reasonable maximum cost + { + TC_LOG_WARN("server.loading", ">> PerksVendorItem {} has excessive cost {}, skipping", itemVendor->ID, itemVendor->Cost); + continue; + } + PerkProgramData::PerkVendorItem vendorItem; vendorItem.Id = itemVendor->ID; vendorItem.ItemID = itemVendor->ItemID; @@ -60,13 +70,15 @@ void PerkProgramDataStoreMgr::LoadPerkVendorItem() vendorItem.TransmogSet = 0; vendorItem.ItemModifiedAppearanceID = 0; vendorItem.ToyItem = 0; + vendorItem.AvailableUntil = 0; // Initialize to 0 (no expiration) vendorItem.Cost = itemVendor->Cost; vendorItem.CategoryId = itemVendor->PerksVendorCategoryID; _vendorTemplates.insert(std::make_pair(itemVendor->ID, vendorItem)); + validCount++; } - TC_LOG_INFO("server.loading", ">> Loaded {} PerkPrograms Vendor Items Templates", uint32(_vendorTemplates.size())); + TC_LOG_INFO("server.loading", ">> Loaded {} PerkPrograms Vendor Items Templates ({} valid)", uint32(_vendorTemplates.size()), validCount); } std::unordered_map PerkProgramDataStoreMgr::GetVendorItems() @@ -88,8 +100,11 @@ PerkProgramData::PerkVendorItem const* PerkProgramDataStoreMgr::GetRandomVendorI if (_vendorTemplates.empty()) return nullptr; + // Use proper random number generation + static thread_local std::mt19937 generator(std::random_device{}()); + std::uniform_int_distribution distribution(0, _vendorTemplates.size() - 1); auto it = _vendorTemplates.begin(); - std::advance(it, rand() % _vendorTemplates.size()); + std::advance(it, distribution(generator)); return &it->second; } diff --git a/src/server/game/PerkProgram/PerkProgramMgr.cpp b/src/server/game/PerkProgram/PerkProgramMgr.cpp index 6a534f888..2e47a1bfd 100644 --- a/src/server/game/PerkProgram/PerkProgramMgr.cpp +++ b/src/server/game/PerkProgram/PerkProgramMgr.cpp @@ -37,7 +37,8 @@ using namespace PerkPrograms; PerkProgramManager::PerkProgramManager(WorldSession* session) { _session = session; - if (session->GetPlayer()) + m_player = nullptr; + if (session && session->GetPlayer()) m_player = session->GetPlayer(); m_currencyAmount = 0; @@ -49,6 +50,17 @@ PerkProgramManager::~PerkProgramManager() void PerkProgramManager::SendVendorItems(Creature* vendor, std::vector purchasedItems) { + // Additional safety check - verify this pointer is valid + if (this == nullptr) + return; + + if (!_session || !vendor) + return; + + // Validate session is still valid + if (_session->GetPlayer() != m_player) + m_player = _session->GetPlayer(); + WorldPackets::PerksProgram::PerksProgramResult perks; perks.Type = PerkPrograms::RESULT_AVAILABLE_ITEMS; @@ -71,7 +83,8 @@ void PerkProgramManager::SendVendorItems(Creature* vendor, std::vector p if (cameraId != 0) { std::list list; - _session->GetPlayer()->GetCreatureListWithEntryInGrid(list, cameraId, 100); + if (_session->GetPlayer()) + _session->GetPlayer()->GetCreatureListWithEntryInGrid(list, cameraId, 100); for (auto cr1 : list) { @@ -93,8 +106,12 @@ void PerkProgramManager::SendVendorItems(Creature* vendor, std::vector p vendorItem.OriginalPrice = items.second.CategoryId; vendorItem.Disabled = false; vendorItem.AvailableUntil = items.second.AvailableUntil; + + // Skip items that are no longer available + if (items.second.AvailableUntil > 0 && GameTime::GetGameTime() > items.second.AvailableUntil) + continue; + perks.Params.VendorItem.FrozenPerksVendorItems.emplace_back(vendorItem); - } _session->SendPacket(perks.Write()); @@ -121,7 +138,7 @@ bool PerkProgramManager::IsAccountWideItem(uint32 item) bool PerkProgramManager::IsItemAlreadyOwned(PerkProgramData::PerkVendorItem const* vendorItem) { - if (!vendorItem) + if (!vendorItem || !m_player) return true; switch (vendorItem->CategoryId) @@ -145,7 +162,7 @@ bool PerkProgramManager::IsItemAlreadyOwned(PerkProgramData::PerkVendorItem cons void PerkProgramManager::GrantItem(PerkProgramData::PerkVendorItem const* vendorItem) { - if (!vendorItem) + if (!vendorItem || !m_player) return; switch (vendorItem->CategoryId) @@ -187,7 +204,7 @@ void PerkProgramManager::GrantItem(PerkProgramData::PerkVendorItem const* vendor void PerkProgramManager::RemoveGrantedItem(PerkProgramData::PerkVendorItem const* vendorItem) { - if (!vendorItem) + if (!vendorItem || !m_player) return; switch (vendorItem->CategoryId) @@ -213,6 +230,12 @@ void PerkProgramManager::RemoveGrantedItem(PerkProgramData::PerkVendorItem const void PerkProgramManager::BuyItem(uint32 itemId, ObjectGuid /*vendor*/) { + if (!m_player) + { + SendPerksProgramError(6); + return; + } + auto const* vendorItem = sPerkProgramDataStore->GetVendorItem(itemId); if (!vendorItem) { @@ -245,6 +268,9 @@ void PerkProgramManager::BuyItem(uint32 itemId, ObjectGuid /*vendor*/) void PerkProgramManager::SendBuyItemResult(int32 itemId) { + if (!_session) + return; + WorldPackets::PerksProgram::PerksProgramResult perks; perks.Type = PerkPrograms::RESULT_BUY_ITEM; perks.Params.BuyItem.VendorItemID = itemId; @@ -259,15 +285,23 @@ void PerkProgramManager::SendBuyItemResult(int32 itemId) void PerkProgramManager::SendCurrencyRefresh() { + if (!_session) + return; + WorldPackets::PerksProgram::PerksProgramResult perks; perks.Type = PerkPrograms::RESULT_CURRENCY_REFRESH; + perks.Params.Currency.CurrencyAmount = m_currencyAmount; _session->SendPacket(perks.Write()); } void PerkProgramManager::SendPerksProgramError(uint32 errorCode) { + if (!_session) + return; + WorldPackets::PerksProgram::PerksProgramResult perks; perks.Type = PerkPrograms::RESULT_ERROR; + perks.Params.Error.ErrorCode = errorCode; _session->SendPacket(perks.Write()); } @@ -278,6 +312,12 @@ void PerkProgramManager::RecordPurchase(uint32 itemId, time_t purchaseTime) void PerkProgramManager::RefundItem(int32 itemId) { + if (!m_player) + { + SendPerksProgramError(6); + return; + } + auto it = std::find_if(m_recentPurchases.begin(), m_recentPurchases.end(), [itemId](std::pair const& p) { return p.first == static_cast(itemId); }); @@ -293,6 +333,13 @@ void PerkProgramManager::RefundItem(int32 itemId) return; } + // Additional safety check for very old purchases + if (it->second > GameTime::GetGameTime()) + { + SendPerksProgramError(5); + return; + } + auto const* vendorItem = sPerkProgramDataStore->GetVendorItem(itemId); if (!vendorItem) return; @@ -310,6 +357,9 @@ void PerkProgramManager::RefundItem(int32 itemId) void PerkProgramManager::SendRefundSuccess(int32 itemId) { + if (!_session) + return; + WorldPackets::PerksProgram::PerksProgramResult perks; perks.Type = PerkPrograms::RESULT_REFUND_SUCCESS; perks.Params.BuyItem.VendorItemID = itemId; @@ -318,6 +368,12 @@ void PerkProgramManager::SendRefundSuccess(int32 itemId) void PerkProgramManager::CartCheckout(std::vector const& vendorItemIDs) { + if (!m_player) + { + SendPerksProgramError(6); + return; + } + uint32 totalCost = 0; for (uint32 itemId : vendorItemIDs) @@ -359,6 +415,9 @@ void PerkProgramManager::CartCheckout(std::vector const& vendorItemIDs) void PerkProgramManager::SendCartCheckoutSuccess() { + if (!_session) + return; + WorldPackets::PerksProgram::PerksProgramResult perks; perks.Type = PerkPrograms::RESULT_PURCHASE_CART; _session->SendPacket(perks.Write()); @@ -366,8 +425,12 @@ void PerkProgramManager::SendCartCheckoutSuccess() void PerkProgramManager::SendPerksProgramStatus() { + if (!_session) + return; + WorldPackets::PerksProgram::PerksProgramResult perks; perks.Type = PerkPrograms::RESULT_CURRENCY_REFRESH; + perks.Params.Currency.CurrencyAmount = m_currencyAmount; _session->SendPacket(perks.Write()); } @@ -377,6 +440,9 @@ void PerkProgramManager::SendPendingRewards() void PerkProgramManager::SetFrozenItem(int32 itemId, bool clear) { + if (!_session) + return; + if (clear) { m_frozenItems.clear(); @@ -408,13 +474,14 @@ void PerkProgramManager::RemoveTrackedActivity(uint32 activityId) { auto it = std::find(m_trackedActivities.begin(), m_trackedActivities.end(), activityId); if (it != m_trackedActivities.end()) - { m_trackedActivities.erase(it); - } } void PerkProgramManager::SendPerksProgramActivities() { + if (!_session) + return; + WorldPackets::PerksProgram::PerksProgramActivityUpdate activity; activity.TimeUntilStart = GameTime::GetGameTime(); diff --git a/src/server/game/Server/Packets/PerksProgramPackets.cpp b/src/server/game/Server/Packets/PerksProgramPackets.cpp index f8829eed8..86c50e3d8 100644 --- a/src/server/game/Server/Packets/PerksProgramPackets.cpp +++ b/src/server/game/Server/Packets/PerksProgramPackets.cpp @@ -73,6 +73,12 @@ WorldPacket const* PerksProgramResult::Write() for (auto&& frozenPerksVendorItem : Params.VendorItem.FrozenPerksVendorItems) _worldPacket << frozenPerksVendorItem; break; + case 11: //ResultTypes::CurrencyRefresh: + _worldPacket << Params.Currency.CurrencyAmount; + break; + case 10: //ResultTypes::Error: + _worldPacket << Params.Error.ErrorCode; + break; default: break; } diff --git a/src/server/game/Server/Packets/PerksProgramPackets.h b/src/server/game/Server/Packets/PerksProgramPackets.h index cb42924ec..36a486f57 100644 --- a/src/server/game/Server/Packets/PerksProgramPackets.h +++ b/src/server/game/Server/Packets/PerksProgramPackets.h @@ -95,6 +95,16 @@ public: ObjectGuid VendorGuid; ObjectGuid ModelSceneCameraGuid; } VendorItem; + + struct + { + uint32 CurrencyAmount = 0; + } Currency; + + struct + { + uint32 ErrorCode = 0; + } Error; } Params = {}; }; diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 36a1a7dd5..36afb6726 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -882,7 +882,7 @@ void OpcodeTable::InitializeClientOpcodes() DEFINE_HANDLER(CMSG_PERKS_PROGRAM_REQUEST_PENDING_REWARDS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePerkProgramPendingReward); DEFINE_HANDLER(CMSG_PERKS_PROGRAM_REQUEST_PURCHASE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePerkProgramRequestPurchase); DEFINE_HANDLER(CMSG_PERKS_PROGRAM_REQUEST_REFUND, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL); - DEFINE_HANDLER(CMSG_PERKS_PROGRAM_SET_FROZEN_VENDOR_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePerkProgamSetForzenVendorItem); + DEFINE_HANDLER(CMSG_PERKS_PROGRAM_SET_FROZEN_VENDOR_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePerkProgramSetFrozenVendorItem); DEFINE_HANDLER(CMSG_PERKS_PROGRAM_STATUS_REQUEST, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGetPerkProgramRequest); DEFINE_HANDLER(CMSG_PETITION_BUY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionBuy); DEFINE_HANDLER(CMSG_PETITION_RENAME_GUILD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePetitionRenameGuild); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 3f674442a..84d032ec8 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -1553,7 +1553,7 @@ class TC_GAME_API WorldSession //PERKS void HandleGetPerkProgramRequest(WorldPackets::PerksProgram::PerksProgramStatusRequest& packet); void HandlePerkProgramRequestPurchase(WorldPackets::PerksProgram::PerksProgramRequestPurchase& packet); - void HandlePerkProgamSetForzenVendorItem(WorldPackets::PerksProgram::PerksProgramSetFrozenVendorItem& packet); + void HandlePerkProgramSetFrozenVendorItem(WorldPackets::PerksProgram::PerksProgramSetFrozenVendorItem& packet); void HandlePerkProgramPendingReward(WorldPackets::PerksProgram::PerksProgramPendingReward& packet); //wowcommunity