/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program 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 2 of the License, or (at your * option) any later version. * * This program 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 this program. If not, see . */ #ifndef _AUCTION_HOUSE_MGR_H #define _AUCTION_HOUSE_MGR_H #include "Define.h" #include "DatabaseEnvFwd.h" #include "Duration.h" #include "EnumFlag.h" #include "ItemTemplate.h" #include "ObjectGuid.h" #include "Optional.h" #include #include #include class Item; class Player; class WorldPacket; namespace WorldPackets { namespace AuctionHouse { struct AuctionBucketKey; struct AuctionItem; struct AuctionSortDef; struct BucketInfo; class AuctionListBucketsResult; class AuctionListBiddedItemsResult; class AuctionListOwnedItemsResult; class AuctionListItemsResult; class AuctionReplicateResponse; } } uint32 constexpr MIN_AUCTION_TIME = 12 * HOUR; enum class AuctionResult : int8 { Ok = 0, Inventory = 1, DatabaseError = 2, NotEnoughMoney = 3, ItemNotFound = 4, HigherBid = 5, BidIncrement = 7, BidOwn = 10, RestrictedAccountTrial = 13, HasRestriction = 17, AuctionHouseBusy = 18, AuctionHouseUnavailable = 19, CommodityPurchaseFailed = 21, ItemHasQuote = 23 }; enum class AuctionCommand : int8 { SellItem = 0, Cancel = 1, PlaceBid = 2 }; enum class AuctionMailType : int32 { Outbid = 0, Won = 1, Sold = 2, Expired = 3, Removed = 4, // for bidder Cancelled = 5, // for seller Invoice = 6 }; enum class AuctionHouseResultLimits : std::size_t { Browse = 500, Items = 50 }; constexpr std::size_t MAX_FAVORITE_AUCTIONS = 100; enum class AuctionHouseFilterMask : uint32 { None = 0x0, UncollectedOnly = 0x1, UsableOnly = 0x2, UpgradesOnly = 0x4, ExactMatch = 0x8, PoorQuality = 0x10, CommonQuality = 0x20, UncommonQuality = 0x40, RareQuality = 0x80, EpicQuality = 0x100, LegendaryQuality = 0x200, ArtifactQuality = 0x400, }; DEFINE_ENUM_FLAG(AuctionHouseFilterMask); enum class AuctionHouseSortOrder : uint8 { Price = 0, Name = 1, Level = 2, Bid = 3, Buyout = 4 }; enum class AuctionHouseBrowseMode : uint8 { Search = 0, SpecificKeys = 1 }; enum class AuctionHouseListType : uint8 { Commodities = 1, Items = 2 }; struct AuctionSearchClassFilters { enum FilterType : uint32 { FILTER_SKIP_CLASS = 0, FILTER_SKIP_SUBCLASS = 0xFFFFFFFF, FILTER_SKIP_INVTYPE = 0xFFFFFFFF }; struct SubclassFilter { uint32 SubclassMask = FILTER_SKIP_CLASS; std::array InvTypes = { }; }; std::array Classes = { }; }; struct AuctionsBucketKey { AuctionsBucketKey() = default; AuctionsBucketKey(uint32 itemId, uint16 itemLevel, uint16 battlePetSpeciesId, uint16 suffixItemNameDescriptionId) : ItemId(itemId), ItemLevel(itemLevel), BattlePetSpeciesId(battlePetSpeciesId), SuffixItemNameDescriptionId(suffixItemNameDescriptionId) { } AuctionsBucketKey(WorldPackets::AuctionHouse::AuctionBucketKey const& key); uint32 ItemId; uint16 ItemLevel; uint16 BattlePetSpeciesId; uint16 SuffixItemNameDescriptionId; bool operator==(AuctionsBucketKey const& right) const { return ItemId == right.ItemId && ItemLevel == right.ItemLevel && BattlePetSpeciesId == right.BattlePetSpeciesId && SuffixItemNameDescriptionId == right.SuffixItemNameDescriptionId; } bool operator!=(AuctionsBucketKey const& right) const { return !(*this == right); } static std::size_t Hash(AuctionsBucketKey const& bucket); static AuctionsBucketKey ForItem(Item* item); static AuctionsBucketKey ForCommodity(ItemTemplate const* itemTemplate); }; bool operator<(AuctionsBucketKey const& left, AuctionsBucketKey const& right); namespace std { template<> struct hash { size_t operator()(AuctionsBucketKey const& key) const { return AuctionsBucketKey::Hash(key); } }; } struct AuctionPosting; struct AuctionsBucketData { AuctionsBucketKey Key; // filter helpers uint8 ItemClass = 0; uint8 ItemSubClass = 0; uint8 InventoryType = 0; AuctionHouseFilterMask QualityMask = AuctionHouseFilterMask::None; std::array QualityCounts = { }; uint64 MinPrice = 0; // for sort std::array, 4> ItemModifiedAppearanceId = { }; // for uncollected search uint8 RequiredLevel = 0; // for usable search uint8 SortLevel = 0; uint8 MinBattlePetLevel = 0; uint8 MaxBattlePetLevel = 0; std::array FullName = { }; std::vector Auctions; void BuildBucketInfo(WorldPackets::AuctionHouse::BucketInfo* bucketInfo, Player* player) const; class Sorter; }; // This structure represents the result of a single C_AuctionHouse.PostItem/PostCommodity call struct AuctionPosting { uint32 Id = 0; AuctionsBucketData* Bucket = nullptr; std::vector Items; ObjectGuid Owner; ObjectGuid OwnerAccount; ObjectGuid Bidder; uint64 MinBid = 0; uint64 BuyoutOrUnitPrice = 0; uint64 Deposit = 0; uint64 BidAmount = 0; std::chrono::system_clock::time_point StartTime = std::chrono::system_clock::time_point::min(); std::chrono::system_clock::time_point EndTime = std::chrono::system_clock::time_point::min(); GuidUnorderedSet BidderHistory; bool IsCommodity() const; uint32 GetTotalItemCount() const; void BuildAuctionItem(WorldPackets::AuctionHouse::AuctionItem* auctionItem, bool alwaysSendItem, bool sendKey, bool censorServerInfo, bool censorBidInfo) const; static uint64 CalculateMinIncrement(uint64 currentBid); uint64 CalculateMinIncrement() const { return CalculateMinIncrement(BidAmount); } class Sorter; }; struct CommodityQuote { uint64 TotalPrice = 0; uint32 Quantity = 0; std::chrono::steady_clock::time_point ValidTo = std::chrono::steady_clock::time_point::min(); }; struct AuctionThrottleResult { Milliseconds DelayUntilNext; bool Throttled; }; //this class is used as auctionhouse instance class TC_GAME_API AuctionHouseObject { public: explicit AuctionHouseObject(uint32 auctionHouseId); ~AuctionHouseObject(); struct PlayerReplicateThrottleData { uint32 Global = 0; uint32 Cursor = 0; uint32 Tombstone = 0; std::chrono::steady_clock::time_point NextAllowedReplication = std::chrono::steady_clock::time_point::min(); bool IsReplicationInProgress() const { return Cursor != Tombstone && Global != 0; } }; uint32 GetAuctionHouseId() const; std::map::iterator GetAuctionsBegin() { return _itemsByAuctionId.begin(); } std::map::iterator GetAuctionsEnd() { return _itemsByAuctionId.end(); } AuctionPosting* GetAuction(uint32 auctionId); void AddAuction(CharacterDatabaseTransaction trans, AuctionPosting auction); void RemoveAuction(CharacterDatabaseTransaction trans, AuctionPosting* auction, std::map::iterator* auctionItr = nullptr); void Update(); void BuildListBuckets(WorldPackets::AuctionHouse::AuctionListBucketsResult& listBucketsResult, Player* player, std::wstring const& name, uint8 minLevel, uint8 maxLevel, EnumFlag filters, Optional const& classFilters, uint8 const* knownPetBits, std::size_t knownPetBitsCount, uint8 maxKnownPetLevel, uint32 offset, WorldPackets::AuctionHouse::AuctionSortDef const* sorts, std::size_t sortCount); void BuildListBuckets(WorldPackets::AuctionHouse::AuctionListBucketsResult& listBucketsResult, Player* player, WorldPackets::AuctionHouse::AuctionBucketKey const* keys, std::size_t keysCount, WorldPackets::AuctionHouse::AuctionSortDef const* sorts, std::size_t sortCount); void BuildListBiddedItems(WorldPackets::AuctionHouse::AuctionListBiddedItemsResult& listBiddedItemsResult, Player* player, uint32 offset, WorldPackets::AuctionHouse::AuctionSortDef const* sorts, std::size_t sortCount) const; void BuildListAuctionItems(WorldPackets::AuctionHouse::AuctionListItemsResult& listItemsResult, Player* player, AuctionsBucketKey const& bucketKey, uint32 offset, WorldPackets::AuctionHouse::AuctionSortDef const* sorts, std::size_t sortCount) const; void BuildListAuctionItems(WorldPackets::AuctionHouse::AuctionListItemsResult& listItemsResult, Player* player, uint32 itemId, uint32 offset, WorldPackets::AuctionHouse::AuctionSortDef const* sorts, std::size_t sortCount) const; void BuildListOwnedItems(WorldPackets::AuctionHouse::AuctionListOwnedItemsResult& listOwnedItemsResult, Player* player, uint32 offset, WorldPackets::AuctionHouse::AuctionSortDef const* sorts, std::size_t sortCount); void BuildReplicate(WorldPackets::AuctionHouse::AuctionReplicateResponse& replicateResponse, Player* player, uint32 global, uint32 cursor, uint32 tombstone, uint32 count); uint64 CalculateAuctionHouseCut(uint64 bidAmount) const; CommodityQuote const* CreateCommodityQuote(Player* player, uint32 itemId, uint32 quantity); void CancelCommodityQuote(ObjectGuid guid); bool BuyCommodity(CharacterDatabaseTransaction trans, Player* player, uint32 itemId, uint32 quantity, Milliseconds delayForNextAction); void SendAuctionOutbid(AuctionPosting const* auction, ObjectGuid newBidder, uint64 newBidAmount, CharacterDatabaseTransaction trans); void SendAuctionWon(AuctionPosting const* auction, Player* player, CharacterDatabaseTransaction trans); void SendAuctionSold(AuctionPosting const* auction, Player* owner, CharacterDatabaseTransaction trans); void SendAuctionExpired(AuctionPosting const* auction, CharacterDatabaseTransaction trans); void SendAuctionRemoved(AuctionPosting const* auction, Player* owner, CharacterDatabaseTransaction trans); void SendAuctionCancelledToBidder(AuctionPosting const* auction, CharacterDatabaseTransaction trans); void SendAuctionInvoice(AuctionPosting const* auction, Player* owner, CharacterDatabaseTransaction trans); private: AuctionHouseEntry const* _auctionHouse; std::map _itemsByAuctionId; // ordered for replicate std::unordered_map _soldItemsById; std::map _buckets; // ordered for search by itemid only std::unordered_map _commodityQuotes; std::unordered_multimap _playerOwnedAuctions; std::unordered_multimap _playerBidderAuctions; // Map of throttled players for GetAll, and throttle expiry time // Stored here, rather than player object to maintain persistence after logout std::unordered_map _replicateThrottleMap; }; class TC_GAME_API AuctionHouseMgr { private: AuctionHouseMgr(); ~AuctionHouseMgr(); public: static AuctionHouseMgr* instance(); AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId); AuctionHouseObject* GetAuctionsById(uint32 auctionHouseId); Item* GetAItem(ObjectGuid itemGuid); static std::string BuildItemAuctionMailSubject(AuctionMailType type, AuctionPosting const* auction); static std::string BuildCommodityAuctionMailSubject(AuctionMailType type, uint32 itemId, uint32 itemCount); static std::string BuildAuctionMailSubject(uint32 itemId, AuctionMailType type, uint32 auctionId, uint32 itemCount, uint32 battlePetSpeciesId, ItemContext context, std::vector const& bonusListIds); static std::string BuildAuctionWonMailBody(ObjectGuid guid, uint64 bid, uint64 buyout); static std::string BuildAuctionSoldMailBody(ObjectGuid guid, uint64 bid, uint64 buyout, uint32 deposit, uint64 consignment); static std::string BuildAuctionInvoiceMailBody(ObjectGuid guid, uint64 bid, uint64 buyout, uint32 deposit, uint64 consignment, uint32 moneyDelay, uint32 eta); static uint64 GetCommodityAuctionDeposit(ItemTemplate const* item, Minutes time, uint32 quantity); static uint64 GetItemAuctionDeposit(Player* player, Item* item, Minutes time); static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId, uint32* houseId); public: void LoadAuctions(); void AddAItem(Item* item); bool RemoveAItem(ObjectGuid itemGuid, bool deleteItem = false, CharacterDatabaseTransaction* trans = nullptr); bool PendingAuctionAdd(Player* player, uint32 auctionHouseId, uint32 auctionId, uint64 deposit); std::size_t PendingAuctionCount(Player const* player) const; void PendingAuctionProcess(Player* player); void UpdatePendingAuctions(); void Update(); uint32 GenerateReplicationId(); AuctionThrottleResult CheckThrottle(Player* player, bool addonTainted, AuctionCommand command = AuctionCommand::SellItem); private: AuctionHouseObject mHordeAuctions; AuctionHouseObject mAllianceAuctions; AuctionHouseObject mNeutralAuctions; AuctionHouseObject mGoblinAuctions; struct PendingAuctionInfo { uint32 AuctionId = 0; uint32 AuctionHouseId = 0; uint64 Deposit = 0; }; struct PlayerPendingAuctions { std::vector Auctions; std::size_t LastAuctionsSize = 0; }; struct PlayerThrottleObject { std::chrono::steady_clock::time_point PeriodEnd; uint8 QueriesRemaining = 100; }; std::unordered_map _pendingAuctionsByPlayer; std::unordered_map _itemsByGuid; uint32 _replicateIdGenerator; std::unordered_map _playerThrottleObjects; std::chrono::steady_clock::time_point _playerThrottleObjectsCleanupTime; }; #define sAuctionMgr AuctionHouseMgr::instance() #endif