OregonCore  revision 3611e8a-git
Your Favourite TBC server
Mail.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "Mail.h"
19 #include "WorldPacket.h"
20 #include "WorldSession.h"
21 #include "Opcodes.h"
22 #include "Log.h"
23 #include "World.h"
24 #include "ObjectMgr.h"
25 #include "Player.h"
26 #include "UpdateMask.h"
27 #include "Unit.h"
28 #include "Language.h"
29 #include "DBCStores.h"
30 #include "BattlegroundMgr.h"
31 #include "AuctionHouseBot.h"
32 #include "AuctionHouseMgr.h"
33 #include "Item.h"
34 #include "ScriptMgr.h"
35 
54 {
55  uint64 mailbox, unk3;
56  std::string receiver, subject, body;
57  uint32 unk1, unk2, money, COD;
58  uint8 unk4;
59  recv_data >> mailbox;
60  recv_data >> receiver;
61 
62  recv_data >> subject;
63 
64  recv_data >> body;
65 
66  recv_data >> unk1; // stationery?
67  recv_data >> unk2; // 0x00000000
68 
69  uint8 items_count;
70  recv_data >> items_count; // attached items count
71 
72  if (items_count > MAX_MAIL_ITEMS) // client limit
73  {
75  recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
76  return;
77  }
78 
79  uint64 itemGUIDs[MAX_MAIL_ITEMS];
80 
81  for (uint8 i = 0; i < items_count; ++i)
82  {
83  recv_data.read_skip<uint8>(); // item slot in mail, not used
84  recv_data >> itemGUIDs[i];
85  }
86 
87  recv_data >> money >> COD; // money and cod
88  recv_data >> unk3; // const 0
89  recv_data >> unk4; // const 0
90 
91  // packet read complete, now do check
92 
94  return;
95 
96  if (receiver.empty())
97  return;
98 
99  Player* pl = _player;
100 
101  uint64 rc = 0;
102  if (normalizePlayerName(receiver))
103  rc = sObjectMgr.GetPlayerGUIDByName(receiver);
104 
105  if (!rc)
106  {
107  sLog.outDetail("Player %u is sending mail to invalid player %s (no GUID) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u",
108  pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
110  return;
111  }
112 
113  sLog.outDetail("Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2);
114 
115  if (pl->GetGUID() == rc)
116  {
118  return;
119  }
120 
121  uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client
122 
123  uint32 reqmoney = cost + money;
124 
125  if (pl->GetMoney() < reqmoney)
126  {
128  return;
129  }
130 
131  Player* receive = sObjectMgr.GetPlayer(rc);
132 
133  uint32 rc_team = 0;
134  uint8 mails_count = 0; // do not allow to send to one player more than 100 mails
135 
136  if (receive)
137  {
138  rc_team = receive->GetTeam();
139  mails_count = receive->GetMailSize();
140  }
141  else
142  {
143  rc_team = sObjectMgr.GetPlayerTeamByGUID(rc);
144  if (QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)))
145  {
146  Field* fields = result->Fetch();
147  mails_count = fields[0].GetUInt32();
148  }
149  }
150 
151  // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255..
152  if (mails_count > 100)
153  {
155  return;
156  }
157 
158  // check the receiver's Faction...
159  if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER)
160  {
162  return;
163  }
164 
165  Item* items[MAX_MAIL_ITEMS];
166 
167  for (uint8 i = 0; i < items_count; ++i)
168  {
169  if (!itemGUIDs[i])
170  {
172  return;
173  }
174 
175  Item* item = pl->GetItemByGuid(itemGUIDs[i]);
176 
177  // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail)
178  if (!item)
179  {
181  return;
182  }
183 
184  if (!item->CanBeTraded())
185  {
187  return;
188  }
189 
191  {
193  return;
194  }
195 
196  if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED))
197  {
199  return;
200  }
201 
202  if (item->IsBag() && !((Bag*)item)->IsEmpty())
203  {
205  return;
206  }
207 
208  items[i] = item;
209  }
210 
212 
213  uint32 itemTextId = !body.empty() ? sObjectMgr.CreateItemText( body ) : 0;
214 
215  pl->ModifyMoney(-int32(reqmoney));
216 
217  bool needItemDelay = false;
218 
219  MailDraft draft(subject, itemTextId);
220 
221  if (items_count > 0 || money > 0)
222  {
223  uint32 rc_account = 0;
224  if (receive)
225  rc_account = receive->GetSession()->GetAccountId();
226  else
227  rc_account = sObjectMgr.GetPlayerAccountIdByGUID(rc);
228 
229  if (items_count > 0)
230  {
231  for (uint8 i = 0; i < items_count; ++i)
232  {
233  Item* item = items[i];
234  if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
235  {
236  sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)",
237  GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account);
238  }
239 
240  pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true);
242  item->DeleteFromInventoryDB(); // deletes item from character's inventory
243  item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
244  // owner in data will set at mail receive and item extracting
245  CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), item->GetGUIDLow());
247 
248  draft.AddItem(item);
249  }
250 
251  // if item send to character at another account, then apply item delivery delay
252  needItemDelay = pl->GetSession()->GetAccountId() != rc_account;
253  }
254 
255  if (money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
256  {
257  sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail money: %u to player: %s (Account: %u)",
258  GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account);
259  }
260  }
261 
262  // If theres is an item, there is a one hour delivery delay if sent to another account's character.
263  uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
264 
265  // will delete item or place to receiver mail list
266  draft
267  .AddMoney(money)
268  .AddCOD(COD)
269  .SendMailTo(MailReceiver(receive, GUID_LOPART(rc)), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay);
270 
274 }
275 
288 {
289  uint64 mailbox;
290  uint32 mailId;
291  recv_data >> mailbox;
292  recv_data >> mailId;
293 
294  if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
295  return;
296 
297  Player* pl = _player;
298 
299  if (Mail* m = pl->GetMail(mailId))
300  {
301  if (pl->unReadMails)
302  --pl->unReadMails;
303  m->checked = m->checked | MAIL_CHECK_MASK_READ;
304  pl->m_mailsUpdated = true;
305  m->state = MAIL_STATE_CHANGED;
306  }
307 }
308 
318 {
319  uint64 mailbox;
320  uint32 mailId;
321  recv_data >> mailbox;
322  recv_data >> mailId;
323  recv_data.read_skip<uint32>(); // mailTemplateId
324 
326  return;
327 
328  Player* pl = _player;
329  pl->m_mailsUpdated = true;
330 
331  if (Mail* m = _player->GetMail(mailId))
332  {
333  // delete shouldn't show up for COD mails
334  if (m->COD)
335  {
337  return;
338  }
339 
340  m->state = MAIL_STATE_DELETED;
341  }
342  pl->SendMailResult(mailId, MAIL_DELETED, MAIL_OK);
343 }
344 
355 {
356  uint64 mailbox;
357  uint32 mailId;
358  recv_data >> mailbox;
359  recv_data >> mailId;
360  recv_data.read_skip<uint64>(); // original sender GUID for return to, not used
361 
363  return;
364 
365  Player* pl = _player;
366  Mail* m = pl->GetMail(mailId);
367  if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
368  {
370  return;
371  }
372 
373  // we can return mail now
374  // so firstly delete the old one
376  CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mailId);
377  // needed?
378  CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId);
380  pl->RemoveMail(mailId);
381 
382  // send back only to existing players and simple drop for other cases
383  if (m->messageType == MAIL_NORMAL && m->sender)
384  {
385  MailDraft draft(m->subject, m->itemTextId);
386  if (m->mailTemplateId)
387  draft = MailDraft(m->mailTemplateId, false); // items already included
388 
389  if (m->HasItems())
390  {
391  for (std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
392  {
393  if (Item* item = pl->GetMItem(itr2->item_guid))
394  draft.AddItem(item);
395 
396  pl->RemoveMItem(itr2->item_guid);
397  }
398  }
399 
400  if (m->sender == auctionbot.GetAHBplayerGUID())
401  draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, auctionbot.GetAHBplayerGUID());
402  else
404  }
405 
406  delete m; // we can deallocate old mail
408 }
409 
414 {
415  uint64 mailbox;
416  uint32 mailId;
417  uint32 itemId;
418  recv_data >> mailbox;
419  recv_data >> mailId;
420  recv_data >> itemId; // item guid low
421 
422  if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
423  return;
424 
425  Player* pl = _player;
426 
427  Mail* m = pl->GetMail(mailId);
428  if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
429  {
431  return;
432  }
433 
434  // prevent cheating with skip client money check
435  if (pl->GetMoney() < m->COD)
436  {
438  return;
439  }
440 
441  Item* it = pl->GetMItem(itemId);
442 
443  ItemPosCountVec dest;
444  uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false);
445  if (msg == EQUIP_ERR_OK)
446  {
447  m->RemoveItem(itemId);
448  m->removedItems.push_back(itemId);
449 
450  if (m->COD > 0) // if there is COD, take COD money from player and send them to sender by mail
451  {
452  uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER);
453  Player* receive = sObjectMgr.GetPlayer(sender_guid);
454 
455  uint32 sender_accId = 0;
456 
457  if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
458  {
459  std::string sender_name;
460  if (receive)
461  {
462  sender_accId = receive->GetSession()->GetAccountId();
463  sender_name = receive->GetName();
464  }
465  else
466  {
467  // can be calculated early
468  sender_accId = sObjectMgr.GetPlayerAccountIdByGUID(sender_guid);
469 
470  if (!sObjectMgr.GetPlayerNameByGUID(sender_guid, sender_name))
471  sender_name = sObjectMgr.GetOregonStringForDBCLocale(LANG_UNKNOWN);
472  }
473  sLog.outCommand(GetAccountId(), "GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)",
474  GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount(), m->COD, sender_name.c_str(), sender_accId);
475  }
476  else if (!receive)
477  sender_accId = sObjectMgr.GetPlayerAccountIdByGUID(sender_guid);
478 
479  // check player existence
480  if (receive || sender_accId)
481  {
482  MailDraft(m->subject)
483  .AddMoney(m->COD)
485  }
486 
487  pl->ModifyMoney(-int32(m->COD));
488  }
489  m->COD = 0;
491  pl->m_mailsUpdated = true;
492  pl->RemoveMItem(it->GetGUIDLow());
493 
494  uint32 count = it->GetCount(); // save counts before store and possible merge with deleting
495  pl->MoveItemToInventory(dest, it, true);
496 
499  pl->_SaveMail();
501 
502  pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count);
503  }
504  else
506 }
507 
512 {
513  uint64 mailbox;
514  uint32 mailId;
515  recv_data >> mailbox;
516  recv_data >> mailId;
517 
518  if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
519  return;
520 
521  Player* pl = _player;
522 
523  Mail* m = pl->GetMail(mailId);
524  if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
525  {
527  return;
528  }
529 
531 
532  pl->ModifyMoney(m->money);
533  m->money = 0;
535  pl->m_mailsUpdated = true;
536 
537  // save money and mail to prevent cheating
539  pl->SaveGoldToDB();
540  pl->_SaveMail();
542 }
543 
549 {
550  uint64 mailbox;
551  recv_data >> mailbox;
552 
553  if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
554  return;
555 
556  Player* pl = _player;
557 
558  //load players mails, and mailed items
559  if (!pl->m_mailsLoaded)
560  pl ->_LoadMail();
561 
562  // client can't work with packets > max int16 value
563  const uint32 maxPacketSize = 32767;
564 
565  uint32 mailsCount = 0; // real send to client mails amount
566 
567  WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size
568  data << uint8(0); // mail's count
569  time_t cur_time = time(NULL);
570 
571  for (PlayerMails::iterator itr = pl->GetmailBegin(); itr != pl->GetmailEnd(); ++itr)
572  {
573  // packet send mail count as uint8, prevent overflow
574  if (mailsCount >= 254)
575  break;
576 
577  // skip deleted or not delivered (deliver delay not expired) mails
578  if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time)
579  continue;
580 
581  uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12)
582 
583  size_t next_mail_size = 2 + 4 + 1 + 8 + 4 * 8 + ((*itr)->subject.size() + 1) + 1 + item_count * (1 + 4 + 4 + 6 * 3 * 4 + 4 + 4 + 1 + 4 + 4 + 4);
584 
585  if (data.wpos() + next_mail_size > maxPacketSize)
586  break;
587 
588  data << uint16(next_mail_size); // Message size
589  data << uint32((*itr)->messageID); // Message ID
590  data << uint8((*itr)->messageType); // Message Type
591 
592  switch ((*itr)->messageType)
593  {
594  case MAIL_NORMAL: // sender guid
595  data << uint64(MAKE_NEW_GUID((*itr)->sender, 0, HIGHGUID_PLAYER));
596  break;
597  case MAIL_CREATURE:
598  case MAIL_GAMEOBJECT:
599  case MAIL_AUCTION:
600  data << (uint32) (*itr)->sender; // creature/gameobject entry, auction id
601  break;
602  case MAIL_ITEM: // item entry (?) sender = "Unknown", NYI
603  break;
604  }
605 
606  data << uint32((*itr)->COD); // COD
607  data << uint32((*itr)->itemTextId); // item text
608  data << uint32(0); // unknown
609  data << uint32((*itr)->stationery); // stationery (Stationery.dbc)
610  data << uint32((*itr)->money); // copper
611  data << uint32((*itr)->checked); // flags
612  data << float(((*itr)->expire_time - time(NULL)) / DAY); // Time
613  data << uint32((*itr)->mailTemplateId); // mail template (MailTemplate.dbc)
614  data << (*itr)->subject; // Subject string - once 00, when mail type = 3, max 256
615 
616  data << (uint8) item_count;
617 
618  for (uint8 i = 0; i < item_count; ++i)
619  {
620  Item* item = pl->GetMItem((*itr)->items[i].item_guid);
621  // item index (0-6?)
622  data << (uint8) i;
623  // item guid low?
624  data << (uint32) (item ? item->GetGUIDLow() : 0);
625  // entry
626  data << (uint32) (item ? item->GetEntry() : 0);
627  for (uint8 j = 0; j < 6; ++j)
628  {
629  data << (uint32) (item ? item->GetEnchantmentId((EnchantmentSlot)j) : 0);
630  data << uint32((item ? item->GetEnchantmentDuration((EnchantmentSlot)j) : 0));
631  data << uint32((item ? item->GetEnchantmentCharges((EnchantmentSlot)j) : 0));
632  }
633  // can be negative
634  data << (uint32) (item ? item->GetItemRandomPropertyId() : 0);
635  // unk
636  data << (uint32) (item ? item->GetItemSuffixFactor() : 0);
637  // stack count
638  data << (uint8) (item ? item->GetCount() : 0);
639  // charges
640  data << (uint32) (item ? item->GetSpellCharges() : 0);
641  // durability
642  data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY) : 0);
643  // durability
644  data << (uint32) (item ? item->GetUInt32Value(ITEM_FIELD_DURABILITY) : 0);
645  }
646 
647  mailsCount += 1;
648  }
649 
650  data.put<uint8>(0, mailsCount); // set real send mails to client
651  SendPacket(&data);
652 
653  // recalculate m_nextMailDelivereTime and unReadMails
655 }
656 
664 {
665  uint32 itemTextId;
666  uint32 mailId; //this value can be item id in bag, but it is also mail id
667  uint32 unk; //maybe something like state - 0x70000000
668 
669  recv_data >> itemTextId >> mailId >> unk;
670 
671  // @todo some check needed, if player has item with guid mailId, or has mail with id mailId
672 
673  sLog.outDebug("CMSG_ITEM_TEXT_QUERY itemguid: %u, mailId: %u, unk: %u", itemTextId, mailId, unk);
674 
675  WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, (4 + 10)); // guess size
676  data << itemTextId;
677  data << sObjectMgr.GetItemText(itemTextId);
678  SendPacket(&data);
679 }
680 
689 {
690  uint64 mailbox;
691  uint32 mailId;
692 
693  recv_data >> mailbox;
694  recv_data >> mailId;
695  recv_data.read_skip<uint32>(); // mailTemplateId, non need, Mail store own 100% correct value anyway
696 
698  return;
699 
700  Player* pl = _player;
701 
702  Mail* m = pl->GetMail(mailId);
703  if (!m || (!m->itemTextId && !m->mailTemplateId) || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL))
704  {
706  return;
707  }
708 
709  Item* bodyItem = new Item; // This is not bag and then can be used new Item.
710  if (!bodyItem->Create(sObjectMgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl))
711  {
712  delete bodyItem;
713  return;
714  }
715 
718 
719  sLog.outDetail("HandleMailCreateTextItem mailid=%u", mailId);
720 
721  ItemPosCountVec dest;
722  uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, bodyItem, false);
723  if (msg == EQUIP_ERR_OK)
724  {
727  pl->m_mailsUpdated = true;
728 
729  pl->StoreItem(dest, bodyItem, true);
731  }
732  else
733  {
735  delete bodyItem;
736  }
737 }
738 
743 {
745 
746  if (!_player->m_mailsLoaded)
747  _player->_LoadMail();
748 
749  if (_player->unReadMails > 0)
750  {
751  data << (uint32) 0; // float
752  data << (uint32) 0; // count
753  uint32 count = 0;
754  for (PlayerMails::iterator itr = _player->GetmailBegin(); itr != _player->GetmailEnd(); ++itr)
755  {
756  Mail* m = (*itr);
757  // not checked yet, already must be delivered
758  if ((m->checked & MAIL_CHECK_MASK_READ) == 0 && (m->deliver_time <= time(NULL)))
759  {
760  ++count;
761 
762  if (count > 2)
763  {
764  count = 2;
765  break;
766  }
767 
768  data << (uint64) m->sender; // sender guid
769 
770  switch (m->messageType)
771  {
772  case MAIL_AUCTION:
773  data << uint32(m->sender); // auction house id
774  data << uint32(MAIL_AUCTION); // message type
775  break;
776  default:
777  data << (uint32) 0;
778  data << (uint32) 0;
779  break;
780  }
781 
782  data << (uint32) m->stationery;
783  data << (uint32) 0xC6000000; // float unk, time or something
784  }
785  }
786  data.put<uint32>(4, count);
787  }
788  else
789  {
790  data << (uint32) 0xC7A8C000;
791  data << (uint32) 0x00000000;
792  }
793  SendPacket(&data);
794 }
795 
802 MailSender::MailSender(Object* sender, MailStationery stationery ) : m_stationery(stationery)
803 {
804  switch (sender->GetTypeId())
805  {
806  case TYPEID_UNIT:
808  m_senderId = sender->GetEntry();
809  break;
810  case TYPEID_GAMEOBJECT:
812  m_senderId = sender->GetEntry();
813  break;
814  case TYPEID_ITEM:
816  m_senderId = sender->GetEntry();
817  break;
818  case TYPEID_PLAYER:
820  m_senderId = sender->GetGUIDLow();
821  if (sWorld.getConfig(CONFIG_GM_MAIL))
822  if (static_cast<Player*>(sender)->IsGameMaster())
824  break;
825  default:
827  m_senderId = 0; // will show mail from not existed player
828  sLog.outError( "MailSender::MailSender - Mail has unexpected sender typeid (%u)", sender->GetTypeId());
829  break;
830  }
831 }
832 
840 {
841 }
842 
848 MailReceiver::MailReceiver(Player* receiver) : m_receiver(receiver), m_receiver_lowguid(receiver->GetGUIDLow())
849 {
850 }
851 
858 MailReceiver::MailReceiver(Player* receiver, uint32 receiver_lowguid) : m_receiver(receiver), m_receiver_lowguid(receiver_lowguid)
859 {
860  ASSERT(!receiver || receiver->GetGUIDLow() == receiver_lowguid);
861 }
862 
870 {
871  m_items[item->GetGUIDLow()] = item;
872  return *this;
873 }
874 
879 {
880  if (!m_mailTemplateId || !m_mailTemplateItemsNeed)
881  return;
882 
883  m_mailTemplateItemsNeed = false;
884 
885  Loot mailLoot;
886 
887  mailLoot.FillLoot(m_mailTemplateId, LootTemplates_Mail, receiver, true);
888 
889  uint32 max_slot = mailLoot.GetMaxSlotInLootFor(receiver);
890  for (uint32 i = 0; m_items.size() < MAX_MAIL_ITEMS && i < max_slot; ++i)
891  {
892  if (LootItem* lootitem = mailLoot.LootItemInSlot(i, receiver))
893  {
894  if (Item* item = Item::CreateItem(lootitem->itemid, lootitem->count, receiver))
895  {
896  item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
897  AddItem(item);
898  }
899  }
900  }
901 }
902 
908 void MailDraft::deleteIncludedItems(bool inDB /*= false*/)
909 {
910  for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
911  {
912  Item* item = mailItemIter->second;
913 
914  if (inDB)
915  CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow());
916 
917  delete item;
918  }
919 
920  m_items.clear();
921 }
922 
923 /*
924  * Returns a mail to its sender.
925  * @param sender_acc The id of the account of the sender.
926  * @param sender_guid The low part of the GUID of the sender.
927  * @param receiver_guid The low part of the GUID of the reciever.
928  */
929 void MailDraft::SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid)
930 {
931  Player* receiver = sObjectMgr.GetPlayer(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
932 
933  uint32 rc_account = 0;
934  if (!receiver)
935  rc_account = sObjectMgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
936 
937  if (!receiver && !rc_account) // sender not exist
938  {
939  deleteIncludedItems(true);
940  return;
941  }
942 
943  // prepare mail and send in other case
944  bool needItemDelay = false;
945 
946  if (!m_items.empty())
947  {
948  // if item send to character at another account, then apply item delivery delay
949  needItemDelay = sender_acc != rc_account;
950 
951  // set owner to new receiver (to prevent delete item with sender char deleting)
953  for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
954  {
955  Item* item = mailItemIter->second;
956  item->SaveToDB(); // item not in inventory and can be save standalone
957  // owner in data will set at mail receive and item extracting
958  CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", receiver_guid, item->GetGUIDLow());
959  }
961  }
962 
963  // If theres is an item, there is a one hour delivery delay.
964  uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
965 
966  // will delete item or place to receiver mail list
967  SendMailTo(MailReceiver(receiver, receiver_guid), MailSender(MAIL_NORMAL, sender_guid), MAIL_CHECK_MASK_RETURNED, deliver_delay);
968 }
969 
978 void MailDraft::SendMailTo(MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay)
979 {
980  Player* pReceiver = receiver.GetPlayer(); // can be NULL
981 
982  if (pReceiver)
983  prepareItems(pReceiver); // generate mail template items
984 
985  if (receiver.GetPlayerGUIDLow() == auctionbot.GetAHBplayerGUID())
986  {
987  if (sender.GetMailMessageType() == MAIL_AUCTION && !m_items.empty()) // auction mail with items
988  deleteIncludedItems(true);
989  return;
990  }
991  uint32 mailId = sObjectMgr.GenerateMailID();
992 
993  time_t deliver_time = time(NULL) + deliver_delay;
994 
995  // expire time if COD 3 days, if no COD 30 days, if auction sale pending 1 hour
996  uint32 expire_delay;
997  if (sender.GetMailMessageType() == MAIL_AUCTION && m_items.empty() && !m_money) // auction mail without any items and money
998  expire_delay = sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY);
999  else
1000  expire_delay = (m_COD > 0) ? 3 * DAY : 30 * DAY;
1001 
1002  time_t expire_time = deliver_time + expire_delay;
1003 
1004  // Add to DB
1005  std::string safe_subject = GetSubject();
1006 
1008  CharacterDatabase.escape_string(safe_subject);
1009  CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked) "
1010  "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%u', '%u', '" UI64FMTD "','" UI64FMTD "', '%u', '%u', '%d')",
1011  mailId, sender.GetMailMessageType(), sender.GetStationery(), GetMailTemplateId(), sender.GetSenderId(), receiver.GetPlayerGUIDLow(), safe_subject.c_str(), GetBodyId(), (m_items.empty() ? 0 : 1), (uint64)expire_time, (uint64)deliver_time, m_money, m_COD, checked);
1012 
1013  for (MailItemMap::const_iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
1014  {
1015  Item* item = mailItemIter->second;
1016  CharacterDatabase.PExecute("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, item->GetGUIDLow(), item->GetEntry(), receiver.GetPlayerGUIDLow());
1017  }
1019 
1020  // For online receiver update in game mail status and data
1021  if (pReceiver)
1022  {
1023  pReceiver->AddNewMailDeliverTime(deliver_time);
1024 
1025  if (pReceiver->IsMailsLoaded())
1026  {
1027  Mail* m = new Mail;
1028  m->messageID = mailId;
1029  m->mailTemplateId = GetMailTemplateId();
1030  m->subject = GetSubject();
1031  m->itemTextId = GetBodyId();
1032  m->money = GetMoney();
1033  m->COD = GetCOD();
1034 
1035  for (MailItemMap::const_iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
1036  {
1037  Item* item = mailItemIter->second;
1038  m->AddItem(item->GetGUIDLow(), item->GetEntry());
1039  }
1040 
1041  m->messageType = sender.GetMailMessageType();
1042  m->stationery = sender.GetStationery();
1043  m->sender = sender.GetSenderId();
1044  m->receiver = receiver.GetPlayerGUIDLow();
1045  m->expire_time = expire_time;
1046  m->deliver_time = deliver_time;
1047  m->checked = checked;
1049 
1050  pReceiver->AddMail(m); // to insert new mail to beginning of maillist
1051 
1052  if (!m_items.empty())
1053  {
1054  for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
1055  pReceiver->AddMItem(mailItemIter->second);
1056  }
1057  }
1058  else if (!m_items.empty())
1059  deleteIncludedItems();
1060  }
1061  else if (!m_items.empty())
1062  deleteIncludedItems();
1063 }
1064 
1066 {
1067  sLog.outDebug("External Mail - Send Mails from Queue...");
1068  QueryResult_AutoPtr result = CharacterDatabase.Query("SELECT id,receiver,subject,message,money,item,item_count FROM mail_external");
1069  if (!result)
1070  {
1071  sLog.outDebug("External Mail - No Mails in Queue...");
1072  return;
1073  }
1074  else
1075  {
1076  do
1077  {
1078  Field* fields = result->Fetch();
1079  uint32 id = fields[0].GetUInt32();
1080  uint64 receiver_guid = fields[1].GetUInt64();
1081  std::string subject = fields[2].GetString();
1082  std::string message = fields[3].GetString();
1083  uint32 money = fields[4].GetUInt32();
1084  uint32 ItemID = fields[5].GetUInt32();
1085  uint32 ItemCount = fields[6].GetUInt32();
1086 
1087  Player* receiver = sObjectMgr.GetPlayer(receiver_guid);
1088 
1089  if (receiver != 0)
1090  {
1091  sLog.outDebug("External Mail - Sending mail to " UI64FMTD ", Item:%u", receiver_guid, ItemID);
1092  uint32 itemTextId = !message.empty() ? sObjectMgr.CreateItemText(message) : 0;
1093  if (ItemID != 0)
1094  {
1095  Item* ToMailItem = Item::CreateItem(ItemID, ItemCount, receiver);
1096  ToMailItem -> SaveToDB();
1097 
1098  MailDraft(subject, itemTextId)
1099  .AddItem(ToMailItem)
1100  .AddMoney(money)
1102  }
1103  else
1104  {
1105  MailDraft(subject, itemTextId)
1106  .AddMoney(money)
1108  }
1109  CharacterDatabase.PExecute("DELETE FROM mail_external WHERE id=%u", id);
1110  }
1111  }
1112  while (result -> NextRow());
1113  }
1114  sLog.outDebug("External Mail - All Mails Sent...");
1115 }
1116 
Definition: Object.h:124
uint32 receiver
Definition: Mail.h:202
void HandleSendMail(WorldPacket &recv_data)
Definition: Mail.cpp:53
std::vector< MailItemInfo > items
Definition: Mail.h:205
uint32 GetCount() const
Definition: Item.h:251
void _LoadMail()
Definition: Player.cpp:15442
ItemTemplate const * GetProto() const
Definition: Item.cpp:460
uint32 GetSenderId() const
Definition: Mail.h:94
void MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
Definition: Player.cpp:10687
const uint32 & GetUInt32Value(uint16 index) const
Definition: Object.h:228
int32 GetSpellCharges(uint8 index=0) const
Definition: Item.h:330
PlayerMails::iterator GetmailBegin()
Definition: Player.h:1612
void read_skip()
Definition: ByteBuffer.h:276
LootItem * LootItemInSlot(uint32 lootslot, Player *player, QuestItem **qitem=NULL, QuestItem **ffaitem=NULL, QuestItem **conditem=NULL)
Definition: LootMgr.cpp:612
#define GUID_LOPART(x)
Definition: ObjectGuid.h:110
void HandleMailDelete(WorldPacket &recv_data)
Definition: Mail.cpp:317
Definition: Mail.h:38
uint32 GetMaxSlotInLootFor(Player *player) const
Definition: LootMgr.cpp:675
uint16 mailTemplateId
Definition: Mail.h:200
uint32 m_receiver_lowguid
Definition: Mail.h:125
uint32 GetItemSuffixFactor() const
Definition: Item.h:302
bool BeginTransaction()
Definition: Database.cpp:533
void HandleMarkAsRead(WorldPacket &recv_data)
Definition: Mail.cpp:287
#define auctionbot
void AddItem(uint32 itemGuidLow, uint32 item_template)
Definition: Mail.h:214
uint32 GetPlayerGUIDLow() const
Definition: Mail.h:119
uint32 sender
Definition: Mail.h:201
QueryResult_AutoPtr PQuery(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:400
void RemoveMail(uint32 id)
Definition: Player.cpp:2822
uint32 GetMailSize()
Definition: Player.h:1606
Definition: Field.h:24
uint8 messageType
Definition: Mail.h:198
void AddMItem(Item *it)
Definition: Player.h:1638
virtual void SaveToDB()
Definition: Item.cpp:286
MailSender(MailMessageType messageType, uint32 sender_guidlow_or_entry, MailStationery stationery=MAIL_STATIONERY_NORMAL)
Definition: Mail.h:83
void prepareItems(Player *receiver)
Definition: Mail.cpp:878
MailState state
Definition: Mail.h:212
MailMessageType GetMailMessageType() const
Definition: Mail.h:90
uint32 checked
Definition: Mail.h:211
void SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError=0, uint32 item_guid=0, uint32 item_count=0)
Definition: Player.cpp:2835
#define sLog
Log class singleton.
Definition: Log.h:187
void UpdateNextMailTimeAndUnreads()
Definition: Player.cpp:2859
void deleteIncludedItems(bool inDB=false)
Definition: Mail.cpp:908
uint8 stationery
Definition: Mail.h:199
void SetUInt32Value(uint16 index, uint32 value)
Definition: Object.cpp:779
ACE_INT32 int32
Definition: Define.h:67
unsigned long escape_string(char *to, const char *from, unsigned long length)
Definition: Database.cpp:212
QueryResult_AutoPtr Query(const char *sql)
Definition: Database.cpp:383
void HandleMsgQueryNextMailtime(WorldPacket &recv_data)
Definition: Mail.cpp:742
uint32 GetAccountId() const
Definition: WorldSession.h:100
Player * GetPlayer() const
Definition: WorldSession.h:104
uint32 GetGUIDLow() const
Definition: Object.h:160
static void SendExternalMails()
Definition: Mail.cpp:1065
void HandleItemTextQuery(WorldPacket &recv_data)
Definition: Mail.cpp:663
void AddNewMailDeliverTime(time_t deliver_time)
Definition: Player.cpp:2878
MailDraft & AddMoney(uint32 money)
Definition: Mail.h:161
void AddMail(Mail *mail)
Definition: Player.h:1602
bool IsBag() const
Definition: Item.h:229
int32 GetItemRandomPropertyId() const
Definition: Item.h:298
#define sObjectMgr
Definition: ObjectMgr.h:1285
void SendPacket(WorldPacket const *packet)
bool CommitTransaction()
Definition: Database.cpp:551
const char * GetString() const
Definition: Field.h:41
uint32 GetEnchantmentDuration(EnchantmentSlot slot) const
Definition: Item.h:317
Definition: Common.h:181
MailCheckMask
Definition: Mail.h:41
void FillLoot(uint32 loot_id, LootStore const &store, Player *loot_owner, bool personal)
Definition: LootMgr.cpp:375
Definition: Mail.h:195
void HandleTakeItem(WorldPacket &recv_data)
Definition: Mail.cpp:413
size_t wpos() const
Definition: ByteBuffer.h:264
uint8 GetTypeId() const
Definition: Object.h:204
bool normalizePlayerName(std::string &name)
Definition: ObjectMgr.cpp:225
Definition: Bag.h:27
void SaveGoldToDB()
Definition: Player.cpp:16423
ACE_UINT8 uint8
Definition: Define.h:73
#define UI64FMTD
Definition: Common.h:149
Definition: Unit.h:297
uint8 CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap=false) const
Definition: Player.h:1151
uint8 unReadMails
Definition: Player.h:1619
bool HasItems() const
Definition: Mail.h:235
std::vector< uint32 > removedItems
Definition: Mail.h:206
virtual bool Create(uint32 guidlow, uint32 itemid, Player const *owner)
Definition: Item.cpp:243
Player * m_receiver
Definition: Mail.h:124
Definition: LootMgr.h:290
void put(size_t pos, T value)
Definition: ByteBuffer.h:79
#define MAKE_NEW_GUID(l, e, h)
Definition: ObjectGuid.h:80
Definition: Item.h:196
static Item * CreateItem(uint32 item, uint32 count, Player const *player=NULL)
Definition: Item.cpp:936
bool IsMailsLoaded() const
Definition: Player.h:1594
LootStore LootTemplates_Mail("mail_loot_template","mail template id")
#define MAIL_BODY_ITEM_TEMPLATE
Definition: Mail.h:29
time_t deliver_time
Definition: Mail.h:208
Mail * GetMail(uint32 id)
Definition: Player.cpp:3615
void SendMailTo(MailReceiver const &receiver, MailSender const &sender, MailCheckMask checked=MAIL_CHECK_MASK_NONE, uint32 deliver_delay=0)
Definition: Mail.cpp:978
bool PExecute(const char *format,...) ATTR_PRINTF(2
Definition: Database.cpp:441
char const * GetPlayerName() const
uint8 GetSlot() const
Definition: Item.h:266
const char * GetName() const
Definition: Object.h:692
MailStationery
Definition: Mail.h:52
std::vector< ItemPosCount > ItemPosCountVec
Definition: Player.h:605
ACE_UINT64 uint64
Definition: Define.h:70
MailStationery GetStationery() const
Definition: Mail.h:98
void HandleReturnToSender(WorldPacket &recv_data)
Definition: Mail.cpp:354
MailDraft & AddItem(Item *item)
Definition: Mail.cpp:869
GameObject * GetGameObjectIfCanInteractWith(uint64 guid, GameobjectTypes type) const
Definition: Player.cpp:2234
time_t expire_time
Definition: Mail.h:207
PlayerMails::iterator GetmailEnd()
Definition: Player.h:1616
uint32 COD
Definition: Mail.h:210
bool m_mailsLoaded
Definition: Player.h:1504
uint32 GetEnchantmentCharges(EnchantmentSlot slot) const
Definition: Item.h:321
Player * GetPlayer() const
Definition: Mail.h:115
Player * _player
Definition: WorldSession.h:729
ACE_Refcounted_Auto_Ptr< QueryResult, ACE_Null_Mutex > QueryResult_AutoPtr
Definition: QueryResult.h:113
uint32 GetMoney()
Definition: Player.h:1524
bool RemoveItem(uint32 item_guid)
Definition: Mail.h:222
void HandleMailCreateTextItem(WorldPacket &recv_data)
Definition: Mail.cpp:688
std::string subject
Definition: Mail.h:203
Item * GetMItem(uint32 id)
Definition: Player.h:1632
EnchantmentSlot
Definition: Item.h:147
uint32 GetTeam() const
Definition: Player.h:2075
#define ASSERT
Definition: Errors.h:33
void SaveInventoryAndGoldToDB()
Definition: Player.cpp:16417
void MoveItemToInventory(ItemPosCountVec const &dest, Item *pItem, bool update, bool in_characterInventoryDB=false)
Definition: Player.cpp:10703
bool m_mailsUpdated
Definition: Player.h:1505
bool CanBeTraded() const
Definition: Item.cpp:738
uint32 GetEntry() const
Definition: Object.h:186
uint32 money
Definition: Mail.h:209
WorldSession * GetSession() const
Definition: Player.h:1959
#define sWorld
Definition: World.h:860
MailMessageType m_messageType
Definition: Mail.h:103
DatabaseType CharacterDatabase
Accessor to the character database.
Definition: Main.cpp:54
void uint32 GetSecurity() const
Definition: WorldSession.h:96
MailReceiver(uint32 receiver_lowguid)
Definition: Mail.h:111
ACE_UINT16 uint16
Definition: Define.h:72
void SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid)
Definition: Mail.cpp:929
#define MAX_MAIL_ITEMS
Definition: Mail.h:30
bool HasFlag(uint16 index, uint32 flag) const
Definition: Object.h:299
size_t rpos() const
Definition: ByteBuffer.h:253
void ModifyMoney(int32 d)
Definition: Player.h:1528
void HandleTakeMoney(WorldPacket &recv_data)
Definition: Mail.cpp:511
ACE_UINT32 uint32
Definition: Define.h:71
bool RemoveMItem(uint32 id)
Definition: Player.h:1645
Item * StoreItem(ItemPosCountVec const &pos, Item *pItem, bool update)
Definition: Player.cpp:10294
void HandleGetMail(WorldPacket &recv_data)
Definition: Mail.cpp:548
MailDraft & AddCOD(uint32 COD)
Definition: Mail.h:166
uint32 messageID
Definition: Mail.h:197
void DeleteFromInventoryDB()
Definition: Item.cpp:455
void _SaveMail()
Definition: Player.cpp:16611
uint32 m_senderId
Definition: Mail.h:104
Definition: Player.h:922
uint32 itemTextId
Definition: Mail.h:204
MailStationery m_stationery
Definition: Mail.h:105
Item * GetItemByGuid(uint64 guid) const
Definition: Player.cpp:8583
const uint64 & GetGUID() const
Definition: Object.h:156
uint32 GetEnchantmentId(EnchantmentSlot slot) const
Definition: Item.h:313