fix(Core/Mail): load mails on login instead of when requested (#8065)

This commit is contained in:
Skjalf
2021-10-10 12:15:24 -03:00
committed by GitHub
parent 1e57b6fb99
commit f96b027ffd
7 changed files with 157 additions and 236 deletions

View File

@@ -90,10 +90,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, "
"item, itemEntry FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND deliver_time <= ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_MAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND (checked & 1) = 0", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT friend, flags, note FROM character_social JOIN characters ON characters.guid = character_social.friend WHERE character_social.guid = ? AND deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, category, item, time, needSend FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
@@ -115,7 +113,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
// End LoginQueryHolder content
PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, item_guid, itemEntry, owner_guid FROM mail_items mi JOIN item_instance ii ON mi.item_guid = ii.guid WHERE mail_id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, item_guid, itemEntry, ii.owner_guid, m.id FROM mail_items mi INNER JOIN mail m ON mi.mail_id = m.id LEFT JOIN item_instance ii ON mi.item_guid = ii.guid WHERE m.receiver = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, itemguid, itemEntry FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, houseid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH);
PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, houseid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
@@ -415,7 +413,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, auctionId 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, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId, auctionId FROM mail WHERE receiver = ? AND deliver_time <= ? ORDER BY id DESC LIMIT 50", CONNECTION_ASYNC);
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

@@ -79,10 +79,8 @@ enum CharacterDatabaseStatements : uint32
CHAR_SEL_CHARACTER_INVENTORY,
CHAR_SEL_CHARACTER_ACTIONS,
CHAR_SEL_CHARACTER_ACTIONS_SPEC,
CHAR_SEL_CHARACTER_MAILCOUNT,
CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD,
CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH,
CHAR_SEL_CHARACTER_MAILDATE,
CHAR_SEL_CHARACTER_SOCIALLIST,
CHAR_SEL_CHARACTER_HOMEBIND,
CHAR_SEL_CHARACTER_SPELLCOOLDOWNS,

View File

