fix(Core/Mail): cleanup pending auction sale mail (#6022)

This commit is contained in:
UltraNix
2021-06-23 12:46:48 +02:00
committed by GitHub
parent 77ad79c928
commit a9b2ddba7a
12 changed files with 160 additions and 80 deletions

View File

@@ -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`;

View File

@@ -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);

View File

@@ -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<uint32>()))
.SendMailTo(trans, MailReceiver(owner, auction->owner.GetCounter()), auction, MAIL_CHECK_MASK_COPIED, 0, 0, false, true, -static_cast<int32>(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();
}

View File

@@ -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

View File

@@ -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<uint32> pendingAuctions;
std::unordered_map<uint32, Mail*> 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()

View File

@@ -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 ***/

View File

@@ -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<uint32 /*messageId*/, MailItemInfoVec> 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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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<HighGuid::Player>((*itr)->sender);
data << ObjectGuid::Create<HighGuid::Player>(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<uint32>(0, player->totalMailCount); // this will display warning about undelivered mail to player if realCount > mailsCount
data.put<uint8>(4, uint8(mailsCount)); // set real send mails to client
data.put<uint32>(0, realCount); // this will display warning about undelivered mail to player if realCount > mailsCount
data.put<uint8>(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<uint32> 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<HighGuid::Player>(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<HighGuid::Player>(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;

View File

@@ -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

View File

@@ -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;