/* EQ2Emulator: Everquest II Server Emulator Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. EQ2Emulator is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. EQ2Emulator is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ #pragma once #include #include #include #include #include #include #include #include #include "../../common/types.h" // Key = (character_id, unique_id) using Key = std::pair; class Item; class Client; struct SaleItem { int64 unique_id; int32 character_id; int32 house_id; int64 item_id; int64 cost_copper; bool for_sale; sint32 inv_slot_id; int16 slot_id; int16 count; bool from_inventory; string creator; }; struct SellerInfo { int32 character_id; std::string seller_name; int64 house_id; bool sale_enabled; bool sell_from_inventory; int64 coin_session; int64 coin_total; }; struct SellerLog { int64 log_id; // auto_increment PK std::string timestamp; // e.g. "October 20, 2005, 05:29:33 AM" std::string message; // the human-readable text }; class BrokerManager { public: BrokerManager(); //–– Player management void AddSeller(int32 cid, const std::string& name, int32 house_id, bool sale_enabled, bool sell_from_inventory); void LoadSeller(int32 cid, const std::string& name, int32 house_id, bool sale_enabled, bool sell_from_inventory, int64 coin_session, int64 coin_total); int64 ResetSellerSessionCoins(int32 cid); void AddSellerSessionCoins(int32 cid, uint64 session); void RemoveSeller(int32 character_id, bool peerCacheOnly = false); //–– Item management void AddItem(const SaleItem& item, bool peerCacheOnly = false); void LoadItem(const SaleItem& item); //–– Activate / deactivate sale flag void SetSaleStatus(int32 cid, int64 uid, bool for_sale); bool IsItemListed(int32 cid, int64 uid); void SetSalePrice(int32 cid, int64 uid, int64 price); int64 GetSalePrice(int32 cid, int64 uid); //–– Remove quantity void RemoveItem(int32 cid, int64 uid, int16 quantity = 1, bool shouldDelete = false); //–– Attempt to buy (atomic DB + in-memory + broadcast) bool BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity); bool IsItemForSale(int32 seller_cid, int64 uid) const; //–– Called when a peer notifies that an item was sold/removed (in-memory only) void OnPeerRemoveItem(int32 character_id, int64 unique_id); //–– Queries std::vector GetActiveForSaleItems(int32 cid) const; std::optional GetActiveItem(int32 cid, int64 uid) const; bool IsSellingItems(int32 cid, bool vaultOnly = false) const; std::vector GetInactiveItems(int32 cid) const; //–– Global search API (only active_items_) vector* GetItems( const std::string& name, int64 itype, int64 ltype, int64 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, const std::string& seller, const std::string& adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, int8 itemclass ) const; //–– UI helper: get (unique_id, cost) for active items std::vector> GetUniqueIDsAndCost(int32 cid) const; std::optional GetSellerInfo(int32 character_id) const; //–– Lookup seller name std::string GetSellerName(int32 cid) const; bool IsSaleEnabled(int32 cid) const; bool CanSellFromInventory(int32 cid) const; int32 GetHouseID(int32 cid) const; bool IsItemFromInventory(int32 cid, int64 uid) const; void LockActiveItemsForClient(Client* client) const; std::string GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin); void LogSale(int32 character_id, const std::string& buyer_name,const std::string& item_desc, int16 quantity, int64 coin); void LogSaleMessage(int32 character_id,const std::string& log_message); std::vector GetSellerLog(int32 character_id) const; static std::string EscapeSQLString(const std::string& s) { std::string out; out.reserve(s.size() * 2); for (char c : s) { if (c == '\'') out += "''"; else out += c; } return out; } private: mutable std::shared_mutex mtx_; std::unordered_map players_; std::unordered_map< int32, std::unordered_map > active_items_by_char_; std::unordered_map< int32, std::unordered_map > inactive_items_by_char_; //–– DB sync (async writes) void SavePlayerToDB(const SellerInfo& p); void SaveItemToDB(const SaleItem& i); void UpdateItemInDB(const SaleItem& i); void DeleteItemFromDB(int32 character_id, int64 unique_id); void DeleteCharacterItemFromDB(int32 cid, int64 uid); void UpdateCharacterItemDB(int32 cid, int64 uid, int16 count); void DeletePlayerFromDB(int32 cid); };