diff options
Diffstat (limited to 'src/game/Mail.cpp')
-rw-r--r-- | src/game/Mail.cpp | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index e52006b9c37..c50b16104f2 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Mail.h" #include "WorldPacket.h" #include "WorldSession.h" @@ -30,6 +31,7 @@ #include "Language.h" #include "AuctionHouseBot.h" #include "DBCStores.h" + enum MailShowFlags { MAIL_SHOW_UNK0 = 0x0001, @@ -38,16 +40,19 @@ enum MailShowFlags MAIL_SHOW_UNK2 = 0x0008, // unknown, COD will be shown even without that flag MAIL_SHOW_RETURN = 0x0010, }; + void MailItem::deleteItem( bool inDB ) { if(item) { if(inDB) CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow()); + delete item; item=NULL; } } + void WorldSession::HandleSendMail(WorldPacket & recv_data ) { uint64 mailbox, unk3; @@ -56,20 +61,28 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) uint8 unk4; recv_data >> mailbox; recv_data >> receiver; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + recv_data >> subject; + recv_data >> body; + recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 + MailItemsInfo mi; + uint8 items_count; recv_data >> items_count; // attached items count + if(items_count > 12) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); return; } + if(items_count) { for(uint8 i = 0; i < items_count; ++i) @@ -81,16 +94,22 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) mi.AddItem(GUID_LOPART(item_guid), item_slot); } } + recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 + items_count = mi.size(); // this is the real size after the duplicates have been removed + if (receiver.empty()) return; + Player* pl = _player; + uint64 rc = 0; if(normalizePlayerName(receiver)) rc = objmgr.GetPlayerGUIDByName(receiver); + if (!rc) { sLog.outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", @@ -98,22 +117,30 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } + 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); + if(pl->GetGUID() == rc) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } + uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client + uint32 reqmoney = cost + money; + if (pl->GetMoney() < reqmoney) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } + Player *receive = objmgr.GetPlayer(rc); + uint32 rc_team = 0; uint8 mails_count = 0; //do not allow to send to one player more than 100 mails + if(receive) { rc_team = receive->GetTeam(); @@ -142,21 +169,25 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } + uint32 rc_account = 0; if(receive) rc_account = receive->GetSession()->GetAccountId(); else rc_account = objmgr.GetPlayerAccountIdByGUID(rc); + if (items_count) { for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) { MailItem& mailItem = mailItemIter->second; + if(!mailItem.item_guidlow) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } + mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM)); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if(!mailItem.item) @@ -164,21 +195,25 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } + if(!mailItem.item->CanBeTraded(true)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } + if(mailItem.item->IsBoundAccountWide() && mailItem.item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); return; } + if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } + if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); @@ -187,14 +222,18 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) } } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); + uint32 itemTextId = 0; if (!body.empty()) { itemTextId = objmgr.CreateItemText( body ); } + pl->ModifyMoney( -int32(reqmoney) ); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); + bool needItemDelay = false; + if(items_count > 0 || money > 0) { if (items_count > 0) @@ -204,12 +243,15 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) MailItem& mailItem = mailItemIter->second; if(!mailItem.item) continue; + mailItem.item_template = mailItem.item ? mailItem.item->GetEntry() : 0; + if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), mailItem.item->GetProto()->Name1, mailItem.item->GetEntry(), mailItem.item->GetCount(), receiver.c_str(), rc_account); } + pl->MoveItemFromInventory(mailItem.item->GetBagSlot(), mailItem.item->GetSlot(), true); CharacterDatabase.BeginTransaction(); mailItem.item->DeleteFromInventoryDB(); //deletes item from character's inventory @@ -218,31 +260,39 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), mailItem.item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); } + // if item send to character at another account, then apply item delivery delay needItemDelay = pl->GetSession()->GetAccountId() != rc_account; } + if(money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(),"GM %s (Account: %u) mail money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } } + // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; + // will delete item or place to receiver mail list WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, pl->GetGUIDLow(), GUID_LOPART(rc), subject, itemTextId, &mi, money, COD, MAIL_CHECK_MASK_NONE, deliver_delay); + CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); } + //called when mail is read void WorldSession::HandleMailMarkAsRead(WorldPacket & recv_data ) { uint64 mailbox; uint32 mailId; recv_data >> mailbox; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + recv_data >> mailId; Player *pl = _player; Mail *m = pl->GetMail(mailId); @@ -256,6 +306,7 @@ void WorldSession::HandleMailMarkAsRead(WorldPacket & recv_data ) m->state = MAIL_STATE_CHANGED; } } + //called when client deletes mail void WorldSession::HandleMailDelete(WorldPacket & recv_data ) { @@ -263,8 +314,10 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data ) uint32 mailId; recv_data >> mailbox; recv_data >> mailId; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + Mail *m = _player->GetMail(mailId); Player* pl = _player; pl->m_mailsUpdated = true; @@ -276,17 +329,21 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data ) pl->SendMailResult(mailId, MAIL_DELETED, MAIL_ERR_INTERNAL_ERROR); return; } + m->state = MAIL_STATE_DELETED; } pl->SendMailResult(mailId, MAIL_DELETED, MAIL_OK); } + void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) { uint64 mailbox; uint32 mailId; recv_data >> mailbox; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + recv_data >> mailId; Player *pl = _player; Mail *m = pl->GetMail(mailId); @@ -303,7 +360,9 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId); CharacterDatabase.CommitTransaction(); pl->RemoveMail(mailId); + MailItemsInfo mi; + if(m->HasItems()) { for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) @@ -315,9 +374,11 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) { //WTF? } + pl->RemoveMItem(itr2->item_guid); } } + if (m->sender == auctionbot.GetAHBplayerGUID()) { SendReturnToSender(MAIL_CREATURE, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId); @@ -326,9 +387,11 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) { SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId); } + delete m; //we can deallocate old mail pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); } + void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, const std::string& subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint16 mailTemplateId ) { if(messageType != MAIL_NORMAL) // return only to players @@ -336,21 +399,27 @@ void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint mi->deleteIncludedItems(true); return; } + Player *receiver = objmgr.GetPlayer(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER)); + uint32 rc_account = 0; if(!receiver) rc_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER)); + if(!receiver && !rc_account) // sender not exist { mi->deleteIncludedItems(true); return; } + // prepare mail and send in other case bool needItemDelay = false; + if(mi && !mi->empty()) { // if item send to character at another account, then apply item delivery delay needItemDelay = sender_acc != rc_account; + // set owner to new receiver (to prevent delete item with sender char deleting) CharacterDatabase.BeginTransaction(); for(MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter) @@ -362,11 +431,14 @@ void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint } CharacterDatabase.CommitTransaction(); } + // If theres is an item, there is a one hour delivery delay. uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; + // will delete item or place to receiver mail list WorldSession::SendMailTo(receiver, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, sender_guid, receiver_guid, subject, itemTextId, mi, money, 0, MAIL_CHECK_MASK_RETURNED,deliver_delay,mailTemplateId); } + //called when player takes item attached in mail void WorldSession::HandleMailTakeItem(WorldPacket & recv_data ) { @@ -374,35 +446,44 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data ) uint32 mailId; uint32 itemId; recv_data >> mailbox; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + recv_data >> mailId; recv_data >> itemId; // item guid low? Player* pl = _player; + Mail* m = pl->GetMail(mailId); if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) { pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } + // prevent cheating with skip client money check if(pl->GetMoney() < m->COD) { pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); return; } + Item *it = pl->GetMItem(itemId); + ItemPosCountVec dest; uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, it, false ); if( msg == EQUIP_ERR_OK ) { m->RemoveItem(itemId); m->removedItems.push_back(itemId); + if (m->COD > 0) //if there is COD, take COD money from player and send them to sender by mail { uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER); Player *receive = objmgr.GetPlayer(sender_guid); + uint32 sender_accId = 0; + if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) { std::string sender_name; @@ -415,6 +496,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data ) { // can be calculated early sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid); + if(!objmgr.GetPlayerNameByGUID(sender_guid,sender_name)) sender_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN); } @@ -423,80 +505,104 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data ) } else if(!receive) sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid); + // check player existence if(receive || sender_accId) { WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, m->receiver, m->sender, m->subject, 0, NULL, m->COD, 0, MAIL_CHECK_MASK_COD_PAYMENT); } + pl->ModifyMoney( -int32(m->COD) ); } m->COD = 0; m->state = MAIL_STATE_CHANGED; pl->m_mailsUpdated = true; pl->RemoveMItem(it->GetGUIDLow()); + uint32 count = it->GetCount(); // save counts before store and possible merge with deleting pl->MoveItemToInventory(dest,it,true); + CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); pl->_SaveMail(); CharacterDatabase.CommitTransaction(); + pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); } else pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); } + void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data ) { uint64 mailbox; uint32 mailId; recv_data >> mailbox; recv_data >> mailId; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + Player *pl = _player; + Mail* m = pl->GetMail(mailId); if(!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) { pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } + pl->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK); + pl->ModifyMoney(m->money); m->money = 0; m->state = MAIL_STATE_CHANGED; pl->m_mailsUpdated = true; + // save money and mail to prevent cheating CharacterDatabase.BeginTransaction(); pl->SaveGoldToDB(); pl->_SaveMail(); CharacterDatabase.CommitTransaction(); } + //called when player lists his received mails void WorldSession::HandleGetMailList(WorldPacket & recv_data ) { uint64 mailbox; recv_data >> mailbox; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + Player* pl = _player; + //load players mails, and mailed items if(!pl->m_mailsLoaded) pl ->_LoadMail(); + // client can't work with packets > max int16 value const uint32 maxPacketSize = 32767; + uint32 mails_count = 0; // real send to client mails amount + WorldPacket data(SMSG_MAIL_LIST_RESULT, (200)); // guess size data << uint8(0); // mail's count time_t cur_time = time(NULL); + for(PlayerMails::iterator itr = pl->GetmailBegin(); itr != pl->GetmailEnd(); ++itr) { // skip deleted or not delivered (deliver delay not expired) mails if ((*itr)->state == MAIL_STATE_DELETED || cur_time < (*itr)->deliver_time) continue; + uint8 item_count = (*itr)->items.size(); // max count is MAX_MAIL_ITEMS (12) + 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); + if(data.wpos()+next_mail_size > maxPacketSize) break; + uint32 show_flags = 0; if ((*itr)->messageType != MAIL_NORMAL) show_flags |= MAIL_SHOW_DELETE; @@ -504,9 +610,11 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data ) show_flags |= MAIL_SHOW_AUCTION; if ((*itr)->HasItems() && (*itr)->messageType == MAIL_NORMAL) show_flags |= MAIL_SHOW_RETURN; + data << (uint16) 0x0040; // unknown 2.3.0, different values data << (uint32) (*itr)->messageID; // Message ID data << (uint8) (*itr)->messageType; // Message Type + switch((*itr)->messageType) { case MAIL_NORMAL: // sender guid @@ -520,6 +628,7 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data ) case MAIL_ITEM: // item entry (?) sender = "Unknown", NYI break; } + data << (uint32) (*itr)->COD; // COD data << (uint32) (*itr)->itemTextId; // sure about this data << (uint32) 0; // unknown @@ -530,7 +639,9 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data ) data << (float) ((*itr)->expire_time-time(NULL))/DAY; data << (uint32) (*itr)->mailTemplateId; // mail template (MailTemplate.dbc) data << (*itr)->subject; // Subject string - once 00, when mail type = 3 + data << (uint8) item_count; // client limit is 0x10 + for(uint8 i = 0; i < item_count; ++i) { Item *item = pl->GetMItem((*itr)->items[i].item_guid); @@ -564,51 +675,68 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data ) // unknown wotlk data << (uint8) 0; } + mails_count += 1; } + data.put<uint8>(0, mails_count); // set real send mails to client SendPacket(&data); + // recalculate m_nextMailDelivereTime and unReadMails _player->UpdateNextMailTimeAndUnreads(); } + ///this function is called when client needs mail message body, or when player clicks on item which has ITEM_FIELD_ITEM_TEXT_ID > 0 void WorldSession::HandleItemTextQuery(WorldPacket & recv_data ) { uint32 itemTextId; uint32 mailId; //this value can be item id in bag, but it is also mail id uint32 unk; //maybe something like state - 0x70000000 + recv_data >> itemTextId >> mailId >> unk; + //some check needed, if player has item with guid mailId, or has mail with id mailId + sLog.outDebug("CMSG_ITEM_TEXT_QUERY itemguid: %u, mailId: %u, unk: %u", itemTextId, mailId, unk); + WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, (4+10));// guess size data << itemTextId; data << objmgr.GetItemText( itemTextId ); SendPacket(&data); } + //used when player copies mail body to his inventory void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data ) { uint64 mailbox; uint32 mailId; + recv_data >> mailbox >> mailId; + if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; + Player *pl = _player; + Mail* m = pl->GetMail(mailId); if(!m || !m->itemTextId || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) { pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_ERR_INTERNAL_ERROR); return; } + Item *bodyItem = new Item; // This is not bag and then can be used new Item. if(!bodyItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), MAIL_BODY_ITEM_TEMPLATE, pl)) { delete bodyItem; return; } + bodyItem->SetUInt32Value( ITEM_FIELD_ITEM_TEXT_ID , m->itemTextId ); bodyItem->SetUInt32Value( ITEM_FIELD_CREATOR, m->sender); + sLog.outDetail("HandleMailCreateTextItem mailid=%u",mailId); + ItemPosCountVec dest; uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, bodyItem, false ); if( msg == EQUIP_ERR_OK ) @@ -616,6 +744,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data ) m->itemTextId = 0; m->state = MAIL_STATE_CHANGED; pl->m_mailsUpdated = true; + pl->StoreItem(dest, bodyItem, true); //bodyItem->SetState(ITEM_NEW, pl); is set automatically pl->SendMailResult(mailId, MAIL_MADE_PERMANENT, MAIL_OK); @@ -626,16 +755,20 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data ) delete bodyItem; } } + //TODO Fix me! ... this void has probably bad condition, but good data are sent void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) { WorldPacket data(MSG_QUERY_NEXT_MAIL_TIME, 8); + if(!_player->m_mailsLoaded) _player->_LoadMail(); + if( _player->unReadMails > 0 ) { data << (uint32) 0; // float data << (uint32) 0; // count + uint32 count = 0; time_t now = time(NULL); for(PlayerMails::iterator itr = _player->GetmailBegin(); itr != _player->GetmailEnd(); ++itr) @@ -644,10 +777,13 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) // must be not checked yet if(m->checked & MAIL_CHECK_MASK_READ) continue; + // and already delivered if(now < m->deliver_time) continue; + data << (uint64) m->sender; // sender guid + switch(m->messageType) { case MAIL_AUCTION: @@ -662,6 +798,7 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) break; } data << (uint32) 0xC6000000; // float unk, time or something + ++count; if(count == 2) // do not display more than 2 mails break; @@ -675,6 +812,7 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ ) } SendPacket(&data); } + void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 stationery, uint32 sender_guidlow_or_entry, uint32 receiver_guidlow, std::string subject, uint32 itemTextId, MailItemsInfo* mi, uint32 money, uint32 COD, uint32 checked, uint32 deliver_delay, uint16 mailTemplateId) { if (receiver_guidlow == auctionbot.GetAHBplayerGUID()) @@ -686,22 +824,28 @@ void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 station return; } uint32 mailId = objmgr.GenerateMailID(); + time_t deliver_time = time(NULL) + deliver_delay; + //expire time if COD 3 days, if no COD 30 days, if auction sale pending 1 hour uint32 expire_delay; if(messageType == MAIL_AUCTION && !mi && !money) // auction mail without any items and money expire_delay = sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY); else expire_delay = (COD > 0) ? 3*DAY : 30*DAY; + time_t expire_time = deliver_time + expire_delay; + if(mailTemplateId && !sMailTemplateStore.LookupEntry(mailTemplateId)) { sLog.outError( "WorldSession::SendMailTo - Mail have not existed MailTemplateId (%u), remove at send", mailTemplateId); mailTemplateId = 0; } + if(receiver) { receiver->AddNewMailDeliverTime(deliver_time); + if ( receiver->IsMailsLoaded() ) { Mail * m = new Mail; @@ -713,15 +857,19 @@ void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 station m->receiver = receiver->GetGUIDLow(); m->subject = subject; m->itemTextId = itemTextId; + if(mi) m->AddAllItems(*mi); + m->expire_time = expire_time; m->deliver_time = deliver_time; m->money = money; m->COD = COD; m->checked = checked; m->state = MAIL_STATE_UNCHANGED; + receiver->AddMail(m); //to insert new mail to beginning of maillist + if(mi) { for(MailItemMap::iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter) @@ -737,11 +885,13 @@ void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 station } else if(mi) mi->deleteIncludedItems(); + CharacterDatabase.BeginTransaction(); CharacterDatabase.escape_string(subject); CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,itemTextId,has_items,expire_time,deliver_time,money,cod,checked) " "VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%u', '%u', '" UI64FMTD "','" UI64FMTD "', '%u', '%u', '%d')", mailId, messageType, stationery, mailTemplateId, sender_guidlow_or_entry, receiver_guidlow, subject.c_str(), itemTextId, (mi && !mi->empty() ? 1 : 0), (uint64)expire_time, (uint64)deliver_time, money, COD, checked); + if(mi) { for(MailItemMap::const_iterator mailItemIter = mi->begin(); mailItemIter != mi->end(); ++mailItemIter) |