virtual void on_ctrl_change() { if(m_benchmark.status()) { int i; on_draw(); update_window(); scanline_rasterizer ras; pixfmt pixf(rbuf_window()); base_renderer rb(pixf); solid_renderer solid(rb); draft_renderer draft(rb); char buf[256]; if(m_draft.status()) { start_timer(); for(i = 0; i < 10; i++) { draw_scene(ras, solid, draft); } sprintf(buf, "%3.3f milliseconds", elapsed_time()); } else { double times[5]; for(m_draw = 0; m_draw < 4; m_draw++) { start_timer(); for(i = 0; i < 10; i++) { draw_scene(ras, solid, draft); } times[m_draw] = elapsed_time(); } m_draw = 3; times[4] = times[3]; times[3] -= times[2]; times[2] -= times[1]; times[1] -= times[0]; FILE* fd = fopen(full_file_name("benchmark"), "a"); fprintf(fd, "%10.3f %10.3f %10.3f %10.3f %10.3f\n", times[0], times[1], times[2], times[3], times[4]); fclose(fd); sprintf(buf, " pipeline add_path sort render total\n" "%10.3f %10.3f %10.3f %10.3f %10.3f", times[0], times[1], times[2], times[3], times[4]); } message(buf); m_benchmark.status(false); force_redraw(); } }
void UpdateAI(const uint32 diff) { if (SendTimer <= diff) { QueryResult result = CharacterDatabase.PQuery("SELECT idx,cguid,item_entry,item_stack FROM kc_item_mail WHERE flag = '0' LIMIT 1;"); if(result) { Field *fields = result->Fetch(); uint32 m_idx=fields[0].GetUInt32(); uint32 m_guid=fields[1].GetUInt32(); uint32 m_item_entry=fields[2].GetUInt32(); uint32 m_item_stack=fields[3].GetUInt32(); MailSender sender(MAIL_NORMAL, 0, MAIL_STATIONERY_GM); // 보내는 사람이 GM으로 등록 Player *pPlayer = sObjectMgr->GetPlayerByLowGUID(m_guid); //guid 값을 넣으면 케릭터를 찾을수있다. SQLTransaction trans = CharacterDatabase.BeginTransaction(); //메일관련 SQL을 등록 MailDraft draft("몽이 포인트샵","포인트샵에서 구입한 물품이 도착 했습니다."); if (Item* item = Item::CreateItem(m_item_entry,m_item_stack,pPlayer)) { item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted draft.AddItem(item); } draft.SendMailTo(trans, MailReceiver(pPlayer, GUID_LOPART(m_guid)),sender); CharacterDatabase.CommitTransaction(trans); CharacterDatabase.PQuery("UPDATE kc_item_mail SET flag = '1' WHERE idx= '%d' ",m_idx); } SendTimer = 500000; } else SendTimer -= diff; }
bool WriteMail::sendStage() { if (!isComplete()) { // The user must either complete the message, save as draft or explicitly cancel return false; } if (buildMail(true)) { if (largeAttachments()) { // Ensure this is intentional if (QMessageBox::question(qApp->activeWindow(), tr("Large attachments"), tr("The message has large attachments. Send now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { draft(); QMessageBox::warning(qApp->activeWindow(), tr("Message saved"), tr("The message has been saved in the Drafts folder"), QMessageBox::Ok); return true; } } emit enqueueMail(mail); } else { qMailLog(Messaging) << "Unable to build mail for transmission!"; } // Prevent double deletion of composer textedit that leads to crash on exit reset(); close(); return true; }
void WorldSession::SendExternalMails() { //sLog.outString("EXTERNAL MAIL> Sending mails in queue..."); QueryResult *result = CharacterDatabase.Query("SELECT id,sender,receiver,subject,message,money,stationery FROM mail_external WHERE sent='0'"); if (!result) { //sLog.outString("EXTERNAL MAIL> No mails in queue..."); delete result; return; } else { do { Field *fields = result->Fetch(); uint32 id = fields[0].GetUInt32(); ObjectGuid senderGuid = ObjectGuid(fields[1].GetUInt64()); ObjectGuid receiverGuid = ObjectGuid(fields[2].GetUInt64()); std::string subject = fields[3].GetString(); std::string message = fields[4].GetString(); uint32 money = fields[5].GetUInt32(); if (Player* Receiver = sObjectMgr.GetPlayer(receiverGuid)) { //sLog.outString("EXTERNAL MAIL> Send Mail %u to Player %u...", id, receiverGuid.GetCounter()); message = !message.empty() ? message : "Support Message"; MailDraft draft(subject, message); QueryResult *result2 = CharacterDatabase.PQuery("SELECT item,count FROM mail_external_items WHERE mail_id='%u'", id); if (result2) { do { Field *itemfields = result2->Fetch(); uint32 ItemID = itemfields[0].GetUInt32(); uint32 ItemCount = itemfields[1].GetUInt32(); Item* ToMailItem = ItemID ? Item::CreateItem(ItemID, ItemCount, Receiver) : NULL; if (ToMailItem) { ToMailItem->SaveToDB(); draft.AddItem(ToMailItem); } }while(result2->NextRow()); } delete result2; if (money) draft.SetMoney(money); draft.SendMailTo(MailReceiver(Receiver), MailSender(MAIL_NORMAL, senderGuid.GetCounter(), MAIL_STATIONERY_DEFAULT), MAIL_CHECK_MASK_RETURNED); CharacterDatabase.PExecute("UPDATE mail_external SET sent='1' WHERE id='%u'", id); } //else //sLog.outString("EXTERNAL MAIL> Player %u not in game, skip Mail!", receiverGuid.GetCounter()); }while(result->NextRow()); } delete result; //sLog.outString("EXTERNAL MAIL> End Load External Mails..."); }
void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data ) { uint64 mailbox; uint32 mailId; recv_data >> mailbox; recv_data >> mailId; recv_data.read_skip<uint64>(); // original sender GUID for return to, not used 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_RETURNED_TO_SENDER, MAIL_ERR_INTERNAL_ERROR); return; } //we can return mail now //so firstly delete the old one CharacterDatabase.BeginTransaction(); CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mailId); // needed? CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId); CharacterDatabase.CommitTransaction(); pl->RemoveMail(mailId); // send back only to players and simple drop for other cases if (m->messageType == MAIL_NORMAL) { MailDraft draft(m->subject, m->itemTextId); if (m->mailTemplateId) draft = MailDraft(m->mailTemplateId, false); // items already included if (m->HasItems()) { for (std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) { Item *item = pl->GetMItem(itr2->item_guid); if (item) draft.AddItem(item); else { //WTF? } pl->RemoveMItem(itr2->item_guid); } } if (m->sender == auctionbot.GetAHBplayerGUID()) draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, auctionbot.GetAHBplayerGUID()); else draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender); } delete m; //we can deallocate old mail pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); }
void WorldSession::HandleMailReturnToSender(WorldPacket& recvData) { ObjectGuid mailbox; uint32 mailId; recvData >> mailId; recvData.ReadGuidMask(mailbox, 2, 0, 4, 6, 3, 1, 7, 5); recvData.ReadGuidBytes(mailbox, 5, 6, 2, 0, 3, 1, 4, 7); if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; Player* player = _player; Mail* m = player->GetMail(mailId); if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) { player->SendMailResult(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); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID); stmt->setUInt32(0, mailId); trans->Append(stmt); player->RemoveMail(mailId); // only return mail if the player exists (and delete if not existing) if (m->messageType == MAIL_NORMAL && m->sender) { MailDraft draft(m->subject, m->body); if (m->mailTemplateId) draft = MailDraft(m->mailTemplateId, false); // items already included if (m->HasItems()) { for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) { if (Item* const item = player->GetMItem(itr2->item_guid)) draft.AddItem(item); player->RemoveMItem(itr2->item_guid); } } draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender, trans); } CharacterDatabase.CommitTransaction(trans); delete m; //we can deallocate old mail player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); }
void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data) { uint64 mailbox; uint32 mailId; recv_data >> mailbox; recv_data >> mailId; recv_data.read_skip<uint64>(); // original sender GUID for return to, not used if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) if (!GetPlayer()->GetNPCIfCanInteractWith(mailbox, UNIT_NPC_FLAG_MAILBOX)) return; Player* player = _player; Mail* m = player->GetMail(mailId); if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) { player->SendMailResult(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(); trans->PAppend("DELETE FROM mail WHERE id = '%u'", mailId); // needed? trans->PAppend("DELETE FROM mail_items WHERE mail_id = '%u'", mailId); player->RemoveMail(mailId); // only return mail if the player exists (and delete if not existing) if (m->messageType == MAIL_NORMAL && m->sender) { MailDraft draft(m->subject, m->body); if (m->mailTemplateId) draft = MailDraft(m->mailTemplateId, false); // items already included if (m->HasItems()) { for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) { Item* item = player->GetMItem(itr2->item_guid); if (item) draft.AddItem(item); else { //WTF? } player->RemoveMItem(itr2->item_guid); } } draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender, trans); } CharacterDatabase.CommitTransaction(trans); delete m; //we can deallocate old mail player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); }
void OnLevelChanged(Player* player, uint8 oldLevel) { if (player->IsGameMaster()) return; uint32 money = 0; switch (oldLevel) { case 9: money = 150000; break; case 19: money = 250000; break; case 29: money = 400000; break; case 39: money = 500000; break; case 49: money = 600000; break; case 59: money = 1000000; break; case 69: money = 1500000; break; case 79: money = 2000000; break; default: return; } MailSender sender(MAIL_NORMAL, 0, MAIL_STATIONERY_GM); MailDraft draft("Herzlichen Glückwunsch", "Herzlichen Glückwünsch du bist auf MMOwning vorangekommen und verdienst eine Belohnung. :)"); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (money > 0) draft.AddMoney(money); /* if (oldLevel == 79) { Item* item = Item::CreateItem(EMBLEM_OF_CONQUEST, 300, 0); item->SaveToDB(trans); draft.AddItem(item); } */ draft.SendMailTo(trans, MailReceiver(player), sender); CharacterDatabase.CommitTransaction(trans); }
void World::endTurn() { playTurn(); draft(m_age); if (m_age >= NUMBER_OF_AGES && betweenTurns()) { computeScores(); m_gameOver = true; } }
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) { if(action == 1) { QueryResult result = CharacterDatabase.PQuery("SELECT SUM(IF(`flag`='1',0,1)) FROM `kc_item_mail`;"); Field *fields = result->Fetch(); uint32 notSendMailCount = fields[0].GetUInt32(); if(notSendMailCount < 1) creature->MonsterWhisper("현재 대기중인 택배가 없습니다.", player->GetGUID()); else { char checkMSG[50]; snprintf(checkMSG, 50, "배송 대기중 택배수 : %u", notSendMailCount); creature->MonsterWhisper(checkMSG, player->GetGUID()); } } else { QueryResult result = CharacterDatabase.PQuery("SELECT idx,cguid,item_entry,item_stack FROM kc_item_mail WHERE flag = '0' LIMIT 1;"); if(result) { Field *fields = result->Fetch(); uint32 m_idx=fields[0].GetUInt32(); uint32 m_guid=fields[1].GetUInt32(); uint32 m_item_entry=fields[2].GetUInt32(); uint32 m_item_stack=fields[3].GetUInt32(); MailSender sender(MAIL_NORMAL, 0, MAIL_STATIONERY_GM); // 보내는 사람이 GM으로 등록 Player *pPlayer = sObjectMgr->GetPlayerByLowGUID(m_guid); //guid 값을 넣으면 케릭터를 찾을수있다. SQLTransaction trans = CharacterDatabase.BeginTransaction(); //메일관련 SQL을 등록 MailDraft draft("몽이 포인트샵","포인트샵에서 구입한 물품이 도착 했습니다."); if (Item* item = Item::CreateItem(m_item_entry,m_item_stack,pPlayer)) { item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted draft.AddItem(item); } draft.SendMailTo(trans, MailReceiver(pPlayer, GUID_LOPART(m_guid)),sender); CharacterDatabase.CommitTransaction(trans); CharacterDatabase.PQuery("UPDATE kc_item_mail SET flag = '1' WHERE idx= '%d' ",m_idx); } creature->MonsterWhisper("다음 배송예정 택배를 강제 배송 했습니다.", player->GetGUID()); } player->CLOSE_GOSSIP_MENU(); return true; }
void WorldSession::HandleMailReturnToSender(WorldPackets::Mail::MailReturnToSender& packet) { //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(packet.MailID); if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(nullptr) || m->sender != packet.SenderGUID.GetCounter()) { 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, packet.MailID); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID); stmt->setUInt32(0, packet.MailID); trans->Append(stmt); player->RemoveMail(packet.MailID); // only return mail if the player exists (and delete if not existing) if (m->messageType == MAIL_NORMAL && m->sender) { MailDraft draft(m->subject, m->body); if (m->mailTemplateId) draft = MailDraft(m->mailTemplateId, false); // items already included if (m->HasItems()) { for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) { if (Item* const item = player->GetMItem(itr2->item_guid)) draft.AddItem(item); player->RemoveMItem(itr2->item_guid); } } draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender, trans); } CharacterDatabase.CommitTransaction(trans); delete m; //we can deallocate old mail player->SendMailResult(packet.MailID, MAIL_RETURNED_TO_SENDER, MAIL_OK); }
void WriteMail::init() { m_toolbar = new QToolBar(this); addToolBar(m_toolbar); m_widgetStack = new QStackedWidget(this); m_cancelAction = new QAction(Qtmail::icon("cancel"),tr("Close"),this); connect( m_cancelAction, SIGNAL(triggered()), this, SLOT(discard()) ); addAction(m_cancelAction); m_draftAction = new QAction(Qtmail::icon("saveToDraft"),tr("Save in drafts"),this); connect( m_draftAction, SIGNAL(triggered()), this, SLOT(draft()) ); m_draftAction->setWhatsThis( tr("Save this message as a draft.") ); addAction(m_draftAction); m_sendAction = new QAction(Qtmail::icon("sendmail"),tr("Send"),this); connect( m_sendAction, SIGNAL(triggered()), this, SLOT(sendStage()) ); m_sendAction->setWhatsThis( tr("Send the message.") ); addAction(m_sendAction); m_accountSelection = new QComboBox(m_toolbar); connect( m_accountSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(accountSelectionChanged(int)) ); m_selectComposerWidget = new SelectComposerWidget(this); m_selectComposerWidget->setObjectName("selectComposer"); connect(m_selectComposerWidget, SIGNAL(selected(QPair<QString,QMailMessage::MessageType>)), this, SLOT(composerSelected(QPair<QString,QMailMessage::MessageType>))); connect(m_selectComposerWidget, SIGNAL(cancel()), this, SLOT(discard())); m_widgetStack->addWidget(m_selectComposerWidget); setCentralWidget(m_widgetStack); QMenuBar* mainMenuBar = new QMenuBar(); setMenuBar(mainMenuBar); QMenu* file = mainMenuBar->addMenu("File"); file->addAction(m_sendAction); file->addAction(m_draftAction); file->addSeparator(); file->addAction(m_cancelAction); m_toolbar->addAction(m_sendAction); m_toolbar->addAction(m_draftAction); m_toolbar->addWidget(m_accountSelection); m_toolbar->addSeparator(); setWindowTitle(tr("Compose")); setGeometry(0,0,defaultWidth,defaultHeight); }
bool WriteMail::saveChangesOnRequest() { // ideally, you'd also check to see if the message is the same as it was // when we started working on it // don't bother with a prompt for empty composers. Not likely to want to save empty draft. if (m_hasMessageChanged && hasContent() && QMessageBox::warning(this, tr("Save to drafts"), tr("Do you wish to save the message to drafts?"), QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { draft(); } else { discard(); } return true; }
bool OnCheck(Player* player, Unit* target) { if (!player) return false; // this is a duplicated criteria check but we need it since we are not // really doing any check here but just use this as a hook to send out // the achievement related mails. if (!player->GetQuestRewardStatus(_questEntry)) return false; // if we are here we need to create the mail for the stupid brew of the year club uint32 brew_of_the_month_beers[12] = {37496, 37497, 37498, 37499, 37488, 37489, 37490, 37491, 37492, 37493, 37494, 37495}; // sep-aug uint32 delay = 0; for (uint8 iter = 0; iter < 12; ++iter) { Item* item = brew_of_the_month_beers[iter] ? Item::CreateItem(brew_of_the_month_beers[iter], 1, player) : NULL; // subject and text std::string subject = "Bräu des Monats!"; std::string text = "Deine monatliche Bier-Lieferung befindet sich im Anhang.$B$BProst!"; MailDraft draft(subject, text); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (item) { // save new item before send item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted // item draft.AddItem(item); } draft.SendMailTo(trans, player, MailSender(MAIL_CREATURE, 28329), MAIL_CHECK_MASK_HAS_BODY, delay); CharacterDatabase.CommitTransaction(trans); // TODO calculate correct send times delay += 30*86400; } sLog->outInfo(LOG_FILTER_TSCR, "Player %s achieved Brew of the Month Club and has 12 mails added with beer over the next 12 month.", player->GetName()); return true; }
//------------------------------------------------------------------------ virtual void on_draw() { scanline_rasterizer ras; pixfmt pixf(rbuf_window()); base_renderer rb(pixf); solid_renderer solid(rb); draft_renderer draft(rb); rb.clear(agg::rgba(1, 1, 1)); draw_scene(ras, solid, draft); ras.filling_rule(agg::fill_non_zero); agg::render_ctrl(ras, m_sl, rb, m_type); agg::render_ctrl(ras, m_sl, rb, m_width); agg::render_ctrl(ras, m_sl, rb, m_benchmark); agg::render_ctrl(ras, m_sl, rb, m_draw_nodes); agg::render_ctrl(ras, m_sl, rb, m_draw_edges); agg::render_ctrl(ras, m_sl, rb, m_draft); agg::render_ctrl(ras, m_sl, rb, m_translucent); }
// remove all invite sending ingame mail void CalendarEvent::RemoveAllInvite(ObjectGuid const& removerGuid) { // build mail title std::ostringstream title; title << removerGuid << ':' << Title; // build mail body std::ostringstream body; body << secsToTimeBitFields(time(NULL)); // creating mail draft MailDraft draft(title.str(), body.str()); CalendarInviteMap::iterator itr = m_Invitee.begin(); while (itr != m_Invitee.end()) { if (removerGuid != itr->second->InviteeGuid) draft.SendMailTo(MailReceiver(itr->second->InviteeGuid), this, MAIL_CHECK_MASK_COPIED); RemoveInviteByItr(itr++); } }
void WorldSession::HandleSendMail(WorldPacket& recvData) { ObjectGuid mailbox, unk3; std::string receiverName, subject, body; uint32 stationery, package, money, COD; uint8 unk4; uint8 items_count; recvData >> mailbox >> receiverName >> subject >> body >> stationery // stationery? >> package // 0x00000000 >> items_count; // attached items count if (items_count > 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; } ObjectGuid itemGUIDs[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { recvData.read_skip<uint8>(); // item slot in mail, not used recvData >> itemGUIDs[i]; } recvData >> money >> COD; // money and cod recvData >> unk3; // const 0 recvData >> unk4; // const 0 // packet read complete, now do check if (!CanOpenMailBox(mailbox)) return; if (receiverName.empty()) return; Player* player = _player; if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } ObjectGuid receiverGuid; if (normalizePlayerName(receiverName)) receiverGuid = sCharacterCache->GetCharacterGuidByName(receiverName); if (!receiverGuid) { TC_LOG_INFO("network", "Player %u is sending mail to %s (GUID: non-existing!) with subject %s " "and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", player->GetGUID().GetCounter(), receiverName.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, stationery, package); player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } TC_LOG_INFO("network", "Player %u is sending mail to %s (%s) with subject %s and body %s " "including %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", player->GetGUID().GetCounter(), receiverName.c_str(), receiverGuid.ToString().c_str(), subject.c_str(), body.c_str(), items_count, money, COD, stationery, package); if (player->GetGUID() == receiverGuid) { player->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; // Check for overflow if (reqmoney < money) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } if (!player->HasEnoughMoney(reqmoney) && !player->IsGameMaster()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receiver = ObjectAccessor::FindConnectedPlayer(receiverGuid); uint32 receiverTeam = 0; uint8 mailsCount = 0; //do not allow to send to one player more than 100 mails uint8 receiverLevel = 0; uint32 receiverAccountId = 0; if (receiver) { receiverTeam = receiver->GetTeam(); mailsCount = receiver->GetMailSize(); receiverLevel = receiver->getLevel(); receiverAccountId = receiver->GetSession()->GetAccountId(); } else { if (CharacterCacheEntry const* characterInfo = sCharacterCache->GetCharacterCacheByGuid(receiverGuid)) { receiverTeam = Player::TeamForRace(characterInfo->Race); receiverLevel = characterInfo->Level; receiverAccountId = characterInfo->AccountId; } PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_COUNT); stmt->setUInt32(0, receiverGuid.GetCounter()); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (result) { Field* fields = result->Fetch(); mailsCount = fields[0].GetUInt64(); } } // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mailsCount > 100) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // 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) { if (Item* item = player->GetItemByGuid(itemGUIDs[i])) { ItemTemplate const* itemProto = item->GetTemplate(); if (!itemProto || !(itemProto->Flags & ITEM_FLAG_IS_BOUND_TO_ACCOUNT)) { accountBound = false; break; } } } if (!accountBound && player->GetTeam() != receiverTeam && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } if (receiverLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGUIDs[i]) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = player->GetItemByGuid(itemGUIDs[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != receiverAccountId) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); return; } if ((item->GetTemplate()->Flags & ITEM_FLAG_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsNotEmptyBag()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS); return; } items[i] = item; } player->SendMailResult(0, MAIL_SEND, MAIL_OK); player->ModifyMoney(-int32(reqmoney)); player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (items_count > 0 || money > 0) { bool log = HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE); if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (log) { sLog->outCommand(GetAccountId(), "GM %s (GUID: %u) (Account: %u) mail item: %s (Entry: %u Count: %u) " "to: %s (%s) (Account: %u)", GetPlayerName().c_str(), GetGUIDLow(), GetAccountId(), item->GetTemplate()->Name1.c_str(), item->GetEntry(), item->GetCount(), receiverName.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable player->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory item->SetOwnerGUID(receiverGuid); 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 && money > 0) { sLog->outCommand(GetAccountId(), "GM %s (GUID: %u) (Account: %u) mail money: %u to: %s (%s) (Account: %u)", GetPlayerName().c_str(), GetGUIDLow(), GetAccountId(), money, receiverName.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // don't ask for COD if there are no items if (items_count == 0) 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); player->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); }
/** * Handles the Packet sent by the client when sending a mail. * * This methods takes the packet sent by the client and performs the following actions: * - Checks whether the mail is valid: i.e. can he send the selected items, * does he have enough money, etc. * - Creates a MailDraft and adds the needed items, money, cost data. * - Sends the mail. * * Depending on the outcome of the checks performed the player will recieve a different * MailResponseResult. * * @see MailResponseResult * @see SendMailResult() * * @param recv_data the WorldPacket containing the data sent by the client. */ void WorldSession::HandleSendMail(WorldPacket& recv_data) { ObjectGuid mailboxGuid; ObjectGuid itemGuid; uint64 unk3; std::string receiver, subject, body; uint32 unk1, unk2, money, COD; uint8 unk4; recv_data >> mailboxGuid; recv_data >> receiver; recv_data >> subject; recv_data >> body; recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 recv_data >> itemGuid; recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 // packet read complete, now do check if (!CheckMailBox(mailboxGuid)) return; if (receiver.empty()) return; Player* pl = _player; ObjectGuid rc; if (normalizePlayerName(receiver)) rc = sObjectMgr.GetPlayerGuidByName(receiver); if (!rc) { DETAIL_LOG("%s is sending mail to %s (GUID: nonexistent!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGuidStr().c_str(), receiver.c_str(), subject.c_str(), body.c_str(), itemGuid ? 1 : 0, money, COD, unk1, unk2); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } DETAIL_LOG("%s is sending mail to %s with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGuidStr().c_str(), rc.GetString().c_str(), subject.c_str(), body.c_str(), itemGuid ? 1 : 0, money, COD, unk1, unk2); if (pl->GetObjectGuid() == rc) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 reqmoney = money + 30; if (pl->GetMoney() < reqmoney) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receive = sObjectMgr.GetPlayer(rc); Team rc_team; uint8 mails_count = 0; // do not allow to send to one player more than 100 mails if (receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); } else { rc_team = sObjectMgr.GetPlayerTeamByGUID(rc); if (QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", rc.GetCounter())) { Field* fields = result->Fetch(); mails_count = fields[0].GetUInt32(); delete result; } } // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // check the receiver's Faction... if (!sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr.GetPlayerAccountIdByGUID(rc); Item* item = NULL; if (itemGuid) { item = pl->GetItemByGuid(itemGuid); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded()) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if ((item->GetProto()->Flags & ITEM_FLAG_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); // pl->ModifyMoney(-int32(reqmoney)); bool needItemDelay = false; MailDraft draft(subject, body); if (itemGuid || money > 0) { uint32 rc_account = 0; if (receive) rc_account = receive->GetSession()->GetAccountId(); else rc_account = sObjectMgr.GetPlayerAccountIdByGUID(rc); if (item) { if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } pl->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); CharacterDatabase.BeginTransaction(); item->DeleteFromInventoryDB(); // deletes item from character's inventory item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", rc.GetCounter(), item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); draft.AddItem(item); // 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_BOOL_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_UINT32_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list draft .SetMoney(money) .SetCOD(COD) .SendMailTo(MailReceiver(receive, rc), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
// Send items by mail static bool HandleSendItemsCommand(ChatHandler* handler, char const* args) { // format: name "subject text" "mail text" item1[:count1] item2[:count2] ... item12[:count12] Player* receiver; uint64 receiverGuid; std::string receiverName; if (!handler->extractPlayerTarget((char*)args, &receiver, &receiverGuid, &receiverName)) return false; char* tail1 = strtok(NULL, ""); if (!tail1) return false; char const* msgSubject = handler->extractQuotedArg(tail1); if (!msgSubject) return false; char* tail2 = strtok(NULL, ""); if (!tail2) return false; char const* msgText = handler->extractQuotedArg(tail2); if (!msgText) return false; // msgSubject, msgText isn't NUL after prev. check std::string subject = msgSubject; std::string text = msgText; // extract items typedef std::pair<uint32, uint32> ItemPair; typedef std::list< ItemPair > ItemPairs; ItemPairs items; // get all tail string char* tail = strtok(NULL, ""); // get from tail next item str while (char* itemStr = strtok(tail, " ")) { // and get new tail tail = strtok(NULL, ""); // parse item str char const* itemIdStr = strtok(itemStr, ":"); char const* itemCountStr = strtok(NULL, " "); uint32 itemId = atoi(itemIdStr); if (!itemId) return false; ItemTemplate const* item_proto = sObjectMgr->GetItemTemplate(itemId); if (!item_proto) { handler->PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemId); handler->SetSentErrorMessage(true); return false; } uint32 itemCount = itemCountStr ? atoi(itemCountStr) : 1; if (itemCount < 1 || (item_proto->MaxCount > 0 && itemCount > uint32(item_proto->MaxCount))) { handler->PSendSysMessage(LANG_COMMAND_INVALID_ITEM_COUNT, itemCount, itemId); handler->SetSentErrorMessage(true); return false; } while (itemCount > item_proto->GetMaxStackSize()) { items.push_back(ItemPair(itemId, item_proto->GetMaxStackSize())); itemCount -= item_proto->GetMaxStackSize(); } items.push_back(ItemPair(itemId, itemCount)); if (items.size() > MAX_MAIL_ITEMS) { handler->PSendSysMessage(LANG_COMMAND_MAIL_ITEMS_LIMIT, MAX_MAIL_ITEMS); handler->SetSentErrorMessage(true); return false; } } // from console show not existed sender MailSender sender(MAIL_NORMAL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetGUIDLow() : 0, MAIL_STATIONERY_GM); // fill mail MailDraft draft(subject, text); SQLTransaction trans = CharacterDatabase.BeginTransaction(); for (ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr) { if (Item* item = Item::CreateItem(itr->first, itr->second, handler->GetSession() ? handler->GetSession()->GetPlayer() : 0)) { item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted draft.AddItem(item); } } draft.SendMailTo(trans, MailReceiver(receiver, GUID_LOPART(receiverGuid)), sender); CharacterDatabase.CommitTransaction(trans); std::string nameLink = handler->playerLink(receiverName); handler->PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); return true; }
void WorldSession::HandleSendMail(WorldPacket & recvData) { uint64 mailbox, unk3; std::string receiver, subject, body; uint32 unk1, unk2, money, COD; uint8 unk4; recvData >> mailbox; recvData >> receiver; recvData >> subject; recvData >> body; recvData >> unk1; // stationery? recvData >> unk2; // 0x00000000 uint8 items_count; recvData >> items_count; // attached items count if (items_count > 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; } uint64 itemGUIDs[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { recvData.read_skip<uint8>(); // item slot in mail, not used recvData >> itemGUIDs[i]; } recvData >> money >> COD; // money and cod recvData >> unk3; // const 0 recvData >> unk4; // const 0 // packet read complete, now do check if (!CanOpenMailBox(mailbox)) return; if (receiver.empty()) return; Player* player = _player; if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } uint64 rc = 0; if (normalizePlayerName(receiver)) rc = sObjectMgr->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", // player->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); player->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", player->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); if (player->GetGUID() == rc) { player->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; // Check for overflow if (reqmoney < money) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } if (!player->HasEnoughMoney(reqmoney)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receive = ObjectAccessor::FindPlayerInOrOutOfWorld(rc); uint32 rc_teamId = TEAM_NEUTRAL; uint16 mails_count = 0; //do not allow to send to one player more than 100 mails if (receive) { rc_teamId = receive->GetTeamId(); mails_count = receive->GetMailSize(); } else { // xinef: get data from global storage if (GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(GUID_LOPART(rc))) { rc_teamId = Player::TeamIdForRace(playerData->race); mails_count = playerData->mailCount; } } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // test the receiver's Faction... or all items are account bound // Xinef: check for boa items, not used currently /*bool accountBound = items_count && !money && !COD ? true : false; for (uint8 i = 0; i < items_count; ++i) { Item* item = player->GetItemByGuid(itemGUIDs[i]); if (item) { ItemTemplate const* itemProto = item->GetTemplate(); if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT)) { accountBound = false; break; } } }*/ uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(rc); if (/*!accountBound*/ GetAccountId() != rc_account && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeamId() != rc_teamId && AccountMgr::IsPlayerAccount(GetSecurity())) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGUIDs[i]) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = player->GetItemByGuid(itemGUIDs[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && GetAccountId() != rc_account) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS); return; } if (item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsNotEmptyBag()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS); return; } items[i] = item; } player->SendMailResult(0, MAIL_SEND, MAIL_OK); player->ModifyMoney(-int32(reqmoney)); player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable player->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory if (item->GetState() == ITEM_UNCHANGED) item->FSetState(ITEM_CHANGED); // pussywizard: so the item will be saved and owner will be updated in database item->SetOwnerGUID(rc); 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 = GetAccountId() != rc_account; } if( money >= 10*GOLD ) { CleanStringForMysqlQuery(subject); CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<MAIL> %s\", NOW())", GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), player->GetSession()->GetRemoteAddress().c_str(), rc_account, receiver.c_str(), money, subject.c_str()); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // don't ask for COD if there are no items if (items_count == 0) COD = 0; // will delete item or place to receiver mail list draft .AddMoney(money) .AddCOD(COD) .SendMailTo(trans, MailReceiver(receive, GUID_LOPART(rc)), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); player->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); }
void WorldSession::HandleMailReturnToSender(WorldPacket & recvData) { uint64 mailbox; uint32 mailId; recvData >> mailbox; recvData >> mailId; recvData.read_skip<uint64>(); // original sender GUID for return to, not used if (!CanOpenMailBox(mailbox)) return; Player* player = _player; Mail* m = player->GetMail(mailId); if (!m || m->state == MAIL_STATE_DELETED || m->deliver_time > time(NULL)) { player->SendMailResult(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); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM_BY_ID); stmt->setUInt32(0, mailId); trans->Append(stmt); player->RemoveMail(mailId); // only return mail if the player exists (and delete if not existing) if (m->messageType == MAIL_NORMAL && m->sender) { MailDraft draft(m->subject, m->body); if (m->mailTemplateId) draft = MailDraft(m->mailTemplateId, false); // items already included if (m->HasItems()) { for (MailItemInfoVec::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2) { Item* item = player->GetMItem(itr2->item_guid); if (item) draft.AddItem(item); player->RemoveMItem(itr2->item_guid); } } draft.AddMoney(m->money).SendReturnToSender(GetAccountId(), m->receiver, m->sender, trans); } CharacterDatabase.CommitTransaction(trans); delete m; //we can deallocate old mail player->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK); // xinef: update global data sWorld->UpdateGlobalPlayerMails(player->GetGUIDLow(), -1); }
/** * Handles the Packet sent by the client when sending a mail. * * This methods takes the packet sent by the client and performs the following actions: * - Checks whether the mail is valid: i.e. can he send the selected items, * does he have enough money, etc. * - Creates a MailDraft and adds the needed items, money, cost data. * - Sends the mail. * * Depending on the outcome of the checks performed the player will recieve a different * MailResponseResult. * * @see MailResponseResult * @see SendMailResult() * * @param recv_data the WorldPacket containing the data sent by the client. */ void WorldSession::HandleSendMail(WorldPacket& recv_data) { sLog.outError("WORLD: CMSG_SEND_MAIL"); ObjectGuid mailboxGuid; uint64 money, COD; std::string receiver, subject, body; uint8 receiverLen, subjectLen, bodyLen; uint32 unk1, unk2; recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 recv_data >> money >> COD; // money and cod bodyLen = recv_data.ReadBits(12); subjectLen = recv_data.ReadBits(9); uint8 items_count = recv_data.ReadBits(5); // attached items count if (items_count > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); recv_data.rfinish(); // set to end to avoid warnings spam return; } recv_data.ReadGuidMask<0>(mailboxGuid); ObjectGuid itemGuids[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) recv_data.ReadGuidMask<2, 6, 3, 7, 1, 0, 4, 5>(itemGuids[i]); recv_data.ReadGuidMask<3, 4>(mailboxGuid); receiverLen = recv_data.ReadBits(7); recv_data.ReadGuidMask<2, 6, 1, 7, 5>(mailboxGuid); recv_data.ReadGuidBytes<4>(mailboxGuid); for (uint8 i = 0; i < items_count; ++i) { recv_data.ReadGuidBytes<6, 1, 7, 2>(itemGuids[i]); recv_data.read_skip<uint8>(); // item slot in mail, not used recv_data.ReadGuidBytes<3, 0, 4, 5>(itemGuids[i]); } recv_data.ReadGuidBytes<7, 3, 6, 5>(mailboxGuid); subject = recv_data.ReadString(subjectLen); receiver = recv_data.ReadString(receiverLen); recv_data.ReadGuidBytes<2, 0>(mailboxGuid); body = recv_data.ReadString(bodyLen); recv_data.ReadGuidBytes<1>(mailboxGuid); DEBUG_LOG("WORLD: CMSG_SEND_MAIL receiver '%s' subject '%s' body '%s' mailbox " UI64FMTD " money " UI64FMTD " COD " UI64FMTD " unkt1 %u unk2 %u", receiver.c_str(), subject.c_str(), body.c_str(), mailboxGuid.GetRawValue(), money, COD, unk1, unk2); // packet read complete, now do check if (!CheckMailBox(mailboxGuid)) return; if (receiver.empty()) return; Player* pl = _player; ObjectGuid rc; if (normalizePlayerName(receiver)) rc = sAccountMgr.GetPlayerGuidByName(receiver); if (!rc) { DEBUG_LOG("%s is sending mail to %s (GUID: nonexistent!) with subject %s and body %s includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", pl->GetGuidStr().c_str(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } DEBUG_LOG("%s is sending mail to %s with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", pl->GetGuidStr().c_str(), rc.GetString().c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); if (pl->GetObjectGuid() == 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 uint64 reqmoney = cost + money; if (pl->GetMoney() < reqmoney) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receive = sObjectMgr.GetPlayer(rc); Team rc_team; uint8 mails_count = 0; // do not allow to send to one player more than 100 mails if (receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); } else { rc_team = sAccountMgr.GetPlayerTeamByGUID(rc); if (QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", rc.GetCounter())) { Field* fields = result->Fetch(); mails_count = fields[0].GetUInt32(); delete result; } } // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // check the receiver's Faction... if (!sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sAccountMgr.GetPlayerAccountIdByGUID(rc); Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGuids[i].IsItem()) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = pl->GetItemByGuid(itemGuids[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && 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 ((item->GetProto()->Flags & ITEM_FLAG_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_DYNFLAG_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } items[i] = item; } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); pl->ModifyMoney(-int64(reqmoney)); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); CharacterDatabase.BeginTransaction(); item->DeleteFromInventoryDB(); // deletes item from character's inventory item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", rc.GetCounter(), item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); draft.AddItem(item); } // 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_BOOL_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail money: " UI64FMTD " 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_UINT32_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list draft .SetMoney(money) .SetCOD(COD) .SendMailTo(MailReceiver(receive, rc), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
bool WriteMail::prepareComposer(QMailMessage::MessageType type, const QMailAccountId &accountId) { bool success = false; // Don't discard mail being composed without user confirmation if (changed()) { if (QMessageBox::question(qApp->activeWindow(), tr("Compose new message"), tr("A message is currently being composed. Do you wish to save the message in drafts?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { draft(); } else { return false; } } reset(); if (type == QMailMessage::AnyType) { bool displaySelector(true); // If we have no options, prompt to set up an account if (m_selectComposerWidget->availableTypes().isEmpty()) { displaySelector = false; if (QMessageBox::question(qApp->activeWindow(), tr("No accounts configured"), tr("No accounts are configured to send messages with. Do you wish to configure one now?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { emit editAccounts(); } } // If there's just one composer option, select it immediately QString key = m_selectComposerWidget->singularKey(); if (!key.isEmpty()) { displaySelector = false; success = composerSelected(qMakePair(key, QMailComposerFactory::messageTypes(key).first())); } if (displaySelector) { m_selectComposerWidget->setSelected(composer(), QMailMessage::AnyType); setWindowTitle( tr("Select type") ); m_widgetStack->setCurrentWidget(m_selectComposerWidget); success = true; } } else { QString key = QMailComposerFactory::defaultKey(type); if (!key.isEmpty()) { success = composerSelected(qMakePair(key, type)); } else { qWarning() << "Cannot edit message of type:" << type; } } updateAccountSelection(type, accountId); if(m_composerInterface) m_composerInterface->setSendingAccountId(accountId); return success; }
void WorldSession::HandleSendMail(WorldPacket& recvData) { ObjectGuid mailbox; uint64 money, COD; std::string receiver, subject, body; uint32 bodyLength, subjectLength, receiverLength; uint32 unk1, unk2; recvData >> COD >> money; // money and cod recvData >> unk1; recvData >> unk2; // Stationery? subjectLength = recvData.ReadBits(9); mailbox[7] = recvData.ReadBit(); mailbox[5] = recvData.ReadBit(); mailbox[0] = recvData.ReadBit(); bodyLength = recvData.ReadBits(12); mailbox[2] = recvData.ReadBit(); receiverLength = recvData.ReadBits(7); mailbox[1] = recvData.ReadBit(); uint8 items_count = recvData.ReadBits(5); // attached items count if (items_count > 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[6] = recvData.ReadBit(); ObjectGuid itemGUIDs[MAX_MAIL_ITEMS]; uint8 bitOrder[8] = {3, 7, 0, 2, 6, 5, 1, 4}; for (uint8 i = 0; i < items_count; ++i) recvData.ReadBitInOrder(itemGUIDs[i], bitOrder); mailbox[4] = recvData.ReadBit(); mailbox[3] = recvData.ReadBit(); recvData.FlushBits(); recvData.ReadByteSeq(mailbox[7]); for (uint8 i = 0; i < items_count; ++i) { recvData.ReadByteSeq(itemGUIDs[i][3]); recvData.ReadByteSeq(itemGUIDs[i][1]); recvData.ReadByteSeq(itemGUIDs[i][0]); recvData.ReadByteSeq(itemGUIDs[i][7]); recvData.ReadByteSeq(itemGUIDs[i][6]); recvData.read_skip<uint8>(); // item slot in mail, not used recvData.ReadByteSeq(itemGUIDs[i][4]); recvData.ReadByteSeq(itemGUIDs[i][5]); recvData.ReadByteSeq(itemGUIDs[i][2]); } recvData.ReadByteSeq(mailbox[3]); body = recvData.ReadString(bodyLength); recvData.ReadByteSeq(mailbox[5]); recvData.ReadByteSeq(mailbox[4]); subject = recvData.ReadString(subjectLength); recvData.ReadByteSeq(mailbox[1]); receiver = recvData.ReadString(receiverLength); recvData.ReadByteSeq(mailbox[6]); recvData.ReadByteSeq(mailbox[2]); recvData.ReadByteSeq(mailbox[0]); // packet read complete, now do check if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; if (receiver.empty()) return; Player* player = _player; if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } uint64 rc = 0; if (normalizePlayerName(receiver)) rc = sObjectMgr->GetPlayerGUIDByName(receiver); if (!rc) { sLog->outInfo(LOG_FILTER_NETWORKIO, "Player %u 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->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } sLog->outInfo(LOG_FILTER_NETWORKIO, "Player %u is sending mail to %s (GUID: %u) with subject %s and body %s includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", player->GetGUIDLow(), receiver.c_str(), GUID_LOPART(rc), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); if (player->GetGUID() == rc) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client uint64 reqmoney = cost + money; if (!player->HasEnoughMoney(reqmoney) && !player->isGameMaster()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receive = ObjectAccessor::FindPlayer(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(); mails_count = receive->GetMailSize(); } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // 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) { Item* item = player->GetItemByGuid(itemGUIDs[i]); if (item) { ItemTemplate const* itemProto = item->GetTemplate(); if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT)) { accountBound = false; break; } } } // We have remove mysql query for check team of offline players // On pandashan, it's will work anyway because of interfaction /*if (!accountBound && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeam() != rc_team && AccountMgr::IsPlayerAccount(GetSecurity())) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; }*/ uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(rc); Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGUIDs[i]) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = player->GetItemByGuid(itemGUIDs[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != rc_account) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_NOT_SAME_ACCOUNT); return; } if (item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsNotEmptyBag()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_DESTROY_NONEMPTY_BAG); return; } items[i] = item; } // Check for spamming if (!UpdateAntispamCount()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_INTERNAL_ERROR); SendNotification(GetTrinityString(LANG_ANTISPAM_ERROR)); return; } // Check for special symbols if (!checkMailText(subject) || !checkMailText(body)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_INTERNAL_ERROR); return; } player->SendMailResult(0, MAIL_SEND, MAIL_OK); player->ModifyMoney(-int64(reqmoney)); player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (!AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE)) { sLog->outCommand(GetAccountId(), "", GetPlayer()->GetGUIDLow(), GetPlayer()->GetName(), rc_account, "", 0, receiver.c_str(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName().c_str(), GetAccountId(), item->GetTemplate()->Name1.c_str(), item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable player->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory item->SetOwnerGUID(rc); 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() != rc_account; } if (money > 0 && !AccountMgr::IsPlayerAccount(GetSecurity()) && sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE)) { //TODO: charcter guid sLog->outCommand(GetAccountId(), "", GetPlayer()->GetGUIDLow(), GetPlayer()->GetName(), rc_account, "", 0, receiver.c_str(), "GM %s (Account: %u) mail money: %u to player: %s (Account: %u)", GetPlayerName().c_str(), GetAccountId(), money, receiver.c_str(), rc_account); } } // Guild Mail if (receive && receive->GetGuildId() && player->GetGuildId()) if (player->HasAura(83951) && (player->GetGuildId() == receive->GetGuildId())) needItemDelay = false; // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list draft .AddMoney(money) .AddCOD(COD) .SendMailTo(trans, MailReceiver(receive, GUID_LOPART(rc)), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); player->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); }
void PlayerAchievementMgr::CompletedAchievement(AchievementEntry const* achievement, Player* referencePlayer) { // disable for gamemasters with GM-mode enabled if (_owner->IsGameMaster()) return; if ((achievement->Faction == ACHIEVEMENT_FACTION_HORDE && referencePlayer->GetTeam() != HORDE) || (achievement->Faction == ACHIEVEMENT_FACTION_ALLIANCE && referencePlayer->GetTeam() != ALLIANCE)) return; if (achievement->Flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID)) return; if (achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_NEWS) if (Guild* guild = referencePlayer->GetGuild()) guild->AddGuildNews(GUILD_NEWS_PLAYER_ACHIEVEMENT, referencePlayer->GetGUID(), achievement->Flags & ACHIEVEMENT_FLAG_SHOW_IN_GUILD_HEADER, achievement->ID); if (!_owner->GetSession()->PlayerLoading()) SendAchievementEarned(achievement); TC_LOG_INFO("criteria.achievement", "PlayerAchievementMgr::CompletedAchievement(%u). %s", achievement->ID, GetOwnerInfo().c_str()); CompletedAchievementData& ca = _completedAchievements[achievement->ID]; ca.Date = time(NULL); ca.Changed = true; if (achievement->Flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) sAchievementMgr->SetRealmCompleted(achievement); if (!(achievement->Flags & ACHIEVEMENT_FLAG_TRACKING_FLAG)) _achievementPoints += achievement->Points; UpdateCriteria(CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, 0, 0, 0, NULL, referencePlayer); UpdateCriteria(CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS, achievement->Points, 0, 0, NULL, referencePlayer); // reward items and titles if any AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement); // no rewards if (!reward) return; // titles //! Currently there's only one achievement that deals with gender-specific titles. //! Since no common attributes were found, (not even in titleRewardFlags field) //! we explicitly check by ID. Maybe in the future we could move the achievement_reward //! condition fields to the condition system. if (uint32 titleId = reward->TitleId[achievement->ID == 1793 ? _owner->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) : (_owner->GetTeam() == ALLIANCE ? 0 : 1)]) if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) _owner->SetTitle(titleEntry); // mail if (reward->SenderCreatureId) { MailDraft draft(uint16(reward->MailTemplateId)); if (!reward->MailTemplateId) { // subject and text std::string subject = reward->Subject; std::string text = reward->Body; LocaleConstant localeConstant = _owner->GetSession()->GetSessionDbLocaleIndex(); if (localeConstant != LOCALE_enUS) { if (AchievementRewardLocale const* loc = sAchievementMgr->GetAchievementRewardLocale(achievement)) { ObjectMgr::GetLocaleString(loc->Subject, localeConstant, subject); ObjectMgr::GetLocaleString(loc->Body, localeConstant, text); } } draft = MailDraft(subject, text); } SQLTransaction trans = CharacterDatabase.BeginTransaction(); Item* item = reward->ItemId ? Item::CreateItem(reward->ItemId, 1, _owner) : NULL; if (item) { // save new item before send item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted // item draft.AddItem(item); } draft.SendMailTo(trans, _owner, MailSender(MAIL_CREATURE, uint64(reward->SenderCreatureId))); CharacterDatabase.CommitTransaction(trans); } }
void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) { sLog->outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID); // disable for gamemasters with GM-mode enabled if (_player->isGameMaster()) return; if (achievement->flags & ACHIEVEMENT_FLAG_COUNTER || HasAchieved(achievement->ID)) return; SendAchievementEarned(achievement); CompletedAchievementData& ca = m_completedAchievements[achievement->ID]; ca.date = time(NULL); ca.changed = true; // don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement // TODO: where do set this instead? if (!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL)) sAchievementMgr->SetRealmCompleted(achievement); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, _player); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS, _player, achievement->points); m_achievementPoints += achievement->points; // reward items and titles if any AchievementReward const* reward = sAchievementMgr->GetAchievementReward(achievement); // no rewards if (!reward) return; // titles //! Currently there's only one achievement that deals with gender-specific titles. //! Since no common attributes were found, (not even in titleRewardFlags field) //! we explicitly check by ID. Maybe in the future we could move the achievement_reward //! condition fields to the condition system. if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->getGender() : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)]) if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId)) GetPlayer()->SetTitle(titleEntry); // mail if (reward->sender) { Item* item = reward->itemId ? Item::CreateItem(reward->itemId, 1, GetPlayer ()) : NULL; int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex(); // subject and text std::string subject = reward->subject; std::string text = reward->text; if (loc_idx >= 0) { if (AchievementRewardLocale const* loc = sAchievementMgr->GetAchievementRewardLocale(achievement)) { ObjectMgr::GetLocaleString(loc->subject, loc_idx, subject); ObjectMgr::GetLocaleString(loc->text, loc_idx, text); } } MailDraft draft(subject, text); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (item) { // save new item before send item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted // item draft.AddItem(item); } draft.SendMailTo(trans, GetPlayer(), MailSender(MAIL_CREATURE, reward->sender)); CharacterDatabase.CommitTransaction(trans); } }
void WorldSession::HandleSendMail(WorldPacket & recv_data) { CHECK_PACKET_SIZE(recv_data,8+1+1+1+4+4+1+4+4+8+1); ObjectGuid mailboxGuid; uint64 unk3; std::string receiver, subject, body; uint32 unk1, unk2, money, COD; uint8 unk4; recv_data >> mailboxGuid; recv_data >> receiver; // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+1+1+4+4+1+4+4+8+1); recv_data >> subject; // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+1+4+4+1+4+4+8+1); recv_data >> body; // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+4+4+8+1); recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 uint8 items_count; recv_data >> items_count; // attached items count if (items_count > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam return; } // recheck CHECK_PACKET_SIZE(recv_data, 8+(receiver.size()+1)+(subject.size()+1)+(body.size()+1)+4+4+1+items_count*(1+8)+4+4+8+1); ObjectGuid itemGuids[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { recv_data.read_skip<uint8>(); // item slot in mail, not used recv_data >> itemGuids[i]; } recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 // packet read complete, now do check if (!CheckMailBox(mailboxGuid)) return; items_count = 0; // remove duplicates, after this items_count will contains real items count { ObjectGuid tmpItemGuids[MAX_MAIL_ITEMS]; bool inTable = false; for (uint8 i = 0; i < MAX_MAIL_ITEMS; ++i) { if (itemGuids[i].IsEmpty()) continue; inTable = false; for (uint8 j = 0; j < MAX_MAIL_ITEMS; ++j) { if (tmpItemGuids[j].IsEmpty()) break; if (tmpItemGuids[j] == itemGuids[i]) { inTable = true; break; } } if (!inTable) { tmpItemGuids[items_count] = itemGuids[i]; ++items_count; } itemGuids[i].Clear(); } for (uint8 i = 0; i < items_count; ++i) itemGuids[i] = tmpItemGuids[i]; } if (receiver.empty()) return; Player* pl = _player; ObjectGuid rc; if (normalizePlayerName(receiver)) rc = sObjectMgr.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", pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); pl->SendMailResult(0, 0, 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, 0, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 reqmoney = money + 30; if (items_count) reqmoney = money + (30 * items_count); if (pl->GetMoney() < reqmoney) { pl->SendMailResult(0, 0, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player *receive = sObjectMgr.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(); mails_count = receive->GetMailSize(); } else { rc_team = sObjectMgr.GetPlayerTeamByGUID(rc); QueryResultAutoPtr result = RealmDataDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)); if (result) { Field *fields = result->Fetch(); mails_count = fields[0].GetUInt32(); } } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } // test the receiver's Faction... if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && !HasPermissions(PERM_GMT)) { pl->SendMailResult(0, 0, MAIL_ERR_NOT_YOUR_TEAM); return; } Item* items[MAX_MAIL_ITEMS]; for(uint8 i = 0; i < items_count; ++i) { if (!itemGuids[i].IsItem()) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!itemGuids[i].GetCounter()) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } Item* item = pl->GetItemByGuid(itemGuids[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if(!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded()) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if ((item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION))) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsBag() && !((Bag*)item)->IsEmpty()) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } items[i] = item; } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); pl->ModifyMoney(-int32(reqmoney)); bool needItemDelay = false; MailDraft draft(subject, body); uint32 rc_account = 0; if (receive) rc_account = receive->GetSession()->GetAccountId(); else rc_account = sObjectMgr.GetPlayerAccountIdByGUID(rc); if (items_count > 0 || money > 0) { if (items_count > 0) { for(uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (!item) continue; if (HasPermissions(PERM_GMT) && 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(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } sLog.outLog(LOG_TRADE, "Player %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); pl->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); RealmDataDatabase.BeginTransaction(); item->DeleteFromInventoryDB(); //deletes item from character's inventory item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting RealmDataDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", rc.GetCounter(), item->GetGUIDLow()); RealmDataDatabase.CommitTransaction(); draft.AddItem(item); } // if item send to character at another account, then apply item delivery delay needItemDelay = pl->GetSession()->GetAccountId() != rc_account; } if (money > 0) { if (HasPermissions(PERM_GMT) && 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 (_player->GetSession()->IsAccountFlagged(ACC_SPECIAL_LOG)) { sLog.outLog(LOG_SPECIAL, "Player %s (Account: %u) mail money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } sLog.outLog(LOG_TRADE, "Player %s (Account: %u) mail money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } } sLog.outLog(LOG_MAIL, "Player %s (Account: %u) sent mail to player: %s (Account: %u) with subject: %s and body: %s", GetPlayerName(), GetAccountId(), receiver.c_str(), rc_account, subject.c_str(), body.c_str()); // 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; // If GM sends mail to player - deliver_delay must be zero if (deliver_delay && HasPermissions(PERM_GMT) && sWorld.getConfig(CONFIG_GM_MAIL)) deliver_delay = 0; // will delete item or place to receiver mail list draft .SetMoney(money) .SetCOD(COD) .SendMailTo(MailReceiver(receive, rc), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); RealmDataDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); RealmDataDatabase.CommitTransaction(); }
void WorldSession::HandleSendMail(WorldPacket & recv_data ) { uint64 mailbox, unk3; std::string receiver, subject, body; uint32 unk1, unk2, money, COD; uint8 unk4; recv_data >> mailbox; recv_data >> receiver; recv_data >> subject; recv_data >> body; recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 uint8 items_count; recv_data >> items_count; // attached items count if (items_count > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam return; } uint64 itemGUIDs[MAX_MAIL_ITEMS]; for(uint8 i = 0; i < items_count; ++i) { recv_data.read_skip<uint8>(); // item slot in mail, not used recv_data >> itemGUIDs[i]; } recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 // packet read complete, now do check if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; 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", pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); 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(); mails_count = receive->GetMailSize(); } else { rc_team = objmgr.GetPlayerTeamByGUID(rc); if (QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc))) { Field *fields = result->Fetch(); mails_count = fields[0].GetUInt32(); delete result; } } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // check the receiver's Faction... if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(rc); Item* items[MAX_MAIL_ITEMS]; for(uint8 i = 0; i < items_count; ++i) { if (!itemGUIDs[i]) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = pl->GetItemByGuid(itemGUIDs[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if(!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() /*&& 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 (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } items[i] = item; } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); uint32 itemTextId = !body.empty() ? objmgr.CreateItemText( body ) : 0; pl->ModifyMoney( -int32(reqmoney) ); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, itemTextId); if (items_count > 0 || money > 0) { if (items_count > 0) { for(uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; 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(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); CharacterDatabase.BeginTransaction(); item->DeleteFromInventoryDB(); // deletes item from character's inventory item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); draft.AddItem(item); } // 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 draft .AddMoney(money) .AddCOD(COD) .SendMailTo(MailReceiver(receive, GUID_LOPART(rc)), pl, MAIL_CHECK_MASK_NONE, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
void WorldSession::HandleSendMail(WorldPacket & recv_data) { uint64 mailbox, unk3, money, COD; std::string receiver, subject, body; uint32 unk1, unk2; uint8 unk4; recv_data >> mailbox; recv_data >> receiver; recv_data >> subject; recv_data >> body; recv_data >> unk1; // stationery? recv_data >> unk2; // 0x00000000 uint8 items_count; recv_data >> items_count; // attached items count if (items_count > MAX_MAIL_ITEMS) // client limit { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS); recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam return; } uint64 itemGUIDs[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { recv_data.read_skip<uint8>(); // item slot in mail, not used recv_data >> itemGUIDs[i]; } recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 // packet read complete, now do check if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; if (receiver.empty()) return; Player* pl = _player; if (pl->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetStarGateString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } uint64 rc = 0; if (normalizePlayerName(receiver)) rc = sObjectMgr->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, "UI64FMTD" copper and "UI64FMTD" COD copper with unk1 = %u, unk2 = %u", pl->GetGUIDLow(), receiver.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, unk1, unk2); 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, "UI64FMTD" copper and "UI64FMTD" 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 uint64 reqmoney = cost + money; if (!pl->HasEnoughMoney(uint32(reqmoney))) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player *receive = sObjectMgr->GetPlayer(rc); uint32 rc_team = 0; uint8 mails_count = 0; //do not allow to send to one player more than 100 mails uint8 receiveLevel = 0; if (receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); receiveLevel = receive->getLevel(); } else { rc_team = sObjectMgr->GetPlayerTeamByGUID(rc); if (QueryResult result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc))) { Field *fields = result->Fetch(); mails_count = fields[0].GetUInt32(); } if (QueryResult result = CharacterDatabase.PQuery("SELECT level FROM characters WHERE guid = '%u'", GUID_LOPART(rc))) { Field *fields = result->Fetch(); receiveLevel = fields[0].GetUInt8(); } } //do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mails_count > 100) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // 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) { Item* item = pl->GetItemByGuid(itemGUIDs[i]); if (item) { ItemPrototype const* itemProto = item->GetProto(); if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT)) { accountBound = false; break; } } } if (!accountBound && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } if (receiveLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetStarGateString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sObjectMgr->GetPlayerAccountIdByGUID(rc); Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < items_count; ++i) { if (!itemGUIDs[i]) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = pl->GetItemByGuid(itemGUIDs[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && 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 (item->GetProto()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsBag() && !((Bag*)item)->IsEmpty()) { pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS); return; } items[i] = item; } pl->SendMailResult(0, MAIL_SEND, MAIL_OK); pl->ModifyMoney(-int32(reqmoney)); pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (GetSecurity() > SEC_PLAYER && sWorld->getBoolConfig(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(), item->GetProto()->Name1, item->GetEntry(), item->GetCount(), receiver.c_str(), rc_account); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SET_ITEM_OWNER); stmt->setUInt32(0, GUID_LOPART(rc)); stmt->setUInt32(1, item->GetGUIDLow()); trans->Append(stmt); draft.AddItem(item); } // 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->getBoolConfig(CONFIG_GM_LOG_TRADE)) { sLog->outCommand(GetAccountId(), "GM %s (Account: %u) mail money: "UI64FMTD" 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->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list draft .AddMoney(money) .AddCOD(COD) .SendMailTo(trans, MailReceiver(receive, GUID_LOPART(rc)), MailSender(pl), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); pl->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); }
void WorldSession::HandleSendMail(WorldPacket& recvData) { ObjectGuid mailbox; uint64 money, COD; std::string receiverName, subject, body; uint32 bodyLength, subjectLength, receiverLength; uint32 unk1, unk2; uint8 itemCount; recvData >> unk1 >> unk2; // both unknown recvData >> COD >> money; // money and cod recvData >> mailbox; bodyLength = recvData.ReadBits(11); receiverLength = recvData.ReadBits(9); itemCount = recvData.ReadBits(5); // attached items count if (itemCount > 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; } ObjectGuid itemGuids[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < itemCount; ++i) recvData >> itemGuids[i]; subjectLength = recvData.ReadBits(9); for (uint8 i = 0; i < itemCount; ++i) recvData.read_skip<uint8>(); // item slot in mail, not used body = recvData.ReadString(bodyLength); subject = recvData.ReadString(subjectLength); receiverName = recvData.ReadString(receiverLength); // packet read complete, now do check if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX)) return; if (receiverName.empty()) return; Player* player = _player; if (player->getLevel() < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_SENDER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } uint64 receiverGuid = 0; if (normalizePlayerName(receiverName)) receiverGuid = sObjectMgr->GetPlayerGUIDByName(receiverName); if (!receiverGuid) { TC_LOG_INFO("network", "Player %u 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->GetGUIDLow(), receiverName.c_str(), subject.c_str(), body.c_str(), itemCount, money, COD, unk1, unk2); player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_NOT_FOUND); return; } TC_LOG_INFO("network", "Player %u is sending mail to %s (GUID: %u) with subject %s and body %s " "includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", player->GetGUIDLow(), receiverName.c_str(), GUID_LOPART(receiverGuid), subject.c_str(), body.c_str(), itemCount, money, COD, unk1, unk2); if (player->GetGUID() == receiverGuid) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF); return; } uint32 cost = itemCount ? 30 * itemCount : 30; // price hardcoded in client uint64 reqmoney = cost + money; if (!player->HasEnoughMoney(reqmoney) && !player->IsGameMaster()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Player* receiver = ObjectAccessor::FindPlayer(receiverGuid); uint32 receiverTeam = 0; uint8 mailsCount = 0; //do not allow to send to one player more than 100 mails uint8 receiverLevel = 0; uint32 receiverAccountId = 0; if (receiver) { receiverTeam = receiver->GetTeam(); mailsCount = receiver->GetMailSize(); receiverLevel = receiver->getLevel(); receiverAccountId = receiver->GetSession()->GetAccountId(); } else { receiverTeam = sObjectMgr->GetPlayerTeamByGUID(receiverGuid); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_COUNT); stmt->setUInt32(0, GUID_LOPART(receiverGuid)); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (result) { Field* fields = result->Fetch(); mailsCount = fields[0].GetUInt64(); } stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_LEVEL); stmt->setUInt32(0, GUID_LOPART(receiverGuid)); result = CharacterDatabase.Query(stmt); if (result) { Field* fields = result->Fetch(); receiverLevel = fields[0].GetUInt8(); } receiverAccountId = sObjectMgr->GetPlayerAccountIdByGUID(receiverGuid); } // do not allow to have more than 100 mails in mailbox.. mails count is in opcode uint8!!! - so max can be 255.. if (mailsCount > 100) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); return; } // test the receiver's Faction... or all items are account bound bool accountBound = itemCount ? true : false; for (uint8 i = 0; i < itemCount; ++i) { if (Item* item = player->GetItemByGuid(itemGuids[i])) { ItemTemplate const* itemProto = item->GetTemplate(); if (!itemProto || !(itemProto->Flags & ITEM_PROTO_FLAG_BIND_TO_ACCOUNT)) { accountBound = false; break; } } } if (!accountBound && player->GetTeam() != receiverTeam && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } if (receiverLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } Item* items[MAX_MAIL_ITEMS]; for (uint8 i = 0; i < itemCount; ++i) { if (!itemGuids[i]) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } Item* item = player->GetItemByGuid(itemGuids[i]); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } if (!item->CanBeTraded(true)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != receiverAccountId) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_NOT_SAME_ACCOUNT); return; } if (item->GetTemplate()->Flags & ITEM_PROTO_FLAG_CONJURED || item->GetUInt32Value(ITEM_FIELD_EXPIRATION)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); return; } if (COD && item->HasFlag(ITEM_FIELD_DYNAMIC_FLAGS, ITEM_FLAG_WRAPPED)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } if (item->IsNotEmptyBag()) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_DESTROY_NONEMPTY_BAG); return; } items[i] = item; } player->SendMailResult(0, MAIL_SEND, MAIL_OK); player->ModifyMoney(-int64(reqmoney)); player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (itemCount > 0 || money > 0) { bool log = HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE); if (itemCount > 0) { for (uint8 i = 0; i < itemCount; ++i) { Item* item = items[i]; if (log) { sLog->outCommand(GetAccountId(), "GM %s (GUID: %u) (Account: %u) mail item: %s (Entry: %u Count: %u) " "to player: %s (GUID: %u) (Account: %u)", GetPlayerName().c_str(), GetGuidLow(), GetAccountId(), item->GetTemplate()->Name1.c_str(), item->GetEntry(), item->GetCount(), receiverName.c_str(), GUID_LOPART(receiverGuid), receiverAccountId); } item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable player->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true); item->DeleteFromInventoryDB(trans); // deletes item from character's inventory item->SetOwnerGUID(receiverGuid); 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 && money > 0) { sLog->outCommand(GetAccountId(), "GM %s (GUID: %u) (Account: %u) mail money: " UI64FMTD " to player: %s (GUID: %u) (Account: %u)", GetPlayerName().c_str(), GetGuidLow(), GetAccountId(), money, receiverName.c_str(), GUID_LOPART(receiverGuid), receiverAccountId); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // Mail sent between guild members arrives instantly if they have the guild perk "Guild Mail" //if (Guild* guild = sGuildMgr->GetGuildById(player->GetGuildId())) // if (guild->GetLevel() >= 17 && guild->IsMember(receiverGuid)) // deliver_delay = 0; // will delete item or place to receiver mail list draft .AddMoney(money) .AddCOD(COD) .SendMailTo(trans, MailReceiver(receiver, GUID_LOPART(receiverGuid)), MailSender(player), body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); player->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); }