@@ -436,7 +436,7 @@ Player::~Player()
delete itr->second;
//all mailed items should be deleted, also all mail should be deallocated
for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end(); ++itr)
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
{
delete *itr;
}
@@ -2747,12 +2747,12 @@ void Player::SendInitialSpells()
void Player::RemoveMail(uint32 id)
{
for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end(); ++itr)
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
{
if ((*itr)->messageID == id)
{
//do not delete item, because Player::removeMail() is called when returning mail to sender.
m_mailCache.erase(itr);
m_mail.erase(itr);
return;
}
}
@@ -2784,7 +2784,8 @@ void Player::SendNewMail()
void Player::AddNewMailDeliverTime(time_t deliver_time)
{
sWorld->UpdateGlobalPlayerMails(GetGUID().GetCounter(), totalMailCount, false);
sWorld->UpdateGlobalPlayerMails(GetGUID().GetCounter(), GetMailSize(), false);
if (deliver_time <= time(nullptr)) // ready now
{
++unReadMails;
@@ -3693,7 +3694,7 @@ void Player::SetFreeTalentPoints(uint32 points)
Mail* Player::GetMail(uint32 id)
{
for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end(); ++itr)
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
{
if ((*itr)->messageID == id)
{
@@ -3912,6 +3913,25 @@ void Player::DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool up
if (resultMail)
{
std::unordered_map<uint32, std::vector<Item*>> itemsByMail;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
stmt->setUInt32(0, lowGuid);
PreparedQueryResult resultItems = CharacterDatabase.Query(stmt);
if (resultItems)
{
do
{
Field* fields = resultItems->Fetch();
uint32 mailId = fields[14].GetUInt32();
if (Item* mailItem = _LoadMailedItem(playerGuid, nullptr, mailId, nullptr, fields))
{
itemsByMail[mailId].push_back(mailItem);
}
} while (resultItems->NextRow());
}
do
{
Field* mailFields = resultMail->Fetch();
@@ -3947,40 +3967,16 @@ void Player::DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool up
if (mailTemplateId)
draft = MailDraft(mailTemplateId, false); // items are already included
if (has_items)
auto itemsItr = itemsByMail.find(mail_id);
if (itemsItr != itemsByMail.end())
{
// Data needs to be at first place for Item::LoadFromDB
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
stmt->setUInt32(0, mail_id);
PreparedQueryResult resultItems = CharacterDatabase.Query(stmt);
if (resultItems)
for (Item* item : itemsItr->second)
{
do
{
Field* itemFields = resultItems->Fetch();
ObjectGuid::LowType item_guidlow = itemFields[11].GetUInt32();
uint32 item_template = itemFields[12].GetUInt32();
ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item_template);
if (!itemProto)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->setUInt32(0, item_guidlow);
trans->Append(stmt);
continue;
}
Item* pItem = NewItemOrBag(itemProto);
if (!pItem->LoadFromDB(item_guidlow, playerGuid, itemFields, item_template))
{
pItem->FSetState(ITEM_REMOVED);
pItem->SaveToDB(trans); // it also deletes item object!
continue;
}
draft.AddItem(pItem);
} while (resultItems->NextRow());
draft.AddItem(item);
}
// MailDraft will take care of freeing memory.
itemsByMail.erase(itemsItr);
}
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);

View File

@@ -842,9 +842,8 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_REPUTATION = 7,
PLAYER_LOGIN_QUERY_LOAD_INVENTORY = 8,
PLAYER_LOGIN_QUERY_LOAD_ACTIONS = 9,
PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT = 10,
PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT = 11,
PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE = 12,
PLAYER_LOGIN_QUERY_LOAD_MAILS = 10,
PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS = 11,
PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST = 13,
PLAYER_LOGIN_QUERY_LOAD_HOME_BIND = 14,
PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS = 15,
@@ -1576,19 +1575,17 @@ public:
void RemoveMail(uint32 id);
void AddMail(Mail* mail) { totalMailCount++; m_mailCache.push_front(mail); }// for call from WorldSession::SendMailTo
uint32 GetMailSize() { return totalMailCount; }
uint32 GetMailCacheSize() { return m_mailCache.size();}
void AddMail(Mail* mail) { m_mail.push_front(mail); }// for call from WorldSession::SendMailTo
uint32 GetMailSize() { return m_mail.size();}
Mail* GetMail(uint32 id);
PlayerMails const& GetMails() const { return m_mailCache; }
PlayerMails const& GetMails() const { return m_mail; }
/*********************************************************/
/*** MAILED ITEMS SYSTEM ***/
/*********************************************************/
uint8 unReadMails;
uint32 totalMailCount;
time_t m_nextMailDelivereTime;
typedef std::unordered_map<ObjectGuid::LowType, Item*> ItemMap;
@@ -2642,9 +2639,8 @@ public:
void _LoadAuras(PreparedQueryResult result, uint32 timediff);
void _LoadGlyphAuras();
void _LoadInventory(PreparedQueryResult result, uint32 timeDiff);
void _LoadMailInit(PreparedQueryResult resultMailCount, PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery);
void _LoadMail();
void _LoadMailedItems(Mail* mail);
void _LoadMail(PreparedQueryResult mailsResult, PreparedQueryResult mailItemsResult);
static Item* _LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint32 mailId, Mail* mail, Field* fields);
void _LoadQuestStatus(PreparedQueryResult result);
void _LoadQuestStatusRewarded(PreparedQueryResult result);
void _LoadDailyQuestStatus(PreparedQueryResult result);
@@ -2741,7 +2737,7 @@ public:
uint32 m_GuildIdInvited;
uint32 m_ArenaTeamIdInvited;
PlayerMails m_mailCache;
PlayerMails m_mail;
PlayerSpellMap m_spells;
PlayerTalentMap m_talents;
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use

View File

@@ -5454,9 +5454,6 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
// apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
//mails are loaded only when needed ;-) - when player in game click on mailbox.
//_LoadMail();
m_specsCount = fields[64].GetUInt8();
m_activeSpec = fields[65].GetUInt8();
@@ -5495,8 +5492,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
m_reputationMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
// xinef: load mails before inventory, so problematic items can be added to already loaded mails
// unread mails and next delivery time, actual mails not loaded
_LoadMailInit(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE));
_LoadMail(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAILS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS));
_LoadInventory(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff);
@@ -6124,161 +6120,98 @@ Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint3
}
// load mailed item which should receive current player
void Player::_LoadMailedItems(Mail* mail)
Item* Player::_LoadMailedItem(ObjectGuid const& playerGuid, Player* player, uint32 mailId, Mail* mail, Field* fields)
{
// data needs to be at first place for Item::LoadFromDB
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
stmt->setUInt32(0, mail->messageID);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
return;
ObjectGuid::LowType itemGuid = fields[11].GetUInt32();
uint32 itemEntry = fields[12].GetUInt32();
do
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry);
if (!proto)
{
Field* fields = result->Fetch();
LOG_ERROR("entities.player", "Player %s (%s) has unknown item in mailed items (GUID: %u, Entry: %u) in mail (%u), deleted.",
player ? player->GetName().c_str() : "<unknown>", playerGuid.ToString().c_str(), itemGuid, itemEntry, mailId);
ObjectGuid::LowType itemGuid = fields[11].GetUInt32();
uint32 itemTemplate = fields[12].GetUInt32();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
mail->AddItem(itemGuid, itemTemplate);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM);
stmt->setUInt32(0, itemGuid);
trans->Append(stmt);
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemTemplate);
CharacterDatabase.CommitTransaction(trans);
return nullptr;
}
if (!proto)
{
LOG_ERROR("entities.player", "Player %s has unknown item_template (ProtoType) in mailed items (GUID: %u, template: %u) in mail (%u), deleted.",
GetGUID().ToString().c_str(), itemGuid, itemTemplate, mail->messageID);
Item* item = NewItemOrBag(proto);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM);
stmt->setUInt32(0, itemGuid);
CharacterDatabase.Execute(stmt);
ObjectGuid ownerGuid = fields[13].GetUInt32() ? ObjectGuid::Create<HighGuid::Player>(fields[13].GetUInt32()) : ObjectGuid::Empty;
if (!item->LoadFromDB(itemGuid, ownerGuid, fields, itemEntry))
{
LOG_ERROR("entities.player", "Player::_LoadMailedItems: Item (GUID: %u) in mail (%u) doesn't exist, deleted from mail.", itemGuid, mailId);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->setUInt32(0, itemGuid);
CharacterDatabase.Execute(stmt);
continue;
}
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
stmt->setUInt32(0, itemGuid);
CharacterDatabase.Execute(stmt);
Item* item = NewItemOrBag(proto);
if (!item->LoadFromDB(itemGuid, ObjectGuid::Create<HighGuid::Player>(fields[13].GetUInt32()), fields, itemTemplate))
{
LOG_ERROR("entities.player", "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, itemGuid);
item->FSetState(ITEM_REMOVED);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM);
stmt->setUInt32(0, itemGuid);
CharacterDatabase.Execute(stmt);
CharacterDatabaseTransaction temp = CharacterDatabaseTransaction(nullptr);
item->SaveToDB(temp);
return nullptr;
}
item->FSetState(ITEM_REMOVED);
if (mail)
{
mail->AddItem(itemGuid, itemEntry);
}
CharacterDatabaseTransaction temp = CharacterDatabaseTransaction(nullptr);
item->SaveToDB(temp); // it also deletes item object !
continue;
}
if (player)
{
player->AddMItem(item);
}
AddMItem(item);
} while (result->NextRow());
return item;
}
void Player::_LoadMailInit(PreparedQueryResult resultMailCount, PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery)
void Player::_LoadMail(PreparedQueryResult mailsResult, PreparedQueryResult mailItemsResult)
{
//Set count for all mails used to display correct size later one
if (resultMailCount)
{
totalMailCount = uint32((*resultMailCount)[0].GetUInt32());
sWorld->UpdateGlobalPlayerMails(GetGUID().GetCounter(), totalMailCount, false);
}
//set a count of unread mails
//QueryResult* resultMails = CharacterDatabase.PQuery("SELECT COUNT(id) FROM mail WHERE receiver = '%u' AND (checked & 1)=0 AND deliver_time <= '" UI64FMTD "'", playerGuid.GetCounter(), (uint64)cTime);
if (resultUnread)
unReadMails = uint8((*resultUnread)[0].GetUInt64());
// store nearest delivery time (it > 0 and if it < current then at next player update SendNewMaill will be called)
//resultMails = CharacterDatabase.PQuery("SELECT MIN(deliver_time) FROM mail WHERE receiver = '%u' AND (checked & 1)=0", playerGuid.GetCounter());
if (resultDelivery)
m_nextMailDelivereTime = time_t((*resultDelivery)[0].GetUInt32());
}
void Player::_LoadMail()
{
//In case we are not expecting to receive new mail we just exits, not really necessary to refresh any data
if (m_nextMailDelivereTime > time(nullptr))
{
return;
}
if (m_mailsUpdated)
{
//Save changed data to the sql before refreshing so we always get up to date data
CharacterDatabaseTransaction saveTransaction = CharacterDatabase.BeginTransaction();
_SaveMail(saveTransaction);
CharacterDatabase.CommitTransaction(saveTransaction);
}
//This should in theory always be < 100
for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end();)
{
Mail* mail = *itr;
itr = m_mailCache.erase(itr);
if (mail)
delete mail;
}
// Delete mailed items aswell
// Created again below in Player::_LoadMailedItems
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->setUInt32(1, uint32(time(nullptr)));
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (result)
m_mail.clear();
std::unordered_map<uint32, Mail*> mailById;
if (mailsResult)
{
do
{
Field* fields = result->Fetch();
Field* fields = mailsResult->Fetch();
Mail* m = new Mail;
m->messageID = fields[0].GetUInt32();
m->messageType = fields[1].GetUInt8();
m->sender = fields[2].GetUInt32();
m->receiver = fields[3].GetUInt32();
m->subject = fields[4].GetString();
m->body = fields[5].GetString();
bool has_items = fields[6].GetBool();
m->expire_time = time_t(fields[7].GetUInt32());
m->deliver_time = time_t(fields[8].GetUInt32());
m->money = fields[9].GetUInt32();
m->COD = fields[10].GetUInt32();
m->checked = fields[11].GetUInt8();
m->stationery = fields[12].GetUInt8();
m->mailTemplateId = fields[13].GetInt16();
m->auctionId = fields[14].GetInt32();
m->messageID = fields[0].GetUInt32();
m->messageType = fields[1].GetUInt8();
m->sender = fields[2].GetUInt32();
m->receiver = fields[3].GetUInt32();
m->subject = fields[4].GetString();
m->body = fields[5].GetString();
m->expire_time = time_t(fields[6].GetUInt32());
m->deliver_time = time_t(fields[7].GetUInt32());
m->money = fields[8].GetUInt32();
m->COD = fields[9].GetUInt32();
m->checked = fields[10].GetUInt8();
m->stationery = fields[11].GetUInt8();
m->mailTemplateId = fields[12].GetInt16();
m->auctionId = fields[13].GetInt32();
if (m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId))
{
LOG_ERROR("entities.player", "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
LOG_ERROR("entities.player", "Player::_LoadMail: Mail (%u) has nonexistent MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId);
m->mailTemplateId = 0;
}
m->state = MAIL_STATE_UNCHANGED;
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))
{
@@ -6289,9 +6222,6 @@ void Player::_LoadMail()
stmt2->setUInt32(0, m->messageID);
pendingAuctionsTrans->Append(stmt2);
if (totalMailCount > 0)
--totalMailCount;
if (unReadMails > 0 && (m->checked & MAIL_CHECK_MASK_READ) == 0)
--unReadMails;
@@ -6302,37 +6232,48 @@ void Player::_LoadMail()
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 (unReadMails > 0 && (mail->checked & MAIL_CHECK_MASK_READ) == 0)
{
--unReadMails;
}
m_mail.erase(std::remove(m_mail.begin(), m_mail.end(), mail));
delete mail;
}
}
m_mail.push_back(m);
mailById[m->messageID] = m;
} while (mailsResult->NextRow());
}
for (auto itr : pendingAuctionMails)
if (mailItemsResult)
{
uint32 auctionId = itr.first;
if (pendingAuctions.count(auctionId))
do
{
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;
}
Field* fields = mailItemsResult->Fetch();
uint32 mailId = fields[14].GetUInt32();
_LoadMailedItem(GetGUID(), this, mailId, mailById[mailId], fields);
} while (mailItemsResult->NextRow());
}
CharacterDatabase.CommitTransaction(pendingAuctionsTrans);
UpdateNextMailTimeAndUnreads();
}
void Player::LoadPet()
@@ -7490,14 +7431,14 @@ void Player::_SaveInventory(CharacterDatabaseTransaction trans)
void Player::_SaveMail(CharacterDatabaseTransaction trans)
{
if (!GetMailCacheSize() || !m_mailsUpdated)
if (!GetMailSize() || !m_mailsUpdated)
{
return;
}
CharacterDatabasePreparedStatement* stmt = nullptr;
for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end(); ++itr)
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
{
Mail* m = (*itr);
if (m->state == MAIL_STATE_CHANGED)
@@ -7543,20 +7484,18 @@ void Player::_SaveMail(CharacterDatabaseTransaction trans)
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
stmt->setUInt32(0, m->messageID);
trans->Append(stmt);
if (totalMailCount > 0)
totalMailCount--;
}
}
//deallocate deleted mails...
for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end();)
for (PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end();)
{
if ((*itr)->state == MAIL_STATE_DELETED)
{
Mail* m = *itr;
m_mailCache.erase(itr);
m_mail.erase(itr);
delete m;
itr = m_mailCache.begin();
itr = m_mail.begin();
}
else
++itr;

View File

@@ -122,19 +122,14 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACTIONS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL);
stmt->setUInt32(0, lowGuid);
stmt->setUInt32(1, uint32(time(nullptr)));
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT, stmt);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAILS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
stmt->setUInt32(0, lowGuid);
stmt->setUInt32(1, uint32(time(nullptr)));
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILDATE);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE, stmt);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SOCIALLIST);
stmt->setUInt32(0, lowGuid);

