mirror of
https://github.com/liyunfan1223/azerothcore-wotlk.git
synced 2026-08-04 13:57:52 +00:00
refactor(Core/Mails): centralize mail count bookkeeping in a new MailMgr (#26727)
This commit is contained in:
@@ -424,7 +424,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, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId FROM mail WHERE receiver = ? ORDER BY id DESC", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_MAIL, "SELECT id, messageType, sender, receiver, subject, body, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId, has_items FROM mail WHERE receiver = ? ORDER BY id DESC", 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);
|
||||
|
||||
@@ -19,9 +19,12 @@
|
||||
#include "ArenaTeam.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "MailMgr.h"
|
||||
#include "Player.h"
|
||||
#include "Timer.h"
|
||||
#include "World.h"
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace
|
||||
@@ -76,15 +79,7 @@ void CharacterCache::LoadCharacterCacheStorage()
|
||||
fields[4].Get<uint8>() /*gender*/, fields[3].Get<uint8>() /*race*/, fields[5].Get<uint8>() /*class*/, fields[6].Get<uint8>() /*level*/);
|
||||
} while (result->NextRow());
|
||||
|
||||
QueryResult mailCountResult = CharacterDatabase.Query("SELECT receiver, COUNT(receiver) FROM mail GROUP BY receiver");
|
||||
if (mailCountResult)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = mailCountResult->Fetch();
|
||||
UpdateCharacterMailCount(ObjectGuid(HighGuid::Player, fields[0].Get<uint32>()), static_cast<int8>(fields[1].Get<uint64>()), true);
|
||||
} while (mailCountResult->NextRow());
|
||||
}
|
||||
sMailMgr->LoadMailCounts();
|
||||
|
||||
LOG_INFO("server.loading", ">> Loaded Character Infos For {} Characters in {} ms", _characterCacheStore.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
@@ -105,15 +100,7 @@ void CharacterCache::RefreshCacheEntry(uint32 lowGuid)
|
||||
AddCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(fields[0].Get<uint32>()) /*guid*/, fields[2].Get<uint32>() /*account*/, fields[1].Get<std::string>() /*name*/, fields[4].Get<uint8>() /*gender*/, fields[3].Get<uint8>() /*race*/, fields[5].Get<uint8>() /*class*/, fields[6].Get<uint8>() /*level*/);
|
||||
} while (result->NextRow());
|
||||
|
||||
QueryResult mailCountResult = CharacterDatabase.Query("SELECT receiver, COUNT(receiver) FROM mail WHERE receiver = {} GROUP BY receiver", lowGuid);
|
||||
if (mailCountResult)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = mailCountResult->Fetch();
|
||||
UpdateCharacterMailCount(ObjectGuid(HighGuid::Player, fields[0].Get<uint32>()), static_cast<int8>(fields[1].Get<uint64>()), true);
|
||||
} while (mailCountResult->NextRow());
|
||||
}
|
||||
sMailMgr->RecountMailCount(lowGuid);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -216,27 +203,25 @@ void CharacterCache::UpdateCharacterArenaTeamId(ObjectGuid const& guid, uint8 sl
|
||||
itr->second.ArenaTeamId[slot] = arenaTeamId;
|
||||
}
|
||||
|
||||
void CharacterCache::UpdateCharacterMailCount(ObjectGuid const& guid, int8 count, bool update)
|
||||
void CharacterCache::UpdateCharacterMailCount(ObjectGuid const& guid, int32 count, bool update)
|
||||
{
|
||||
auto itr = _characterCacheStore.find(guid);
|
||||
if (itr == _characterCacheStore.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int32 maxCount = std::numeric_limits<uint16>::max();
|
||||
|
||||
if (update)
|
||||
{
|
||||
itr->second.MailCount = count;
|
||||
itr->second.MailCount = static_cast<uint16>(std::clamp<int32>(count, 0, maxCount));
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's be safe and prevent overflow
|
||||
if (!itr->second.MailCount && count < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int32 newCount = static_cast<int32>(itr->second.MailCount) + count;
|
||||
if (newCount < 0)
|
||||
LOG_WARN("entities.player", "CharacterCache::UpdateCharacterMailCount: mail count for {} would go negative ({}), a mail insert was not reported; clamping to 0", guid.ToString(), newCount);
|
||||
|
||||
itr->second.MailCount += count;
|
||||
itr->second.MailCount = static_cast<uint16>(std::clamp(newCount, 0, maxCount));
|
||||
}
|
||||
|
||||
void CharacterCache::UpdateCharacterGroup(ObjectGuid const& guid, ObjectGuid groupGUID)
|
||||
|
||||
@@ -33,7 +33,7 @@ struct CharacterCacheEntry
|
||||
uint8 Race;
|
||||
uint8 Sex;
|
||||
uint8 Level;
|
||||
uint8 MailCount;
|
||||
uint16 MailCount;
|
||||
ObjectGuid::LowType GuildId;
|
||||
std::array<uint32, MAX_ARENA_SLOT> ArenaTeamId;
|
||||
ObjectGuid GroupGuid;
|
||||
@@ -58,10 +58,6 @@ class AC_GAME_API CharacterCache
|
||||
void UpdateCharacterGuildId(ObjectGuid const& guid, ObjectGuid::LowType guildId);
|
||||
void UpdateCharacterArenaTeamId(ObjectGuid const& guid, uint8 slot, uint32 arenaTeamId);
|
||||
|
||||
void UpdateCharacterMailCount(ObjectGuid const& guid, int8 count, bool update = false);
|
||||
void DecreaseCharacterMailCount(ObjectGuid const& guid) { UpdateCharacterMailCount(guid, -1); };
|
||||
void IncreaseCharacterMailCount(ObjectGuid const& guid) { UpdateCharacterMailCount(guid, 1); };
|
||||
|
||||
[[nodiscard]] bool HasCharacterCacheEntry(ObjectGuid const& guid) const;
|
||||
[[nodiscard]] CharacterCacheEntry const* GetCharacterCacheByGuid(ObjectGuid const& guid) const;
|
||||
[[nodiscard]] CharacterCacheEntry const* GetCharacterCacheByName(std::string const& name) const;
|
||||
@@ -78,6 +74,15 @@ class AC_GAME_API CharacterCache
|
||||
[[nodiscard]] ObjectGuid::LowType GetCharacterGuildIdByGuid(ObjectGuid guid) const;
|
||||
[[nodiscard]] uint32 GetCharacterArenaTeamIdByGuid(ObjectGuid guid, uint8 type) const;
|
||||
[[nodiscard]] ObjectGuid GetCharacterGroupGuidByGuid(ObjectGuid guid) const;
|
||||
|
||||
private:
|
||||
// Only MailMgr may touch the mail count, so every change is paired
|
||||
// with the matching write to the `mail` table
|
||||
void UpdateCharacterMailCount(ObjectGuid const& guid, int32 count, bool update = false);
|
||||
void DecreaseCharacterMailCount(ObjectGuid const& guid) { UpdateCharacterMailCount(guid, -1); }
|
||||
void IncreaseCharacterMailCount(ObjectGuid const& guid) { UpdateCharacterMailCount(guid, 1); }
|
||||
|
||||
friend class MailMgr;
|
||||
};
|
||||
|
||||
#define sCharacterCache CharacterCache::instance()
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "LootItemStorage.h"
|
||||
#include "MailMgr.h"
|
||||
#include "MapMgr.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
@@ -6298,10 +6299,21 @@ void Player::_LoadMail(PreparedQueryResult mailsResult, PreparedQueryResult mail
|
||||
m->checked = fields[10].Get<uint8>();
|
||||
m->stationery = fields[11].Get<uint8>();
|
||||
m->mailTemplateId = fields[12].Get<int16>();
|
||||
bool has_items = fields[13].Get<bool>();
|
||||
|
||||
if (cur_time > m->expire_time)
|
||||
{
|
||||
LOG_DEBUG("entities.player", "Player::_LoadMail: Mail ({}) has expired - ignored.", m->messageID);
|
||||
// Drop empty expired mail now; mail with items or money is left
|
||||
// for ReturnOrDeleteOldMails, which owns return-to-sender handling
|
||||
if (!has_items && !m->money && !m->COD)
|
||||
{
|
||||
LOG_DEBUG("entities.player", "Player::_LoadMail: Mail ({}) has expired - deleted.", m->messageID);
|
||||
sMailMgr->DeleteEmptyExpiredMail(m->messageID, GetGUID().GetCounter());
|
||||
}
|
||||
else
|
||||
LOG_DEBUG("entities.player", "Player::_LoadMail: Mail ({}) has expired - ignored.", m->messageID);
|
||||
|
||||
delete m;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -6804,129 +6804,6 @@ void ObjectMgr::LoadNpcTextLocales()
|
||||
LOG_INFO("server.loading", ">> Loaded {} Npc Text Locale Strings in {} ms", (uint32)_npcTextLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
|
||||
void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
time_t curTime = GameTime::GetGameTime().count();
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL);
|
||||
stmt->SetData(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->SetData(0, uint32(curTime));
|
||||
if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
|
||||
{
|
||||
MailItemInfo item;
|
||||
do
|
||||
{
|
||||
Field* fields = items->Fetch();
|
||||
item.item_guid = fields[0].Get<uint32>();
|
||||
item.item_template = fields[1].Get<uint32>();
|
||||
uint32 mailId = fields[2].Get<uint32>();
|
||||
itemsCache[mailId].push_back(item);
|
||||
} while (items->NextRow());
|
||||
}
|
||||
|
||||
uint32 deletedCount = 0;
|
||||
uint32 returnedCount = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
Mail* m = new Mail;
|
||||
m->messageID = fields[0].Get<uint32>();
|
||||
m->messageType = fields[1].Get<uint8>();
|
||||
m->sender = fields[2].Get<uint32>();
|
||||
m->receiver = fields[3].Get<uint32>();
|
||||
bool has_items = fields[4].Get<bool>();
|
||||
m->expire_time = time_t(fields[5].Get<uint32>());
|
||||
m->deliver_time = time_t(0);
|
||||
m->stationery = fields[6].Get<uint8>();
|
||||
m->checked = fields[7].Get<uint8>();
|
||||
m->mailTemplateId = fields[8].Get<int16>();
|
||||
|
||||
Player* player = nullptr;
|
||||
if (serverUp)
|
||||
player = ObjectAccessor::FindPlayerByLowGUID(m->receiver);
|
||||
|
||||
if (player) // don't modify mails of a logged in player
|
||||
{
|
||||
delete m;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Delete or return mail
|
||||
if (has_items)
|
||||
{
|
||||
// read items from cache
|
||||
m->items.swap(itemsCache[m->messageID]);
|
||||
|
||||
// If it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
|
||||
if (!m->IsSentByPlayer() || m->IsSentByGM() || (m->IsCODPayment() || m->IsReturnedMail()))
|
||||
{
|
||||
for (auto const& mailedItem : m->items)
|
||||
{
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
|
||||
stmt->SetData(0, mailedItem.item_guid);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
|
||||
stmt->SetData(0, m->messageID);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mail will be returned
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
|
||||
stmt->SetData(0, m->receiver);
|
||||
stmt->SetData(1, m->sender);
|
||||
stmt->SetData(2, uint32(curTime + 30 * DAY));
|
||||
stmt->SetData(3, uint32(curTime));
|
||||
stmt->SetData (4, uint8(MAIL_CHECK_MASK_RETURNED));
|
||||
stmt->SetData(5, m->messageID);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
for (auto const& mailedItem : m->items)
|
||||
{
|
||||
// Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
|
||||
stmt->SetData(0, m->sender);
|
||||
stmt->SetData(1, mailedItem.item_guid);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
|
||||
stmt->SetData(0, m->sender);
|
||||
stmt->SetData(1, mailedItem.item_guid);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
// xinef: update global data
|
||||
sCharacterCache->IncreaseCharacterMailCount(ObjectGuid(HighGuid::Player, m->sender));
|
||||
sCharacterCache->DecreaseCharacterMailCount(ObjectGuid(HighGuid::Player, m->receiver));
|
||||
|
||||
delete m;
|
||||
++returnedCount;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
sCharacterCache->DecreaseCharacterMailCount(ObjectGuid(HighGuid::Player, m->receiver));
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
|
||||
stmt->SetData(0, m->messageID);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
delete m;
|
||||
++deletedCount;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Processed {} expired mails: {} deleted and {} returned in {} ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
|
||||
void ObjectMgr::LoadQuestAreaTriggers()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
@@ -1129,8 +1129,6 @@ public:
|
||||
return itr != _fishingBaseForAreaStore.end() ? itr->second : 0;
|
||||
}
|
||||
|
||||
void ReturnOrDeleteOldMails(bool serverUp);
|
||||
|
||||
CreatureBaseStats const* GetCreatureBaseStats(uint8 level, uint8 unitClass);
|
||||
|
||||
void SetHighestGuids();
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "Language.h"
|
||||
#include "Log.h"
|
||||
#include "Mail.h"
|
||||
#include "MailMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Opcodes.h"
|
||||
#include "Player.h"
|
||||
@@ -416,7 +417,7 @@ void WorldSession::HandleMailDelete(WorldPacket& recvData)
|
||||
Mail* m = _player->GetMail(mailId);
|
||||
Player* player = _player;
|
||||
player->m_mailsUpdated = true;
|
||||
if (m)
|
||||
if (m && m->state != MAIL_STATE_DELETED)
|
||||
{
|
||||
// delete shouldn't show up for COD mails
|
||||
if (m->COD)
|
||||
@@ -427,7 +428,7 @@ void WorldSession::HandleMailDelete(WorldPacket& recvData)
|
||||
|
||||
m->state = MAIL_STATE_DELETED;
|
||||
|
||||
sCharacterCache->DecreaseCharacterMailCount(player->GetGUID());
|
||||
sMailMgr->OnMailDeleted(player->GetGUID().GetCounter());
|
||||
}
|
||||
player->SendMailResult(mailId, MAIL_DELETED, MAIL_OK);
|
||||
}
|
||||
@@ -509,7 +510,7 @@ void WorldSession::HandleMailReturnToSender(WorldPacket& recvData)
|
||||
delete m; //we can deallocate old mail
|
||||
player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK);
|
||||
|
||||
sCharacterCache->DecreaseCharacterMailCount(player->GetGUID());
|
||||
sMailMgr->OnMailDeleted(player->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
//called when player takes item attached in mail
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "GameTime.h"
|
||||
#include "Item.h"
|
||||
#include "Log.h"
|
||||
#include "MailMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
@@ -251,7 +252,7 @@ void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver cons
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
sCharacterCache->IncreaseCharacterMailCount(ObjectGuid(HighGuid::Player, receiver.GetPlayerGUIDLow()));
|
||||
sMailMgr->OnMailSent(receiver.GetPlayerGUIDLow());
|
||||
|
||||
// For online receiver update in game mail status and data
|
||||
if (pReceiver)
|
||||
|
||||
209
src/server/game/Mails/MailMgr.cpp
Normal file
209
src/server/game/Mails/MailMgr.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "MailMgr.h"
|
||||
#include "CharacterCache.h"
|
||||
#include "Common.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "GameTime.h"
|
||||
#include "Log.h"
|
||||
#include "Mail.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Timer.h"
|
||||
#include <map>
|
||||
|
||||
MailMgr* MailMgr::instance()
|
||||
{
|
||||
static MailMgr instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void MailMgr::OnMailSent(ObjectGuid::LowType receiverLow)
|
||||
{
|
||||
sCharacterCache->IncreaseCharacterMailCount(ObjectGuid(HighGuid::Player, receiverLow));
|
||||
}
|
||||
|
||||
void MailMgr::OnMailDeleted(ObjectGuid::LowType receiverLow)
|
||||
{
|
||||
sCharacterCache->DecreaseCharacterMailCount(ObjectGuid(HighGuid::Player, receiverLow));
|
||||
}
|
||||
|
||||
void MailMgr::OnMailReturned(ObjectGuid::LowType oldReceiverLow, ObjectGuid::LowType newReceiverLow)
|
||||
{
|
||||
sCharacterCache->DecreaseCharacterMailCount(ObjectGuid(HighGuid::Player, oldReceiverLow));
|
||||
sCharacterCache->IncreaseCharacterMailCount(ObjectGuid(HighGuid::Player, newReceiverLow));
|
||||
}
|
||||
|
||||
void MailMgr::LoadMailCounts()
|
||||
{
|
||||
QueryResult result = CharacterDatabase.Query("SELECT receiver, COUNT(receiver) FROM mail GROUP BY receiver");
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
sCharacterCache->UpdateCharacterMailCount(ObjectGuid(HighGuid::Player, fields[0].Get<uint32>()), static_cast<int32>(fields[1].Get<uint64>()), true);
|
||||
} while (result->NextRow());
|
||||
}
|
||||
|
||||
void MailMgr::RecountMailCount(ObjectGuid::LowType receiverLow)
|
||||
{
|
||||
int32 count = 0;
|
||||
if (QueryResult result = CharacterDatabase.Query("SELECT COUNT(*) FROM mail WHERE receiver = {}", receiverLow))
|
||||
count = static_cast<int32>((*result)[0].Get<uint64>());
|
||||
|
||||
sCharacterCache->UpdateCharacterMailCount(ObjectGuid(HighGuid::Player, receiverLow), count, true);
|
||||
}
|
||||
|
||||
void MailMgr::DeleteEmptyExpiredMail(uint32 mailId, ObjectGuid::LowType receiverLow)
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
|
||||
stmt->SetData(0, mailId);
|
||||
CharacterDatabase.Execute(stmt);
|
||||
|
||||
OnMailDeleted(receiverLow);
|
||||
}
|
||||
|
||||
void MailMgr::ReturnOrDeleteOldMails(bool serverUp)
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
time_t curTime = GameTime::GetGameTime().count();
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL);
|
||||
stmt->SetData(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->SetData(0, uint32(curTime));
|
||||
if (PreparedQueryResult items = CharacterDatabase.Query(stmt))
|
||||
{
|
||||
MailItemInfo item;
|
||||
do
|
||||
{
|
||||
Field* fields = items->Fetch();
|
||||
item.item_guid = fields[0].Get<uint32>();
|
||||
item.item_template = fields[1].Get<uint32>();
|
||||
uint32 mailId = fields[2].Get<uint32>();
|
||||
itemsCache[mailId].push_back(item);
|
||||
} while (items->NextRow());
|
||||
}
|
||||
|
||||
uint32 deletedCount = 0;
|
||||
uint32 returnedCount = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
Mail* m = new Mail;
|
||||
m->messageID = fields[0].Get<uint32>();
|
||||
m->messageType = fields[1].Get<uint8>();
|
||||
m->sender = fields[2].Get<uint32>();
|
||||
m->receiver = fields[3].Get<uint32>();
|
||||
bool has_items = fields[4].Get<bool>();
|
||||
m->expire_time = time_t(fields[5].Get<uint32>());
|
||||
m->deliver_time = time_t(0);
|
||||
m->stationery = fields[6].Get<uint8>();
|
||||
m->checked = fields[7].Get<uint8>();
|
||||
m->mailTemplateId = fields[8].Get<int16>();
|
||||
|
||||
Player* player = nullptr;
|
||||
if (serverUp)
|
||||
player = ObjectAccessor::FindPlayerByLowGUID(m->receiver);
|
||||
|
||||
if (player) // don't modify mails of a logged in player
|
||||
{
|
||||
delete m;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Keep each mail's correlated writes atomic
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
// Delete or return mail
|
||||
if (has_items)
|
||||
{
|
||||
// read items from cache
|
||||
m->items.swap(itemsCache[m->messageID]);
|
||||
|
||||
// If it is mail from non-player, or if it's already return mail, it shouldn't be returned, but deleted
|
||||
if (!m->IsSentByPlayer() || m->IsSentByGM() || (m->IsCODPayment() || m->IsReturnedMail()))
|
||||
{
|
||||
for (auto const& mailedItem : m->items)
|
||||
{
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
|
||||
stmt->SetData(0, mailedItem.item_guid);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID);
|
||||
stmt->SetData(0, m->messageID);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Mail will be returned
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_RETURNED);
|
||||
stmt->SetData(0, m->receiver);
|
||||
stmt->SetData(1, m->sender);
|
||||
stmt->SetData(2, uint32(curTime + 30 * DAY));
|
||||
stmt->SetData(3, uint32(curTime));
|
||||
stmt->SetData(4, uint8(MAIL_CHECK_MASK_RETURNED));
|
||||
stmt->SetData(5, m->messageID);
|
||||
trans->Append(stmt);
|
||||
for (auto const& mailedItem : m->items)
|
||||
{
|
||||
// Update receiver in mail items for its proper delivery, and in instance_item for avoid lost item at sender delete
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_MAIL_ITEM_RECEIVER);
|
||||
stmt->SetData(0, m->sender);
|
||||
stmt->SetData(1, mailedItem.item_guid);
|
||||
trans->Append(stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
|
||||
stmt->SetData(0, m->sender);
|
||||
stmt->SetData(1, mailedItem.item_guid);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
OnMailReturned(m->receiver, m->sender);
|
||||
|
||||
delete m;
|
||||
++returnedCount;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
|
||||
stmt->SetData(0, m->messageID);
|
||||
trans->Append(stmt);
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
OnMailDeleted(m->receiver);
|
||||
|
||||
delete m;
|
||||
++deletedCount;
|
||||
} while (result->NextRow());
|
||||
|
||||
LOG_INFO("server.loading", ">> Processed {} expired mails: {} deleted and {} returned in {} ms", deletedCount + returnedCount, deletedCount, returnedCount, GetMSTimeDiffToNow(oldMSTime));
|
||||
LOG_INFO("server.loading", " ");
|
||||
}
|
||||
87
src/server/game/Mails/MailMgr.h
Normal file
87
src/server/game/Mails/MailMgr.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 _MAILMGR_H
|
||||
#define _MAILMGR_H
|
||||
|
||||
#include "Define.h"
|
||||
#include "ObjectGuid.h"
|
||||
|
||||
/**
|
||||
* @brief Owns the mail lifecycle bookkeeping that lives outside a single player
|
||||
* session: the per-character mail count mirrored in CharacterCache and the
|
||||
* cleanup of expired mail.
|
||||
*
|
||||
* Every code path that inserts or deletes a row in the characters `mail` table
|
||||
* must report it here, otherwise the cached count drifts until the next recount.
|
||||
*/
|
||||
class AC_GAME_API MailMgr
|
||||
{
|
||||
public:
|
||||
static MailMgr* instance();
|
||||
|
||||
/**
|
||||
* @brief Reports a mail row inserted for a character.
|
||||
* @param receiverLow Low GUID of the mail receiver
|
||||
*/
|
||||
void OnMailSent(ObjectGuid::LowType receiverLow);
|
||||
|
||||
/**
|
||||
* @brief Reports a mail row deleted from a character's mailbox.
|
||||
* @param receiverLow Low GUID of the mail receiver
|
||||
*/
|
||||
void OnMailDeleted(ObjectGuid::LowType receiverLow);
|
||||
|
||||
/**
|
||||
* @brief Reports a mail row handed to a new receiver (return to sender).
|
||||
* @param oldReceiverLow Low GUID of the previous receiver
|
||||
* @param newReceiverLow Low GUID of the new receiver
|
||||
*/
|
||||
void OnMailReturned(ObjectGuid::LowType oldReceiverLow, ObjectGuid::LowType newReceiverLow);
|
||||
|
||||
/**
|
||||
* @brief Recounts the mail of all characters from the database.
|
||||
* Called once at startup after the character cache is filled.
|
||||
*/
|
||||
void LoadMailCounts();
|
||||
|
||||
/**
|
||||
* @brief Recounts one character's mail from the database, overwriting the
|
||||
* cached value.
|
||||
* @param receiverLow Low GUID of the character to recount
|
||||
*/
|
||||
void RecountMailCount(ObjectGuid::LowType receiverLow);
|
||||
|
||||
/**
|
||||
* @brief Deletes an expired mail row that has no items, money or COD
|
||||
* attached. Used at login for mail that would otherwise stay invisible in
|
||||
* the DB until ReturnOrDeleteOldMails catches the receiver offline.
|
||||
* @param mailId Id of the mail row to delete
|
||||
* @param receiverLow Low GUID of the mail receiver
|
||||
*/
|
||||
void DeleteEmptyExpiredMail(uint32 mailId, ObjectGuid::LowType receiverLow);
|
||||
|
||||
/**
|
||||
* @brief Returns expired mail with items to the sender and deletes the rest.
|
||||
* @param serverUp When true, receivers that are currently online are skipped.
|
||||
*/
|
||||
void ReturnOrDeleteOldMails(bool serverUp);
|
||||
};
|
||||
|
||||
#define sMailMgr MailMgr::instance()
|
||||
|
||||
#endif
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "LootItemStorage.h"
|
||||
#include "LootMgr.h"
|
||||
#include "M2Stores.h"
|
||||
#include "MailMgr.h"
|
||||
#include "MapMgr.h"
|
||||
#include "Metric.h"
|
||||
#include "MotdMgr.h"
|
||||
@@ -847,7 +848,7 @@ void World::SetInitialWorldSettings()
|
||||
///- Handle outdated emails (delete/return)
|
||||
LOG_INFO("server.loading", "Returning Old Mails...");
|
||||
LOG_INFO("server.loading", " ");
|
||||
sObjectMgr->ReturnOrDeleteOldMails(false);
|
||||
sMailMgr->ReturnOrDeleteOldMails(false);
|
||||
|
||||
///- Load AutoBroadCast
|
||||
LOG_INFO("server.loading", "Loading Autobroadcasts...");
|
||||
@@ -1196,7 +1197,7 @@ void World::Update(uint32 diff)
|
||||
|
||||
if (currentGameTime > _mail_expire_check_timer)
|
||||
{
|
||||
sObjectMgr->ReturnOrDeleteOldMails(true);
|
||||
sMailMgr->ReturnOrDeleteOldMails(true);
|
||||
_mail_expire_check_timer = currentGameTime + 6h;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "CommandScript.h"
|
||||
#include "Group.h"
|
||||
#include "Language.h"
|
||||
#include "MailMgr.h"
|
||||
#include "Player.h"
|
||||
#include "RBAC.h"
|
||||
|
||||
@@ -122,7 +123,7 @@ public:
|
||||
|
||||
sCharacterCache->UpdateCharacterAccountId(cPlayer->GetGUID(), cPlayer->GetSession()->GetAccountId());
|
||||
sCharacterCache->UpdateCharacterGuildId(cPlayer->GetGUID(), cPlayer->GetGuildId());
|
||||
sCharacterCache->UpdateCharacterMailCount(cPlayer->GetGUID(), cPlayer->GetMailSize(), true);
|
||||
sMailMgr->RecountMailCount(cPlayer->GetGUID().GetCounter());
|
||||
sCharacterCache->UpdateCharacterArenaTeamId(cPlayer->GetGUID(), ARENA_SLOT_2v2, cPlayer->GetArenaTeamId(ARENA_SLOT_2v2));
|
||||
sCharacterCache->UpdateCharacterArenaTeamId(cPlayer->GetGUID(), ARENA_SLOT_3v3, cPlayer->GetArenaTeamId(ARENA_SLOT_3v3));
|
||||
sCharacterCache->UpdateCharacterArenaTeamId(cPlayer->GetGUID(), ARENA_SLOT_5v5, cPlayer->GetArenaTeamId(ARENA_SLOT_5v5));
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "GameTime.h"
|
||||
#include "Language.h"
|
||||
#include "Mail.h"
|
||||
#include "MailMgr.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "Player.h"
|
||||
@@ -391,7 +392,7 @@ public:
|
||||
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
sCharacterCache->DecreaseCharacterMailCount(ObjectGuid(HighGuid::Player, receiver));
|
||||
sMailMgr->OnMailDeleted(receiver);
|
||||
|
||||
handler->PSendSysMessage(LANG_MAIL_RETURN_SUCCESS, mailId, handler->playerLink(target.GetName()));
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user