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); }
static bool HandleQuestComplete(ChatHandler* handler, const char* args) { Player* player; std::string name; char* questArg; char* playerName; handler->extractOptFirstArg((char*)args, &playerName, &questArg); if (!playerName) player = handler->getSelectedPlayer(); else { name = playerName; normalizePlayerName(name); player = sObjectAccessor->FindPlayerByName(name); } if (!player) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); handler->SetSentErrorMessage(true); return false; } if (!questArg) return false; // .quest complete #entry // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r char* cId = handler->extractKeyFromLink(questArg, "Hquest"); if (!cId) return false; uint32 entry = atol(cId); Quest const* quest = sObjectMgr->GetQuestTemplate(entry); // If player doesn't have the quest if (!quest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE) { handler->PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry); handler->SetSentErrorMessage(true); return false; } // Add quest items for quests that require items for (uint8 x = 0; x < QUEST_ITEM_OBJECTIVES_COUNT; ++x) { uint32 id = quest->RequiredItemId[x]; uint32 count = quest->RequiredItemCount[x]; if (!id || !count) continue; uint32 curItemCount = player->GetItemCount(id, true); ItemPosCountVec dest; uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, id, count-curItemCount); if (msg == EQUIP_ERR_OK) { Item* item = player->StoreNewItem(dest, id, true); player->SendNewItem(item, count-curItemCount, true, false); } } // All creature/GO slain/cast (not required, but otherwise it will display "Creature slain 0/10") for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) { int32 creature = quest->RequiredNpcOrGo[i]; uint32 creatureCount = quest->RequiredNpcOrGoCount[i]; if (creature > 0) { if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creature)) for (uint16 z = 0; z < creatureCount; ++z) player->KilledMonster(creatureInfo, ObjectGuid::Empty); } else if (creature < 0) for (uint16 z = 0; z < creatureCount; ++z) player->KillCreditGO(creature); } // If the quest requires reputation to complete if (uint32 repFaction = quest->GetRepObjectiveFaction()) { uint32 repValue = quest->GetRepObjectiveValue(); uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); if (curRep < repValue) if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction)) player->GetReputationMgr().SetReputation(factionEntry, repValue); } // If the quest requires a SECOND reputation to complete if (uint32 repFaction = quest->GetRepObjectiveFaction2()) { uint32 repValue2 = quest->GetRepObjectiveValue2(); uint32 curRep = player->GetReputationMgr().GetReputation(repFaction); if (curRep < repValue2) if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(repFaction)) player->GetReputationMgr().SetReputation(factionEntry, repValue2); } // If the quest requires money int32 ReqOrRewMoney = quest->GetRewOrReqMoney(); if (ReqOrRewMoney < 0) player->ModifyMoney(-ReqOrRewMoney); player->CompleteQuest(entry); return true; }
void WorldSession::HandleAcceptTradeOpcode(WorldPacket& recvPacket) { recvPacket.read_skip<uint32>(); TradeData* my_trade = _player->m_trade; if (!my_trade) { return; } Player* trader = my_trade->GetTrader(); TradeData* his_trade = trader->m_trade; if (!his_trade) { return; } Item* myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; Item* hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; bool myCanCompleteTrade = true, hisCanCompleteTrade = true; // set before checks to properly undo at problems (it already set in to client) my_trade->SetAccepted(true); // not accept case incorrect money amount if (my_trade->GetMoney() > _player->GetMoney()) { SendNotification(LANG_NOT_ENOUGH_GOLD); my_trade->SetAccepted(false, true); return; } if (!_player->IsWithinDistInMap(trader, TRADE_DISTANCE, false)) { SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); my_trade->SetAccepted(false); return; } // not accept case incorrect money amount if (his_trade->GetMoney() > trader->GetMoney()) { trader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD); his_trade->SetAccepted(false, true); return; } // not accept if some items now can't be trade (cheating) for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { if (Item* item = my_trade->GetItem(TradeSlots(i))) { if (!item->CanBeTraded()) { SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); return; } } if (Item* item = his_trade->GetItem(TradeSlots(i))) { if (!item->CanBeTraded()) { SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); return; } } } if (his_trade->IsAccepted()) { setAcceptTradeMode(my_trade, his_trade, myItems, hisItems); Spell* my_spell = NULL; SpellCastTargets my_targets; Spell* his_spell = NULL; SpellCastTargets his_targets; // not accept if spell can't be casted now (cheating) if (uint32 my_spell_id = my_trade->GetSpell()) { SpellEntry const* spellEntry = sSpellStore.LookupEntry(my_spell_id); Item* castItem = my_trade->GetSpellCastItem(); if (!spellEntry || !his_trade->GetItem(TRADE_SLOT_NONTRADED) || (my_trade->HasSpellCastItem() && !castItem)) { clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); my_trade->SetSpell(0); return; } my_spell = new Spell(_player, spellEntry, true); my_spell->m_CastItem = castItem; my_targets.setTradeItemTarget(_player); my_spell->m_targets = my_targets; SpellCastResult res = my_spell->CheckCast(true); if (res != SPELL_CAST_OK) { my_spell->SendCastResult(res); clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); delete my_spell; my_trade->SetSpell(0); return; } } // not accept if spell can't be casted now (cheating) if (uint32 his_spell_id = his_trade->GetSpell()) { SpellEntry const* spellEntry = sSpellStore.LookupEntry(his_spell_id); Item* castItem = his_trade->GetSpellCastItem(); if (!spellEntry || !my_trade->GetItem(TRADE_SLOT_NONTRADED) || (his_trade->HasSpellCastItem() && !castItem)) { delete my_spell; his_trade->SetSpell(0); clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); return; } his_spell = new Spell(trader, spellEntry, true); his_spell->m_CastItem = castItem; his_targets.setTradeItemTarget(trader); his_spell->m_targets = his_targets; SpellCastResult res = his_spell->CheckCast(true); if (res != SPELL_CAST_OK) { his_spell->SendCastResult(res); clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); delete my_spell; delete his_spell; his_trade->SetSpell(0); return; } } // inform partner client trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); // test if item will fit in each inventory hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); clearAcceptTradeMode(myItems, hisItems); // in case of missing space report error if (!myCanCompleteTrade) { clearAcceptTradeMode(my_trade, his_trade); SendNotification(LANG_NOT_FREE_TRADE_SLOTS); trader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); my_trade->SetAccepted(false); his_trade->SetAccepted(false); return; } else if (!hisCanCompleteTrade) { clearAcceptTradeMode(my_trade, his_trade); SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); trader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS); my_trade->SetAccepted(false); his_trade->SetAccepted(false); return; } // execute trade: 1. remove for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { if (Item* item = myItems[i]) { item->SetGuidValue(ITEM_FIELD_GIFTCREATOR, _player->GetObjectGuid()); _player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); } if (Item* item = hisItems[i]) { item->SetGuidValue(ITEM_FIELD_GIFTCREATOR, trader->GetObjectGuid()); trader->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); } } // execute trade: 2. store moveItems(myItems, hisItems); // logging money if (sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { if (_player->GetSession()->GetSecurity() > SEC_PLAYER && my_trade->GetMoney() > 0) { sLog.outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", _player->GetName(), _player->GetSession()->GetAccountId(), my_trade->GetMoney(), trader->GetName(), trader->GetSession()->GetAccountId()); } if (trader->GetSession()->GetSecurity() > SEC_PLAYER && his_trade->GetMoney() > 0) { sLog.outCommand(trader->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", trader->GetName(), trader->GetSession()->GetAccountId(), his_trade->GetMoney(), _player->GetName(), _player->GetSession()->GetAccountId()); } } // update money _player->ModifyMoney(-int32(my_trade->GetMoney())); _player->ModifyMoney(his_trade->GetMoney()); trader->ModifyMoney(-int32(his_trade->GetMoney())); trader->ModifyMoney(my_trade->GetMoney()); if (my_spell) { my_spell->prepare(&my_targets); } if (his_spell) { his_spell->prepare(&his_targets); } // cleanup clearAcceptTradeMode(my_trade, his_trade); delete _player->m_trade; _player->m_trade = NULL; delete trader->m_trade; trader->m_trade = NULL; // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) CharacterDatabase.BeginTransaction(); _player->SaveInventoryAndGoldToDB(); trader->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); } else { trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); } }
void WorldSession::HandleSendMail(WorldPacket & recv_data) { if (sWorld->getIntConfig(CONFIG_CORE_TYPE) > NODE_TYPE_MASTER) { GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_INTERNAL_ERROR); return; } 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.rfinish(); // 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)) if (!GetPlayer()->GetNPCIfCanInteractWith(mailbox, UNIT_NPC_FLAG_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; 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 uint8 receiveLevel = 0; if (receive) { rc_team = receive->GetTeam(); mails_count = receive->GetMailSize(); receiveLevel = receive->getLevel(); } else { rc_team = sObjectMgr->GetPlayerTeamByGUID(rc); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_COUNT); stmt->setUInt32(0, GUID_LOPART(rc)); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (result) { Field* fields = result->Fetch(); mails_count = fields[0].GetUInt64(); } stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_LEVEL); stmt->setUInt32(0, GUID_LOPART(rc)); result = CharacterDatabase.Query(stmt); if (result) { 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) { 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; } } } 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; } if (receiveLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { SendNotification(GetTrinityString(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]) { 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_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->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; MailDraft draft(subject, body); SQLTransaction trans = CharacterDatabase.BeginTransaction(); player->AddTransaction(trans); if (items_count > 0 || money > 0) { if (items_count > 0) { for (uint8 i = 0; i < items_count; ++i) { Item* item = items[i]; if (!AccountMgr::IsVIPorPlayer(GetSecurity()) && 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->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, player->GetGUIDLow()); // 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::IsVIPorPlayer(GetSecurity()) && sWorld->getBoolConfig(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->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 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 mailbox[0] = recvData.ReadBit(); mailbox[6] = recvData.ReadBit(); mailbox[4] = recvData.ReadBit(); mailbox[1] = recvData.ReadBit(); bodyLength = recvData.ReadBits(11); mailbox[3] = recvData.ReadBit(); receiverLength = recvData.ReadBits(9); mailbox[7] = recvData.ReadBit(); mailbox[5] = recvData.ReadBit(); 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) { itemGuids[i][1] = recvData.ReadBit(); itemGuids[i][7] = recvData.ReadBit(); itemGuids[i][2] = recvData.ReadBit(); itemGuids[i][5] = recvData.ReadBit(); itemGuids[i][0] = recvData.ReadBit(); itemGuids[i][6] = recvData.ReadBit(); itemGuids[i][3] = recvData.ReadBit(); itemGuids[i][4] = recvData.ReadBit(); } subjectLength = recvData.ReadBits(9); mailbox[2] = recvData.ReadBit(); for (uint8 i = 0; i < itemCount; ++i) { recvData.read_skip<uint8>(); // item slot in mail, not used recvData.ReadByteSeq(itemGuids[i][3]); recvData.ReadByteSeq(itemGuids[i][0]); recvData.ReadByteSeq(itemGuids[i][2]); recvData.ReadByteSeq(itemGuids[i][1]); recvData.ReadByteSeq(itemGuids[i][6]); recvData.ReadByteSeq(itemGuids[i][5]); recvData.ReadByteSeq(itemGuids[i][7]); recvData.ReadByteSeq(itemGuids[i][4]); } recvData.ReadByteSeq(mailbox[1]); body = recvData.ReadString(bodyLength); recvData.ReadByteSeq(mailbox[0]); subject = recvData.ReadString(subjectLength); recvData.ReadByteSeq(mailbox[2]); recvData.ReadByteSeq(mailbox[6]); recvData.ReadByteSeq(mailbox[5]); recvData.ReadByteSeq(mailbox[7]); recvData.ReadByteSeq(mailbox[3]); recvData.ReadByteSeq(mailbox[4]); 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); }
/** * Handles the packet sent by the client when taking an item from the mail. */ void WorldSession::HandleMailTakeItem(WorldPacket& recv_data) { ObjectGuid mailboxGuid; uint32 mailId; recv_data >> mailboxGuid; recv_data >> mailId; if (!CheckMailBox(mailboxGuid)) { 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_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } // prevent cheating with skip client money check if (pl->GetMoney() < m->COD) { pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); return; } uint32 itemId = m->items[0].item_template; uint32 itemGuid = m->items[0].item_guid; Item* it = pl->GetMItem(itemGuid); ItemPosCountVec dest; InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false); if (msg == EQUIP_ERR_OK) { m->RemoveItem(itemGuid); m->removedItems.push_back(itemGuid); if (m->COD > 0) // if there is COD, take COD money from player and send them to sender by mail { ObjectGuid sender_guid = ObjectGuid(HIGHGUID_PLAYER, m->sender); Player* sender = sObjectMgr.GetPlayer(sender_guid); uint32 sender_accId = 0; if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { std::string sender_name; if (sender) { sender_accId = sender->GetSession()->GetAccountId(); sender_name = sender->GetName(); } else if (sender_guid) { // can be calculated early sender_accId = sObjectMgr.GetPlayerAccountIdByGUID(sender_guid); if (!sObjectMgr.GetPlayerNameByGUID(sender_guid, sender_name)) { sender_name = sObjectMgr.GetMangosStringForDBCLocale(LANG_UNKNOWN); } } sLog.outCommand(GetAccountId(), "GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount(), m->COD, sender_name.c_str(), sender_accId); } else if (!sender) { sender_accId = sObjectMgr.GetPlayerAccountIdByGUID(sender_guid); } // check player existence if (sender || sender_accId) { MailDraft(m->subject) .SetMoney(m->COD) .SendMailTo(MailReceiver(sender, sender_guid), _player, MAIL_CHECK_MASK_COD_PAYMENT); } pl->ModifyMoney(-int32(m->COD)); } m->COD = 0; m->state = MAIL_STATE_CHANGED; pl->m_mailsUpdated = true; pl->RemoveMItem(it->GetGUIDLow()); uint32 count = it->GetCount(); // save counts before store and possible merge with deleting pl->MoveItemToInventory(dest, it, true); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); pl->_SaveMail(); CharacterDatabase.CommitTransaction(); pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); } else { pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); } }
/** * Handles the packet sent by the client when taking an item from the mail. */ void WorldSession::HandleTakeItem(WorldPacket & recv_data) { uint64 mailbox; uint32 mailId; uint32 itemId; recv_data >> mailbox; recv_data >> mailId; recv_data >> itemId; // item guid low 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_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } // prevent cheating with skip client money check if (pl->GetMoney() < m->COD) { pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Item *it = pl->GetMItem(itemId); ItemPosCountVec dest; uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false); if (msg == EQUIP_ERR_OK) { m->RemoveItem(itemId); m->removedItems.push_back(itemId); if (m->COD > 0) // if there is COD, take COD money from player and send them to sender by mail { uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER); Player *receive = sObjectMgr->GetPlayer(sender_guid); uint32 sender_accId = 0; if (GetSecurity() > SEC_PLAYER && sWorld->getConfig(CONFIG_GM_LOG_TRADE)) { std::string sender_name; if (receive) { sender_accId = receive->GetSession()->GetAccountId(); sender_name = receive->GetName(); } else { // can be calculated early sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid); if (!sObjectMgr->GetPlayerNameByGUID(sender_guid, sender_name)) sender_name = sObjectMgr->GetSkyFireStringForDBCLocale(LANG_UNKNOWN); } sLog->outCommand(GetAccountId(), "GM %s (Account: %u) receive mail item: %s (Entry: %u Count: %u) and send COD money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount(), m->COD, sender_name.c_str(), sender_accId); } else if (!receive) sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid); // check player existence if (receive || sender_accId) { MailDraft(m->subject) .AddMoney(m->COD) .SendMailTo(MailReceiver(receive, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT); } pl->ModifyMoney(-int32(m->COD)); } m->COD = 0; m->state = MAIL_STATE_CHANGED; pl->m_mailsUpdated = true; pl->RemoveMItem(it->GetGUIDLow()); uint32 count = it->GetCount(); // save counts before store and possible merge with deleting pl->MoveItemToInventory(dest, it, true); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); pl->_SaveMail(); CharacterDatabase.CommitTransaction(); pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); } else pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); }
//Edit Player money static bool HandleModifyMoneyCommand(ChatHandler* handler, const char* args) { if (!*args) return false; Player* target = handler->getSelectedPlayer(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); handler->SetSentErrorMessage(true); return false; } // check online security if (handler->HasLowerSecurity(target, 0)) return false; int32 addmoney = atoi((char*)args); uint32 moneyuser = target->GetMoney(); if (addmoney < 0) { int32 newmoney = int32(moneyuser) + addmoney; sLog->outDetail(handler->GetSkyFireString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney); if (newmoney <= 0) { handler->PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, handler->GetNameLink(target).c_str()); if (handler->needReportToTarget(target)) (ChatHandler(target)).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, handler->GetNameLink().c_str()); target->SetMoney(0); } else { if (newmoney > MAX_MONEY_AMOUNT) newmoney = MAX_MONEY_AMOUNT; handler->PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), handler->GetNameLink(target).c_str()); if (handler->needReportToTarget(target)) (ChatHandler(target)).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, handler->GetNameLink().c_str(), abs(addmoney)); target->SetMoney(newmoney); } } else { handler->PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, handler->GetNameLink(target).c_str()); if (handler->needReportToTarget(target)) (ChatHandler(target)).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, handler->GetNameLink().c_str(), addmoney); if (addmoney >=MAX_MONEY_AMOUNT) target->SetMoney(MAX_MONEY_AMOUNT); else target->ModifyMoney(addmoney); } sLog->outDetail(handler->GetSkyFireString(LANG_NEW_MONEY), moneyuser, addmoney, target->GetMoney()); return true; }
//this void creates new auction and adds auction to some auctionhouse void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data) { uint64 auctioneer, item; uint32 etime, bid, buyout; recv_data >> auctioneer; recv_data.read_skip<uint32>(); // const 1? recv_data >> item; recv_data.read_skip<uint32>(); // unk 3.2.2, const 1? recv_data >> bid; recv_data >> buyout; recv_data >> etime; Player *pl = GetPlayer(); if (!item || !bid || !etime) return; //check for cheaters Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER); if (!pCreature) { sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); return; } AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(pCreature->getFaction()); if (!auctionHouseEntry) { sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) has wrong faction.", uint32(GUID_LOPART(auctioneer))); return; } sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime); // client send time in minutes, convert to common used sec time etime *= MINUTE; sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime); // client understand only 3 auction time switch(etime) { case 1*MIN_AUCTION_TIME: case 2*MIN_AUCTION_TIME: case 4*MIN_AUCTION_TIME: break; default: return; } // remove fake death if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); Item *it = pl->GetItemByGuid(item); //do not allow to sell already auctioned items if (auctionmgr.GetAItem(GUID_LOPART(item))) { sLog.outError("AuctionError, player %s is sending item id: %u, but item is already in another auction", pl->GetName(), GUID_LOPART(item)); SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); return; } // prevent sending bag with items (cheat: can be placed in bag after adding equiped empty bag to auction) if (!it) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND); return; } if (!it->CanBeTraded()) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); return; } if (it->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); return; } AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); //we have to take deposit : uint32 deposit = auctionmgr.GetAuctionDeposit(auctionHouseEntry, etime, it); if (pl->GetMoney() < deposit) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY); return; } if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount()); } pl->ModifyMoney(-int32(deposit)); uint32 auction_time = uint32(etime * sWorld.getRate(RATE_AUCTION_TIME)); AuctionEntry *AH = new AuctionEntry; AH->Id = objmgr.GenerateAuctionID(); if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) AH->auctioneer = 23442; else AH->auctioneer = GUID_LOPART(auctioneer); AH->item_guidlow = GUID_LOPART(item); AH->item_template = it->GetEntry(); AH->owner = pl->GetGUIDLow(); AH->startbid = bid; AH->bidder = 0; AH->bid = 0; AH->buyout = buyout; AH->expire_time = time(NULL) + auction_time; AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; sLog.outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", GUID_LOPART(item), AH->auctioneer, bid, buyout, auction_time, AH->GetHouseId()); auctionmgr.AddAItem(it); auctionHouse->AddAuction(AH); pl->MoveItemFromInventory(it->GetBagSlot(), it->GetSlot(), true); CharacterDatabase.BeginTransaction(); it->DeleteFromInventoryDB(); it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone AH->SaveToDB(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK); }
//this function is called when client bids or buys out auction void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data) { uint64 auctioneer; uint32 auctionId; uint32 priceTmp; uint64 price; recv_data >> auctioneer; recv_data >> auctionId >> priceTmp; price = priceTmp; if (!auctionId || !price) return; //check for cheaters Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); if (!pCreature) { sLog->outDebug("WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); return; } // remove fake death if (GetPlayer()->HasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject *auctionHouse = sAuctionMgr->GetAuctionsMap(pCreature->getFaction()); AuctionEntry *auction = auctionHouse->GetAuction(auctionId); Player *pl = GetPlayer(); if (!auction || auction->owner == pl->GetGUIDLow()) { //you cannot bid your own auction: SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); return; } // impossible have online own another character (use this for speedup check in case online owner) Player* auction_owner = sObjectMgr->GetPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)); if (!auction_owner && sObjectMgr->GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == pl->GetSession()->GetAccountId()) { //you cannot bid your another character auction: SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); return; } // cheating if (price <= auction->bid || price < auction->startbid) return; // price too low for next bid if not buyout if ((price < auction->buyout || auction->buyout == 0) && price < auction->bid + auction->GetAuctionOutBid()) { //auction has already higher bid, client tests it! return; } if (!pl->HasEnoughMoney(price)) { //you don't have enought money!, client tests! //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); return; } SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (price < auction->buyout || auction->buyout == 0) { if (auction->bidder > 0) { if (auction->bidder == pl->GetGUIDLow()) pl->ModifyMoney(-int32(price - auction->bid)); else { // mail to last bidder and return money sAuctionMgr->SendAuctionOutbiddedMail(auction, price, GetPlayer(), trans); pl->ModifyMoney(-int32(price)); } } else pl->ModifyMoney(-int32(price)); auction->bidder = pl->GetGUIDLow(); auction->bid = (price >> 32); GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); trans->PAppend("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0); } else { //buyout: if (pl->GetGUIDLow() == auction->bidder)
static bool HandleQuestComplete(ChatHandler* handler, const char* args) { Player* player = handler->getSelectedPlayerOrSelf(); if (!player) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); handler->SetSentErrorMessage(true); return false; } // .quest complete #entry // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r char* cId = handler->extractKeyFromLink((char*)args, "Hquest"); if (!cId) return false; uint32 entry = atoul(cId); Quest const* quest = sObjectMgr->GetQuestTemplate(entry); // If player doesn't have the quest if (!quest || player->GetQuestStatus(entry) == QUEST_STATUS_NONE) { handler->PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry); handler->SetSentErrorMessage(true); return false; } for (uint32 i = 0; i < quest->Objectives.size(); ++i) { QuestObjective const& obj = quest->Objectives[i]; switch (obj.Type) { case QUEST_OBJECTIVE_ITEM: { uint32 curItemCount = player->GetItemCount(obj.ObjectID, true); ItemPosCountVec dest; uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, obj.ObjectID, obj.Amount - curItemCount); if (msg == EQUIP_ERR_OK) { Item* item = player->StoreNewItem(dest, obj.ObjectID, true); player->SendNewItem(item, obj.Amount - curItemCount, true, false); } break; } case QUEST_OBJECTIVE_MONSTER: { if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(obj.ObjectID)) for (uint16 z = 0; z < obj.Amount; ++z) player->KilledMonster(creatureInfo, ObjectGuid::Empty); break; } case QUEST_OBJECTIVE_GAMEOBJECT: { for (uint16 z = 0; z < obj.Amount; ++z) player->KillCreditGO(obj.ObjectID); break; } case QUEST_OBJECTIVE_MIN_REPUTATION: { uint32 curRep = player->GetReputationMgr().GetReputation(obj.ObjectID); if (curRep < uint32(obj.Amount)) if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(obj.ObjectID)) player->GetReputationMgr().SetReputation(factionEntry, obj.Amount); break; } case QUEST_OBJECTIVE_MAX_REPUTATION: { uint32 curRep = player->GetReputationMgr().GetReputation(obj.ObjectID); if (curRep > uint32(obj.Amount)) if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(obj.ObjectID)) player->GetReputationMgr().SetReputation(factionEntry, obj.Amount); break; } case QUEST_OBJECTIVE_MONEY: { player->ModifyMoney(obj.Amount); break; } } } if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled { // prepare Quest Tracker datas PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE); stmt->setUInt32(0, quest->GetQuestId()); stmt->setUInt64(1, player->GetGUID().GetCounter()); // add to Quest Tracker CharacterDatabase.Execute(stmt); } player->CompleteQuest(entry); return true; }
void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) { TradeData* my_trade = _player->m_trade; if (!my_trade) return; Player* trader = my_trade->GetTrader(); TradeData* his_trade = trader->m_trade; if (!his_trade) return; Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; bool myCanCompleteTrade = true, hisCanCompleteTrade = true; // set before checks for propertly undo at problems (it already set in to client) my_trade->SetAccepted(true); // not accept case incorrect money amount if (!_player->HasEnoughMoney(my_trade->GetMoney())) { SendNotification(LANG_NOT_ENOUGH_GOLD); my_trade->SetAccepted(false, true); return; } // not accept case incorrect money amount if (!trader->HasEnoughMoney(his_trade->GetMoney())) { trader->GetSession()->SendNotification(LANG_NOT_ENOUGH_GOLD); his_trade->SetAccepted(false, true); return; } // not accept if some items now can't be trade (cheating) for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { if (Item* item = my_trade->GetItem(TradeSlots(i))) { if (!item->CanBeTraded(false, true)) { SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); return; } if (item->IsBindedNotWith(trader)) { SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE); SendTradeStatus(TRADE_STATUS_CLOSE_WINDOW/*TRADE_STATUS_TRADE_CANCELED*/); return; } } if (Item* item = his_trade->GetItem(TradeSlots(i))) { if (!item->CanBeTraded(false, true)) { SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); return; } //if (item->IsBindedNotWith(_player)) // dont mark as invalid when his item isnt good (not exploitable because if item is invalid trade will fail anyway later on the same check) //{ // SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE); // his_trade->SetAccepted(false, true); // return; //} } } if (his_trade->IsAccepted()) { setAcceptTradeMode(my_trade, his_trade, myItems, hisItems); Spell* my_spell = NULL; SpellCastTargets my_targets; Spell* his_spell = NULL; SpellCastTargets his_targets; // not accept if spell can't be casted now (cheating) if (uint32 my_spell_id = my_trade->GetSpell()) { SpellEntry const* spellEntry = sSpellStore.LookupEntry(my_spell_id); Item* castItem = my_trade->GetSpellCastItem(); if (!spellEntry || !his_trade->GetItem(TRADE_SLOT_NONTRADED) || (my_trade->HasSpellCastItem() && !castItem)) { clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); my_trade->SetSpell(0); return; } my_spell = new Spell(_player, spellEntry, true); my_spell->m_CastItem = castItem; my_targets.setTradeItemTarget(_player); my_spell->m_targets = my_targets; SpellCastResult res = my_spell->CheckCast(true); if (res != SPELL_CAST_OK) { my_spell->SendCastResult(res); clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); delete my_spell; my_trade->SetSpell(0); return; } } // not accept if spell can't be casted now (cheating) if (uint32 his_spell_id = his_trade->GetSpell()) { SpellEntry const* spellEntry = sSpellStore.LookupEntry(his_spell_id); Item* castItem = his_trade->GetSpellCastItem(); if (!spellEntry || !my_trade->GetItem(TRADE_SLOT_NONTRADED) || (his_trade->HasSpellCastItem() && !castItem)) { delete my_spell; his_trade->SetSpell(0); clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); return; } his_spell = new Spell(trader, spellEntry, true); his_spell->m_CastItem = castItem; his_targets.setTradeItemTarget(trader); his_spell->m_targets = his_targets; SpellCastResult res = his_spell->CheckCast(true); if (res != SPELL_CAST_OK) { his_spell->SendCastResult(res); clearAcceptTradeMode(my_trade, his_trade); clearAcceptTradeMode(myItems, hisItems); delete my_spell; delete his_spell; his_trade->SetSpell(0); return; } } // inform partner client trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); // test if item will fit in each inventory hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); clearAcceptTradeMode(myItems, hisItems); // in case of missing space report error if (!myCanCompleteTrade) { clearAcceptTradeMode(my_trade, his_trade); SendNotification(LANG_NOT_FREE_TRADE_SLOTS); trader->GetSession()->SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); my_trade->SetAccepted(false); his_trade->SetAccepted(false); return; } else if (!hisCanCompleteTrade) { clearAcceptTradeMode(my_trade, his_trade); SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); trader->GetSession()->SendNotification(LANG_NOT_FREE_TRADE_SLOTS); my_trade->SetAccepted(false); his_trade->SetAccepted(false); return; } // execute trade: 1. remove for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) { if (myItems[i]) { myItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID()); _player->MoveItemFromInventory(myItems[i]->GetBagSlot(), myItems[i]->GetSlot(), true); } if (hisItems[i]) { hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, trader->GetGUID()); trader->MoveItemFromInventory(hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot(), true); } } // execute trade: 2. store moveItems(myItems, hisItems); // logging money if (sWorld->getBoolConfig(CONFIG_GM_LOG_TRADE)) { if (_player->GetSession()->GetSecurity() > SEC_PLAYER && my_trade->GetMoney() > 0) { sLog->outCommand(_player->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", _player->GetName(), _player->GetSession()->GetAccountId(), my_trade->GetMoney(), trader->GetName(), trader->GetSession()->GetAccountId()); } if (trader->GetSession()->GetSecurity() > SEC_PLAYER && his_trade->GetMoney() > 0) { sLog->outCommand(trader->GetSession()->GetAccountId(),"GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)", trader->GetName(), trader->GetSession()->GetAccountId(), his_trade->GetMoney(), _player->GetName(), _player->GetSession()->GetAccountId()); } } // update money _player->ModifyMoney(-int32(my_trade->GetMoney())); _player->ModifyMoney(his_trade->GetMoney()); trader->ModifyMoney(-int32(his_trade->GetMoney())); trader->ModifyMoney(my_trade->GetMoney()); if (my_spell) my_spell->prepare(&my_targets); if (his_spell) his_spell->prepare(&his_targets); // cleanup clearAcceptTradeMode(my_trade, his_trade); delete _player->m_trade; _player->m_trade = NULL; delete trader->m_trade; trader->m_trade = NULL; // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) SQLTransaction trans = CharDB.BeginTransaction(); _player->SaveInventoryAndGoldToDB(trans); trader->SaveInventoryAndGoldToDB(trans); CharDB.CommitTransaction(trans); trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); } else { trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); } }
// this void is called when auction_owner cancels his auction void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data ) { DEBUG_LOG("WORLD: HandleAuctionRemoveItem"); ObjectGuid auctioneerGuid; uint32 auctionId; recv_data >> auctioneerGuid; recv_data >> auctionId; //DEBUG_LOG( "Cancel AUCTION AuctionID: %u", auctionId); AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) return; // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); AuctionEntry *auction = auctionHouse->GetAuction(auctionId); Player *pl = GetPlayer(); if (auction && auction->owner == pl->GetGUIDLow()) { Item *pItem = sAuctionMgr.GetAItem(auction->item_guidlow); if (pItem) { if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid { uint32 auctionCut = auction->GetAuctionCut(); if ( pl->GetMoney() < auctionCut ) //player doesn't have enough money, maybe message needed return; //some auctionBidderNotification would be needed, but don't know that parts.. SendAuctionCancelledToBidderMail( auction ); pl->ModifyMoney( -int32(auctionCut) ); } // Return the item by mail std::ostringstream msgAuctionCanceledOwner; msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED; // item will deleted or added to received mail list MailDraft(msgAuctionCanceledOwner.str()) .AddItem(pItem) .SendMailTo(pl, auction, MAIL_CHECK_MASK_COPIED); } else { sLog.outError("Auction id: %u has nonexistent item (item guid : %u)!!!", auction->Id, auction->item_guidlow); SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR ); return; } } else { SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR ); //this code isn't possible ... maybe there should be ASSERT sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId ); return; } //inform player, that auction is removed SendAuctionCommandResult( auction->Id, AUCTION_CANCEL, AUCTION_OK ); // Now remove the auction CharacterDatabase.BeginTransaction(); auction->DeleteFromDB(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); sAuctionMgr.RemoveAItem( auction->item_guidlow ); auctionHouse->RemoveAuction( auction->Id ); delete auction; }
// this function is called when client bids or buys out auction void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) { DEBUG_LOG("WORLD: HandleAuctionPlaceBid"); ObjectGuid auctioneerGuid; uint32 auctionId; uint32 price; recv_data >> auctioneerGuid; recv_data >> auctionId >> price; if (!auctionId || !price) return; // check for cheaters AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) return; // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); AuctionEntry *auction = auctionHouse->GetAuction(auctionId); Player *pl = GetPlayer(); if( !auction || auction->owner == pl->GetGUIDLow() ) { // you cannot bid your own auction: SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR ); return; } ObjectGuid ownerGuid = ObjectGuid(HIGHGUID_PLAYER, auction->owner); // impossible have online own another character (use this for speedup check in case online owner) Player* auction_owner = sObjectMgr.GetPlayer(ownerGuid); if (!auction_owner && sObjectMgr.GetPlayerAccountIdByGUID(ownerGuid) == pl->GetSession()->GetAccountId()) { // you cannot bid your another character auction: SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR ); return; } // cheating if(price <= auction->bid || price < auction->startbid) return; // price too low for next bid if not buyout if ((price < auction->buyout || auction->buyout == 0) && price < auction->bid + auction->GetAuctionOutBid()) { // auction has already higher bid, client tests it! return; } if (price > pl->GetMoney()) { // you don't have enough money!, client tests! // SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); return; } if ((price < auction->buyout) || (auction->buyout == 0)) { if (auction->bidder > 0) { if ( auction->bidder == pl->GetGUIDLow() ) { pl->ModifyMoney( -int32(price - auction->bid)); } else { // mail to last bidder and return money SendAuctionOutbiddedMail( auction , price ); pl->ModifyMoney( -int32(price) ); } } else { pl->ModifyMoney( -int32(price) ); } auction->bidder = pl->GetGUIDLow(); auction->bid = price; // after this update we should save player's money ... CharacterDatabase.PExecute("UPDATE auction SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0 ); } else { // buyout: if (pl->GetGUIDLow() == auction->bidder ) { pl->ModifyMoney(-int32(auction->buyout - auction->bid)); } else { pl->ModifyMoney(-int32(auction->buyout)); if ( auction->bidder ) // buyout for bidded auction .. { SendAuctionOutbiddedMail( auction, auction->buyout ); } } auction->bidder = pl->GetGUIDLow(); auction->bid = auction->buyout; sAuctionMgr.SendAuctionSalePendingMail( auction ); sAuctionMgr.SendAuctionSuccessfulMail( auction ); sAuctionMgr.SendAuctionWonMail( auction ); SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK); sAuctionMgr.RemoveAItem(auction->item_guidlow); auctionHouse->RemoveAuction(auction->Id); auction->DeleteFromDB(); delete auction; } CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/) { DEBUG_LOG("WORLD: CMSG_LOOT_MONEY"); Player* player = GetPlayer(); ObjectGuid guid = player->GetLootGuid(); if (!guid) return; Loot* pLoot = NULL; Item* pItem = NULL; switch (guid.GetHigh()) { case HIGHGUID_GAMEOBJECT: { GameObject* pGameObject = GetPlayer()->GetMap()->GetGameObject(guid); // not check distance for GO in case owned GO (fishing bobber case, for example) if (pGameObject && (pGameObject->GetOwnerGuid() == _player->GetObjectGuid() || pGameObject->IsWithinDistInMap(_player, INTERACTION_DISTANCE))) pLoot = &pGameObject->loot; break; } case HIGHGUID_CORPSE: // remove insignia ONLY in BG { Corpse* bones = _player->GetMap()->GetCorpse(guid); if (bones && bones->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) pLoot = &bones->loot; break; } case HIGHGUID_ITEM: { pItem = GetPlayer()->GetItemByGuid(guid); if (!pItem || !pItem->HasGeneratedLoot()) return; pLoot = &pItem->loot; break; } case HIGHGUID_UNIT: { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(guid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed); if (ok_loot && pCreature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) pLoot = &pCreature->loot ; break; } default: return; // unlootable type } if (pLoot) { pLoot->NotifyMoneyRemoved(); if (!guid.IsItem() && player->GetGroup()) // item can be looted only single player { Group* group = player->GetGroup(); std::vector<Player*> playersNear; for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* playerGroup = itr->getSource(); if (!playerGroup) continue; if (player->IsWithinDistInMap(playerGroup, sWorld.getConfig(CONFIG_FLOAT_GROUP_XP_DISTANCE), false)) playersNear.push_back(playerGroup); } uint32 money_per_player = uint32((pLoot->gold) / (playersNear.size())); for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i) { (*i)->ModifyMoney(money_per_player); WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4); data << uint32(money_per_player); (*i)->GetSession()->SendPacket(&data); } } else player->ModifyMoney(pLoot->gold); pLoot->gold = 0; if (pItem) pItem->SetLootState(ITEM_LOOT_CHANGED); } }
//this function is called when client bids or buys out auction void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data) { uint64 auctioneer; uint32 auctionId; uint32 price; recv_data >> auctioneer; recv_data >> auctionId >> price; if (!auctionId || !price) return; //check for cheaters Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); if (!pCreature) { sLog.outDebug("WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); return; } // remove fake death if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject *auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); AuctionEntry *auction = auctionHouse->GetAuction(auctionId); Player *pl = GetPlayer(); if (!auction || auction->owner == pl->GetGUIDLow()) { //you cannot bid your own auction: SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); return; } // impossible have online own another character (use this for speedup check in case online owner) Player* auction_owner = objmgr.GetPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)); if (!auction_owner && objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == pl->GetSession()->GetAccountId()) { //you cannot bid your another character auction: SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR); return; } // cheating if (price <= auction->bid || price < auction->startbid) return; // price too low for next bid if not buyout if ((price < auction->buyout || auction->buyout == 0) && price < auction->bid + auction->GetAuctionOutBid()) { //auction has already higher bid, client tests it! return; } if (price > pl->GetMoney()) { //you don't have enought money!, client tests! //SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???); return; } if (price < auction->buyout || auction->buyout == 0) { if (auction->bidder > 0) { if (auction->bidder == pl->GetGUIDLow()) pl->ModifyMoney(-int32(price - auction->bid)); else { // mail to last bidder and return money SendAuctionOutbiddedMail(auction, price); pl->ModifyMoney(-int32(price)); } } else pl->ModifyMoney(-int32(price)); auction->bidder = pl->GetGUIDLow(); auction->bid = price; GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); // after this update we should save player's money ... CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0); } else { //buyout: if (pl->GetGUIDLow() == auction->bidder) pl->ModifyMoney(-int32(auction->buyout - auction->bid)); else { pl->ModifyMoney(-int32(auction->buyout)); if (auction->bidder) //buyout for bidded auction .. SendAuctionOutbiddedMail(auction, auction->buyout); } auction->bidder = pl->GetGUIDLow(); auction->bid = auction->buyout; GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); auctionmgr.SendAuctionSalePendingMail(auction); auctionmgr.SendAuctionSuccessfulMail(auction); auctionmgr.SendAuctionWonMail(auction); SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK); auction->DeleteFromDB(); uint32 item_template = auction->item_template; auctionmgr.RemoveAItem(auction->item_guidlow); auctionHouse->RemoveAuction(auction, item_template); } CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
//Edit Player money static bool HandleModifyMoneyCommand(ChatHandler* handler, const char* args) { if (!*args) return false; Player* target = handler->getSelectedPlayer(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); handler->SetSentErrorMessage(true); return false; } // check online security if (handler->HasLowerSecurity(target, 0)) return false; int64 moneyToAdd = 0; if (strchr(args, 'g') || strchr(args, 's') || strchr(args, 'c')) moneyToAdd = MoneyStringToMoney(std::string(args)); else moneyToAdd = atol(args); uint64 targetMoney = target->GetMoney(); if (moneyToAdd < 0) { int64 newmoney = int64(targetMoney) + moneyToAdd; SF_LOG_DEBUG("misc", handler->GetSkyFireString(LANG_CURRENT_MONEY), uint32(targetMoney), int32(moneyToAdd), uint32(newmoney)); if (newmoney <= 0) { handler->PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, handler->GetNameLink(target).c_str()); if (handler->needReportToTarget(target)) ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, handler->GetNameLink().c_str()); target->SetMoney(0); } else { uint32 moneyToAddMsg = moneyToAdd * -1; if (newmoney > int64(MAX_MONEY_AMOUNT)) newmoney = MAX_MONEY_AMOUNT; handler->PSendSysMessage(LANG_YOU_TAKE_MONEY, moneyToAddMsg, handler->GetNameLink(target).c_str()); if (handler->needReportToTarget(target)) ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, handler->GetNameLink().c_str(), moneyToAddMsg); target->SetMoney(newmoney); } } else { handler->PSendSysMessage(LANG_YOU_GIVE_MONEY, uint32(moneyToAdd), handler->GetNameLink(target).c_str()); if (handler->needReportToTarget(target)) ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, handler->GetNameLink().c_str(), uint32(moneyToAdd)); if (moneyToAdd >= int64(MAX_MONEY_AMOUNT)) moneyToAdd = MAX_MONEY_AMOUNT; if (targetMoney >= uint64(MAX_MONEY_AMOUNT) - moneyToAdd) moneyToAdd -= targetMoney; target->ModifyMoney(moneyToAdd); } SF_LOG_DEBUG("misc", handler->GetSkyFireString(LANG_NEW_MONEY), uint32(targetMoney), int32(moneyToAdd), uint32(target->GetMoney())); return true; }
//this void is called when auction_owner cancels his auction void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data) { uint64 auctioneer; uint32 auctionId; recv_data >> auctioneer; recv_data >> auctionId; //sLog.outDebug("Cancel AUCTION AuctionID: %u", auctionId); Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer,UNIT_NPC_FLAG_AUCTIONEER); if (!pCreature) { sLog.outDebug("WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); return; } // remove fake death if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction()); AuctionEntry *auction = auctionHouse->GetAuction(auctionId); Player *pl = GetPlayer(); if (auction && auction->owner == pl->GetGUIDLow()) { Item *pItem = auctionmgr.GetAItem(auction->item_guidlow); if (pItem) { if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid { uint32 auctionCut = auction->GetAuctionCut(); if (pl->GetMoney() < auctionCut) //player doesn't have enough money, maybe message needed return; //some auctionBidderNotification would be needed, but don't know that parts.. SendAuctionCancelledToBidderMail(auction); pl->ModifyMoney(-int32(auctionCut)); } // Return the item by mail std::ostringstream msgAuctionCanceledOwner; msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED; // item will deleted or added to received mail list MailDraft(msgAuctionCanceledOwner.str()) .AddItem(pItem) .SendMailTo(pl, auction); } else { sLog.outError("Auction id: %u has non-existed item (item guid : %u)!!!", auction->Id, auction->item_guidlow); SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR); return; } } else { SendAuctionCommandResult(0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR); //this code isn't possible ... maybe there should be assert sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId); return; } //inform player, that auction is removed SendAuctionCommandResult(auction->Id, AUCTION_CANCEL, AUCTION_OK); // Now remove the auction CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); auction->DeleteFromDB(); uint32 item_template = auction->item_template; auctionmgr.RemoveAItem(auction->item_guidlow); auctionHouse->RemoveAuction(auction, item_template); }
/** * 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(); }
// this function is called when client bids or buys out auction void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: Received CMSG_AUCTION_PLACE_BID"); uint64 auctioneer; uint32 auctionId; uint64 price; recvData >> auctioneer; recvData >> auctionId; recvData >> price; if (!auctionId || !price) return; // check for cheaters Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); if (!creature) { TC_LOG_DEBUG("network", "WORLD: HandleAuctionPlaceBid - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); return; } // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); AuctionEntry* auction = auctionHouse->GetAuction(auctionId); Player* player = GetPlayer(); if (!auction || auction->owner == player->GetGUIDLow()) { //you cannot bid your own auction: SendAuctionCommandResult(NULL, AUCTION_PLACE_BID, ERR_AUCTION_BID_OWN); return; } // impossible have online own another character (use this for speedup check in case online owner) /*Player* auction_owner = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)); if (!auction_owner && sObjectMgr->GetPlayerAccountIdByGUID(MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER)) == player->GetSession()->GetAccountId()) { //you cannot bid your another character auction: SendAuctionCommandResult(NULL, AUCTION_PLACE_BID, ERR_AUCTION_BID_OWN); return; }*/ // cheating if (price <= auction->bid || price < auction->startbid) return; // price too low for next bid if not buyout if ((price < auction->buyout || auction->buyout == 0) && price < auction->bid + auction->GetAuctionOutBid()) { // client already test it but just in case ... SendAuctionCommandResult(auction, AUCTION_PLACE_BID, ERR_AUCTION_HIGHER_BID); return; } if (!player->HasEnoughMoney(price)) { // client already test it but just in case ... SendAuctionCommandResult(auction, AUCTION_PLACE_BID, ERR_AUCTION_NOT_ENOUGHT_MONEY); return; } SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (price < auction->buyout || auction->buyout == 0) { if (auction->bidder > 0) { if (auction->bidder == player->GetGUIDLow()) player->ModifyMoney(-int64(price - auction->bid)); else { // mail to last bidder and return money sAuctionMgr->SendAuctionOutbiddedMail(auction, price, GetPlayer(), trans); player->ModifyMoney(-int64(price)); } } else player->ModifyMoney(-int64(price)); auction->bidder = player->GetGUIDLow(); auction->bid = price; GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID); stmt->setUInt32(0, auction->bidder); stmt->setUInt32(1, auction->bid); stmt->setUInt32(2, auction->Id); trans->Append(stmt); SendAuctionCommandResult(auction, AUCTION_PLACE_BID, ERR_AUCTION_OK); } else { //buyout: if (player->GetGUIDLow() == auction->bidder) player->ModifyMoney(-int64(auction->buyout - auction->bid)); else { player->ModifyMoney(-int64(auction->buyout)); if (auction->bidder) //buyout for bidded auction .. sAuctionMgr->SendAuctionOutbiddedMail(auction, auction->buyout, GetPlayer(), trans); } auction->bidder = player->GetGUIDLow(); auction->bid = auction->buyout; GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); //- Mails must be under transaction control too to prevent data loss sAuctionMgr->SendAuctionSalePendingMail(auction, trans); sAuctionMgr->SendAuctionSuccessfulMail(auction, trans); sAuctionMgr->SendAuctionWonMail(auction, trans); SendAuctionCommandResult(auction, AUCTION_PLACE_BID, ERR_AUCTION_OK); auction->DeleteFromDB(trans); uint32 itemEntry = auction->itemEntry; sAuctionMgr->RemoveAItem(auction->itemGUIDLow); auctionHouse->RemoveAuction(auction, itemEntry); } 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) { 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 = sObjectMgr->GetPlayerGUIDByName(receiver); if (!rc) { sLog->outDetail("Player %u is sending mail to invalid player %s (no GUID) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", 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 = 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); if (QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc))) { 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, 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() : 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()) { 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, 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); uint32 itemTextId = !body.empty() ? sObjectMgr->CreateItemText( body ) : 0; pl->ModifyMoney(-int32(reqmoney)); bool needItemDelay = false; MailDraft draft(subject, itemTextId); if (items_count > 0 || money > 0) { uint32 rc_account = 0; if (receive) rc_account = receive->GetSession()->GetAccountId(); else rc_account = sObjectMgr->GetPlayerAccountIdByGUID(rc); 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, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
//this void is called when auction_owner cancels his auction void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: Received CMSG_AUCTION_REMOVE_ITEM"); uint64 auctioneer; uint32 auctionId; recvData >> auctioneer; recvData >> auctionId; Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); if (!creature) { TC_LOG_DEBUG("network", "WORLD: HandleAuctionRemoveItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(auctioneer))); return; } // remove fake death if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(creature->getFaction()); AuctionEntry* auction = auctionHouse->GetAuction(auctionId); Player* player = GetPlayer(); SQLTransaction trans = CharacterDatabase.BeginTransaction(); if (auction && auction->owner == player->GetGUIDLow()) { Item* pItem = sAuctionMgr->GetAItem(auction->itemGUIDLow); if (pItem) { if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid { uint32 auctionCut = auction->GetAuctionCut(); if (!player->HasEnoughMoney((uint64)auctionCut)) //player doesn't have enough money, maybe message needed return; sAuctionMgr->SendAuctionCancelledToBidderMail(auction, trans, pItem); player->ModifyMoney(-int64(auctionCut)); } // item will deleted or added to received mail list MailDraft(auction->BuildAuctionMailSubject(AUCTION_CANCELED), AuctionEntry::BuildAuctionMailBody(0, 0, auction->buyout, auction->deposit, 0)) .AddItem(pItem) .SendMailTo(trans, player, auction, MAIL_CHECK_MASK_COPIED); } else { TC_LOG_ERROR("network", "Auction id: %u got non existing item (item guid : %u)!", auction->Id, auction->itemGUIDLow); SendAuctionCommandResult(NULL, AUCTION_CANCEL, ERR_AUCTION_DATABASE_ERROR); return; } } else { SendAuctionCommandResult(NULL, AUCTION_CANCEL, ERR_AUCTION_DATABASE_ERROR); //this code isn't possible ... maybe there should be assert TC_LOG_ERROR("network", "CHEATER: %u tried to cancel auction (id: %u) of another player or auction is NULL", player->GetGUIDLow(), auctionId); return; } //inform player, that auction is removed SendAuctionCommandResult(auction, AUCTION_CANCEL, ERR_AUCTION_OK); // Now remove the auction player->SaveInventoryAndGoldToDB(trans); auction->DeleteFromDB(trans); CharacterDatabase.CommitTransaction(trans); uint32 itemEntry = auction->itemEntry; sAuctionMgr->RemoveAItem(auction->itemGUIDLow); auctionHouse->RemoveAuction(auction, itemEntry); }
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_MONEY"); Player *player = GetPlayer(); uint64 guid = player->GetLootGUID(); if (!guid) return; Loot *pLoot = NULL; switch(GUID_HIPART(guid)) { case HIGHGUID_GAMEOBJECT: { GameObject *pGameObject = GetPlayer()->GetMap()->GetGameObject(guid); // not check distance for GO in case owned GO (fishing bobber case, for example) if (pGameObject && ((pGameObject->GetOwnerGUID() == _player->GetGUID() || pGameObject->IsWithinDistInMap(_player, INTERACTION_DISTANCE)))) pLoot = &pGameObject->loot; break; } case HIGHGUID_CORPSE: // remove insignia ONLY in BG { Corpse *bones = ObjectAccessor::GetCorpse(*GetPlayer(), guid); if (bones && bones->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) pLoot = &bones->loot; break; } case HIGHGUID_ITEM: { if (Item *item = GetPlayer()->GetItemByGuid(guid)) pLoot = &item->loot; break; } case HIGHGUID_UNIT: { Creature* pCreature = GetPlayer()->GetMap()->GetCreature(guid); bool ok_loot = pCreature && pCreature->isAlive() == (player->getClass() == CLASS_ROGUE && pCreature->lootForPickPocketed); if (ok_loot && pCreature->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) pLoot = &pCreature->loot ; break; } default: return; // unlootable type } if (pLoot) { if (!IS_ITEM_GUID(guid) && player->GetGroup()) //item can be looted only single player { Group *group = player->GetGroup(); std::vector<Player*> playersNear; for (GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* playerGroup = itr->getSource(); if (!playerGroup) continue; if (player->IsWithinDistInMap(playerGroup, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) playersNear.push_back(playerGroup); } uint32 money_per_player = uint32((pLoot->gold)/(playersNear.size())); for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i) { (*i)->ModifyMoney(money_per_player); (*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, money_per_player); //Offset surely incorrect, but works WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 9); data << uint32(money_per_player); data << uint32(((pLoot->gold)* (*i)->GetGuildMoneyModifier())); data << uint8(0); (*i)->GetSession()->SendPacket(&data); } } else { player->ModifyMoney(pLoot->gold); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, pLoot->gold); } pLoot->gold = 0; pLoot->NotifyMoneyRemoved(); } }
// this void creates new auction and adds auction to some auctionhouse void WorldSession::HandleAuctionSellItem(WorldPacket& recv_data) { DEBUG_LOG("WORLD: HandleAuctionSellItem"); ObjectGuid auctioneerGuid; ObjectGuid itemGuid; uint32 etime, bid, buyout; recv_data >> auctioneerGuid; recv_data >> itemGuid; recv_data >> bid; recv_data >> buyout; recv_data >> etime; if (!bid || !etime) { return; } // check for cheaters Player* pl = GetPlayer(); AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) { return; } // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // client send time in minutes, convert to common used sec time etime *= MINUTE; // client understand only 3 auction time switch (etime) { case 1*MIN_AUCTION_TIME: case 4*MIN_AUCTION_TIME: case 12*MIN_AUCTION_TIME: break; default: return; } // remove fake death if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) { GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); } if (!itemGuid) { return; } Item* it = pl->GetItemByGuid(itemGuid); // do not allow to sell already auctioned items if (sAuctionMgr.GetAItem(itemGuid.GetCounter())) { sLog.outError("AuctionError, %s is sending %s, but item is already in another auction", pl->GetGuidStr().c_str(), itemGuid.GetString().c_str()); SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); return; } // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to auction) if (!it) { SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); return; } if (!it->CanBeTraded()) { SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); return; } if ((it->GetProto()->Flags & ITEM_FLAG_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) { SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); return; } // check money for deposit uint32 deposit = AuctionHouseMgr::GetAuctionDeposit(auctionHouseEntry, etime, it); if (pl->GetMoney() < deposit) { SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_NOT_ENOUGH_MONEY); return; } if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount()); } pl->ModifyMoney(-int32(deposit)); AuctionEntry* AH = auctionHouse->AddAuction(auctionHouseEntry, it, etime, bid, buyout, deposit, pl); DETAIL_LOG("selling %s to auctioneer %s with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", itemGuid.GetString().c_str(), auctioneerGuid.GetString().c_str(), bid, buyout, etime, auctionHouseEntry->houseId); SendAuctionCommandResult(AH, AUCTION_STARTED, AUCTION_OK); #ifdef ENABLE_ELUNA sEluna->OnAdd(auctionHouse, AH); #endif /* ENABLE_ELUNA */ }
//called when player takes item attached in mail void WorldSession::HandleMailTakeItem(WorldPacket& recvData) { ObjectGuid mailbox; uint32 mailId; uint32 itemId; recvData >> mailId; recvData >> itemId; mailbox[6] = recvData.ReadBit(); mailbox[5] = recvData.ReadBit(); mailbox[2] = recvData.ReadBit(); mailbox[3] = recvData.ReadBit(); mailbox[0] = recvData.ReadBit(); mailbox[1] = recvData.ReadBit(); mailbox[4] = recvData.ReadBit(); mailbox[7] = recvData.ReadBit(); recvData.ReadByteSeq(mailbox[0]); recvData.ReadByteSeq(mailbox[1]); recvData.ReadByteSeq(mailbox[4]); recvData.ReadByteSeq(mailbox[2]); recvData.ReadByteSeq(mailbox[5]); recvData.ReadByteSeq(mailbox[6]); recvData.ReadByteSeq(mailbox[3]); recvData.ReadByteSeq(mailbox[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_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } // prevent cheating with skip client money check if (!player->HasEnoughMoney(uint64(m->COD))) { player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Item* it = player->GetMItem(itemId); ItemPosCountVec dest; uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false); if (msg == EQUIP_ERR_OK) { SQLTransaction trans = CharacterDatabase.BeginTransaction(); m->RemoveItem(itemId); m->removedItems.push_back(itemId); if (m->COD > 0) //if there is COD, take COD money from player and send them to sender by mail { uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER); Player* receiver = ObjectAccessor::FindPlayer(sender_guid); uint32 sender_accId = 0; if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) { std::string sender_name; if (receiver) { sender_accId = receiver->GetSession()->GetAccountId(); sender_name = receiver->GetName(); } else { // can be calculated early sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid); if (!sObjectMgr->GetPlayerNameByGUID(sender_guid, sender_name)) sender_name = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN); } sLog->outCommand(GetAccountId(), "GM %s (Account: %u) receiver mail item: %s (Entry: %u Count: %u) and send COD money: " UI64FMTD " to player: %s (Account: %u)", GetPlayerName().c_str(), GetAccountId(), it->GetTemplate()->Name1.c_str(), it->GetEntry(), it->GetCount(), m->COD, sender_name.c_str(), sender_accId); } else if (!receiver) sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid); // check player existence if (receiver || sender_accId) { MailDraft(m->subject, "") .AddMoney(m->COD) .SendMailTo(trans, MailReceiver(receiver, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT); } player->ModifyMoney(-int32(m->COD)); } m->COD = 0; m->state = MAIL_STATE_CHANGED; player->m_mailsUpdated = true; player->RemoveMItem(it->GetGUIDLow()); uint32 count = it->GetCount(); // save counts before store and possible merge with deleting it->SetState(ITEM_UNCHANGED); // need to set this state, otherwise item cannot be removed later, if neccessary player->MoveItemToInventory(dest, it, true); player->SaveInventoryAndGoldToDB(trans); player->_SaveMail(trans); CharacterDatabase.CommitTransaction(trans); player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); } else player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); }
// this void is called when auction_owner cancels his auction void WorldSession::HandleAuctionRemoveItem(WorldPacket& recv_data) { DEBUG_LOG("WORLD: HandleAuctionRemoveItem"); ObjectGuid auctioneerGuid; uint32 auctionId; recv_data >> auctioneerGuid; recv_data >> auctionId; // DEBUG_LOG("Cancel AUCTION AuctionID: %u", auctionId); AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) { return; } // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // remove fake death if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) { GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); } AuctionEntry* auction = auctionHouse->GetAuction(auctionId); Player* pl = GetPlayer(); if (!auction || auction->owner != pl->GetGUIDLow()) { SendAuctionCommandResult(NULL, AUCTION_REMOVED, AUCTION_ERR_DATABASE); sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId); return; } Item* pItem = sAuctionMgr.GetAItem(auction->itemGuidLow); if (!pItem) { sLog.outError("Auction id: %u has nonexistent item (item guid : %u)!!!", auction->Id, auction->itemGuidLow); SendAuctionCommandResult(NULL, AUCTION_REMOVED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND); return; } if (auction->bid) // If we have a bid, we have to send him the money he paid { uint32 auctionCut = auction->GetAuctionCut(); if (pl->GetMoney() < auctionCut) // player doesn't have enough money, maybe message needed { return; } if (auction->bidder) // if auction have real existed bidder send mail { SendAuctionCancelledToBidderMail(auction); } pl->ModifyMoney(-int32(auctionCut)); } // Return the item by mail std::ostringstream msgAuctionCanceledOwner; msgAuctionCanceledOwner << auction->itemTemplate << ":" << auction->itemRandomPropertyId << ":" << AUCTION_CANCELED; // item will deleted or added to received mail list MailDraft(msgAuctionCanceledOwner.str()) .AddItem(pItem) .SendMailTo(pl, auction, MAIL_CHECK_MASK_COPIED); // inform player, that auction is removed SendAuctionCommandResult(auction, AUCTION_REMOVED, AUCTION_OK); // Now remove the auction CharacterDatabase.BeginTransaction(); auction->DeleteFromDB(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); sAuctionMgr.RemoveAItem(auction->itemGuidLow); auctionHouse->RemoveAuction(auction->Id); #ifdef ENABLE_ELUNA sEluna->OnRemove(auctionHouse, auction); #endif /* ENABLE_ELUNA */ delete auction; }
void WorldSession::HandleLootMoneyOpcode(WorldPacket & /*recv_data*/) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_LOOT_MONEY"); Player* player = GetPlayer(); uint64 guid = player->GetLootGUID(); if (!guid) return; Loot* loot = NULL; bool shareMoney = true; switch(GUID_HIPART(guid)) { case HIGHGUID_GAMEOBJECT: { GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid); // do not check distance for GO if player is the owner of it (ex. fishing bobber) if (go && ((go->GetOwnerGUID() == player->GetGUID() || go->IsWithinDistInMap(player, INTERACTION_DISTANCE)))) loot = &go->loot; break; } case HIGHGUID_CORPSE: // remove insignia ONLY in BG { Corpse* bones = ObjectAccessor::GetCorpse(*player, guid); if (bones && bones->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { loot = &bones->loot; shareMoney = false; } break; } case HIGHGUID_ITEM: { if (Item* item = player->GetItemByGuid(guid)) { loot = &item->loot; shareMoney = false; } break; } case HIGHGUID_UNIT: { Creature* creature = player->GetMap()->GetCreature(guid); bool lootAllowed = creature && creature->isAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed); if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE)) { loot = &creature->loot; if (creature->isAlive()) shareMoney = false; } break; } default: return; // unlootable type } if (loot) { if (shareMoney && player->GetGroup()) //item, pickpocket and players can be looted only single player { Group* group = player->GetGroup(); std::vector<Player*> playersNear; for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { Player* member = itr->getSource(); if (!member) continue; if (player->IsWithinDistInMap(member, sWorld->getFloatConfig(CONFIG_GROUP_XP_DISTANCE), false)) playersNear.push_back(member); } uint32 goldPerPlayer = uint32((loot->gold) / (playersNear.size())); for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i) { WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4); data << uint32(goldPerPlayer); data << uint8(0); // Controls the text displayed 0 is "Your share is...", 1 "You loot..." (*i)->GetSession()->SendPacket(&data); (*i)->ModifyMoney(goldPerPlayer); (*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, goldPerPlayer); } } else { WorldPacket data(SMSG_LOOT_MONEY_NOTIFY, 4); data << uint32(loot->gold); data << uint8(1); SendPacket(&data); player->ModifyMoney(loot->gold); player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, loot->gold); } loot->gold = 0; loot->NotifyMoneyRemoved(); } }
void WorldSession::HandleSendMail(WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,8+1+1+1+4+4+1+4+4+8+1); uint64 mailbox, unk3; std::string receiver, subject, body; uint32 unk1, unk2, money, COD; uint8 unk4; recv_data >> mailbox; 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 MailItemsInfo mi; uint8 items_count; recv_data >> items_count; // attached items count if(items_count > 12) // client limit 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); if(items_count) { for(uint8 i = 0; i < items_count; ++i) { uint8 item_slot; uint64 item_guid; recv_data >> item_slot; recv_data >> item_guid; mi.AddItem(GUID_LOPART(item_guid), item_slot); } } recv_data >> money >> COD; // money and cod recv_data >> unk3; // const 0 recv_data >> unk4; // const 0 items_count = mi.size(); // this is the real size after the duplicates have been removed if (receiver.empty()) return; Player* pl = _player; uint64 rc = 0; if(normalizePlayerName(receiver)) rc = objmgr.GetPlayerGUIDByName(receiver); if (!rc) { sLog.outDetail("Player %u is sending mail to %s (GUID: not existed!) with subject %s and body %s includes %u items, %u copper and %u COD copper with unk1 = %u, unk2 = %u", 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 = 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); QueryResult* result = CharacterDatabase.PQuery("SELECT COUNT(*) FROM mail WHERE receiver = '%u'", GUID_LOPART(rc)); if(result) { 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, 0, MAIL_ERR_INTERNAL_ERROR); return; } // test the receiver's Faction... if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && pl->GetTeam() != rc_team && GetSecurity() == SEC_PLAYER) { pl->SendMailResult(0, 0, MAIL_ERR_NOT_YOUR_TEAM); return; } if (items_count) { for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) { MailItem& mailItem = mailItemIter->second; if(!mailItem.item_guidlow) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } mailItem.item = pl->GetItemByGuid(MAKE_NEW_GUID(mailItem.item_guidlow, 0, HIGHGUID_ITEM)); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if(!mailItem.item || !mailItem.item->CanBeTraded()) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION)) { pl->SendMailResult(0, 0, MAIL_ERR_INTERNAL_ERROR); return; } if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED)) { pl->SendMailResult(0, 0, MAIL_ERR_CANT_SEND_WRAPPED_COD); return; } } } pl->SendMailResult(0, 0, MAIL_OK); uint32 itemTextId = 0; if (!body.empty()) { itemTextId = objmgr.CreateItemText( body ); } pl->ModifyMoney( -int32(reqmoney) ); bool needItemDelay = false; if(items_count > 0 || money > 0) { uint32 rc_account = 0; if(receive) rc_account = receive->GetSession()->GetAccountId(); else rc_account = objmgr.GetPlayerAccountIdByGUID(rc); if (items_count > 0) { for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter) { MailItem& mailItem = mailItemIter->second; if(!mailItem.item) continue; mailItem.item_template = mailItem.item ? mailItem.item->GetEntry() : 0; if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE) ) { sLog.outCommand(GetAccountId(), "GM %s (Account: %u) mail item: %s (Entry: %u Count: %u) to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), mailItem.item->GetProto()->Name1, mailItem.item->GetEntry(), mailItem.item->GetCount(), receiver.c_str(), rc_account); } pl->MoveItemFromInventory(mailItem.item->GetBagSlot(), mailItem.item->GetSlot(), true); CharacterDatabase.BeginTransaction(); mailItem.item->DeleteFromInventoryDB(); //deletes item from character's inventory mailItem.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), mailItem.item->GetGUIDLow()); CharacterDatabase.CommitTransaction(); } // if item send to character at another account, then apply item delivery delay needItemDelay = pl->GetSession()->GetAccountId() != rc_account; } if(money > 0 && GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { sLog.outCommand(GetAccountId(),"GM %s (Account: %u) mail money: %u to player: %s (Account: %u)", GetPlayerName(), GetAccountId(), money, receiver.c_str(), rc_account); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; // will delete item or place to receiver mail list WorldSession::SendMailTo(receive, MAIL_NORMAL, MAIL_STATIONERY_NORMAL, pl->GetGUIDLow(), GUID_LOPART(rc), subject, itemTextId, &mi, money, COD, MAIL_CHECK_MASK_NONE, deliver_delay); CharacterDatabase.BeginTransaction(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); }
// this void creates new auction and adds auction to some auctionhouse void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data ) { DEBUG_LOG("WORLD: HandleAuctionSellItem"); ObjectGuid auctioneerGuid; ObjectGuid itemGuid; uint32 etime, bid, buyout; recv_data >> auctioneerGuid; recv_data.read_skip<uint32>(); // const 1? recv_data >> itemGuid; recv_data.read_skip<uint32>(); // stack size recv_data >> bid; recv_data >> buyout; recv_data >> etime; if (itemGuid.IsEmpty() || !bid || !etime) return; // check for cheaters Player *pl = GetPlayer(); AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid); if (!auctionHouseEntry) return; // always return pointer AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry); // client send time in minutes, convert to common used sec time etime *= MINUTE; // client understand only 3 auction time switch(etime) { case 1*MIN_AUCTION_TIME: case 2*MIN_AUCTION_TIME: case 4*MIN_AUCTION_TIME: break; default: return; } // remove fake death if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); Item *it = pl->GetItemByGuid(itemGuid); // do not allow to sell already auctioned items if (sAuctionMgr.GetAItem(itemGuid.GetCounter())) { sLog.outError("AuctionError, %s is sending %s, but item is already in another auction", pl->GetGuidStr().c_str(), itemGuid.GetString().c_str()); SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); return; } // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to auction) if(!it) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND); return; } if(!it->CanBeTraded()) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); return; } if ((it->GetProto()->Flags & ITEM_FLAG_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION)) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR); return; } //we have to take deposit : uint32 deposit = AuctionHouseMgr::GetAuctionDeposit( auctionHouseEntry, etime, it ); if ( pl->GetMoney() < deposit ) { SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY); return; } if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE) ) { sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)", GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount()); } pl->ModifyMoney( -int32(deposit) ); uint32 auction_time = uint32(etime * sWorld.getConfig(CONFIG_FLOAT_RATE_AUCTION_TIME)); AuctionEntry *AH = new AuctionEntry; AH->Id = sObjectMgr.GenerateAuctionID(); AH->item_guidlow = itemGuid.GetCounter(); AH->item_template = it->GetEntry(); AH->owner = pl->GetGUIDLow(); AH->startbid = bid; AH->bidder = 0; AH->bid = 0; AH->buyout = buyout; AH->expire_time = time(NULL) + auction_time; AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; DETAIL_LOG("selling %s to auctioneer %s with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", itemGuid.GetString().c_str(), auctioneerGuid.GetString().c_str(), bid, buyout, auction_time, AH->GetHouseId()); auctionHouse->AddAuction(AH); //remove from refundable map pl->RemoveRefundableItem(it->GetGUID()); sAuctionMgr.AddAItem(it); // Remove item from refundable map pl->RemoveRefundableItem(it->GetGUID()); pl->MoveItemFromInventory( it->GetBagSlot(), it->GetSlot(), true); CharacterDatabase.BeginTransaction(); it->DeleteFromInventoryDB(); it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone AH->SaveToDB(); pl->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK); GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1); }
//called when player takes item attached in mail void WorldSession::HandleMailTakeItem(WorldPacket & recvData) { uint64 mailbox; uint32 mailId; uint32 itemId; recvData >> mailbox; recvData >> mailId; recvData >> itemId; // item guid low 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_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } // verify that the mail has the item to avoid cheaters taking COD items without paying bool foundItem = false; for (std::vector<MailItemInfo>::const_iterator itr = m->items.begin(); itr != m->items.end(); ++itr) if (itr->item_guid == itemId) { foundItem = true; break; } if (!foundItem) { player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_INTERNAL_ERROR); return; } // prevent cheating with skip client money check if (!player->HasEnoughMoney(m->COD)) { player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_NOT_ENOUGH_MONEY); return; } Item* it = player->GetMItem(itemId); ItemPosCountVec dest; uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false); if (msg == EQUIP_ERR_OK) { SQLTransaction trans = CharacterDatabase.BeginTransaction(); m->RemoveItem(itemId); m->removedItems.push_back(itemId); if (m->COD > 0) // if there is COD, take COD money from player and send them to sender by mail { uint64 sender_guid = MAKE_NEW_GUID(m->sender, 0, HIGHGUID_PLAYER); uint32 sender_accId = 0; Player* sender = ObjectAccessor::FindPlayerInOrOutOfWorld(sender_guid); if (sender) sender_accId = sender->GetSession()->GetAccountId(); else sender_accId = sObjectMgr->GetPlayerAccountIdByGUID(sender_guid); // check player existence if (sender || sender_accId) { MailDraft(m->subject, "") .AddMoney(m->COD) .SendMailTo(trans, MailReceiver(sender, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT); if( m->COD >= 10*GOLD ) { std::string senderName; if (!sObjectMgr->GetPlayerNameByGUID(sender_guid, senderName)) senderName = sObjectMgr->GetTrinityStringForDBCLocale(LANG_UNKNOWN); std::string subj = m->subject; CleanStringForMysqlQuery(subj); CharacterDatabase.PExecute("INSERT INTO log_money VALUES(%u, %u, \"%s\", \"%s\", %u, \"%s\", %u, \"<COD> %s\", NOW())", GetAccountId(), player->GetGUIDLow(), player->GetName().c_str(), player->GetSession()->GetRemoteAddress().c_str(), sender_accId, senderName.c_str(), m->COD, subj.c_str()); } } player->ModifyMoney(-int32(m->COD)); } m->COD = 0; m->state = MAIL_STATE_CHANGED; player->m_mailsUpdated = true; player->RemoveMItem(it->GetGUIDLow()); uint32 count = it->GetCount(); // save counts before store and possible merge with deleting it->SetState(ITEM_UNCHANGED); // need to set this state, otherwise item cannot be removed later, if neccessary player->MoveItemToInventory(dest, it, true); player->SaveInventoryAndGoldToDB(trans); player->_SaveMail(trans); CharacterDatabase.CommitTransaction(trans); player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count); } else player->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_ERR_EQUIP_ERROR, msg); }