View File

@@ -120,11 +120,11 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
return;
}
ObjectGuid rc;
ObjectGuid receiverGuid;
if (normalizePlayerName(receiver))
rc = sObjectMgr->GetPlayerGUIDByName(receiver);
receiverGuid = sObjectMgr->GetPlayerGUIDByName(receiver);
if (!rc)
if (!receiverGuid)
{
LOG_DEBUG("network.opcode", "Player %s is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u",
player->GetGUID().ToString().c_str(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
@@ -133,9 +133,9 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
}
LOG_DEBUG("network.opcode", "Player %s is sending mail to %s (%s) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u",
player->GetGUID().ToString().c_str(), receiver.c_str(), rc.ToString().c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
player->GetGUID().ToString().c_str(), receiver.c_str(), receiverGuid.ToString().c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
if (player->GetGUID() == rc)
if (player->GetGUID() == receiverGuid)
{
player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF);
return;
@@ -165,7 +165,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
return;
}
Player* receive = ObjectAccessor::FindConnectedPlayer(rc);
Player* receive = ObjectAccessor::FindConnectedPlayer(receiverGuid);
uint32 rc_teamId = TEAM_NEUTRAL;
uint16 mails_count = 0; //do not allow to send to one player more than 100 mails
@@ -178,7 +178,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
else
{
// xinef: get data from global storage
if (GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(rc.GetCounter()))
if (GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(receiverGuid.GetCounter()))
{
rc_teamId = Player::TeamIdForRace(playerData->race);
mails_count = playerData->mailCount;
@@ -207,7 +207,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
}
}*/
uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(rc.GetCounter());
uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(receiverGuid.GetCounter());
if (/*!accountBound*/ GetAccountId() != rc_account && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeamId() != rc_teamId && AccountMgr::IsPlayerAccount(GetSecurity()))
{
@@ -264,8 +264,10 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
return;
}
if (!sScriptMgr->CanSendMail(player, rc, mailbox, subject, body, money, COD, item))
if (!sScriptMgr->CanSendMail(player, receiverGuid, mailbox, subject, body, money, COD, item))
{
return;
}
items[i] = item;
}
@@ -295,7 +297,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
item->DeleteFromInventoryDB(trans); // deletes item from character's inventory
if (item->GetState() == ITEM_UNCHANGED)
item->FSetState(ITEM_CHANGED); // pussywizard: so the item will be saved and owner will be updated in database
item->SetOwnerGUID(rc);
item->SetOwnerGUID(receiverGuid);
item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
draft.AddItem(item);
@@ -324,7 +326,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
draft
.AddMoney(money)
.AddCOD(COD)
.SendMailTo(trans, MailReceiver(receive, rc.GetCounter()), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay);
.SendMailTo(trans, MailReceiver(receive, receiverGuid.GetCounter()), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay);
player->SaveInventoryAndGoldToDB(trans);
CharacterDatabase.CommitTransaction(trans);
@@ -597,7 +599,6 @@ void WorldSession::HandleGetMailList(WorldPacket& recvData)
return;
Player* player = _player;
player->_LoadMail();
uint8 mailsCount = 0;
uint32 realCount = 0;
@@ -788,8 +789,6 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket& /*recvData*/)
{
WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8);
_player->_LoadMail();
if (_player->unReadMails > 0)
{
data << float(0); // float