From 116040914cca1f3807e5335d29d9ab378332bd36 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sun, 13 Apr 2025 09:48:38 -0300 Subject: [PATCH] New BazaarSearch query routine --- common/bazaar.cpp | 93 ++++++++++++++++++------- common/repositories/items_repository.h | 4 +- common/repositories/trader_repository.h | 50 +++++++++++-- common/strings.cpp | 19 +++++ common/strings.h | 2 + zone/client_packet.cpp | 2 +- zone/trading.cpp | 45 ++++++++---- 7 files changed, 165 insertions(+), 50 deletions(-) diff --git a/common/bazaar.cpp b/common/bazaar.cpp index 2943a9b93..a9c2fad21 100644 --- a/common/bazaar.cpp +++ b/common/bazaar.cpp @@ -262,39 +262,78 @@ Bazaar::GetSearchResults( } std::vector all_entries; - std::vector trader_items_ids{}; + std::unordered_set trader_items_ids{}; - auto const trader_results = TraderRepository::GetBazaarTraderDetails(db, search_criteria_trader); - if (trader_results.empty()) { - LogTradingDetail("Bazaar - No traders found in bazaar search."); - return all_entries; - } - - for (auto const &i: trader_results) { - trader_items_ids.push_back(std::to_string(i.trader.item_id)); - } - - auto const item_results = ItemsRepository::GetItemsForBazaarSearch( - content_db, - trader_items_ids, - std::string(search.item_name), +// auto const trader_results = TraderRepository::GetBazaarTraderDetails(db, search_criteria_trader, search.max_results); + auto const item_results = TraderRepository::GetBazaarTraderDetails( + db, + search_criteria_trader, + search.item_name, field_criteria_items, where_criteria_items, search.max_results ); + // if (trader_results.empty()) { + // LogTradingDetail("Bazaar - No traders found in bazaar search."); + // return all_entries; + // } + + // for (auto const &i: trader_results) { + // trader_items_ids.emplace(std::to_string(i.trader.item_id)); + // //trader_items_ids.push_back(std::to_string(i.trader.item_id)); + // } + + // auto const item_results = ItemsRepository::GetItemsForBazaarSearch( + // content_db, + // trader_items_ids, + // std::string(search.item_name), + // field_criteria_items, + // where_criteria_items, + // search.max_results + // ); + if (item_results.empty()) { LogTradingDetail("Bazaar - No items found in bazaar search."); return all_entries; } - all_entries.reserve(trader_results.size()); + //all_entries.reserve(trader_results.size()); - for (auto const& t:trader_results) { - if (!item_results.contains(t.trader.item_id)) { - continue; - } + // for (auto const& t:trader_results) { + // if (!item_results.contains(t.trader.item_id)) { + // continue; + // } + // + // BazaarSearchResultsFromDB_Struct r{}; + // r.count = 1; + // r.trader_id = t.trader.character_id; + // r.item_unique_id = t.trader.item_unique_id; + // r.cost = t.trader.item_cost; + // r.slot_id = t.trader.slot_id; + // r.charges = t.trader.item_charges; + // r.stackable = item_results.at(t.trader.item_id).stackable; + // r.icon_id = item_results.at(t.trader.item_id).icon; + // r.trader_zone_id = t.trader.char_zone_id; + // r.trader_zone_instance_id = t.trader.char_zone_instance_id; + // r.trader_entity_id = t.trader.char_entity_id; + // r.item_name = fmt::format("{:.63}\0", item_results.at(t.trader.item_id).name); + // r.trader_name = fmt::format("{:.63}\0", t.trader_name); + // r.item_stat = item_results.at(t.trader.item_id).stats; + // + // if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + // if (convert || + // char_zone_id != Zones::BAZAAR || + // (char_zone_id == Zones::BAZAAR && r.trader_zone_instance_id != char_zone_instance_id) + // ) { + // r.trader_id = TraderRepository::TRADER_CONVERT_ID + r.trader_zone_instance_id; + // } + // } + // + // all_entries.push_back(r); + // } + for (auto const& t:item_results) { BazaarSearchResultsFromDB_Struct r{}; r.count = 1; r.trader_id = t.trader.character_id; @@ -302,14 +341,14 @@ Bazaar::GetSearchResults( r.cost = t.trader.item_cost; r.slot_id = t.trader.slot_id; r.charges = t.trader.item_charges; - r.stackable = item_results.at(t.trader.item_id).stackable; - r.icon_id = item_results.at(t.trader.item_id).icon; + r.stackable = t.stackable; + r.icon_id = t.icon; r.trader_zone_id = t.trader.char_zone_id; r.trader_zone_instance_id = t.trader.char_zone_instance_id; r.trader_entity_id = t.trader.char_entity_id; - r.item_name = fmt::format("{:.63}\0", item_results.at(t.trader.item_id).name); + r.item_name = fmt::format("{:.63}\0", t.name); r.trader_name = fmt::format("{:.63}\0", t.trader_name); - r.item_stat = item_results.at(t.trader.item_id).stats; + r.item_stat = t.stats; if (RuleB(Bazaar, UseAlternateBazaarSearch)) { if (convert || @@ -323,9 +362,9 @@ Bazaar::GetSearchResults( all_entries.push_back(r); } - if (all_entries.size() > search.max_results) { - all_entries.resize(search.max_results); - } + // if (all_entries.size() > search.max_results) { + // all_entries.resize(search.max_results); + // } LogTrading("Returning [{}] items from search results", all_entries.size()); diff --git a/common/repositories/items_repository.h b/common/repositories/items_repository.h index 4e5a8868e..7ec91a61f 100644 --- a/common/repositories/items_repository.h +++ b/common/repositories/items_repository.h @@ -47,7 +47,7 @@ public: static std::unordered_map GetItemsForBazaarSearch( Database& db, - const std::vector &search_ids, + const std::unordered_set &search_ids, const std::string &name, const std::string &field_criteria_items, const std::string &where_criteria_items, @@ -57,7 +57,7 @@ public: auto query = fmt::format( "SELECT id, name, stackable, icon, {} " "FROM items " - "WHERE `name` LIKE '%%{}%%' AND {} AND id IN({}) " + "WHERE `name` LIKE '%{}%' AND {} AND id IN({}) " "ORDER BY id ASC", field_criteria_items, Strings::Escape(name), diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index 5d33a2f88..32381da55 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -29,8 +29,12 @@ public: }; struct BazaarTraderSearch_Struct { - Trader trader; + Trader trader; std::string trader_name; + std::string name; + bool stackable; + uint32 icon; + uint32 stats; }; struct WelcomeData_Struct { @@ -296,22 +300,50 @@ public: static std::vector GetBazaarTraderDetails( Database &db, - std::string &search_criteria_trader + const std::string &search_criteria_trader, + const std::string &name, + const std::string &field_criteria_items, + const std::string &where_criteria_items, + uint32 max_results ) { std::vector all_entries{}; - auto query = fmt::format( + auto query_2 = fmt::format( + "WITH ranked_trader_items AS (" "SELECT trader.id, trader.character_id, trader.item_id, trader.item_unique_id, trader.augment_one, " "trader.augment_two, trader.augment_three, trader.augment_four, trader.augment_five, trader.augment_six, " "trader.item_charges, trader.item_cost, trader.slot_id, trader.char_entity_id, trader.char_zone_id, " - "trader.char_zone_instance_id, trader.active_transaction, c.`name` FROM `trader` " + "trader.char_zone_instance_id, trader.active_transaction, c.`name`, " + "items.name AS n1, items.stackable, items.icon, {}, " + "ROW_NUMBER() OVER (PARTITION BY trader.character_id) AS row_num " + "FROM trader " "INNER JOIN character_data AS c ON trader.character_id = c.id " - "WHERE {} ORDER BY trader.character_id ASC", - search_criteria_trader + "JOIN peq642024_content.items AS items ON trader.item_id = items.id " + "WHERE items.`name` LIKE '%{}%' AND {} AND {}" + ") " + "SELECT * FROM ranked_trader_items " + "WHERE row_num <= '{}';", + field_criteria_items, + Strings::Escape(name), + where_criteria_items, + search_criteria_trader, + max_results ); - auto results = db.QueryDatabase(query); + // auto query = fmt::format( + // "SELECT trader.id, trader.character_id, trader.item_id, trader.item_unique_id, trader.augment_one, " + // "trader.augment_two, trader.augment_three, trader.augment_four, trader.augment_five, trader.augment_six, " + // "trader.item_charges, trader.item_cost, trader.slot_id, trader.char_entity_id, trader.char_zone_id, " + // "trader.char_zone_instance_id, trader.active_transaction, c.`name` FROM `trader` " + // "INNER JOIN character_data AS c ON trader.character_id = c.id " + // "WHERE {} " + // "GROUP BY trader.item_id " + // "ORDER BY trader.character_id ASC", + // search_criteria_trader + // ); + + auto results = db.QueryDatabase(query_2); if (results.RowCount() == 0) { return all_entries; @@ -339,6 +371,10 @@ public: e.trader.char_zone_instance_id = row[15] ? static_cast(atoi(row[15])) : 0; e.trader.active_transaction = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; e.trader_name = row[17] ? row[17] : std::string(""); + e.name = row[18] ? row[18] : ""; + e.stackable = atoi(row[19]) ? true : false; + e.icon = row[20] ? static_cast(atoi(row[20])) : 0; + e.stats = row[21] ? static_cast(atoi(row[21])) : 0; all_entries.push_back(e); } diff --git a/common/strings.cpp b/common/strings.cpp index eb56ab2f5..af8c4187b 100644 --- a/common/strings.cpp +++ b/common/strings.cpp @@ -970,3 +970,22 @@ bool Strings::IsValidJson(const std::string &json) return result; } + +std::string Strings::Implode(const std::string& glue, std::unordered_set src) +{ + if (src.empty()) { + return {}; + } + + std::ostringstream output; + std::unordered_set::iterator src_iter; + + for (src_iter = src.begin(); src_iter != src.end(); src_iter++) { + output << *src_iter << glue; + } + + std::string final_output = output.str(); + final_output.resize(output.str().size() - glue.size()); + + return final_output; +} diff --git a/common/strings.h b/common/strings.h index 8ae8fee25..dcb6b01f7 100644 --- a/common/strings.h +++ b/common/strings.h @@ -42,6 +42,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -79,6 +80,7 @@ public: static std::string Escape(const std::string &s); static std::string GetBetween(const std::string &s, std::string start_delim, std::string stop_delim); static std::string Implode(const std::string& glue, std::vector src); + static std::string Implode(const std::string& glue, std::unordered_set src); static std::string Join(const std::vector &ar, const std::string &delim); static std::string Join(const std::vector &ar, const std::string &delim); static std::string MillisecondsToTime(int duration); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ab3126c70..89b0d1d07 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -15456,7 +15456,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app) break; } default: { - LogTrading("Unknown size for OP_Trader: [{}]\n", app->size); + //LogTradingDetail("Unknown size for OP_Trader: [{}]", app->size); } } } diff --git a/zone/trading.cpp b/zone/trading.cpp index f2c10f14d..5305d5425 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1399,8 +1399,20 @@ static void BazaarAuditTrail(const char *seller, const char *buyer, const char * void Client::BuyTraderItem(const EQApplicationPacket *app) { - auto in = reinterpret_cast(app->pBuffer); - auto trader = entity_list.GetClientByID(in->trader_id); + struct Checks { + bool take_customer_money {false}; + bool give_trader_money {false}; + bool give_customer_item {false}; + bool take_trader_item {false}; + uint64 customer_money {false}; + uint64 trader_money {false}; + EQ::ItemInstance *trader_item {nullptr}; + EQ::ItemInstance *customer_item {nullptr}; + }; + + Checks checks{}; + auto in = reinterpret_cast(app->pBuffer); + auto trader = entity_list.GetClientByID(in->trader_id); if (!trader || !trader->IsTrader()) { Message(Chat::Red, "The trader could not be found."); @@ -1436,6 +1448,8 @@ void Client::BuyTraderItem(const EQApplicationPacket *app) if (CheckLoreConflict(inst_copy->GetItem())) { MessageString(Chat::Red, DUPLICATE_LORE); + in->method = BazaarByParcel; + in->sub_action = DataOutDated; TradeRequestFailed(app); return; } @@ -1474,6 +1488,7 @@ void Client::BuyTraderItem(const EQApplicationPacket *app) TradeRequestFailed(app); return; } + checks.take_customer_money = true; if (!trader->RemoveItemByItemUniqueId(buy_inst->GetUniqueID(), quantity)) { AddMoneyToPP(total_cost, true); @@ -1481,8 +1496,10 @@ void Client::BuyTraderItem(const EQApplicationPacket *app) TradeRequestFailed(app); return; } + checks.take_trader_item = true; trader->AddMoneyToPP(total_cost, true); + checks.give_trader_money = true; if (!PutItemInInventoryWithStacking(inst_copy.get())) { AddMoneyToPP(total_cost, true); @@ -1492,6 +1509,7 @@ void Client::BuyTraderItem(const EQApplicationPacket *app) TradeRequestFailed(app); return; } + checks.give_customer_item = true; auto [slot_id, merchant_data] = GetDataFromMerchantListByItemUniqueId(buy_inst->GetUniqueID()); auto [item_id, merchant_quantity, item_unique_id] = merchant_data; @@ -2576,21 +2594,22 @@ void Client::TraderUpdateItem(const EQApplicationPacket *app) } } else { - for (auto const i : result) { - auto [slot_id, merchant_data] = customer->GetDataFromMerchantListByItemUniqueId(i.item_unique_id); - auto [item_id, merchant_quantity, item_unique_id] = merchant_data; - std::unique_ptr vendor_inst_copy(inst ? inst->Clone() : nullptr); - vendor_inst_copy->SetUniqueID(i.item_unique_id); - vendor_inst_copy->SetMerchantCount(i.item_charges); - vendor_inst_copy->SetMerchantSlot(slot_id); - vendor_inst_copy->SetPrice(new_price); - customer->SendItemPacket(slot_id, vendor_inst_copy.get(), ItemPacketMerchant); - } - + if (customer) { + for (auto const i : result) { + auto [slot_id, merchant_data] = customer->GetDataFromMerchantListByItemUniqueId(i.item_unique_id); + auto [item_id, merchant_quantity, item_unique_id] = merchant_data; + std::unique_ptr vendor_inst_copy(inst ? inst->Clone() : nullptr); + vendor_inst_copy->SetUniqueID(i.item_unique_id); + vendor_inst_copy->SetMerchantCount(i.item_charges); + vendor_inst_copy->SetMerchantSlot(slot_id); + vendor_inst_copy->SetPrice(new_price); + customer->SendItemPacket(slot_id, vendor_inst_copy.get(), ItemPacketMerchant); + } customer->Message( Chat::Red, fmt::format("Trader {} updated the price of item {}", GetCleanName(), inst->GetItem()->Name).c_str() ); + } } in->sub_action = BazaarPriceChange_UpdatePrice;