Core/Mail: Replaced blocking db query in mail sending with async version

Port From (https://github.com/TrinityCore/TrinityCore/commit/081fa7cfc4d044d47e719d0a6880d2a9b6d30128)
This commit is contained in:
hondacrx
2022-05-24 11:09:49 -04:00
parent 334ea523c3
commit ba71bd81b4
3 changed files with 243 additions and 244 deletions
@@ -161,6 +161,13 @@ namespace Game
return 0; return 0;
} }
public QueryCallback GetIdByGameAccountAsync(uint gameAccountId)
{
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT);
stmt.AddValue(0, gameAccountId);
return DB.Login.AsyncQuery(stmt);
}
public byte GetMaxIndex(uint accountId) public byte GetMaxIndex(uint accountId)
{ {
PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_MAX_ACCOUNT_INDEX); PreparedStatement stmt = DB.Login.GetPreparedStatement(LoginStatements.SEL_BNET_MAX_ACCOUNT_INDEX);
+1
View File
@@ -2761,6 +2761,7 @@ namespace Game.Entities
public Item GetMItem(ulong id) { return mMitems.LookupByKey(id); } public Item GetMItem(ulong id) { return mMitems.LookupByKey(id); }
public Mail GetMail(uint id) { return m_mail.Find(p => p.messageID == id); } public Mail GetMail(uint id) { return m_mail.Find(p => p.messageID == id); }
public List<Mail> GetMails() { return m_mail; } public List<Mail> GetMails() { return m_mail; }
public uint GetMailSize() { return (uint)m_mail.Count; }
//Binds //Binds
public bool HasPendingBind() { return _pendingBindId > 0; } public bool HasPendingBind() { return _pendingBindId > 0; }
+235 -244
View File
@@ -59,21 +59,21 @@ namespace Game
} }
[WorldPacketHandler(ClientOpcodes.SendMail)] [WorldPacketHandler(ClientOpcodes.SendMail)]
void HandleSendMail(SendMail packet) void HandleSendMail(SendMail sendMail)
{ {
if (packet.Info.Attachments.Count > SharedConst.MaxClientMailItems) // client limit if (sendMail.Info.Attachments.Count > SharedConst.MaxClientMailItems) // client limit
{ {
GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.TooManyAttachments); GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.TooManyAttachments);
return; return;
} }
if (!CanOpenMailBox(packet.Info.Mailbox)) if (!CanOpenMailBox(sendMail.Info.Mailbox))
return; return;
if (string.IsNullOrEmpty(packet.Info.Target)) if (string.IsNullOrEmpty(sendMail.Info.Target))
return; return;
if (!ValidateHyperlinksAndMaybeKick(packet.Info.Subject) || !ValidateHyperlinksAndMaybeKick(packet.Info.Body)) if (!ValidateHyperlinksAndMaybeKick(sendMail.Info.Subject) || !ValidateHyperlinksAndMaybeKick(sendMail.Info.Body))
return; return;
Player player = GetPlayer(); Player player = GetPlayer();
@@ -84,39 +84,39 @@ namespace Game
} }
ObjectGuid receiverGuid = ObjectGuid.Empty; ObjectGuid receiverGuid = ObjectGuid.Empty;
if (ObjectManager.NormalizePlayerName(ref packet.Info.Target)) if (ObjectManager.NormalizePlayerName(ref sendMail.Info.Target))
receiverGuid = Global.CharacterCacheStorage.GetCharacterGuidByName(packet.Info.Target); receiverGuid = Global.CharacterCacheStorage.GetCharacterGuidByName(sendMail.Info.Target);
if (receiverGuid.IsEmpty()) if (receiverGuid.IsEmpty())
{ {
Log.outInfo(LogFilter.Network, "Player {0} is sending mail to {1} (GUID: not existed!) with subject {2}" + Log.outInfo(LogFilter.Network, "Player {0} is sending mail to {1} (GUID: not existed!) with subject {2}" +
"and body {3} includes {4} items, {5} copper and {6} COD copper with StationeryID = {7}", "and body {3} includes {4} items, {5} copper and {6} COD copper with StationeryID = {7}",
GetPlayerInfo(), packet.Info.Target, packet.Info.Subject, packet.Info.Body, GetPlayerInfo(), sendMail.Info.Target, sendMail.Info.Subject, sendMail.Info.Body,
packet.Info.Attachments.Count, packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID); sendMail.Info.Attachments.Count, sendMail.Info.SendMoney, sendMail.Info.Cod, sendMail.Info.StationeryID);
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.RecipientNotFound); player.SendMailResult(0, MailResponseType.Send, MailResponseResult.RecipientNotFound);
return; return;
} }
if (packet.Info.SendMoney < 0) if (sendMail.Info.SendMoney < 0)
{ {
GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.InternalError); GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.InternalError);
Log.outWarn(LogFilter.Server, "Player {0} attempted to send mail to {1} ({2}) with negative money value (SendMoney: {3})", Log.outWarn(LogFilter.Server, "Player {0} attempted to send mail to {1} ({2}) with negative money value (SendMoney: {3})",
GetPlayerInfo(), packet.Info.Target, receiverGuid.ToString(), packet.Info.SendMoney); GetPlayerInfo(), sendMail.Info.Target, receiverGuid.ToString(), sendMail.Info.SendMoney);
return; return;
} }
if (packet.Info.Cod < 0) if (sendMail.Info.Cod < 0)
{ {
GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.InternalError); GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.InternalError);
Log.outWarn(LogFilter.Server, "Player {0} attempted to send mail to {1} ({2}) with negative COD value (Cod: {3})", Log.outWarn(LogFilter.Server, "Player {0} attempted to send mail to {1} ({2}) with negative COD value (Cod: {3})",
GetPlayerInfo(), packet.Info.Target, receiverGuid.ToString(), packet.Info.Cod); GetPlayerInfo(), sendMail.Info.Target, receiverGuid.ToString(), sendMail.Info.Cod);
return; return;
} }
Log.outInfo(LogFilter.Network, "Player {0} is sending mail to {1} ({2}) with subject {3} and body {4}" + Log.outInfo(LogFilter.Network, "Player {0} is sending mail to {1} ({2}) with subject {3} and body {4}" +
"includes {5} items, {6} copper and {7} COD copper with StationeryID = {8}", "includes {5} items, {6} copper and {7} COD copper with StationeryID = {8}",
GetPlayerInfo(), packet.Info.Target, receiverGuid.ToString(), packet.Info.Subject, GetPlayerInfo(), sendMail.Info.Target, receiverGuid.ToString(), sendMail.Info.Subject,
packet.Info.Body, packet.Info.Attachments.Count, packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID); sendMail.Info.Body, sendMail.Info.Attachments.Count, sendMail.Info.SendMoney, sendMail.Info.Cod, sendMail.Info.StationeryID);
if (player.GetGUID() == receiverGuid) if (player.GetGUID() == receiverGuid)
{ {
@@ -124,12 +124,12 @@ namespace Game
return; return;
} }
uint cost = (uint)(!packet.Info.Attachments.Empty() ? 30 * packet.Info.Attachments.Count : 30); // price hardcoded in client uint cost = (uint)(!sendMail.Info.Attachments.Empty() ? 30 * sendMail.Info.Attachments.Count : 30); // price hardcoded in client
long reqmoney = cost + packet.Info.SendMoney; long reqmoney = cost + sendMail.Info.SendMoney;
// Check for overflow // Check for overflow
if (reqmoney < packet.Info.SendMoney) if (reqmoney < sendMail.Info.SendMoney)
{ {
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.NotEnoughMoney); player.SendMailResult(0, MailResponseType.Send, MailResponseResult.NotEnoughMoney);
return; return;
@@ -141,207 +141,198 @@ namespace Game
return; return;
} }
Player receiver = Global.ObjAccessor.FindPlayer(receiverGuid); void mailCountCheckContinuation(Team receiverTeam, ulong mailsCount, uint receiverLevel, uint receiverAccountId, uint receiverBnetAccountId)
Team receiverTeam = 0;
byte mailsCount = 0; //do not allow to send to one player more than 100 mails
byte receiverLevel = 0;
uint receiverAccountId = 0;
uint receiverBnetAccountId;
if (receiver)
{ {
receiverTeam = receiver.GetTeam(); if (_player != player)
mailsCount = (byte)receiver.GetMails().Count; return;
receiverLevel = (byte)receiver.GetLevel();
receiverAccountId = receiver.GetSession().GetAccountId(); // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255..
receiverBnetAccountId = receiver.GetSession().GetBattlenetAccountId(); if (mailsCount > 100)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.RecipientCapReached);
return;
}
// test the receiver's Faction... or all items are account bound
bool accountBound = !sendMail.Info.Attachments.Empty();
foreach (var att in sendMail.Info.Attachments)
{
Item item = player.GetItemByGuid(att.ItemGUID);
if (item != null)
{
ItemTemplate itemProto = item.GetTemplate();
if (itemProto == null || !itemProto.HasFlag(ItemFlags.IsBoundToAccount))
{
accountBound = false;
break;
}
}
}
if (!accountBound && player.GetTeam() != receiverTeam && !HasPermission(RBACPermissions.TwoSideInteractionMail))
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.NotYourTeam);
return;
}
if (receiverLevel < WorldConfig.GetIntValue(WorldCfg.MailLevelReq))
{
SendNotification(CypherStrings.MailReceiverReq, WorldConfig.GetIntValue(WorldCfg.MailLevelReq));
return;
}
List<Item> items = new();
foreach (var att in sendMail.Info.Attachments)
{
if (att.ItemGUID.IsEmpty())
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.MailAttachmentInvalid);
return;
}
Item item = player.GetItemByGuid(att.ItemGUID);
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
if (item == null)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.MailAttachmentInvalid);
return;
}
if (!item.CanBeTraded(true))
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.MailBoundItem);
return;
}
if (item.IsBoundAccountWide() && item.IsSoulBound() && player.GetSession().GetAccountId() != receiverAccountId)
{
if (!item.IsBattlenetAccountBound() || player.GetSession().GetBattlenetAccountId() == 0 || player.GetSession().GetBattlenetAccountId() != receiverBnetAccountId)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.NotSameAccount);
return;
}
}
if (item.GetTemplate().HasFlag(ItemFlags.Conjured) || item.m_itemData.Expiration != 0)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.MailBoundItem);
return;
}
if (sendMail.Info.Cod != 0 && item.IsWrapped())
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.CantSendWrappedCod);
return;
}
if (item.IsNotEmptyBag())
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.DestroyNonemptyBag);
return;
}
items.Add(item);
}
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.Ok);
player.ModifyMoney(-reqmoney);
player.UpdateCriteria(CriteriaType.MoneySpentOnPostage, cost);
bool needItemDelay = false;
MailDraft draft = new(sendMail.Info.Subject, sendMail.Info.Body);
SQLTransaction trans = new SQLTransaction();
if (!sendMail.Info.Attachments.Empty() || sendMail.Info.SendMoney > 0)
{
bool log = HasPermission(RBACPermissions.LogGmTrade);
if (!sendMail.Info.Attachments.Empty())
{
foreach (var item in items)
{
if (log)
{
Log.outCommand(GetAccountId(), $"GM {GetPlayerName()} ({_player.GetGUID()}) (Account: {GetAccountId()}) mail item: {item.GetTemplate().GetName()} " +
$"(Entry: {item.GetEntry()} Count: {item.GetCount()}) to: {sendMail.Info.Target} ({receiverGuid}) (Account: {receiverAccountId})");
}
item.SetNotRefundable(GetPlayer()); // makes the item no longer refundable
player.MoveItemFromInventory(item.GetBagSlot(), item.GetSlot(), true);
item.DeleteFromInventoryDB(trans); // deletes item from character's inventory
item.SetOwnerGUID(receiverGuid);
item.SetState(ItemUpdateState.Changed);
item.SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
draft.AddItem(item);
}
// if item send to character at another account, then apply item delivery delay
needItemDelay = player.GetSession().GetAccountId() != receiverAccountId;
}
if (log && sendMail.Info.SendMoney > 0)
Log.outCommand(GetAccountId(), "GM {GetPlayerName()} ({_player.GetGUID()}) (Account: {GetAccountId()}) mail money: {mailInfo.SendMoney} to: {mailInfo.Target} ({receiverGuid}) (Account: {receiverAccountId})");
}
// If theres is an item, there is a one hour delivery delay if sent to another account's character.
uint deliver_delay = needItemDelay ? WorldConfig.GetUIntValue(WorldCfg.MailDeliveryDelay) : 0;
// Mail sent between guild members arrives instantly
Guild guild = Global.GuildMgr.GetGuildById(player.GetGuildId());
if (guild != null)
if (guild.IsMember(receiverGuid))
deliver_delay = 0;
// don't ask for COD if there are no items
if (sendMail.Info.Attachments.Empty())
sendMail.Info.Cod = 0;
// will delete item or place to receiver mail list
draft.AddMoney((ulong)sendMail.Info.SendMoney)
.AddCOD((uint)sendMail.Info.Cod)
.SendMailTo(trans, new MailReceiver(Global.ObjAccessor.FindConnectedPlayer(receiverGuid), receiverGuid.GetCounter()), new MailSender(player), sendMail.Info.Body.IsEmpty() ? MailCheckMask.Copied : MailCheckMask.HasBody, deliver_delay);
player.SaveInventoryAndGoldToDB(trans);
DB.Characters.CommitTransaction(trans);
}
Player receiver = Global.ObjAccessor.FindPlayer(receiverGuid);
if (receiver != null)
{
mailCountCheckContinuation(receiver.GetTeam(), receiver.GetMailSize(), receiver.GetLevel(), receiver.GetSession().GetAccountId(), receiver.GetSession().GetBattlenetAccountId());
} }
else else
{ {
CharacterCacheEntry characterInfo = Global.CharacterCacheStorage.GetCharacterCacheByGuid(receiverGuid);
if (characterInfo != null)
{
receiverTeam = Player.TeamForRace(characterInfo.RaceId);
receiverLevel = characterInfo.Level;
receiverAccountId = characterInfo.AccountId;
}
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_MAIL_COUNT); PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_MAIL_COUNT);
stmt.AddValue(0, receiverGuid.GetCounter()); stmt.AddValue(0, receiverGuid.GetCounter());
SQLResult result = DB.Characters.Query(stmt); GetQueryProcessor().AddCallback(DB.Characters.AsyncQuery(stmt).WithChainingCallback((queryCallback, mailCountResult) =>
if (!result.IsEmpty())
mailsCount = (byte)result.Read<ulong>(0);
receiverBnetAccountId = Global.BNetAccountMgr.GetIdByGameAccount(receiverAccountId);
}
// do not allow to have more than 100 mails in mailbox.. mails count is in opcode byte!!! - so max can be 255..
if (mailsCount > 100)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.RecipientCapReached);
return;
}
// test the receiver's Faction... or all items are account bound
bool accountBound = !packet.Info.Attachments.Empty();
foreach (var att in packet.Info.Attachments)
{
Item item = player.GetItemByGuid(att.ItemGUID);
if (item)
{ {
ItemTemplate itemProto = item.GetTemplate(); CharacterCacheEntry characterInfo = Global.CharacterCacheStorage.GetCharacterCacheByGuid(receiverGuid);
if (itemProto == null || !itemProto.HasFlag(ItemFlags.IsBoundToAccount)) if (characterInfo != null)
{ queryCallback.WithCallback(bnetAccountResult =>
accountBound = false;
break;
}
}
}
if (!accountBound && player.GetTeam() != receiverTeam && !HasPermission(RBACPermissions.TwoSideInteractionMail))
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.NotYourTeam);
return;
}
if (receiverLevel < WorldConfig.GetIntValue(WorldCfg.MailLevelReq))
{
SendNotification(CypherStrings.MailReceiverReq, WorldConfig.GetIntValue(WorldCfg.MailLevelReq));
return;
}
List<Item> items = new();
foreach (var att in packet.Info.Attachments)
{
if (att.ItemGUID.IsEmpty())
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.MailAttachmentInvalid);
return;
}
Item item = player.GetItemByGuid(att.ItemGUID);
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
if (!item)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.MailAttachmentInvalid);
return;
}
if (!item.CanBeTraded(true))
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.MailBoundItem);
return;
}
if (item.IsBoundAccountWide() && item.IsSoulBound() && player.GetSession().GetAccountId() != receiverAccountId)
{
if (!item.IsBattlenetAccountBound() || player.GetSession().GetBattlenetAccountId() == 0 || player.GetSession().GetBattlenetAccountId() != receiverBnetAccountId)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.NotSameAccount);
return;
}
}
if (item.GetTemplate().HasFlag(ItemFlags.Conjured) || item.m_itemData.Expiration != 0)
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.MailBoundItem);
return;
}
if (packet.Info.Cod != 0 && item.IsWrapped())
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.CantSendWrappedCod);
return;
}
if (item.IsNotEmptyBag())
{
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.DestroyNonemptyBag);
return;
}
items.Add(item);
}
player.SendMailResult(0, MailResponseType.Send, MailResponseResult.Ok);
player.ModifyMoney(-reqmoney);
player.UpdateCriteria(CriteriaType.MoneySpentOnPostage, cost);
bool needItemDelay = false;
MailDraft draft = new(packet.Info.Subject, packet.Info.Body);
SQLTransaction trans = new();
if (!packet.Info.Attachments.Empty() || packet.Info.SendMoney > 0)
{
bool log = HasPermission(RBACPermissions.LogGmTrade);
if (!packet.Info.Attachments.Empty())
{
foreach (var item in items)
{
if (log)
{ {
Log.outCommand(GetAccountId(), "GM {0} ({1}) (Account: {2}) mail item: {3} (Entry: {4} Count: {5}) to player: {6} ({7}) (Account: {8})", mailCountCheckContinuation(Player.TeamForRace(characterInfo.RaceId), !mailCountResult.IsEmpty() ? mailCountResult.Read<ulong>(0) : 0,
GetPlayerName(), GetPlayer().GetGUID().ToString(), GetAccountId(), item.GetTemplate().GetName(), item.GetEntry(), item.GetCount(), characterInfo.Level, characterInfo.AccountId, !bnetAccountResult.IsEmpty() ? bnetAccountResult.Read<uint>(0) : 0);
packet.Info.Target, receiverGuid.ToString(), receiverAccountId); }).SetNextQuery(Global.BNetAccountMgr.GetIdByGameAccountAsync(characterInfo.AccountId));
} }));
item.SetNotRefundable(GetPlayer()); // makes the item no longer refundable
player.MoveItemFromInventory(item.GetBagSlot(), item.GetSlot(), true);
item.DeleteFromInventoryDB(trans); // deletes item from character's inventory
item.SetOwnerGUID(receiverGuid);
item.SetState(ItemUpdateState.Changed);
item.SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
draft.AddItem(item);
}
// if item send to character at another account, then apply item delivery delay
needItemDelay = player.GetSession().GetAccountId() != receiverAccountId;
}
if (log && packet.Info.SendMoney > 0)
{
Log.outCommand(GetAccountId(), "GM {0} ({1}) (Account: {2}) mail money: {3} to player: {4} ({5}) (Account: {6})",
GetPlayerName(), GetPlayer().GetGUID().ToString(), GetAccountId(), packet.Info.SendMoney, packet.Info.Target, receiverGuid.ToString(), receiverAccountId);
}
} }
// If theres is an item, there is a one hour delivery delay if sent to another account's character.
uint deliver_delay = needItemDelay ? WorldConfig.GetUIntValue(WorldCfg.MailDeliveryDelay) : 0;
// Mail sent between guild members arrives instantly
Guild guild = Global.GuildMgr.GetGuildById(player.GetGuildId());
if (guild)
if (guild.IsMember(receiverGuid))
deliver_delay = 0;
// don't ask for COD if there are no items
if (packet.Info.Attachments.Empty())
packet.Info.Cod = 0;
// will delete item or place to receiver mail list
draft.AddMoney((ulong)packet.Info.SendMoney).AddCOD((uint)packet.Info.Cod).SendMailTo(trans, new MailReceiver(receiver, receiverGuid.GetCounter()), new MailSender(player), string.IsNullOrEmpty(packet.Info.Body) ? MailCheckMask.Copied : MailCheckMask.HasBody, deliver_delay);
player.SaveInventoryAndGoldToDB(trans);
DB.Characters.CommitTransaction(trans);
} }
//called when mail is read //called when mail is read
[WorldPacketHandler(ClientOpcodes.MailMarkAsRead)] [WorldPacketHandler(ClientOpcodes.MailMarkAsRead)]
void HandleMailMarkAsRead(MailMarkAsRead packet) void HandleMailMarkAsRead(MailMarkAsRead markAsRead)
{ {
if (!CanOpenMailBox(packet.Mailbox)) if (!CanOpenMailBox(markAsRead.Mailbox))
return; return;
Player player = GetPlayer(); Player player = GetPlayer();
Mail m = player.GetMail(packet.MailID); Mail m = player.GetMail(markAsRead.MailID);
if (m != null && m.state != MailState.Deleted) if (m != null && m.state != MailState.Deleted)
{ {
if (player.unReadMails != 0) if (player.unReadMails != 0)
@@ -354,9 +345,9 @@ namespace Game
//called when client deletes mail //called when client deletes mail
[WorldPacketHandler(ClientOpcodes.MailDelete)] [WorldPacketHandler(ClientOpcodes.MailDelete)]
void HandleMailDelete(MailDelete packet) void HandleMailDelete(MailDelete mailDelete)
{ {
Mail m = GetPlayer().GetMail(packet.MailID); Mail m = GetPlayer().GetMail(mailDelete.MailID);
Player player = GetPlayer(); Player player = GetPlayer();
player.m_mailsUpdated = true; player.m_mailsUpdated = true;
if (m != null) if (m != null)
@@ -364,40 +355,40 @@ namespace Game
// delete shouldn't show up for COD mails // delete shouldn't show up for COD mails
if (m.COD != 0) if (m.COD != 0)
{ {
player.SendMailResult(packet.MailID, MailResponseType.Deleted, MailResponseResult.InternalError); player.SendMailResult(mailDelete.MailID, MailResponseType.Deleted, MailResponseResult.InternalError);
return; return;
} }
m.state = MailState.Deleted; m.state = MailState.Deleted;
} }
player.SendMailResult(packet.MailID, MailResponseType.Deleted, MailResponseResult.Ok); player.SendMailResult(mailDelete.MailID, MailResponseType.Deleted, MailResponseResult.Ok);
} }
[WorldPacketHandler(ClientOpcodes.MailReturnToSender)] [WorldPacketHandler(ClientOpcodes.MailReturnToSender)]
void HandleMailReturnToSender(MailReturnToSender packet) void HandleMailReturnToSender(MailReturnToSender returnToSender)
{ {
if (!CanOpenMailBox(_player.PlayerTalkClass.GetInteractionData().SourceGuid)) if (!CanOpenMailBox(_player.PlayerTalkClass.GetInteractionData().SourceGuid))
return; return;
Player player = GetPlayer(); Player player = GetPlayer();
Mail m = player.GetMail(packet.MailID); Mail m = player.GetMail(returnToSender.MailID);
if (m == null || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime() || m.sender != packet.SenderGUID.GetCounter()) if (m == null || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime() || m.sender != returnToSender.SenderGUID.GetCounter())
{ {
player.SendMailResult(packet.MailID, MailResponseType.ReturnedToSender, MailResponseResult.InternalError); player.SendMailResult(returnToSender.MailID, MailResponseType.ReturnedToSender, MailResponseResult.InternalError);
return; return;
} }
//we can return mail now, so firstly delete the old one //we can return mail now, so firstly delete the old one
SQLTransaction trans = new(); SQLTransaction trans = new();
PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_MAIL_BY_ID); PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_MAIL_BY_ID);
stmt.AddValue(0, packet.MailID); stmt.AddValue(0, returnToSender.MailID);
trans.Append(stmt); trans.Append(stmt);
stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_MAIL_ITEM_BY_ID); stmt = DB.Characters.GetPreparedStatement(CharStatements.DEL_MAIL_ITEM_BY_ID);
stmt.AddValue(0, packet.MailID); stmt.AddValue(0, returnToSender.MailID);
trans.Append(stmt); trans.Append(stmt);
player.RemoveMail(packet.MailID); player.RemoveMail(returnToSender.MailID);
// only return mail if the player exists (and delete if not existing) // only return mail if the player exists (and delete if not existing)
if (m.messageType == MailMessageType.Normal && m.sender != 0) if (m.messageType == MailMessageType.Normal && m.sender != 0)
@@ -421,50 +412,50 @@ namespace Game
DB.Characters.CommitTransaction(trans); DB.Characters.CommitTransaction(trans);
player.SendMailResult(packet.MailID, MailResponseType.ReturnedToSender, MailResponseResult.Ok); player.SendMailResult(returnToSender.MailID, MailResponseType.ReturnedToSender, MailResponseResult.Ok);
} }
//called when player takes item attached in mail //called when player takes item attached in mail
[WorldPacketHandler(ClientOpcodes.MailTakeItem)] [WorldPacketHandler(ClientOpcodes.MailTakeItem)]
void HandleMailTakeItem(MailTakeItem packet) void HandleMailTakeItem(MailTakeItem takeItem)
{ {
uint AttachID = packet.AttachID; uint AttachID = takeItem.AttachID;
if (!CanOpenMailBox(packet.Mailbox)) if (!CanOpenMailBox(takeItem.Mailbox))
return; return;
Player player = GetPlayer(); Player player = GetPlayer();
Mail m = player.GetMail(packet.MailID); Mail m = player.GetMail(takeItem.MailID);
if (m == null || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime()) if (m == null || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime())
{ {
player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.InternalError); player.SendMailResult(takeItem.MailID, MailResponseType.ItemTaken, MailResponseResult.InternalError);
return; return;
} }
// verify that the mail has the item to avoid cheaters taking COD items without paying // verify that the mail has the item to avoid cheaters taking COD items without paying
if (!m.items.Any(p => p.item_guid == AttachID)) if (!m.items.Any(p => p.item_guid == AttachID))
{ {
player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.InternalError); player.SendMailResult(takeItem.MailID, MailResponseType.ItemTaken, MailResponseResult.InternalError);
return; return;
} }
// prevent cheating with skip client money check // prevent cheating with skip client money check
if (!player.HasEnoughMoney(m.COD)) if (!player.HasEnoughMoney(m.COD))
{ {
player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.NotEnoughMoney); player.SendMailResult(takeItem.MailID, MailResponseType.ItemTaken, MailResponseResult.NotEnoughMoney);
return; return;
} }
Item it = player.GetMItem(packet.AttachID); Item it = player.GetMItem(takeItem.AttachID);
List<ItemPosCount> dest = new(); List<ItemPosCount> dest = new();
InventoryResult msg = GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, dest, it, false); InventoryResult msg = GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, dest, it, false);
if (msg == InventoryResult.Ok) if (msg == InventoryResult.Ok)
{ {
SQLTransaction trans = new(); SQLTransaction trans = new();
m.RemoveItem(packet.AttachID); m.RemoveItem(takeItem.AttachID);
m.removedItems.Add(packet.AttachID); m.removedItems.Add(takeItem.AttachID);
if (m.COD > 0) //if there is COD, take COD money from player and send them to sender by mail if (m.COD > 0) //if there is COD, take COD money from player and send them to sender by mail
{ {
@@ -518,31 +509,31 @@ namespace Game
player._SaveMail(trans); player._SaveMail(trans);
DB.Characters.CommitTransaction(trans); DB.Characters.CommitTransaction(trans);
player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.Ok, 0, packet.AttachID, count); player.SendMailResult(takeItem.MailID, MailResponseType.ItemTaken, MailResponseResult.Ok, 0, takeItem.AttachID, count);
} }
else else
player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.EquipError, msg); player.SendMailResult(takeItem.MailID, MailResponseType.ItemTaken, MailResponseResult.EquipError, msg);
} }
[WorldPacketHandler(ClientOpcodes.MailTakeMoney)] [WorldPacketHandler(ClientOpcodes.MailTakeMoney)]
void HandleMailTakeMoney(MailTakeMoney packet) void HandleMailTakeMoney(MailTakeMoney takeMoney)
{ {
if (!CanOpenMailBox(packet.Mailbox)) if (!CanOpenMailBox(takeMoney.Mailbox))
return; return;
Player player = GetPlayer(); Player player = GetPlayer();
Mail m = player.GetMail(packet.MailID); Mail m = player.GetMail(takeMoney.MailID);
if ((m == null || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime()) || if ((m == null || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime()) ||
(packet.Money > 0 && m.money != (ulong)packet.Money)) (takeMoney.Money > 0 && m.money != (ulong)takeMoney.Money))
{ {
player.SendMailResult(packet.MailID, MailResponseType.MoneyTaken, MailResponseResult.InternalError); player.SendMailResult(takeMoney.MailID, MailResponseType.MoneyTaken, MailResponseResult.InternalError);
return; return;
} }
if (!player.ModifyMoney((long)m.money, false)) if (!player.ModifyMoney((long)m.money, false))
{ {
player.SendMailResult(packet.MailID, MailResponseType.MoneyTaken, MailResponseResult.EquipError, InventoryResult.TooMuchGold); player.SendMailResult(takeMoney.MailID, MailResponseType.MoneyTaken, MailResponseResult.EquipError, InventoryResult.TooMuchGold);
return; return;
} }
@@ -550,7 +541,7 @@ namespace Game
m.state = MailState.Changed; m.state = MailState.Changed;
player.m_mailsUpdated = true; player.m_mailsUpdated = true;
player.SendMailResult(packet.MailID, MailResponseType.MoneyTaken, MailResponseResult.Ok); player.SendMailResult(takeMoney.MailID, MailResponseType.MoneyTaken, MailResponseResult.Ok);
// save money and mail to prevent cheating // save money and mail to prevent cheating
SQLTransaction trans = new(); SQLTransaction trans = new();
@@ -561,9 +552,9 @@ namespace Game
//called when player lists his received mails //called when player lists his received mails
[WorldPacketHandler(ClientOpcodes.MailGetList)] [WorldPacketHandler(ClientOpcodes.MailGetList)]
void HandleGetMailList(MailGetList packet) void HandleGetMailList(MailGetList getList)
{ {
if (!CanOpenMailBox(packet.Mailbox)) if (!CanOpenMailBox(getList.Mailbox))
return; return;
Player player = GetPlayer(); Player player = GetPlayer();
@@ -585,7 +576,7 @@ namespace Game
} }
player.PlayerTalkClass.GetInteractionData().Reset(); player.PlayerTalkClass.GetInteractionData().Reset();
player.PlayerTalkClass.GetInteractionData().SourceGuid = packet.Mailbox; player.PlayerTalkClass.GetInteractionData().SourceGuid = getList.Mailbox;
SendPacket(response); SendPacket(response);
// recalculate m_nextMailDelivereTime and unReadMails // recalculate m_nextMailDelivereTime and unReadMails
@@ -594,17 +585,17 @@ namespace Game
//used when player copies mail body to his inventory //used when player copies mail body to his inventory
[WorldPacketHandler(ClientOpcodes.MailCreateTextItem)] [WorldPacketHandler(ClientOpcodes.MailCreateTextItem)]
void HandleMailCreateTextItem(MailCreateTextItem packet) void HandleMailCreateTextItem(MailCreateTextItem createTextItem)
{ {
if (!CanOpenMailBox(packet.Mailbox)) if (!CanOpenMailBox(createTextItem.Mailbox))
return; return;
Player player = GetPlayer(); Player player = GetPlayer();
Mail m = player.GetMail(packet.MailID); Mail m = player.GetMail(createTextItem.MailID);
if (m == null || (string.IsNullOrEmpty(m.body) && m.mailTemplateId == 0) || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime()) if (m == null || (string.IsNullOrEmpty(m.body) && m.mailTemplateId == 0) || m.state == MailState.Deleted || m.deliver_time > GameTime.GetGameTime())
{ {
player.SendMailResult(packet.MailID, MailResponseType.MadePermanent, MailResponseResult.InternalError); player.SendMailResult(createTextItem.MailID, MailResponseType.MadePermanent, MailResponseResult.InternalError);
return; return;
} }
@@ -618,7 +609,7 @@ namespace Game
MailTemplateRecord mailTemplateEntry = CliDB.MailTemplateStorage.LookupByKey(m.mailTemplateId); MailTemplateRecord mailTemplateEntry = CliDB.MailTemplateStorage.LookupByKey(m.mailTemplateId);
if (mailTemplateEntry == null) if (mailTemplateEntry == null)
{ {
player.SendMailResult(packet.MailID, MailResponseType.MadePermanent, MailResponseResult.InternalError); player.SendMailResult(createTextItem.MailID, MailResponseType.MadePermanent, MailResponseResult.InternalError);
return; return;
} }
@@ -632,7 +623,7 @@ namespace Game
bodyItem.AddItemFlag(ItemFieldFlags.Readable); bodyItem.AddItemFlag(ItemFieldFlags.Readable);
Log.outInfo(LogFilter.Network, "HandleMailCreateTextItem mailid={0}", packet.MailID); Log.outInfo(LogFilter.Network, "HandleMailCreateTextItem mailid={0}", createTextItem.MailID);
List<ItemPosCount> dest = new(); List<ItemPosCount> dest = new();
InventoryResult msg = GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, dest, bodyItem, false); InventoryResult msg = GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, dest, bodyItem, false);
@@ -643,14 +634,14 @@ namespace Game
player.m_mailsUpdated = true; player.m_mailsUpdated = true;
player.StoreItem(dest, bodyItem, true); player.StoreItem(dest, bodyItem, true);
player.SendMailResult(packet.MailID, MailResponseType.MadePermanent, MailResponseResult.Ok); player.SendMailResult(createTextItem.MailID, MailResponseType.MadePermanent, MailResponseResult.Ok);
} }
else else
player.SendMailResult(packet.MailID, MailResponseType.MadePermanent, MailResponseResult.EquipError, msg); player.SendMailResult(createTextItem.MailID, MailResponseType.MadePermanent, MailResponseResult.EquipError, msg);
} }
[WorldPacketHandler(ClientOpcodes.QueryNextMailTime)] [WorldPacketHandler(ClientOpcodes.QueryNextMailTime)]
void HandleQueryNextMailTime(MailQueryNextMailTime packet) void HandleQueryNextMailTime(MailQueryNextMailTime queryNextMailTime)
{ {
MailQueryNextTimeResult result = new(); MailQueryNextTimeResult result = new();