diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index e0c93e552..4a64160ce 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6378,6 +6378,19 @@ CREATE INDEX idx_trader_active_transaction ON trader (active_transaction); )", .content_schema_update = false }, + ManifestEntry{ + .version = 9296, + .description = "2025_02_01_trader_table_listing_date.sql", + .check = "SHOW CREATE TABLE `trader`", + .condition = "missing", + .match = "listing_date", + .sql = R"( +ALTER TABLE `trader` + ADD COLUMN `listing_date` DATETIME NULL DEFAULT NULL AFTER `active_transaction`, + ADD INDEX `idx_trader_listing_date` (`listing_date`); +)", + .content_schema_update = false + } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ diff --git a/common/repositories/base/base_trader_repository.h b/common/repositories/base/base_trader_repository.h index 7670587df..99236fcac 100644 --- a/common/repositories/base/base_trader_repository.h +++ b/common/repositories/base/base_trader_repository.h @@ -36,6 +36,7 @@ public: uint32_t char_zone_id; int32_t char_zone_instance_id; uint8_t active_transaction; + time_t listing_date; }; static std::string PrimaryKey() @@ -63,6 +64,7 @@ public: "char_zone_id", "char_zone_instance_id", "active_transaction", + "listing_date", }; } @@ -86,6 +88,7 @@ public: "char_zone_id", "char_zone_instance_id", "active_transaction", + "UNIX_TIMESTAMP(listing_date)", }; } @@ -143,6 +146,7 @@ public: e.char_zone_id = 0; e.char_zone_instance_id = 0; e.active_transaction = 0; + e.listing_date = 0; return e; } @@ -196,6 +200,7 @@ public: e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.char_zone_instance_id = row[15] ? static_cast(atoi(row[15])) : 0; e.active_transaction = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; + e.listing_date = strtoll(row[17] ? row[17] : "-1", nullptr, 10); return e; } @@ -245,6 +250,7 @@ public: v.push_back(columns[14] + " = " + std::to_string(e.char_zone_id)); v.push_back(columns[15] + " = " + std::to_string(e.char_zone_instance_id)); v.push_back(columns[16] + " = " + std::to_string(e.active_transaction)); + v.push_back(columns[17] + " = FROM_UNIXTIME(" + (e.listing_date > 0 ? std::to_string(e.listing_date) : "null") + ")"); auto results = db.QueryDatabase( fmt::format( @@ -283,6 +289,7 @@ public: v.push_back(std::to_string(e.char_zone_id)); v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); + v.push_back("FROM_UNIXTIME(" + (e.listing_date > 0 ? std::to_string(e.listing_date) : "null") + ")"); auto results = db.QueryDatabase( fmt::format( @@ -329,6 +336,7 @@ public: v.push_back(std::to_string(e.char_zone_id)); v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); + v.push_back("FROM_UNIXTIME(" + (e.listing_date > 0 ? std::to_string(e.listing_date) : "null") + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -379,6 +387,7 @@ public: e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.char_zone_instance_id = row[15] ? static_cast(atoi(row[15])) : 0; e.active_transaction = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; + e.listing_date = strtoll(row[17] ? row[17] : "-1", nullptr, 10); all_entries.push_back(e); } @@ -420,6 +429,7 @@ public: e.char_zone_id = row[14] ? static_cast(strtoul(row[14], nullptr, 10)) : 0; e.char_zone_instance_id = row[15] ? static_cast(atoi(row[15])) : 0; e.active_transaction = row[16] ? static_cast(strtoul(row[16], nullptr, 10)) : 0; + e.listing_date = strtoll(row[17] ? row[17] : "-1", nullptr, 10); all_entries.push_back(e); } @@ -511,6 +521,7 @@ public: v.push_back(std::to_string(e.char_zone_id)); v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); + v.push_back("FROM_UNIXTIME(" + (e.listing_date > 0 ? std::to_string(e.listing_date) : "null") + ")"); auto results = db.QueryDatabase( fmt::format( @@ -550,6 +561,7 @@ public: v.push_back(std::to_string(e.char_zone_id)); v.push_back(std::to_string(e.char_zone_instance_id)); v.push_back(std::to_string(e.active_transaction)); + v.push_back("FROM_UNIXTIME(" + (e.listing_date > 0 ? std::to_string(e.listing_date) : "null") + ")"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index 1892de6be..5d2104fc7 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -130,7 +130,8 @@ public: } for (auto &i: items) { - i.item_cost = new_price; + i.item_cost = new_price; + i.listing_date = time(nullptr); } return ReplaceMany(db, items); @@ -178,6 +179,7 @@ public: auto m = trader_item[0]; m.item_charges = quantity; + m.listing_date = time(nullptr); return UpdateOne(db, m); } @@ -221,6 +223,7 @@ public: } e.active_transaction = status == true ? 1 : 0; + e.listing_date = time(nullptr); return UpdateOne(db, e); } diff --git a/common/version.h b/common/version.h index dec788622..80b014757 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9295 +#define CURRENT_BINARY_DATABASE_VERSION 9296 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 #endif diff --git a/zone/client.h b/zone/client.h index 819560d1c..5ce9df4d8 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1139,6 +1139,8 @@ public: uint32 GetTraderCount() { return m_trader_count; } void IncrementTraderCount() { m_trader_count += 1; } void DecrementTraderCount() { m_trader_count > 0 ? m_trader_count -= 1 : m_trader_count = 0; } + void SetTraderTransactionDate() { m_trader_transaction_date = time(nullptr); } + time_t GetTraderTransactionDate() { return m_trader_transaction_date; } eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; } void SetFilter(eqFilterType filter_id, eqFilterMode filter_mode) { ClientFilters[filter_id] = filter_mode; } @@ -1976,6 +1978,7 @@ private: uint8 firstlogon; uint32 mercid; // current merc uint8 mercSlot; // selected merc slot + time_t m_trader_transaction_date; uint32 m_trader_count{}; uint32 m_buyer_id; uint32 m_barter_time; diff --git a/zone/trading.cpp b/zone/trading.cpp index 486c2f4ff..48d2d8b26 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1106,6 +1106,7 @@ void Client::TraderStartTrader(const EQApplicationPacket *app) trader_item.item_id = inst->GetID(); trader_item.item_sn = in->serial_number[i]; trader_item.slot_id = i; + trader_item.listing_date = time(nullptr); if (inst->IsAugmented()) { auto augs = inst->GetAugmentIDs(); trader_item.aug_slot_1 = augs.at(0); @@ -1812,6 +1813,7 @@ void Client::DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria) return; } + SetTraderTransactionDate(); std::stringstream ss{}; cereal::BinaryOutputArchive ar(ss); ar(results); @@ -2798,6 +2800,7 @@ void Client::TraderPriceUpdate(const EQApplicationPacket *app) trader_item.item_cost = tpus->NewPrice; trader_item.item_id = newgis->items[i]; trader_item.item_sn = newgis->serial_number[i]; + trader_item.listing_date = time(nullptr); if (item_detail->IsAugmented()) { auto augs = item_detail->GetAugmentIDs(); trader_item.aug_slot_1 = augs.at(0); @@ -3072,7 +3075,7 @@ void Client::BuyTraderItemOutsideBazaar(TraderBuy_Struct *tbs, const EQApplicati { auto in = (TraderBuy_Struct *) app->pBuffer; auto trader_item = TraderRepository::GetItemBySerialNumber(database, tbs->serial_number, tbs->trader_id); - if (!trader_item.id) { + if (!trader_item.id || GetTraderTransactionDate() < trader_item.listing_date) { LogTrading("Attempt to purchase an item outside of the Bazaar trader_id [{}] item serial_number " "[{}] The Traders data was outdated.", tbs->trader_id,