From 37a7b7fc419ec5012a4c718aaa1570bcc1fee6a2 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Sun, 19 Jan 2025 21:02:53 -0400 Subject: [PATCH] [Feature] Add Alternate Bazaar Search Approach (#4600) * Add Alternate Bazaar Search This adds an alternate bazaar search allowing multinstance bazaar searching and traders above 600. Allows searches based on Bazaar Shard * Update worldserver.cpp --------- Co-authored-by: Mitch Freeman Co-authored-by: Akkadius --- common/bazaar.cpp | 25 +++++++++- common/repositories/trader_repository.h | 42 ++++++++++++++++- common/ruletypes.h | 1 + zone/client.h | 2 +- zone/client_packet.cpp | 20 ++++++++ zone/trading.cpp | 63 ++++++++++++++++++++++--- zone/worldserver.cpp | 26 ++++++++-- 7 files changed, 166 insertions(+), 13 deletions(-) diff --git a/common/bazaar.cpp b/common/bazaar.cpp index b24fcb1d1..cce3fcdd3 100644 --- a/common/bazaar.cpp +++ b/common/bazaar.cpp @@ -31,6 +31,7 @@ Bazaar::GetSearchResults( char_zone_instance_id ); + bool convert = false; std::string search_criteria_trader("TRUE "); if (search.search_scope == NonRoFBazaarSearchScope) { @@ -51,8 +52,24 @@ Bazaar::GetSearchResults( ); } else if (search.trader_id > 0) { - search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (search.trader_id >= TraderRepository::TRADER_CONVERT_ID) { + convert = true; + search_criteria_trader.append(fmt::format( + " AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}", + Zones::BAZAAR, + search.trader_id - TraderRepository::TRADER_CONVERT_ID) + ); + } + else { + search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + } + } + else { + search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + } } + if (search.min_cost != 0) { search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost * 1000)); } @@ -355,6 +372,12 @@ Bazaar::GetSearchResults( } LogTradingDetail("Found item [{}] meeting search criteria.", r.item_name); + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (convert || (r.trader_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); } diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index 2999b738a..a8e3c13c1 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -12,6 +12,7 @@ class TraderRepository : public BaseTraderRepository { public: + static constexpr uint32 TRADER_CONVERT_ID = 4000000000; struct DistinctTraders_Struct { uint32 trader_id; @@ -40,7 +41,11 @@ public: int32 char_zone_instance_id ); - static BulkTraders_Struct GetDistinctTraders(Database &db, uint32 char_zone_instance_id, uint32 max_results) + static BulkTraders_Struct GetDistinctTraders( + Database &db, + uint32 char_zone_instance_id, + uint32 max_results = std::numeric_limits::max() + ) { BulkTraders_Struct all_entries{}; std::vector distinct_traders; @@ -49,7 +54,9 @@ public: "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " "FROM trader AS t " "JOIN character_data AS c ON t.char_id = c.id " - "ORDER BY t.char_zone_instance_id = {} DESC LIMIT {};", + "WHERE t.char_zone_instance_id = {} " + "ORDER BY t.char_zone_instance_id ASC " + "LIMIT {}", char_zone_instance_id, max_results) ); @@ -227,6 +234,37 @@ public: return DeleteWhere(db, fmt::format("`id` IN({})", Strings::Implode(",", delete_ids))); } + + static DistinctTraders_Struct GetTraderByInstanceAndSerialnumber( + Database &db, + uint32 instance_id, + const char *serial_number + ) + { + DistinctTraders_Struct trader{}; + + auto query = fmt::format( + "SELECT t.id, t.char_id, c.name " + "FROM trader AS t " + "JOIN character_data AS c ON c.id = t.char_id " + "WHERE t.char_zone_id = 151 AND t.char_zone_instance_id = {} AND t.item_sn = {} LIMIT 1", + instance_id, + serial_number + ); + + auto results = db.QueryDatabase(query); + + if (results.RowCount() == 0) { + return trader; + } + + auto row = results.begin(); + std::string name = row[2]; + trader.trader_id = Strings::ToUnsignedInt(row[1]); + trader.trader_name = row[2] ? row[2] : ""; + + return trader; + } }; #endif //EQEMU_TRADER_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index e27ed3f3c..2b52e081d 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -830,6 +830,7 @@ RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a ba RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.") RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.") RULE_INT(Bazaar, MaxBuyerInventorySearchResults, 200, "Maximum number of search results when a Buyer searches the global item list. Default is 200. RoF+ Only.") +RULE_BOOL(Bazaar, UseAlternateBazaarSearch, false, "Allows the bazaar search window to search across bazaar shards. Default is false.") RULE_CATEGORY_END() RULE_CATEGORY(Mail) diff --git a/zone/client.h b/zone/client.h index adba50bac..e8a93bac5 100644 --- a/zone/client.h +++ b/zone/client.h @@ -295,7 +295,7 @@ public: void SendBazaarDone(uint32 trader_id); void SendBulkBazaarTraders(); void SendBulkBazaarBuyers(); - void DoBazaarInspect(const BazaarInspect_Struct &in); + void DoBazaarInspect(BazaarInspect_Struct &in); void SendBazaarDeliveryCosts(); static std::string DetermineMoneyString(uint64 copper); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 26da7ebce..92ef6a3d9 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -15535,6 +15535,26 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) // Client has elected to buy an item from a Trader // auto in = (TraderBuy_Struct *) app->pBuffer; + + if (RuleB(Bazaar, UseAlternateBazaarSearch) && in->trader_id >= TraderRepository::TRADER_CONVERT_ID) { + auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( + database, + in->trader_id - TraderRepository::TRADER_CONVERT_ID, + in->serial_number + ); + + if (!trader.trader_id) { + LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", + in->trader_id - TraderRepository::TRADER_CONVERT_ID, + in->serial_number + ); + return; + } + + in->trader_id = trader.trader_id; + strn0cpy(in->seller_name, trader.trader_name.c_str(), sizeof(in->seller_name)); + } + auto trader = entity_list.GetClientByID(in->trader_id); switch (in->method) { diff --git a/zone/trading.cpp b/zone/trading.cpp index d9fb3499b..bdbb6326c 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -3222,11 +3222,42 @@ void Client::SendBulkBazaarTraders() return; } - auto results = TraderRepository::GetDistinctTraders( - database, - GetInstanceID(), - EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit - ); + TraderRepository::BulkTraders_Struct results{}; + + if (RuleB(Bazaar, UseAlternateBazaarSearch)) + { + if (GetZoneID() == Zones::BAZAAR) { + results = TraderRepository::GetDistinctTraders(database, GetInstanceID()); + } + + uint32 number = 1; + auto shards = CharacterDataRepository::GetInstanceZonePlayerCounts(database, Zones::BAZAAR); + for (auto const &shard: shards) { + if (GetZoneID() != Zones::BAZAAR || (GetZoneID() == Zones::BAZAAR && GetInstanceID() != shard.instance_id)) { + + TraderRepository::DistinctTraders_Struct t{}; + t.entity_id = 0; + t.trader_id = TraderRepository::TRADER_CONVERT_ID + shard.instance_id; + t.trader_name = fmt::format("Bazaar Shard {}", number); + t.zone_id = Zones::BAZAAR; + t.zone_instance_id = shard.instance_id; + results.count += 1; + results.name_length += t.trader_name.length() + 1; + results.traders.push_back(t); + } + + number++; + } + } + else { + results = TraderRepository::GetDistinctTraders( + database, + GetInstanceID(), + EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit + ); + } + + SetTraderCount(results.count); SetTraderCount(results.count); @@ -3251,8 +3282,28 @@ void Client::SendBulkBazaarTraders() QueuePacket(outapp.get()); } -void Client::DoBazaarInspect(const BazaarInspect_Struct &in) +void Client::DoBazaarInspect(BazaarInspect_Struct &in) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (in.trader_id >= TraderRepository::TRADER_CONVERT_ID) { + auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( + database, + in.trader_id - TraderRepository::TRADER_CONVERT_ID, + fmt::format("{}", in.serial_number).c_str() + ); + + if (!trader.trader_id) { + LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", + in.trader_id - TraderRepository::TRADER_CONVERT_ID, + in.serial_number + ); + return; + } + + in.trader_id = trader.trader_id; + } + } + auto items = TraderRepository::GetWhere( database, fmt::format("`char_id` = '{}' AND `item_sn` = '{}'", in.trader_id, in.serial_number) ); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 8112d422c..995887f17 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3926,16 +3926,36 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) out->action = AddTraderToBazaarWindow; if (c.second->GetTraderCount() < EQ::constants::StaticLookup(c.second->ClientVersion())->BazaarTraderLimit) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (out->zone_id == Zones::BAZAAR && + out->zone_instance_id == c.second->GetInstanceID()) { + c.second->IncrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + else { c.second->IncrementTraderCount(); c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } } - break; } case TraderOff: { out->action = RemoveTraderFromBazaarWindow; - c.second->DecrementTraderCount(); - c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + if (c.second->GetTraderCount() <= + EQ::constants::StaticLookup(c.second->ClientVersion())->BazaarTraderLimit) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (out->zone_id == Zones::BAZAAR && + out->zone_instance_id == c.second->GetInstanceID()) { + c.second->DecrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + else { + c.second->DecrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } break; } default: {