aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiacomo Pozzoni <giacomopoz@gmail.com>2020-05-30 15:43:54 +0000
committerGitHub <noreply@github.com>2020-05-30 17:43:54 +0200
commit141ada19b00fb9d0dd1a260d86d97bbb709a0f5e (patch)
tree03c8559a245f404d7d84655fe5c5066ec22e2ee7
parent00bc6e7d54e924dabaf4636a4b8084f0db067194 (diff)
Cherry pick some db commits (#24713)
* Core/DBLayer: Implement async transaction completion callbacks (cherry picked from commit 0f0ca3a9194d76afa0227943e86469ad8368c5e2) # Conflicts: # src/server/bnetserver/REST/LoginRESTService.cpp # src/server/bnetserver/Server/Session.cpp # src/server/bnetserver/Server/Session.h # src/server/database/Database/Transaction.cpp # src/server/game/Handlers/CharacterHandler.cpp # src/server/game/Handlers/SpellHandler.cpp # src/server/game/Server/WorldSession.cpp # src/server/game/Server/WorldSocket.cpp # src/server/game/Server/WorldSocket.h * Core/Misc: Fixed char enum packet sometimes not showing newly created character when client latency is too low (cherry picked from commit fc9d2e728e5c21bd2a1645a29be60d0e280ad940) # Conflicts: # src/server/game/Handlers/CharacterHandler.cpp * Fix no-pch build * Core/Player: Wait for correct transaction on character creation (cherry picked from commit 01c68b2aa2ed25ea9b4c007f238a0e958f6525b1) # Conflicts: # src/server/game/Entities/Player/Player.cpp * Fix warning * Remove unused login transaction Co-authored-by: Shauren <shauren.trinity@gmail.com>
-rw-r--r--src/common/Utilities/AsyncCallbackProcessor.h63
-rw-r--r--src/server/authserver/Server/AuthSession.cpp10
-rw-r--r--src/server/authserver/Server/AuthSession.h2
-rw-r--r--src/server/database/Database/DatabaseEnvFwd.h26
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.cpp26
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.h4
-rw-r--r--src/server/database/Database/QueryCallback.cpp10
-rw-r--r--src/server/database/Database/QueryCallback.h10
-rw-r--r--src/server/database/Database/QueryCallbackProcessor.cpp48
-rw-r--r--src/server/database/Database/QueryCallbackProcessor.h42
-rw-r--r--src/server/database/Database/Transaction.cpp56
-rw-r--r--src/server/database/Database/Transaction.h36
-rw-r--r--src/server/game/Entities/Player/Player.cpp14
-rw-r--r--src/server/game/Entities/Player/Player.h1
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp52
-rw-r--r--src/server/game/Handlers/NPCHandler.cpp8
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp2
-rw-r--r--src/server/game/Server/WorldSession.cpp8
-rw-r--r--src/server/game/Server/WorldSession.h4
-rw-r--r--src/server/game/Server/WorldSocket.cpp10
-rw-r--r--src/server/game/Server/WorldSocket.h1
-rw-r--r--src/server/game/World/World.cpp4
-rw-r--r--src/server/game/World/World.h2
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"