diff options
Diffstat (limited to 'src')
5 files changed, 148 insertions, 150 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 3d0631b5216..675036a5500 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -121,7 +121,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT " SelectItemInstanceContent ", bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid LEFT JOIN item_instance_gems ig ON ii.guid = ig.itemGuid LEFT JOIN item_instance_transmog iit ON ii.guid = iit.itemGuid LEFT JOIN item_instance_modifiers im ON ii.guid = im.itemGuid WHERE ci.guid = ? ORDER BY (ii.flags & 0x80000) ASC, bag ASC, slot ASC", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT cs.friend, c.account, cs.flags, cs.note FROM character_social cs JOIN characters c ON c.guid = cs.friend WHERE cs.guid = ? AND c.deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ, orientation FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time, categoryId, categoryEnd FROM character_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 09372319688..f02ad1a4e68 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -141,7 +141,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_BNET_CHECK_PASSWORD, "SELECT 1 FROM battlenet_accounts WHERE id = ? AND sha_pass_hash = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_UPD_BNET_ACCOUNT_LOCK, "UPDATE battlenet_accounts SET locked = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_BNET_ACCOUNT_LOCK_CONTRY, "UPDATE battlenet_accounts SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT, "SELECT battlenet_account FROM account WHERE id = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT, "SELECT battlenet_account FROM account WHERE id = ?", CONNECTION_BOTH); PrepareStatement(LOGIN_UPD_BNET_GAME_ACCOUNT_LINK, "UPDATE account SET battlenet_account = ?, battlenet_index = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_BNET_MAX_ACCOUNT_INDEX, "SELECT MAX(battlenet_index) FROM account WHERE battlenet_account = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_BNET_GAME_ACCOUNT_LIST_SMALL, "SELECT a.id, a.username FROM account a LEFT JOIN battlenet_accounts ba ON a.battlenet_account = ba.id WHERE ba.email = ?", CONNECTION_SYNCH); diff --git a/src/server/game/Accounts/BattlenetAccountMgr.cpp b/src/server/game/Accounts/BattlenetAccountMgr.cpp index a204e99f337..08a807b8db6 100644 --- a/src/server/game/Accounts/BattlenetAccountMgr.cpp +++ b/src/server/game/Accounts/BattlenetAccountMgr.cpp @@ -161,6 +161,13 @@ uint32 Battlenet::AccountMgr::GetIdByGameAccount(uint32 gameAccountId) return 0; } +QueryCallback Battlenet::AccountMgr::GetIdByGameAccountAsync(uint32 gameAccountId) +{ + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ACCOUNT_ID_BY_GAME_ACCOUNT); + stmt->setUInt32(0, gameAccountId); + return LoginDatabase.AsyncQuery(stmt); +} + uint8 Battlenet::AccountMgr::GetMaxIndex(uint32 accountId) { LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_MAX_ACCOUNT_INDEX); diff --git a/src/server/game/Accounts/BattlenetAccountMgr.h b/src/server/game/Accounts/BattlenetAccountMgr.h index 43c60c5b006..bfeb02302f7 100644 --- a/src/server/game/Accounts/BattlenetAccountMgr.h +++ b/src/server/game/Accounts/BattlenetAccountMgr.h @@ -21,6 +21,7 @@ #include "Define.h" #include <string> +class QueryCallback; enum class AccountOpResult : uint8; #define MAX_BNET_EMAIL_STR 320 @@ -38,6 +39,7 @@ namespace Battlenet TC_GAME_API uint32 GetId(std::string_view username); TC_GAME_API bool GetName(uint32 accountId, std::string& name); TC_GAME_API uint32 GetIdByGameAccount(uint32 gameAccountId); + [[nodiscard]] TC_GAME_API QueryCallback GetIdByGameAccountAsync(uint32 gameAccountId); TC_GAME_API uint8 GetMaxIndex(uint32 accountId); TC_GAME_API std::string CalculateShaPassHash(std::string_view name, std::string_view password); diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 6959405c23b..410726ec394 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -144,201 +144,190 @@ void WorldSession::HandleSendMail(WorldPackets::Mail::SendMail& sendMail) return; } - Player* receiver = ObjectAccessor::FindConnectedPlayer(receiverGuid); - - uint32 receiverTeam = 0; - uint8 mailsCount = 0; //do not allow to send to one player more than 100 mails - uint8 receiverLevel = 0; - uint32 receiverAccountId = 0; - uint32 receiverBnetAccountId = 0; - - if (receiver) + auto mailCountCheckContinuation = [this, player = _player, receiverGuid, mailInfo = std::move(sendMail.Info), reqmoney, cost](uint32 receiverTeam, uint64 mailsCount, uint8 receiverLevel, uint32 receiverAccountId, uint32 receiverBnetAccountId) mutable { - receiverTeam = receiver->GetTeam(); - mailsCount = receiver->GetMailSize(); - receiverLevel = receiver->GetLevel(); - receiverAccountId = receiver->GetSession()->GetAccountId(); - receiverBnetAccountId = receiver->GetSession()->GetBattlenetAccountId(); - } - else - { - if (CharacterCacheEntry const* characterInfo = sCharacterCache->GetCharacterCacheByGuid(receiverGuid)) - { - receiverTeam = Player::TeamForRace(characterInfo->Race); - receiverLevel = characterInfo->Level; - receiverAccountId = characterInfo->AccountId; - } - - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_COUNT); - stmt->setUInt64(0, receiverGuid.GetCounter()); + if (_player != player) + return; - PreparedQueryResult result = CharacterDatabase.Query(stmt); - if (result) + // 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) { - Field* fields = result->Fetch(); - mailsCount = fields[0].GetUInt64(); + player->SendMailResult(0, MAIL_SEND, MAIL_ERR_RECIPIENT_CAP_REACHED); + return; } - receiverBnetAccountId = Battlenet::AccountMgr::GetIdByGameAccount(receiverAccountId); - } - - // 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 = !sendMail.Info.Attachments.empty(); - for (auto const& att : sendMail.Info.Attachments) - { - if (Item* item = player->GetItemByGuid(att.ItemGUID)) + // test the receiver's Faction... or all items are account bound + bool accountBound = !mailInfo.Attachments.empty(); + for (auto const& att : mailInfo.Attachments) { - ItemTemplate const* itemProto = item->GetTemplate(); - if (!itemProto || !itemProto->HasFlag(ITEM_FLAG_IS_BOUND_TO_ACCOUNT)) + if (Item* item = player->GetItemByGuid(att.ItemGUID)) { - accountBound = false; - break; + ItemTemplate const* itemProto = item->GetTemplate(); + if (!itemProto || !itemProto->HasFlag(ITEM_FLAG_IS_BOUND_TO_ACCOUNT)) + { + accountBound = false; + break; + } } } - } - if (!accountBound && player->GetTeam() != receiverTeam && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) - { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); - return; - } - - if (receiverLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) - { - SendNotification(GetTrinityString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); - return; - } - - std::vector<Item*> items; - - for (auto const& att : sendMail.Info.Attachments) - { - if (att.ItemGUID.IsEmpty()) + if (!accountBound && player->GetTeam() != receiverTeam && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); + player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; } - Item* item = player->GetItemByGuid(att.ItemGUID); - - // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) - if (!item) + if (receiverLevel < sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)) { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); + SendNotification(GetTrinityString(LANG_MAIL_RECEIVER_REQ), sWorld->getIntConfig(CONFIG_MAIL_LEVEL_REQ)); return; } - if (!item->CanBeTraded(true)) - { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); - return; - } + std::vector<Item*> items; - if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != receiverAccountId) + for (auto const& att : mailInfo.Attachments) { - if (!item->IsBattlenetAccountBound() || !player->GetSession()->GetBattlenetAccountId() || player->GetSession()->GetBattlenetAccountId() != receiverBnetAccountId) + if (att.ItemGUID.IsEmpty()) { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_NOT_SAME_ACCOUNT); + player->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID); return; } - } - if (item->GetTemplate()->HasFlag(ITEM_FLAG_CONJURED) || *item->m_itemData->Expiration) - { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); - return; - } + Item* item = player->GetItemByGuid(att.ItemGUID); - if (sendMail.Info.Cod && item->IsWrapped()) - { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD); - return; - } + // 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->IsNotEmptyBag()) - { - player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_DESTROY_NONEMPTY_BAG); - return; - } + if (!item->CanBeTraded(true)) + { + player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); + return; + } - items.push_back(item); - } + if (item->IsBoundAccountWide() && item->IsSoulBound() && player->GetSession()->GetAccountId() != receiverAccountId) + { + if (!item->IsBattlenetAccountBound() || !player->GetSession()->GetBattlenetAccountId() || player->GetSession()->GetBattlenetAccountId() != receiverBnetAccountId) + { + player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_NOT_SAME_ACCOUNT); + return; + } + } + + if (item->GetTemplate()->HasFlag(ITEM_FLAG_CONJURED) || *item->m_itemData->Expiration) + { + player->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM); + return; + } + + if (mailInfo.Cod && item->IsWrapped()) + { + 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; + } - player->SendMailResult(0, MAIL_SEND, MAIL_OK); + items.push_back(item); + } - player->ModifyMoney(-reqmoney); - player->UpdateCriteria(CriteriaType::MoneySpentOnPostage, cost); + player->SendMailResult(0, MAIL_SEND, MAIL_OK); - bool needItemDelay = false; + player->ModifyMoney(-reqmoney); + player->UpdateCriteria(CriteriaType::MoneySpentOnPostage, cost); - MailDraft draft(sendMail.Info.Subject, sendMail.Info.Body); + bool needItemDelay = false; - CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + MailDraft draft(mailInfo.Subject, mailInfo.Body); - if (!sendMail.Info.Attachments.empty() || sendMail.Info.SendMoney > 0) - { - bool log = HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE); - if (!sendMail.Info.Attachments.empty()) + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + if (!mailInfo.Attachments.empty() || mailInfo.SendMoney > 0) { - for (auto const& item : items) + bool log = HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE); + if (!mailInfo.Attachments.empty()) { - if (log) + for (auto const& item : items) { - sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail item: %s (Entry: %u Count: %u) " - "to: %s (%s) (Account: %u)", GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), - item->GetTemplate()->GetDefaultLocaleName(), item->GetEntry(), item->GetCount(), - sendMail.Info.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); - } + if (log) + { + sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail item: %s (Entry: %u Count: %u) " + "to: %s (%s) (Account: %u)", GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), + item->GetTemplate()->GetDefaultLocaleName(), item->GetEntry(), item->GetCount(), + mailInfo.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); + } + + item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable + player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); + + item->DeleteFromInventoryDB(trans); // deletes item from character's inventory + item->SetOwnerGUID(receiverGuid); + item->SetState(ITEM_CHANGED); + item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone - item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable - player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); - - item->DeleteFromInventoryDB(trans); // deletes item from character's inventory - item->SetOwnerGUID(receiverGuid); - item->SetState(ITEM_CHANGED); - item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone + draft.AddItem(item); + } - draft.AddItem(item); + // if item send to character at another account, then apply item delivery delay + needItemDelay = player->GetSession()->GetAccountId() != receiverAccountId; } - // if item send to character at another account, then apply item delivery delay - needItemDelay = player->GetSession()->GetAccountId() != receiverAccountId; + if (log && mailInfo.SendMoney > 0) + { + sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail money: " SI64FMTD " to: %s (%s) (Account: %u)", + GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), mailInfo.SendMoney, mailInfo.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); + } } - if (log && sendMail.Info.SendMoney > 0) - { - sLog->outCommand(GetAccountId(), "GM %s (%s) (Account: %u) mail money: " SI64FMTD " to: %s (%s) (Account: %u)", - GetPlayerName().c_str(), _player->GetGUID().ToString().c_str(), GetAccountId(), sendMail.Info.SendMoney, sendMail.Info.Target.c_str(), receiverGuid.ToString().c_str(), receiverAccountId); - } - } + // If theres is an item, there is a one hour delivery delay if sent to another account's character. + uint32 deliver_delay = needItemDelay ? sWorld->getIntConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0; + + // Mail sent between guild members arrives instantly + if (Guild* guild = sGuildMgr->GetGuildById(player->GetGuildId())) + if (guild->IsMember(receiverGuid)) + deliver_delay = 0; - // 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 (mailInfo.Attachments.empty()) + mailInfo.Cod = 0; - // Mail sent between guild members arrives instantly - if (Guild* guild = sGuildMgr->GetGuildById(player->GetGuildId())) - if (guild->IsMember(receiverGuid)) - deliver_delay = 0; + // will delete item or place to receiver mail list + draft + .AddMoney(mailInfo.SendMoney) + .AddCOD(mailInfo.Cod) + .SendMailTo(trans, MailReceiver(ObjectAccessor::FindConnectedPlayer(receiverGuid), receiverGuid.GetCounter()), MailSender(player), mailInfo.Body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); - // don't ask for COD if there are no items - if (sendMail.Info.Attachments.empty()) - sendMail.Info.Cod = 0; + player->SaveInventoryAndGoldToDB(trans); + CharacterDatabase.CommitTransaction(trans); + }; - // will delete item or place to receiver mail list - draft - .AddMoney(sendMail.Info.SendMoney) - .AddCOD(sendMail.Info.Cod) - .SendMailTo(trans, MailReceiver(receiver, receiverGuid.GetCounter()), MailSender(player), sendMail.Info.Body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay); + if (Player* receiver = ObjectAccessor::FindConnectedPlayer(receiverGuid)) + { + mailCountCheckContinuation(receiver->GetTeam(), receiver->GetMailSize(), receiver->GetLevel(), receiver->GetSession()->GetAccountId(), receiver->GetSession()->GetBattlenetAccountId()); + } + else + { + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL_COUNT); + stmt->setUInt64(0, receiverGuid.GetCounter()); - player->SaveInventoryAndGoldToDB(trans); - CharacterDatabase.CommitTransaction(trans); + GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithChainingPreparedCallback([continuation = std::move(mailCountCheckContinuation), receiverGuid](QueryCallback& queryCallback, PreparedQueryResult mailCountResult) mutable + { + if (CharacterCacheEntry const* characterInfo = sCharacterCache->GetCharacterCacheByGuid(receiverGuid)) + queryCallback.WithPreparedCallback([continuation = std::move(continuation), characterInfo, mailCountResult](PreparedQueryResult bnetAccountResult) mutable + { + continuation(Player::TeamForRace(characterInfo->Race), mailCountResult ? (*mailCountResult)[0].GetUInt64() : UI64LIT(0), + characterInfo->Level, characterInfo->AccountId, bnetAccountResult ? (*bnetAccountResult)[0].GetUInt32() : 0); + }).SetNextQuery(Battlenet::AccountMgr::GetIdByGameAccountAsync(characterInfo->AccountId)); + })); + } } //called when mail is read |