diff options
Diffstat (limited to 'src')
23 files changed, 280 insertions, 159 deletions
diff --git a/src/common/Utilities/AsyncCallbackProcessor.h b/src/common/Utilities/AsyncCallbackProcessor.h new file mode 100644 index 00000000000..76781f49425 --- /dev/null +++ b/src/common/Utilities/AsyncCallbackProcessor.h @@ -0,0 +1,63 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef AsyncCallbackProcessor_h__ +#define AsyncCallbackProcessor_h__ + +#include "Define.h" +#include <algorithm> +#include <vector> + +//template <class T> +//concept AsyncCallback = requires(T t) { { t.InvokeIfReady() } -> std::convertible_to<bool> }; + +template<typename T> // requires AsyncCallback<T> +class AsyncCallbackProcessor +{ +public: + AsyncCallbackProcessor() = default; + ~AsyncCallbackProcessor() = default; + + T& AddCallback(T&& query) + { + _callbacks.emplace_back(std::move(query)); + return _callbacks.back(); + } + + void ProcessReadyCallbacks() + { + if (_callbacks.empty()) + return; + + std::vector<T> updateCallbacks{ std::move(_callbacks) }; + + updateCallbacks.erase(std::remove_if(updateCallbacks.begin(), updateCallbacks.end(), [](T& callback) + { + return callback.InvokeIfReady(); + }), updateCallbacks.end()); + + _callbacks.insert(_callbacks.end(), std::make_move_iterator(updateCallbacks.begin()), std::make_move_iterator(updateCallbacks.end())); + } + +private: + AsyncCallbackProcessor(AsyncCallbackProcessor const&) = delete; + AsyncCallbackProcessor& operator=(AsyncCallbackProcessor const&) = delete; + + std::vector<T> _callbacks; +}; + +#endif // AsyncCallbackProcessor_h__ diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 0cf0f83d346..60167564f35 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -175,7 +175,7 @@ void AuthSession::Start() LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); stmt->setString(0, ip_address); - _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1))); + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1))); } bool AuthSession::Update() @@ -183,7 +183,7 @@ bool AuthSession::Update() if (!AuthSocket::Update()) return false; - _queryProcessor.ProcessReadyQueries(); + _queryProcessor.ProcessReadyCallbacks(); return true; } @@ -308,7 +308,7 @@ bool AuthSession::HandleLogonChallenge() LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE); stmt->setString(0, login); - _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1))); + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1))); return true; } @@ -736,7 +736,7 @@ bool AuthSession::HandleReconnectChallenge() LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE); stmt->setString(0, login); - _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1))); + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1))); return true; } @@ -820,7 +820,7 @@ bool AuthSession::HandleRealmList() LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS); stmt->setUInt32(0, _accountInfo.Id); - _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1))); + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1))); _status = STATUS_WAITING_FOR_REALM_LIST; return true; } diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h index 51f422cb09b..86bbb4a6f9a 100644 --- a/src/server/authserver/Server/AuthSession.h +++ b/src/server/authserver/Server/AuthSession.h @@ -18,13 +18,13 @@ #ifndef __AUTHSESSION_H__ #define __AUTHSESSION_H__ +#include "AsyncCallbackProcessor.h" #include "BigNumber.h" #include "ByteBuffer.h" #include "Common.h" #include "Optional.h" #include "Socket.h" #include "QueryResult.h" -#include "QueryCallbackProcessor.h" #include <memory> #include <boost/asio/ip/tcp.hpp> diff --git a/src/server/database/Database/DatabaseEnvFwd.h b/src/server/database/Database/DatabaseEnvFwd.h index dd8cc464cae..930957fb898 100644 --- a/src/server/database/Database/DatabaseEnvFwd.h +++ b/src/server/database/Database/DatabaseEnvFwd.h @@ -24,9 +24,9 @@ class Field; class ResultSet; -typedef std::shared_ptr<ResultSet> QueryResult; -typedef std::future<QueryResult> QueryResultFuture; -typedef std::promise<QueryResult> QueryResultPromise; +using QueryResult = std::shared_ptr<ResultSet>; +using QueryResultFuture = std::future<QueryResult>; +using QueryResultPromise = std::promise<QueryResult>; class CharacterDatabaseConnection; class LoginDatabaseConnection; @@ -42,17 +42,27 @@ using LoginDatabasePreparedStatement = PreparedStatement<LoginDatabaseConnection using WorldDatabasePreparedStatement = PreparedStatement<WorldDatabaseConnection>; class PreparedResultSet; -typedef std::shared_ptr<PreparedResultSet> PreparedQueryResult; -typedef std::future<PreparedQueryResult> PreparedQueryResultFuture; -typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise; +using PreparedQueryResult = std::shared_ptr<PreparedResultSet>; +using PreparedQueryResultFuture = std::future<PreparedQueryResult>; +using PreparedQueryResultPromise = std::promise<PreparedQueryResult>; class QueryCallback; +template<typename T> +class AsyncCallbackProcessor; + +using QueryCallbackProcessor = AsyncCallbackProcessor<QueryCallback>; + class TransactionBase; +using TransactionFuture = std::future<bool>; +using TransactionPromise = std::promise<bool>; + template<typename T> class Transaction; +class TransactionCallback; + template<typename T> using SQLTransaction = std::shared_ptr<Transaction<T>>; @@ -61,8 +71,8 @@ using LoginDatabaseTransaction = SQLTransaction<LoginDatabaseConnection>; using WorldDatabaseTransaction = SQLTransaction<WorldDatabaseConnection>; class SQLQueryHolderBase; -typedef std::future<SQLQueryHolderBase*> QueryResultHolderFuture; -typedef std::promise<SQLQueryHolderBase*> QueryResultHolderPromise; +using QueryResultHolderFuture = std::future<SQLQueryHolderBase*>; +using QueryResultHolderPromise = std::promise<SQLQueryHolderBase*>; template<typename T> class SQLQueryHolder; diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp index 75bda12ffbf..94478f7e18f 100644 --- a/src/server/database/Database/DatabaseWorkerPool.cpp +++ b/src/server/database/Database/DatabaseWorkerPool.cpp @@ -261,6 +261,32 @@ void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction<T> transaction) } template <class T> +TransactionCallback DatabaseWorkerPool<T>::AsyncCommitTransaction(SQLTransaction<T> transaction) +{ +#ifdef TRINITY_DEBUG + //! Only analyze transaction weaknesses in Debug mode. + //! Ideally we catch the faults in Debug mode and then correct them, + //! so there's no need to waste these CPU cycles in Release mode. + switch (transaction->GetSize()) + { + case 0: + TC_LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing."); + return; + case 1: + TC_LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code."); + break; + default: + break; + } +#endif // TRINITY_DEBUG + + TransactionWithResultTask* task = new TransactionWithResultTask(transaction); + TransactionFuture result = task->GetFuture(); + Enqueue(task); + return TransactionCallback(std::move(result)); +} + +template <class T> void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction<T>& transaction) { T* connection = GetFreeConnection(); diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h index 8824dc38d85..f72817c3a8f 100644 --- a/src/server/database/Database/DatabaseWorkerPool.h +++ b/src/server/database/Database/DatabaseWorkerPool.h @@ -173,6 +173,10 @@ class DatabaseWorkerPool //! were appended to the transaction will be respected during execution. void CommitTransaction(SQLTransaction<T> transaction); + //! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations + //! were appended to the transaction will be respected during execution. + TransactionCallback AsyncCommitTransaction(SQLTransaction<T> transaction); + //! Directly executes a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations //! were appended to the transaction will be respected during execution. void DirectCommitTransaction(SQLTransaction<T>& transaction); diff --git a/src/server/database/Database/QueryCallback.cpp b/src/server/database/Database/QueryCallback.cpp index 65b52ff7627..02365a76fe7 100644 --- a/src/server/database/Database/QueryCallback.cpp +++ b/src/server/database/Database/QueryCallback.cpp @@ -175,7 +175,7 @@ void QueryCallback::SetNextQuery(QueryCallback&& next) MoveFrom(this, std::move(next)); } -QueryCallback::Status QueryCallback::InvokeIfReady() +bool QueryCallback::InvokeIfReady() { QueryCallbackData& callback = _callbacks.front(); auto checkStateAndReturnCompletion = [this]() @@ -185,15 +185,15 @@ QueryCallback::Status QueryCallback::InvokeIfReady() if (_callbacks.empty()) { ASSERT(!hasNext); - return Completed; + return true; } // abort chain if (!hasNext) - return Completed; + return true; ASSERT(_isPrepared == _callbacks.front()._isPrepared); - return NextStep; + return false; }; if (!_isPrepared) @@ -217,5 +217,5 @@ QueryCallback::Status QueryCallback::InvokeIfReady() } } - return NotReady; + return false; } diff --git a/src/server/database/Database/QueryCallback.h b/src/server/database/Database/QueryCallback.h index 1d7e835e999..6cc315964b7 100644 --- a/src/server/database/Database/QueryCallback.h +++ b/src/server/database/Database/QueryCallback.h @@ -44,14 +44,8 @@ public: // Moves std::future from next to this object void SetNextQuery(QueryCallback&& next); - enum Status - { - NotReady, - NextStep, - Completed - }; - - Status InvokeIfReady(); + // returns true when completed + bool InvokeIfReady(); private: QueryCallback(QueryCallback const& right) = delete; diff --git a/src/server/database/Database/QueryCallbackProcessor.cpp b/src/server/database/Database/QueryCallbackProcessor.cpp deleted file mode 100644 index 546a6d17c04..00000000000 --- a/src/server/database/Database/QueryCallbackProcessor.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "QueryCallbackProcessor.h" -#include "QueryCallback.h" -#include <algorithm> - -QueryCallbackProcessor::QueryCallbackProcessor() -{ -} - -QueryCallbackProcessor::~QueryCallbackProcessor() -{ -} - -void QueryCallbackProcessor::AddQuery(QueryCallback&& query) -{ - _callbacks.emplace_back(std::move(query)); -} - -void QueryCallbackProcessor::ProcessReadyQueries() -{ - if (_callbacks.empty()) - return; - - std::vector<QueryCallback> updateCallbacks{ std::move(_callbacks) }; - - updateCallbacks.erase(std::remove_if(updateCallbacks.begin(), updateCallbacks.end(), [](QueryCallback& callback) - { - return callback.InvokeIfReady() == QueryCallback::Completed; - }), updateCallbacks.end()); - - _callbacks.insert(_callbacks.end(), std::make_move_iterator(updateCallbacks.begin()), std::make_move_iterator(updateCallbacks.end())); -} diff --git a/src/server/database/Database/QueryCallbackProcessor.h b/src/server/database/Database/QueryCallbackProcessor.h deleted file mode 100644 index 85596e90092..00000000000 --- a/src/server/database/Database/QueryCallbackProcessor.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef QueryCallbackProcessor_h__ -#define QueryCallbackProcessor_h__ - -#include "Define.h" -#include <vector> - -class QueryCallback; - -class TC_DATABASE_API QueryCallbackProcessor -{ -public: - QueryCallbackProcessor(); - ~QueryCallbackProcessor(); - - void AddQuery(QueryCallback&& query); - void ProcessReadyQueries(); - -private: - QueryCallbackProcessor(QueryCallbackProcessor const&) = delete; - QueryCallbackProcessor& operator=(QueryCallbackProcessor const&) = delete; - - std::vector<QueryCallback> _callbacks; -}; - -#endif // QueryCallbackProcessor_h__ diff --git a/src/server/database/Database/Transaction.cpp b/src/server/database/Database/Transaction.cpp index be5c28bceb3..9b2178d2c4b 100644 --- a/src/server/database/Database/Transaction.cpp +++ b/src/server/database/Database/Transaction.cpp @@ -71,7 +71,7 @@ void TransactionBase::Cleanup() bool TransactionTask::Execute() { - int errorCode = m_conn->ExecuteTransaction(m_trans); + int errorCode = TryExecute(); if (!errorCode) return true; @@ -86,7 +86,7 @@ bool TransactionTask::Execute() for (uint32 loopDuration = 0, startMSTime = getMSTime(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime)) { - if (!m_conn->ExecuteTransaction(m_trans)) + if (!TryExecute()) return true; TC_LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: %u ms, Thread Id: %s", loopDuration, threadId.c_str()); @@ -96,7 +96,59 @@ bool TransactionTask::Execute() } // Clean up now. + CleanupOnFailure(); + + return false; +} + +int TransactionTask::TryExecute() +{ + return m_conn->ExecuteTransaction(m_trans); +} + +void TransactionTask::CleanupOnFailure() +{ m_trans->Cleanup(); +} + +bool TransactionWithResultTask::Execute() +{ + int errorCode = TryExecute(); + if (!errorCode) + { + m_result.set_value(true); + return true; + } + + if (errorCode == ER_LOCK_DEADLOCK) + { + // Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other + std::lock_guard<std::mutex> lock(_deadlockLock); + uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself + for (uint8 i = 0; i < loopBreaker; ++i) + { + if (!TryExecute()) + { + m_result.set_value(true); + return true; + } + } + } + + // Clean up now. + CleanupOnFailure(); + m_result.set_value(false); + + return false; +} + +bool TransactionCallback::InvokeIfReady() +{ + if (m_future.valid() && m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + m_callback(m_future.get()); + return true; + } return false; } diff --git a/src/server/database/Database/Transaction.h b/src/server/database/Database/Transaction.h index b11f94ce14e..205b06908fd 100644 --- a/src/server/database/Database/Transaction.h +++ b/src/server/database/Database/Transaction.h @@ -22,6 +22,7 @@ #include "DatabaseEnvFwd.h" #include "SQLOperation.h" #include "StringFormat.h" +#include <functional> #include <mutex> #include <vector> @@ -72,6 +73,7 @@ class TC_DATABASE_API TransactionTask : public SQLOperation { template <class T> friend class DatabaseWorkerPool; friend class DatabaseWorker; + friend class TransactionCallback; public: TransactionTask(std::shared_ptr<TransactionBase> trans) : m_trans(trans) { } @@ -79,9 +81,43 @@ class TC_DATABASE_API TransactionTask : public SQLOperation protected: bool Execute() override; + int TryExecute(); + void CleanupOnFailure(); std::shared_ptr<TransactionBase> m_trans; static std::mutex _deadlockLock; }; +class TC_DATABASE_API TransactionWithResultTask : public TransactionTask +{ +public: + TransactionWithResultTask(std::shared_ptr<TransactionBase> trans) : TransactionTask(trans) { } + + TransactionFuture GetFuture() { return m_result.get_future(); } + +protected: + bool Execute() override; + + TransactionPromise m_result; +}; + +class TC_DATABASE_API TransactionCallback +{ +public: + TransactionCallback(TransactionFuture&& future) : m_future(std::move(future)) { } + TransactionCallback(TransactionCallback&&) = default; + + TransactionCallback& operator=(TransactionCallback&&) = default; + + void AfterComplete(std::function<void(bool)> callback) & + { + m_callback = std::move(callback); + } + + bool InvokeIfReady(); + + TransactionFuture m_future; + std::function<void(bool)> m_callback; +}; + #endif diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 806ffb49c07..a9ef1c5d157 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -19319,6 +19319,15 @@ bool Player::_LoadHomeBind(PreparedQueryResult result) void Player::SaveToDB(bool create /*=false*/) { + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + SaveToDB(trans, create); + + CharacterDatabase.CommitTransaction(trans); +} + +void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create /* = false */) +{ // delay auto save at any saves (manual, in code, or autosave) m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE); @@ -19338,7 +19347,6 @@ void Player::SaveToDB(bool create /*=false*/) if (!create) sScriptMgr->OnPlayerSave(this); - CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); CharacterDatabasePreparedStatement* stmt = nullptr; uint8 index = 0; @@ -19630,8 +19638,6 @@ void Player::SaveToDB(bool create /*=false*/) if (m_session->isLogingOut() || !sWorld->getBoolConfig(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT)) _SaveStats(trans); - CharacterDatabase.CommitTransaction(trans); - // save pet (hunter pet level and experience and all type pets health/mana). if (Pet* pet = GetPet()) pet->SavePetToDB(PET_SAVE_AS_CURRENT); @@ -26196,7 +26202,7 @@ void Player::ActivateSpec(uint8 spec) stmt->setUInt8(1, m_activeSpec); WorldSession* mySess = GetSession(); - mySess->GetQueryProcessor().AddQuery(CharacterDatabase.AsyncQuery(stmt) + mySess->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(stmt) .WithPreparedCallback([mySess](PreparedQueryResult result) { // safe callback, we can't pass this pointer directly diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 97835fb8712..9fc1281158d 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1333,6 +1333,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> /*********************************************************/ void SaveToDB(bool create = false); + void SaveToDB(CharacterDatabaseTransaction trans, bool create = false); void SaveInventoryAndGoldToDB(CharacterDatabaseTransaction& trans); // fast save function for item/money cheating preventing void SaveGoldToDB(CharacterDatabaseTransaction& trans) const; diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 7744301eb98..81e107f2db3 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -265,7 +265,7 @@ void WorldSession::HandleCharEnumOpcode(WorldPacket& /*recvData*/) stmt->setUInt8(0, PET_SAVE_AS_CURRENT); stmt->setUInt32(1, GetAccountId()); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleCharEnum, this, std::placeholders::_1))); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleCharEnum, this, std::placeholders::_1))); } void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) @@ -408,7 +408,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); stmt->setString(0, createInfo->Name); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result) { if (result) @@ -563,29 +563,31 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) return; } - Player newChar(this); - newChar.GetMotionMaster()->Initialize(); - if (!newChar.Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(), createInfo.get())) - + std::shared_ptr<Player> newChar(new Player(this), [](Player* ptr) + { + ptr->CleanupsBeforeDelete(); + delete ptr; + }); + newChar->GetMotionMaster()->Initialize(); + if (!newChar->Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(), createInfo.get())) { // Player not create (race/class/etc problem?) - newChar.CleanupsBeforeDelete(); - SendCharCreate(CHAR_CREATE_ERROR); return; } if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2) - newChar.setCinematic(1); // not show intro + newChar->setCinematic(1); // not show intro - newChar.SetAtLoginFlag(AT_LOGIN_FIRST); // First login + newChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login + + CharacterDatabaseTransaction characterTransaction = CharacterDatabase.BeginTransaction(); + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); // Player created, save it now - newChar.SaveToDB(true); + newChar->SaveToDB(characterTransaction, true); createInfo->CharCount += 1; - LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); stmt->setUInt32(0, GetAccountId()); stmt->setUInt32(1, realm.Id.Realm); @@ -599,13 +601,19 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) LoginDatabase.CommitTransaction(trans); - SendCharCreate(CHAR_CREATE_SUCCESS); - - TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s) Create Character:[%s] %s", GetAccountId(), GetRemoteAddress().c_str(), createInfo->Name.c_str(), newChar.GetGUID().ToString().c_str()); - sScriptMgr->OnPlayerCreate(&newChar); - sCharacterCache->AddCharacterCacheEntry(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetNativeGender(), newChar.GetRace(), newChar.GetClass(), newChar.GetLevel()); + AddTransactionCallback(CharacterDatabase.AsyncCommitTransaction(characterTransaction)).AfterComplete([this, newChar = std::move(newChar)](bool success) + { + if (success) + { + TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Create Character: %s %s", GetAccountId(), GetRemoteAddress().c_str(), newChar->GetName().c_str(), newChar->GetGUID().ToString().c_str()); + sScriptMgr->OnPlayerCreate(newChar.get()); + sCharacterCache->AddCharacterCacheEntry(newChar->GetGUID(), GetAccountId(), newChar->GetName(), newChar->GetNativeGender(), newChar->GetRace(), newChar->GetClass(), newChar->GetLevel()); - newChar.CleanupsBeforeDelete(); + SendCharCreate(CHAR_CREATE_SUCCESS); + } + else + SendCharCreate(CHAR_CREATE_ERROR); + }); }; if (allowTwoSideAccounts && !skipCinematics && createInfo->Class != CLASS_DEATH_KNIGHT) @@ -1135,7 +1143,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) stmt->setUInt32(1, GetAccountId()); stmt->setString(2, renameInfo->Name); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) .WithPreparedCallback(std::bind(&WorldSession::HandleCharRenameCallBack, this, renameInfo, std::placeholders::_1))); } @@ -1376,7 +1384,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO); stmt->setUInt32(0, customizeInfo->Guid.GetCounter()); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) .WithPreparedCallback(std::bind(&WorldSession::HandleCharCustomizeCallback, this, customizeInfo, std::placeholders::_1))); } @@ -1632,7 +1640,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS); stmt->setUInt32(0, factionChangeInfo->Guid.GetCounter()); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) .WithPreparedCallback(std::bind(&WorldSession::HandleCharFactionOrRaceChangeCallback, this, factionChangeInfo, std::placeholders::_1))); } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 1078de17111..f25efb18b70 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -308,7 +308,7 @@ void WorldSession::SendStablePet(ObjectGuid guid) stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT); stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::SendStablePetCallback, this, guid, std::placeholders::_1))); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::SendStablePetCallback, this, guid, std::placeholders::_1))); } void WorldSession::SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result) @@ -409,7 +409,7 @@ void WorldSession::HandleStablePet(WorldPacket& recvData) stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT); stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1))); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1))); } void WorldSession::HandleStablePetCallback(PreparedQueryResult result) @@ -471,7 +471,7 @@ void WorldSession::HandleUnstablePet(WorldPacket& recvData) stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT); stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleUnstablePetCallback, this, petnumber, std::placeholders::_1))); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleUnstablePetCallback, this, petnumber, std::placeholders::_1))); } void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result) @@ -597,7 +597,7 @@ void WorldSession::HandleStableSwapPet(WorldPacket& recvData) stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt32(1, petId); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableSwapPetCallback, this, petId, std::placeholders::_1))); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableSwapPetCallback, this, petId, std::placeholders::_1))); } void WorldSession::HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result) diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index fc62887894a..740f4144d3b 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -241,7 +241,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM); stmt->setUInt32(0, item->GetGUID().GetCounter()); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) .WithPreparedCallback(std::bind(&WorldSession::HandleOpenWrappedItemCallback, this, item->GetPos(), item->GetGUID(), std::placeholders::_1))); } else diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 55282e1a7bb..256e818de5f 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1193,7 +1193,8 @@ void WorldSession::SetPlayer(Player* player) void WorldSession::ProcessQueryCallbacks() { - _queryProcessor.ProcessReadyQueries(); + _queryProcessor.ProcessReadyCallbacks(); + _transactionCallbacks.ProcessReadyCallbacks(); if (_realmAccountLoginCallback.valid() && _realmAccountLoginCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) InitializeSessionCallback(static_cast<CharacterDatabaseQueryHolder*>(_realmAccountLoginCallback.get())); @@ -1203,6 +1204,11 @@ void WorldSession::ProcessQueryCallbacks() HandlePlayerLogin(reinterpret_cast<LoginQueryHolder*>(_charLoginCallback.get())); } +TransactionCallback& WorldSession::AddTransactionCallback(TransactionCallback&& callback) +{ + return _transactionCallbacks.AddCallback(std::move(callback)); +} + void WorldSession::InitWarden(BigNumber* k, std::string const& os) { if (os == "Win") diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 57794ac81bf..b9df671a502 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -23,11 +23,11 @@ #define __WORLDSESSION_H #include "Common.h" +#include "AsyncCallbackProcessor.h" #include "DatabaseEnvFwd.h" #include "LockedQueue.h" #include "ObjectGuid.h" #include "Packet.h" -#include "QueryCallbackProcessor.h" #include "SharedDefines.h" #include <string> #include <map> @@ -1091,6 +1091,7 @@ class TC_GAME_API WorldSession public: QueryCallbackProcessor& GetQueryProcessor() { return _queryProcessor; } + TransactionCallback& AddTransactionCallback(TransactionCallback&& callback); private: void ProcessQueryCallbacks(); @@ -1099,6 +1100,7 @@ class TC_GAME_API WorldSession QueryResultHolderFuture _charLoginCallback; QueryCallbackProcessor _queryProcessor; + AsyncCallbackProcessor<TransactionCallback> _transactionCallbacks; friend class World; protected: diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 0e65704076d..ff85f71c769 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -39,13 +39,15 @@ WorldSocket::WorldSocket(tcp::socket&& socket) _headerBuffer.Resize(sizeof(ClientPktHeader)); } +WorldSocket::~WorldSocket() = default; + void WorldSocket::Start() { std::string ip_address = GetRemoteIpAddress().to_string(); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); stmt->setString(0, ip_address); - _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1))); + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1))); } void WorldSocket::CheckIpCallback(PreparedQueryResult result) @@ -115,7 +117,7 @@ bool WorldSocket::Update() if (!BaseSocket::Update()) return false; - _queryProcessor.ProcessReadyQueries(); + _queryProcessor.ProcessReadyCallbacks(); return true; } @@ -439,7 +441,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) stmt->setInt32(0, int32(realm.Id.Realm)); stmt->setString(1, authSession->Account); - _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1))); + _queryProcessor.AddCallback(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1))); } void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSession, PreparedQueryResult result) @@ -600,7 +602,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes if (wardenActive) _worldSession->InitWarden(&account.SessionKey, account.OS); - _queryProcessor.AddQuery(_worldSession->LoadPermissionsAsync().WithPreparedCallback(std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1))); + _queryProcessor.AddCallback(_worldSession->LoadPermissionsAsync().WithPreparedCallback(std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1))); AsyncRead(); } diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 6f6b0848423..00d2ec59651 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -71,6 +71,7 @@ class TC_GAME_API WorldSocket : public Socket<WorldSocket> public: WorldSocket(tcp::socket&& socket); + ~WorldSocket(); WorldSocket(WorldSocket const& right) = delete; WorldSocket& operator=(WorldSocket const& right) = delete; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 93f1b919dab..34864e46779 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -3120,7 +3120,7 @@ void World::UpdateRealmCharCount(uint32 accountId) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_COUNT); stmt->setUInt32(0, accountId); - _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&World::_UpdateRealmCharCount, this, std::placeholders::_1))); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&World::_UpdateRealmCharCount, this, std::placeholders::_1))); } void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount) @@ -3492,7 +3492,7 @@ uint64 World::getWorldState(uint32 index) const void World::ProcessQueryCallbacks() { - _queryProcessor.ProcessReadyQueries(); + _queryProcessor.ProcessReadyCallbacks(); } void World::ReloadRBAC() diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 24338a7eee3..98c3df02ba7 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -23,10 +23,10 @@ #define __WORLD_H #include "Common.h" +#include "AsyncCallbackProcessor.h" #include "DatabaseEnvFwd.h" #include "LockedQueue.h" #include "ObjectGuid.h" -#include "QueryCallbackProcessor.h" #include "SharedDefines.h" #include "Timer.h" |