diff --git a/data/sql/updates/pending_db_characters/rev_1621780938723425400.sql b/data/sql/updates/pending_db_characters/rev_1621780938723425400.sql new file mode 100644 index 000000000..654e0f227 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1621780938723425400.sql @@ -0,0 +1,4 @@ +INSERT INTO `version_db_characters` (`sql_rev`) VALUES ('1621780938723425400'); + +ALTER TABLE `mail` +ADD COLUMN `auctionId` INT DEFAULT 0 NOT NULL AFTER `checked`; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 8bf4f2649..0c42a8840 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -109,12 +109,12 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, houseid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_AUCTION, "DELETE FROM auctionhouse WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_AUCTION_BID, "UPDATE auctionhouse SET buyguid = ?, lastbid = ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_MAIL, "INSERT INTO mail(id, messageType, stationery, mailTemplateId, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_MAIL, "INSERT INTO mail(id, messageType, stationery, mailTemplateId, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked, auctionId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_MAIL_BY_ID, "DELETE FROM mail WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_MAIL_ITEM, "INSERT INTO mail_items(mail_id, item_guid, receiver) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_MAIL_ITEM, "DELETE FROM mail_items WHERE item_guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INVALID_MAIL_ITEM, "DELETE FROM mail_items WHERE item_guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_EXPIRED_MAIL, "SELECT id, messageType, sender, receiver, has_items, expire_time, cod, checked, mailTemplateId FROM mail WHERE expire_time < ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_EXPIRED_MAIL, "SELECT id, messageType, sender, receiver, has_items, expire_time, cod, checked, mailTemplateId, auctionId FROM mail WHERE expire_time < ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS, "SELECT item_guid, itemEntry, mail_id FROM mail_items mi INNER JOIN item_instance ii ON ii.guid = mi.item_guid LEFT JOIN mail mm ON mi.mail_id = mm.id WHERE mm.id IS NOT NULL AND mm.expire_time < ?", CONNECTION_SYNCH); PrepareStatement(CHAR_UPD_MAIL_RETURNED, "UPDATE mail SET sender = ?, receiver = ?, expire_time = ?, deliver_time = ?, cod = 0, checked = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_MAIL_ITEM_RECEIVER, "UPDATE mail_items SET receiver = ? WHERE item_guid = ?", CONNECTION_ASYNC); @@ -402,7 +402,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_OLD_CHARS, "SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID, "SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid = ? AND type = ? LIMIT 1", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_MAIL, "SELECT id, messageType, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId FROM mail WHERE receiver = ? AND deliver_time <= ? ORDER BY id DESC LIMIT 50", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_MAIL, "SELECT id, messageType, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId, auctionId FROM mail WHERE receiver = ? AND deliver_time <= ? ORDER BY id DESC LIMIT 50", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND deliver_time > ? AND (checked & 1) = 0 LIMIT 1", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_CHAR_AURA_FROZEN, "DELETE FROM character_aura WHERE spell = 9454 AND guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_INVENTORY_COUNT_ITEM, "SELECT COUNT(itemEntry) FROM character_inventory ci INNER JOIN item_instance ii ON ii.guid = ci.item WHERE itemEntry = ?", CONNECTION_SYNCH); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 91adc5cdd..78d422519 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -135,7 +135,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabas } if (sendMail) // can be changed in the hook - MailDraft(auction->BuildAuctionMailSubject(AUCTION_WON), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, 0, 0)) + MailDraft(auction->BuildAuctionMailSubject(AUCTION_WON), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout)) .AddItem(pItem) .SendMailTo(trans, MailReceiver(bidder, auction->bidder.GetCounter()), auction, MAIL_CHECK_MASK_COPIED); } @@ -151,9 +151,16 @@ void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, Characte if (owner || owner_accId) { sScriptMgr->OnBeforeAuctionHouseMgrSendAuctionSalePendingMail(this, auction, owner, owner_accId, sendMail); + + uint32 deliveryDelay = sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY); + + ByteBuffer timePacker; + timePacker.AppendPackedTime(time_t(time(nullptr) + deliveryDelay)); + if (sendMail) // can be changed in the hook - MailDraft(auction->BuildAuctionMailSubject(AUCTION_SALE_PENDING), AuctionEntry::BuildAuctionMailBody(auction->bidder, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut())) - .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED); + MailDraft(auction->BuildAuctionMailSubject(AUCTION_SALE_PENDING), + AuctionEntry::BuildAuctionMailBody(auction->bidder, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut(), deliveryDelay, timePacker.read())) + .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, 0, 0, false, true, -static_cast(auction->Id)); } } @@ -183,7 +190,7 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, Character if (sendMail) // can be changed in the hook MailDraft(auction->BuildAuctionMailSubject(AUCTION_SUCCESSFUL), AuctionEntry::BuildAuctionMailBody(auction->bidder, auction->bid, auction->buyout, auction->deposit, auction->GetAuctionCut())) .AddMoney(profit) - .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY)); + .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY), 0, false, true, auction->Id); if (auction->bid >= 500 * GOLD) if (const GlobalPlayerData* gpd = sWorld->GetGlobalPlayerData(auction->bidder.GetCounter())) @@ -222,7 +229,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDat owner->GetSession()->SendAuctionOwnerNotification(auction); if (sendMail) // can be changed in the hook - MailDraft(auction->BuildAuctionMailSubject(AUCTION_EXPIRED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit, 0)) + MailDraft(auction->BuildAuctionMailSubject(AUCTION_EXPIRED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit)) .AddItem(pItem) .SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, 0); } @@ -268,7 +275,7 @@ void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, Ch { sScriptMgr->OnBeforeAuctionHouseMgrSendAuctionCancelledToBidderMail(this, auction, bidder, bidder_accId, sendMail); if (sendMail) // can be changed in the hook - MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELLED_TO_BIDDER), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, auction->deposit, 0)) + MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELLED_TO_BIDDER), AuctionEntry::BuildAuctionMailBody(auction->owner, auction->bid, auction->buyout, auction->deposit)) .AddMoney(auction->bid) .SendMailTo(trans, MailReceiver(bidder, auction->bidder.GetCounter()), auction, MAIL_CHECK_MASK_COPIED); } @@ -777,12 +784,13 @@ std::string AuctionEntry::BuildAuctionMailSubject(MailAuctionAnswers response) c return strm.str(); } -std::string AuctionEntry::BuildAuctionMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 cut) +std::string AuctionEntry::BuildAuctionMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit /*= 0*/, uint32 cut /*= 0*/, uint32 moneyDelay /*= 0*/, uint32 eta /*= 0*/) { std::ostringstream strm; strm.width(16); strm << std::right << std::hex << guid.GetRawValue(); strm << std::dec << ':' << bid << ':' << buyout; strm << ':' << deposit << ':' << cut; + strm << ':' << moneyDelay << ':' << eta; return strm.str(); } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index e2e275ac9..d2919d294 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -84,7 +84,7 @@ struct AuctionEntry void SaveToDB(CharacterDatabaseTransaction trans) const; bool LoadFromDB(Field* fields); [[nodiscard]] std::string BuildAuctionMailSubject(MailAuctionAnswers response) const; - static std::string BuildAuctionMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 cut); + static std::string BuildAuctionMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit = 0, uint32 cut = 0, uint32 moneyDelay = 0, uint32 eta = 0); }; //this class is used as auctionhouse instance diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 955e58746..3551c2a44 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -847,7 +847,7 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this) m_mailsUpdated = false; unReadMails = 0; - m_nextMailDelivereTime = 0; + m_nextMailDelivereTime = time_t(0); m_resetTalentsCost = 0; m_resetTalentsTime = 0; @@ -1605,7 +1605,7 @@ void Player::Update(uint32 p_time) ++unReadMails; // It will be recalculate at mailbox open (for unReadMails important non-0 until mailbox open, it also will be recalculated) - m_nextMailDelivereTime = 0; + m_nextMailDelivereTime = time_t(0); } // Update cinematic location, if 500ms have passed and we're doing a cinematic now. @@ -3736,18 +3736,18 @@ void Player::UpdateNextMailTimeAndUnreads() //Get the next delivery time CharacterDatabasePreparedStatement* stmtNextDeliveryTime = CharacterDatabase.GetPreparedStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME); stmtNextDeliveryTime->setUInt32(0, GetGUID().GetCounter()); - stmtNextDeliveryTime->setUInt64(1, cTime); + stmtNextDeliveryTime->setUInt32(1, uint32(cTime)); PreparedQueryResult resultNextDeliveryTime = CharacterDatabase.Query(stmtNextDeliveryTime); if (resultNextDeliveryTime) { Field* fields = resultNextDeliveryTime->Fetch(); - m_nextMailDelivereTime = fields[0].GetUInt64(); + m_nextMailDelivereTime = time_t(fields[0].GetUInt32()); } //Get unread mails count CharacterDatabasePreparedStatement* stmtUnreadAmount = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH); stmtUnreadAmount->setUInt32(0, GetGUID().GetCounter()); - stmtUnreadAmount->setUInt64(1, cTime); + stmtUnreadAmount->setUInt32(1, uint32(cTime)); PreparedQueryResult resultUnreadAmount = CharacterDatabase.Query(stmtUnreadAmount); if (resultUnreadAmount) { @@ -19148,13 +19148,13 @@ void Player::_LoadMail() } //This should in theory always be < 100 - for (PlayerMails::iterator itr = GetMailBegin(); itr != GetMailEnd();) + for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end();) { - Mail* m = *itr; - m_mailCache.erase(itr); - if(m) - delete m; - itr = GetMailBegin(); + Mail* mail = *itr; + itr = m_mailCache.erase(itr); + + if (mail) + delete mail; } // Delete mailed items aswell @@ -19162,13 +19162,19 @@ void Player::_LoadMail() for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter) delete iter->second; + std::set pendingAuctions; + std::unordered_map pendingAuctionMails; + mMitems.clear(); //Now load the new ones m_mailCache.clear(); + + CharacterDatabaseTransaction pendingAuctionsTrans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL); stmt->setUInt32(0, GetGUID().GetCounter()); - stmt->setUInt64(1, time(nullptr)); + stmt->setUInt32(1, uint32(time(nullptr))); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (result) { @@ -19192,6 +19198,7 @@ void Player::_LoadMail() m->checked = fields[11].GetUInt8(); m->stationery = fields[12].GetUInt8(); m->mailTemplateId = fields[13].GetInt16(); + m->auctionId = fields[14].GetInt32(); if (m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId)) { @@ -19204,10 +19211,60 @@ void Player::_LoadMail() if (has_items) _LoadMailedItems(m); + // Do not load expired pending sale mail if there is already delivery auction mail + if (m->auctionId < 0 && m->expire_time <= time(nullptr)) + { + uint32 auctionId = std::abs(m->auctionId); + if (pendingAuctions.count(auctionId)) + { + CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID); + stmt2->setUInt32(0, m->messageID); + pendingAuctionsTrans->Append(stmt2); + + if (totalMailCount > 0) + --totalMailCount; + + if (unReadMails > 0 && (m->checked & MAIL_CHECK_MASK_READ) == 0) + --unReadMails; + + delete m; + continue; + } + + pendingAuctionMails[auctionId] = m; + } + else if (m->auctionId > 0) + pendingAuctions.insert(m->auctionId); + m_mailCache.push_back(m); } while (result->NextRow()); } + + for (auto itr : pendingAuctionMails) + { + uint32 auctionId = itr.first; + if (pendingAuctions.count(auctionId)) + { + Mail* mail = itr.second; + + CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID); + stmt2->setUInt32(0, mail->messageID); + pendingAuctionsTrans->Append(stmt2); + + if (totalMailCount > 0) + --totalMailCount; + + if (unReadMails > 0 && (mail->checked & MAIL_CHECK_MASK_READ) == 0) + --unReadMails; + + m_mailCache.erase(std::remove(m_mailCache.begin(), m_mailCache.end(), mail)); + + delete mail; + } + } + + CharacterDatabase.CommitTransaction(pendingAuctionsTrans); } void Player::LoadPet() diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index f20721726..f4ab58f7a 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1653,8 +1653,7 @@ public: uint32 GetMailCacheSize() { return m_mailCache.size();} Mail* GetMail(uint32 id); - PlayerMails::iterator GetMailBegin() { return m_mailCache.begin();} - PlayerMails::iterator GetMailEnd() { return m_mailCache.end();} + PlayerMails const& GetMails() const { return m_mailCache; } /*********************************************************/ /*** MAILED ITEMS SYSTEM ***/ diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 8458bf02c..4bcf9b00b 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -5681,14 +5681,14 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) time_t curTime = time(nullptr); CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL); - stmt->setUInt32(0, curTime); + stmt->setUInt32(0, uint32(curTime)); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) return; std::map itemsCache; stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS); - stmt->setUInt32(0, curTime); + stmt->setUInt32(0, uint32(curTime)); if (PreparedQueryResult items = CharacterDatabase.Query(stmt)) { MailItemInfo item; @@ -5714,10 +5714,11 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) m->receiver = fields[3].GetUInt32(); bool has_items = fields[4].GetBool(); m->expire_time = time_t(fields[5].GetUInt32()); - m->deliver_time = 0; + m->deliver_time = time_t(0); m->COD = fields[6].GetUInt32(); m->checked = fields[7].GetUInt8(); m->mailTemplateId = fields[8].GetInt16(); + m->auctionId = fields[9].GetInt32(); Player* player = nullptr; if (serverUp) @@ -5755,8 +5756,8 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED); stmt->setUInt32(0, m->receiver); stmt->setUInt32(1, m->sender); - stmt->setUInt32(2, curTime + 30 * DAY); - stmt->setUInt32(3, curTime); + stmt->setUInt32(2, uint32(curTime + 30 * DAY)); + stmt->setUInt32(3, uint32(curTime)); stmt->setUInt8 (4, uint8(MAIL_CHECK_MASK_RETURNED)); stmt->setUInt32(5, m->messageID); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 8bc0400b9..7b8d6729f 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -550,7 +550,7 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) } // item will deleted or added to received mail list - MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit, 0)) + MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELED), AuctionEntry::BuildAuctionMailBody(ObjectGuid::Empty, 0, auction->buyout, auction->deposit)) .AddItem(pItem) .SendMailTo(trans, player, auction, MAIL_CHECK_MASK_COPIED); } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index c9620f650..57466b110 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -113,12 +113,12 @@ bool LoginQueryHolder::Initialize() stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT); stmt->setUInt32(0, lowGuid); - stmt->setUInt64(1, uint64(time(nullptr))); + stmt->setUInt32(1, uint32(time(nullptr))); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD); stmt->setUInt32(0, lowGuid); - stmt->setUInt64(1, uint64(time(nullptr))); + stmt->setUInt32(1, uint32(time(nullptr))); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILDATE); diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index e79a6a2c7..83dd07140 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -588,54 +588,59 @@ void WorldSession::HandleGetMailList(WorldPacket& recvData) Player* player = _player; player->_LoadMail(); - uint32 mailsCount = 0; // real send to client mails amount // real mails amount + uint8 mailsCount = 0; + uint32 realCount = 0; WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size data << uint32(0); // real mail's count data << uint8(0); // mail's count time_t cur_time = time(nullptr); - for (PlayerMails::iterator itr = player->GetMailBegin(); itr != player->GetMailEnd(); ++itr) + for (Mail const* mail : player->GetMails()) { // prevent client storage overflow if (mailsCount >= MAX_INBOX_CLIENT_CAPACITY) { - break; + ++realCount; + continue; } // skip deleted or not delivered (deliver delay not expired) mails - if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time) - continue; - - uint8 item_count = uint8((*itr)->items.size()); // max count is MAX_MAIL_ITEMS (12) - - size_t next_mail_size = 2 + 4 + 1 + ((*itr)->messageType == MAIL_NORMAL ? 8 : 4) + 4 * 8 + ((*itr)->subject.size() + 1) + ((*itr)->body.size() + 1) + 1 + item_count * (1 + 4 + 4 + MAX_INSPECTED_ENCHANTMENT_SLOT * 3 * 4 + 4 + 4 + 4 + 4 + 4 + 4 + 1); - - if (data.wpos() + next_mail_size > MAX_NETCLIENT_PACKET_SIZE) + if (mail->state == MAIL_STATE_DELETED || cur_time < mail->deliver_time) { continue; } - data << uint16(next_mail_size); // Message size - data << uint32((*itr)->messageID); // Message ID - data << uint8((*itr)->messageType); // Message Type + uint8 item_count = uint8(mail->items.size()); // max count is MAX_MAIL_ITEMS (12) - switch ((*itr)->messageType) + size_t next_mail_size = 2 + 4 + 1 + (mail->messageType == MAIL_NORMAL ? 8 : 4) + 4 * 8 + (mail->subject.size() + 1) + (mail->body.size() + 1) + 1 + item_count * (1 + 4 + 4 + MAX_INSPECTED_ENCHANTMENT_SLOT * 3 * 4 + 4 + 4 + 4 + 4 + 4 + 4 + 1); + + if (data.wpos() + next_mail_size > MAX_NETCLIENT_PACKET_SIZE) + { + ++realCount; + continue; + } + + data << uint16(next_mail_size); // Message size + data << uint32(mail->messageID); // Message ID + data << uint8(mail->messageType); // Message Type + + switch (mail->messageType) { case MAIL_NORMAL: // sender guid - data << ObjectGuid::Create((*itr)->sender); + data << ObjectGuid::Create(mail->sender); break; case MAIL_CREATURE: case MAIL_GAMEOBJECT: case MAIL_AUCTION: case MAIL_CALENDAR: - data << uint32((*itr)->sender); // creature/gameobject entry, auction id, calendar event id? + data << uint32(mail->sender); // creature/gameobject entry, auction id, calendar event id? break; } // prevent client crash - std::string subject = (*itr)->subject; - std::string body = (*itr)->body; + std::string subject = mail->subject; + std::string body = mail->body; if (subject.find("| |") != std::string::npos) { @@ -646,20 +651,20 @@ void WorldSession::HandleGetMailList(WorldPacket& recvData) body = ""; } - data << uint32((*itr)->COD); // COD - data << uint32(0); // probably changed in 3.3.3 - data << uint32((*itr)->stationery); // stationery (Stationery.dbc) - data << uint32((*itr)->money); // Gold - data << uint32((*itr)->checked); // flags - data << float(float((*itr)->expire_time - time(nullptr)) / DAY); // Time - data << uint32((*itr)->mailTemplateId); // mail template (MailTemplate.dbc) - data << subject; // Subject string - once 00, when mail type = 3, max 256 - data << body; // message? max 8000 - data << uint8(item_count); // client limit is 0x10 + data << uint32(mail->COD); // COD + data << uint32(0); // probably changed in 3.3.3 + data << uint32(mail->stationery); // stationery (Stationery.dbc) + data << uint32(mail->money); // Gold + data << uint32(mail->checked); // flags + data << float(float(mail->expire_time - time(nullptr)) / DAY); // Time + data << uint32(mail->mailTemplateId); // mail template (MailTemplate.dbc) + data << subject; // Subject string - once 00, when mail type = 3, max 256 + data << body; // message? max 8000 + data << uint8(item_count); // client limit is 0x10 for (uint8 i = 0; i < item_count; ++i) { - Item* item = player->GetMItem((*itr)->items[i].item_guid); + Item* item = player->GetMItem(mail->items[i].item_guid); // item index (0-6?) data << uint8(i); // item guid low? @@ -688,11 +693,12 @@ void WorldSession::HandleGetMailList(WorldPacket& recvData) data << uint8(0); } + ++realCount; ++mailsCount; } - data.put(0, player->totalMailCount); // this will display warning about undelivered mail to player if realCount > mailsCount - data.put(4, uint8(mailsCount)); // set real send mails to client + data.put(0, realCount); // this will display warning about undelivered mail to player if realCount > mailsCount + data.put(4, mailsCount); // set real send mails to client SendPacket(&data); // recalculate m_nextMailDelivereTime and unReadMails @@ -781,28 +787,27 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket& /*recvData*/) uint32 count = 0; time_t now = time(nullptr); std::set sentSenders; - for (PlayerMails::iterator itr = _player->GetMailBegin(); itr != _player->GetMailEnd(); ++itr) + for (Mail const* mail : _player->GetMails()) { - Mail* m = (*itr); // must be not checked yet - if (m->checked & MAIL_CHECK_MASK_READ) + if (mail->checked & MAIL_CHECK_MASK_READ) continue; // and already delivered - if (now < m->deliver_time) + if (now < mail->deliver_time) continue; // only send each mail sender once - if (sentSenders.count(m->sender)) + if (sentSenders.count(mail->sender)) continue; - data << (m->messageType == MAIL_NORMAL ? ObjectGuid::Create(m->sender) : ObjectGuid::Empty); // player guid - data << uint32(m->messageType != MAIL_NORMAL ? m->sender : 0); // non-player entries - data << uint32(m->messageType); - data << uint32(m->stationery); - data << float(m->deliver_time - now); + data << (mail->messageType == MAIL_NORMAL ? ObjectGuid::Create(mail->sender) : ObjectGuid::Empty); // player guid + data << uint32(mail->messageType != MAIL_NORMAL ? mail->sender : 0); // non-player entries + data << uint32(mail->messageType); + data << uint32(mail->stationery); + data << float(mail->deliver_time - now); - sentSenders.insert(m->sender); + sentSenders.insert(mail->sender); ++count; if (count == 2) // do not display more than 2 mails break; diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index 2df948461..9f58cc32f 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -167,7 +167,7 @@ void MailDraft::SendReturnToSender(uint32 /*sender_acc*/, ObjectGuid::LowType se SendMailTo(trans, MailReceiver(receiver, receiver_guid), MailSender(MAIL_NORMAL, sender_guid), MAIL_CHECK_MASK_RETURNED, 0); } -void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay, uint32 custom_expiration, bool deleteMailItemsFromDB, bool sendMail) +void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay, uint32 custom_expiration, bool deleteMailItemsFromDB, bool sendMail, int32 auctionId) { sScriptMgr->OnBeforeMailDraftSendMailTo(this, receiver, sender, checked, deliver_delay, custom_expiration, deleteMailItemsFromDB, sendMail); @@ -221,11 +221,12 @@ void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver cons stmt->setString(++index, GetSubject()); stmt->setString(++index, GetBody()); stmt->setBool (++index, !m_items.empty()); - stmt->setUInt64(++index, uint64(expire_time)); - stmt->setUInt64(++index, uint64(deliver_time)); + stmt->setUInt32(++index, uint32(expire_time)); + stmt->setUInt32(++index, uint32(deliver_time)); stmt->setUInt32(++index, m_money); stmt->setUInt32(++index, m_COD); stmt->setUInt8 (++index, uint8(checked)); + stmt->setInt32(++index, auctionId); trans->Append(stmt); for (MailItemMap::const_iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter) @@ -267,6 +268,7 @@ void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver cons m->expire_time = expire_time; m->deliver_time = deliver_time; m->checked = checked; + m->auctionId = auctionId; m->state = MAIL_STATE_UNCHANGED; pReceiver->AddMail(m); // to insert new mail to beginning of maillist diff --git a/src/server/game/Mails/Mail.h b/src/server/game/Mails/Mail.h index d2c9d1e13..e65d4b8c6 100644 --- a/src/server/game/Mails/Mail.h +++ b/src/server/game/Mails/Mail.h @@ -127,7 +127,7 @@ public: // modifiers public: // finishers void SendReturnToSender(uint32 sender_acc, ObjectGuid::LowType sender_guid, ObjectGuid::LowType receiver_guid, CharacterDatabaseTransaction trans); - void SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0, uint32 custom_expiration = 0, bool deleteMailItemsFromDB = false, bool sendMail = true); + void SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0, uint32 custom_expiration = 0, bool deleteMailItemsFromDB = false, bool sendMail = true, int32 auctionId = 0); private: void deleteIncludedItems(CharacterDatabaseTransaction trans, bool inDB = false); @@ -170,6 +170,10 @@ struct Mail uint32 checked; MailState state; + // < 0 Pending + // > 0 Delivery + int32 auctionId; + void AddItem(ObjectGuid::LowType itemGuidLow, uint32 item_template) { MailItemInfo mii;