From 1221e88d928a5bc1d905769686ea100178d6c684 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Wed, 14 May 2025 22:24:59 -0300 Subject: [PATCH] [Fix] Add trader/buyer cleanup actions (#4843) * Add trader/buyer cleanup actions Add trader/buyer db cleanup for - on zone idle - on client first login - when world drops a zone connection - in Client::ProcessMovePC Cleanup several compiler warnings * Formatting Updates --- common/repositories/buyer_repository.h | 65 ++++++++++++++++++------ world/zonelist.cpp | 4 ++ world/zoneserver.cpp | 18 +++++++ world/zoneserver.h | 1 + zone/client_packet.cpp | 11 +++- zone/entity.cpp | 24 ++++++++- zone/entity.h | 1 + zone/trading.cpp | 70 ++++++++++++++++---------- zone/worldserver.cpp | 6 +-- zone/zoning.cpp | 13 ++++- 10 files changed, 166 insertions(+), 47 deletions(-) diff --git a/common/repositories/buyer_repository.h b/common/repositories/buyer_repository.h index 382ad9ccf..2d402b0dc 100644 --- a/common/repositories/buyer_repository.h +++ b/common/repositories/buyer_repository.h @@ -106,13 +106,8 @@ public: return false; } - auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere( - db, - fmt::format("`buyer_id` = '{}'", buyer.front().id) - ); - if (buy_lines.empty()) { - return false; - } + auto buy_lines = + BaseBuyerBuyLinesRepository::GetWhere(db, fmt::format("`buyer_id` = {}", buyer.front().id)); std::vector buy_line_ids{}; for (auto const &bl: buy_lines) { @@ -121,23 +116,65 @@ public: DeleteWhere(db, fmt::format("`char_id` = '{}';", char_id)); if (buy_line_ids.empty()) { - return false; + return true; } BaseBuyerBuyLinesRepository::DeleteWhere( - db, - fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids)) + db, fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids)) ); BaseBuyerTradeItemsRepository::DeleteWhere( - db, - fmt::format( - "`buyer_buy_lines_id` IN({})", - Strings::Implode(", ", buy_line_ids)) + db, fmt::format("`buyer_buy_lines_id` IN({})", Strings::Implode(", ", buy_line_ids)) ); } return true; } + + static bool DeleteBuyers(Database &db, uint32 char_zone_id, uint32 char_zone_instance_id) + { + auto buyers = GetWhere( + db, + fmt::format( + "`char_zone_id` = {} AND `char_zone_instance_id` = {}", char_zone_id, char_zone_instance_id + ) + ); + if (buyers.empty()) { + return false; + } + + std::vector buyer_ids{}; + std::vector buy_line_ids{}; + + for (auto const &b: buyers) { + buyer_ids.push_back(std::to_string(b.id)); + } + + auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere( + db, fmt::format("`buyer_id` IN({})", Strings::Implode(", ", buyer_ids)) + ); + + if (!buy_lines.empty()) { + for (auto const &bl: buy_lines) { + buy_line_ids.push_back(std::to_string(bl.id)); + } + } + + DeleteWhere(db, fmt::format("`id` IN({});", Strings::Implode(", ", buyer_ids))); + if (buy_line_ids.empty()) { + return true; + } + + BaseBuyerBuyLinesRepository::DeleteWhere( + db, + fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids)) + ); + BaseBuyerTradeItemsRepository::DeleteWhere( + db, + fmt::format("`buyer_buy_lines_id` IN({})", Strings::Implode(", ", buy_line_ids)) + ); + + return true; + } }; #endif //EQEMU_BUYER_REPOSITORY_H diff --git a/world/zonelist.cpp b/world/zonelist.cpp index ac3bc76ff..008f16b5c 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -37,6 +37,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "dynamic_zone_manager.h" #include "ucs.h" #include "clientlist.h" +#include "../common/repositories/trader_repository.h" +#include "../common/repositories/buyer_repository.h" extern uint32 numzones; extern EQ::Random emu_random; @@ -84,6 +86,8 @@ void ZSList::Remove(const std::string &uuid) while (iter != zone_server_list.end()) { if ((*iter)->GetUUID().compare(uuid) == 0) { auto port = (*iter)->GetCPort(); + (*iter)->CheckToClearTraderAndBuyerTables(); + zone_server_list.erase(iter); if (port != 0) { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 1e8dd9f81..3a244f785 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -50,6 +50,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/repositories/guild_tributes_repository.h" #include "../common/skill_caps.h" #include "../common/server_reload_types.h" +#include "../common/repositories/trader_repository.h" +#include "../common/repositories/buyer_repository.h" extern ClientList client_list; extern GroupLFPList LFPGroupList; @@ -1860,3 +1862,19 @@ void ZoneServer::IncomingClient(Client* client) { SendPacket(pack); delete pack; } + +void ZoneServer::CheckToClearTraderAndBuyerTables() +{ + if (GetZoneID() == Zones::BAZAAR) { + TraderRepository::DeleteWhere( + database, + fmt::format("`char_zone_id` = {} AND `char_zone_instance_id` = {}", GetZoneID(), GetInstanceID() + ) + ); + BuyerRepository::DeleteBuyers(database, GetZoneID(), GetInstanceID()); + + LogTradingDetail( + "Removed trader and buyer entries for Zone ID [{}] and Instance ID [{}]", GetZoneID(), GetInstanceID() + ); + } +} diff --git a/world/zoneserver.h b/world/zoneserver.h index 232b592c8..4e93c9b7e 100644 --- a/world/zoneserver.h +++ b/world/zoneserver.h @@ -54,6 +54,7 @@ public: inline const char* GetZoneName() const { return zone_name; } inline const char* GetZoneLongName() const { return long_name; } inline std::string GetCurrentVersion() const { return CURRENT_VERSION; } + void CheckToClearTraderAndBuyerTables(); inline std::string GetCompileDate() const { return COMPILE_DATE; } const char* GetCompileTime() const{ return compiled; } void SetCompile(char* in_compile){ strcpy(compiled,in_compile); } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2ec433b36..7eff0210b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -808,6 +808,13 @@ void Client::CompleteConnect() /* This sub event is for if a player logs in for the first time since entering world. */ if (firstlogon == 1) { + TraderRepository::DeleteWhere(database, fmt::format("`char_id` = '{}'", CharacterID())); + BuyerRepository::DeleteBuyer(database, CharacterID()); + LogTradingDetail( + "Removed trader abd buyer entries for Character ID {} on first logon to ensure table consistency.", + CharacterID() + ); + RecordPlayerEventLog(PlayerEvent::WENT_ONLINE, PlayerEvent::EmptyEvent{}); if (parse->PlayerHasQuestSub(EVENT_CONNECT)) { @@ -15567,7 +15574,9 @@ void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) switch (in->Code) { case ClickTrader: { LogTrading("Handle_OP_TraderShop case ClickTrader [{}]", in->Code); - auto outapp = std::make_unique(OP_TraderShop, sizeof(TraderClick_Struct)); + auto outapp = + std::make_unique(OP_TraderShop, static_cast(sizeof(TraderClick_Struct)) + ); auto data = (TraderClick_Struct *) outapp->pBuffer; auto trader_client = entity_list.GetClientByID(in->TraderID); diff --git a/zone/entity.cpp b/zone/entity.cpp index 4eb080cb8..3658c9045 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -505,6 +505,9 @@ void EntityList::MobProcess() zone->GetSecondsBeforeIdle(), zone->GetSecondsBeforeIdle() != 1 ? "s" : "" ); + + CheckToClearTraderAndBuyerTables(); + mob_settle_timer->Disable(); } @@ -2335,7 +2338,7 @@ void EntityList::QueueClientsGuild(const EQApplicationPacket *app, uint32 guild_ void EntityList::QueueClientsGuildBankItemUpdate(GuildBankItemUpdate_Struct *gbius, uint32 guild_id) { auto outapp = std::make_unique(OP_GuildBank, sizeof(GuildBankItemUpdate_Struct)); - auto data = reinterpret_cast(outapp->pBuffer); + auto data = reinterpret_cast(outapp->pBuffer); memcpy(data, gbius, sizeof(GuildBankItemUpdate_Struct)); @@ -6009,3 +6012,22 @@ void EntityList::RestoreCorpse(NPC *npc, uint32_t decay_time) c->SetDecayTimer(decay_time); } } + +void EntityList::CheckToClearTraderAndBuyerTables() +{ + if (zone->GetZoneID() == Zones::BAZAAR) { + TraderRepository::DeleteWhere( + database, + fmt::format( + "`char_zone_id` = {} AND `char_zone_instance_id` = {}", zone->GetZoneID(), zone->GetInstanceID() + ) + ); + BuyerRepository::DeleteBuyers(database, zone->GetZoneID(), zone->GetInstanceID()); + + LogTradingDetail( + "Removed trader and buyer entries for Zone ID [{}] and Instance ID [{}]", + zone->GetZoneID(), + zone->GetInstanceID() + ); + } +} diff --git a/zone/entity.h b/zone/entity.h index 226a306e8..766cd7eda 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -581,6 +581,7 @@ public: void SendMerchantEnd(Mob* merchant); void SendMerchantInventory(Mob* m, int32 slot_id = -1, bool is_delete = false); void RestoreCorpse(NPC* npc, uint32_t decay_time); + void CheckToClearTraderAndBuyerTables(); protected: friend class Zone; diff --git a/zone/trading.cpp b/zone/trading.cpp index 1a1d7bc4d..86f8e8896 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1351,12 +1351,12 @@ void Client::BuyTraderItem(TraderBuy_Struct *tbs, Client *Trader, const EQApplic return; } - auto in = (TraderBuy_Struct *) app->pBuffer; - auto outapp = std::make_unique(OP_Trader, sizeof(TraderBuy_Struct)); - auto outtbs = (TraderBuy_Struct *) outapp->pBuffer; - outtbs->item_id = tbs->item_id; + auto outapp = std::make_unique(OP_Trader, static_cast(sizeof(TraderBuy_Struct))); + auto outtbs = (TraderBuy_Struct *) outapp->pBuffer; + outtbs->item_id = tbs->item_id; + const EQ::ItemInstance *buy_item = nullptr; - uint32 item_id = 0; + uint32 item_id = 0; if (ClientVersion() >= EQ::versions::ClientVersion::RoF) { tbs->item_id = Strings::ToUnsignedBigInt(tbs->serial_number); @@ -1557,15 +1557,15 @@ void Client::BuyTraderItem(TraderBuy_Struct *tbs, Client *Trader, const EQApplic void Client::SendBazaarWelcome() { - const auto results = TraderRepository::GetWelcomeData(database); - auto outapp = std::make_unique(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); - auto data = (BazaarWelcome_Struct *) outapp->pBuffer; + const auto results = TraderRepository::GetWelcomeData(database); + EQApplicationPacket outapp(OP_BazaarSearch, static_cast(sizeof(BazaarWelcome_Struct))); + auto data = (BazaarWelcome_Struct *) outapp.pBuffer; - data->action = BazaarWelcome; - data->traders = results.count_of_traders; - data->items = results.count_of_items; + data->action = BazaarWelcome; + data->traders = results.count_of_traders; + data->items = results.count_of_items; - QueuePacket(outapp.get()); + QueuePacket(&outapp); } void Client::SendBarterWelcome() @@ -1798,7 +1798,10 @@ void Client::SendBuyerResults(BarterSearchRequest_Struct& bsr) { ar(results); } - auto packet = std::make_unique(OP_BuyerItems, ss.str().length() + sizeof(BuyerGeneric_Struct)); + auto packet = std::make_unique( + OP_BuyerItems, + static_cast(ss.str().length()) + static_cast(sizeof(BuyerGeneric_Struct)) + ); auto emu = (BuyerGeneric_Struct *) packet->pBuffer; emu->action = Barter_BuyerSearch; @@ -1851,7 +1854,10 @@ void Client::ShowBuyLines(const EQApplicationPacket *app) { ar(l); } - auto packet = std::make_unique(OP_BuyerItems, ss.str().length() + sizeof(BuyerGeneric_Struct)); + auto packet = std::make_unique( + OP_BuyerItems, + static_cast(ss.str().length()) + static_cast(sizeof(BuyerGeneric_Struct)) + ); auto emu = (BuyerGeneric_Struct *) packet->pBuffer; emu->action = Barter_BuyerInspectBegin; @@ -2075,7 +2081,7 @@ void Client::SellToBuyer(const EQApplicationPacket *app) auto server_packet = std::make_unique( ServerOP_BuyerMessaging, - sizeof(BuyerMessaging_Struct) + static_cast(sizeof(BuyerMessaging_Struct)) ); auto data = (BuyerMessaging_Struct *) server_packet->pBuffer; @@ -2123,7 +2129,10 @@ void Client::SendBuyerPacket(Client* Buyer) { void Client::ToggleBuyerMode(bool status) { - auto outapp = std::make_unique(OP_Barter, sizeof(BuyerSetAppearance_Struct)); + auto outapp = std::make_unique( + OP_Barter, + static_cast(sizeof(BuyerSetAppearance_Struct)) + ); auto data = (BuyerSetAppearance_Struct *) outapp->pBuffer; data->action = Barter_BuyerAppearance; @@ -2319,8 +2328,7 @@ void Client::ModifyBuyLine(const EQApplicationPacket *app) auto packet = std::make_unique( OP_BuyerItems, - ss_customer.str().length() + - sizeof(BuyerGeneric_Struct) + static_cast(ss_customer.str().length()) + static_cast(sizeof(BuyerGeneric_Struct)) ); auto emu = (BuyerGeneric_Struct *) packet->pBuffer; @@ -2813,7 +2821,10 @@ void Client::DoBazaarInspect(BazaarInspect_Struct &in) void Client::SendBazaarDeliveryCosts() { - auto outapp = std::make_unique(OP_BazaarSearch, sizeof(BazaarDeliveryCost_Struct)); + auto outapp = std::make_unique( + OP_BazaarSearch, + static_cast(sizeof(BazaarDeliveryCost_Struct)) + ); auto data = (BazaarDeliveryCost_Struct *) outapp->pBuffer; data->action = DeliveryCostUpdate; @@ -3074,7 +3085,9 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati BazaarAuditTrail(tbs->seller_name, GetName(), buy_item->GetItem()->Name, tbs->quantity, tbs->price, 0); } - auto out_server = std::make_unique(ServerOP_BazaarPurchase, sizeof(BazaarPurchaseMessaging_Struct)); + auto out_server = std::make_unique( + ServerOP_BazaarPurchase, static_cast(sizeof(BazaarPurchaseMessaging_Struct)) + ); auto out_data = (BazaarPurchaseMessaging_Struct *) out_server->pBuffer; out_data->trader_buy_struct = *tbs; @@ -3111,7 +3124,7 @@ void Client::SendBuyerGreeting(uint32 buyer_id) void Client::SendSellerBrowsing(const std::string &browser) { - auto outapp = std::make_unique(OP_Barter, sizeof(BuyerBrowsing_Struct)); + auto outapp = std::make_unique(OP_Barter, static_cast(sizeof(BuyerBrowsing_Struct))); auto eq = (BuyerBrowsing_Struct *) outapp->pBuffer; eq->action = Barter_SellerBrowsing; @@ -3309,7 +3322,7 @@ void Client::SendWindowUpdatesToSellerAndBuyer(BuyerLineSellItem_Struct &blsi) if (blsi.item_quantity - blsi.seller_quantity <= 0) { auto outapp = std::make_unique( OP_BuyerItems, - sizeof(BuyerRemoveItemFromMerchantWindow_Struct) + static_cast(sizeof(BuyerRemoveItemFromMerchantWindow_Struct)) ); auto data = (BuyerRemoveItemFromMerchantWindow_Struct *) outapp->pBuffer; @@ -3399,7 +3412,7 @@ void Client::SendBuyerToBarterWindow(Client *buyer, uint32 action) { auto server_packet = std::make_unique( ServerOP_BuyerMessaging, - sizeof(BuyerMessaging_Struct) + static_cast(sizeof(BuyerMessaging_Struct)) ); auto data = (BuyerMessaging_Struct *) server_packet->pBuffer; @@ -3420,7 +3433,10 @@ void Client::SendBulkBazaarBuyers() return; } - auto outapp = std::make_unique(OP_Barter, sizeof(BuyerAddBuyertoBarterWindow_Struct)); + auto outapp = std::make_unique( + OP_Barter, + static_cast(sizeof(BuyerAddBuyertoBarterWindow_Struct)) + ); auto emu = (BuyerAddBuyertoBarterWindow_Struct *) outapp->pBuffer; for (auto const &b: results) { @@ -3663,11 +3679,11 @@ bool Client::ValidateBuyLineItems(std::map &i int64 Client::ValidateBuyLineCost(std::map &item_map) { - int64 proposed_total_cost = std::accumulate( + uint64 proposed_total_cost = std::accumulate( item_map.cbegin(), item_map.cend(), - 0, - [](auto prev_sum, const std::pair &x) { + static_cast(0), + [](uint64 prev_sum, const std::pair &x) { return prev_sum + x.second.item_cost; } ); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 985f02509..209ff87db 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3794,7 +3794,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } auto item_sn = Strings::ToUnsignedBigInt(in->trader_buy_struct.serial_number); - auto outapp = std::make_unique(OP_Trader, sizeof(TraderBuy_Struct)); + auto outapp = std::make_unique(OP_Trader, static_cast(sizeof(TraderBuy_Struct))); auto data = (TraderBuy_Struct *) outapp->pBuffer; memcpy(data, &in->trader_buy_struct, sizeof(TraderBuy_Struct)); @@ -3841,7 +3841,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case Barter_AddToBarterWindow: { auto outapp = std::make_unique( OP_Barter, - sizeof(BuyerAddBuyertoBarterWindow_Struct) + static_cast(sizeof(BuyerAddBuyertoBarterWindow_Struct)) ); auto emu = (BuyerAddBuyertoBarterWindow_Struct *) outapp->pBuffer; @@ -3858,7 +3858,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) case Barter_RemoveFromBarterWindow: { auto outapp = std::make_unique( OP_Barter, - sizeof(BuyerRemoveBuyerFromBarterWindow_Struct) + static_cast(sizeof(BuyerRemoveBuyerFromBarterWindow_Struct)) ); auto emu = (BuyerRemoveBuyerFromBarterWindow_Struct *) outapp->pBuffer; diff --git a/zone/zoning.cpp b/zone/zoning.cpp index e8a4b5461..1f84ff6e3 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -681,9 +681,20 @@ void Client::MoveZoneInstanceRaid(uint16 instance_id, const glm::vec4 &location) void Client::ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm) { - // From what I have read, dragged corpses should stay with the player for Intra-zone summons etc, but we can implement that later. + // From what I have read, dragged corpses should stay with the player for Intra-zone summons etc, but we can + // implement that later. ClearDraggedCorpses(); + // Added to ensure that if a player is moved (ported, gmmove, etc) and they are an active trader or buyer, they will + // be removed from future transactions. + if (IsTrader()) { + TraderEndTrader(); + } + + if (IsBuyer()) { + ToggleBuyerMode(false); + } + if(zoneID == 0) zoneID = zone->GetZoneID();