aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/base/characters_database.sql26
-rw-r--r--sql/updates/characters/2016_04_11_01_characters.sql9
-rw-r--r--sql/updates/world/2016_04_11_02_world.sql12
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp6
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h5
-rw-r--r--src/server/game/BlackMarket/BlackMarketMgr.cpp492
-rw-r--r--src/server/game/BlackMarket/BlackMarketMgr.h158
-rw-r--r--src/server/game/Entities/Unit/Unit.h7
-rw-r--r--src/server/game/Handlers/BlackMarketHandler.cpp120
-rw-r--r--src/server/game/Mails/Mail.cpp4
-rw-r--r--src/server/game/Mails/Mail.h5
-rw-r--r--src/server/game/Server/Packets/AllPackets.h1
-rw-r--r--src/server/game/Server/Packets/BlackMarketPackets.cpp114
-rw-r--r--src/server/game/Server/Packets/BlackMarketPackets.h93
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp12
-rw-r--r--src/server/game/Server/WorldSession.h12
-rw-r--r--src/server/game/World/World.cpp39
-rw-r--r--src/server/game/World/World.h5
-rw-r--r--src/server/worldserver/worldserver.conf.dist32
19 files changed, 1135 insertions, 17 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql
index 23e7bb77d25..d5117954f5c 100644
--- a/sql/base/characters_database.sql
+++ b/sql/base/characters_database.sql
@@ -240,6 +240,32 @@ LOCK TABLES `banned_addons` WRITE;
UNLOCK TABLES;
--
+-- Table structure for table `blackmarket_auctions`
+--
+
+DROP TABLE IF EXISTS `blackmarket_auctions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `blackmarket_auctions` (
+ `marketId` int(10) NOT NULL DEFAULT '0',
+ `currentBid` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `time` int(10) NOT NULL DEFAULT '0',
+ `numBids` int(10) NOT NULL DEFAULT '0',
+ `bidder` bigint(20) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`marketId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `blackmarket_auctions`
+--
+
+LOCK TABLES `blackmarket_auctions` WRITE;
+/*!40000 ALTER TABLE `blackmarket_auctions` DISABLE KEYS */;
+/*!40000 ALTER TABLE `blackmarket_auctions` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
-- Table structure for table `bugreport`
--
diff --git a/sql/updates/characters/2016_04_11_01_characters.sql b/sql/updates/characters/2016_04_11_01_characters.sql
new file mode 100644
index 00000000000..bdc6532b1e2
--- /dev/null
+++ b/sql/updates/characters/2016_04_11_01_characters.sql
@@ -0,0 +1,9 @@
+DROP TABLE IF EXISTS `blackmarket_auctions`;
+CREATE TABLE `blackmarket_auctions` (
+ `marketId` int(10) NOT NULL DEFAULT '0',
+ `currentBid` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `time` int(10) NOT NULL DEFAULT '0',
+ `numBids` int(10) NOT NULL DEFAULT '0',
+ `bidder` bigint(20) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`marketId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/sql/updates/world/2016_04_11_02_world.sql b/sql/updates/world/2016_04_11_02_world.sql
new file mode 100644
index 00000000000..2c362b80564
--- /dev/null
+++ b/sql/updates/world/2016_04_11_02_world.sql
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS `blackmarket_template`;
+CREATE TABLE `blackmarket_template` (
+ `marketId` int(10) NOT NULL DEFAULT '0',
+ `sellerNpc` mediumint(8) NOT NULL DEFAULT '0',
+ `itemEntry` mediumint(8) unsigned NOT NULL DEFAULT '0',
+ `quantity` int(10) NOT NULL DEFAULT '1',
+ `minBid` bigint(20) unsigned NOT NULL DEFAULT '0',
+ `duration` int(10) NOT NULL DEFAULT '0',
+ `chance` float NOT NULL DEFAULT '0',
+ `bonusListIDs` text,
+ PRIMARY KEY (`marketId`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
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"