diff options
author | Gigi1237 <luigi.sciolla@gmail.com> | 2015-08-22 20:29:17 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-04-11 18:22:09 +0200 |
commit | c19172cad9a3302b7f5c8d5ca1d96de9a7311847 (patch) | |
tree | 674d46c12b63b15a726909093f082aaa58128f7b /src | |
parent | 116c8070769b32b67400b4506a2e5b999764185a (diff) |
Core/Blackmarket: Implemented black market
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.cpp | 6 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.h | 5 | ||||
-rw-r--r-- | src/server/game/BlackMarket/BlackMarketMgr.cpp | 492 | ||||
-rw-r--r-- | src/server/game/BlackMarket/BlackMarketMgr.h | 158 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 7 | ||||
-rw-r--r-- | src/server/game/Handlers/BlackMarketHandler.cpp | 120 | ||||
-rw-r--r-- | src/server/game/Mails/Mail.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Mails/Mail.h | 5 | ||||
-rw-r--r-- | src/server/game/Server/Packets/AllPackets.h | 1 | ||||
-rw-r--r-- | src/server/game/Server/Packets/BlackMarketPackets.cpp | 114 | ||||
-rw-r--r-- | src/server/game/Server/Packets/BlackMarketPackets.h | 93 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 12 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.h | 12 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 39 | ||||
-rw-r--r-- | src/server/game/World/World.h | 5 | ||||
-rw-r--r-- | src/server/worldserver/worldserver.conf.dist | 32 |
16 files changed, 1088 insertions, 17 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index c519dfc02b7..a6eda970402 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -689,4 +689,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_CHARACTER_GARRISON_FOLLOWERS, "DELETE gfab, gf FROM character_garrison_follower_abilities gfab INNER JOIN character_garrison_followers gf ON gfab.dbId = gf.dbId WHERE gf.guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES, "SELECT gfab.dbId, gfab.abilityId FROM character_garrison_follower_abilities gfab INNER JOIN character_garrison_followers gf ON gfab.dbId = gf.dbId WHERE guid = ? ORDER BY gfab.slot", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHARACTER_GARRISON_FOLLOWER_ABILITIES, "INSERT INTO character_garrison_follower_abilities (dbId, abilityId, slot) VALUES (?, ?, ?)", CONNECTION_ASYNC); + + // Black Market + PrepareStatement(CHAR_SEL_BLACKMARKET_AUCTIONS, "SELECT marketId, currentBid, time, numBids, bidder FROM blackmarket_auctions", CONNECTION_SYNCH); + PrepareStatement(CHAR_DEL_BLACKMARKET_AUCTIONS, "DELETE FROM blackmarket_auctions WHERE marketId = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_BLACKMARKET_AUCTIONS, "UPDATE blackmarket_auctions SET currentBid = ?, time = ?, numBids = ?, bidder = ? WHERE marketId = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_BLACKMARKET_AUCTIONS, "INSERT INTO blackmarket_auctions (marketId, currentBid, time, numBids, bidder) VALUES (?, ?, ?, ? ,?)", CONNECTION_ASYNC); } diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index f3cb4a7c0c1..18faea4c5b0 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -594,6 +594,11 @@ enum CharacterDatabaseStatements CHAR_SEL_CHARACTER_GARRISON_FOLLOWER_ABILITIES, CHAR_INS_CHARACTER_GARRISON_FOLLOWER_ABILITIES, + CHAR_SEL_BLACKMARKET_AUCTIONS, + CHAR_DEL_BLACKMARKET_AUCTIONS, + CHAR_UPD_BLACKMARKET_AUCTIONS, + CHAR_INS_BLACKMARKET_AUCTIONS, + MAX_CHARACTERDATABASE_STATEMENTS }; diff --git a/src/server/game/BlackMarket/BlackMarketMgr.cpp b/src/server/game/BlackMarket/BlackMarketMgr.cpp new file mode 100644 index 00000000000..0303f089742 --- /dev/null +++ b/src/server/game/BlackMarket/BlackMarketMgr.cpp @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "BlackMarketMgr.h" +#include "BlackMarketPackets.h" +#include "Containers.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Language.h" + +BlackMarketMgr::BlackMarketMgr() +{ +} + +BlackMarketMgr::~BlackMarketMgr() +{ + for (auto itr = _auctions.begin(); itr != _auctions.end(); ++itr) + delete itr->second; + + for (auto itr = _templates.begin(); itr != _templates.end(); ++itr) + delete itr->second; +} + +BlackMarketMgr* BlackMarketMgr::Instance() +{ + static BlackMarketMgr instance; + return &instance; +} + +void BlackMarketMgr::LoadTemplates() +{ + uint32 oldMSTime = getMSTime(); + + // Clear in case we are reloading + if (!_templates.empty()) + { + for (BlackMarketTemplateMap::iterator itr = _templates.begin(); itr != _templates.end(); ++itr) + delete itr->second; + + _templates.clear(); + } + + QueryResult result = WorldDatabase.Query("SELECT marketId, sellerNpc, itemEntry, quantity, minBid, duration, chance, bonusListIDs FROM blackmarket_template"); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 black market templates. DB table `blackmarket_template` is empty."); + return; + } + + do + { + Field* fields = result->Fetch(); + BlackMarketTemplate* templ = new BlackMarketTemplate(); + + if (!templ->LoadFromDB(fields)) // Add checks + { + delete templ; + continue; + } + + AddTemplate(templ); + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u black market templates in %u ms.", uint32(_templates.size()), GetMSTimeDiffToNow(oldMSTime)); +} + +void BlackMarketMgr::LoadAuctions() +{ + uint32 oldMSTime = getMSTime(); + + // Clear in case we are reloading + if (!_auctions.empty()) + { + for (BlackMarketEntryMap::iterator itr = _auctions.begin(); itr != _auctions.end(); ++itr) + delete itr->second; + + _auctions.clear(); + } + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BLACKMARKET_AUCTIONS); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 black market auctions. DB table `blackmarket_auctions` is empty."); + return; + } + + uint32 count = 0; + _lastUpdate = time(nullptr); //Set update time before loading + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + do + { + Field* fields = result->Fetch(); + BlackMarketEntry* auction = new BlackMarketEntry(); + + if (!auction->LoadFromDB(fields)) + { + auction->DeleteFromDB(trans); + delete auction; + continue; + } + + if (auction->IsCompleted()) + { + auction->DeleteFromDB(trans); + delete auction; + continue; + } + + AddAuction(auction); + } while (result->NextRow()); + + CharacterDatabase.CommitTransaction(trans); + + TC_LOG_INFO("server.loading", ">> Loaded %u black market auctions in %u ms.", uint32(_auctions.size()), GetMSTimeDiffToNow(oldMSTime)); +} + +void BlackMarketMgr::Update(bool updateTime) +{ + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + time_t now = time(nullptr); + for (BlackMarketEntryMap::iterator itr = _auctions.begin(); itr != _auctions.end(); ++itr) + { + BlackMarketEntry* entry = itr->second; + + if (entry->IsCompleted() && entry->GetBidder()) + SendAuctionWonMail(entry, trans); + + if (updateTime) + entry->Update(now); + } + + if (updateTime) + _lastUpdate = now; + + CharacterDatabase.CommitTransaction(trans); +} + +void BlackMarketMgr::RefreshAuctions() +{ + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + // Delete completed auctions + for (BlackMarketEntryMap::iterator itr = _auctions.begin(); itr != _auctions.end();) + { + BlackMarketEntry* entry = itr->second; + if (!entry->IsCompleted()) + { + ++itr; + continue; + } + + entry->DeleteFromDB(trans); + itr = _auctions.erase(itr); + delete entry; + } + + CharacterDatabase.CommitTransaction(trans); + trans = CharacterDatabase.BeginTransaction(); + + std::list<BlackMarketTemplate const*> templates; + for (auto const& pair : _templates) + { + if (GetAuctionByID(pair.second->MarketID)) + continue; + if (!roll_chance_f(pair.second->Chance)) + continue; + + templates.push_back(pair.second); + } + + Trinity::Containers::RandomResizeList(templates, sWorld->getIntConfig(CONFIG_BLACKMARKET_MAXAUCTIONS)); + + for (BlackMarketTemplate const* templat : templates) + { + BlackMarketEntry* entry = new BlackMarketEntry(); + entry->Initialize(templat->MarketID, templat->Duration); + entry->SaveToDB(trans); + AddAuction(entry); + } + + CharacterDatabase.CommitTransaction(trans); + + Update(true); +} + +bool BlackMarketMgr::IsEnabled() const +{ + return sWorld->getBoolConfig(CONFIG_BLACKMARKET_ENABLED); +} + +void BlackMarketMgr::BuildItemsResponse(WorldPackets::BlackMarket::BlackMarketRequestItemsResult& packet, Player* player) +{ + packet.LastUpdateID = _lastUpdate; + packet.Items.reserve(_auctions.size()); + for (auto itr = _auctions.begin(); itr != _auctions.end(); ++itr) + { + WorldPackets::BlackMarket::BlackMarketItem item; + item.Initialize(itr->second, player); + + packet.Items.push_back(item); + } +} + +void BlackMarketMgr::AddAuction(BlackMarketEntry* auction) +{ + _auctions[auction->GetMarketId()] = auction; +} + +void BlackMarketMgr::AddTemplate(BlackMarketTemplate* templ) +{ + _templates[templ->MarketID] = templ; +} + +void BlackMarketMgr::SendAuctionWonMail(BlackMarketEntry* entry, SQLTransaction& trans) +{ + // Mail already sent + if (entry->GetMailSent()) + return; + + uint32 bidderAccId = 0; + ObjectGuid bidderGuid = ObjectGuid::Create<HighGuid::Player>(entry->GetBidder()); + Player* bidder = ObjectAccessor::FindConnectedPlayer(bidderGuid); + // data for gm.log + std::string bidderName; + bool logGmTrade = false; + + if (bidder) + { + bidderAccId = bidder->GetSession()->GetAccountId(); + bidderName = bidder->GetName(); + logGmTrade = bidder->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE); + } + else + { + bidderAccId = ObjectMgr::GetPlayerAccountIdByGUID(bidderGuid); + if (!bidderAccId) // Account exists + return; + + logGmTrade = AccountMgr::HasPermission(bidderAccId, rbac::RBAC_PERM_LOG_GM_TRADE, realm.Id.Realm); + + if (logGmTrade && !ObjectMgr::GetPlayerNameByGUID(bidderGuid, bidderName)) + bidderName = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN); + } + + // Create item + BlackMarketTemplate const* templ = entry->GetTemplate(); + Item* item = Item::CreateItem(templ->Item.ItemID, templ->Quantity); + if (!item) + return; + + if (templ->Item.ItemBonus) + for (int32 bonusList : templ->Item.ItemBonus->BonusListIDs) + item->AddBonuses(bonusList); + + item->SetOwnerGUID(bidderGuid); + + item->SaveToDB(trans); + + // Log trade + if (logGmTrade) + sLog->outCommand(bidderAccId, "GM %s (Account: %u) won item in blackmarket auction: %s (Entry: %u Count: %u) and payed gold : %u.", + bidderName.c_str(), bidderAccId, item->GetTemplate()->GetDefaultLocaleName(), item->GetEntry(), item->GetCount(), entry->GetCurrentBid() / GOLD); + + if (bidder) + bidder->GetSession()->SendBlackMarketWonNotification(entry, item); + + MailDraft(entry->BuildAuctionMailSubject(BMAH_AUCTION_WON), entry->BuildAuctionMailBody()) + .AddItem(item) + .SendMailTo(trans, MailReceiver(bidder, entry->GetBidder()), entry, MAIL_CHECK_MASK_COPIED); + + entry->MailSent(); +} + +void BlackMarketMgr::SendAuctionOutbidMail(BlackMarketEntry* entry, SQLTransaction& trans) +{ + ObjectGuid oldBidder_guid = ObjectGuid::Create<HighGuid::Player>(entry->GetBidder()); + Player* oldBidder = ObjectAccessor::FindConnectedPlayer(oldBidder_guid); + + uint32 oldBidder_accId = 0; + if (!oldBidder) + oldBidder_accId = ObjectMgr::GetPlayerAccountIdByGUID(oldBidder_guid); + + // old bidder exist + if (!oldBidder && !oldBidder_accId) + return; + + if (oldBidder) + oldBidder->GetSession()->SendBlackMarketOutbidNotification(entry->GetTemplate()); + + MailDraft(entry->BuildAuctionMailSubject(BMAH_AUCTION_OUTBID), entry->BuildAuctionMailBody()) + .AddMoney(entry->GetCurrentBid()) + .SendMailTo(trans, MailReceiver(oldBidder, entry->GetBidder()), entry, MAIL_CHECK_MASK_COPIED); +} + +BlackMarketEntry* BlackMarketMgr::GetAuctionByID(int32 marketId) const +{ + BlackMarketEntryMap::const_iterator itr = _auctions.find(marketId); + if (itr != _auctions.end()) + return itr->second; + + return nullptr; +} + +BlackMarketTemplate const* BlackMarketMgr::GetTemplateByID(int32 marketId) const +{ + BlackMarketTemplateMap::const_iterator itr = _templates.find(marketId); + if (itr != _templates.end()) + return itr->second; + + return nullptr; +} + +bool BlackMarketTemplate::LoadFromDB(Field* fields) +{ + MarketID = fields[0].GetInt32(); + SellerNPC = fields[1].GetInt32(); + Item.ItemID = fields[2].GetUInt32(); + Quantity = fields[3].GetInt32(); + MinBid = fields[4].GetUInt64(); + Duration = static_cast<time_t>(fields[5].GetUInt32()); + Chance = fields[6].GetFloat(); + + Tokenizer bonusListIDsTok(fields[7].GetString(), ' '); + std::vector<int32> bonusListIDs; + for (char const* token : bonusListIDsTok) + bonusListIDs.push_back(int32(atol(token))); + + if (!bonusListIDs.empty()) + { + Item.ItemBonus = boost::in_place(); + Item.ItemBonus->BonusListIDs = bonusListIDs; + } + + if (!sObjectMgr->GetCreatureTemplate(SellerNPC)) + { + TC_LOG_ERROR("misc", "Black market template %i does not have a valid seller. (Entry: %u)", MarketID, SellerNPC); + return false; + } + + if (!sObjectMgr->GetItemTemplate(Item.ItemID)) + { + TC_LOG_ERROR("misc", "Black market template %i does not have a valid item. (Entry: %u)", MarketID, Item.ItemID); + return false; + } + + return true; +} + +void BlackMarketEntry::Update(time_t newTimeOfUpdate) +{ + _secondsRemaining = _secondsRemaining - (newTimeOfUpdate - sBlackMarketMgr->GetLastUpdate()); +} + +BlackMarketTemplate const* BlackMarketEntry::GetTemplate() const +{ + return sBlackMarketMgr->GetTemplateByID(_marketId); +} + +uint32 BlackMarketEntry::GetSecondsRemaining() const +{ + return _secondsRemaining - (time(nullptr) - sBlackMarketMgr->GetLastUpdate()); +} + +time_t BlackMarketEntry::GetExpirationTime() const +{ + return time(nullptr) + GetSecondsRemaining(); +} + +bool BlackMarketEntry::IsCompleted() const +{ + return GetSecondsRemaining() <= 0; +} + +bool BlackMarketEntry::LoadFromDB(Field* fields) +{ + _marketId = fields[0].GetInt32(); + + // Invalid MarketID + BlackMarketTemplate const* templ = sBlackMarketMgr->GetTemplateByID(_marketId); + if (!templ) + { + TC_LOG_ERROR("misc", "Black market auction %i does not have a valid id.", _marketId); + return false; + } + + _currentBid = fields[1].GetUInt64(); + _secondsRemaining = static_cast<time_t>(fields[2].GetInt32()) - sBlackMarketMgr->GetLastUpdate(); + _numBids = fields[3].GetInt32(); + _bidder = fields[4].GetUInt64(); + + // Either no bidder or existing player + if (_bidder && !sObjectMgr->GetPlayerAccountIdByGUID(ObjectGuid::Create<HighGuid::Player>(_bidder))) // Probably a better way to check if player exists + { + TC_LOG_ERROR("misc", "Black market auction %i does not have a valid bidder (GUID: " UI64FMTD " ).", _marketId, _bidder); + return false; + } + + return true; +} + +void BlackMarketEntry::SaveToDB(SQLTransaction& trans) const +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BLACKMARKET_AUCTIONS); + + stmt->setInt32(0, _marketId); + stmt->setUInt64(1, _currentBid); + stmt->setInt32(2, GetExpirationTime()); + stmt->setInt32(3, _numBids); + stmt->setUInt64(4, _bidder); + + trans->Append(stmt); +} + +void BlackMarketEntry::DeleteFromDB(SQLTransaction& trans) const +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_BLACKMARKET_AUCTIONS); + stmt->setInt32(0, _marketId); + trans->Append(stmt); +} + +bool BlackMarketEntry::ValidateBid(uint64 bid) const +{ + if (bid <= _currentBid) + return false; + + if (bid < _currentBid + GetMinIncrement()) + return false; + + if (bid >= BMAH_MAX_BID) + return false; + + return true; +} + +void BlackMarketEntry::PlaceBid(uint64 bid, Player* player, SQLTransaction& trans) //Updated +{ + if (bid < _currentBid) + return; + + _currentBid = bid; + ++_numBids; + + if (GetSecondsRemaining() < 30 * MINUTE) + _secondsRemaining += 30 * MINUTE; + + _bidder = player->GetGUID().GetCounter(); + + player->ModifyMoney(-static_cast<int64>(bid)); + + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_BLACKMARKET_AUCTIONS); + + stmt->setUInt64(0, _currentBid); + stmt->setInt32(1, GetExpirationTime()); + stmt->setInt32(2, _numBids); + stmt->setUInt64(3, _bidder); + stmt->setInt32(4, _marketId); + + trans->Append(stmt); + + sBlackMarketMgr->Update(true); +} + +std::string BlackMarketEntry::BuildAuctionMailSubject(BMAHMailAuctionAnswers response) const +{ + std::ostringstream strm; + strm << GetTemplate()->Item.ItemID << ":0:" << response << ':' << GetMarketId() << ':' << GetTemplate()->Quantity; + return strm.str(); +} + +std::string BlackMarketEntry::BuildAuctionMailBody() +{ + std::ostringstream strm; + strm << GetTemplate()->SellerNPC << ':' << _currentBid; + + return strm.str(); +} diff --git a/src/server/game/BlackMarket/BlackMarketMgr.h b/src/server/game/BlackMarket/BlackMarketMgr.h new file mode 100644 index 00000000000..b386991e2af --- /dev/null +++ b/src/server/game/BlackMarket/BlackMarketMgr.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef BLACK_MARKET_H +#define BLACK_MARKET_H + +#include "Common.h" +#include "ObjectGuid.h" +#include "ItemPackets.h" +#include "Item.h" + +namespace WorldPackets +{ + namespace BlackMarket + { + class BlackMarketRequestItemsResult; + } +} + +enum BlackMarketError : int32 // Extracted from client +{ + ERR_BMAH_OK = 0, + ERR_BMAH_ITEM_NOT_FOUND = 1, + ERR_BMAH_ALREADY_BID = 2, + ERR_BMAH_HIGHER_BID = 4, + ERR_BMAH_DATABASE_ERROR = 6, + ERR_BMAH_NOT_ENOUGH_MONEY = 7, + ERR_BMAH_RESTRICTED_ACCOUNT_TRIAL = 9 +}; + +enum BMAHMailAuctionAnswers +{ + BMAH_AUCTION_OUTBID = 0, + BMAH_AUCTION_WON = 1, +}; + +const static uint64 BMAH_MAX_BID = 1000000LL * GOLD; + +struct BlackMarketTemplate +{ + int32 MarketID = 0; + int32 SellerNPC = 0; + int32 Quantity = 0; + uint64 MinBid = UI64LIT(0); + time_t Duration = time_t(0); + float Chance = 0.0f; + WorldPackets::Item::ItemInstance Item; + + // Helpers + bool LoadFromDB(Field* fields); +}; + +class BlackMarketEntry +{ +public: + + void Update(time_t newTimeOfUpdate); + void Initialize(int32 marketId, uint32 duration) + { + _marketId = marketId; + _secondsRemaining = duration; + } + + BlackMarketTemplate const* GetTemplate() const; + int32 GetMarketId() const { return _marketId; } + + uint32 GetCurrentBid() const { return _currentBid; } + void SetCurrentBid(uint32 bid) { _currentBid = bid; } + + int32 GetNumBids() const { return _numBids; } + void SetNumBids(int32 numBids) { _numBids = numBids; } + + ObjectGuid::LowType GetBidder() const { return _bidder; } + void SetBidder(ObjectGuid::LowType bidder) { _bidder = bidder; } + + uint32 GetSecondsRemaining() const; // Get seconds remaining relative to now + time_t GetExpirationTime() const; + bool IsCompleted() const; + + void DeleteFromDB(SQLTransaction& trans) const; + void SaveToDB(SQLTransaction& trans) const; + bool LoadFromDB(Field* fields); + + uint64 GetMinIncrement() const { return (_currentBid / 20) - ((_currentBid / 20) % GOLD); } //5% increase every bid (has to be round gold value) + bool ValidateBid(uint64 bid) const; + void PlaceBid(uint64 bid, Player* player, SQLTransaction& trans); + + std::string BuildAuctionMailSubject(BMAHMailAuctionAnswers response) const; + std::string BuildAuctionMailBody(); + + void MailSent() { _mailSent = true; } // Set when mail has been sent + bool GetMailSent() const { return _mailSent; } + +private: + int32 _marketId = 0; + uint64 _currentBid = 0; + int32 _numBids = 0; + ObjectGuid::LowType _bidder = 0; + uint32 _secondsRemaining = 0; + bool _mailSent = false; +}; + +class TC_GAME_API BlackMarketMgr +{ + private: + BlackMarketMgr(); + ~BlackMarketMgr(); + + public: + static BlackMarketMgr* Instance(); + + typedef std::unordered_map<int32, BlackMarketEntry*> BlackMarketEntryMap; + typedef std::unordered_map<int32, BlackMarketTemplate const*> BlackMarketTemplateMap; + + // Load templates first + void LoadTemplates(); + void LoadAuctions(); + + void Update(bool updateTime = false); + void RefreshAuctions(); + time_t GetLastUpdate() const { return _lastUpdate; } + + bool IsEnabled() const; + + void BuildItemsResponse(WorldPackets::BlackMarket::BlackMarketRequestItemsResult& packet, Player* player); + + BlackMarketEntry* GetAuctionByID(int32 marketId) const; + BlackMarketTemplate const* GetTemplateByID(int32 marketId) const; + + void AddAuction(BlackMarketEntry* auction); + void AddTemplate(BlackMarketTemplate* templ); + + void SendAuctionWonMail(BlackMarketEntry* entry, SQLTransaction& trans); + void SendAuctionOutbidMail(BlackMarketEntry* entry, SQLTransaction& trans); // Call before incrementing bid + + private: + BlackMarketEntryMap _auctions; + BlackMarketTemplateMap _templates; + time_t _lastUpdate = time_t(0); +}; + +#define sBlackMarketMgr BlackMarketMgr::Instance() + +#endif diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 24fd9fefc35..9a2a702ff5b 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -765,9 +765,10 @@ enum NPCFlags : uint64 UNIT_NPC_FLAG_BLACK_MARKET = 0x0080000000, // black market UNIT_NPC_FLAG_ITEM_UPGRADE_MASTER = 0x0100000000, UNIT_NPC_FLAG_GARRISON_ARCHITECT = 0x0200000000, - UNIT_NPC_FLAG_SHIPMENT_CRAFTER = 0x2000000000, - UNIT_NPC_FLAG_GARRISON_MISSION_NPC = 0x4000000000, - UNIT_NPC_FLAG_TRADESKILL_NPC = 0x8000000000 + UNIT_NPC_FLAG_SHIPMENT_CRAFTER = 0x1000000000, + UNIT_NPC_FLAG_GARRISON_MISSION_NPC = 0x2000000000, + UNIT_NPC_FLAG_TRADESKILL_NPC = 0x4000000000, + UNIT_NPC_FLAG_BLACK_MARKET_VIEW = 0x8000000000 }; enum MovementFlags diff --git a/src/server/game/Handlers/BlackMarketHandler.cpp b/src/server/game/Handlers/BlackMarketHandler.cpp index 5eb077b8a69..9fae4cb53c9 100644 --- a/src/server/game/Handlers/BlackMarketHandler.cpp +++ b/src/server/game/Handlers/BlackMarketHandler.cpp @@ -20,13 +20,14 @@ #include "Player.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "BlackMarketMgr.h" -void WorldSession::HandleBlackMarketOpen(WorldPackets::BlackMarket::BlackMarketOpen& packet) +void WorldSession::HandleBlackMarketOpen(WorldPackets::BlackMarket::BlackMarketOpen& blackMarketOpen) { - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Guid, UNIT_NPC_FLAG_BLACK_MARKET); + Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(blackMarketOpen.Guid, UNIT_NPC_FLAG_BLACK_MARKET | UNIT_NPC_FLAG_BLACK_MARKET_VIEW); if (!unit) { - TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketHello - Unit (GUID: %s) not found or you can't interact with him.", packet.Guid.ToString().c_str()); + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketHello - Unit (GUID: %s) not found or you can't interact with him.", blackMarketOpen.Guid.ToString().c_str()); return; } @@ -34,13 +35,122 @@ void WorldSession::HandleBlackMarketOpen(WorldPackets::BlackMarket::BlackMarketO if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - SendBlackMarketOpenResult(packet.Guid, unit); + SendBlackMarketOpenResult(blackMarketOpen.Guid, unit); } void WorldSession::SendBlackMarketOpenResult(ObjectGuid guid, Creature* /*auctioneer*/) { WorldPackets::BlackMarket::BlackMarketOpenResult packet; packet.Guid = guid; - packet.Enable = false; + packet.Enable = sBlackMarketMgr->IsEnabled(); + SendPacket(packet.Write()); +} + +void WorldSession::HandleBlackMarketRequestItems(WorldPackets::BlackMarket::BlackMarketRequestItems& blackMarketRequestItems) +{ + if (!sBlackMarketMgr->IsEnabled()) + return; + + Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(blackMarketRequestItems.Guid, UNIT_NPC_FLAG_BLACK_MARKET | UNIT_NPC_FLAG_BLACK_MARKET_VIEW); + if (!unit) + { + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketRequestItems - Unit (GUID: %s) not found or you can't interact with him.", blackMarketRequestItems.Guid.ToString().c_str()); + return; + } + + WorldPackets::BlackMarket::BlackMarketRequestItemsResult result; + sBlackMarketMgr->BuildItemsResponse(result, GetPlayer()); + SendPacket(result.Write()); +} + +void WorldSession::HandleBlackMarketBidOnItem(WorldPackets::BlackMarket::BlackMarketBidOnItem& blackMarketBidOnItem) +{ + if (!sBlackMarketMgr->IsEnabled()) + return; + + Player* player = GetPlayer(); + Creature* unit = player->GetNPCIfCanInteractWith(blackMarketBidOnItem.Guid, UNIT_NPC_FLAG_BLACK_MARKET); + if (!unit) + { + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketBidOnItem - Unit (GUID: %s) not found or you can't interact with him.", blackMarketBidOnItem.Guid.ToString().c_str()); + return; + } + + BlackMarketEntry* entry = sBlackMarketMgr->GetAuctionByID(blackMarketBidOnItem.MarketID); + if (!entry) + { + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketBidOnItem - Player (%s, name: %s) tried to bid on a nonexistent auction (MarketId: %i).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), blackMarketBidOnItem.MarketID); + SendBlackMarketBidOnItemResult(ERR_BMAH_ITEM_NOT_FOUND, blackMarketBidOnItem.MarketID, blackMarketBidOnItem.Item); + return; + } + + if (entry->GetBidder() == player->GetGUID().GetCounter()) + { + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketBidOnItem - Player (%s, name: %s) tried to place a bid on an item he already bid on. (MarketId: %i).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), blackMarketBidOnItem.MarketID); + SendBlackMarketBidOnItemResult(ERR_BMAH_ALREADY_BID, blackMarketBidOnItem.MarketID, blackMarketBidOnItem.Item); + return; + } + + if (!entry->ValidateBid(blackMarketBidOnItem.BidAmount)) + { + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketBidOnItem - Player (%s, name: %s) tried to place an invalid bid. Amount: %lu (MarketId: %i).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), blackMarketBidOnItem.BidAmount, blackMarketBidOnItem.MarketID); + SendBlackMarketBidOnItemResult(ERR_BMAH_HIGHER_BID, blackMarketBidOnItem.MarketID, blackMarketBidOnItem.Item); + return; + } + + if (!player->HasEnoughMoney(blackMarketBidOnItem.BidAmount)) + { + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketBidOnItem - Player (%s, name: %s) does not have enough money to place bid. (MarketId: %i).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), blackMarketBidOnItem.MarketID); + SendBlackMarketBidOnItemResult(ERR_BMAH_NOT_ENOUGH_MONEY, blackMarketBidOnItem.MarketID, blackMarketBidOnItem.Item); + return; + } + + if (entry->GetSecondsRemaining() <= 0) + { + TC_LOG_DEBUG("network", "WORLD: HandleBlackMarketBidOnItem - Player (%s, name: %s) tried to bid on a completed auction. (MarketId: %i).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), blackMarketBidOnItem.MarketID); + SendBlackMarketBidOnItemResult(ERR_BMAH_DATABASE_ERROR, blackMarketBidOnItem.MarketID, blackMarketBidOnItem.Item); + return; + } + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + + sBlackMarketMgr->SendAuctionOutbidMail(entry, trans); + entry->PlaceBid(blackMarketBidOnItem.BidAmount, player, trans); + + CharacterDatabase.CommitTransaction(trans); + + SendBlackMarketBidOnItemResult(ERR_BMAH_OK, blackMarketBidOnItem.MarketID, blackMarketBidOnItem.Item); +} + +void WorldSession::SendBlackMarketBidOnItemResult(int32 result, int32 marketId, WorldPackets::Item::ItemInstance& item) +{ + WorldPackets::BlackMarket::BlackMarketBidOnItemResult packet; + + packet.MarketID = marketId; + packet.Item = item; + packet.Result = result; + + SendPacket(packet.Write()); +} + +void WorldSession::SendBlackMarketWonNotification(BlackMarketEntry const* entry, Item const* item) +{ + WorldPackets::BlackMarket::BlackMarketWon packet; + + packet.MarketID = entry->GetMarketId(); + packet.Item.Initialize(item); + packet.RandomPropertiesID = item->GetItemRandomPropertyId(); + + SendPacket(packet.Write()); +} + +void WorldSession::SendBlackMarketOutbidNotification(BlackMarketTemplate const* templ) +{ + WorldPackets::BlackMarket::BlackMarketOutbid packet; + + packet.MarketID = templ->MarketID; + packet.Item = templ->Item; + packet.RandomPropertiesID = templ->Item.RandomPropertiesID; + SendPacket(packet.Write()); } diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index 2eae707f9c7..f90b3f498ab 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -24,6 +24,7 @@ #include "Player.h" #include "Item.h" #include "AuctionHouseMgr.h" +#include "BlackMarketMgr.h" #include "CalendarMgr.h" MailSender::MailSender(Object* sender, MailStationery stationery) : m_stationery(stationery) @@ -62,6 +63,9 @@ MailSender::MailSender(CalendarEvent* sender) MailSender::MailSender(AuctionEntry* sender) : m_messageType(MAIL_AUCTION), m_senderId(uint64(sender->GetHouseId())), m_stationery(MAIL_STATIONERY_AUCTION) { } +MailSender::MailSender(BlackMarketEntry* sender) + : m_messageType(MAIL_BLACKMARKET), m_senderId(sender->GetTemplate()->SellerNPC), m_stationery(MAIL_STATIONERY_AUCTION) { } + MailSender::MailSender(Player* sender) { m_messageType = MAIL_NORMAL; diff --git a/src/server/game/Mails/Mail.h b/src/server/game/Mails/Mail.h index 0031d3f7c3e..9f03b35ae24 100644 --- a/src/server/game/Mails/Mail.h +++ b/src/server/game/Mails/Mail.h @@ -26,6 +26,7 @@ struct AuctionEntry; struct CalendarEvent; +class BlackMarketEntry; class Item; class Object; class Player; @@ -39,7 +40,8 @@ enum MailMessageType MAIL_AUCTION = 2, MAIL_CREATURE = 3, // client send CMSG_CREATURE_QUERY on this mailmessagetype MAIL_GAMEOBJECT = 4, // client send CMSG_GAMEOBJECT_QUERY on this mailmessagetype - MAIL_CALENDAR = 5 + MAIL_CALENDAR = 5, + MAIL_BLACKMARKET = 6 }; enum MailCheckMask @@ -90,6 +92,7 @@ class TC_GAME_API MailSender MailSender(Object* sender, MailStationery stationery = MAIL_STATIONERY_DEFAULT); MailSender(CalendarEvent* sender); MailSender(AuctionEntry* sender); + MailSender(BlackMarketEntry* sender); MailSender(Player* sender); public: // Accessors MailMessageType GetMailMessageType() const { return m_messageType; } diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 0d6d28446bc..2c2aa528bb1 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -22,6 +22,7 @@ #include "AuctionHousePackets.h" #include "AuthenticationPackets.h" #include "BankPackets.h" +#include "BlackMarketPackets.h" #include "BattlefieldPackets.h" #include "BattlegroundPackets.h" #include "BattlenetPackets.h" diff --git a/src/server/game/Server/Packets/BlackMarketPackets.cpp b/src/server/game/Server/Packets/BlackMarketPackets.cpp index d67f778a0b7..fb1098941b6 100644 --- a/src/server/game/Server/Packets/BlackMarketPackets.cpp +++ b/src/server/game/Server/Packets/BlackMarketPackets.cpp @@ -16,6 +16,35 @@ */ #include "BlackMarketPackets.h" +#include "BlackMarketMgr.h" +#include "Player.h" + +void WorldPackets::BlackMarket::BlackMarketItem::Initialize(BlackMarketEntry *const entry, Player* player) +{ + BlackMarketTemplate const* templ = entry->GetTemplate(); + + MarketID = entry->GetMarketId(); + SellerNPC = templ->SellerNPC; + Item = templ->Item; + Quantity = templ->Quantity; + + // No bids yet + if (!entry->GetNumBids()) + { + MinBid = templ->MinBid; + MinIncrement = 1; + } + else + { + MinIncrement = entry->GetMinIncrement(); // 5% increment minimum + MinBid = entry->GetCurrentBid() + MinIncrement; + } + + CurrentBid = entry->GetCurrentBid(); + SecondsRemaining = entry->GetSecondsRemaining(); + HighBid = (entry->GetBidder() == player->GetGUID().GetCounter()); + NumBids = entry->GetNumBids(); +} void WorldPackets::BlackMarket::BlackMarketOpen::Read() { @@ -30,3 +59,88 @@ WorldPacket const* WorldPackets::BlackMarket::BlackMarketOpenResult::Write() return &_worldPacket; } + +void WorldPackets::BlackMarket::BlackMarketRequestItems::Read() +{ + _worldPacket >> Guid; + _worldPacket >> uint32(LastUpdateID); +} + +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::BlackMarket::BlackMarketItem const& blackMarketItem) +{ + data << int32(blackMarketItem.MarketID); + data << int32(blackMarketItem.SellerNPC); + data << blackMarketItem.Item; + data << int32(blackMarketItem.Quantity); + data << uint64(blackMarketItem.MinBid); + data << uint64(blackMarketItem.MinIncrement); + data << uint64(blackMarketItem.CurrentBid); + data << int32(blackMarketItem.SecondsRemaining); + data << int32(blackMarketItem.NumBids); + data.WriteBit(blackMarketItem.HighBid); + data.FlushBits(); + + return data; +} + +ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::BlackMarket::BlackMarketItem& blackMarketItem) +{ + data >> blackMarketItem.MarketID; + data >> blackMarketItem.SellerNPC; + data >> blackMarketItem.Item; + data >> blackMarketItem.Quantity; + data >> blackMarketItem.MinBid; + data >> blackMarketItem.MinIncrement; + data >> blackMarketItem.CurrentBid; + data >> blackMarketItem.SecondsRemaining; + data >> blackMarketItem.NumBids; + blackMarketItem.HighBid = data.ReadBit(); + + return data; +} + +WorldPacket const* WorldPackets::BlackMarket::BlackMarketRequestItemsResult::Write() +{ + _worldPacket << uint32(LastUpdateID); + _worldPacket << uint32(Items.size()); + + for (BlackMarketItem const& item : Items) + _worldPacket << item; + + return &_worldPacket; +} + +void WorldPackets::BlackMarket::BlackMarketBidOnItem::Read() +{ + _worldPacket >> Guid; + _worldPacket >> MarketID; + _worldPacket >> Item; + _worldPacket >> BidAmount; +} + +WorldPacket const* WorldPackets::BlackMarket::BlackMarketBidOnItemResult::Write() +{ + _worldPacket << int32(MarketID); + _worldPacket << Item; + _worldPacket << int32(Result); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::BlackMarket::BlackMarketOutbid::Write() +{ + _worldPacket << int32(MarketID); + _worldPacket << Item; + _worldPacket << int32(RandomPropertiesID); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::BlackMarket::BlackMarketWon::Write() +{ + _worldPacket << int32(MarketID); + _worldPacket << Item; + _worldPacket << int32(RandomPropertiesID); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/BlackMarketPackets.h b/src/server/game/Server/Packets/BlackMarketPackets.h index 0fb6280811d..1193a9f0147 100644 --- a/src/server/game/Server/Packets/BlackMarketPackets.h +++ b/src/server/game/Server/Packets/BlackMarketPackets.h @@ -20,12 +20,31 @@ #include "Packet.h" #include "ObjectGuid.h" -#include "WorldSession.h" +#include "ItemPackets.h" + +class Player; +class BlackMarketEntry; namespace WorldPackets { namespace BlackMarket { + struct BlackMarketItem + { + void Initialize(BlackMarketEntry *const entry, Player* player); + + int32 MarketID = 0; + int32 SellerNPC = 0; + Item::ItemInstance Item; + int32 Quantity = 0; + uint64 MinBid = 0; + uint64 MinIncrement = 0; + uint64 CurrentBid = 0; + int32 SecondsRemaining = 0; + int32 NumBids = 0; + bool HighBid = false; + }; + class BlackMarketOpen final : public ClientPacket { public: @@ -46,6 +65,78 @@ namespace WorldPackets ObjectGuid Guid; bool Enable = true; }; + + class BlackMarketRequestItems final : public ClientPacket + { + public: + BlackMarketRequestItems(WorldPacket&& packet) : ClientPacket(CMSG_BLACK_MARKET_REQUEST_ITEMS, std::move(packet)) { } + + void Read() override; + + ObjectGuid Guid; + uint32 LastUpdateID = 0; + }; + + class BlackMarketRequestItemsResult final : public ServerPacket + { + public: + BlackMarketRequestItemsResult() : ServerPacket(SMSG_BLACK_MARKET_REQUEST_ITEMS_RESULT, 4) { } + + WorldPacket const* Write() override; + + uint32 LastUpdateID = 0; + std::vector<BlackMarketItem> Items; + }; + + class BlackMarketBidOnItem final : public ClientPacket + { + public: + BlackMarketBidOnItem(WorldPacket&& packet) : ClientPacket(CMSG_BLACK_MARKET_BID_ON_ITEM, std::move(packet)) { } + + void Read() override; + + ObjectGuid Guid; + int32 MarketID = 0; + Item::ItemInstance Item; + uint64 BidAmount = 0; + }; + + class BlackMarketBidOnItemResult final : public ServerPacket + { + public: + BlackMarketBidOnItemResult() : ServerPacket(SMSG_BLACK_MARKET_BID_ON_ITEM_RESULT, 4 + 76 + 4) { } + + WorldPacket const* Write() override; + + int32 MarketID = 0; + Item::ItemInstance Item; + int32 Result = 0; + }; + + class BlackMarketOutbid final : public ServerPacket + { + public: + BlackMarketOutbid() : ServerPacket(SMSG_BLACK_MARKET_OUTBID, 4 + 76 + 4) { } + + WorldPacket const* Write() override; + + int32 MarketID = 0; + Item::ItemInstance Item; + int32 RandomPropertiesID; + }; + + class BlackMarketWon final : public ServerPacket + { + public: + BlackMarketWon() : ServerPacket(SMSG_BLACK_MARKET_WON, 4 + 76 + 4) { } + + WorldPacket const* Write() override; + + int32 MarketID = 0; + Item::ItemInstance Item; + int32 RandomPropertiesID; + }; } } + #endif // BlackMarketPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 931b74dd234..3463421a2ea 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -183,9 +183,9 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_BF_MGR_QUEUE_INVITE_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Battlefield::BFMgrQueueInviteResponse, &WorldSession::HandleBfQueueInviteResponse); DEFINE_HANDLER(CMSG_BF_MGR_QUEUE_REQUEST, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_BINDER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleBinderActivateOpcode); - DEFINE_HANDLER(CMSG_BLACK_MARKET_BID_ON_ITEM, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_BLACK_MARKET_BID_ON_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BlackMarket::BlackMarketBidOnItem, &WorldSession::HandleBlackMarketBidOnItem); DEFINE_HANDLER(CMSG_BLACK_MARKET_OPEN, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BlackMarket::BlackMarketOpen, &WorldSession::HandleBlackMarketOpen); - DEFINE_HANDLER(CMSG_BLACK_MARKET_REQUEST_ITEMS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_BLACK_MARKET_REQUEST_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::BlackMarket::BlackMarketRequestItems, &WorldSession::HandleBlackMarketRequestItems); DEFINE_HANDLER(CMSG_BUG_REPORT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::BugReport, &WorldSession::HandleBugReportOpcode); DEFINE_HANDLER(CMSG_BUSY_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Trade::BusyTrade, &WorldSession::HandleBusyTradeOpcode); DEFINE_HANDLER(CMSG_BUY_BACK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::BuyBackItem, &WorldSession::HandleBuybackItem); @@ -906,11 +906,11 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_BF_MGR_STATE_CHANGED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BINDER_CONFIRM, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BIND_POINT_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_BID_ON_ITEM_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_OPEN_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_BID_ON_ITEM_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_OPEN_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_OUTBID, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_REQUEST_ITEMS_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_WON, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_REQUEST_ITEMS_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_BLACK_MARKET_WON, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BONUS_ROLL_EMPTY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BOSS_KILL_CREDIT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_BREAK_TARGET, STATUS_NEVER, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 35ce9c7026f..5be40946b1f 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -37,6 +37,7 @@ class BattlePetMgr; class Channel; class CollectionMgr; class Creature; +class BlackMarketEntry; class GameObject; class InstanceSave; class Item; @@ -55,6 +56,7 @@ struct AuctionEntry; struct DeclinedName; struct ItemTemplate; struct MovementInfo; +struct BlackMarketTemplate; namespace lfg { @@ -149,6 +151,9 @@ namespace WorldPackets namespace BlackMarket { class BlackMarketOpen; + class BlackMarketRequestItems; + class BlackMarketBidOnItem; + class BlackMarketOutbid; } namespace Calendar @@ -1034,6 +1039,9 @@ class TC_GAME_API WorldSession // Black Market void SendBlackMarketOpenResult(ObjectGuid guid, Creature* auctioneer); + void SendBlackMarketBidOnItemResult(int32 result, int32 marketId, WorldPackets::Item::ItemInstance& item); + void SendBlackMarketWonNotification(BlackMarketEntry const* entry, Item const* item); + void SendBlackMarketOutbidNotification(BlackMarketTemplate const* templ); //Item Enchantment void SendEnchantmentLog(ObjectGuid target, ObjectGuid caster, uint32 itemId, uint32 enchantId); @@ -1383,7 +1391,9 @@ class TC_GAME_API WorldSession void HandleBuyBankSlotOpcode(WorldPackets::Bank::BuyBankSlot& packet); // Black Market - void HandleBlackMarketOpen(WorldPackets::BlackMarket::BlackMarketOpen& packet); + void HandleBlackMarketOpen(WorldPackets::BlackMarket::BlackMarketOpen& blackMarketOpen); + void HandleBlackMarketRequestItems(WorldPackets::BlackMarket::BlackMarketRequestItems& blackMarketRequestItems); + void HandleBlackMarketBidOnItem(WorldPackets::BlackMarket::BlackMarketBidOnItem& blackMarketBidOnItem); void HandleGetMailList(WorldPackets::Mail::MailGetList& packet); void HandleSendMail(WorldPackets::Mail::SendMail& packet); diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 9d8b88abfe6..0c1be4097ca 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -29,6 +29,7 @@ #include "BattlegroundMgr.h" #include "BattlenetRpcErrorCodes.h" #include "BattlePetMgr.h" +#include "BlackMarketMgr.h" #include "CalendarMgr.h" #include "Channel.h" #include "CharacterDatabaseCleaner.h" @@ -1431,6 +1432,12 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Creature.Zone.Area.Data", false); m_bool_configs[CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Gameoject.Zone.Area.Data", false); + // Black Market + m_bool_configs[CONFIG_BLACKMARKET_ENABLED] = sConfigMgr->GetBoolDefault("BlackMarket.Enabled", true); + + m_int_configs[CONFIG_BLACKMARKET_MAXAUCTIONS] = sConfigMgr->GetIntDefault("BlackMarket.MaxAuctions", 12); + m_int_configs[CONFIG_BLACKMARKET_UPDATE_PERIOD] = sConfigMgr->GetIntDefault("BlackMarket.UpdatePeriod", 24); + // call ScriptMgr if we're reloading the configuration if (reload) sScriptMgr->OnConfigLoad(reload); @@ -1821,6 +1828,15 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Auctions..."); sAuctionMgr->LoadAuctions(); + if (m_bool_configs[CONFIG_BLACKMARKET_ENABLED]) + { + TC_LOG_INFO("server.loading", "Loading Black Market Templates..."); + sBlackMarketMgr->LoadTemplates(); + + TC_LOG_INFO("server.loading", "Loading Black Market Auctions..."); + sBlackMarketMgr->LoadAuctions(); + } + TC_LOG_INFO("server.loading", "Loading Guild rewards..."); sGuildMgr->LoadGuildRewards(); @@ -1983,6 +1999,10 @@ void World::SetInitialWorldSettings() m_timers[WUPDATE_GUILDSAVE].SetInterval(getIntConfig(CONFIG_GUILD_SAVE_INTERVAL) * MINUTE * IN_MILLISECONDS); + m_timers[WUPDATE_BLACKMARKET].SetInterval(10 * IN_MILLISECONDS); + + blackmarket_timer = 0; + //to set mailtimer to return mails every day between 4 and 5 am //mailtimer is increased when updating auctions //one second is 1000 -(tested on win system) @@ -2236,6 +2256,25 @@ void World::Update(uint32 diff) sAuctionMgr->UpdatePendingAuctions(); } + if (m_timers[WUPDATE_BLACKMARKET].Passed()) + { + m_timers[WUPDATE_BLACKMARKET].Reset(); + + ///- Update blackmarket, refresh auctions if necessary + if ((blackmarket_timer * m_timers[WUPDATE_BLACKMARKET].GetInterval() >= + getIntConfig(CONFIG_BLACKMARKET_UPDATE_PERIOD) * HOUR * IN_MILLISECONDS) + || !blackmarket_timer) + { + sBlackMarketMgr->RefreshAuctions(); + blackmarket_timer = 1; // timer is 0 on startup + } + else + { + ++blackmarket_timer; + sBlackMarketMgr->Update(); + } + } + /// <li> Handle AHBot operations if (m_timers[WUPDATE_AHBOT].Passed()) { diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 751caa4b720..2b0af679ed2 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -91,6 +91,7 @@ enum WorldTimers WUPDATE_AHBOT, WUPDATE_PINGDB, WUPDATE_GUILDSAVE, + WUPDATE_BLACKMARKET, WUPDATE_COUNT }; @@ -179,6 +180,7 @@ enum WorldBoolConfigs CONFIG_RESET_DUEL_HEALTH_MANA, CONFIG_BASEMAP_LOAD_GRIDS, CONFIG_INSTANCEMAP_LOAD_GRIDS, + CONFIG_BLACKMARKET_ENABLED, BOOL_CONFIG_VALUE_COUNT }; @@ -378,6 +380,8 @@ enum WorldIntConfigs CONFIG_AUCTION_GETALL_DELAY, CONFIG_AUCTION_SEARCH_DELAY, CONFIG_TALENTS_INSPECTING, + CONFIG_BLACKMARKET_MAXAUCTIONS, + CONFIG_BLACKMARKET_UPDATE_PERIOD, INT_CONFIG_VALUE_COUNT }; @@ -809,6 +813,7 @@ class TC_GAME_API World IntervalTimer m_timers[WUPDATE_COUNT]; time_t mail_timer; time_t mail_timer_expires; + time_t blackmarket_timer; uint32 m_updateTime, m_updateTimeSum; uint32 m_updateTimeCount; uint32 m_currentTime; diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 28dcf903625..0086cbcdf04 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -32,6 +32,7 @@ # AUCTION HOUSE BOT SETTINGS # AUCTION HOUSE BOT ITEM FINE TUNING # AUCTION HOUSE BOT BUYER CONFIG +# BLACK MARKET SETTINGS # LOGGING SYSTEM SETTINGS # CURRENCIES SETTINGS # PACKET SPOOF PROTECTION SETTINGS @@ -3441,6 +3442,37 @@ AuctionHouseBot.Buyer.Recheck.Interval = 20 ################################################################################################### ################################################################################################### +# BLACK MARKET SETTINGS +# +# BlackMarket.Enabled +# Description: General Enable or Disable of the Black Market. +# Default: 1 - (Enabled) +# 0 - (Disabled) +# + +BlackMarket.Enabled = 1 + +# +# BlackMarket.MaxAuctions +# Description: Maximum amount of aucions present at the same time. +# Default: 12 +# + +BlackMarket.MaxAuctions = 12 + +# +# BlackMarket.UpdatePeriod +# Description: How often are the auctions on the black market restored +# Default: 24 (hours) +# + +BlackMarket.UpdatePeriod = 24 + +# +# +################################################################################################### + +################################################################################################### # LOGGING SYSTEM SETTINGS # # Appender config values: Given a appender "name" |