diff options
Diffstat (limited to 'src/server/game/Handlers/MailHandler.cpp')
-rw-r--r-- | src/server/game/Handlers/MailHandler.cpp | 461 |
1 files changed, 134 insertions, 327 deletions
diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index d173b409008..136f998b308 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -24,6 +24,7 @@ #include "World.h" #include "ObjectMgr.h" #include "Player.h" +#include "MailPackets.h" #include "Language.h" #include "DBCStores.h" #include "Item.h" @@ -57,91 +58,18 @@ bool WorldSession::CanOpenMailBox(ObjectGuid guid) return true; } -void WorldSession::HandleSendMail(WorldPacket& recvData) +void WorldSession::HandleSendMail(WorldPackets::Mail::SendMail& packet) { - ObjectGuid mailbox; - uint64 money, COD; - std::string receiverName, subject, body; - uint32 bodyLength, subjectLength, receiverLength; - uint32 package, stationery; - - recvData >> package; - recvData >> stationery; - - recvData >> COD >> money; // money and cod - bodyLength = recvData.ReadBits(12); - subjectLength = recvData.ReadBits(9); - - uint8 items_count = recvData.ReadBits(5); // attached items count - - if (items_count > MAX_MAIL_ITEMS) // client limit + if (packet.Info.Attachments.size() > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); - recvData.rfinish(); // set to end to avoid warnings spam return; } - mailbox[0] = recvData.ReadBit(); - - ObjectGuid itemGUIDs[MAX_MAIL_ITEMS]; - - for (uint8 i = 0; i < items_count; ++i) - { - itemGUIDs[i][2] = recvData.ReadBit(); - itemGUIDs[i][6] = recvData.ReadBit(); - itemGUIDs[i][3] = recvData.ReadBit(); - itemGUIDs[i][7] = recvData.ReadBit(); - itemGUIDs[i][1] = recvData.ReadBit(); - itemGUIDs[i][0] = recvData.ReadBit(); - itemGUIDs[i][4] = recvData.ReadBit(); - itemGUIDs[i][5] = recvData.ReadBit(); - } - - mailbox[3] = recvData.ReadBit(); - mailbox[4] = recvData.ReadBit(); - receiverLength = recvData.ReadBits(7); - mailbox[2] = recvData.ReadBit(); - mailbox[6] = recvData.ReadBit(); - mailbox[1] = recvData.ReadBit(); - mailbox[7] = recvData.ReadBit(); - mailbox[5] = recvData.ReadBit(); - - recvData.ReadByteSeq(mailbox[4]); - - for (uint8 i = 0; i < items_count; ++i) - { - recvData.ReadByteSeq(itemGUIDs[i][6]); - recvData.ReadByteSeq(itemGUIDs[i][1]); - recvData.ReadByteSeq(itemGUIDs[i][7]); - recvData.ReadByteSeq(itemGUIDs[i][2]); - recvData.read_skip<uint8>(); // item slot in mail, not used - recvData.ReadByteSeq(itemGUIDs[i][3]); - recvData.ReadByteSeq(itemGUIDs[i][0]); - recvData.ReadByteSeq(itemGUIDs[i][4]); - recvData.ReadByteSeq(itemGUIDs[i][5]); - } - - recvData.ReadByteSeq(mailbox[7]); - recvData.ReadByteSeq(mailbox[3]); - recvData.ReadByteSeq(mailbox[6]); - recvData.ReadByteSeq(mailbox[5]); - - subject = recvData.ReadString(subjectLength); - receiverName = recvData.ReadString(receiverLength); - - recvData.ReadByteSeq(mailbox[2]); - recvData.ReadByteSeq(mailbox[0]); - - body = recvData.ReadString(bodyLength); - - recvData.ReadByteSeq(mailbox[1]); - - // packet read complete, now do check - - if (!CanOpenMailBox(mailbox)) + if (!CanOpenMailBox(packet.Info.Mailbox)) return; - if (receiverName.empty()) + if (packet.Info.Target.empty()) return; Player* player = _player; @@ -153,23 +81,39 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) } ObjectGuid receiverGuid; - if (normalizePlayerName(receiverName)) - receiverGuid = ObjectMgr::GetPlayerGUIDByName(receiverName); + if (normalizePlayerName(packet.Info.Target)) + receiverGuid = ObjectMgr::GetPlayerGUIDByName(packet.Info.Target); if (!receiverGuid) { TC_LOG_INFO("network", "Player %s is sending mail to %s (GUID: not existed!) with subject %s " - "and body %s includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", - player->GetGUID().ToString().c_str(), receiverName.c_str(), subject.c_str(), body.c_str(), - items_count, money, COD, stationery, package); + "and body %s includes " SZFMTD " items, " SI64FMTD " copper and " SI64FMTD " COD copper with StationeryID = %d, PackageID = %d", + GetPlayerInfo().c_str(), packet.Info.Target.c_str(), packet.Info.Subject.c_str(), packet.Info.Body.c_str(), + packet.Info.Attachments.size(), packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID, packet.Info.PackageID); player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } + if (packet.Info.SendMoney < 0) + { + GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_INTERNAL_ERROR); + TC_LOG_WARN("cheat", "Player %s attempted to send mail to %s (%s) with negative money value (SendMoney: " SI64FMTD ")", + GetPlayerInfo().c_str(), packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), packet.Info.SendMoney); + return; + } + + if (packet.Info.Cod < 0) + { + GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_INTERNAL_ERROR); + TC_LOG_WARN("cheat", "Player %s attempted to send mail to %s (%s) with negative COD value (Cod: " SI64FMTD ")", + GetPlayerInfo().c_str(), packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), packet.Info.Cod); + return; + } + TC_LOG_INFO("network", "Player %s is sending mail to %s (%s) with subject %s and body %s " - "includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", - player->GetGUID().ToString().c_str(), receiverName.c_str(), receiverGuid.ToString().c_str(), subject.c_str(), - body.c_str(), items_count, money, COD, stationery, package); + "includes " SZFMTD " items, " SI64FMTD " copper and " SI64FMTD " COD copper with StationeryID = %d, PackageID = %d", + GetPlayerInfo().c_str(), packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), packet.Info.Subject.c_str(), + packet.Info.Body.c_str(), packet.Info.Attachments.size(), packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID, packet.Info.PackageID); if (player->GetGUID() == receiverGuid) { @@ -177,12 +121,12 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) return; } - uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client + uint32 cost = !packet.Info.Attachments.empty() ? 30 * packet.Info.Attachments.size() : 30; // price hardcoded in client - uint64 reqmoney = cost + money; + int64 reqmoney = cost + packet.Info.SendMoney; // Check for overflow - if (reqmoney < money) + if (reqmoney < packet.Info.SendMoney) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; @@ -246,10 +190,10 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) } // test the receiver's Faction... or all items are account bound - bool accountBound = items_count ? true : false; - for (uint8 i = 0; i < items_count; ++i) + bool accountBound = !packet.Info.Attachments.empty(); + for (auto const& att : packet.Info.Attachments) { - if (Item* item = player->GetItemByGuid(itemGUIDs[i])) + if (Item* item = player->GetItemByGuid(att.ItemGUID)) { ItemTemplate const* itemProto = item->GetTemplate(); if (!itemProto || !(itemProto->GetFlags() & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT)) @@ -272,17 +216,17 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) return; } - Item* items[MAX_MAIL_ITEMS]; + std::vector<Item*> items; - for (uint8 i = 0; i < items_count; ++i) + for (auto const& att : packet.Info.Attachments) { - if (!itemGUIDs[i]) + if (att.ItemGUID.IsEmpty()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } - Item* item = player->GetItemByGuid(itemGUIDs[i]); + 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) @@ -312,7 +256,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) return; } - if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) + if (packet.Info.Cod && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; @@ -324,38 +268,37 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) return; } - items[i] = item; + items.push_back(item); } player->SendMailResult(0, MAIL_SEND, MAIL_OK); - player->ModifyMoney(-int64(reqmoney)); + player->ModifyMoney(-reqmoney); player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; - MailDraft draft(subject, body); + MailDraft draft(packet.Info.Subject, packet.Info.Body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); - if (items_count > 0 || money > 0) + if (!packet.Info.Attachments.empty() || packet.Info.SendMoney > 0) { bool log = HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE); - if (items_count > 0) + if (!packet.Info.Attachments.empty()) { - for (uint8 i = 0; i < items_count; ++i) + for (auto const& item : items) { - Item* item = items[i]; if (log) { sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail item: %s (Entry: %u Count: %u) " "to: %s (%s) (Account: %u)", GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), item->GetTemplate()->GetDefaultLocaleName(), item->GetEntry(), item->GetCount(), - receiverName.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); + packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable - player->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); + player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory item->SetOwnerGUID(receiverGuid); @@ -368,10 +311,10 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) needItemDelay = player->GetSession()->GetAccountId() != receiverAccountId; } - if (log && money > 0) + if (log && packet.Info.SendMoney > 0) { - sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail money: " UI64FMTD " to: %s (%s) (Account: %u)", - GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), money, receiverName.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); + sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail money: " SI64FMTD " to: %s (%s) (Account: %u)", + GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), packet.Info.SendMoney, packet.Info.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); } } @@ -384,32 +327,27 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) deliver_delay = 0; // don't ask for COD if there are no items - if (items_count == 0) - COD = 0; + if (packet.Info.Attachments.empty()) + packet.Info.Cod = 0; // will delete item or place to receiver mail list draft - .AddMoney(money) - .AddCOD(COD) - .SendMailTo(trans, MailReceiver(receiver, receiverGuid.GetCounter()), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); + .AddMoney(packet.Info.SendMoney) + .AddCOD(packet.Info.Cod) + .SendMailTo(trans, MailReceiver(receiver, receiverGuid.GetCounter()), MailSender(player), packet.Info.Body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); player->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); } //called when mail is read -void WorldSession::HandleMailMarkAsRead(WorldPacket& recvData) +void WorldSession::HandleMailMarkAsRead(WorldPackets::Mail::MailMarkAsRead& packet) { - ObjectGuid mailbox; - uint32 mailId; - recvData >> mailbox; - recvData >> mailId; - - if (!CanOpenMailBox(mailbox)) + if (!CanOpenMailBox(packet.Mailbox)) return; Player* player = _player; - Mail* m = player->GetMail(mailId); + Mail* m = player->GetMail(packet.MailID); if (m && m->state != MAIL_STATE_DELETED) { if (player->unReadMails) @@ -421,18 +359,9 @@ void WorldSession::HandleMailMarkAsRead(WorldPacket& recvData) } //called when client deletes mail -void WorldSession::HandleMailDelete(WorldPacket& recvData) +void WorldSession::HandleMailDelete(WorldPackets::Mail::MailDelete& packet) { - ObjectGuid mailbox; - uint32 mailId; - recvData >> mailbox; - recvData >> mailId; - recvData.read_skip<uint32>(); // mailTemplateId - - if (!CanOpenMailBox(mailbox)) - return; - - Mail* m = _player->GetMail(mailId); + Mail* m = _player->GetMail(packet.MailID); Player* player = _player; player->m_mailsUpdated = true; if (m) @@ -440,45 +369,40 @@ void WorldSession::HandleMailDelete(WorldPacket& recvData) // delete shouldn't show up for COD mails if (m->COD) { - player->SendMailResult(mailId, MAIL_DELETED, MAIL_ERR_INTERNAL_ERROR); + player->SendMailResult(packet.MailID, MAIL_DELETED, MAIL_ERR_INTERNAL_ERROR); return; } m->state = MAIL_STATE_DELETED; } - player->SendMailResult(mailId, MAIL_DELETED, MAIL_OK); + player->SendMailResult(packet.MailID, MAIL_DELETED, MAIL_OK); } -void WorldSession::HandleMailReturnToSender(WorldPacket& recvData) +void WorldSession::HandleMailReturnToSender(WorldPackets::Mail::MailReturnToSender& packet) { - ObjectGuid mailbox; - uint32 mailId; - recvData >> mailbox; - recvData >> mailId; - recvData.read_skip<uint64>(); // original sender GUID for return to, not used - - if (!CanOpenMailBox(mailbox)) - return; + //TODO: find a proper way to replace this check. Idea: Save Guid form MailGetList until CMSG_CLOSE_INTERACTION is sent + /*if (!CanOpenMailBox(mailbox)) + return;*/ Player* player = _player; - Mail* m = player->GetMail(mailId); - if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) + Mail* m = player->GetMail(packet.MailID); + if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(nullptr) || m->sender != packet.SenderGUID.GetCounter()) { - player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR); + player->SendMailResult(packet.MailID, MAIL_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR); return; } //we can return mail now, so firstly delete the old one SQLTransaction trans = CharacterDatabase.BeginTransaction(); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID); - stmt->setUInt32(0, mailId); + stmt->setUInt32(0, packet.MailID); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID); - stmt->setUInt32(0, mailId); + stmt->setUInt32(0, packet.MailID); trans->Append(stmt); - player->RemoveMail(mailId); + player->RemoveMail(packet.MailID); // only return mail if the player exists (and delete if not existing) if (m->messageType == MAIL_NORMAL && m->sender) @@ -502,54 +426,49 @@ void WorldSession::HandleMailReturnToSender(WorldPacket& recvData) CharacterDatabase.CommitTransaction(trans); delete m; //we can deallocate old mail - player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); + player->SendMailResult(packet.MailID, MAIL_RETURNED_TO_SENDER, MAIL_OK); } //called when player takes item attached in mail -void WorldSession::HandleMailTakeItem(WorldPacket& recvData) +void WorldSession::HandleMailTakeItem(WorldPackets::Mail::MailTakeItem& packet) { - ObjectGuid mailbox; - uint32 mailId; - ObjectGuid::LowType itemId; - recvData >> mailbox; - recvData >> mailId; - recvData >> itemId; // item guid low - - if (!CanOpenMailBox(mailbox)) + uint32 AttachID = packet.AttachID; + + if (!CanOpenMailBox(packet.Mailbox)) return; Player* player = _player; - Mail* m = player->GetMail(mailId); - if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) + Mail* m = player->GetMail(packet.MailID); + if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(nullptr)) { - player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); + player->SendMailResult(packet.MailID, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } // verify that the mail has the item to avoid cheaters taking COD items without paying - if (std::find_if(m->items.begin(), m->items.end(), [itemId](MailItemInfo info){ return info.item_guid == itemId; }) == m->items.end()) + if (std::find_if(m->items.begin(), m->items.end(), [AttachID](MailItemInfo info){ return info.item_guid == AttachID; }) == m->items.end()) { - player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); + player->SendMailResult(packet.MailID, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } // prevent cheating with skip client money check if (!player->HasEnoughMoney(uint64(m->COD))) { - player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); + player->SendMailResult(packet.MailID, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); return; } - Item* it = player->GetMItem(itemId); + Item* it = player->GetMItem(packet.AttachID); ItemPosCountVec dest; uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false); if (msg == EQUIP_ERR_OK) { SQLTransaction trans = CharacterDatabase.BeginTransaction(); - m->RemoveItem(itemId); - m->removedItems.push_back(itemId); + m->RemoveItem(packet.AttachID); + m->removedItems.push_back(packet.AttachID); if (m->COD > 0) //if there is COD, take COD money from player and send them to sender by mail { @@ -603,38 +522,30 @@ void WorldSession::HandleMailTakeItem(WorldPacket& recvData) player->_SaveMail(trans); CharacterDatabase.CommitTransaction(trans); - player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); + player->SendMailResult(packet.MailID, MAIL_ITEM_TAKEN, MAIL_OK, 0, packet.AttachID, count); } else - player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); + player->SendMailResult(packet.MailID, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); } -void WorldSession::HandleMailTakeMoney(WorldPacket& recvData) +void WorldSession::HandleMailTakeMoney(WorldPackets::Mail::MailTakeMoney& packet) { - ObjectGuid mailbox; - uint64 money; - uint32 mailId; - - recvData >> mailbox; - recvData >> mailId; - recvData >> money; - - if (!CanOpenMailBox(mailbox)) + if (!CanOpenMailBox(packet.Mailbox)) return; Player* player = _player; - Mail* m = player->GetMail(mailId); + Mail* m = player->GetMail(packet.MailID); if ((!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) || - (money > 0 && m->money != money)) + (packet.Money > 0 && m->money != uint64(packet.Money))) { - player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR); + player->SendMailResult(packet.MailID, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } if (!player->ModifyMoney(m->money, false)) { - player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_TOO_MUCH_GOLD); + player->SendMailResult(packet.MailID, MAIL_MONEY_TAKEN, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_TOO_MUCH_GOLD); return; } @@ -642,7 +553,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket& recvData) m->state = MAIL_STATE_CHANGED; player->m_mailsUpdated = true; - player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK); + player->SendMailResult(packet.MailID, MAIL_MONEY_TAKEN, MAIL_OK); // save money and mail to prevent cheating SQLTransaction trans = CharacterDatabase.BeginTransaction(); @@ -652,12 +563,9 @@ void WorldSession::HandleMailTakeMoney(WorldPacket& recvData) } //called when player lists his received mails -void WorldSession::HandleGetMailList(WorldPacket& recvData) +void WorldSession::HandleGetMailList(WorldPackets::Mail::MailGetList& packet) { - ObjectGuid mailbox; - recvData >> mailbox; - - if (!CanOpenMailBox(mailbox)) + if (!CanOpenMailBox(packet.Mailbox)) return; Player* player = _player; @@ -666,130 +574,42 @@ void WorldSession::HandleGetMailList(WorldPacket& recvData) if (!player->m_mailsLoaded) player->_LoadMail(); - // client can't work with packets > max int16 value - const uint32 maxPacketSize = 32767; - - uint32 mailsCount = 0; // real send to client mails amount - uint32 realCount = 0; // real mails amount - - WorldPacket data(SMSG_MAIL_LIST_RESULT, 200); // guess size - data << uint32(0); // real mail's count - data << uint8(0); // mail's count - time_t cur_time = time(NULL); + WorldPackets::Mail::MailListResult response; + response.TotalNumRecords = player->GetMailSize(); - for (PlayerMails::iterator itr = player->GetMailBegin(); itr != player->GetMailEnd(); ++itr) + time_t cur_time = time(nullptr); + + for (Mail* m : player->GetMails()) { - // Only first 50 mails are displayed - if (mailsCount >= 50) - { - realCount += 1; - continue; - } - // skip deleted or not delivered (deliver delay not expired) mails - if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time) + if (m->state == MAIL_STATE_DELETED || cur_time < m->deliver_time) continue; - uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12) - - size_t next_mail_size = 2+4+1+((*itr)->messageType == MAIL_NORMAL ? 8 : 4)+4*8+((*itr)->subject.size()+1)+((*itr)->body.size()+1)+1+item_count*(1+8+4+MAX_INSPECTED_ENCHANTMENT_SLOT*3*4+4+4+4+4+4+4+1); - - if (data.wpos()+next_mail_size > maxPacketSize) - { - realCount += 1; - continue; - } - - data << uint16(next_mail_size); // Message size - data << uint32((*itr)->messageID); // Message ID - data << uint8((*itr)->messageType); // Message Type - - switch ((*itr)->messageType) - { - case MAIL_NORMAL: // sender guid - data << ObjectGuid::Create<HighGuid::Player>((*itr)->sender); - break; - case MAIL_CREATURE: - case MAIL_GAMEOBJECT: - case MAIL_AUCTION: - case MAIL_CALENDAR: - data << uint32((*itr)->sender); // creature/gameobject entry, auction id, calendar event id? - break; - } - - data << uint64((*itr)->COD); // COD - data << uint32(0); // Package.dbc ID ? - data << uint32((*itr)->stationery); // stationery (Stationery.dbc) - data << uint64((*itr)->money); // Gold - data << uint32((*itr)->checked); // flags - data << float(float((*itr)->expire_time-time(NULL))/DAY); // Time - data << uint32((*itr)->mailTemplateId); // mail template (MailTemplate.dbc) - data << (*itr)->subject; // Subject string - once 00, when mail type = 3, max 256 - data << (*itr)->body; // message? max 8000 - data << uint8(item_count); // client limit is 0x10 - - for (uint8 i = 0; i < item_count; ++i) - { - Item* item = player->GetMItem((*itr)->items[i].item_guid); - // item index (0-6?) - data << uint8(i); - // item guid low? - data << uint64((item ? item->GetGUID().GetCounter() : UI64LIT(0))); - // entry - data << uint32((item ? item->GetEntry() : 0)); - for (uint8 j = 0; j < MAX_INSPECTED_ENCHANTMENT_SLOT; ++j) - { - data << uint32((item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0)); - data << uint32((item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0)); - data << uint32((item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0)); - } - - // can be negative - data << int32((item ? item->GetItemRandomPropertyId() : 0)); - // unk - data << uint32((item ? item->GetItemSuffixFactor() : 0)); - // stack count - data << uint32((item ? item->GetCount() : 0)); - // charges - data << uint32((item ? item->GetSpellCharges() : 0)); - // durability - data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0)); - // durability - data << uint32((item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0)); - // unknown wotlk - data << uint8(0); - } + response.Mails.emplace_back(m, player); - ++realCount; - ++mailsCount; + // max. 50 mails can be sent + if (response.Mails.size() >= 50) + break; } - data.put<uint32>(0, realCount); // this will display warning about undelivered mail to player if realCount > mailsCount - data.put<uint8>(4, mailsCount); // set real send mails to client - SendPacket(&data); + SendPacket(response.Write()); // recalculate m_nextMailDelivereTime and unReadMails _player->UpdateNextMailTimeAndUnreads(); } //used when player copies mail body to his inventory -void WorldSession::HandleMailCreateTextItem(WorldPacket& recvData) +void WorldSession::HandleMailCreateTextItem(WorldPackets::Mail::MailCreateTextItem& packet) { - ObjectGuid mailbox; - uint32 mailId; - - recvData >> mailbox; - recvData >> mailId; - - if (!CanOpenMailBox(mailbox)) + if (!CanOpenMailBox(packet.Mailbox)) return; Player* player = _player; - Mail* m = player->GetMail(mailId); - if (!m || (m->body.empty() && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL) || (m->checked & MAIL_CHECK_MASK_COPIED)) + Mail* m = player->GetMail(packet.MailID); + if (!m || (m->body.empty() && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(nullptr) || (m->checked & MAIL_CHECK_MASK_COPIED)) { - player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); + player->SendMailResult(packet.MailID, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); return; } @@ -806,7 +626,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket& recvData) MailTemplateEntry const* mailTemplateEntry = sMailTemplateStore.LookupEntry(m->mailTemplateId); if (!mailTemplateEntry) { - player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); + player->SendMailResult(packet.MailID, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); return; } @@ -820,8 +640,6 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket& recvData) bodyItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_MAIL_TEXT_MASK); - TC_LOG_INFO("network", "HandleMailCreateTextItem mailid=%u", mailId); - ItemPosCountVec dest; uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, bodyItem, false); if (msg == EQUIP_ERR_OK) @@ -831,65 +649,54 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket& recvData) player->m_mailsUpdated = true; player->StoreItem(dest, bodyItem, true); - player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_OK); + player->SendMailResult(packet.MailID, MAIL_MADE_PERMANENT, MAIL_OK); } else { - player->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_EQUIP_ERROR, msg); + player->SendMailResult(packet.MailID, MAIL_MADE_PERMANENT, MAIL_ERR_EQUIP_ERROR, msg); delete bodyItem; } } /// @todo Fix me! ... this void has probably bad condition, but good data are sent -void WorldSession::HandleQueryNextMailTime(WorldPacket& /*recvData*/) +void WorldSession::HandleQueryNextMailTime(WorldPackets::Mail::MailQueryNextMailTime& /*packet*/) { - WorldPacket data(SMSG_MAIL_QUERY_NEXT_TIME_RESULT, 8); + WorldPackets::Mail::MailQueryNextTimeResult result; if (!_player->m_mailsLoaded) _player->_LoadMail(); if (_player->unReadMails > 0) { - data << float(0); // float - data << uint32(0); // count + result.NextMailTime = 0.0f; - uint32 count = 0; - time_t now = time(NULL); + time_t now = time(nullptr); std::set<ObjectGuid::LowType> sentSenders; - for (PlayerMails::iterator itr = _player->GetMailBegin(); itr != _player->GetMailEnd(); ++itr) + + for (Mail* mail : _player->GetMails()) { - Mail* m = (*itr); - // must be not checked yet - if (m->checked & MAIL_CHECK_MASK_READ) + if (mail->checked & MAIL_CHECK_MASK_READ) continue; - // and already delivered - if (now < m->deliver_time) + // already delivered + if (now < mail->deliver_time) continue; // only send each mail sender once - if (sentSenders.count(m->sender)) + if (sentSenders.count(mail->sender)) continue; - data << (m->messageType == MAIL_NORMAL ? ObjectGuid::Create<HighGuid::Player>(m->sender) : ObjectGuid::Empty); // player guid - data << uint32(m->messageType != MAIL_NORMAL ? m->sender : 0); // non-player entries - data << uint32(m->messageType); - data << uint32(m->stationery); - data << float(m->deliver_time - now); + result.Next.emplace_back(mail); - sentSenders.insert(m->sender); - ++count; - if (count == 2) // do not display more than 2 mails + sentSenders.insert(mail->sender); + + // do not send more than 2 mails + if (sentSenders.size() > 2) break; } - - data.put<uint32>(4, count); } else - { - data << float(-DAY); - data << uint32(0); - } + result.NextMailTime = -DAY; - SendPacket(&data); + SendPacket(result.Write()); } |