diff options
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"  | 
