diff --git a/conf/dist/config.cmake b/conf/dist/config.cmake index d11b5a1db..22f349f85 100644 --- a/conf/dist/config.cmake +++ b/conf/dist/config.cmake @@ -13,6 +13,7 @@ option(ENABLE_EXTRAS "Set to 0 to disable extra features optimizing perfor option(ENABLE_VMAP_CHECKS "Enable Checks relative to DisableMgr system on vmap" 1) option(ENABLE_EXTRA_LOGS "Enable extra log functions that can be CPU intensive" 0) option(WITH_DYNAMIC_LINKING "Enable dynamic library linking." 0) +option(WITH_STRICT_DATABASE_TYPE_CHECKS "Enable strict checking of database field value accessors" 0) IsDynamicLinkingRequired(WITH_DYNAMIC_LINKING_FORCED) diff --git a/src/cmake/showoptions.cmake b/src/cmake/showoptions.cmake index 3ee69ae69..c0955eefc 100644 --- a/src/cmake/showoptions.cmake +++ b/src/cmake/showoptions.cmake @@ -144,6 +144,15 @@ else() message("* Show source tree : No (For UNIX default)") endif() +if(WITH_STRICT_DATABASE_TYPE_CHECKS) + message("") + message(" *** WITH_STRICT_DATABASE_TYPE_CHECKS - WARNING!") + message(" *** Validates uses of database Get***() functions from Field class") + message(" *** invalid calls will result in returning value 0") + message(" *** NOT COMPATIBLE WITH MARIADB!") + add_definitions(-DACORE_STRICT_DATABASE_TYPE_CHECKS) +endif() + if(BUILD_SHARED_LIBS) message("") message(" *** WITH_DYNAMIC_LINKING - INFO!") diff --git a/src/common/Threading/ProducerConsumerQueue.h b/src/common/Threading/ProducerConsumerQueue.h new file mode 100644 index 000000000..4ad29f174 --- /dev/null +++ b/src/common/Threading/ProducerConsumerQueue.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef _PCQ_H +#define _PCQ_H + +#include +#include +#include +#include +#include + +template +class ProducerConsumerQueue +{ +private: + std::mutex _queueLock; + std::queue _queue; + std::condition_variable _condition; + std::atomic _shutdown; + +public: + + ProducerConsumerQueue() : _shutdown(false) { } + + void Push(const T& value) + { + std::lock_guard lock(_queueLock); + _queue.push(std::move(value)); + + _condition.notify_one(); + } + + bool Empty() + { + std::lock_guard lock(_queueLock); + + return _queue.empty(); + } + + bool Pop(T& value) + { + std::lock_guard lock(_queueLock); + + if (_queue.empty() || _shutdown) + return false; + + value = _queue.front(); + + _queue.pop(); + + return true; + } + + void WaitAndPop(T& value) + { + std::unique_lock lock(_queueLock); + + // we could be using .wait(lock, predicate) overload here but it is broken + // https://connect.microsoft.com/VisualStudio/feedback/details/1098841 + while (_queue.empty() && !_shutdown) + _condition.wait(lock); + + if (_queue.empty() || _shutdown) + return; + + value = _queue.front(); + + _queue.pop(); + } + + void Cancel() + { + std::unique_lock lock(_queueLock); + + while (!_queue.empty()) + { + T& value = _queue.front(); + + DeleteQueuedObject(value); + + _queue.pop(); + } + + _shutdown = true; + + _condition.notify_all(); + } + +private: + template + typename std::enable_if::value>::type DeleteQueuedObject(E& obj) { delete obj; } + + template + typename std::enable_if::value>::type DeleteQueuedObject(E const& /*packet*/) { } +}; + +#endif diff --git a/src/common/Utilities/AsyncCallbackProcessor.h b/src/common/Utilities/AsyncCallbackProcessor.h new file mode 100644 index 000000000..c75f25b9f --- /dev/null +++ b/src/common/Utilities/AsyncCallbackProcessor.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef AsyncCallbackProcessor_h__ +#define AsyncCallbackProcessor_h__ + +#include "Define.h" +#include +#include + +//template +//concept AsyncCallback = requires(T t) { { t.InvokeIfReady() } -> std::convertible_to }; + +template // requires AsyncCallback +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 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 _callbacks; +}; + +#endif // AsyncCallbackProcessor_h__ diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index da4bd599f..0447337c5 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -23,6 +23,8 @@ #include "Log.h" #include "RealmAcceptor.h" #include "RealmList.h" +#include "DatabaseLoader.h" +#include "MySQLThreading.h" #include "SecretMgr.h" #include "SharedDefines.h" #include "SignalHandler.h" @@ -211,7 +213,7 @@ bool StartDB() // Load databases // NOTE: While authserver is singlethreaded you should keep synch_threads == 1. // Increasing it is just silly since only 1 will be used ever. - DatabaseLoader loader; + DatabaseLoader loader("server.authserver"); loader .AddDatabase(LoginDatabase, "Login"); diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp index 53e9e6052..655f71cc8 100644 --- a/src/server/authserver/Server/AuthSocket.cpp +++ b/src/server/authserver/Server/AuthSocket.cpp @@ -20,6 +20,7 @@ #include "SecretMgr.h" #include "TOTP.h" #include "Threading.h" +#include "Util.h" #include #include #include @@ -577,7 +578,7 @@ bool AuthSocket::_HandleLogonProof() // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account // No SQL injection (escaped user name) and IP address as received by socket - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); stmt->setBinary(0, _sessionKey); stmt->setString(1, socket().getRemoteAddress().c_str()); stmt->setUInt32(2, GetLocaleByName(_localizationName)); @@ -625,7 +626,7 @@ bool AuthSocket::_HandleLogonProof() // We can not include the failed account login hook. However, this is a workaround to still log this. if (sConfigMgr->GetOption("WrongPass.Logging", false)) { - PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING); + LoginDatabasePreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING); logstmt->setString(0, _accountInfo.Login); logstmt->setString(1, socket().getRemoteAddress()); logstmt->setString(2, "Logged on failed AccountLogin due wrong password"); @@ -636,7 +637,7 @@ bool AuthSocket::_HandleLogonProof() if (MaxWrongPassCount > 0) { //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); stmt->setString(0, _accountInfo.Login); LoginDatabase.Execute(stmt); @@ -720,7 +721,6 @@ bool AuthSocket::_HandleReconnectChallenge() auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE); stmt->setString(0, login); - PreparedQueryResult result = LoginDatabase.Query(stmt); // Stop if the account is not found @@ -835,7 +835,7 @@ bool AuthSocket::_HandleRealmList() // Get the user id (else close the connection) // No SQL injection (prepared statement) - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME); stmt->setString(0, _accountInfo.Login); PreparedQueryResult result = LoginDatabase.Query(stmt); diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt index 1e71108e8..ae30ee613 100644 --- a/src/server/database/CMakeLists.txt +++ b/src/server/database/CMakeLists.txt @@ -6,6 +6,7 @@ CollectSourceFiles( ${CMAKE_CURRENT_SOURCE_DIR} PRIVATE_SOURCES # Exclude + ${CMAKE_CURRENT_SOURCE_DIR}/Updater ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders) if(USE_COREPCH) @@ -40,12 +41,11 @@ target_include_directories(database ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(database - # PRIVATE + PRIVATE # acore-core-interface - # mysql + mysql PUBLIC - common - mysql) + common) set_target_properties(database PROPERTIES diff --git a/src/server/database/Database/AdhocStatement.cpp b/src/server/database/Database/AdhocStatement.cpp index 50f8e3ca6..c8cbb6b03 100644 --- a/src/server/database/Database/AdhocStatement.cpp +++ b/src/server/database/Database/AdhocStatement.cpp @@ -1,29 +1,30 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "AdhocStatement.h" +#include "Errors.h" #include "MySQLConnection.h" +#include "QueryResult.h" +#include +#include /*! Basic, ad-hoc queries. */ -BasicStatementTask::BasicStatementTask(const char* sql) : - m_has_result(false) -{ - m_sql = strdup(sql); -} - -BasicStatementTask::BasicStatementTask(const char* sql, QueryResultFuture result) : - m_has_result(true), - m_result(result) +BasicStatementTask::BasicStatementTask(char const* sql, bool async) : +m_result(nullptr) { m_sql = strdup(sql); + m_has_result = async; // If the operation is async, then there's a result + if (async) + m_result = new QueryResultPromise(); } BasicStatementTask::~BasicStatementTask() { free((void*)m_sql); + if (m_has_result && m_result != nullptr) + delete m_result; } bool BasicStatementTask::Execute() @@ -31,14 +32,14 @@ bool BasicStatementTask::Execute() if (m_has_result) { ResultSet* result = m_conn->Query(m_sql); - if (!result || !result->GetRowCount()) + if (!result || !result->GetRowCount() || !result->NextRow()) { delete result; - m_result.set(QueryResult(nullptr)); + m_result->set_value(QueryResult(nullptr)); return false; } - result->NextRow(); - m_result.set(QueryResult(result)); + + m_result->set_value(QueryResult(result)); return true; } diff --git a/src/server/database/Database/AdhocStatement.h b/src/server/database/Database/AdhocStatement.h index 61b60af98..0662c428f 100644 --- a/src/server/database/Database/AdhocStatement.h +++ b/src/server/database/Database/AdhocStatement.h @@ -1,30 +1,29 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _ADHOCSTATEMENT_H #define _ADHOCSTATEMENT_H -#include +#include "DatabaseEnvFwd.h" +#include "Define.h" #include "SQLOperation.h" -typedef ACE_Future QueryResultFuture; /*! Raw, ad-hoc query. */ -class BasicStatementTask : public SQLOperation +class AC_DATABASE_API BasicStatementTask : public SQLOperation { public: - BasicStatementTask(const char* sql); - BasicStatementTask(const char* sql, QueryResultFuture result); - ~BasicStatementTask() override; + BasicStatementTask(char const* sql, bool async = false); + ~BasicStatementTask(); bool Execute() override; + QueryResultFuture GetFuture() const { return m_result->get_future(); } private: - const char* m_sql; //- Raw query to be executed + char const* m_sql; //- Raw query to be executed bool m_has_result; - QueryResultFuture m_result; + QueryResultPromise* m_result; }; #endif diff --git a/src/server/database/Database/DatabaseEnv.cpp b/src/server/database/Database/DatabaseEnv.cpp index 3fd2f2a65..2ded9baa7 100644 --- a/src/server/database/Database/DatabaseEnv.cpp +++ b/src/server/database/Database/DatabaseEnv.cpp @@ -1,10 +1,10 @@ /* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. * Copyright (C) 2021+ WarheadCore */ #include "DatabaseEnv.h" -WorldDatabaseWorkerPool WorldDatabase; -CharacterDatabaseWorkerPool CharacterDatabase; -LoginDatabaseWorkerPool LoginDatabase; +DatabaseWorkerPool WorldDatabase; +DatabaseWorkerPool CharacterDatabase; +DatabaseWorkerPool LoginDatabase; diff --git a/src/server/database/Database/DatabaseEnv.h b/src/server/database/Database/DatabaseEnv.h index aaa1088c9..025e3eb3f 100644 --- a/src/server/database/Database/DatabaseEnv.h +++ b/src/server/database/Database/DatabaseEnv.h @@ -1,38 +1,29 @@ /* - * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef DATABASEENV_H #define DATABASEENV_H -#include "Common.h" -#include "Errors.h" -#include "Log.h" +#include "DatabaseWorkerPool.h" +#include "Define.h" + +#include "Implementation/CharacterDatabase.h" +#include "Implementation/LoginDatabase.h" +#include "Implementation/WorldDatabase.h" #include "Field.h" +#include "PreparedStatement.h" +#include "QueryCallback.h" #include "QueryResult.h" - -#include "MySQLThreading.h" #include "Transaction.h" -#define _LIKE_ "LIKE" -#define _TABLE_SIM_ "`" -#define _CONCAT3_(A, B, C) "CONCAT( " A ", " B ", " C " )" -#define _OFFSET_ "LIMIT %d, 1" - -#include "LoginDatabase.h" -#include "CharacterDatabase.h" -#include "WorldDatabase.h" - /// Accessor to the world database -extern WorldDatabaseWorkerPool WorldDatabase; - +AC_DATABASE_API extern DatabaseWorkerPool WorldDatabase; /// Accessor to the character database -extern CharacterDatabaseWorkerPool CharacterDatabase; - +AC_DATABASE_API extern DatabaseWorkerPool CharacterDatabase; /// Accessor to the realm/login database -extern LoginDatabaseWorkerPool LoginDatabase; +AC_DATABASE_API extern DatabaseWorkerPool LoginDatabase; #endif diff --git a/src/server/database/Database/DatabaseEnvFwd.h b/src/server/database/Database/DatabaseEnvFwd.h new file mode 100644 index 000000000..a17d01afc --- /dev/null +++ b/src/server/database/Database/DatabaseEnvFwd.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef DatabaseEnvFwd_h__ +#define DatabaseEnvFwd_h__ + +#include +#include + +struct QueryResultFieldMetadata; +class Field; + +class ResultSet; +using QueryResult = std::shared_ptr; +using QueryResultFuture = std::future; +using QueryResultPromise = std::promise; + +class CharacterDatabaseConnection; +class LoginDatabaseConnection; +class WorldDatabaseConnection; + +class PreparedStatementBase; + +template +class PreparedStatement; + +using CharacterDatabasePreparedStatement = PreparedStatement; +using LoginDatabasePreparedStatement = PreparedStatement; +using WorldDatabasePreparedStatement = PreparedStatement; + +class PreparedResultSet; +using PreparedQueryResult = std::shared_ptr; +using PreparedQueryResultFuture = std::future; +using PreparedQueryResultPromise = std::promise; + +class QueryCallback; + +template +class AsyncCallbackProcessor; + +using QueryCallbackProcessor = AsyncCallbackProcessor; + +class TransactionBase; + +using TransactionFuture = std::future; +using TransactionPromise = std::promise; + +template +class Transaction; + +class TransactionCallback; + +template +using SQLTransaction = std::shared_ptr>; + +using CharacterDatabaseTransaction = SQLTransaction; +using LoginDatabaseTransaction = SQLTransaction; +using WorldDatabaseTransaction = SQLTransaction; + +class SQLQueryHolderBase; +using QueryResultHolderFuture = std::future; +using QueryResultHolderPromise = std::promise; + +template +class SQLQueryHolder; + +using CharacterDatabaseQueryHolder = SQLQueryHolder; +using LoginDatabaseQueryHolder = SQLQueryHolder; +using WorldDatabaseQueryHolder = SQLQueryHolder; + +class SQLQueryHolderCallback; + +// mysql +struct MySQLHandle; +struct MySQLResult; +struct MySQLField; +struct MySQLBind; +struct MySQLStmt; + +#endif // DatabaseEnvFwd_h__ diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp index 3ea9456eb..b18cc1401 100644 --- a/src/server/database/Database/DatabaseLoader.cpp +++ b/src/server/database/Database/DatabaseLoader.cpp @@ -5,12 +5,18 @@ #include "DatabaseLoader.h" #include "Config.h" +// #include "DBUpdater.h" not implement #include "DatabaseEnv.h" +#include "Duration.h" #include "Log.h" #include "Duration.h" #include +#include #include +DatabaseLoader::DatabaseLoader(std::string const& logger) + : _logger(logger) { } + template DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool& pool, std::string const& name) { @@ -19,14 +25,15 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool& pool, std::st std::string const dbString = sConfigMgr->GetOption(name + "DatabaseInfo", ""); if (dbString.empty()) { - LOG_INFO("sql.driver", "Database %s not specified in configuration file!", name.c_str()); + LOG_ERROR(_logger, "Database %s not specified in configuration file!", name.c_str()); return false; } uint8 const asyncThreads = sConfigMgr->GetOption(name + "Database.WorkerThreads", 1); if (asyncThreads < 1 || asyncThreads > 32) { - LOG_INFO("sql.driver", "%s database: invalid number of worker threads specified. Please pick a value between 1 and 32.", name.c_str()); + LOG_ERROR(_logger, "%s database: invalid number of worker threads specified. " + "Please pick a value between 1 and 32.", name.c_str()); return false; } @@ -39,36 +46,36 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool& pool, std::st // Try reconnect if (error == CR_CONNECTION_ERROR) { - uint8 const ATTEMPTS = sConfigMgr->GetOption("Database.Reconnect.Attempts", 20); - Seconds RECONNECT_SECONDS = Seconds(sConfigMgr->GetOption("Database.Reconnect.Seconds", 15)); - uint8 count = 0; + uint8 const attempts = sConfigMgr->GetOption("Database.Reconnect.Attempts", 20); + Seconds reconnectSeconds = Seconds(sConfigMgr->GetOption("Database.Reconnect.Seconds", 15)); + uint8 reconnectCount = 0; - while (count < ATTEMPTS) + while (reconnectCount < attempts) { - LOG_INFO("sql.driver", "> Retrying after %u seconds", static_cast(RECONNECT_SECONDS.count())); - std::this_thread::sleep_for(RECONNECT_SECONDS); + LOG_INFO(_logger, "> Retrying after %u seconds", static_cast(reconnectSeconds.count())); + std::this_thread::sleep_for(reconnectSeconds); error = pool.Open(); if (error == CR_CONNECTION_ERROR) { - count++; + reconnectCount++; } else { break; } - } } // If the error wasn't handled quit if (error) { - LOG_ERROR("sql.driver", "DatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile for specific errors", name.c_str()); + LOG_ERROR(_logger, "DatabasePool %s NOT opened. There were errors opening the MySQL connections. " + "Check your log file for specific errors", name.c_str()); + return false; } } - // Add the close operation _close.push([&pool] { @@ -78,11 +85,11 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool& pool, std::st return true; }); - _prepare.push([name, &pool]() -> bool + _prepare.push([this, name, &pool]() -> bool { if (!pool.PrepareStatements()) { - LOG_ERROR("sql.driver", "Could not prepare statements of the %s database, see log for details.", name.c_str()); + LOG_ERROR(_logger, "Could not prepare statements of the %s database, see log for details.", name.c_str()); return false; } @@ -129,6 +136,9 @@ bool DatabaseLoader::Process(std::queue& queue) return true; } -template DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool&, std::string const&); -template DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool&, std::string const&); -template DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool&, std::string const&); +template AC_DATABASE_API +DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool&, std::string const&); +template AC_DATABASE_API +DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool&, std::string const&); +template AC_DATABASE_API +DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool&, std::string const&); diff --git a/src/server/database/Database/DatabaseLoader.h b/src/server/database/Database/DatabaseLoader.h index df0dcc678..0e845e75a 100644 --- a/src/server/database/Database/DatabaseLoader.h +++ b/src/server/database/Database/DatabaseLoader.h @@ -17,9 +17,11 @@ class DatabaseWorkerPool; // A helper class to initiate all database worker pools, // handles updating, delays preparing of statements and cleans up on failure. -class DatabaseLoader +class AC_DATABASE_API DatabaseLoader { public: + DatabaseLoader(std::string const& logger); + // Register a database to the loader (lazy implemented) template DatabaseLoader& AddDatabase(DatabaseWorkerPool& pool, std::string const& name); @@ -49,6 +51,8 @@ private: // Returns false when there was an error. bool Process(std::queue& queue); + std::string const _logger; + std::queue _open, _prepare; std::stack _close; }; diff --git a/src/server/database/Database/DatabaseWorker.cpp b/src/server/database/Database/DatabaseWorker.cpp index 399872202..e1d292662 100644 --- a/src/server/database/Database/DatabaseWorker.cpp +++ b/src/server/database/Database/DatabaseWorker.cpp @@ -1,39 +1,46 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "DatabaseWorker.h" +#include "ProducerConsumerQueue.h" #include "SQLOperation.h" -#include "MySQLConnection.h" -#include "MySQLThreading.h" -DatabaseWorker::DatabaseWorker(ACE_Activation_Queue* new_queue, MySQLConnection* con) : - m_queue(new_queue), - m_conn(con) +DatabaseWorker::DatabaseWorker(ProducerConsumerQueue* newQueue, MySQLConnection* connection) { - /// Assign thread to task - activate(); + _connection = connection; + _queue = newQueue; + _cancelationToken = false; + _workerThread = std::thread(&DatabaseWorker::WorkerThread, this); } -int DatabaseWorker::svc() +DatabaseWorker::~DatabaseWorker() { - if (!m_queue) - return -1; + _cancelationToken = true; - SQLOperation* request = nullptr; - while (1) + _queue->Cancel(); + + _workerThread.join(); +} + +void DatabaseWorker::WorkerThread() +{ + if (!_queue) + return; + + for (;;) { - request = (SQLOperation*)(m_queue->dequeue()); - if (!request) - break; + SQLOperation* operation = nullptr; - request->SetConnection(m_conn); - request->call(); + _queue->WaitAndPop(operation); - delete request; + if (_cancelationToken || !operation) + return; + + operation->SetConnection(_connection); + operation->call(); + + delete operation; } - - return 0; } diff --git a/src/server/database/Database/DatabaseWorker.h b/src/server/database/Database/DatabaseWorker.h index 9f15f13dc..158a78fa2 100644 --- a/src/server/database/Database/DatabaseWorker.h +++ b/src/server/database/Database/DatabaseWorker.h @@ -1,30 +1,38 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _WORKERTHREAD_H #define _WORKERTHREAD_H -#include -#include +#include "Define.h" +#include +#include + +template +class ProducerConsumerQueue; class MySQLConnection; +class SQLOperation; -class DatabaseWorker : protected ACE_Task_Base +class AC_DATABASE_API DatabaseWorker { public: - DatabaseWorker(ACE_Activation_Queue* new_queue, MySQLConnection* con); - - ///- Inherited from ACE_Task_Base - int svc() override; - int wait() override { return ACE_Task_Base::wait(); } + DatabaseWorker(ProducerConsumerQueue* newQueue, MySQLConnection* connection); + ~DatabaseWorker(); private: - DatabaseWorker() : ACE_Task_Base() { } - ACE_Activation_Queue* m_queue; - MySQLConnection* m_conn; + ProducerConsumerQueue* _queue; + MySQLConnection* _connection; + + void WorkerThread(); + std::thread _workerThread; + + std::atomic _cancelationToken; + + DatabaseWorker(DatabaseWorker const& right) = delete; + DatabaseWorker& operator=(DatabaseWorker const& right) = delete; }; #endif diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp index 734e47636..4341b51b0 100644 --- a/src/server/database/Database/DatabaseWorkerPool.cpp +++ b/src/server/database/Database/DatabaseWorkerPool.cpp @@ -1,33 +1,65 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "DatabaseWorkerPool.h" -#include "DatabaseEnv.h" - +#include "AdhocStatement.h" +#include "Common.h" +#include "Errors.h" +#include "Implementation/CharacterDatabase.h" +#include "Implementation/LoginDatabase.h" +#include "Implementation/WorldDatabase.h" +#include "Log.h" +#include "MySQLPreparedStatement.h" +#include "MySQLWorkaround.h" +#include "PreparedStatement.h" +#include "ProducerConsumerQueue.h" +#include "QueryCallback.h" +#include "QueryHolder.h" +#include "QueryResult.h" +#include "SQLOperation.h" +#include "Transaction.h" #include +#ifdef ACORE_DEBUG +#include +#include +#endif + #define MIN_MYSQL_SERVER_VERSION 50700u #define MIN_MYSQL_CLIENT_VERSION 50700u -template DatabaseWorkerPool::DatabaseWorkerPool() : - _mqueue(new ACE_Message_Queue(2 * 1024 * 1024, 2 * 1024 * 1024)), - _queue(new ACE_Activation_Queue(_mqueue)), - _async_threads(0), - _synch_threads(0) +class PingOperation : public SQLOperation { - memset(_connectionCount, 0, sizeof(_connectionCount)); - _connections.resize(IDX_SIZE); + //! Operation for idle delaythreads + bool Execute() override + { + m_conn->Ping(); + return true; + } +}; +template +DatabaseWorkerPool::DatabaseWorkerPool() + : _queue(new ProducerConsumerQueue()), + _async_threads(0), _synch_threads(0) +{ WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe."); WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "AzerothCore does not support MySQL versions below 5.7"); + WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s id %lu) does not match the version id used to compile AzerothCore (id %u)", + mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID); +} + +template +DatabaseWorkerPool::~DatabaseWorkerPool() +{ + _queue->Cancel(); } template void DatabaseWorkerPool::SetConnectionInfo(std::string const& infoString, - uint8 const asyncThreads, uint8 const synchThreads) + uint8 const asyncThreads, uint8 const synchThreads) { _connectionInfo = std::make_unique(infoString); @@ -40,22 +72,22 @@ uint32 DatabaseWorkerPool::Open() { WPFatal(_connectionInfo.get(), "Connection info was not set!"); - LOG_INFO("sql.driver", "Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.", + LOG_INFO("sql.driver", "Opening DatabasePool '%s'. " + "Asynchronous connections: %u, synchronous connections: %u.", GetDatabaseName(), _async_threads, _synch_threads); uint32 error = OpenConnections(IDX_ASYNC, _async_threads); if (error) - { return error; - } error = OpenConnections(IDX_SYNCH, _synch_threads); if (!error) { - LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.", - GetDatabaseName(), (_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC])); + LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. " SZFMTD + " total connections running.", GetDatabaseName(), + (_connections[IDX_SYNCH].size() + _connections[IDX_ASYNC].size())); } LOG_INFO("sql.driver", " "); @@ -68,109 +100,59 @@ void DatabaseWorkerPool::Close() { LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName()); - //! Shuts down delaythreads for this connection pool by underlying deactivate(). - //! The next dequeue attempt in the worker thread tasks will result in an error, - //! ultimately ending the worker thread task. - _queue->queue()->close(); + //! Closes the actualy MySQL connection. + _connections[IDX_ASYNC].clear(); - for (uint8 i = 0; i < _connectionCount[IDX_ASYNC]; ++i) - { - T* t = _connections[IDX_ASYNC][i]; - DatabaseWorker* worker = t->m_worker; - worker->wait(); //! Block until no more threads are running this task. - delete worker; - t->Close(); //! Closes the actualy MySQL connection. - } - - LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. Proceeding with synchronous connections.", + LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. " + "Proceeding with synchronous connections.", GetDatabaseName()); //! Shut down the synchronous connections //! There's no need for locking the connection, because DatabaseWorkerPool<>::Close //! should only be called after any other thread tasks in the core have exited, //! meaning there can be no concurrent access at this point. - for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i) - _connections[IDX_SYNCH][i]->Close(); - - //! Deletes the ACE_Activation_Queue object and its underlying ACE_Message_Queue - delete _queue; - delete _mqueue; + _connections[IDX_SYNCH].clear(); LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName()); } -template -uint32 DatabaseWorkerPool::OpenConnections(InternalIndex type, uint8 numConnections) -{ - _connections[type].resize(numConnections); - for (uint8 i = 0; i < numConnections; ++i) - { - T* t; - - if (type == IDX_ASYNC) - { - t = new T(_queue, *_connectionInfo); - } - else if (type == IDX_SYNCH) - { - t = new T(*_connectionInfo); - } - else - { - ASSERT(false, "> Incorrect InternalIndex (%u)", static_cast(type)); - } - - _connections[type][i] = t; - ++_connectionCount[type]; - - uint32 error = t->Open(); - - if (!error) - { - if (mysql_get_server_version(t->GetHandle()) < MIN_MYSQL_SERVER_VERSION) - { - LOG_ERROR("sql.driver", "Not support MySQL versions below 5.7"); - error = 1; - } - } - - // Failed to open a connection or invalid version, abort and cleanup - if (error) - { - while (_connectionCount[type] != 0) - { - T* t = _connections[type][i--]; - delete t; - --_connectionCount[type]; - } - - return error; - } - } - - // Everything is fine - return 0; -} - template bool DatabaseWorkerPool::PrepareStatements() { - for (uint8 i = 0; i < IDX_SIZE; ++i) + for (auto& connections : _connections) { - for (uint32 c = 0; c < _connectionCount[i]; ++c) + for (auto& connection : connections) { - T* t = _connections[i][c]; - t->LockIfReady(); - - if (!t->PrepareStatements()) + connection->LockIfReady(); + if (!connection->PrepareStatements()) { - t->Unlock(); + connection->Unlock(); Close(); return false; } else + connection->Unlock(); + + size_t const preparedSize = connection->m_stmts.size(); + if (_preparedStatementSize.size() < preparedSize) + _preparedStatementSize.resize(preparedSize); + + for (size_t i = 0; i < preparedSize; ++i) { - t->Unlock(); + // already set by another connection + // (each connection only has prepared statements of it's own type sync/async) + if (_preparedStatementSize[i] > 0) + continue; + + if (MySQLPreparedStatement* stmt = connection->m_stmts[i].get()) + { + uint32 const paramCount = stmt->GetParameterCount(); + + // TC only supports uint8 indices. + ASSERT(paramCount < std::numeric_limits::max()); + + _preparedStatementSize[i] = static_cast(paramCount); + } } } } @@ -179,74 +161,28 @@ bool DatabaseWorkerPool::PrepareStatements() } template -char const* DatabaseWorkerPool::GetDatabaseName() const +QueryResult DatabaseWorkerPool::Query(char const* sql, T* connection /*= nullptr*/) { - return _connectionInfo->database.c_str(); -} + if (!connection) + connection = GetFreeConnection(); -template -void DatabaseWorkerPool::Execute(const char* sql) -{ - if (!sql) - return; - - BasicStatementTask* task = new BasicStatementTask(sql); - Enqueue(task); -} - -template -void DatabaseWorkerPool::Execute(PreparedStatement* stmt) -{ - PreparedStatementTask* task = new PreparedStatementTask(stmt); - Enqueue(task); -} - -template -void DatabaseWorkerPool::DirectExecute(const char* sql) -{ - if (!sql) - return; - - T* t = GetFreeConnection(); - t->Execute(sql); - t->Unlock(); -} - -template -void DatabaseWorkerPool::DirectExecute(PreparedStatement* stmt) -{ - T* t = GetFreeConnection(); - t->Execute(stmt); - t->Unlock(); - - //! Delete proxy-class. Not needed anymore - delete stmt; -} - -template -QueryResult DatabaseWorkerPool::Query(const char* sql, T* conn /* = nullptr*/) -{ - if (!conn) - conn = GetFreeConnection(); - - ResultSet* result = conn->Query(sql); - conn->Unlock(); - if (!result || !result->GetRowCount()) + ResultSet* result = connection->Query(sql); + connection->Unlock(); + if (!result || !result->GetRowCount() || !result->NextRow()) { delete result; return QueryResult(nullptr); } - result->NextRow(); return QueryResult(result); } template -PreparedQueryResult DatabaseWorkerPool::Query(PreparedStatement* stmt) +PreparedQueryResult DatabaseWorkerPool::Query(PreparedStatement* stmt) { - T* t = GetFreeConnection(); - PreparedResultSet* ret = t->Query(stmt); - t->Unlock(); + auto connection = GetFreeConnection(); + PreparedResultSet* ret = connection->Query(stmt); + connection->Unlock(); //! Delete proxy-class. Not needed anymore delete stmt; @@ -261,40 +197,66 @@ PreparedQueryResult DatabaseWorkerPool::Query(PreparedStatement* stmt) } template -QueryResultFuture DatabaseWorkerPool::AsyncQuery(const char* sql) +QueryCallback DatabaseWorkerPool::AsyncQuery(char const* sql) { - QueryResultFuture res; - BasicStatementTask* task = new BasicStatementTask(sql, res); + BasicStatementTask* task = new BasicStatementTask(sql, true); + // Store future result before enqueueing - task might get already processed and deleted before returning from this method + QueryResultFuture result = task->GetFuture(); Enqueue(task); - return res; //! Actual return value has no use yet + return QueryCallback(std::move(result)); } template -PreparedQueryResultFuture DatabaseWorkerPool::AsyncQuery(PreparedStatement* stmt) +QueryCallback DatabaseWorkerPool::AsyncQuery(PreparedStatement* stmt) { - PreparedQueryResultFuture res; - PreparedStatementTask* task = new PreparedStatementTask(stmt, res); + PreparedStatementTask* task = new PreparedStatementTask(stmt, true); + // Store future result before enqueueing - task might get already processed and deleted before returning from this method + PreparedQueryResultFuture result = task->GetFuture(); Enqueue(task); - return res; + return QueryCallback(std::move(result)); } template -QueryResultHolderFuture DatabaseWorkerPool::DelayQueryHolder(SQLQueryHolder* holder) +SQLQueryHolderCallback DatabaseWorkerPool::DelayQueryHolder(std::shared_ptr> holder) { - QueryResultHolderFuture res; - SQLQueryHolderTask* task = new SQLQueryHolderTask(holder, res); + SQLQueryHolderTask* task = new SQLQueryHolderTask(holder); + // Store future result before enqueueing - task might get already processed and deleted before returning from this method + QueryResultHolderFuture result = task->GetFuture(); Enqueue(task); - return res; //! Fool compiler, has no use yet + return { std::move(holder), std::move(result) }; } template -SQLTransaction DatabaseWorkerPool::BeginTransaction() +SQLTransaction DatabaseWorkerPool::BeginTransaction() { - return SQLTransaction(new Transaction); + return std::make_shared>(); } template -void DatabaseWorkerPool::CommitTransaction(SQLTransaction transaction) +void DatabaseWorkerPool::CommitTransaction(SQLTransaction transaction) +{ +#ifdef ACORE_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: + LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing."); + return; + case 1: + LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code."); + break; + default: + break; + } +#endif // ACORE_DEBUG + + Enqueue(new TransactionTask(transaction)); +} + +template +TransactionCallback DatabaseWorkerPool::AsyncCommitTransaction(SQLTransaction transaction) { #ifdef ACORE_DEBUG //! Only analyze transaction weaknesses in Debug mode. @@ -303,38 +265,42 @@ void DatabaseWorkerPool::CommitTransaction(SQLTransaction transaction) switch (transaction->GetSize()) { case 0: - LOG_INFO("sql.driver", "Transaction contains 0 queries. Not executing."); - return; + LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing."); + break; case 1: - LOG_INFO("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code."); + LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code."); break; default: break; } #endif // ACORE_DEBUG - Enqueue(new TransactionTask(transaction)); + TransactionWithResultTask* task = new TransactionWithResultTask(transaction); + TransactionFuture result = task->GetFuture(); + Enqueue(task); + return TransactionCallback(std::move(result)); } template -void DatabaseWorkerPool::DirectCommitTransaction(SQLTransaction& transaction) +void DatabaseWorkerPool::DirectCommitTransaction(SQLTransaction& transaction) { - T* con = GetFreeConnection(); - int errorCode = con->ExecuteTransaction(transaction); + T* connection = GetFreeConnection(); + int errorCode = connection->ExecuteTransaction(transaction); if (!errorCode) { - con->Unlock(); // OK, operation succesful + connection->Unlock(); // OK, operation succesful return; } //! Handle MySQL Errno 1213 without extending deadlock to the core itself - //! TODO: More elegant way + /// @todo More elegant way if (errorCode == ER_LOCK_DEADLOCK) { + //todo: handle multiple sync threads deadlocking in a similar way as async threads uint8 loopBreaker = 5; for (uint8 i = 0; i < loopBreaker; ++i) { - if (!con->ExecuteTransaction(transaction)) + if (!connection->ExecuteTransaction(transaction)) break; } } @@ -342,20 +308,177 @@ void DatabaseWorkerPool::DirectCommitTransaction(SQLTransaction& transaction) //! Clean up now. transaction->Cleanup(); - con->Unlock(); + connection->Unlock(); } template -void DatabaseWorkerPool::ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt) +PreparedStatement* DatabaseWorkerPool::GetPreparedStatement(PreparedStatementIndex index) { - if (!trans) - Execute(stmt); - else - trans->Append(stmt); + return new PreparedStatement(index, _preparedStatementSize[index]); } template -void DatabaseWorkerPool::ExecuteOrAppend(SQLTransaction& trans, const char* sql) +void DatabaseWorkerPool::EscapeString(std::string& str) +{ + if (str.empty()) + return; + + char* buf = new char[str.size() * 2 + 1]; + EscapeString(buf, str.c_str(), uint32(str.size())); + str = buf; + delete[] buf; +} + +template +void DatabaseWorkerPool::KeepAlive() +{ + //! Ping synchronous connections + for (auto& connection : _connections[IDX_SYNCH]) + { + if (connection->LockIfReady()) + { + connection->Ping(); + connection->Unlock(); + } + } + + //! Assuming all worker threads are free, every worker thread will receive 1 ping operation request + //! If one or more worker threads are busy, the ping operations will not be split evenly, but this doesn't matter + //! as the sole purpose is to prevent connections from idling. + auto const count = _connections[IDX_ASYNC].size(); + for (uint8 i = 0; i < count; ++i) + Enqueue(new PingOperation); +} + +template +uint32 DatabaseWorkerPool::OpenConnections(InternalIndex type, uint8 numConnections) +{ + for (uint8 i = 0; i < numConnections; ++i) + { + // Create the connection + auto connection = [&] { + switch (type) + { + case IDX_ASYNC: + return std::make_unique(_queue.get(), *_connectionInfo); + case IDX_SYNCH: + return std::make_unique(*_connectionInfo); + default: + ABORT(); + } + }(); + + if (uint32 error = connection->Open()) + { + // Failed to open a connection or invalid version, abort and cleanup + _connections[type].clear(); + return error; + } + else if (connection->GetServerVersion() < MIN_MYSQL_SERVER_VERSION) + { + LOG_ERROR("sql.driver", "AzerothCore does not support MySQL versions below 5.7"); + return 1; + } + else + { + _connections[type].push_back(std::move(connection)); + } + } + + // Everything is fine + return 0; +} + +template +unsigned long DatabaseWorkerPool::EscapeString(char* to, char const* from, unsigned long length) +{ + if (!to || !from || !length) + return 0; + + return _connections[IDX_SYNCH].front()->EscapeString(to, from, length); +} + +template +void DatabaseWorkerPool::Enqueue(SQLOperation* op) +{ + _queue->Push(op); +} + +template +T* DatabaseWorkerPool::GetFreeConnection() +{ +#ifdef ACORE_DEBUG + if (_warnSyncQueries) + { + std::ostringstream ss; + ss << boost::stacktrace::stacktrace(); + LOG_WARN("sql.performances", "Sync query at:\n%s", ss.str().c_str()); + } +#endif + + uint8 i = 0; + auto const num_cons = _connections[IDX_SYNCH].size(); + T* connection = nullptr; + + //! Block forever until a connection is free + for (;;) + { + connection = _connections[IDX_SYNCH][++i % num_cons].get(); + //! Must be matched with t->Unlock() or you will get deadlocks + if (connection->LockIfReady()) + break; + } + + return connection; +} + +template +char const* DatabaseWorkerPool::GetDatabaseName() const +{ + return _connectionInfo->database.c_str(); +} + +template +void DatabaseWorkerPool::Execute(char const* sql) +{ + if (Acore::IsFormatEmptyOrNull(sql)) + return; + + BasicStatementTask* task = new BasicStatementTask(sql); + Enqueue(task); +} + +template +void DatabaseWorkerPool::Execute(PreparedStatement* stmt) +{ + PreparedStatementTask* task = new PreparedStatementTask(stmt); + Enqueue(task); +} + +template +void DatabaseWorkerPool::DirectExecute(char const* sql) +{ + if (Acore::IsFormatEmptyOrNull(sql)) + return; + + T* connection = GetFreeConnection(); + connection->Execute(sql); + connection->Unlock(); +} + +template +void DatabaseWorkerPool::DirectExecute(PreparedStatement* stmt) +{ + T* connection = GetFreeConnection(); + connection->Execute(stmt); + connection->Unlock(); + + //! Delete proxy-class. Not needed anymore + delete stmt; +} + +template +void DatabaseWorkerPool::ExecuteOrAppend(SQLTransaction& trans, char const* sql) { if (!trans) Execute(sql); @@ -364,51 +487,14 @@ void DatabaseWorkerPool::ExecuteOrAppend(SQLTransaction& trans, const char* s } template -PreparedStatement* DatabaseWorkerPool::GetPreparedStatement(uint32 index) +void DatabaseWorkerPool::ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt) { - return new PreparedStatement(index); + if (!trans) + Execute(stmt); + else + trans->Append(stmt); } -template -void DatabaseWorkerPool::KeepAlive() -{ - //! Ping synchronous connections - for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i) - { - T* t = _connections[IDX_SYNCH][i]; - if (t->LockIfReady()) - { - t->Ping(); - t->Unlock(); - } - } - - //! Assuming all worker threads are free, every worker thread will receive 1 ping operation request - //! If one or more worker threads are busy, the ping operations will not be split evenly, but this doesn't matter - //! as the sole purpose is to prevent connections from idling. - for (size_t i = 0; i < _connections[IDX_ASYNC].size(); ++i) - Enqueue(new PingOperation); -} - -template -T* DatabaseWorkerPool::GetFreeConnection() -{ - uint8 i = 0; - size_t num_cons = _connectionCount[IDX_SYNCH]; - T* t = nullptr; - - //! Block forever until a connection is free - for (;;) - { - t = _connections[IDX_SYNCH][++i % num_cons]; - //! Must be matched with t->Unlock() or you will get deadlocks - if (t->LockIfReady()) - break; - } - - return t; -} - -template class DatabaseWorkerPool; -template class DatabaseWorkerPool; -template class DatabaseWorkerPool; +template class AC_DATABASE_API DatabaseWorkerPool; +template class AC_DATABASE_API DatabaseWorkerPool; +template class AC_DATABASE_API DatabaseWorkerPool; diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h index e50fd17c8..160d52daa 100644 --- a/src/server/database/Database/DatabaseWorkerPool.h +++ b/src/server/database/Database/DatabaseWorkerPool.h @@ -1,45 +1,45 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _DATABASEWORKERPOOL_H #define _DATABASEWORKERPOOL_H -#include "Common.h" -#include "Callback.h" -#include "MySQLConnection.h" -#include "Transaction.h" -#include "DatabaseWorker.h" -#include "PreparedStatement.h" -#include "Log.h" -#include "QueryResult.h" -#include "QueryHolder.h" -#include "AdhocStatement.h" +#include "DatabaseEnvFwd.h" +#include "Define.h" #include "StringFormat.h" -#include +#include +#include +#include -class PingOperation : public SQLOperation -{ - //! Operation for idle delaythreads - bool Execute() override - { - m_conn->Ping(); - return true; - } -}; +template +class ProducerConsumerQueue; + +class SQLOperation; +struct MySQLConnectionInfo; template class DatabaseWorkerPool { +private: + enum InternalIndex + { + IDX_ASYNC, + IDX_SYNCH, + IDX_SIZE + }; + public: /* Activity state */ DatabaseWorkerPool(); - ~DatabaseWorkerPool() = default; + + ~DatabaseWorkerPool(); void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads); + uint32 Open(); + void Close(); //! Prepares all prepared statements @@ -56,12 +56,12 @@ public: //! Enqueues a one-way SQL operation in string format that will be executed asynchronously. //! This method should only be used for queries that are only executed once, e.g during startup. - void Execute(const char* sql); + void Execute(char const* sql); //! Enqueues a one-way SQL operation in string format -with variable args- that will be executed asynchronously. //! This method should only be used for queries that are only executed once, e.g during startup. template - void PExecute(Format&& sql, Args&& ... args) + void PExecute(Format&& sql, Args&&... args) { if (Acore::IsFormatEmptyOrNull(sql)) return; @@ -71,7 +71,7 @@ public: //! Enqueues a one-way SQL operation in prepared statement format that will be executed asynchronously. //! Statement must be prepared with CONNECTION_ASYNC flag. - void Execute(PreparedStatement* stmt); + void Execute(PreparedStatement* stmt); /** Direct synchronous one-way statement methods. @@ -79,12 +79,12 @@ public: //! Directly executes a one-way SQL operation in string format, that will block the calling thread until finished. //! This method should only be used for queries that are only executed once, e.g during startup. - void DirectExecute(const char* sql); + void DirectExecute(char const* sql); //! Directly executes a one-way SQL operation in string format -with variable args-, that will block the calling thread until finished. //! This method should only be used for queries that are only executed once, e.g during startup. template - void DirectPExecute(Format&& sql, Args&& ... args) + void DirectPExecute(Format&& sql, Args&&... args) { if (Acore::IsFormatEmptyOrNull(sql)) return; @@ -94,7 +94,7 @@ public: //! Directly executes a one-way SQL operation in prepared statement format, that will block the calling thread until finished. //! Statement must be prepared with the CONNECTION_SYNCH flag. - void DirectExecute(PreparedStatement* stmt); + void DirectExecute(PreparedStatement* stmt); /** Synchronous query (with resultset) methods. @@ -102,12 +102,12 @@ public: //! Directly executes an SQL query in string format that will block the calling thread until finished. //! Returns reference counted auto pointer, no need for manual memory management in upper level code. - QueryResult Query(const char* sql, T* conn = nullptr); + QueryResult Query(char const* sql, T* connection = nullptr); //! Directly executes an SQL query in string format -with variable args- that will block the calling thread until finished. //! Returns reference counted auto pointer, no need for manual memory management in upper level code. template - QueryResult PQuery(Format&& sql, T* conn, Args&& ... args) + QueryResult PQuery(Format&& sql, T* conn, Args&&... args) { if (Acore::IsFormatEmptyOrNull(sql)) return QueryResult(nullptr); @@ -118,7 +118,7 @@ public: //! Directly executes an SQL query in string format -with variable args- that will block the calling thread until finished. //! Returns reference counted auto pointer, no need for manual memory management in upper level code. template - QueryResult PQuery(Format&& sql, Args&& ... args) + QueryResult PQuery(Format&& sql, Args&&... args) { if (Acore::IsFormatEmptyOrNull(sql)) return QueryResult(nullptr); @@ -129,7 +129,7 @@ public: //! Directly executes an SQL query in prepared format that will block the calling thread until finished. //! Returns reference counted auto pointer, no need for manual memory management in upper level code. //! Statement must be prepared with CONNECTION_SYNCH flag. - PreparedQueryResult Query(PreparedStatement* stmt); + PreparedQueryResult Query(PreparedStatement* stmt); /** Asynchronous query (with resultset) methods. @@ -137,113 +137,92 @@ public: //! Enqueues a query in string format that will set the value of the QueryResultFuture return object as soon as the query is executed. //! The return value is then processed in ProcessQueryCallback methods. - QueryResultFuture AsyncQuery(const char* sql); - - //! Enqueues a query in string format -with variable args- that will set the value of the QueryResultFuture return object as soon as the query is executed. - //! The return value is then processed in ProcessQueryCallback methods. - template - QueryResultFuture AsyncPQuery(Format&& sql, Args&& ... args) - { - if (Acore::IsFormatEmptyOrNull(sql)) - return QueryResult(nullptr); - - return AsyncQuery(Acore::StringFormat(std::forward(sql), std::forward(args)...).c_str()); - } + QueryCallback AsyncQuery(char const* sql); //! Enqueues a query in prepared format that will set the value of the PreparedQueryResultFuture return object as soon as the query is executed. //! The return value is then processed in ProcessQueryCallback methods. //! Statement must be prepared with CONNECTION_ASYNC flag. - PreparedQueryResultFuture AsyncQuery(PreparedStatement* stmt); + QueryCallback AsyncQuery(PreparedStatement* stmt); //! Enqueues a vector of SQL operations (can be both adhoc and prepared) that will set the value of the QueryResultHolderFuture //! return object as soon as the query is executed. //! The return value is then processed in ProcessQueryCallback methods. //! Any prepared statements added to this holder need to be prepared with the CONNECTION_ASYNC flag. - QueryResultHolderFuture DelayQueryHolder(SQLQueryHolder* holder); + SQLQueryHolderCallback DelayQueryHolder(std::shared_ptr> holder); /** Transaction context methods. */ //! Begins an automanaged transaction pointer that will automatically rollback if not commited. (Autocommit=0) - SQLTransaction BeginTransaction(); + SQLTransaction BeginTransaction(); //! 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. - void CommitTransaction(SQLTransaction transaction); + void CommitTransaction(SQLTransaction 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 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& transaction); - - //! Method used to execute prepared statements in a diverse context. - //! Will be wrapped in a transaction if valid object is present, otherwise executed standalone. - void ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt); + void DirectCommitTransaction(SQLTransaction& transaction); //! Method used to execute ad-hoc statements in a diverse context. //! Will be wrapped in a transaction if valid object is present, otherwise executed standalone. - void ExecuteOrAppend(SQLTransaction& trans, const char* sql); + void ExecuteOrAppend(SQLTransaction& trans, char const* sql); + + //! Method used to execute prepared statements in a diverse context. + //! Will be wrapped in a transaction if valid object is present, otherwise executed standalone. + void ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt); /** Other */ + typedef typename T::Statements PreparedStatementIndex; + //! Automanaged (internally) pointer to a prepared statement object for usage in upper level code. //! Pointer is deleted in this->DirectExecute(PreparedStatement*), this->Query(PreparedStatement*) or PreparedStatementTask::~PreparedStatementTask. //! This object is not tied to the prepared statement on the MySQL context yet until execution. - PreparedStatement* GetPreparedStatement(uint32 index); + PreparedStatement* GetPreparedStatement(PreparedStatementIndex index); //! Apply escape string'ing for current collation. (utf8) - unsigned long EscapeString(char* to, const char* from, unsigned long length) - { - if (!to || !from || !length) - return 0; - - return mysql_real_escape_string(_connections[IDX_SYNCH][0]->GetHandle(), to, from, length); - } + void EscapeString(std::string& str); //! Keeps all our MySQL connections alive, prevent the server from disconnecting us. void KeepAlive(); - void EscapeString(std::string& str) + void WarnAboutSyncQueries([[maybe_unused]] bool warn) { - if (str.empty()) - return; - - char* buf = new char[str.size() * 2 + 1]; - EscapeString(buf, str.c_str(), str.size()); - str = buf; - delete[] buf; +#ifdef ACORE_DEBUG + _warnSyncQueries = warn; +#endif } private: - enum InternalIndex - { - IDX_ASYNC, - IDX_SYNCH, - IDX_SIZE - }; - uint32 OpenConnections(InternalIndex type, uint8 numConnections); - void Enqueue(SQLOperation* op) - { - _queue->enqueue(op); - } + unsigned long EscapeString(char* to, char const* from, unsigned long length); - [[nodiscard]] char const* GetDatabaseName() const; + void Enqueue(SQLOperation* op); //! Gets a free connection in the synchronous connection pool. //! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks. T* GetFreeConnection(); - ACE_Message_Queue* _mqueue; - ACE_Activation_Queue* _queue; //! Queue shared by async worker threads. - std::vector> _connections; - uint32 _connectionCount[IDX_SIZE]; //! Counter of MySQL connections; + char const* GetDatabaseName() const; + + //! Queue shared by async worker threads. + std::unique_ptr> _queue; + std::array>, IDX_SIZE> _connections; std::unique_ptr _connectionInfo; std::vector _preparedStatementSize; uint8 _async_threads, _synch_threads; +#ifdef ACORE_DEBUG + static inline thread_local bool _warnSyncQueries = false; +#endif }; #endif diff --git a/src/server/database/Database/Field.cpp b/src/server/database/Database/Field.cpp index d1e01a7a2..b6af1e434 100644 --- a/src/server/database/Database/Field.cpp +++ b/src/server/database/Database/Field.cpp @@ -1,57 +1,240 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "Field.h" #include "Errors.h" +#include "Log.h" +#include "MySQLHacks.h" Field::Field() { data.value = nullptr; - data.type = MYSQL_TYPE_NULL; data.length = 0; data.raw = false; + meta = nullptr; } -Field::~Field() -{ - CleanUp(); -} +Field::~Field() = default; -void Field::SetByteValue(const void* newValue, const size_t newSize, enum_field_types newType, uint32 length) +uint8 Field::GetUInt8() const { - if (data.value) - CleanUp(); + if (!data.value) + return 0; - // This value stores raw bytes that have to be explicitly cast later - if (newValue) +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int8)) { - data.value = new char[newSize]; - memcpy(data.value, newValue, newSize); - data.length = length; + LogWrongType(__FUNCTION__); + return 0; } - data.type = newType; - data.raw = true; +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtoul(data.value, nullptr, 10)); } -void Field::SetStructuredValue(char* newValue, enum_field_types newType, uint32 length) +int8 Field::GetInt8() const { - if (data.value) - CleanUp(); + if (!data.value) + return 0; - // This value stores somewhat structured data that needs function style casting - if (newValue) +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int8)) { - data.value = new char[length + 1]; - memcpy(data.value, newValue, length); - *(reinterpret_cast(data.value) + length) = '\0'; - data.length = length; + LogWrongType(__FUNCTION__); + return 0; } +#endif - data.type = newType; - data.raw = false; + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtol(data.value, nullptr, 10)); +} + +uint16 Field::GetUInt16() const +{ + if (!data.value) + return 0; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int16)) + { + LogWrongType(__FUNCTION__); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtoul(data.value, nullptr, 10)); +} + +int16 Field::GetInt16() const +{ + if (!data.value) + return 0; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int16)) + { + LogWrongType(__FUNCTION__); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtol(data.value, nullptr, 10)); +} + +uint32 Field::GetUInt32() const +{ + if (!data.value) + return 0; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int32)) + { + LogWrongType(__FUNCTION__); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtoul(data.value, nullptr, 10)); +} + +int32 Field::GetInt32() const +{ + if (!data.value) + return 0; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int32)) + { + LogWrongType(__FUNCTION__); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtol(data.value, nullptr, 10)); +} + +uint64 Field::GetUInt64() const +{ + if (!data.value) + return 0; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int64)) + { + LogWrongType(__FUNCTION__); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtoull(data.value, nullptr, 10)); +} + +int64 Field::GetInt64() const +{ + if (!data.value) + return 0; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Int64)) + { + LogWrongType(__FUNCTION__); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(strtoll(data.value, nullptr, 10)); +} + +float Field::GetFloat() const +{ + if (!data.value) + return 0.0f; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Float)) + { + LogWrongType(__FUNCTION__); + return 0.0f; + } +#endif + + if (data.raw) + return *reinterpret_cast(data.value); + return static_cast(atof(data.value)); +} + +double Field::GetDouble() const +{ + if (!data.value) + return 0.0f; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (!IsType(DatabaseFieldTypes::Double) && !IsType(DatabaseFieldTypes::Decimal)) + { + LogWrongType(__FUNCTION__); + return 0.0f; + } +#endif + + if (data.raw && !IsType(DatabaseFieldTypes::Decimal)) + return *reinterpret_cast(data.value); + return static_cast(atof(data.value)); +} + +char const* Field::GetCString() const +{ + if (!data.value) + return nullptr; + +#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS + if (IsNumeric() && data.raw) + { + LogWrongType(__FUNCTION__); + return nullptr; + } +#endif + return static_cast(data.value); +} + +std::string Field::GetString() const +{ + if (!data.value) + return ""; + + char const* string = GetCString(); + if (!string) + return ""; + + return std::string(string, data.length); +} + +std::string_view Field::GetStringView() const +{ + if (!data.value) + return {}; + + char const* const string = GetCString(); + if (!string) + return {}; + + return { string, data.length }; } std::vector Field::GetBinary() const @@ -67,6 +250,48 @@ std::vector Field::GetBinary() const void Field::GetBinarySizeChecked(uint8* buf, size_t length) const { - ASSERT(data.value && (data.length == length)); + ASSERT(data.value && (data.length == length), "Expected %zu-byte binary blob, got %sdata (%u bytes) instead", length, data.value ? "" : "no ", data.length); memcpy(buf, data.value, length); } + +void Field::SetByteValue(char const* newValue, uint32 length) +{ + // This value stores raw bytes that have to be explicitly cast later + data.value = newValue; + data.length = length; + data.raw = true; +} + +void Field::SetStructuredValue(char const* newValue, uint32 length) +{ + // This value stores somewhat structured data that needs function style casting + data.value = newValue; + data.length = length; + data.raw = false; +} + +bool Field::IsType(DatabaseFieldTypes type) const +{ + return meta->Type == type; +} + +bool Field::IsNumeric() const +{ + return (meta->Type == DatabaseFieldTypes::Int8 || + meta->Type == DatabaseFieldTypes::Int16 || + meta->Type == DatabaseFieldTypes::Int32 || + meta->Type == DatabaseFieldTypes::Int64 || + meta->Type == DatabaseFieldTypes::Float || + meta->Type == DatabaseFieldTypes::Double); +} + +void Field::LogWrongType(char const* getter) const +{ + LOG_WARN("sql.sql", "Warning: %s on %s field %s.%s (%s.%s) at index %u.", + getter, meta->TypeName, meta->TableAlias, meta->Alias, meta->TableName, meta->Name, meta->Index); +} + +void Field::SetMetadata(QueryResultFieldMetadata const* fieldMeta) +{ + meta = fieldMeta; +} diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h index bcc73ec04..be2eea2b8 100644 --- a/src/server/database/Database/Field.h +++ b/src/server/database/Database/Field.h @@ -1,437 +1,135 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ -#ifndef AZEROTHCORE_FIELD_H -#define AZEROTHCORE_FIELD_H +#ifndef _FIELD_H +#define _FIELD_H -#include "Common.h" -#include "Log.h" +#include "DatabaseEnvFwd.h" +#include "Define.h" #include -#include +#include +#include +#include -class Field +enum class DatabaseFieldTypes : uint8 { - friend class ResultSet; - friend class PreparedResultSet; + Null, + Int8, + Int16, + Int32, + Int64, + Float, + Double, + Decimal, + Date, + Binary +}; + +struct QueryResultFieldMetadata +{ + char const* TableName = nullptr; + char const* TableAlias = nullptr; + char const* Name = nullptr; + char const* Alias = nullptr; + char const* TypeName = nullptr; + uint32 Index = 0; + DatabaseFieldTypes Type = DatabaseFieldTypes::Null; +}; + +/** + @class Field + + @brief Class used to access individual fields of database query result + + Guideline on field type matching: + + | MySQL type | method to use | + |------------------------|----------------------------------------| + | TINYINT | GetBool, GetInt8, GetUInt8 | + | SMALLINT | GetInt16, GetUInt16 | + | MEDIUMINT, INT | GetInt32, GetUInt32 | + | BIGINT | GetInt64, GetUInt64 | + | FLOAT | GetFloat | + | DOUBLE, DECIMAL | GetDouble | + | CHAR, VARCHAR, | GetCString, GetString | + | TINYTEXT, MEDIUMTEXT, | GetCString, GetString | + | TEXT, LONGTEXT | GetCString, GetString | + | TINYBLOB, MEDIUMBLOB, | GetBinary, GetString | + | BLOB, LONGBLOB | GetBinary, GetString | + | BINARY, VARBINARY | GetBinary | + + Return types of aggregate functions: + + | Function | Type | + |----------|-------------------| + | MIN, MAX | Same as the field | + | SUM, AVG | DECIMAL | + | COUNT | BIGINT | +*/ +class AC_DATABASE_API Field +{ +friend class ResultSet; +friend class PreparedResultSet; public: - [[nodiscard]] bool GetBool() const // Wrapper, actually gets integer + Field(); + ~Field(); + + bool GetBool() const // Wrapper, actually gets integer { - return (GetUInt8() == 1); + return GetUInt8() == 1 ? true : false; } - [[nodiscard]] uint8 GetUInt8() const - { - if (!data.value) - return 0; + uint8 GetUInt8() const; + int8 GetInt8() const; + uint16 GetUInt16() const; + int16 GetInt16() const; + uint32 GetUInt32() const; + int32 GetInt32() const; + uint64 GetUInt64() const; + int64 GetInt64() const; + float GetFloat() const; + double GetDouble() const; + char const* GetCString() const; + std::string GetString() const; + std::string_view GetStringView() const; + std::vector GetBinary() const; -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_TINY)) - { - LOG_INFO("sql.driver", "Warning: GetUInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atol((char*)data.value)); - } - - [[nodiscard]] int8 GetInt8() const - { - if (!data.value) - return 0; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_TINY)) - { - LOG_INFO("sql.driver", "Warning: GetInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atol((char*)data.value)); - } - -#ifdef ELUNA - enum_field_types GetType() const - { - return data.type; - } -#endif - - [[nodiscard]] uint16 GetUInt16() const - { - if (!data.value) - return 0; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR)) - { - LOG_INFO("sql.driver", "Warning: GetUInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atol((char*)data.value)); - } - - [[nodiscard]] int16 GetInt16() const - { - if (!data.value) - return 0; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR)) - { - LOG_INFO("sql.driver", "Warning: GetInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atol((char*)data.value)); - } - - [[nodiscard]] uint32 GetUInt32() const - { - if (!data.value) - return 0; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG)) - { - LOG_INFO("sql.driver", "Warning: GetUInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atol((char*)data.value)); - } - - [[nodiscard]] int32 GetInt32() const - { - if (!data.value) - return 0; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG)) - { - LOG_INFO("sql.driver", "Warning: GetInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atol((char*)data.value)); - } - - [[nodiscard]] uint64 GetUInt64() const - { - if (!data.value) - return 0; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT)) - { - LOG_INFO("sql.driver", "Warning: GetUInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atol((char*)data.value)); - } - - [[nodiscard]] int64 GetInt64() const - { - if (!data.value) - return 0; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT)) - { - LOG_INFO("sql.driver", "Warning: GetInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type)); - return 0; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(strtol((char*)data.value, nullptr, 10)); - } - - [[nodiscard]] float GetFloat() const - { - if (!data.value) - return 0.0f; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_FLOAT)) - { - LOG_INFO("sql.driver", "Warning: GetFloat() on non-float field. Using type: %s.", FieldTypeToString(data.type)); - return 0.0f; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atof((char*)data.value)); - } - - [[nodiscard]] double GetDouble() const - { - if (!data.value) - return 0.0f; - -#ifdef ACORE_DEBUG - if (!IsType(MYSQL_TYPE_DOUBLE)) - { - LOG_INFO("sql.driver", "Warning: GetDouble() on non-double field. Using type: %s.", FieldTypeToString(data.type)); - return 0.0f; - } -#endif - - if (data.raw) - return *reinterpret_cast(data.value); - return static_cast(atof((char*)data.value)); - } - - [[nodiscard]] char const* GetCString() const - { - if (!data.value) - return nullptr; - -#ifdef ACORE_DEBUG - if (IsNumeric()) - { - LOG_INFO("sql.driver", "Error: GetCString() on numeric field. Using type: %s.", FieldTypeToString(data.type)); - return nullptr; - } -#endif - return static_cast(data.value); - } - - [[nodiscard]] std::string GetString() const - { - if (!data.value) - return ""; - - if (data.raw) - { - char const* string = GetCString(); - if (!string) - string = ""; - return std::string(string, data.length); - } - return std::string((char*)data.value); - } - - [[nodiscard]] bool IsNull() const - { - if (IsBinary() && data.length == 0) - { - return true; - } - return data.value == nullptr; - } - - [[nodiscard]] std::vector GetBinary() const; - template - [[nodiscard]] std::array GetBinary() const + template + std::array GetBinary() const { std::array buf; GetBinarySizeChecked(buf.data(), S); return buf; } -protected: - Field(); - ~Field(); + bool IsNull() const + { + return data.value == nullptr; + } -#if defined(__GNUC__) -#pragma pack(1) -#else -#pragma pack(push, 1) -#endif + DatabaseFieldTypes GetType() { return meta->Type; } + +protected: struct { - uint32 length; // Length (prepared strings only) - void* value; // Actual data in memory - enum_field_types type; // Field type + char const* value; // Actual data in memory + uint32 length; // Length bool raw; // Raw bytes? (Prepared statement or ad hoc) } data; -#if defined(__GNUC__) -#pragma pack() -#else -#pragma pack(pop) -#endif - void SetByteValue(void const* newValue, size_t const newSize, enum_field_types newType, uint32 length); - void SetStructuredValue(char* newValue, enum_field_types newType, uint32 length); - - void CleanUp() - { - delete[] ((char*)data.value); - data.value = nullptr; - } - - static size_t SizeForType(MYSQL_FIELD* field) - { - switch (field->type) - { - case MYSQL_TYPE_NULL: - return 0; - case MYSQL_TYPE_TINY: - return 1; - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_SHORT: - return 2; - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_FLOAT: - return 4; - case MYSQL_TYPE_DOUBLE: - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_BIT: - return 8; - - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - return sizeof(MYSQL_TIME); - - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VAR_STRING: - return field->max_length + 1; - - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - return 64; - - case MYSQL_TYPE_GEOMETRY: - /* - Following types are not sent over the wire: - MYSQL_TYPE_ENUM: - MYSQL_TYPE_SET: - */ - default: - LOG_INFO("sql.driver", "SQL::SizeForType(): invalid field type %u", uint32(field->type)); - return 0; - } - } - - [[nodiscard]] bool IsType(enum_field_types type) const - { - return data.type == type; - } - - [[nodiscard]] bool IsNumeric() const - { - return (data.type == MYSQL_TYPE_TINY || - data.type == MYSQL_TYPE_SHORT || - data.type == MYSQL_TYPE_INT24 || - data.type == MYSQL_TYPE_LONG || - data.type == MYSQL_TYPE_FLOAT || - data.type == MYSQL_TYPE_DOUBLE || - data.type == MYSQL_TYPE_LONGLONG ); - } - - [[nodiscard]] bool IsBinary() const - { - return ( - data.type == MYSQL_TYPE_TINY_BLOB || - data.type == MYSQL_TYPE_MEDIUM_BLOB || - data.type == MYSQL_TYPE_LONG_BLOB || - data.type == MYSQL_TYPE_BLOB || - data.type == MYSQL_TYPE_VAR_STRING || - data.type == MYSQL_TYPE_STRING - ); - } - - void GetBinarySizeChecked(uint8* buf, size_t size) const; + void SetByteValue(char const* newValue, uint32 length); + void SetStructuredValue(char const* newValue, uint32 length); + bool IsType(DatabaseFieldTypes type) const; + bool IsNumeric() const; private: -#ifdef ACORE_DEBUG - static char const* FieldTypeToString(enum_field_types type) - { - switch (type) - { - case MYSQL_TYPE_BIT: - return "BIT"; - case MYSQL_TYPE_BLOB: - return "BLOB"; - case MYSQL_TYPE_DATE: - return "DATE"; - case MYSQL_TYPE_DATETIME: - return "DATETIME"; - case MYSQL_TYPE_NEWDECIMAL: - return "NEWDECIMAL"; - case MYSQL_TYPE_DECIMAL: - return "DECIMAL"; - case MYSQL_TYPE_DOUBLE: - return "DOUBLE"; - case MYSQL_TYPE_ENUM: - return "ENUM"; - case MYSQL_TYPE_FLOAT: - return "FLOAT"; - case MYSQL_TYPE_GEOMETRY: - return "GEOMETRY"; - case MYSQL_TYPE_INT24: - return "INT24"; - case MYSQL_TYPE_LONG: - return "LONG"; - case MYSQL_TYPE_LONGLONG: - return "LONGLONG"; - case MYSQL_TYPE_LONG_BLOB: - return "LONG_BLOB"; - case MYSQL_TYPE_MEDIUM_BLOB: - return "MEDIUM_BLOB"; - case MYSQL_TYPE_NEWDATE: - return "NEWDATE"; - case MYSQL_TYPE_NULL: - return "nullptr"; - case MYSQL_TYPE_SET: - return "SET"; - case MYSQL_TYPE_SHORT: - return "SHORT"; - case MYSQL_TYPE_STRING: - return "STRING"; - case MYSQL_TYPE_TIME: - return "TIME"; - case MYSQL_TYPE_TIMESTAMP: - return "TIMESTAMP"; - case MYSQL_TYPE_TINY: - return "TINY"; - case MYSQL_TYPE_TINY_BLOB: - return "TINY_BLOB"; - case MYSQL_TYPE_VAR_STRING: - return "VAR_STRING"; - case MYSQL_TYPE_YEAR: - return "YEAR"; - default: - return "-Unknown-"; - } - } -#endif + QueryResultFieldMetadata const* meta; + void LogWrongType(char const* getter) const; + void SetMetadata(QueryResultFieldMetadata const* fieldMeta); + void GetBinarySizeChecked(uint8* buf, size_t size) const; }; #endif diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index f5e26f606..8bf4f2649 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -1,10 +1,10 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "CharacterDatabase.h" +#include "MySQLPreparedStatement.h" void CharacterDatabaseConnection::DoPrepareStatements() { @@ -37,7 +37,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "cb.guid, c.extra_flags, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? " "LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid " "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid", CONNECTION_ASYNC); - PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name FROM characters WHERE guid = ? AND account = ? AND (at_login & ?) = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH); @@ -138,7 +138,6 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating, maxMMR FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT ? AS account,(SELECT COUNT(*) FROM characters WHERE account =?) AS cnt", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_NAME, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); // Guild handling @@ -361,7 +360,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_INVALID_PET_SPELL, "DELETE FROM pet_spell WHERE spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME, "UPDATE instance_reset SET resettime = ? WHERE mapid = ? AND difficulty = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_ONLINE, "UPDATE characters SET online = 1 WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN, "UPDATE characters set name = ?, at_login = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_WORLDSTATE, "UPDATE worldstates SET value = ? WHERE entry = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_WORLDSTATE, "INSERT INTO worldstates (entry, value) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, "DELETE FROM character_instance WHERE instance = ?", CONNECTION_ASYNC); @@ -396,6 +395,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_AT_LOGIN, "SELECT at_login FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, "SELECT class, level, at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login FROM characters WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, "SELECT at_login, knownTitles, money FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY, "SELECT at_login, knownTitles, money FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH); @@ -437,7 +438,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID, "DELETE FROM petition_sign WHERE petitionguid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_DECLINED_NAME, "INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_FACTION_OR_RACE, "UPDATE characters SET name = ?, race = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_CHAR_RACE, "UPDATE characters SET race = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_SKILL_LANGUAGES, "DELETE FROM character_skills WHERE skill IN (98, 113, 759, 111, 313, 109, 115, 315, 673, 137) AND guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_SKILL_LANGUAGE, "INSERT INTO `character_skills` (guid, skill, value, max) VALUES (?, ?, 300, 300)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_TAXI_PATH, "UPDATE characters SET taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC); @@ -546,6 +547,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC); @@ -578,3 +580,15 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Character names PrepareStatement(CHAR_INS_RESERVED_PLAYER_NAME, "INSERT IGNORE INTO reserved_name (name) VALUES (?)", CONNECTION_ASYNC); } + +CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) +{ +} + +CharacterDatabaseConnection::CharacterDatabaseConnection(ProducerConsumerQueue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) +{ +} + +CharacterDatabaseConnection::~CharacterDatabaseConnection() +{ +} diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index dab93381c..b762a5989 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -1,29 +1,14 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _CHARACTERDATABASE_H #define _CHARACTERDATABASE_H -#include "DatabaseWorkerPool.h" #include "MySQLConnection.h" -class CharacterDatabaseConnection : public MySQLConnection -{ -public: - //- Constructors for sync and async connections - CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) {} - CharacterDatabaseConnection(ACE_Activation_Queue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) {} - - //- Loads database type specific prepared statements - void DoPrepareStatements() override; -}; - -typedef DatabaseWorkerPool CharacterDatabaseWorkerPool; - -enum CharacterDatabaseStatements +enum CharacterDatabaseStatements : uint32 { /* Naming standard for defines: {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed} @@ -137,7 +122,6 @@ enum CharacterDatabaseStatements CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, CHAR_SEL_MATCH_MAKER_RATING, CHAR_SEL_CHARACTER_COUNT, - CHAR_UPD_NAME, CHAR_DEL_DECLINED_NAME, CHAR_INS_GUILD, @@ -342,6 +326,8 @@ enum CharacterDatabaseStatements CHAR_SEL_POOL_QUEST_SAVE, CHAR_SEL_CHARACTER_AT_LOGIN, CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, + CHAR_SEL_CHAR_CUSTOMIZE_INFO, + CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY, CHAR_SEL_CHAR_COD_ITEM_MAIL, CHAR_SEL_CHAR_SOCIAL, @@ -378,7 +364,7 @@ enum CharacterDatabaseStatements CHAR_DEL_PETITION_SIGNATURE_BY_GUID, CHAR_DEL_CHAR_DECLINED_NAME, CHAR_INS_CHAR_DECLINED_NAME, - CHAR_UPD_FACTION_OR_RACE, + CHAR_UPD_CHAR_RACE, CHAR_DEL_CHAR_SKILL_LANGUAGES, CHAR_INS_CHAR_SKILL_LANGUAGE, CHAR_UPD_CHAR_TAXI_PATH, @@ -466,6 +452,7 @@ enum CharacterDatabaseStatements CHAR_DEL_CHAR_PET_BY_OWNER, CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, + CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS, CHAR_SEL_PET_SLOTS, CHAR_SEL_PET_SLOTS_DETAIL, CHAR_SEL_PET_ENTRY, @@ -512,4 +499,18 @@ enum CharacterDatabaseStatements MAX_CHARACTERDATABASE_STATEMENTS }; +class AC_DATABASE_API CharacterDatabaseConnection : public MySQLConnection +{ +public: + typedef CharacterDatabaseStatements Statements; + + //- Constructors for sync and async connections + CharacterDatabaseConnection(MySQLConnectionInfo& connInfo); + CharacterDatabaseConnection(ProducerConsumerQueue* q, MySQLConnectionInfo& connInfo); + ~CharacterDatabaseConnection(); + + //- Loads database type specific prepared statements + void DoPrepareStatements() override; +}; + #endif diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 7c05d5024..90989bde9 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -1,10 +1,10 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "LoginDatabase.h" +#include "MySQLPreparedStatement.h" void LoginDatabaseConnection::DoPrepareStatements() { @@ -110,3 +110,15 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_TOTP_SECRET, "SELECT totp_secret FROM account WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_UPD_ACCOUNT_TOTP_SECRET, "UPDATE account SET totp_secret = ? WHERE id = ?", CONNECTION_ASYNC); } + +LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) +{ +} + +LoginDatabaseConnection::LoginDatabaseConnection(ProducerConsumerQueue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) +{ +} + +LoginDatabaseConnection::~LoginDatabaseConnection() +{ +} diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index d1126d10c..312440d6f 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -1,29 +1,14 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _LOGINDATABASE_H #define _LOGINDATABASE_H -#include "DatabaseWorkerPool.h" #include "MySQLConnection.h" -class LoginDatabaseConnection : public MySQLConnection -{ -public: - //- Constructors for sync and async connections - LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { } - LoginDatabaseConnection(ACE_Activation_Queue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { } - - //- Loads database type specific prepared statements - void DoPrepareStatements() override; -}; - -typedef DatabaseWorkerPool LoginDatabaseWorkerPool; - -enum LoginDatabaseStatements +enum LoginDatabaseStatements : uint32 { /* Naming standard for defines: {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed} @@ -121,4 +106,18 @@ enum LoginDatabaseStatements MAX_LOGINDATABASE_STATEMENTS }; +class AC_DATABASE_API LoginDatabaseConnection : public MySQLConnection +{ +public: + typedef LoginDatabaseStatements Statements; + + //- Constructors for sync and async connections + LoginDatabaseConnection(MySQLConnectionInfo& connInfo); + LoginDatabaseConnection(ProducerConsumerQueue* q, MySQLConnectionInfo& connInfo); + ~LoginDatabaseConnection(); + + //- Loads database type specific prepared statements + void DoPrepareStatements() override; +}; + #endif diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 398cd53f5..98fdcc75a 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -1,10 +1,10 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "WorldDatabase.h" +#include "MySQLPreparedStatement.h" void WorldDatabaseConnection::DoPrepareStatements() { @@ -84,3 +84,15 @@ void WorldDatabaseConnection::DoPrepareStatements() // 0: uint8 PrepareStatement(WORLD_SEL_REQ_XP, "SELECT Experience FROM player_xp_for_level WHERE Level = ?", CONNECTION_SYNCH); } + +WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) +{ +} + +WorldDatabaseConnection::WorldDatabaseConnection(ProducerConsumerQueue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) +{ +} + +WorldDatabaseConnection::~WorldDatabaseConnection() +{ +} diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h index f00ef5b1c..c34c398cc 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.h +++ b/src/server/database/Database/Implementation/WorldDatabase.h @@ -1,29 +1,14 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _WORLDDATABASE_H #define _WORLDDATABASE_H -#include "DatabaseWorkerPool.h" #include "MySQLConnection.h" -class WorldDatabaseConnection : public MySQLConnection -{ -public: - //- Constructors for sync and async connections - WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { } - WorldDatabaseConnection(ACE_Activation_Queue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { } - - //- Loads database type specific prepared statements - void DoPrepareStatements() override; -}; - -typedef DatabaseWorkerPool WorldDatabaseWorkerPool; - -enum WorldDatabaseStatements +enum WorldDatabaseStatements : uint32 { /* Naming standard for defines: {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed} @@ -107,4 +92,18 @@ enum WorldDatabaseStatements MAX_WORLDDATABASE_STATEMENTS }; +class AC_DATABASE_API WorldDatabaseConnection : public MySQLConnection +{ +public: + typedef WorldDatabaseStatements Statements; + + //- Constructors for sync and async connections + WorldDatabaseConnection(MySQLConnectionInfo& connInfo); + WorldDatabaseConnection(ProducerConsumerQueue* q, MySQLConnectionInfo& connInfo); + ~WorldDatabaseConnection(); + + //- Loads database type specific prepared statements + void DoPrepareStatements() override; +}; + #endif diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index 2abf88513..f2f10d4d9 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -1,54 +1,71 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ -#include "Common.h" #include "MySQLConnection.h" -#include "MySQLThreading.h" -#include "QueryResult.h" -#include "SQLOperation.h" -#include "PreparedStatement.h" +#include "Common.h" #include "DatabaseWorker.h" -#include "Timer.h" #include "Log.h" -#include "Duration.h" -#include -#include +#include "MySQLHacks.h" +#include "MySQLPreparedStatement.h" +#include "MySQLWorkaround.h" +#include "PreparedStatement.h" +#include "QueryResult.h" +#include "Timer.h" +#include "Tokenize.h" +#include "Transaction.h" +#include "Util.h" #include -#include +#include -#ifdef _WIN32 -#include -#endif - -MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) : - m_reconnecting(false), - m_prepareError(false), - m_queue(nullptr), - m_worker(nullptr), - m_Mysql(nullptr), - m_connectionInfo(connInfo), - m_connectionFlags(CONNECTION_SYNCH) +MySQLConnectionInfo::MySQLConnectionInfo(std::string const& infoString) { + std::vector tokens = Acore::Tokenize(infoString, ';', true); + + if (tokens.size() != 5 && tokens.size() != 6) + return; + + host.assign(tokens[0]); + port_or_socket.assign(tokens[1]); + user.assign(tokens[2]); + password.assign(tokens[3]); + database.assign(tokens[4]); + + if (tokens.size() == 6) + ssl.assign(tokens[5]); } -MySQLConnection::MySQLConnection(ACE_Activation_Queue* queue, MySQLConnectionInfo& connInfo) : - m_reconnecting(false), - m_prepareError(false), - m_queue(queue), - m_Mysql(nullptr), - m_connectionInfo(connInfo), - m_connectionFlags(CONNECTION_ASYNC) +MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) : +m_reconnecting(false), +m_prepareError(false), +m_queue(nullptr), +m_Mysql(nullptr), +m_connectionInfo(connInfo), +m_connectionFlags(CONNECTION_SYNCH) { } + +MySQLConnection::MySQLConnection(ProducerConsumerQueue* queue, MySQLConnectionInfo& connInfo) : +m_reconnecting(false), +m_prepareError(false), +m_queue(queue), +m_Mysql(nullptr), +m_connectionInfo(connInfo), +m_connectionFlags(CONNECTION_ASYNC) { - m_worker = new DatabaseWorker(m_queue, this); + m_worker = std::make_unique(m_queue, this); } MySQLConnection::~MySQLConnection() { - for (auto stmt : m_stmts) - delete stmt; + Close(); +} + +void MySQLConnection::Close() +{ + // Stop the worker thread before the statements are cleared + m_worker.reset(); + + m_stmts.clear(); if (m_Mysql) { @@ -57,19 +74,14 @@ MySQLConnection::~MySQLConnection() } } -void MySQLConnection::Close() -{ - /// Only close us if we're not operating - delete this; -} - uint32 MySQLConnection::Open() { - MYSQL* mysqlInit = mysql_init(nullptr); + MYSQL *mysqlInit; + mysqlInit = mysql_init(nullptr); if (!mysqlInit) { LOG_ERROR("sql.sql", "Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str()); - return false; + return CR_UNKNOWN_ERROR; } int port; @@ -78,7 +90,7 @@ uint32 MySQLConnection::Open() mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8"); //mysql_options(mysqlInit, MYSQL_OPT_READ_TIMEOUT, (char const*)&timeout); -#ifdef _WIN32 + #ifdef _WIN32 if (m_connectionInfo.host == ".") // named pipe use option (Windows) { unsigned int opt = MYSQL_PROTOCOL_PIPE; @@ -91,7 +103,7 @@ uint32 MySQLConnection::Open() port = atoi(m_connectionInfo.port_or_socket.c_str()); unix_socket = 0; } -#else + #else if (m_connectionInfo.host == ".") // socket use option (Unix/Linux) { unsigned int opt = MYSQL_PROTOCOL_SOCKET; @@ -103,19 +115,31 @@ uint32 MySQLConnection::Open() else // generic case { port = atoi(m_connectionInfo.port_or_socket.c_str()); - unix_socket = 0; + unix_socket = nullptr; } -#endif + #endif - m_Mysql = mysql_real_connect( - mysqlInit, - m_connectionInfo.host.c_str(), - m_connectionInfo.user.c_str(), - m_connectionInfo.password.c_str(), - m_connectionInfo.database.c_str(), - port, - unix_socket, - 0); + if (m_connectionInfo.ssl != "") + { +#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80000 + mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED; + if (m_connectionInfo.ssl == "ssl") + { + opt_use_ssl = SSL_MODE_REQUIRED; + } + mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (char const*)&opt_use_ssl); +#else + MySQLBool opt_use_ssl = MySQLBool(0); + if (m_connectionInfo.ssl == "ssl") + { + opt_use_ssl = MySQLBool(1); + } + mysql_options(mysqlInit, MYSQL_OPT_SSL_ENFORCE, (char const*)&opt_use_ssl); +#endif + } + + m_Mysql = reinterpret_cast(mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(), + m_connectionInfo.password.c_str(), m_connectionInfo.database.c_str(), port, unix_socket, 0)); if (m_Mysql) { @@ -123,15 +147,12 @@ uint32 MySQLConnection::Open() { LOG_INFO("sql.sql", "MySQL client library: %s", mysql_get_client_info()); LOG_INFO("sql.sql", "MySQL server ver: %s ", mysql_get_server_info(m_Mysql)); - - if (mysql_get_server_version(m_Mysql) != mysql_get_client_version()) - { - LOG_WARN("sql.sql", "[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements."); - } + // MySQL version above 5.1 IS required in both client and server and there is no known issue with different versions above 5.1 + // if (mysql_get_server_version(m_Mysql) != mysql_get_client_version()) + // LOG_INFO("sql.sql", "[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements."); } LOG_INFO("sql.sql", "Connected to MySQL database at %s", m_connectionInfo.host.c_str()); - mysql_autocommit(m_Mysql, 1); // set connection properties to UTF8 to properly handle locales for different @@ -139,11 +160,13 @@ uint32 MySQLConnection::Open() mysql_set_character_set(m_Mysql, "utf8"); return 0; } - - LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit)); - uint32 errorCode = mysql_errno(mysqlInit); - mysql_close(mysqlInit); - return errorCode; + else + { + LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit)); + uint32 errorCode = mysql_errno(mysqlInit); + mysql_close(mysqlInit); + return errorCode; + } } bool MySQLConnection::PrepareStatements() @@ -152,154 +175,7 @@ bool MySQLConnection::PrepareStatements() return !m_prepareError; } -bool MySQLConnection::Execute(const char* sql) -{ - if (!m_Mysql) - return false; - - uint32 _s = getMSTime(); - - if (mysql_query(m_Mysql, sql)) - { - uint32 lErrno = mysql_errno(m_Mysql); - - LOG_ERROR("sql.sql", "SQL: %s", sql); - LOG_ERROR("sql.sql", "ERROR: [%u] %s", lErrno, mysql_error(m_Mysql)); - - if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) - return Execute(sql); // Try again - - return false; - } - - LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql); - - return true; -} - -bool MySQLConnection::Execute(PreparedStatement* stmt) -{ - if (!m_Mysql) - return false; - - uint32 index = stmt->m_index; - { - MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index); - ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query - m_mStmt->m_stmt = stmt; // Cross reference them for debug output - stmt->m_stmt = m_mStmt; // TODO: Cleaner way - - stmt->BindParameters(); - - MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT(); - MYSQL_BIND* msql_BIND = m_mStmt->GetBind(); - - uint32 _s = getMSTime(); - - if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) - { - uint32 lErrno = mysql_errno(m_Mysql); - LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT)); - - if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) - return Execute(stmt); // Try again - - m_mStmt->ClearParameters(); - return false; - } - - if (mysql_stmt_execute(msql_STMT)) - { - uint32 lErrno = mysql_errno(m_Mysql); - LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT)); - - if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) - return Execute(stmt); // Try again - - m_mStmt->ClearParameters(); - return false; - } - - LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString(m_queries[index].first).c_str()); - - m_mStmt->ClearParameters(); - return true; - } -} - -bool MySQLConnection::_Query(PreparedStatement* stmt, MYSQL_RES** pResult, uint64* pRowCount, uint32* pFieldCount) -{ - if (!m_Mysql) - return false; - - uint32 index = stmt->m_index; - { - MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index); - ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query - m_mStmt->m_stmt = stmt; // Cross reference them for debug output - stmt->m_stmt = m_mStmt; // TODO: Cleaner way - - stmt->BindParameters(); - - MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT(); - MYSQL_BIND* msql_BIND = m_mStmt->GetBind(); - - uint32 _s = getMSTime(); - - if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) - { - uint32 lErrno = mysql_errno(m_Mysql); - LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT)); - - if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) - return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again - - m_mStmt->ClearParameters(); - return false; - } - - if (mysql_stmt_execute(msql_STMT)) - { - uint32 lErrno = mysql_errno(m_Mysql); - LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", - m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT)); - - if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) - return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again - - m_mStmt->ClearParameters(); - return false; - } - - LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString(m_queries[index].first).c_str()); - - m_mStmt->ClearParameters(); - - *pResult = mysql_stmt_result_metadata(msql_STMT); - *pRowCount = mysql_stmt_num_rows(msql_STMT); - *pFieldCount = mysql_stmt_field_count(msql_STMT); - - return true; - } -} - -ResultSet* MySQLConnection::Query(const char* sql) -{ - if (!sql) - return nullptr; - - MYSQL_RES* result = nullptr; - MYSQL_FIELD* fields = nullptr; - uint64 rowCount = 0; - uint32 fieldCount = 0; - - if (!_Query(sql, &result, &fields, &rowCount, &fieldCount)) - return nullptr; - - return new ResultSet(result, fields, rowCount, fieldCount); -} - -bool MySQLConnection::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount) +bool MySQLConnection::Execute(char const* sql) { if (!m_Mysql) return false; @@ -310,23 +186,167 @@ bool MySQLConnection::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** if (mysql_query(m_Mysql, sql)) { uint32 lErrno = mysql_errno(m_Mysql); - LOG_ERROR("sql.sql", "SQL: %s", sql); - LOG_ERROR("sql.sql", "ERROR: [%u] %s", lErrno, mysql_error(m_Mysql)); + + LOG_INFO("sql.sql", "SQL: %s", sql); + LOG_ERROR("sql.sql", "[%u] %s", lErrno, mysql_error(m_Mysql)); + + if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) + return Execute(sql); // Try again + + return false; + } + else + LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql); + } + + return true; +} + +bool MySQLConnection::Execute(PreparedStatementBase* stmt) +{ + if (!m_Mysql) + return false; + + uint32 index = stmt->GetIndex(); + + MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index); + ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query + + m_mStmt->BindParameters(stmt); + + MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT(); + MYSQL_BIND* msql_BIND = m_mStmt->GetBind(); + + uint32 _s = getMSTime(); + + if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) + { + uint32 lErrno = mysql_errno(m_Mysql); + LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT)); + + if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) + return Execute(stmt); // Try again + + m_mStmt->ClearParameters(); + return false; + } + + if (mysql_stmt_execute(msql_STMT)) + { + uint32 lErrno = mysql_errno(m_Mysql); + LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT)); + + if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) + return Execute(stmt); // Try again + + m_mStmt->ClearParameters(); + return false; + } + + LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString().c_str()); + + m_mStmt->ClearParameters(); + return true; +} + +bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement** mysqlStmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount) +{ + if (!m_Mysql) + return false; + + uint32 index = stmt->GetIndex(); + + MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index); + ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query + + m_mStmt->BindParameters(stmt); + *mysqlStmt = m_mStmt; + + MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT(); + MYSQL_BIND* msql_BIND = m_mStmt->GetBind(); + + uint32 _s = getMSTime(); + + if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) + { + uint32 lErrno = mysql_errno(m_Mysql); + LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT)); + + if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) + return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again + + m_mStmt->ClearParameters(); + return false; + } + + if (mysql_stmt_execute(msql_STMT)) + { + uint32 lErrno = mysql_errno(m_Mysql); + LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", + m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT)); + + if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) + return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again + + m_mStmt->ClearParameters(); + return false; + } + + LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString().c_str()); + + m_mStmt->ClearParameters(); + + *pResult = reinterpret_cast(mysql_stmt_result_metadata(msql_STMT)); + *pRowCount = mysql_stmt_num_rows(msql_STMT); + *pFieldCount = mysql_stmt_field_count(msql_STMT); + + return true; +} + +ResultSet* MySQLConnection::Query(char const* sql) +{ + if (!sql) + return nullptr; + + MySQLResult* result = nullptr; + MySQLField* fields = nullptr; + uint64 rowCount = 0; + uint32 fieldCount = 0; + + if (!_Query(sql, &result, &fields, &rowCount, &fieldCount)) + return nullptr; + + return new ResultSet(result, fields, rowCount, fieldCount); +} + +bool MySQLConnection::_Query(const char* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount) +{ + if (!m_Mysql) + return false; + + { + uint32 _s = getMSTime(); + + if (mysql_query(m_Mysql, sql)) + { + uint32 lErrno = mysql_errno(m_Mysql); + LOG_INFO("sql.sql", "SQL: %s", sql); + LOG_ERROR("sql.sql", "[%u] %s", lErrno, mysql_error(m_Mysql)); if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection) return _Query(sql, pResult, pFields, pRowCount, pFieldCount); // We try again return false; } + else + LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql); - LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql); - - *pResult = mysql_store_result(m_Mysql); + *pResult = reinterpret_cast(mysql_store_result(m_Mysql)); *pRowCount = mysql_affected_rows(m_Mysql); *pFieldCount = mysql_field_count(m_Mysql); } - if (!*pResult) + if (!*pResult ) return false; if (!*pRowCount) @@ -335,7 +355,7 @@ bool MySQLConnection::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** return false; } - *pFields = mysql_fetch_fields(*pResult); + *pFields = reinterpret_cast(mysql_fetch_fields(*pResult)); return true; } @@ -355,27 +375,26 @@ void MySQLConnection::CommitTransaction() Execute("COMMIT"); } -int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) +int MySQLConnection::ExecuteTransaction(std::shared_ptr transaction) { - std::list const& queries = transaction->m_queries; + std::vector const& queries = transaction->m_queries; if (queries.empty()) return -1; BeginTransaction(); - std::list::const_iterator itr; - for (itr = queries.begin(); itr != queries.end(); ++itr) + for (auto itr = queries.begin(); itr != queries.end(); ++itr) { SQLElementData const& data = *itr; switch (itr->type) { case SQL_ELEMENT_PREPARED: { - PreparedStatement* stmt = data.element.stmt; + PreparedStatementBase* stmt = data.element.stmt; ASSERT(stmt); if (!Execute(stmt)) { - LOG_INFO("sql.driver", "[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size()); + LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size()); int errorCode = GetLastError(); RollbackTransaction(); return errorCode; @@ -384,11 +403,11 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) break; case SQL_ELEMENT_RAW: { - const char* sql = data.element.query; + char const* sql = data.element.query; ASSERT(sql); if (!Execute(sql)) { - LOG_INFO("sql.driver", "[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size()); + LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size()); int errorCode = GetLastError(); RollbackTransaction(); return errorCode; @@ -407,103 +426,157 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) return 0; } +size_t MySQLConnection::EscapeString(char* to, const char* from, size_t length) +{ + return mysql_real_escape_string(m_Mysql, to, from, length); +} + +void MySQLConnection::Ping() +{ + mysql_ping(m_Mysql); +} + +uint32 MySQLConnection::GetLastError() +{ + return mysql_errno(m_Mysql); +} + +bool MySQLConnection::LockIfReady() +{ + return m_Mutex.try_lock(); +} + +void MySQLConnection::Unlock() +{ + m_Mutex.unlock(); +} + +uint32 MySQLConnection::GetServerVersion() const +{ + return mysql_get_server_version(m_Mysql); +} + MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index) { - ASSERT(index < m_stmts.size()); - MySQLPreparedStatement* ret = m_stmts[index]; + ASSERT(index < m_stmts.size(), "Tried to access invalid prepared statement index %u (max index " SZFMTD ") on database `%s`, connection type: %s", + index, m_stmts.size(), m_connectionInfo.database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous"); + MySQLPreparedStatement* ret = m_stmts[index].get(); if (!ret) - LOG_INFO("sql.driver", "ERROR: Could not fetch prepared statement %u on database `%s`, connection type: %s.", - index, m_connectionInfo.database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous"); + LOG_ERROR("sql.sql", "Could not fetch prepared statement %u on database `%s`, connection type: %s.", + index, m_connectionInfo.database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous"); return ret; } -void MySQLConnection::PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags) +void MySQLConnection::PrepareStatement(uint32 index, std::string const& sql, ConnectionFlags flags) { - m_queries.insert(PreparedStatementMap::value_type(index, std::make_pair(sql, flags))); - - // For reconnection case - if (m_reconnecting) - delete m_stmts[index]; - // Check if specified query should be prepared on this connection // i.e. don't prepare async statements on synchronous connections // to save memory that will not be used. if (!(m_connectionFlags & flags)) { - m_stmts[index] = nullptr; + m_stmts[index].reset(); return; } MYSQL_STMT* stmt = mysql_stmt_init(m_Mysql); if (!stmt) { - LOG_INFO("sql.driver", "[ERROR]: In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql); - LOG_INFO("sql.driver", "[ERROR]: %s", mysql_error(m_Mysql)); + LOG_ERROR("sql.sql", "In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql.c_str()); + LOG_ERROR("sql.sql", "%s", mysql_error(m_Mysql)); m_prepareError = true; } else { - if (mysql_stmt_prepare(stmt, sql, static_cast(strlen(sql)))) + if (mysql_stmt_prepare(stmt, sql.c_str(), static_cast(sql.size()))) { - LOG_INFO("sql.driver", "[ERROR]: In mysql_stmt_prepare() id: %u, sql: \"%s\"", index, sql); - LOG_INFO("sql.driver", "[ERROR]: %s", mysql_stmt_error(stmt)); + LOG_ERROR("sql.sql", "In mysql_stmt_prepare() id: %u, sql: \"%s\"", index, sql.c_str()); + LOG_ERROR("sql.sql", "%s", mysql_stmt_error(stmt)); mysql_stmt_close(stmt); m_prepareError = true; } else - { - MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt); - m_stmts[index] = mStmt; - } + m_stmts[index] = std::make_unique(reinterpret_cast(stmt), sql); } } -PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt) +PreparedResultSet* MySQLConnection::Query(PreparedStatementBase* stmt) { - MYSQL_RES* result = nullptr; + MySQLPreparedStatement* mysqlStmt = nullptr; + MySQLResult* result = nullptr; uint64 rowCount = 0; uint32 fieldCount = 0; - if (!_Query(stmt, &result, &rowCount, &fieldCount)) + if (!_Query(stmt, &mysqlStmt, &result, &rowCount, &fieldCount)) return nullptr; if (mysql_more_results(m_Mysql)) { mysql_next_result(m_Mysql); } - return new PreparedResultSet(stmt->m_stmt->GetSTMT(), result, rowCount, fieldCount); + return new PreparedResultSet(mysqlStmt->GetSTMT(), result, rowCount, fieldCount); } -bool MySQLConnection::_HandleMySQLErrno(uint32 errNo) +bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/) { switch (errNo) { case CR_SERVER_GONE_ERROR: case CR_SERVER_LOST: case CR_SERVER_LOST_EXTENDED: -#if !(MARIADB_VERSION_ID >= 100200) - case CR_INVALID_CONN_HANDLE: -#endif { - m_reconnecting = true; - uint64 oldThreadId = mysql_thread_id(GetHandle()); - mysql_close(GetHandle()); - if (this->Open()) // Don't remove 'this' pointer unless you want to skip loading all prepared statements.... + if (m_Mysql) { - LOG_INFO("sql.driver", "Connection to the MySQL server is active."); - if (oldThreadId != mysql_thread_id(GetHandle())) - LOG_INFO("sql.driver", "Successfully reconnected to %s @%s:%s (%s).", - m_connectionInfo.database.c_str(), m_connectionInfo.host.c_str(), m_connectionInfo.port_or_socket.c_str(), - (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous"); + LOG_ERROR("sql.sql", "Lost the connection to the MySQL server!"); + + mysql_close(m_Mysql); + m_Mysql = nullptr; + } + [[fallthrough]]; + } + case CR_CONN_HOST_ERROR: + { + LOG_INFO("sql.sql", "Attempting to reconnect to the MySQL server..."); + + m_reconnecting = true; + + uint32 const lErrno = Open(); + if (!lErrno) + { + // Don't remove 'this' pointer unless you want to skip loading all prepared statements... + if (!this->PrepareStatements()) + { + LOG_FATAL("sql.sql", "Could not re-prepare statements!"); + std::this_thread::sleep_for(std::chrono::seconds(10)); + std::abort(); + } + + LOG_INFO("sql.sql", "Successfully reconnected to %s @%s:%s (%s).", + m_connectionInfo.database.c_str(), m_connectionInfo.host.c_str(), m_connectionInfo.port_or_socket.c_str(), + (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous"); m_reconnecting = false; return true; } - uint32 lErrno = mysql_errno(GetHandle()); // It's possible this attempted reconnect throws 2006 at us. To prevent crazy recursive calls, sleep here. - std::this_thread::sleep_for(3s); // Sleep 3 seconds - return _HandleMySQLErrno(lErrno); // Call self (recursive) + if ((--attempts) == 0) + { + // Shut down the server when the mysql server isn't + // reachable for some time + LOG_FATAL("sql.sql", "Failed to reconnect to the MySQL server, " + "terminating the server to prevent data corruption!"); + + // We could also initiate a shutdown through using std::raise(SIGTERM) + std::this_thread::sleep_for(std::chrono::seconds(10)); + std::abort(); + } + else + { + // It's possible this attempted reconnect throws 2006 at us. + // To prevent crazy recursive calls, sleep here. + std::this_thread::sleep_for(std::chrono::seconds(3)); // Sleep 3 seconds + return _HandleMySQLErrno(lErrno, attempts); // Call self (recursive) + } } case ER_LOCK_DEADLOCK: @@ -516,17 +589,17 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo) // Outdated table or database structure - terminate core case ER_BAD_FIELD_ERROR: case ER_NO_SUCH_TABLE: - LOG_ERROR("server", "Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders."); - std::this_thread::sleep_for(10s); + LOG_ERROR("sql.sql", "Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders."); + std::this_thread::sleep_for(std::chrono::seconds(10)); std::abort(); return false; case ER_PARSE_ERROR: - LOG_ERROR("server", "Error while parsing SQL. Core fix required."); - std::this_thread::sleep_for(10s); + LOG_ERROR("sql.sql", "Error while parsing SQL. Core fix required."); + std::this_thread::sleep_for(std::chrono::seconds(10)); std::abort(); return false; default: - LOG_ERROR("server", "Unhandled MySQL errno %u. Unexpected behaviour possible.", errNo); + LOG_ERROR("sql.sql", "Unhandled MySQL errno %u. Unexpected behaviour possible.", errNo); return false; } } diff --git a/src/server/database/Database/MySQLConnection.h b/src/server/database/Database/MySQLConnection.h index 5b8f22689..777cf164c 100644 --- a/src/server/database/Database/MySQLConnection.h +++ b/src/server/database/Database/MySQLConnection.h @@ -1,22 +1,25 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ -#include - -#include "DatabaseWorkerPool.h" -#include "Transaction.h" -#include "Util.h" - #ifndef _MYSQLCONNECTION_H #define _MYSQLCONNECTION_H +#include "DatabaseEnvFwd.h" +#include "Define.h" +#include +#include +#include +#include +#include + +template +class ProducerConsumerQueue; + class DatabaseWorker; -class PreparedStatement; class MySQLPreparedStatement; -class PingOperation; +class SQLOperation; enum ConnectionFlags { @@ -25,99 +28,75 @@ enum ConnectionFlags CONNECTION_BOTH = CONNECTION_ASYNC | CONNECTION_SYNCH }; -struct MySQLConnectionInfo +struct AC_DATABASE_API MySQLConnectionInfo { - MySQLConnectionInfo() = default; - MySQLConnectionInfo(const std::string& infoString) - { - Tokenizer tokens(infoString, ';'); - - if (tokens.size() != 5) - return; - - uint8 i = 0; - - host.assign(tokens[i++]); - port_or_socket.assign(tokens[i++]); - user.assign(tokens[i++]); - password.assign(tokens[i++]); - database.assign(tokens[i++]); - } + explicit MySQLConnectionInfo(std::string const& infoString); std::string user; std::string password; std::string database; std::string host; std::string port_or_socket; + std::string ssl; }; -typedef std::map> PreparedStatementMap; - -class MySQLConnection +class AC_DATABASE_API MySQLConnection { - template friend class DatabaseWorkerPool; - friend class PingOperation; +template friend class DatabaseWorkerPool; +friend class PingOperation; public: MySQLConnection(MySQLConnectionInfo& connInfo); //! Constructor for synchronous connections. - MySQLConnection(ACE_Activation_Queue* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections. + MySQLConnection(ProducerConsumerQueue* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections. virtual ~MySQLConnection(); virtual uint32 Open(); void Close(); + bool PrepareStatements(); -public: - bool Execute(const char* sql); - bool Execute(PreparedStatement* stmt); - ResultSet* Query(const char* sql); - PreparedResultSet* Query(PreparedStatement* stmt); - bool _Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount); - bool _Query(PreparedStatement* stmt, MYSQL_RES** pResult, uint64* pRowCount, uint32* pFieldCount); + bool Execute(char const* sql); + bool Execute(PreparedStatementBase* stmt); + ResultSet* Query(char const* sql); + PreparedResultSet* Query(PreparedStatementBase* stmt); + bool _Query(char const* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount); + bool _Query(PreparedStatementBase* stmt, MySQLPreparedStatement** mysqlStmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount); void BeginTransaction(); void RollbackTransaction(); void CommitTransaction(); - int ExecuteTransaction(SQLTransaction& transaction); + int ExecuteTransaction(std::shared_ptr transaction); + size_t EscapeString(char* to, const char* from, size_t length); + void Ping(); - operator bool () const { return m_Mysql != nullptr; } - void Ping() { mysql_ping(m_Mysql); } - - uint32 GetLastError() { return mysql_errno(m_Mysql); } + uint32 GetLastError(); protected: - bool LockIfReady() - { - /// Tries to acquire lock. If lock is acquired by another thread - /// the calling parent will just try another connection - return m_Mutex.try_lock(); - } + /// Tries to acquire lock. If lock is acquired by another thread + /// the calling parent will just try another connection + bool LockIfReady(); - void Unlock() - { - /// Called by parent databasepool. Will let other threads access this connection - m_Mutex.unlock(); - } + /// Called by parent databasepool. Will let other threads access this connection + void Unlock(); - MYSQL* GetHandle() { return m_Mysql; } + uint32 GetServerVersion() const; MySQLPreparedStatement* GetPreparedStatement(uint32 index); - void PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags); + void PrepareStatement(uint32 index, std::string const& sql, ConnectionFlags flags); virtual void DoPrepareStatements() = 0; -protected: - std::vector m_stmts; //! PreparedStatements storage - PreparedStatementMap m_queries; //! Query storage + typedef std::vector> PreparedStatementContainer; + + PreparedStatementContainer m_stmts; //! PreparedStatements storage bool m_reconnecting; //! Are we reconnecting? bool m_prepareError; //! Was there any error while preparing statements? private: - bool _HandleMySQLErrno(uint32 errNo); + bool _HandleMySQLErrno(uint32 errNo, uint8 attempts = 5); -private: - ACE_Activation_Queue* m_queue; //! Queue shared with other asynchronous connections. - DatabaseWorker* m_worker; //! Core worker task. - MYSQL* m_Mysql; //! MySQL Handle. + ProducerConsumerQueue* m_queue; //! Queue shared with other asynchronous connections. + std::unique_ptr m_worker; //! Core worker task. + MySQLHandle* m_Mysql; //! MySQL Handle. MySQLConnectionInfo& m_connectionInfo; //! Connection info (used for logging) ConnectionFlags m_connectionFlags; //! Connection flags (for preparing relevant statements) std::mutex m_Mutex; diff --git a/src/server/database/Database/MySQLHacks.h b/src/server/database/Database/MySQLHacks.h new file mode 100644 index 000000000..9e914eb7b --- /dev/null +++ b/src/server/database/Database/MySQLHacks.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef MySQLHacks_h__ +#define MySQLHacks_h__ + +#include "MySQLWorkaround.h" +#include + +struct MySQLHandle : MYSQL { }; +struct MySQLResult : MYSQL_RES { }; +struct MySQLField : MYSQL_FIELD { }; +struct MySQLBind : MYSQL_BIND { }; +struct MySQLStmt : MYSQL_STMT { }; + +// mysql 8 removed my_bool typedef (it was char) and started using bools directly +// to maintain compatibility we use this trick to retrieve which type is being used +using MySQLBool = std::remove_pointer_t().is_null)>; + +#endif // MySQLHacks_h__ diff --git a/src/server/database/Database/MySQLPreparedStatement.cpp b/src/server/database/Database/MySQLPreparedStatement.cpp new file mode 100644 index 000000000..766cfa5c8 --- /dev/null +++ b/src/server/database/Database/MySQLPreparedStatement.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#include "MySQLPreparedStatement.h" +#include "Errors.h" +#include "Log.h" +#include "MySQLHacks.h" +#include "PreparedStatement.h" + +template +struct MySQLType { }; + +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; +template<> struct MySQLType : std::integral_constant { }; + +MySQLPreparedStatement::MySQLPreparedStatement(MySQLStmt* stmt, std::string queryString) : + m_stmt(nullptr), m_Mstmt(stmt), m_bind(nullptr), m_queryString(std::move(queryString)) +{ + /// Initialize variable parameters + m_paramCount = mysql_stmt_param_count(stmt); + m_paramsSet.assign(m_paramCount, false); + m_bind = new MySQLBind[m_paramCount]; + memset(m_bind, 0, sizeof(MySQLBind) * m_paramCount); + + /// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value." + MySQLBool bool_tmp = MySQLBool(1); + mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp); +} + +MySQLPreparedStatement::~MySQLPreparedStatement() +{ + ClearParameters(); + if (m_Mstmt->bind_result_done) + { + delete[] m_Mstmt->bind->length; + delete[] m_Mstmt->bind->is_null; + } + mysql_stmt_close(m_Mstmt); + delete[] m_bind; +} + +void MySQLPreparedStatement::BindParameters(PreparedStatementBase* stmt) +{ + m_stmt = stmt; // Cross reference them for debug output + + uint8 pos = 0; + for (PreparedStatementData const& data : stmt->GetParameters()) + { + std::visit([&](auto&& param) + { + SetParameter(pos, param); + }, data.data); + ++pos; + } +#ifdef _DEBUG + if (pos < m_paramCount) + LOG_WARN("sql.sql", "[WARNING]: BindParameters() for statement %u did not bind all allocated parameters", stmt->GetIndex()); +#endif +} + +void MySQLPreparedStatement::ClearParameters() +{ + for (uint32 i=0; i < m_paramCount; ++i) + { + delete m_bind[i].length; + m_bind[i].length = nullptr; + delete[] (char*) m_bind[i].buffer; + m_bind[i].buffer = nullptr; + m_paramsSet[i] = false; + } +} + +static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount) +{ + LOG_ERROR("sql.driver", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount); + return false; +} + +//- Bind on mysql level +void MySQLPreparedStatement::AssertValidIndex(uint8 index) +{ + ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->GetIndex(), index, m_paramCount)); + + if (m_paramsSet[index]) + LOG_ERROR("sql.sql", "[ERROR] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->GetIndex(), index); +} + +void MySQLPreparedStatement::SetParameter(uint8 index, std::nullptr_t) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + param->buffer_type = MYSQL_TYPE_NULL; + delete[] static_cast(param->buffer); + param->buffer = nullptr; + param->buffer_length = 0; + param->is_null_value = 1; + delete param->length; + param->length = nullptr; +} + +void MySQLPreparedStatement::SetParameter(uint8 index, bool value) +{ + SetParameter(index, uint8(value ? 1 : 0)); +} + +template +void MySQLPreparedStatement::SetParameter(uint8 index, T value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + uint32 len = uint32(sizeof(T)); + param->buffer_type = MySQLType::value; + delete[] static_cast(param->buffer); + param->buffer = new char[len]; + param->buffer_length = 0; + param->is_null_value = 0; + param->length = nullptr; // Only != NULL for strings + param->is_unsigned = std::is_unsigned_v; + + memcpy(param->buffer, &value, len); +} + +void MySQLPreparedStatement::SetParameter(uint8 index, std::string const& value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + uint32 len = uint32(value.size()); + param->buffer_type = MYSQL_TYPE_VAR_STRING; + delete [] static_cast(param->buffer); + param->buffer = new char[len]; + param->buffer_length = len; + param->is_null_value = 0; + delete param->length; + param->length = new unsigned long(len); + + memcpy(param->buffer, value.c_str(), len); +} + +void MySQLPreparedStatement::SetParameter(uint8 index, std::vector const& value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + uint32 len = uint32(value.size()); + param->buffer_type = MYSQL_TYPE_BLOB; + delete [] static_cast(param->buffer); + param->buffer = new char[len]; + param->buffer_length = len; + param->is_null_value = 0; + delete param->length; + param->length = new unsigned long(len); + + memcpy(param->buffer, value.data(), len); +} + +std::string MySQLPreparedStatement::getQueryString() const +{ + std::string queryString(m_queryString); + + size_t pos = 0; + for (PreparedStatementData const& data : m_stmt->GetParameters()) + { + pos = queryString.find('?', pos); + + std::string replaceStr = std::visit([&](auto&& data) + { + return PreparedStatementData::ToString(data); + }, data.data); + + queryString.replace(pos, 1, replaceStr); + pos += replaceStr.length(); + } + + return queryString; +} diff --git a/src/server/database/Database/MySQLPreparedStatement.h b/src/server/database/Database/MySQLPreparedStatement.h new file mode 100644 index 000000000..915c5da4f --- /dev/null +++ b/src/server/database/Database/MySQLPreparedStatement.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef MySQLPreparedStatement_h__ +#define MySQLPreparedStatement_h__ + +#include "DatabaseEnvFwd.h" +#include "Define.h" +#include "MySQLWorkaround.h" +#include +#include + +class MySQLConnection; +class PreparedStatementBase; + +//- Class of which the instances are unique per MySQLConnection +//- access to these class objects is only done when a prepared statement task +//- is executed. +class AC_DATABASE_API MySQLPreparedStatement +{ +friend class MySQLConnection; +friend class PreparedStatementBase; + +public: + MySQLPreparedStatement(MySQLStmt* stmt, std::string queryString); + ~MySQLPreparedStatement(); + + void BindParameters(PreparedStatementBase* stmt); + + uint32 GetParameterCount() const { return m_paramCount; } + +protected: + void SetParameter(uint8 index, std::nullptr_t); + void SetParameter(uint8 index, bool value); + template + void SetParameter(uint8 index, T value); + void SetParameter(uint8 index, std::string const& value); + void SetParameter(uint8 index, std::vector const& value); + + MySQLStmt* GetSTMT() { return m_Mstmt; } + MySQLBind* GetBind() { return m_bind; } + PreparedStatementBase* m_stmt; + void ClearParameters(); + void AssertValidIndex(uint8 index); + std::string getQueryString() const; + +private: + MySQLStmt* m_Mstmt; + uint32 m_paramCount; + std::vector m_paramsSet; + MySQLBind* m_bind; + std::string const m_queryString; + + MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete; + MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete; +}; + +#endif // MySQLPreparedStatement_h__ diff --git a/src/server/database/Database/MySQLThreading.cpp b/src/server/database/Database/MySQLThreading.cpp index 5d70ee4f8..70c35ddf8 100644 --- a/src/server/database/Database/MySQLThreading.cpp +++ b/src/server/database/Database/MySQLThreading.cpp @@ -1,11 +1,10 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2021 TrinityCore + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore */ #include "MySQLThreading.h" -#include "Log.h" -#include +#include "MySQLWorkaround.h" void MySQL::Library_Init() { diff --git a/src/server/database/Database/MySQLThreading.h b/src/server/database/Database/MySQLThreading.h index 8a26ba375..80d38aa15 100644 --- a/src/server/database/Database/MySQLThreading.h +++ b/src/server/database/Database/MySQLThreading.h @@ -1,19 +1,18 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _MYSQLTHREADING_H #define _MYSQLTHREADING_H -#include "Log.h" +#include "Define.h" namespace MySQL { - void Library_Init(); - void Library_End(); - uint32 GetLibraryVersion(); + AC_DATABASE_API void Library_Init(); + AC_DATABASE_API void Library_End(); + AC_DATABASE_API uint32 GetLibraryVersion(); } #endif diff --git a/src/server/database/Database/MySQLWorkaround.h b/src/server/database/Database/MySQLWorkaround.h new file mode 100644 index 000000000..b16a99207 --- /dev/null +++ b/src/server/database/Database/MySQLWorkaround.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 +#include +#endif +#include diff --git a/src/server/database/Database/PreparedStatement.cpp b/src/server/database/Database/PreparedStatement.cpp index 767d89442..842ee93b9 100644 --- a/src/server/database/Database/PreparedStatement.cpp +++ b/src/server/database/Database/PreparedStatement.cpp @@ -1,481 +1,126 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #include "PreparedStatement.h" -#include "MySQLConnection.h" +#include "Errors.h" #include "Log.h" -#include +#include "MySQLConnection.h" +#include "MySQLPreparedStatement.h" +#include "MySQLWorkaround.h" +#include "QueryResult.h" -PreparedStatement::PreparedStatement(uint32 index) : - m_stmt(nullptr), - m_index(index) -{ -} +PreparedStatementBase::PreparedStatementBase(uint32 index, uint8 capacity) : +m_index(index), statement_data(capacity) { } -PreparedStatement::~PreparedStatement() -{ -} - -void PreparedStatement::BindParameters() -{ - ASSERT (m_stmt); - - uint8 i = 0; - for (; i < statement_data.size(); i++) - { - switch (statement_data[i].type) - { - case TYPE_BOOL: - m_stmt->setBool(i, statement_data[i].data.boolean); - break; - case TYPE_UI8: - m_stmt->setUInt8(i, statement_data[i].data.ui8); - break; - case TYPE_UI16: - m_stmt->setUInt16(i, statement_data[i].data.ui16); - break; - case TYPE_UI32: - m_stmt->setUInt32(i, statement_data[i].data.ui32); - break; - case TYPE_I8: - m_stmt->setInt8(i, statement_data[i].data.i8); - break; - case TYPE_I16: - m_stmt->setInt16(i, statement_data[i].data.i16); - break; - case TYPE_I32: - m_stmt->setInt32(i, statement_data[i].data.i32); - break; - case TYPE_UI64: - m_stmt->setUInt64(i, statement_data[i].data.ui64); - break; - case TYPE_I64: - m_stmt->setInt64(i, statement_data[i].data.i64); - break; - case TYPE_FLOAT: - m_stmt->setFloat(i, statement_data[i].data.f); - break; - case TYPE_DOUBLE: - m_stmt->setDouble(i, statement_data[i].data.d); - break; - case TYPE_STRING: - m_stmt->setBinary(i, statement_data[i].binary, true); - break; - case TYPE_BINARY: - m_stmt->setBinary(i, statement_data[i].binary, false); - break; - case TYPE_NULL: - m_stmt->setNull(i); - break; - } - } -#ifdef _DEBUG - if (i < m_stmt->m_paramCount) - LOG_INFO("sql.driver", "[WARNING]: BindParameters() for statement %u did not bind all allocated parameters", m_index); -#endif -} +PreparedStatementBase::~PreparedStatementBase() { } //- Bind to buffer -void PreparedStatement::setBool(const uint8 index, const bool value) +void PreparedStatementBase::setBool(const uint8 index, const bool value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.boolean = value; - statement_data[index].type = TYPE_BOOL; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setUInt8(const uint8 index, const uint8 value) +void PreparedStatementBase::setUInt8(const uint8 index, const uint8 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.ui8 = value; - statement_data[index].type = TYPE_UI8; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setUInt16(const uint8 index, const uint16 value) +void PreparedStatementBase::setUInt16(const uint8 index, const uint16 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.ui16 = value; - statement_data[index].type = TYPE_UI16; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setUInt32(const uint8 index, const uint32 value) +void PreparedStatementBase::setUInt32(const uint8 index, const uint32 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.ui32 = value; - statement_data[index].type = TYPE_UI32; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setUInt64(const uint8 index, const uint64 value) +void PreparedStatementBase::setUInt64(const uint8 index, const uint64 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.ui64 = value; - statement_data[index].type = TYPE_UI64; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setInt8(const uint8 index, const int8 value) +void PreparedStatementBase::setInt8(const uint8 index, const int8 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.i8 = value; - statement_data[index].type = TYPE_I8; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setInt16(const uint8 index, const int16 value) +void PreparedStatementBase::setInt16(const uint8 index, const int16 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.i16 = value; - statement_data[index].type = TYPE_I16; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setInt32(const uint8 index, const int32 value) +void PreparedStatementBase::setInt32(const uint8 index, const int32 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.i32 = value; - statement_data[index].type = TYPE_I32; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setInt64(const uint8 index, const int64 value) +void PreparedStatementBase::setInt64(const uint8 index, const int64 value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.i64 = value; - statement_data[index].type = TYPE_I64; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setFloat(const uint8 index, const float value) +void PreparedStatementBase::setFloat(const uint8 index, const float value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.f = value; - statement_data[index].type = TYPE_FLOAT; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setDouble(const uint8 index, const double value) +void PreparedStatementBase::setDouble(const uint8 index, const double value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].data.d = value; - statement_data[index].type = TYPE_DOUBLE; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setString(const uint8 index, const std::string& value) +void PreparedStatementBase::setString(const uint8 index, const std::string& value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].binary.resize(value.length() + 1); - memcpy(statement_data[index].binary.data(), value.c_str(), value.length() + 1); - statement_data[index].type = TYPE_STRING; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -void PreparedStatement::setBinary(const uint8 index, const std::vector& value) +void PreparedStatementBase::setStringView(const uint8 index, const std::string_view value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].binary = value; - statement_data[index].type = TYPE_BINARY; + ASSERT(index < statement_data.size()); + statement_data[index].data.emplace(value); } -void PreparedStatement::setNull(const uint8 index) +void PreparedStatementBase::setBinary(const uint8 index, const std::vector& value) { - if (index >= statement_data.size()) - statement_data.resize(index + 1); - - statement_data[index].type = TYPE_NULL; + ASSERT(index < statement_data.size()); + statement_data[index].data = value; } -MySQLPreparedStatement::MySQLPreparedStatement(MYSQL_STMT* stmt) : - m_stmt(nullptr), - m_Mstmt(stmt), - m_bind(nullptr) +void PreparedStatementBase::setNull(const uint8 index) { - /// Initialize variable parameters - m_paramCount = mysql_stmt_param_count(stmt); - m_paramsSet.assign(m_paramCount, false); - m_bind = new MYSQL_BIND[m_paramCount]; - memset(m_bind, 0, sizeof(MYSQL_BIND)*m_paramCount); - - /// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value." - my_bool bool_tmp = 1; - mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp); -} - -MySQLPreparedStatement::~MySQLPreparedStatement() -{ - ClearParameters(); - if (m_Mstmt->bind_result_done) - { - delete[] m_Mstmt->bind->length; - delete[] m_Mstmt->bind->is_null; - } - mysql_stmt_close(m_Mstmt); - delete[] m_bind; -} - -void MySQLPreparedStatement::ClearParameters() -{ - for (uint32 i = 0; i < m_paramCount; ++i) - { - delete m_bind[i].length; - m_bind[i].length = nullptr; - delete[] (char*) m_bind[i].buffer; - m_bind[i].buffer = nullptr; - m_paramsSet[i] = false; - } -} - -static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount) -{ - LOG_ERROR("server", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount); - return false; -} - -//- Bind on mysql level -bool MySQLPreparedStatement::CheckValidIndex(uint8 index) -{ - ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->m_index, index, m_paramCount)); - - if (m_paramsSet[index]) - LOG_INFO("sql.driver", "[WARNING] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->m_index, index); - return true; -} - -void MySQLPreparedStatement::setBool(const uint8 index, const bool value) -{ - setUInt8(index, value ? 1 : 0); -} - -void MySQLPreparedStatement::setUInt8(const uint8 index, const uint8 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_TINY, &value, sizeof(uint8), true); -} - -void MySQLPreparedStatement::setUInt16(const uint8 index, const uint16 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(uint16), true); -} - -void MySQLPreparedStatement::setUInt32(const uint8 index, const uint32 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true); -} - -void MySQLPreparedStatement::setUInt64(const uint8 index, const uint64 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true); -} - -void MySQLPreparedStatement::setInt8(const uint8 index, const int8 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_TINY, &value, sizeof(int8), false); -} - -void MySQLPreparedStatement::setInt16(const uint8 index, const int16 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(int16), false); -} - -void MySQLPreparedStatement::setInt32(const uint8 index, const int32 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false); -} - -void MySQLPreparedStatement::setInt64(const uint8 index, const int64 value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false); -} - -void MySQLPreparedStatement::setFloat(const uint8 index, const float value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_FLOAT, &value, sizeof(float), (value > 0.0f)); -} - -void MySQLPreparedStatement::setDouble(const uint8 index, const double value) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f)); -} - -void MySQLPreparedStatement::setBinary(const uint8 index, const std::vector& value, bool isString) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - uint32 len = uint32(value.size()); - param->buffer_type = MYSQL_TYPE_BLOB; - delete [] static_cast(param->buffer); - param->buffer = new char[len]; - param->buffer_length = len; - param->is_null_value = 0; - delete param->length; - param->length = new unsigned long(len); - if (isString) - { - *param->length -= 1; - param->buffer_type = MYSQL_TYPE_VAR_STRING; - } - - memcpy(param->buffer, value.data(), len); -} - -void MySQLPreparedStatement::setNull(const uint8 index) -{ - CheckValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - param->buffer_type = MYSQL_TYPE_NULL; - delete [] static_cast(param->buffer); - param->buffer = nullptr; - param->buffer_length = 0; - param->is_null_value = 1; - delete param->length; - param->length = nullptr; -} - -void MySQLPreparedStatement::setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned) -{ - param->buffer_type = type; - delete [] static_cast(param->buffer); - param->buffer = new char[len]; - param->buffer_length = 0; - param->is_null_value = 0; - param->length = nullptr; // Only != nullptr for strings - param->is_unsigned = isUnsigned; - - memcpy(param->buffer, value, len); -} - -std::string MySQLPreparedStatement::getQueryString(std::string const& sqlPattern) const -{ - std::string queryString = sqlPattern; - - size_t pos = 0; - for (uint32 i = 0; i < m_stmt->statement_data.size(); i++) - { - pos = queryString.find('?', pos); - std::stringstream ss; - - switch (m_stmt->statement_data[i].type) - { - case TYPE_BOOL: - ss << uint16(m_stmt->statement_data[i].data.boolean); - break; - case TYPE_UI8: - ss << uint16(m_stmt->statement_data[i].data.ui8); // stringstream will append a character with that code instead of numeric representation - break; - case TYPE_UI16: - ss << m_stmt->statement_data[i].data.ui16; - break; - case TYPE_UI32: - ss << m_stmt->statement_data[i].data.ui32; - break; - case TYPE_I8: - ss << int16(m_stmt->statement_data[i].data.i8); // stringstream will append a character with that code instead of numeric representation - break; - case TYPE_I16: - ss << m_stmt->statement_data[i].data.i16; - break; - case TYPE_I32: - ss << m_stmt->statement_data[i].data.i32; - break; - case TYPE_UI64: - ss << m_stmt->statement_data[i].data.ui64; - break; - case TYPE_I64: - ss << m_stmt->statement_data[i].data.i64; - break; - case TYPE_FLOAT: - ss << m_stmt->statement_data[i].data.f; - break; - case TYPE_DOUBLE: - ss << m_stmt->statement_data[i].data.d; - break; - case TYPE_STRING: - ss << '\'' << (char const*)m_stmt->statement_data[i].binary.data() << '\''; - break; - case TYPE_BINARY: - ss << "BINARY"; - break; - case TYPE_NULL: - ss << "nullptr"; - break; - } - - std::string replaceStr = ss.str(); - queryString.replace(pos, 1, replaceStr); - pos += replaceStr.length(); - } - - return queryString; + ASSERT(index < statement_data.size()); + statement_data[index].data = nullptr; } //- Execution -PreparedStatementTask::PreparedStatementTask(PreparedStatement* stmt) : - m_stmt(stmt), - m_has_result(false) -{ -} - -PreparedStatementTask::PreparedStatementTask(PreparedStatement* stmt, PreparedQueryResultFuture result) : - m_stmt(stmt), - m_has_result(true), - m_result(result) +PreparedStatementTask::PreparedStatementTask(PreparedStatementBase* stmt, bool async) : +m_stmt(stmt), m_result(nullptr) { + m_has_result = async; // If it's async, then there's a result + if (async) + m_result = new PreparedQueryResultPromise(); } PreparedStatementTask::~PreparedStatementTask() { delete m_stmt; + if (m_has_result && m_result != nullptr) + delete m_result; } bool PreparedStatementTask::Execute() @@ -486,12 +131,58 @@ bool PreparedStatementTask::Execute() if (!result || !result->GetRowCount()) { delete result; - m_result.set(PreparedQueryResult(nullptr)); + m_result->set_value(PreparedQueryResult(nullptr)); return false; } - m_result.set(PreparedQueryResult(result)); + m_result->set_value(PreparedQueryResult(result)); return true; } return m_conn->Execute(m_stmt); } + +template +std::string PreparedStatementData::ToString(T value) +{ + return fmt::format("{}", value); +} + +std::string PreparedStatementData::ToString(bool value) +{ + return ToString(value); +} + +std::string PreparedStatementData::ToString(uint8 value) +{ + return ToString(value); +} + +template std::string PreparedStatementData::ToString(uint16); +template std::string PreparedStatementData::ToString(uint32); +template std::string PreparedStatementData::ToString(uint64); + +std::string PreparedStatementData::ToString(int8 value) +{ + return ToString(value); +} + +template std::string PreparedStatementData::ToString(int16); +template std::string PreparedStatementData::ToString(int32); +template std::string PreparedStatementData::ToString(int64); +template std::string PreparedStatementData::ToString(float); +template std::string PreparedStatementData::ToString(double); + +std::string PreparedStatementData::ToString(std::string const& value) +{ + return fmt::format("'{}'", value); +} + +std::string PreparedStatementData::ToString(std::vector const& /*value*/) +{ + return "BINARY"; +} + +std::string PreparedStatementData::ToString(std::nullptr_t) +{ + return "NULL"; +} diff --git a/src/server/database/Database/PreparedStatement.h b/src/server/database/Database/PreparedStatement.h index 64c7f979e..c1da632d5 100644 --- a/src/server/database/Database/PreparedStatement.h +++ b/src/server/database/Database/PreparedStatement.h @@ -1,75 +1,57 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _PREPAREDSTATEMENT_H #define _PREPAREDSTATEMENT_H +#include "Define.h" #include "SQLOperation.h" -#include - -#ifdef __APPLE__ -#undef TYPE_BOOL -#endif - -//- Union for data buffer (upper-level bind -> queue -> lower-level bind) -union PreparedStatementDataUnion -{ - bool boolean; - uint8 ui8; - int8 i8; - uint16 ui16; - int16 i16; - uint32 ui32; - int32 i32; - uint64 ui64; - int64 i64; - float f; - double d; -}; - -//- This enum helps us differ data held in above union -enum PreparedStatementValueType -{ - TYPE_BOOL, - TYPE_UI8, - TYPE_UI16, - TYPE_UI32, - TYPE_UI64, - TYPE_I8, - TYPE_I16, - TYPE_I32, - TYPE_I64, - TYPE_FLOAT, - TYPE_DOUBLE, - TYPE_STRING, - TYPE_BINARY, - TYPE_NULL -}; +#include +#include +#include struct PreparedStatementData { - PreparedStatementDataUnion data; - PreparedStatementValueType type; - std::vector binary; + std::variant< + bool, + uint8, + uint16, + uint32, + uint64, + int8, + int16, + int32, + int64, + float, + double, + std::string, + std::vector, + std::nullptr_t + > data; + + template + static std::string ToString(T value); + + static std::string ToString(bool value); + static std::string ToString(uint8 value); + static std::string ToString(int8 value); + static std::string ToString(std::string const& value); + static std::string ToString(std::vector const& value); + static std::string ToString(std::nullptr_t); }; -//- Forward declare -class MySQLPreparedStatement; - //- Upper-level class that is used in code -class PreparedStatement +class AC_DATABASE_API PreparedStatementBase { - friend class PreparedStatementTask; - friend class MySQLPreparedStatement; - friend class MySQLConnection; +friend class PreparedStatementTask; public: - explicit PreparedStatement(uint32 index); - ~PreparedStatement(); + explicit PreparedStatementBase(uint32 index, uint8 capacity); + virtual ~PreparedStatementBase(); + void setNull(const uint8 index); void setBool(const uint8 index, const bool value); void setUInt8(const uint8 index, const uint8 value); void setUInt16(const uint8 index, const uint16 value); @@ -82,84 +64,55 @@ public: void setFloat(const uint8 index, const float value); void setDouble(const uint8 index, const double value); void setString(const uint8 index, const std::string& value); + void setStringView(const uint8 index, const std::string_view value); void setBinary(const uint8 index, const std::vector& value); - template + template void setBinary(const uint8 index, std::array const& value) { std::vector vec(value.begin(), value.end()); setBinary(index, vec); } - void setNull(const uint8 index); + + uint32 GetIndex() const { return m_index; } + std::vector const& GetParameters() const { return statement_data; } protected: - void BindParameters(); - -protected: - MySQLPreparedStatement* m_stmt; uint32 m_index; - std::vector statement_data; //- Buffer of parameters, not tied to MySQL in any way yet + + //- Buffer of parameters, not tied to MySQL in any way yet + std::vector statement_data; + + PreparedStatementBase(PreparedStatementBase const& right) = delete; + PreparedStatementBase& operator=(PreparedStatementBase const& right) = delete; }; -//- Class of which the instances are unique per MySQLConnection -//- access to these class objects is only done when a prepared statement task -//- is executed. -class MySQLPreparedStatement +template +class PreparedStatement : public PreparedStatementBase { - friend class MySQLConnection; - friend class PreparedStatement; - public: - MySQLPreparedStatement(MYSQL_STMT* stmt); - ~MySQLPreparedStatement(); - - void setBool(const uint8 index, const bool value); - void setUInt8(const uint8 index, const uint8 value); - void setUInt16(const uint8 index, const uint16 value); - void setUInt32(const uint8 index, const uint32 value); - void setUInt64(const uint8 index, const uint64 value); - void setInt8(const uint8 index, const int8 value); - void setInt16(const uint8 index, const int16 value); - void setInt32(const uint8 index, const int32 value); - void setInt64(const uint8 index, const int64 value); - void setFloat(const uint8 index, const float value); - void setDouble(const uint8 index, const double value); - void setBinary(const uint8 index, const std::vector& value, bool isString); - void setNull(const uint8 index); - -protected: - MYSQL_STMT* GetSTMT() { return m_Mstmt; } - MYSQL_BIND* GetBind() { return m_bind; } - PreparedStatement* m_stmt; - void ClearParameters(); - bool CheckValidIndex(uint8 index); - [[nodiscard]] std::string getQueryString(std::string const& sqlPattern) const; + explicit PreparedStatement(uint32 index, uint8 capacity) : PreparedStatementBase(index, capacity) + { + } private: - void setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned); - -private: - MYSQL_STMT* m_Mstmt; - uint32 m_paramCount; - std::vector m_paramsSet; - MYSQL_BIND* m_bind; + PreparedStatement(PreparedStatement const& right) = delete; + PreparedStatement& operator=(PreparedStatement const& right) = delete; }; -typedef ACE_Future PreparedQueryResultFuture; - //- Lower-level class, enqueuable operation -class PreparedStatementTask : public SQLOperation +class AC_DATABASE_API PreparedStatementTask : public SQLOperation { public: - PreparedStatementTask(PreparedStatement* stmt); - PreparedStatementTask(PreparedStatement* stmt, PreparedQueryResultFuture result); - ~PreparedStatementTask() override; + PreparedStatementTask(PreparedStatementBase* stmt, bool async = false); + ~PreparedStatementTask(); bool Execute() override; + PreparedQueryResultFuture GetFuture() { return m_result->get_future(); } protected: - PreparedStatement* m_stmt; + PreparedStatementBase* m_stmt; bool m_has_result; - PreparedQueryResultFuture m_result; + PreparedQueryResultPromise* m_result; }; #endif diff --git a/src/server/database/Database/QueryCallback.cpp b/src/server/database/Database/QueryCallback.cpp new file mode 100644 index 000000000..56ada4f02 --- /dev/null +++ b/src/server/database/Database/QueryCallback.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#include "QueryCallback.h" +#include "Errors.h" + +template +inline void Construct(T& t, Args&&... args) +{ + new (&t) T(std::forward(args)...); +} + +template +inline void Destroy(T& t) +{ + t.~T(); +} + +template +inline void ConstructActiveMember(T* obj) +{ + if (!obj->_isPrepared) + Construct(obj->_string); + else + Construct(obj->_prepared); +} + +template +inline void DestroyActiveMember(T* obj) +{ + if (!obj->_isPrepared) + Destroy(obj->_string); + else + Destroy(obj->_prepared); +} + +template +inline void MoveFrom(T* to, T&& from) +{ + ASSERT(to->_isPrepared == from._isPrepared); + + if (!to->_isPrepared) + to->_string = std::move(from._string); + else + to->_prepared = std::move(from._prepared); +} + +struct QueryCallback::QueryCallbackData +{ +public: + friend class QueryCallback; + + QueryCallbackData(std::function&& callback) : _string(std::move(callback)), _isPrepared(false) { } + QueryCallbackData(std::function&& callback) : _prepared(std::move(callback)), _isPrepared(true) { } + QueryCallbackData(QueryCallbackData&& right) + { + _isPrepared = right._isPrepared; + ConstructActiveMember(this); + MoveFrom(this, std::move(right)); + } + QueryCallbackData& operator=(QueryCallbackData&& right) + { + if (this != &right) + { + if (_isPrepared != right._isPrepared) + { + DestroyActiveMember(this); + _isPrepared = right._isPrepared; + ConstructActiveMember(this); + } + MoveFrom(this, std::move(right)); + } + return *this; + } + ~QueryCallbackData() { DestroyActiveMember(this); } + +private: + QueryCallbackData(QueryCallbackData const&) = delete; + QueryCallbackData& operator=(QueryCallbackData const&) = delete; + + template friend void ConstructActiveMember(T* obj); + template friend void DestroyActiveMember(T* obj); + template friend void MoveFrom(T* to, T&& from); + + union + { + std::function _string; + std::function _prepared; + }; + bool _isPrepared; +}; + +// Not using initialization lists to work around segmentation faults when compiling with clang without precompiled headers +QueryCallback::QueryCallback(std::future&& result) +{ + _isPrepared = false; + Construct(_string, std::move(result)); +} + +QueryCallback::QueryCallback(std::future&& result) +{ + _isPrepared = true; + Construct(_prepared, std::move(result)); +} + +QueryCallback::QueryCallback(QueryCallback&& right) +{ + _isPrepared = right._isPrepared; + ConstructActiveMember(this); + MoveFrom(this, std::move(right)); + _callbacks = std::move(right._callbacks); +} + +QueryCallback& QueryCallback::operator=(QueryCallback&& right) +{ + if (this != &right) + { + if (_isPrepared != right._isPrepared) + { + DestroyActiveMember(this); + _isPrepared = right._isPrepared; + ConstructActiveMember(this); + } + MoveFrom(this, std::move(right)); + _callbacks = std::move(right._callbacks); + } + return *this; +} + +QueryCallback::~QueryCallback() +{ + DestroyActiveMember(this); +} + +QueryCallback&& QueryCallback::WithCallback(std::function&& callback) +{ + return WithChainingCallback([callback](QueryCallback& /*this*/, QueryResult result) { callback(std::move(result)); }); +} + +QueryCallback&& QueryCallback::WithPreparedCallback(std::function&& callback) +{ + return WithChainingPreparedCallback([callback](QueryCallback& /*this*/, PreparedQueryResult result) { callback(std::move(result)); }); +} + +QueryCallback&& QueryCallback::WithChainingCallback(std::function&& callback) +{ + ASSERT(!_callbacks.empty() || !_isPrepared, "Attempted to set callback function for string query on a prepared async query"); + _callbacks.emplace(std::move(callback)); + return std::move(*this); +} + +QueryCallback&& QueryCallback::WithChainingPreparedCallback(std::function&& callback) +{ + ASSERT(!_callbacks.empty() || _isPrepared, "Attempted to set callback function for prepared query on a string async query"); + _callbacks.emplace(std::move(callback)); + return std::move(*this); +} + +void QueryCallback::SetNextQuery(QueryCallback&& next) +{ + MoveFrom(this, std::move(next)); +} + +bool QueryCallback::InvokeIfReady() +{ + QueryCallbackData& callback = _callbacks.front(); + auto checkStateAndReturnCompletion = [this]() + { + _callbacks.pop(); + bool hasNext = !_isPrepared ? _string.valid() : _prepared.valid(); + if (_callbacks.empty()) + { + ASSERT(!hasNext); + return true; + } + + // abort chain + if (!hasNext) + return true; + + ASSERT(_isPrepared == _callbacks.front()._isPrepared); + return false; + }; + + if (!_isPrepared) + { + if (_string.valid() && _string.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + QueryResultFuture f(std::move(_string)); + std::function cb(std::move(callback._string)); + cb(*this, f.get()); + return checkStateAndReturnCompletion(); + } + } + else + { + if (_prepared.valid() && _prepared.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + PreparedQueryResultFuture f(std::move(_prepared)); + std::function cb(std::move(callback._prepared)); + cb(*this, f.get()); + return checkStateAndReturnCompletion(); + } + } + + return false; +} diff --git a/src/server/database/Database/QueryCallback.h b/src/server/database/Database/QueryCallback.h new file mode 100644 index 000000000..44e46fd47 --- /dev/null +++ b/src/server/database/Database/QueryCallback.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef _QUERY_CALLBACK_H +#define _QUERY_CALLBACK_H + +#include "DatabaseEnvFwd.h" +#include "Define.h" +#include +#include +#include +#include +#include + +class AC_DATABASE_API QueryCallback +{ +public: + explicit QueryCallback(QueryResultFuture&& result); + explicit QueryCallback(PreparedQueryResultFuture&& result); + QueryCallback(QueryCallback&& right); + QueryCallback& operator=(QueryCallback&& right); + ~QueryCallback(); + + QueryCallback&& WithCallback(std::function&& callback); + QueryCallback&& WithPreparedCallback(std::function&& callback); + + QueryCallback&& WithChainingCallback(std::function&& callback); + QueryCallback&& WithChainingPreparedCallback(std::function&& callback); + + // Moves std::future from next to this object + void SetNextQuery(QueryCallback&& next); + + // returns true when completed + bool InvokeIfReady(); + +private: + QueryCallback(QueryCallback const& right) = delete; + QueryCallback& operator=(QueryCallback const& right) = delete; + + template friend void ConstructActiveMember(T* obj); + template friend void DestroyActiveMember(T* obj); + template friend void MoveFrom(T* to, T&& from); + + union + { + QueryResultFuture _string; + PreparedQueryResultFuture _prepared; + }; + bool _isPrepared; + + struct QueryCallbackData; + std::queue> _callbacks; +}; + +#endif // _QUERY_CALLBACK_H diff --git a/src/server/database/Database/QueryHolder.cpp b/src/server/database/Database/QueryHolder.cpp index 5aee00031..61cfe73a3 100644 --- a/src/server/database/Database/QueryHolder.cpp +++ b/src/server/database/Database/QueryHolder.cpp @@ -1,109 +1,37 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ -#include "MySQLConnection.h" #include "QueryHolder.h" -#include "PreparedStatement.h" +#include "Errors.h" #include "Log.h" +#include "MySQLConnection.h" +#include "PreparedStatement.h" +#include "QueryResult.h" -bool SQLQueryHolder::SetQuery(size_t index, const char* sql) +bool SQLQueryHolderBase::SetPreparedQueryImpl(size_t index, PreparedStatementBase* stmt) { if (m_queries.size() <= index) { - LOG_ERROR("server", "Query index (%u) out of range (size: %u) for query: %s", uint32(index), (uint32)m_queries.size(), sql); + LOG_ERROR("sql.sql", "Query index (%u) out of range (size: %u) for prepared statement", uint32(index), (uint32)m_queries.size()); return false; } - /// not executed yet, just stored (it's not called a holder for nothing) - SQLElementData element; - element.type = SQL_ELEMENT_RAW; - element.element.query = strdup(sql); - - SQLResultSetUnion result; - result.qresult = nullptr; - - m_queries[index] = SQLResultPair(element, result); + m_queries[index].first = stmt; return true; } -bool SQLQueryHolder::SetPQuery(size_t index, const char* format, ...) -{ - if (!format) - { - LOG_ERROR("server", "Query (index: %u) is empty.", uint32(index)); - return false; - } - - va_list ap; - char szQuery [MAX_QUERY_LEN]; - va_start(ap, format); - int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap); - va_end(ap); - - if (res == -1) - { - LOG_ERROR("server", "SQL Query truncated (and not execute) for format: %s", format); - return false; - } - - return SetQuery(index, szQuery); -} - -bool SQLQueryHolder::SetPreparedQuery(size_t index, PreparedStatement* stmt) -{ - if (m_queries.size() <= index) - { - LOG_ERROR("server", "Query index (%u) out of range (size: %u) for prepared statement", uint32(index), (uint32)m_queries.size()); - return false; - } - - /// not executed yet, just stored (it's not called a holder for nothing) - SQLElementData element; - element.type = SQL_ELEMENT_PREPARED; - element.element.stmt = stmt; - - SQLResultSetUnion result; - result.presult = nullptr; - - m_queries[index] = SQLResultPair(element, result); - return true; -} - -QueryResult SQLQueryHolder::GetResult(size_t index) -{ - // Don't call to this function if the index is of an ad-hoc statement - if (index < m_queries.size()) - { - ResultSet* result = m_queries[index].second.qresult; - if (!result || !result->GetRowCount()) - return QueryResult(nullptr); - - result->NextRow(); - return QueryResult(result); - } - else - return QueryResult(nullptr); -} - -PreparedQueryResult SQLQueryHolder::GetPreparedResult(size_t index) +PreparedQueryResult SQLQueryHolderBase::GetPreparedResult(size_t index) const { // Don't call to this function if the index is of a prepared statement - if (index < m_queries.size()) - { - PreparedResultSet* result = m_queries[index].second.presult; - if (!result || !result->GetRowCount()) - return PreparedQueryResult(nullptr); + ASSERT(index < m_queries.size(), "Query holder result index out of range, tried to access index " SZFMTD " but there are only " SZFMTD " results", + index, m_queries.size()); - return PreparedQueryResult(result); - } - else - return PreparedQueryResult(nullptr); + return m_queries[index].second; } -void SQLQueryHolder::SetResult(size_t index, ResultSet* result) +void SQLQueryHolderBase::SetPreparedResult(size_t index, PreparedResultSet* result) { if (result && !result->GetRowCount()) { @@ -113,85 +41,45 @@ void SQLQueryHolder::SetResult(size_t index, ResultSet* result) /// store the result in the holder if (index < m_queries.size()) - m_queries[index].second.qresult = result; + m_queries[index].second = PreparedQueryResult(result); } -void SQLQueryHolder::SetPreparedResult(size_t index, PreparedResultSet* result) +SQLQueryHolderBase::~SQLQueryHolderBase() { - if (result && !result->GetRowCount()) - { - delete result; - result = nullptr; - } - - /// store the result in the holder - if (index < m_queries.size()) - m_queries[index].second.presult = result; -} - -SQLQueryHolder::~SQLQueryHolder() -{ - for (size_t i = 0; i < m_queries.size(); i++) + for (std::pair& query : m_queries) { /// if the result was never used, free the resources /// results used already (getresult called) are expected to be deleted - if (SQLElementData* data = &m_queries[i].first) - { - switch (data->type) - { - case SQL_ELEMENT_RAW: - free((void*)(const_cast(data->element.query))); - break; - case SQL_ELEMENT_PREPARED: - delete data->element.stmt; - break; - } - } + delete query.first; } } -void SQLQueryHolder::SetSize(size_t size) +void SQLQueryHolderBase::SetSize(size_t size) { /// to optimize push_back, reserve the number of queries about to be executed m_queries.resize(size); } +SQLQueryHolderTask::~SQLQueryHolderTask() = default; + bool SQLQueryHolderTask::Execute() { - //the result can't be ready as we are processing it right now - ASSERT(!m_result.ready()); + /// execute all queries in the holder and pass the results + for (size_t i = 0; i < m_holder->m_queries.size(); ++i) + if (PreparedStatementBase* stmt = m_holder->m_queries[i].first) + m_holder->SetPreparedResult(i, m_conn->Query(stmt)); - if (!m_holder) - return false; - - /// we can do this, we are friends - std::vector& queries = m_holder->m_queries; - - for (size_t i = 0; i < queries.size(); i++) - { - /// execute all queries in the holder and pass the results - if (SQLElementData* data = &queries[i].first) - { - switch (data->type) - { - case SQL_ELEMENT_RAW: - { - char const* sql = data->element.query; - if (sql) - m_holder->SetResult(i, m_conn->Query(sql)); - break; - } - case SQL_ELEMENT_PREPARED: - { - PreparedStatement* stmt = data->element.stmt; - if (stmt) - m_holder->SetPreparedResult(i, m_conn->Query(stmt)); - break; - } - } - } - } - - m_result.set(m_holder); + m_result.set_value(); return true; } + +bool SQLQueryHolderCallback::InvokeIfReady() +{ + if (m_future.valid() && m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + m_callback(*m_holder); + return true; + } + + return false; +} diff --git a/src/server/database/Database/QueryHolder.h b/src/server/database/Database/QueryHolder.h index cfe037042..2c575d6ea 100644 --- a/src/server/database/Database/QueryHolder.h +++ b/src/server/database/Database/QueryHolder.h @@ -1,45 +1,76 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _QUERYHOLDER_H #define _QUERYHOLDER_H -#include +#include "SQLOperation.h" +#include -class SQLQueryHolder +class AC_DATABASE_API SQLQueryHolderBase { - friend class SQLQueryHolderTask; +friend class SQLQueryHolderTask; private: - typedef std::pair SQLResultPair; - std::vector m_queries; + std::vector> m_queries; public: - SQLQueryHolder() = default; - ~SQLQueryHolder(); - bool SetQuery(size_t index, const char* sql); - bool SetPQuery(size_t index, const char* format, ...) ATTR_PRINTF(3, 4); - bool SetPreparedQuery(size_t index, PreparedStatement* stmt); + SQLQueryHolderBase() = default; + virtual ~SQLQueryHolderBase(); void SetSize(size_t size); - QueryResult GetResult(size_t index); - PreparedQueryResult GetPreparedResult(size_t index); - void SetResult(size_t index, ResultSet* result); + PreparedQueryResult GetPreparedResult(size_t index) const; void SetPreparedResult(size_t index, PreparedResultSet* result); + +protected: + bool SetPreparedQueryImpl(size_t index, PreparedStatementBase* stmt); }; -typedef ACE_Future QueryResultHolderFuture; +template +class SQLQueryHolder : public SQLQueryHolderBase +{ +public: + bool SetPreparedQuery(size_t index, PreparedStatement* stmt) + { + return SetPreparedQueryImpl(index, stmt); + } +}; -class SQLQueryHolderTask : public SQLOperation +class AC_DATABASE_API SQLQueryHolderTask : public SQLOperation { private: - SQLQueryHolder* m_holder; - QueryResultHolderFuture m_result; + std::shared_ptr m_holder; + QueryResultHolderPromise m_result; public: - SQLQueryHolderTask(SQLQueryHolder* holder, QueryResultHolderFuture res) - : m_holder(holder), m_result(res) { }; + explicit SQLQueryHolderTask(std::shared_ptr holder) + : m_holder(std::move(holder)) { } + + ~SQLQueryHolderTask(); + bool Execute() override; + QueryResultHolderFuture GetFuture() { return m_result.get_future(); } +}; + +class AC_DATABASE_API SQLQueryHolderCallback +{ +public: + SQLQueryHolderCallback(std::shared_ptr&& holder, QueryResultHolderFuture&& future) + : m_holder(std::move(holder)), m_future(std::move(future)) { } + + SQLQueryHolderCallback(SQLQueryHolderCallback&&) = default; + + SQLQueryHolderCallback& operator=(SQLQueryHolderCallback&&) = default; + + void AfterComplete(std::function callback) & + { + m_callback = std::move(callback); + } + + bool InvokeIfReady(); + + std::shared_ptr m_holder; + QueryResultHolderFuture m_future; + std::function m_callback; }; #endif diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp index 617d137fa..34c81ec69 100644 --- a/src/server/database/Database/QueryResult.cpp +++ b/src/server/database/Database/QueryResult.cpp @@ -1,33 +1,181 @@ /* - * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ -#include "DatabaseEnv.h" +#include "QueryResult.h" +#include "Errors.h" +#include "Field.h" #include "Log.h" +#include "MySQLHacks.h" +#include "MySQLWorkaround.h" -ResultSet::ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount) : - _rowCount(rowCount), - _fieldCount(fieldCount), - _result(result), - _fields(fields) +namespace { - _currentRow = new Field[_fieldCount]; - ASSERT(_currentRow); +static uint32 SizeForType(MYSQL_FIELD* field) +{ + switch (field->type) + { + case MYSQL_TYPE_NULL: + return 0; + case MYSQL_TYPE_TINY: + return 1; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: + return 2; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + return 4; + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_BIT: + return 8; + + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + return sizeof(MYSQL_TIME); + + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + return field->max_length + 1; + + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return 64; + + case MYSQL_TYPE_GEOMETRY: + /* + Following types are not sent over the wire: + MYSQL_TYPE_ENUM: + MYSQL_TYPE_SET: + */ + default: + LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type %u", uint32(field->type)); + return 0; + } } -PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount) : - m_rowCount(rowCount), - m_rowPosition(0), - m_fieldCount(fieldCount), - m_rBind(nullptr), - m_stmt(stmt), - m_res(result), - m_isNull(nullptr), - m_length(nullptr) +DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type) { - if (!m_res) + switch (type) + { + case MYSQL_TYPE_NULL: + return DatabaseFieldTypes::Null; + case MYSQL_TYPE_TINY: + return DatabaseFieldTypes::Int8; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: + return DatabaseFieldTypes::Int16; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + return DatabaseFieldTypes::Int32; + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_BIT: + return DatabaseFieldTypes::Int64; + case MYSQL_TYPE_FLOAT: + return DatabaseFieldTypes::Float; + case MYSQL_TYPE_DOUBLE: + return DatabaseFieldTypes::Double; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return DatabaseFieldTypes::Decimal; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + return DatabaseFieldTypes::Date; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + return DatabaseFieldTypes::Binary; + default: + LOG_WARN("sql.sql", "MysqlTypeToFieldType(): invalid field type %u", uint32(type)); + break; + } + + return DatabaseFieldTypes::Null; +} + +static char const* FieldTypeToString(enum_field_types type) +{ + switch (type) + { + case MYSQL_TYPE_BIT: return "BIT"; + case MYSQL_TYPE_BLOB: return "BLOB"; + case MYSQL_TYPE_DATE: return "DATE"; + case MYSQL_TYPE_DATETIME: return "DATETIME"; + case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL"; + case MYSQL_TYPE_DECIMAL: return "DECIMAL"; + case MYSQL_TYPE_DOUBLE: return "DOUBLE"; + case MYSQL_TYPE_ENUM: return "ENUM"; + case MYSQL_TYPE_FLOAT: return "FLOAT"; + case MYSQL_TYPE_GEOMETRY: return "GEOMETRY"; + case MYSQL_TYPE_INT24: return "INT24"; + case MYSQL_TYPE_LONG: return "LONG"; + case MYSQL_TYPE_LONGLONG: return "LONGLONG"; + case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB"; + case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB"; + case MYSQL_TYPE_NEWDATE: return "NEWDATE"; + case MYSQL_TYPE_NULL: return "NULL"; + case MYSQL_TYPE_SET: return "SET"; + case MYSQL_TYPE_SHORT: return "SHORT"; + case MYSQL_TYPE_STRING: return "STRING"; + case MYSQL_TYPE_TIME: return "TIME"; + case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP"; + case MYSQL_TYPE_TINY: return "TINY"; + case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB"; + case MYSQL_TYPE_VAR_STRING: return "VAR_STRING"; + case MYSQL_TYPE_YEAR: return "YEAR"; + default: return "-Unknown-"; + } +} + +void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex) +{ + meta->TableName = field->org_table; + meta->TableAlias = field->table; + meta->Name = field->org_name; + meta->Alias = field->name; + meta->TypeName = FieldTypeToString(field->type); + meta->Index = fieldIndex; + meta->Type = MysqlTypeToFieldType(field->type); +} +} + +ResultSet::ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount) : +_rowCount(rowCount), +_fieldCount(fieldCount), +_result(result), +_fields(fields) +{ + _fieldMetadata.resize(_fieldCount); + _currentRow = new Field[_fieldCount]; + for (uint32 i = 0; i < _fieldCount; i++) + { + InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i); + _currentRow[i].SetMetadata(&_fieldMetadata[i]); + } +} + +PreparedResultSet::PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount) : +m_rowCount(rowCount), +m_rowPosition(0), +m_fieldCount(fieldCount), +m_rBind(nullptr), +m_stmt(stmt), +m_metadataResult(result) +{ + if (!m_metadataResult) return; if (m_stmt->bind_result_done) @@ -36,48 +184,22 @@ PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 delete[] m_stmt->bind->is_null; } - m_rBind = new MYSQL_BIND[m_fieldCount]; - m_isNull = new my_bool[m_fieldCount]; - m_length = new unsigned long[m_fieldCount]; + m_rBind = new MySQLBind[m_fieldCount]; - memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount); - memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount); + //- for future readers wondering where the fuck this is freed - mysql_stmt_bind_result moves pointers to these + // from m_rBind to m_stmt->bind and it is later freed by the `if (m_stmt->bind_result_done)` block just above here + // MYSQL_STMT lifetime is equal to connection lifetime + MySQLBool* m_isNull = new MySQLBool[m_fieldCount]; + unsigned long* m_length = new unsigned long[m_fieldCount]; + + memset(m_isNull, 0, sizeof(MySQLBool) * m_fieldCount); + memset(m_rBind, 0, sizeof(MySQLBind) * m_fieldCount); memset(m_length, 0, sizeof(unsigned long) * m_fieldCount); //- This is where we store the (entire) resultset if (mysql_stmt_store_result(m_stmt)) { - LOG_INFO("sql.driver", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); - delete[] m_rBind; - delete[] m_isNull; - delete[] m_length; - return; - } - - //- This is where we prepare the buffer based on metadata - uint32 i = 0; - MYSQL_FIELD* field = mysql_fetch_field(m_res); - while (field) - { - size_t size = Field::SizeForType(field); - - m_rBind[i].buffer_type = field->type; - m_rBind[i].buffer = malloc(size); - memset(m_rBind[i].buffer, 0, size); - m_rBind[i].buffer_length = size; - m_rBind[i].length = &m_length[i]; - m_rBind[i].is_null = &m_isNull[i]; - m_rBind[i].error = nullptr; - m_rBind[i].is_unsigned = field->flags & UNSIGNED_FLAG; - - ++i; - field = mysql_fetch_field(m_res); - } - - //- This is where we bind the bind the buffer to the statement - if (mysql_stmt_bind_result(m_stmt, m_rBind)) - { - LOG_INFO("sql.driver", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); + LOG_WARN("sql.sql", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); delete[] m_rBind; delete[] m_isNull; delete[] m_length; @@ -86,18 +208,55 @@ PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 m_rowCount = mysql_stmt_num_rows(m_stmt); - m_rows.resize(uint32(m_rowCount)); + //- This is where we prepare the buffer based on metadata + MySQLField* field = reinterpret_cast(mysql_fetch_fields(m_metadataResult)); + m_fieldMetadata.resize(m_fieldCount); + std::size_t rowSize = 0; + for (uint32 i = 0; i < m_fieldCount; ++i) + { + uint32 size = SizeForType(&field[i]); + rowSize += size; + + InitializeDatabaseFieldMetadata(&m_fieldMetadata[i], &field[i], i); + + m_rBind[i].buffer_type = field[i].type; + m_rBind[i].buffer_length = size; + m_rBind[i].length = &m_length[i]; + m_rBind[i].is_null = &m_isNull[i]; + m_rBind[i].error = nullptr; + m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG; + } + + char* dataBuffer = new char[rowSize * m_rowCount]; + for (uint32 i = 0, offset = 0; i < m_fieldCount; ++i) + { + m_rBind[i].buffer = dataBuffer + offset; + offset += m_rBind[i].buffer_length; + } + + //- This is where we bind the bind the buffer to the statement + if (mysql_stmt_bind_result(m_stmt, m_rBind)) + { + LOG_WARN("sql.sql", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); + mysql_stmt_free_result(m_stmt); + CleanUp(); + delete[] m_isNull; + delete[] m_length; + return; + } + + m_rows.resize(uint32(m_rowCount) * m_fieldCount); while (_NextRow()) { - m_rows[uint32(m_rowPosition)] = new Field[m_fieldCount]; - for (uint64 fIndex = 0; fIndex < m_fieldCount; ++fIndex) + for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex) { + m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetMetadata(&m_fieldMetadata[fIndex]); + + unsigned long buffer_length = m_rBind[fIndex].buffer_length; + unsigned long fetched_length = *m_rBind[fIndex].length; if (!*m_rBind[fIndex].is_null) - m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( m_rBind[fIndex].buffer, - m_rBind[fIndex].buffer_length, - m_rBind[fIndex].buffer_type, - *m_rBind[fIndex].length ); - else + { + void* buffer = m_stmt->bind[fIndex].buffer; switch (m_rBind[fIndex].buffer_type) { case MYSQL_TYPE_TINY_BLOB: @@ -106,24 +265,38 @@ PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 case MYSQL_TYPE_BLOB: case MYSQL_TYPE_STRING: case MYSQL_TYPE_VAR_STRING: - m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( "", - m_rBind[fIndex].buffer_length, - m_rBind[fIndex].buffer_type, - *m_rBind[fIndex].length ); + // warning - the string will not be null-terminated if there is no space for it in the buffer + // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED + // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string + // in this case using Field::GetCString will result in garbage + // TODO: remove Field::GetCString and use std::string_view in C++17 + if (fetched_length < buffer_length) + *((char*)buffer + fetched_length) = '\0'; break; default: - m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( 0, - m_rBind[fIndex].buffer_length, - m_rBind[fIndex].buffer_type, - *m_rBind[fIndex].length ); + break; } + + m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( + (char const*)buffer, + fetched_length); + + // move buffer pointer to next part + m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize; + } + else + { + m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( + nullptr, + *m_rBind[fIndex].length); + } } m_rowPosition++; } m_rowPosition = 0; /// All data is buffered, let go of mysql c api structures - CleanUp(); + mysql_stmt_free_result(m_stmt); } ResultSet::~ResultSet() @@ -133,8 +306,7 @@ ResultSet::~ResultSet() PreparedResultSet::~PreparedResultSet() { - for (uint32 i = 0; i < uint32(m_rowCount); ++i) - delete[] m_rows[i]; + CleanUp(); } bool ResultSet::NextRow() @@ -154,16 +326,23 @@ bool ResultSet::NextRow() unsigned long* lengths = mysql_fetch_lengths(_result); if (!lengths) { + LOG_WARN("sql.sql", "%s:mysql_fetch_lengths, cannot retrieve value lengths. Error %s.", __FUNCTION__, mysql_error(_result->handle)); CleanUp(); return false; } for (uint32 i = 0; i < _fieldCount; i++) - _currentRow[i].SetStructuredValue(row[i], _fields[i].type, lengths[i]); + _currentRow[i].SetStructuredValue(row[i], lengths[i]); return true; } +std::string ResultSet::GetFieldName(uint32 index) const +{ + ASSERT(index < _fieldCount); + return _fields[index].name; +} + bool PreparedResultSet::NextRow() { /// Only updates the m_rowPosition so upper level code knows in which element @@ -181,25 +360,10 @@ bool PreparedResultSet::_NextRow() if (m_rowPosition >= m_rowCount) return false; - int retval = mysql_stmt_fetch( m_stmt ); - - if (!retval || retval == MYSQL_DATA_TRUNCATED) - retval = true; - - if (retval == MYSQL_NO_DATA) - retval = false; - - return retval; + int retval = mysql_stmt_fetch(m_stmt); + return retval == 0 || retval == MYSQL_DATA_TRUNCATED; } -#ifdef ELUNA -std::string ResultSet::GetFieldName(uint32 index) const -{ - ASSERT(index < _fieldCount); - return _fields[index].name; -} -#endif - void ResultSet::CleanUp() { if (_currentRow) @@ -215,20 +379,34 @@ void ResultSet::CleanUp() } } +Field const& ResultSet::operator[](std::size_t index) const +{ + ASSERT(index < _fieldCount); + return _currentRow[index]; +} + +Field* PreparedResultSet::Fetch() const +{ + ASSERT(m_rowPosition < m_rowCount); + return const_cast(&m_rows[uint32(m_rowPosition) * m_fieldCount]); +} + +Field const& PreparedResultSet::operator[](std::size_t index) const +{ + ASSERT(m_rowPosition < m_rowCount); + ASSERT(index < m_fieldCount); + return m_rows[uint32(m_rowPosition) * m_fieldCount + index]; +} + void PreparedResultSet::CleanUp() { - /// More of the in our code allocated sources are deallocated by the poorly documented mysql c api - if (m_res) - mysql_free_result(m_res); + if (m_metadataResult) + mysql_free_result(m_metadataResult); - FreeBindBuffer(); - mysql_stmt_free_result(m_stmt); - - delete[] m_rBind; -} - -void PreparedResultSet::FreeBindBuffer() -{ - for (uint32 i = 0; i < m_fieldCount; ++i) - free (m_rBind[i].buffer); + if (m_rBind) + { + delete[](char*)m_rBind->buffer; + delete[] m_rBind; + m_rBind = nullptr; + } } diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h index fedc0a073..ee1b86454 100644 --- a/src/server/database/Database/QueryResult.h +++ b/src/server/database/Database/QueryResult.h @@ -1,99 +1,74 @@ /* - * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef QUERYRESULT_H #define QUERYRESULT_H -#include "Errors.h" -#include "Field.h" -#include +#include "DatabaseEnvFwd.h" +#include "Define.h" +#include -#ifdef _WIN32 -#include -#endif -#include - -#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80001 -typedef bool my_bool; -#endif - -class ResultSet +class AC_DATABASE_API ResultSet { public: - ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount); + ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount); ~ResultSet(); bool NextRow(); - [[nodiscard]] uint64 GetRowCount() const { return _rowCount; } - [[nodiscard]] uint32 GetFieldCount() const { return _fieldCount; } -#ifdef ELUNA + uint64 GetRowCount() const { return _rowCount; } + uint32 GetFieldCount() const { return _fieldCount; } std::string GetFieldName(uint32 index) const; -#endif - [[nodiscard]] Field* Fetch() const { return _currentRow; } - const Field& operator [] (uint32 index) const - { - ASSERT(index < _fieldCount); - return _currentRow[index]; - } + + Field* Fetch() const { return _currentRow; } + Field const& operator[](std::size_t index) const; protected: + std::vector _fieldMetadata; uint64 _rowCount; Field* _currentRow; uint32 _fieldCount; private: void CleanUp(); - MYSQL_RES* _result; - MYSQL_FIELD* _fields; + MySQLResult* _result; + MySQLField* _fields; + + ResultSet(ResultSet const& right) = delete; + ResultSet& operator=(ResultSet const& right) = delete; }; -typedef std::shared_ptr QueryResult; - -class PreparedResultSet +class AC_DATABASE_API PreparedResultSet { public: - PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount); + PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount); ~PreparedResultSet(); bool NextRow(); - [[nodiscard]] uint64 GetRowCount() const { return m_rowCount; } - [[nodiscard]] uint32 GetFieldCount() const { return m_fieldCount; } + uint64 GetRowCount() const { return m_rowCount; } + uint32 GetFieldCount() const { return m_fieldCount; } - [[nodiscard]] Field* Fetch() const - { - ASSERT(m_rowPosition < m_rowCount); - return m_rows[uint32(m_rowPosition)]; - } - - const Field& operator [] (uint32 index) const - { - ASSERT(m_rowPosition < m_rowCount); - ASSERT(index < m_fieldCount); - return m_rows[uint32(m_rowPosition)][index]; - } + Field* Fetch() const; + Field const& operator[](std::size_t index) const; protected: - std::vector m_rows; + std::vector m_fieldMetadata; + std::vector m_rows; uint64 m_rowCount; uint64 m_rowPosition; uint32 m_fieldCount; private: - MYSQL_BIND* m_rBind; - MYSQL_STMT* m_stmt; - MYSQL_RES* m_res; + MySQLBind* m_rBind; + MySQLStmt* m_stmt; + MySQLResult* m_metadataResult; ///< Field metadata, returned by mysql_stmt_result_metadata - my_bool* m_isNull; - unsigned long* m_length; - - void FreeBindBuffer(); void CleanUp(); bool _NextRow(); + + PreparedResultSet(PreparedResultSet const& right) = delete; + PreparedResultSet& operator=(PreparedResultSet const& right) = delete; }; -typedef std::shared_ptr PreparedQueryResult; - #endif diff --git a/src/server/database/Database/SQLOperation.h b/src/server/database/Database/SQLOperation.h index c5af1a5b5..5e5d9872a 100644 --- a/src/server/database/Database/SQLOperation.h +++ b/src/server/database/Database/SQLOperation.h @@ -1,25 +1,19 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _SQLOPERATION_H #define _SQLOPERATION_H -#include -#include - -#include "QueryResult.h" - -//- Forward declare (don't include header to prevent circular includes) -class PreparedStatement; +#include "DatabaseEnvFwd.h" +#include "Define.h" //- Union that holds element data union SQLElementUnion { - PreparedStatement* stmt; - const char* query; + PreparedStatementBase* stmt; + char const* query; }; //- Type specifier of our element data @@ -36,20 +30,15 @@ struct SQLElementData SQLElementDataType type; }; -//- For ambigious resultsets -union SQLResultSetUnion -{ - PreparedResultSet* presult; - ResultSet* qresult; -}; - class MySQLConnection; -class SQLOperation : public ACE_Method_Request +class AC_DATABASE_API SQLOperation { public: SQLOperation(): m_conn(nullptr) { } - int call() override + virtual ~SQLOperation() { } + + virtual int call() { Execute(); return 0; @@ -58,6 +47,10 @@ public: virtual void SetConnection(MySQLConnection* con) { m_conn = con; } MySQLConnection* m_conn; + +private: + SQLOperation(SQLOperation const& right) = delete; + SQLOperation& operator=(SQLOperation const& right) = delete; }; #endif diff --git a/src/server/database/Database/Transaction.cpp b/src/server/database/Database/Transaction.cpp index 72cedec9f..5a43cb951 100644 --- a/src/server/database/Database/Transaction.cpp +++ b/src/server/database/Database/Transaction.cpp @@ -1,16 +1,23 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ -#include "DatabaseEnv.h" #include "Transaction.h" - +#include "Log.h" +#include "MySQLConnection.h" +#include "PreparedStatement.h" +#include "Timer.h" #include +#include +#include + +std::mutex TransactionTask::_deadlockLock; + +#define DEADLOCK_MAX_RETRY_TIME_MS 60000 //- Append a raw ad-hoc query to the transaction -void Transaction::Append(const char* sql) +void TransactionBase::Append(char const* sql) { SQLElementData data; data.type = SQL_ELEMENT_RAW; @@ -18,19 +25,8 @@ void Transaction::Append(const char* sql) m_queries.push_back(data); } -void Transaction::PAppend(const char* sql, ...) -{ - va_list ap; - char szQuery [MAX_QUERY_LEN]; - va_start(ap, sql); - vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap); - va_end(ap); - - Append(szQuery); -} - //- Append a prepared statement to the transaction -void Transaction::Append(PreparedStatement* stmt) +void TransactionBase::AppendPreparedStatement(PreparedStatementBase* stmt) { SQLElementData data; data.type = SQL_ELEMENT_PREPARED; @@ -38,47 +34,116 @@ void Transaction::Append(PreparedStatement* stmt) m_queries.push_back(data); } -void Transaction::Cleanup() +void TransactionBase::Cleanup() { // This might be called by explicit calls to Cleanup or by the auto-destructor if (_cleanedUp) return; - while (!m_queries.empty()) + for (SQLElementData const& data : m_queries) { - SQLElementData const& data = m_queries.front(); switch (data.type) { case SQL_ELEMENT_PREPARED: delete data.element.stmt; - break; + break; case SQL_ELEMENT_RAW: free((void*)(data.element.query)); - break; + break; } - - m_queries.pop_front(); } + m_queries.clear(); _cleanedUp = true; } bool TransactionTask::Execute() { - int errorCode = m_conn->ExecuteTransaction(m_trans); + int errorCode = TryExecute(); if (!errorCode) return true; if (errorCode == ER_LOCK_DEADLOCK) { - uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself - for (uint8 i = 0; i < loopBreaker; ++i) - if (!m_conn->ExecuteTransaction(m_trans)) + std::ostringstream threadIdStream; + threadIdStream << std::this_thread::get_id(); + std::string threadId = threadIdStream.str(); + + // Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other + std::lock_guard lock(_deadlockLock); + + for (uint32 loopDuration = 0, startMSTime = getMSTime(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime)) + { + if (!TryExecute()) return true; + + LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: %u ms, Thread Id: %s", loopDuration, threadId.c_str()); + } + + LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: %s", threadId.c_str()); } // Clean up now. - m_trans->Cleanup(); + 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) + { + std::ostringstream threadIdStream; + threadIdStream << std::this_thread::get_id(); + std::string threadId = threadIdStream.str(); + + // Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other + std::lock_guard lock(_deadlockLock); + for (uint32 loopDuration = 0, startMSTime = getMSTime(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime)) + { + if (!TryExecute()) + { + m_result.set_value(true); + return true; + } + + LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: %u ms, Thread Id: %s", loopDuration, threadId.c_str()); + } + + LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: %s", threadId.c_str()); + } + + // 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 45fa987d5..d4ee086ac 100644 --- a/src/server/database/Database/Transaction.h +++ b/src/server/database/Database/Transaction.h @@ -1,62 +1,111 @@ /* - * Copyright (C) 2016+ AzerothCore - * Copyright (C) 2008-2016 TrinityCore - * Copyright (C) 2005-2009 MaNGOS + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2021+ WarheadCore */ #ifndef _TRANSACTION_H #define _TRANSACTION_H +#include "DatabaseEnvFwd.h" +#include "Define.h" #include "SQLOperation.h" -#include -#include - -//- Forward declare (don't include header to prevent circular includes) -class PreparedStatement; +#include "StringFormat.h" +#include +#include +#include /*! Transactions, high level class. */ -class Transaction +class AC_DATABASE_API TransactionBase { - friend class TransactionTask; - friend class MySQLConnection; +friend class TransactionTask; +friend class MySQLConnection; - template - friend class DatabaseWorkerPool; +template +friend class DatabaseWorkerPool; public: - Transaction() { } - ~Transaction() { Cleanup(); } + TransactionBase() : _cleanedUp(false) { } + virtual ~TransactionBase() { Cleanup(); } - void Append(PreparedStatement* statement); - void Append(const char* sql); - void PAppend(const char* sql, ...); + void Append(char const* sql); + template + void PAppend(Format&& sql, Args&&... args) + { + Append(Acore::StringFormat(std::forward(sql), std::forward(args)...).c_str()); + } - [[nodiscard]] size_t GetSize() const { return m_queries.size(); } + std::size_t GetSize() const { return m_queries.size(); } protected: + void AppendPreparedStatement(PreparedStatementBase* statement); void Cleanup(); - std::list m_queries; + std::vector m_queries; private: - bool _cleanedUp{false}; + bool _cleanedUp; }; -typedef std::shared_ptr SQLTransaction; +template +class Transaction : public TransactionBase +{ +public: + using TransactionBase::Append; + void Append(PreparedStatement* statement) + { + AppendPreparedStatement(statement); + } +}; /*! Low level class*/ -class TransactionTask : public SQLOperation +class AC_DATABASE_API TransactionTask : public SQLOperation { - template friend class DatabaseWorkerPool; - friend class DatabaseWorker; +template friend class DatabaseWorkerPool; +friend class DatabaseWorker; +friend class TransactionCallback; public: - TransactionTask(SQLTransaction trans) : m_trans(std::move(trans)) { } ; - ~TransactionTask() override = default; + TransactionTask(std::shared_ptr trans) : m_trans(trans) { } + ~TransactionTask() { } + +protected: + bool Execute() override; + int TryExecute(); + void CleanupOnFailure(); + + std::shared_ptr m_trans; + static std::mutex _deadlockLock; +}; + +class AC_DATABASE_API TransactionWithResultTask : public TransactionTask +{ +public: + TransactionWithResultTask(std::shared_ptr trans) : TransactionTask(trans) { } + + TransactionFuture GetFuture() { return m_result.get_future(); } protected: bool Execute() override; - SQLTransaction m_trans; + TransactionPromise m_result; +}; + +class AC_DATABASE_API TransactionCallback +{ +public: + TransactionCallback(TransactionFuture&& future) : m_future(std::move(future)) { } + TransactionCallback(TransactionCallback&&) = default; + + TransactionCallback& operator=(TransactionCallback&&) = default; + + void AfterComplete(std::function callback) & + { + m_callback = std::move(callback); + } + + bool InvokeIfReady(); + + TransactionFuture m_future; + std::function m_callback; }; #endif diff --git a/src/server/database/Logging/AppenderDB.cpp b/src/server/database/Logging/AppenderDB.cpp index 296792897..2a75f28f5 100644 --- a/src/server/database/Logging/AppenderDB.cpp +++ b/src/server/database/Logging/AppenderDB.cpp @@ -19,7 +19,7 @@ void AppenderDB::_write(LogMessage const* message) if (!enabled || (message->type.find("sql") != std::string::npos)) return; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG); stmt->setUInt64(0, message->mtime); stmt->setUInt32(1, realmId); stmt->setString(2, message->type); diff --git a/src/server/database/PrecompiledHeaders/databasePCH.h b/src/server/database/PrecompiledHeaders/databasePCH.h index 34a41616a..edd42c516 100644 --- a/src/server/database/PrecompiledHeaders/databasePCH.h +++ b/src/server/database/PrecompiledHeaders/databasePCH.h @@ -1,19 +1,23 @@ /* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore */ #include "Define.h" -// #include "DatabaseEnvFwd.h" +#include "DatabaseEnvFwd.h" #include "Errors.h" #include "Field.h" #include "Log.h" #include "MySQLConnection.h" -// #include "MySQLPreparedStatement.h" -// #include "MySQLWorkaround.h" +#include "MySQLPreparedStatement.h" +#include "MySQLWorkaround.h" #include "PreparedStatement.h" #include "QueryResult.h" #include "SQLOperation.h" #include "Transaction.h" +#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 +#include +#endif #include #include #include diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp new file mode 100644 index 000000000..1750902bf --- /dev/null +++ b/src/server/database/Updater/DBUpdater.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ +#include "DBUpdater.h" +#include "BuiltInConfig.h" +#include "Config.h" +#include "DatabaseEnv.h" +#include "DatabaseLoader.h" +#include "GitRevision.h" +#include "Log.h" +#include "QueryResult.h" +#include "StartProcess.h" +#include "UpdateFetcher.h" +#include +#include +#include + +std::string DBUpdaterUtil::GetCorrectedMySQLExecutable() +{ + if (!corrected_path().empty()) + return corrected_path(); + else + return BuiltInConfig::GetMySQLExecutable(); +} + +bool DBUpdaterUtil::CheckExecutable() +{ + boost::filesystem::path exe(GetCorrectedMySQLExecutable()); + if (!is_regular_file(exe)) + { + exe = Warhead::SearchExecutableInPath("mysql"); + if (!exe.empty() && is_regular_file(exe)) + { + // Correct the path to the cli + corrected_path() = absolute(exe).generic_string(); + return true; + } + + LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").", + absolute(exe).generic_string().c_str()); + + return false; + } + return true; +} + +std::string& DBUpdaterUtil::corrected_path() +{ + static std::string path; + return path; +} + +// Auth Database +template<> +std::string DBUpdater::GetConfigEntry() +{ + return "Updates.Auth"; +} + +template<> +std::string DBUpdater::GetTableName() +{ + return "Auth"; +} + +template<> +std::string DBUpdater::GetBaseFilesDirectory() +{ + return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_auth/"; +} + +template<> +bool DBUpdater::IsEnabled(uint32 const updateMask) +{ + // This way silences warnings under msvc + return (updateMask & DatabaseLoader::DATABASE_LOGIN) ? true : false; +} + +template<> +std::string DBUpdater::GetDBModuleName() +{ + return "db_auth"; +} + +// World Database +template<> +std::string DBUpdater::GetConfigEntry() +{ + return "Updates.World"; +} + +template<> +std::string DBUpdater::GetTableName() +{ + return "World"; +} + +template<> +std::string DBUpdater::GetBaseFilesDirectory() +{ + return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_world/"; +} + +template<> +bool DBUpdater::IsEnabled(uint32 const updateMask) +{ + // This way silences warnings under msvc + return (updateMask & DatabaseLoader::DATABASE_WORLD) ? true : false; +} + +template<> +std::string DBUpdater::GetDBModuleName() +{ + return "db_world"; +} + +// Character Database +template<> +std::string DBUpdater::GetConfigEntry() +{ + return "Updates.Character"; +} + +template<> +std::string DBUpdater::GetTableName() +{ + return "Character"; +} + +template<> +std::string DBUpdater::GetBaseFilesDirectory() +{ + return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_characters/"; +} + +template<> +bool DBUpdater::IsEnabled(uint32 const updateMask) +{ + // This way silences warnings under msvc + return (updateMask & DatabaseLoader::DATABASE_CHARACTER) ? true : false; +} + +template<> +std::string DBUpdater::GetDBModuleName() +{ + return "db_characters"; +} + +// All +template +BaseLocation DBUpdater::GetBaseLocationType() +{ + return LOCATION_REPOSITORY; +} + +template +bool DBUpdater::Create(DatabaseWorkerPool& pool) +{ + LOG_WARN("sql.updates", "Database \"%s\" does not exist, do you want to create it? [yes (default) / no]: ", + pool.GetConnectionInfo()->database.c_str()); + + std::string answer; + std::getline(std::cin, answer); + if (!sConfigMgr->isDryRun() && !answer.empty() && !(answer.substr(0, 1) == "y")) + return false; + + LOG_INFO("sql.updates", "Creating database \"%s\"...", pool.GetConnectionInfo()->database.c_str()); + + // Path of temp file + static Path const temp("create_table.sql"); + + // Create temporary query to use external MySQL CLi + std::ofstream file(temp.generic_string()); + if (!file.is_open()) + { + LOG_FATAL("sql.updates", "Failed to create temporary query file \"%s\"!", temp.generic_string().c_str()); + return false; + } + + file << "CREATE DATABASE `" << pool.GetConnectionInfo()->database << "` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_general_ci;\n\n"; + + file.close(); + + try + { + DBUpdater::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password, + pool.GetConnectionInfo()->port_or_socket, "", pool.GetConnectionInfo()->ssl, temp); + } + catch (UpdateException&) + { + LOG_FATAL("sql.updates", "Failed to create database %s! Does the user (named in *.conf) have `CREATE`, `ALTER`, `DROP`, `INSERT` and `DELETE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str()); + boost::filesystem::remove(temp); + return false; + } + + LOG_INFO("sql.updates", "Done."); + LOG_INFO("sql.updates", " "); + boost::filesystem::remove(temp); + return true; +} + +template +bool DBUpdater::Update(DatabaseWorkerPool& pool) +{ + if (!DBUpdaterUtil::CheckExecutable()) + return false; + + LOG_INFO("sql.updates", "Updating %s database...", DBUpdater::GetTableName().c_str()); + + Path const sourceDirectory(BuiltInConfig::GetSourceDirectory()); + + if (!is_directory(sourceDirectory)) + { + LOG_ERROR("sql.updates", "DBUpdater: The given source directory %s does not exist, change the path to the directory where your sql directory exists (for example c:\\source\\trinitycore). Shutting down.", + sourceDirectory.generic_string().c_str()); + return false; + } + + auto CheckUpdateTable = [&](std::string const& tableName) + { + auto checkTable = DBUpdater::Retrieve(pool, Warhead::StringFormat("SHOW TABLES LIKE '%s'", tableName.c_str())); + if (!checkTable) + { + LOG_WARN("sql.updates", "> Table '%s' not exist! Try add based table", tableName.c_str()); + + Path const temp(GetBaseFilesDirectory() + tableName + ".sql"); + + try + { + DBUpdater::ApplyFile(pool, temp); + } + catch (UpdateException&) + { + LOG_FATAL("sql.updates", "Failed apply file to database %s! Does the user (named in *.conf) have `INSERT` and `DELETE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str()); + return false; + } + + return true; + } + + return true; + }; + + if (!CheckUpdateTable("updates") || !CheckUpdateTable("updates_include")) + return false; + + UpdateFetcher updateFetcher(sourceDirectory, [&](std::string const & query) { DBUpdater::Apply(pool, query); }, + [&](Path const & file) { DBUpdater::ApplyFile(pool, file); }, + [&](std::string const & query) -> QueryResult { return DBUpdater::Retrieve(pool, query); }, DBUpdater::GetDBModuleName()); + + UpdateResult result; + try + { + result = updateFetcher.Update( + sConfigMgr->GetBoolDefault("Updates.Redundancy", true), + sConfigMgr->GetBoolDefault("Updates.AllowRehash", true), + sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false), + sConfigMgr->GetIntDefault("Updates.CleanDeadRefMaxCount", 3)); + } + catch (UpdateException&) + { + return false; + } + + std::string const info = Warhead::StringFormat("Containing " SZFMTD " new and " SZFMTD " archived updates.", + result.recent, result.archived); + + if (!result.updated) + LOG_INFO("sql.updates", ">> %s database is up-to-date! %s", DBUpdater::GetTableName().c_str(), info.c_str()); + else + LOG_INFO("sql.updates", ">> Applied " SZFMTD " %s. %s", result.updated, result.updated == 1 ? "query" : "queries", info.c_str()); + + LOG_INFO("sql.updates", " "); + + return true; +} + +template +bool DBUpdater::Populate(DatabaseWorkerPool& pool) +{ + { + QueryResult const result = Retrieve(pool, "SHOW TABLES"); + if (result && (result->GetRowCount() > 0)) + return true; + } + + if (!DBUpdaterUtil::CheckExecutable()) + return false; + + LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater::GetTableName().c_str()); + + std::string const DirPathStr = DBUpdater::GetBaseFilesDirectory(); + + Path const DirPath(DirPathStr); + if (!boost::filesystem::is_directory(DirPath)) + { + LOG_ERROR("sql.updates", ">> Directory \"%s\" not exist", DirPath.generic_string().c_str()); + return false; + } + + if (DirPath.empty()) + { + LOG_ERROR("sql.updates", ">> Directory \"%s\" is empty", DirPath.generic_string().c_str()); + return false; + } + + boost::filesystem::directory_iterator const DirItr; + uint32 FilesCount = 0; + + for (boost::filesystem::directory_iterator itr(DirPath); itr != DirItr; ++itr) + { + if (itr->path().extension() == ".sql") + FilesCount++; + } + + if (!FilesCount) + { + LOG_ERROR("sql.updates", ">> In directory \"%s\" not exist '*.sql' files", DirPath.generic_string().c_str()); + return false; + } + + for (boost::filesystem::directory_iterator itr(DirPath); itr != DirItr; ++itr) + { + if (itr->path().extension() != ".sql") + continue; + + LOG_INFO("sql.updates", ">> Applying \'%s\'...", itr->path().filename().generic_string().c_str()); + + try + { + ApplyFile(pool, itr->path()); + } + catch (UpdateException&) + { + return false; + } + } + + LOG_INFO("sql.updates", ">> Done!"); + LOG_INFO("sql.updates", " "); + return true; +} + +template +QueryResult DBUpdater::Retrieve(DatabaseWorkerPool& pool, std::string const& query) +{ + return pool.Query(query.c_str()); +} + +template +void DBUpdater::Apply(DatabaseWorkerPool& pool, std::string const& query) +{ + pool.DirectExecute(query.c_str()); +} + +template +void DBUpdater::ApplyFile(DatabaseWorkerPool& pool, Path const& path) +{ + DBUpdater::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password, + pool.GetConnectionInfo()->port_or_socket, pool.GetConnectionInfo()->database, pool.GetConnectionInfo()->ssl, path); +} + +template +void DBUpdater::ApplyFile(DatabaseWorkerPool& pool, std::string const& host, std::string const& user, + std::string const& password, std::string const& port_or_socket, std::string const& database, std::string const& ssl, Path const& path) +{ + std::vector args; + args.reserve(7); + + // CLI Client connection info + args.emplace_back("-h" + host); + args.emplace_back("-u" + user); + + if (!password.empty()) + args.emplace_back("-p" + password); + + // Check if we want to connect through ip or socket (Unix only) +#ifdef _WIN32 + + if (host == ".") + args.emplace_back("--protocol=PIPE"); + else + args.emplace_back("-P" + port_or_socket); + +#else + + if (!std::isdigit(port_or_socket[0])) + { + // We can't check if host == "." here, because it is named localhost if socket option is enabled + args.emplace_back("-P0"); + args.emplace_back("--protocol=SOCKET"); + args.emplace_back("-S" + port_or_socket); + } + else + // generic case + args.emplace_back("-P" + port_or_socket); + +#endif + + // Set the default charset to utf8 + args.emplace_back("--default-character-set=utf8"); + + // Set max allowed packet to 1 GB + args.emplace_back("--max-allowed-packet=1GB"); + + if (ssl == "ssl") + args.emplace_back("--ssl"); + + // Database + if (!database.empty()) + args.emplace_back(database); + + // Invokes a mysql process which doesn't leak credentials to logs + int const ret = Warhead::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args, + "sql.updates", path.generic_string(), true); + + if (ret != EXIT_SUCCESS) + { + LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \ + " If you are a user, please pull the latest revision from the repository. " + "Also make sure you have not applied any of the databases with your sql client. " + "You cannot use auto-update system and import sql files from WarheadCore repository with your sql client. " + "If you are a developer, please fix your sql query.", + path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str()); + + throw UpdateException("update failed"); + } +} + +template class AC_DATABASE_API DBUpdater; +template class AC_DATABASE_API DBUpdater; +template class AC_DATABASE_API DBUpdater; diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h new file mode 100644 index 000000000..f7f05076b --- /dev/null +++ b/src/server/database/Updater/DBUpdater.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef DBUpdater_h__ +#define DBUpdater_h__ + +#include "DatabaseEnv.h" +#include "Define.h" +#include + +template +class DatabaseWorkerPool; + +namespace boost +{ + namespace filesystem + { + class path; + } +} + +class AC_DATABASE_API UpdateException : public std::exception +{ +public: + UpdateException(std::string const& msg) : _msg(msg) { } + ~UpdateException() throw() { } + + char const* what() const throw() override { return _msg.c_str(); } + +private: + std::string const _msg; +}; + +enum BaseLocation +{ + LOCATION_REPOSITORY, + LOCATION_DOWNLOAD +}; + +class AC_DATABASE_API DBUpdaterUtil +{ +public: + static std::string GetCorrectedMySQLExecutable(); + + static bool CheckExecutable(); + +private: + static std::string& corrected_path(); +}; + +template +class AC_DATABASE_API DBUpdater +{ +public: + using Path = boost::filesystem::path; + + static inline std::string GetConfigEntry(); + static inline std::string GetTableName(); + static std::string GetBaseFilesDirectory(); + static bool IsEnabled(uint32 const updateMask); + static BaseLocation GetBaseLocationType(); + static bool Create(DatabaseWorkerPool& pool); + static bool Update(DatabaseWorkerPool& pool); + static bool Populate(DatabaseWorkerPool& pool); + + // module + static std::string GetDBModuleName(); + +private: + static QueryResult Retrieve(DatabaseWorkerPool& pool, std::string const& query); + static void Apply(DatabaseWorkerPool& pool, std::string const& query); + static void ApplyFile(DatabaseWorkerPool& pool, Path const& path); + static void ApplyFile(DatabaseWorkerPool& pool, std::string const& host, std::string const& user, + std::string const& password, std::string const& port_or_socket, std::string const& database, std::string const& ssl, Path const& path); +}; + +#endif // DBUpdater_h__ diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp new file mode 100644 index 000000000..323bba8b3 --- /dev/null +++ b/src/server/database/Updater/UpdateFetcher.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#include "UpdateFetcher.h" +#include "Common.h" +#include "CryptoHash.h" +#include "DBUpdater.h" +#include "Field.h" +#include "Log.h" +#include "QueryResult.h" +#include "Tokenize.h" +#include "Util.h" +#include +#include +#include + +using namespace boost::filesystem; + +struct UpdateFetcher::DirectoryEntry +{ + DirectoryEntry(Path const& path_, State state_) : path(path_), state(state_) { } + + Path const path; + State const state; +}; + +UpdateFetcher::UpdateFetcher(Path const& sourceDirectory, + std::function const& apply, + std::function const& applyFile, + std::function const& retrieve, std::string const& dbModuleName_) : + _sourceDirectory(std::make_unique(sourceDirectory)), _apply(apply), _applyFile(applyFile), + _retrieve(retrieve), _dbModuleName(dbModuleName_) +{ +} + +UpdateFetcher::~UpdateFetcher() +{ +} + +UpdateFetcher::LocaleFileStorage UpdateFetcher::GetFileList() const +{ + LocaleFileStorage files; + DirectoryStorage directories = ReceiveIncludedDirectories(); + for (auto const& entry : directories) + FillFileListRecursively(entry.path, files, entry.state, 1); + + return files; +} + +void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const +{ + static uint32 const MAX_DEPTH = 10; + static directory_iterator const end; + + for (directory_iterator itr(path); itr != end; ++itr) + { + if (is_directory(itr->path())) + { + if (depth < MAX_DEPTH) + FillFileListRecursively(itr->path(), storage, state, depth + 1); + } + else if (itr->path().extension() == ".sql") + { + LOG_TRACE("sql.updates", "Added locale file \"%s\".", itr->path().filename().generic_string().c_str()); + + LocaleFileEntry const entry = { itr->path(), state }; + + // Check for doubled filenames + // Because elements are only compared by their filenames, this is ok + if (storage.find(entry) != storage.end()) + { + LOG_FATAL("sql.updates", "Duplicate filename \"%s\" occurred. Because updates are ordered " \ + "by their filenames, every name needs to be unique!", itr->path().generic_string().c_str()); + + throw UpdateException("Updating failed, see the log for details."); + } + + storage.insert(entry); + } + } +} + +UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() const +{ + DirectoryStorage directories; + + QueryResult const result = _retrieve("SELECT `path`, `state` FROM `updates_include`"); + if (!result) + return directories; + + do + { + Field* fields = result->Fetch(); + + std::string path = fields[0].GetString(); + if (path.substr(0, 1) == "$") + path = _sourceDirectory->generic_string() + path.substr(1); + + Path const p(path); + + if (!is_directory(p)) + { + LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"%s\" does not exist, skipped!", p.generic_string().c_str()); + continue; + } + + DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert(fields[1].GetString()) }; + directories.push_back(entry); + + LOG_TRACE("sql.updates", "Added applied file \"%s\" from remote.", p.filename().generic_string().c_str()); + + } while (result->NextRow()); + + std::vector moduleList; + + auto const& _modulesTokens = Warhead::Tokenize(WH_MODULES_LIST, ',', true); + for (auto const& itr : _modulesTokens) + moduleList.push_back(std::string(itr)); + + for (auto const& itr : moduleList) + { + std::string path = _sourceDirectory->generic_string() + "/modules/" + itr + "/sql/" + _dbModuleName; // module/mod-name/sql/db_world + + Path const p(path); + if (!is_directory(p)) + continue; + + DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert("RELEASED") }; + directories.push_back(entry); + + LOG_TRACE("sql.updates", "Added applied modules file \"%s\" from remote.", p.filename().generic_string().c_str()); + } + + return directories; +} + +UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const +{ + AppliedFileStorage map; + + QueryResult result = _retrieve("SELECT `name`, `hash`, `state`, UNIX_TIMESTAMP(`timestamp`) FROM `updates` ORDER BY `name` ASC"); + if (!result) + return map; + + do + { + Field* fields = result->Fetch(); + + AppliedFileEntry const entry = { fields[0].GetString(), fields[1].GetString(), + AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt64() + }; + + map.insert(std::make_pair(entry.name, entry)); + } while (result->NextRow()); + + return map; +} + +std::string UpdateFetcher::ReadSQLUpdate(Path const& file) const +{ + std::ifstream in(file.c_str()); + if (!in.is_open()) + { + LOG_FATAL("sql.updates", "Failed to open the sql update \"%s\" for reading! " + "Stopping the server to keep the database integrity, " + "try to identify and solve the issue or disable the database updater.", + file.generic_string().c_str()); + + throw UpdateException("Opening the sql update failed!"); + } + + auto update = [&in] + { + std::ostringstream ss; + ss << in.rdbuf(); + return ss.str(); + }(); + + in.close(); + return update; +} + +UpdateResult UpdateFetcher::Update(bool const redundancyChecks, + bool const allowRehash, + bool const archivedRedundancy, + int32 const cleanDeadReferencesMaxCount) const +{ + LocaleFileStorage const available = GetFileList(); + AppliedFileStorage applied = ReceiveAppliedFiles(); + + size_t countRecentUpdates = 0; + size_t countArchivedUpdates = 0; + + // Count updates + for (auto const& entry : applied) + if (entry.second.state == RELEASED) + ++countRecentUpdates; + else + ++countArchivedUpdates; + + // Fill hash to name cache + HashToFileNameStorage hashToName; + for (auto entry : applied) + hashToName.insert(std::make_pair(entry.second.hash, entry.first)); + + size_t importedUpdates = 0; + + for (auto const& availableQuery : available) + { + LOG_DEBUG("sql.updates", "Checking update \"%s\"...", availableQuery.first.filename().generic_string().c_str()); + + AppliedFileStorage::const_iterator iter = applied.find(availableQuery.first.filename().string()); + if (iter != applied.end()) + { + // If redundancy is disabled, skip it, because the update is already applied. + if (!redundancyChecks) + { + LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks."); + applied.erase(iter); + continue; + } + + // If the update is in an archived directory and is marked as archived in our database, skip redundancy checks (archived updates never change). + if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (availableQuery.second == ARCHIVED)) + { + LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks."); + applied.erase(iter); + continue; + } + } + + std::string const hash = ByteArrayToHexStr(Warhead::Crypto::SHA1::GetDigestOf(ReadSQLUpdate(availableQuery.first))); + + UpdateMode mode = MODE_APPLY; + + // Update is not in our applied list + if (iter == applied.end()) + { + // Catch renames (different filename, but same hash) + HashToFileNameStorage::const_iterator const hashIter = hashToName.find(hash); + if (hashIter != hashToName.end()) + { + // Check if the original file was removed. If not, we've got a problem. + LocaleFileStorage::const_iterator localeIter; + // Push localeIter forward + for (localeIter = available.begin(); (localeIter != available.end()) && + (localeIter->first.filename().string() != hashIter->second); ++localeIter); + + // Conflict! + if (localeIter != available.end()) + { + LOG_WARN("sql.updates", ">> It seems like the update \"%s\" \'%s\' was renamed, but the old file is still there! " \ + "Treating it as a new file! (It is probably an unmodified copy of the file \"%s\")", + availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str(), + localeIter->first.filename().string().c_str()); + } + // It is safe to treat the file as renamed here + else + { + LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.", + hashIter->second.c_str(), availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str()); + + RenameEntry(hashIter->second, availableQuery.first.filename().string()); + applied.erase(hashIter->second); + continue; + } + } + // Apply the update if it was never seen before. + else + { + LOG_INFO("sql.updates", ">> Applying update \"%s\" \'%s\'...", + availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str()); + } + } + // Rehash the update entry if it exists in our database with an empty hash. + else if (allowRehash && iter->second.hash.empty()) + { + mode = MODE_REHASH; + + LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", availableQuery.first.filename().string().c_str(), + hash.substr(0, 7).c_str()); + } + else + { + // If the hash of the files differs from the one stored in our database, reapply the update (because it changed). + if (iter->second.hash != hash) + { + LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", availableQuery.first.filename().string().c_str(), + iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str()); + } + else + { + // If the file wasn't changed and just moved, update its state (if necessary). + if (iter->second.state != availableQuery.second) + { + LOG_DEBUG("sql.updates", ">> Updating the state of \"%s\" to \'%s\'...", + availableQuery.first.filename().string().c_str(), AppliedFileEntry::StateConvert(availableQuery.second).c_str()); + + UpdateState(availableQuery.first.filename().string(), availableQuery.second); + } + + LOG_DEBUG("sql.updates", ">> Update is already applied and matches the hash \'%s\'.", hash.substr(0, 7).c_str()); + + applied.erase(iter); + continue; + } + } + + uint32 speed = 0; + AppliedFileEntry const file = { availableQuery.first.filename().string(), hash, availableQuery.second, 0 }; + + switch (mode) + { + case MODE_APPLY: + speed = Apply(availableQuery.first); + /* fallthrough */ + case MODE_REHASH: + UpdateEntry(file, speed); + break; + } + + if (iter != applied.end()) + applied.erase(iter); + + if (mode == MODE_APPLY) + ++importedUpdates; + } + + // Cleanup up orphaned entries (if enabled) + if (!applied.empty()) + { + bool const doCleanup = (cleanDeadReferencesMaxCount < 0) || (applied.size() <= static_cast(cleanDeadReferencesMaxCount)); + + for (auto const& entry : applied) + { + LOG_WARN("sql.updates", ">> The file \'%s\' was applied to the database, but is missing in" \ + " your update directory now!", entry.first.c_str()); + + if (doCleanup) + LOG_INFO("sql.updates", "Deleting orphaned entry \'%s\'...", entry.first.c_str()); + } + + if (doCleanup) + CleanUp(applied); + else + { + LOG_ERROR("sql.updates", "Cleanup is disabled! There were " SZFMTD " dirty files applied to your database, " \ + "but they are now missing in your source directory!", applied.size()); + } + } + + return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates); +} + +uint32 UpdateFetcher::Apply(Path const& path) const +{ + using Time = std::chrono::high_resolution_clock; + + // Benchmark query speed + auto const begin = Time::now(); + + // Update database + _applyFile(path); + + // Return the time it took the query to apply + return uint32(std::chrono::duration_cast(Time::now() - begin).count()); +} + +void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const +{ + std::string const update = "REPLACE INTO `updates` (`name`, `hash`, `state`, `speed`) VALUES (\"" + + entry.name + "\", \"" + entry.hash + "\", \'" + entry.GetStateAsString() + "\', " + std::to_string(speed) + ")"; + + // Update database + _apply(update); +} + +void UpdateFetcher::RenameEntry(std::string const& from, std::string const& to) const +{ + // Delete the target if it exists + { + std::string const update = "DELETE FROM `updates` WHERE `name`=\"" + to + "\""; + + // Update database + _apply(update); + } + + // Rename + { + std::string const update = "UPDATE `updates` SET `name`=\"" + to + "\" WHERE `name`=\"" + from + "\""; + + // Update database + _apply(update); + } +} + +void UpdateFetcher::CleanUp(AppliedFileStorage const& storage) const +{ + if (storage.empty()) + return; + + std::stringstream update; + size_t remaining = storage.size(); + + update << "DELETE FROM `updates` WHERE `name` IN("; + + for (auto const& entry : storage) + { + update << "\"" << entry.first << "\""; + if ((--remaining) > 0) + update << ", "; + } + + update << ")"; + + // Update database + _apply(update.str()); +} + +void UpdateFetcher::UpdateState(std::string const& name, State const state) const +{ + std::string const update = "UPDATE `updates` SET `state`=\'" + AppliedFileEntry::StateConvert(state) + "\' WHERE `name`=\"" + name + "\""; + + // Update database + _apply(update); +} + +bool UpdateFetcher::PathCompare::operator()(LocaleFileEntry const& left, LocaleFileEntry const& right) const +{ + return left.first.filename().string() < right.first.filename().string(); +} diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h new file mode 100644 index 000000000..e2d0702af --- /dev/null +++ b/src/server/database/Updater/UpdateFetcher.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 + * Copyright (C) 2021+ WarheadCore + */ + +#ifndef UpdateFetcher_h__ +#define UpdateFetcher_h__ + +#include "DatabaseEnv.h" +#include "Define.h" +#include +#include +#include +#include + +namespace boost::filesystem +{ + class path; +} + +struct AC_DATABASE_API UpdateResult +{ + UpdateResult() + : updated(0), recent(0), archived(0) { } + + UpdateResult(size_t const updated_, size_t const recent_, size_t const archived_) + : updated(updated_), recent(recent_), archived(archived_) { } + + size_t updated; + size_t recent; + size_t archived; +}; + +class AC_DATABASE_API UpdateFetcher +{ + typedef boost::filesystem::path Path; + +public: + UpdateFetcher(Path const& updateDirectory, + std::function const& apply, + std::function const& applyFile, + std::function const& retrieve, std::string const& dbModuleName); + ~UpdateFetcher(); + + UpdateResult Update(bool const redundancyChecks, bool const allowRehash, + bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const; + +private: + enum UpdateMode + { + MODE_APPLY, + MODE_REHASH + }; + + enum State + { + RELEASED, + ARCHIVED + }; + + struct AppliedFileEntry + { + AppliedFileEntry(std::string const& name_, std::string const& hash_, State state_, uint64 timestamp_) + : name(name_), hash(hash_), state(state_), timestamp(timestamp_) { } + + std::string const name; + + std::string const hash; + + State const state; + + uint64 const timestamp; + + static inline State StateConvert(std::string const& state) + { + return (state == "RELEASED") ? RELEASED : ARCHIVED; + } + + static inline std::string StateConvert(State const state) + { + return (state == RELEASED) ? "RELEASED" : "ARCHIVED"; + } + + std::string GetStateAsString() const + { + return StateConvert(state); + } + }; + + struct DirectoryEntry; + + typedef std::pair LocaleFileEntry; + + struct PathCompare + { + bool operator()(LocaleFileEntry const& left, LocaleFileEntry const& right) const; + }; + + typedef std::set LocaleFileStorage; + typedef std::unordered_map HashToFileNameStorage; + typedef std::unordered_map AppliedFileStorage; + typedef std::vector DirectoryStorage; + + LocaleFileStorage GetFileList() const; + void FillFileListRecursively(Path const& path, LocaleFileStorage& storage, + State const state, uint32 const depth) const; + + DirectoryStorage ReceiveIncludedDirectories() const; + AppliedFileStorage ReceiveAppliedFiles() const; + + std::string ReadSQLUpdate(Path const& file) const; + + uint32 Apply(Path const& path) const; + + void UpdateEntry(AppliedFileEntry const& entry, uint32 const speed = 0) const; + void RenameEntry(std::string const& from, std::string const& to) const; + void CleanUp(AppliedFileStorage const& storage) const; + + void UpdateState(std::string const& name, State const state) const; + + std::unique_ptr const _sourceDirectory; + + std::function const _apply; + std::function const _applyFile; + std::function const _retrieve; + + // modules + std::string const _dbModuleName; +}; + +#endif // UpdateFetcher_h__ diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index e7dfce587..f0b8f82cb 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -39,7 +39,7 @@ void SmartWaypointMgr::LoadFromDB() waypoint_map.clear(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) @@ -109,7 +109,7 @@ void SmartAIMgr::LoadSmartAIFromDB() for (uint8 i = 0; i < SMART_SCRIPT_TYPE_MAX; i++) mEventMap[i].clear(); //Drop Existing SmartAI List - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMART_SCRIPTS); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMART_SCRIPTS); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 0835d8d03..3d3ce4249 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -31,7 +31,7 @@ namespace AccountMgr if (GetId(username)) return AOR_NAME_ALREADY_EXIST; // username does already exist - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT); stmt->setString(0, username); auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, password); @@ -51,16 +51,15 @@ namespace AccountMgr AccountOpResult DeleteAccount(uint32 accountId) { // Check if accounts exists - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); - stmt->setUInt32(0, accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); + LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); + loginStmt->setUInt32(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(loginStmt); if (!result) return AOR_NAME_NOT_EXIST; // Obtain accounts characters - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); - + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); stmt->setUInt32(0, accountId); result = CharacterDatabase.Query(stmt); @@ -96,27 +95,27 @@ namespace AccountMgr stmt->setUInt32(0, accountId); CharacterDatabase.Execute(stmt); - SQLTransaction trans = LoginDatabase.BeginTransaction(); + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT); - stmt->setUInt32(0, accountId); - trans->Append(stmt); + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT); + loginStmt->setUInt32(0, accountId); + trans->Append(loginStmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); - stmt->setUInt32(0, accountId); - trans->Append(stmt); + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); + loginStmt->setUInt32(0, accountId); + trans->Append(loginStmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS); - stmt->setUInt32(0, accountId); - trans->Append(stmt); + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS); + loginStmt->setUInt32(0, accountId); + trans->Append(loginStmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED); - stmt->setUInt32(0, accountId); - trans->Append(stmt); + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED); + loginStmt->setUInt32(0, accountId); + trans->Append(loginStmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_MUTED); - stmt->setUInt32(0, accountId); - trans->Append(stmt); + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_MUTED); + loginStmt->setUInt32(0, accountId); + trans->Append(loginStmt); LoginDatabase.CommitTransaction(trans); @@ -126,7 +125,7 @@ namespace AccountMgr AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword) { // Check if accounts exists - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); stmt->setUInt32(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -178,7 +177,7 @@ namespace AccountMgr auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, newPassword); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); stmt->setBinary(0, salt); stmt->setBinary(1, verifier); stmt->setUInt32(2, accountId); @@ -190,7 +189,7 @@ namespace AccountMgr uint32 GetId(std::string const& username) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); stmt->setString(0, username); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -199,7 +198,7 @@ namespace AccountMgr uint32 GetSecurity(uint32 accountId) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); stmt->setUInt32(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -208,7 +207,7 @@ namespace AccountMgr uint32 GetSecurity(uint32 accountId, int32 realmId) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); stmt->setUInt32(0, accountId); stmt->setInt32(1, realmId); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -218,7 +217,7 @@ namespace AccountMgr bool GetName(uint32 accountId, std::string& name) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); stmt->setUInt32(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -241,7 +240,7 @@ namespace AccountMgr Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(password); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); stmt->setUInt32(0, accountId); if (PreparedQueryResult result = LoginDatabase.Query(stmt)) { @@ -257,7 +256,7 @@ namespace AccountMgr uint32 GetCharactersCount(uint32 accountId) { // check character count - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); stmt->setUInt32(0, accountId); PreparedQueryResult result = CharacterDatabase.Query(stmt); diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 2789eac77..64577264b 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -521,9 +521,9 @@ void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaCondition condi void AchievementMgr::DeleteFromDB(ObjectGuid::LowType lowguid) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT); stmt->setUInt32(0, lowguid); trans->Append(stmt); @@ -534,7 +534,7 @@ void AchievementMgr::DeleteFromDB(ObjectGuid::LowType lowguid) CharacterDatabase.CommitTransaction(trans); } -void AchievementMgr::SaveToDB(SQLTransaction& trans) +void AchievementMgr::SaveToDB(CharacterDatabaseTransaction trans) { if (!m_completedAchievements.empty()) { @@ -543,7 +543,7 @@ void AchievementMgr::SaveToDB(SQLTransaction& trans) if (!iter->second.changed) continue; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT); stmt->setUInt16(0, iter->first); stmt->setUInt32(1, GetPlayer()->GetGUID().GetCounter()); trans->Append(stmt); @@ -567,7 +567,7 @@ void AchievementMgr::SaveToDB(SQLTransaction& trans) if (!iter->second.changed) continue; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA); stmt->setUInt32(0, GetPlayer()->GetGUID().GetCounter()); stmt->setUInt16(1, iter->first); trans->Append(stmt); @@ -632,7 +632,7 @@ void AchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, PreparedQ // we will remove not existed criteria for all characters LOG_ERROR("achievement", "Non-existing achievement criteria %u data removed from table `character_achievement_progress`.", id); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA); stmt->setUInt16(0, uint16(id)); @@ -2264,7 +2264,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) draft = MailDraft(subject, text); } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); Item* item = reward->itemId ? Item::CreateItem(reward->itemId, 1, GetPlayer()) : nullptr; if (item) @@ -2788,7 +2788,8 @@ void AchievementGlobalMgr::LoadCompletedAchievements() // Remove non existent achievements from all characters LOG_ERROR("achievement", "Non-existing achievement %u data removed from table `character_achievement`.", achievementId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEVMENT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEVMENT); + stmt->setUInt16(0, uint16(achievementId)); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Achievements/AchievementMgr.h b/src/server/game/Achievements/AchievementMgr.h index 6035cc017..ef7ea3821 100644 --- a/src/server/game/Achievements/AchievementMgr.h +++ b/src/server/game/Achievements/AchievementMgr.h @@ -261,7 +261,7 @@ public: void Reset(); static void DeleteFromDB(ObjectGuid::LowType lowguid); void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult); - void SaveToDB(SQLTransaction& trans); + void SaveToDB(CharacterDatabaseTransaction trans); void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint32 value, bool evenIfCriteriaComplete = false); void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, Unit* unit = nullptr); void CompletedAchievement(AchievementEntry const* entry); diff --git a/src/server/game/Addons/AddonMgr.cpp b/src/server/game/Addons/AddonMgr.cpp index f3c08d267..9d035efcf 100644 --- a/src/server/game/Addons/AddonMgr.cpp +++ b/src/server/game/Addons/AddonMgr.cpp @@ -90,7 +90,7 @@ namespace AddonMgr { std::string name = addon.Name; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ADDON); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ADDON); stmt->setString(0, name); stmt->setUInt32(1, addon.CRC); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 773f1e16f..91adc5cdd 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -101,7 +101,7 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 } //does not clear ram -void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail) +void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail) { Item* pItem = GetAItem(auction->item_guid); if (!pItem) @@ -120,7 +120,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& sScriptMgr->OnBeforeAuctionHouseMgrSendAuctionWonMail(this, auction, bidder, bidder_accId, sendNotification, updateAchievementCriteria, sendMail); // set owner to bidder (to prevent delete item with sender char deleting) // owner in `data` will set at mail receive and item extracting - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER); stmt->setUInt32(0, auction->bidder.GetCounter()); stmt->setUInt32(1, pItem->GetGUID().GetCounter()); trans->Append(stmt); @@ -143,7 +143,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans); } -void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail) +void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail) { Player* owner = ObjectAccessor::FindConnectedPlayer(auction->owner); uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(auction->owner.GetCounter()); @@ -158,7 +158,7 @@ void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, SQLTrans } //call this method to send mail to auction owner, when auction is successful, it does not clear ram -void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail) +void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail) { Player* owner = ObjectAccessor::FindConnectedPlayer(auction->owner); uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(auction->owner.GetCounter()); @@ -203,7 +203,7 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransa } //does not clear ram -void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification, bool sendMail) +void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool sendMail) { //return an item in auction to its owner by mail Item* pItem = GetAItem(auction->item_guid); @@ -231,7 +231,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, SQLTransacti } //this function sends mail to old bidder -void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, SQLTransaction& trans, bool sendNotification, bool sendMail) +void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, CharacterDatabaseTransaction trans, bool sendNotification, bool sendMail) { Player* oldBidder = ObjectAccessor::FindConnectedPlayer(auction->bidder); @@ -255,7 +255,7 @@ void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 new } //this function sends mail, when auction is cancelled to old bidder -void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail) +void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail) { Player* bidder = ObjectAccessor::FindConnectedPlayer(auction->bidder); @@ -288,7 +288,7 @@ void AuctionHouseMgr::LoadAuctionItems() } // data needs to be at first place for Item::LoadFromDB - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTION_ITEMS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTION_ITEMS); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) @@ -333,7 +333,7 @@ void AuctionHouseMgr::LoadAuctions() { uint32 oldMSTime = getMSTime(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTIONS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTIONS); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) @@ -345,7 +345,7 @@ void AuctionHouseMgr::LoadAuctions() uint32 count = 0; - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); do { Field* fields = result->Fetch(); @@ -375,7 +375,7 @@ void AuctionHouseMgr::AddAItem(Item* it) mAitems[it->GetGUID()] = it; } -bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB, SQLTransaction* trans /*= nullptr*/) +bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB, CharacterDatabaseTransaction* trans /*= nullptr*/) { ItemMap::iterator i = mAitems.find(itemGuid); if (i == mAitems.end()) @@ -458,7 +458,7 @@ void AuctionHouseObject::Update() if (AuctionsMap.empty()) return; - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); for (AuctionEntryMap::iterator itr, iter = AuctionsMap.begin(); iter != AuctionsMap.end(); ) { @@ -715,16 +715,16 @@ uint32 AuctionEntry::GetAuctionOutBid() const return outbid ? outbid : 1; } -void AuctionEntry::DeleteFromDB(SQLTransaction& trans) const +void AuctionEntry::DeleteFromDB(CharacterDatabaseTransaction trans) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION); stmt->setUInt32(0, Id); trans->Append(stmt); } -void AuctionEntry::SaveToDB(SQLTransaction& trans) const +void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION); stmt->setUInt32(0, Id); stmt->setUInt8(1, houseId); stmt->setUInt32(2, item_guid.GetCounter()); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 8a8f6392c..265272af0 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -79,8 +79,8 @@ struct AuctionEntry [[nodiscard]] uint32 GetAuctionCut() const; [[nodiscard]] uint32 GetAuctionOutBid() const; bool BuildAuctionInfo(WorldPacket& data) const; - void DeleteFromDB(SQLTransaction& trans) const; - void SaveToDB(SQLTransaction& trans) const; + void DeleteFromDB(CharacterDatabaseTransaction trans) const; + void SaveToDB(CharacterDatabaseTransaction trans) const; bool LoadFromDB(Field* fields); [[nodiscard]] std::string BuildAuctionMailSubject(MailAuctionAnswers response) const; static std::string BuildAuctionMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 cut); @@ -102,8 +102,9 @@ public: [[nodiscard]] uint32 Getcount() const { return AuctionsMap.size(); } - AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();} - AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();} + AuctionEntryMap::iterator GetAuctionsBegin() { return AuctionsMap.begin(); } + AuctionEntryMap::iterator GetAuctionsEnd() { return AuctionsMap.end(); } + AuctionEntryMap const& GetAuctions() { return AuctionsMap; } [[nodiscard]] AuctionEntry* GetAuction(uint32 id) const { @@ -156,12 +157,12 @@ public: } //auction messages - void SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true); - void SendAuctionSalePendingMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail = true); - void SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true); - void SendAuctionExpiredMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification = true, bool sendMail = true); - void SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, SQLTransaction& trans, bool sendNotification = true, bool sendMail = true); - void SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail = true); + void SendAuctionWonMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true); + void SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail = true); + void SendAuctionSuccessfulMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true); + void SendAuctionExpiredMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification = true, bool sendMail = true); + void SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, CharacterDatabaseTransaction trans, bool sendNotification = true, bool sendMail = true); + void SendAuctionCancelledToBidderMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail = true); static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count); static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId); @@ -173,7 +174,7 @@ public: void LoadAuctions(); void AddAItem(Item* it); - bool RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB = false, SQLTransaction* trans = nullptr); + bool RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB = false, CharacterDatabaseTransaction* trans = nullptr); void Update(); diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.h b/src/server/game/Battlefield/Zones/BattlefieldWG.h index 95803b9d1..aeef7afb0 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.h +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.h @@ -9,6 +9,7 @@ #include "Battlefield.h" #include "ObjectAccessor.h" +#include "Log.h" #include "World.h" #include "WorldPacket.h" diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp index 7a758486b..60551ed3d 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.cpp +++ b/src/server/game/Battlegrounds/ArenaTeam.cpp @@ -56,7 +56,7 @@ bool ArenaTeam::Create(ObjectGuid captainGuid, uint8 type, std::string const& te BorderColor = borderColor; // Save arena team to db - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_TEAM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_TEAM); stmt->setUInt32(0, TeamId); stmt->setString(1, TeamName); stmt->setUInt32(2, captainGuid.GetCounter()); @@ -120,7 +120,7 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid) // xinef: zomg! sync query // Try to get player's match maker rating from db and fall back to config setting if not found - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MATCH_MAKER_RATING); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MATCH_MAKER_RATING); stmt->setUInt32(0, playerGuid.GetCounter()); stmt->setUInt8(1, GetSlot()); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -271,7 +271,7 @@ bool ArenaTeam::SetName(std::string const& name) return false; TeamName = name; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_NAME); stmt->setString(0, TeamName); stmt->setUInt32(1, GetId()); CharacterDatabase.Execute(stmt); @@ -289,7 +289,7 @@ void ArenaTeam::SetCaptain(ObjectGuid guid) CaptainGuid = guid; // Update database - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_CAPTAIN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_CAPTAIN); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt32(1, GetId()); CharacterDatabase.Execute(stmt); @@ -359,7 +359,7 @@ void ArenaTeam::DelMember(ObjectGuid guid, bool cleanDb) // Only used for single member deletion, for arena team disband we use a single query for more efficiency if (cleanDb) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM_MEMBER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM_MEMBER); stmt->setUInt32(0, GetId()); stmt->setUInt32(1, guid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -379,9 +379,9 @@ void ArenaTeam::Disband(WorldSession* session) } // Update database - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM); stmt->setUInt32(0, TeamId); trans->Append(stmt); @@ -402,9 +402,9 @@ void ArenaTeam::Disband() DelMember(Members.front().Guid, false); // Update database - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM); stmt->setUInt32(0, TeamId); trans->Append(stmt); @@ -921,9 +921,9 @@ void ArenaTeam::SaveToDB() // Save team and member stats to db // Called after a match has ended or when calculating arena_points - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_STATS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_STATS); stmt->setUInt16(0, Stats.Rating); stmt->setUInt16(1, Stats.WeekGames); stmt->setUInt16(2, Stats.WeekWins); diff --git a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp index 3d4821bd4..8ea00a5a8 100644 --- a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp +++ b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp @@ -198,16 +198,16 @@ void ArenaTeamMgr::DistributeArenaPoints() sScriptMgr->OnBeforeUpdateArenaPoints(at, PlayerPoints); } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt; + CharacterDatabasePreparedStatement* stmt; // Cycle that gives points to all players for (std::map::iterator playerItr = PlayerPoints.begin(); playerItr != PlayerPoints.end(); ++playerItr) { // Add points to player if online if (Player* player = ObjectAccessor::FindPlayer(playerItr->first)) - player->ModifyArenaPoints(playerItr->second, &trans); + player->ModifyArenaPoints(playerItr->second, trans); else // Update database { stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ARENA_POINTS); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 005961316..ec409ee13 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -768,7 +768,7 @@ void Battleground::EndBattleground(TeamId winnerTeamId) else SetWinner(TEAM_NEUTRAL); - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; uint64 battlegroundId = 1; if (isBattleground() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE)) { @@ -819,8 +819,8 @@ void Battleground::EndBattleground(TeamId winnerTeamId) uint32 fightId = sArenaTeamMgr->GetNextArenaLogId(); uint32 currOnline = (uint32)(sWorld->GetActiveSessionCount()); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT); stmt2->setUInt32(0, fightId); stmt2->setUInt8(1, m_ArenaType); stmt2->setUInt32(2, ((GetStartTime() <= startDelay ? 0 : GetStartTime() - startDelay) / 1000)); @@ -878,8 +878,8 @@ void Battleground::EndBattleground(TeamId winnerTeamId) uint32 fightId = sArenaTeamMgr->GetNextArenaLogId(); uint32 currOnline = (uint32)(sWorld->GetActiveSessionCount()); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt3 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt3 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT); stmt3->setUInt32(0, fightId); stmt3->setUInt8(1, m_ArenaType); stmt3->setUInt32(2, ((GetStartTime() <= startDelay ? 0 : GetStartTime() - startDelay) / 1000)); diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp index 90ae07db2..325d1f0fe 100644 --- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp +++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp @@ -1165,7 +1165,7 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); stmt->setUInt32(0, player->GetGUID().GetCounter()); stmt->setUInt8(1, BG_DESERTION_TYPE_NO_ENTER_BUTTON); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Calendar/CalendarMgr.cpp b/src/server/game/Calendar/CalendarMgr.cpp index 84e0bea06..f3061775c 100644 --- a/src/server/game/Calendar/CalendarMgr.cpp +++ b/src/server/game/Calendar/CalendarMgr.cpp @@ -121,13 +121,7 @@ void CalendarMgr::AddEvent(CalendarEvent* calendarEvent, CalendarSendEventType s SendCalendarEvent(calendarEvent->GetCreatorGUID(), *calendarEvent, sendType); } -void CalendarMgr::AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite) -{ - SQLTransaction dummy; - AddInvite(calendarEvent, invite, dummy); -} - -void CalendarMgr::AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, SQLTransaction& trans) +void CalendarMgr::AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, CharacterDatabaseTransaction trans) { if (!calendarEvent->IsGuildAnnouncement()) SendCalendarEventInvite(*invite); @@ -165,8 +159,8 @@ void CalendarMgr::RemoveEvent(CalendarEvent* calendarEvent, ObjectGuid remover) SendCalendarEventRemovedAlert(*calendarEvent); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt; + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt; MailDraft mail(calendarEvent->BuildCalendarMailSubject(remover), calendarEvent->BuildCalendarMailBody()); CalendarInviteStore& eventInvites = _invites[calendarEvent->GetEventId()]; @@ -212,8 +206,8 @@ void CalendarMgr::RemoveInvite(uint64 inviteId, uint64 eventId, ObjectGuid /*rem if (itr == _invites[eventId].end()) return; - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CALENDAR_INVITE); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CALENDAR_INVITE); stmt->setUInt64(0, (*itr)->GetInviteId()); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); @@ -234,7 +228,7 @@ void CalendarMgr::RemoveInvite(uint64 inviteId, uint64 eventId, ObjectGuid /*rem void CalendarMgr::UpdateEvent(CalendarEvent* calendarEvent) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_EVENT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_EVENT); stmt->setUInt64(0, calendarEvent->GetEventId()); stmt->setUInt32(1, calendarEvent->GetCreatorGUID().GetCounter()); stmt->setString(2, calendarEvent->GetTitle()); @@ -247,15 +241,9 @@ void CalendarMgr::UpdateEvent(CalendarEvent* calendarEvent) CharacterDatabase.Execute(stmt); } -void CalendarMgr::UpdateInvite(CalendarInvite* invite) +void CalendarMgr::UpdateInvite(CalendarInvite* invite, CharacterDatabaseTransaction trans) { - SQLTransaction dummy; - UpdateInvite(invite, dummy); -} - -void CalendarMgr::UpdateInvite(CalendarInvite* invite, SQLTransaction& trans) -{ - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_INVITE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_INVITE); stmt->setUInt64(0, invite->GetInviteId()); stmt->setUInt64(1, invite->GetEventId()); stmt->setUInt32(2, invite->GetInviteeGUID().GetCounter()); diff --git a/src/server/game/Calendar/CalendarMgr.h b/src/server/game/Calendar/CalendarMgr.h index 59d8ddcbc..b42cd7922 100644 --- a/src/server/game/Calendar/CalendarMgr.h +++ b/src/server/game/Calendar/CalendarMgr.h @@ -307,11 +307,10 @@ public: void RemoveEvent(CalendarEvent* calendarEvent, ObjectGuid remover); void UpdateEvent(CalendarEvent* calendarEvent); - void AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite); - void AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, SQLTransaction& trans); + void AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, CharacterDatabaseTransaction trans = nullptr); void RemoveInvite(uint64 inviteId, uint64 eventId, ObjectGuid remover); - void UpdateInvite(CalendarInvite* invite); - void UpdateInvite(CalendarInvite* invite, SQLTransaction& trans); + void RemoveInvite(uint64 inviteId, uint64 eventId, uint64 remover); + void UpdateInvite(CalendarInvite* invite, CharacterDatabaseTransaction trans = nullptr); void RemoveAllPlayerEventsAndInvites(ObjectGuid guid); void RemovePlayerGuildEventsAndSignups(ObjectGuid guid, uint32 guildId); diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp index e7598075b..16d64bf5c 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -70,7 +70,7 @@ Channel::Channel(std::string const& name, uint32 channelId, uint32 channelDBId, { _channelDBId = ++ChannelMgr::_channelIdMax; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL); stmt->setUInt32(0, _channelDBId); stmt->setString(1, name); stmt->setUInt32(2, _teamId); @@ -90,7 +90,7 @@ void Channel::UpdateChannelInDB() const { if (_IsSaved) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL); stmt->setBool(0, _announce); stmt->setString(1, _password); stmt->setUInt32(2, _channelDBId); @@ -102,14 +102,14 @@ void Channel::UpdateChannelInDB() const void Channel::UpdateChannelUseageInDB() const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_USAGE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_USAGE); stmt->setUInt32(0, _channelDBId); CharacterDatabase.Execute(stmt); } void Channel::AddChannelBanToDB(ObjectGuid guid, uint32 time) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL_BAN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL_BAN); stmt->setUInt32(0, _channelDBId); stmt->setUInt32(1, guid.GetCounter()); stmt->setUInt32(2, time); @@ -118,7 +118,7 @@ void Channel::AddChannelBanToDB(ObjectGuid guid, uint32 time) const void Channel::RemoveChannelBanFromDB(ObjectGuid guid) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL_BAN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL_BAN); stmt->setUInt32(0, _channelDBId); stmt->setUInt32(1, guid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -128,9 +128,9 @@ void Channel::CleanOldChannelsInDB() { if (sWorld->getIntConfig(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION) > 0) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_OLD_CHANNELS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_OLD_CHANNELS); stmt->setUInt32(0, sWorld->getIntConfig(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION) * DAY); trans->Append(stmt); diff --git a/src/server/game/Chat/Channels/ChannelMgr.cpp b/src/server/game/Chat/Channels/ChannelMgr.cpp index 54fb1a76d..4ff827898 100644 --- a/src/server/game/Chat/Channels/ChannelMgr.cpp +++ b/src/server/game/Chat/Channels/ChannelMgr.cpp @@ -94,7 +94,7 @@ void ChannelMgr::LoadChannels() for (auto pair : toDelete) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL); stmt->setString(0, pair.first); stmt->setUInt32(1, pair.second); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 04361aa82..a185d3322 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -41,7 +41,7 @@ std::vector const& ChatHandler::getCommandTable() std::vector cmds = sScriptMgr->GetChatCommands(); commandTableCache.swap(cmds); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS); PreparedQueryResult result = WorldDatabase.Query(stmt); if (result) { diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index d504ebd30..a2f0593f3 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -85,7 +85,7 @@ namespace lfg if (!guid.IsGroup()) return; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_LFG_DATA); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_LFG_DATA); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt32(1, GetDungeon(guid)); stmt->setUInt32(2, GetState(guid)); diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index f4d0222b8..b155857d0 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -81,10 +81,10 @@ bool Corpse::Create(ObjectGuid::LowType guidlow, Player* owner) void Corpse::SaveToDB() { // prevent DB data inconsistence problems and duplicates - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); DeleteFromDB(trans); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CORPSE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CORPSE); stmt->setUInt32(0, GetOwnerGUID().GetCounter()); // guid stmt->setFloat (1, GetPositionX()); // posX stmt->setFloat (2, GetPositionY()); // posY @@ -107,14 +107,14 @@ void Corpse::SaveToDB() CharacterDatabase.CommitTransaction(trans); } -void Corpse::DeleteFromDB(SQLTransaction& trans) +void Corpse::DeleteFromDB(CharacterDatabaseTransaction trans) { DeleteFromDB(GetOwnerGUID(), trans); } -void Corpse::DeleteFromDB(ObjectGuid const ownerGuid, SQLTransaction& trans) +void Corpse::DeleteFromDB(ObjectGuid const ownerGuid, CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); stmt->setUInt32(0, ownerGuid.GetCounter()); CharacterDatabase.ExecuteOrAppend(trans, stmt); } diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index 4f0b9b97e..0da99bbf1 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -49,8 +49,8 @@ public: void SaveToDB(); bool LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields); - void DeleteFromDB(SQLTransaction& trans); - static void DeleteFromDB(ObjectGuid const ownerGuid, SQLTransaction& trans); + void DeleteFromDB(CharacterDatabaseTransaction trans); + static void DeleteFromDB(ObjectGuid const ownerGuid, CharacterDatabaseTransaction trans); [[nodiscard]] ObjectGuid GetOwnerGUID() const { return GetGuidValue(CORPSE_FIELD_OWNER); } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 4c7df1aa7..c33bea18f 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1254,9 +1254,9 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.dynamicflags = dynamicflags; // update in DB - SQLTransaction trans = WorldDatabase.BeginTransaction(); + WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); stmt->setUInt32(0, m_spawnId); trans->Append(stmt); @@ -1635,9 +1635,9 @@ void Creature::DeleteFromDB() GetMap()->RemoveCreatureRespawnTime(m_spawnId); sObjectMgr->DeleteCreatureData(m_spawnId); - SQLTransaction trans = WorldDatabase.BeginTransaction(); + WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); stmt->setUInt32(0, m_spawnId); trans->Append(stmt); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 3f39df984..bbeaae26b 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -885,11 +885,11 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool data.artKit = GetGoArtKit(); // Update in DB - SQLTransaction trans = WorldDatabase.BeginTransaction(); + WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction(); uint8 index = 0; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT); stmt->setUInt32(0, m_spawnId); trans->Append(stmt); @@ -993,7 +993,7 @@ void GameObject::DeleteFromDB() GetMap()->RemoveGORespawnTime(m_spawnId); sObjectMgr->DeleteGOData(m_spawnId); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT); stmt->setUInt32(0, m_spawnId); WorldDatabase.Execute(stmt); diff --git a/src/server/game/Entities/Item/Container/Bag.cpp b/src/server/game/Entities/Item/Container/Bag.cpp index 7c941199e..20b92ab54 100644 --- a/src/server/game/Entities/Item/Container/Bag.cpp +++ b/src/server/game/Entities/Item/Container/Bag.cpp @@ -87,7 +87,7 @@ bool Bag::Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owner return true; } -void Bag::SaveToDB(SQLTransaction& trans) +void Bag::SaveToDB(CharacterDatabaseTransaction trans) { Item::SaveToDB(trans); } @@ -110,7 +110,7 @@ bool Bag::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fie return true; } -void Bag::DeleteFromDB(SQLTransaction& trans) +void Bag::DeleteFromDB(CharacterDatabaseTransaction trans) { for (uint8 i = 0; i < MAX_BAG_SIZE; ++i) if (m_bagslot[i]) diff --git a/src/server/game/Entities/Item/Container/Bag.h b/src/server/game/Entities/Item/Container/Bag.h index 4058e7cc5..3143ecc7c 100644 --- a/src/server/game/Entities/Item/Container/Bag.h +++ b/src/server/game/Entities/Item/Container/Bag.h @@ -39,11 +39,11 @@ public: // DB operations // overwrite virtual Item::SaveToDB - void SaveToDB(SQLTransaction& trans) override; + void SaveToDB(CharacterDatabaseTransaction trans) override; // overwrite virtual Item::LoadFromDB bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry) override; // overwrite virtual Item::DeleteFromDB - void DeleteFromDB(SQLTransaction& trans) override; + void DeleteFromDB(CharacterDatabaseTransaction trans) override; void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index c11c73317..e12116899 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -300,7 +300,7 @@ void Item::UpdateDuration(Player* owner, uint32 diff) SetState(ITEM_CHANGED, owner); // save new time in database } -void Item::SaveToDB(SQLTransaction& trans) +void Item::SaveToDB(CharacterDatabaseTransaction trans) { bool isInTransaction = static_cast(trans); if (!isInTransaction) @@ -313,7 +313,7 @@ void Item::SaveToDB(SQLTransaction& trans) case ITEM_CHANGED: { uint8 index = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(uState == ITEM_NEW ? CHAR_REP_ITEM_INSTANCE : CHAR_UPD_ITEM_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(uState == ITEM_NEW ? CHAR_REP_ITEM_INSTANCE : CHAR_UPD_ITEM_INSTANCE); stmt->setUInt32( index, GetEntry()); stmt->setUInt32(++index, GetOwnerGUID().GetCounter()); stmt->setUInt32(++index, GetGuidValue(ITEM_FIELD_CREATOR).GetCounter()); @@ -356,7 +356,7 @@ void Item::SaveToDB(SQLTransaction& trans) } case ITEM_REMOVED: { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); stmt->setUInt32(0, guid); trans->Append(stmt); @@ -455,7 +455,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi if (need_save) // normal item changed state set not work at loading { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD); stmt->setUInt32(0, GetUInt32Value(ITEM_FIELD_DURATION)); stmt->setUInt32(1, GetUInt32Value(ITEM_FIELD_FLAGS)); stmt->setUInt32(2, GetUInt32Value(ITEM_FIELD_DURABILITY)); @@ -467,28 +467,28 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi } /*static*/ -void Item::DeleteFromDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid) +void Item::DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid) { sScriptMgr->OnGlobalItemDelFromDB(trans, itemGuid); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); stmt->setUInt32(0, itemGuid); trans->Append(stmt); } -void Item::DeleteFromDB(SQLTransaction& trans) +void Item::DeleteFromDB(CharacterDatabaseTransaction trans) { DeleteFromDB(trans, GetGUID().GetCounter()); } /*static*/ -void Item::DeleteFromInventoryDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid) +void Item::DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM); stmt->setUInt32(0, itemGuid); trans->Append(stmt); } -void Item::DeleteFromInventoryDB(SQLTransaction& trans) +void Item::DeleteFromInventoryDB(CharacterDatabaseTransaction trans) { DeleteFromInventoryDB(trans, GetGUID().GetCounter()); } @@ -1127,9 +1127,9 @@ void Item::RemoveFromObjectUpdate() void Item::SaveRefundDataToDB() { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -1143,17 +1143,17 @@ void Item::SaveRefundDataToDB() CharacterDatabase.CommitTransaction(trans); } -void Item::DeleteRefundDataFromDB(SQLTransaction* trans) +void Item::DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans) { if (trans) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE); stmt->setUInt32(0, GetGUID().GetCounter()); (*trans)->Append(stmt); } } -void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransaction* trans /*=nullptr*/) +void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, CharacterDatabaseTransaction* trans /*=nullptr*/) { if (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) return; @@ -1225,7 +1225,7 @@ void Item::ClearSoulboundTradeable(Player* currentOwner) allowedGUIDs.clear(); SetState(ITEM_CHANGED, currentOwner); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE); stmt->setUInt32(0, GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); } diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index f3274d2b1..5563eff1e 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -218,14 +218,14 @@ public: bool IsBindedNotWith(Player const* player) const; [[nodiscard]] bool IsBoundByEnchant() const; [[nodiscard]] bool IsBoundByTempEnchant() const; - virtual void SaveToDB(SQLTransaction& trans); + virtual void SaveToDB(CharacterDatabaseTransaction trans); virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry); - static void DeleteFromDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid); - virtual void DeleteFromDB(SQLTransaction& trans); - static void DeleteFromInventoryDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid); - void DeleteFromInventoryDB(SQLTransaction& trans); + static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid); + virtual void DeleteFromDB(CharacterDatabaseTransaction trans); + static void DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid); + void DeleteFromInventoryDB(CharacterDatabaseTransaction trans); void SaveRefundDataToDB(); - void DeleteRefundDataFromDB(SQLTransaction* trans); + void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans); Bag* ToBag() { if (IsBag()) return reinterpret_cast(this); else return nullptr; } [[nodiscard]] const Bag* ToBag() const { if (IsBag()) return reinterpret_cast(this); else return nullptr; } @@ -317,7 +317,7 @@ public: [[nodiscard]] bool IsConjuredConsumable() const { return GetTemplate()->IsConjuredConsumable(); } // Item Refund system - void SetNotRefundable(Player* owner, bool changestate = true, SQLTransaction* trans = nullptr); + void SetNotRefundable(Player* owner, bool changestate = true, CharacterDatabaseTransaction* trans = nullptr); void SetRefundRecipient(ObjectGuid::LowType pGuidLow) { m_refundRecipient = pGuidLow; } void SetPaidMoney(uint32 money) { m_paidMoney = money; } void SetPaidExtendedCost(uint32 iece) { m_paidExtendedCost = iece; } diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 8972052fd..fb5b4dbbf 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -96,12 +96,11 @@ void Pet::RemoveFromWorld() SpellCastResult Pet::TryLoadFromDB(Player* owner, bool current /*= false*/, PetType mandatoryPetType /*= MAX_PET_TYPE*/) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS); stmt->setUInt32(0, owner->GetGUID().GetCounter()); stmt->setUInt8(1, uint8(current ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT)); - PreparedQueryResult result = CharacterDatabase.AsyncQuery(stmt); - + PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) return SPELL_FAILED_NO_PET; @@ -157,7 +156,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry, ui return false; ObjectGuid::LowType ownerGuid = owner->GetGUID().GetCounter(); - PreparedStatement* stmt; + CharacterDatabasePreparedStatement* stmt; if (petnumber) { @@ -191,15 +190,21 @@ bool Pet::LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry, ui stmt->setUInt8(2, uint8(PET_SAVE_LAST_STABLE_SLOT)); } - if (AsynchPetSummon* infoToDelete = owner->GetSession()->_loadPetFromDBFirstCallback.GetSecondParam()) + // load them asynchronously { - delete infoToDelete; + WorldSession* mySess = owner->GetSession(); + mySess->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback([mySess, asynchLoadType, info, owner](PreparedQueryResult result) + { + // process only if player is in world (teleport crashes?) + // otherwise wait with result till he logs in + uint8 loadResult = mySess->HandleLoadPetFromDBFirstCallback(result, asynchLoadType); + + if (loadResult != PET_LOAD_OK) + Pet::HandleAsynchLoadFailed(info, owner, asynchLoadType, loadResult); + })); } - owner->GetSession()->_loadPetFromDBFirstCallback.Reset(); - owner->GetSession()->_loadPetFromDBFirstCallback.SetFirstParam(asynchLoadType); - owner->GetSession()->_loadPetFromDBFirstCallback.SetSecondParam(info); - owner->GetSession()->_loadPetFromDBFirstCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); return true; } @@ -229,7 +234,7 @@ void Pet::SavePetToDB(PetSaveMode mode, bool logout) uint32 curhealth = GetHealth(); uint32 curmana = GetPower(POWER_MANA); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // save auras before possibly removing them _SaveAuras(trans, logout); @@ -248,7 +253,7 @@ void Pet::SavePetToDB(PetSaveMode mode, bool logout) trans = CharacterDatabase.BeginTransaction(); // remove current data - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); stmt->setUInt32(0, m_charmInfo->GetPetNumber()); trans->Append(stmt); @@ -310,9 +315,9 @@ void Pet::SavePetToDB(PetSaveMode mode, bool logout) void Pet::DeleteFromDB(ObjectGuid::LowType guidlow) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID); stmt->setUInt32(0, guidlow); trans->Append(stmt); @@ -1164,9 +1169,9 @@ void Pet::_LoadSpellCooldowns(PreparedQueryResult result) } } -void Pet::_SaveSpellCooldowns(SQLTransaction& trans, bool logout) +void Pet::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS); stmt->setUInt32(0, m_charmInfo->GetPetNumber()); trans->Append(stmt); @@ -1206,7 +1211,7 @@ void Pet::_LoadSpells(PreparedQueryResult result) } } -void Pet::_SaveSpells(SQLTransaction& trans) +void Pet::_SaveSpells(CharacterDatabaseTransaction trans) { for (PetSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) { @@ -1216,7 +1221,7 @@ void Pet::_SaveSpells(SQLTransaction& trans) if (itr->second.type == PETSPELL_FAMILY) continue; - PreparedStatement* stmt; + CharacterDatabasePreparedStatement* stmt; switch (itr->second.state) { @@ -1333,9 +1338,9 @@ void Pet::_LoadAuras(PreparedQueryResult result, uint32 timediff) } } -void Pet::_SaveAuras(SQLTransaction& trans, bool logout) +void Pet::_SaveAuras(CharacterDatabaseTransaction trans, bool logout) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURAS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURAS); stmt->setUInt32(0, m_charmInfo->GetPetNumber()); trans->Append(stmt); @@ -1395,7 +1400,7 @@ void Pet::_SaveAuras(SQLTransaction& trans, bool logout) uint8 index = 0; - PreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_AURA); + CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_AURA); stmt2->setUInt32(index++, m_charmInfo->GetPetNumber()); stmt2->setUInt64(index++, casterGUID.GetRawValue()); stmt2->setUInt32(index++, itr->second->GetId()); @@ -1411,7 +1416,6 @@ void Pet::_SaveAuras(SQLTransaction& trans, bool logout) stmt2->setInt32(index++, itr->second->GetMaxDuration()); stmt2->setInt32(index++, itr->second->GetDuration()); stmt2->setUInt8(index++, itr->second->GetCharges()); - trans->Append(stmt2); } } @@ -1426,7 +1430,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel { LOG_ERROR("entities.pet", "Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.", spellId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_PET_SPELL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_PET_SPELL); stmt->setUInt32(0, spellId); @@ -1795,7 +1799,7 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* online_pet /*= nullptr*/) uint32 except_petnumber = online_pet ? online_pet->GetCharmInfo()->GetPetNumber() : 0; // xinef: zomg! sync query - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET); stmt->setUInt32(0, owner->GetGUID().GetCounter()); stmt->setUInt32(1, except_petnumber); PreparedQueryResult resultPets = CharacterDatabase.Query(stmt); diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index 62888fc17..ad8e6475d 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -113,9 +113,9 @@ public: void ClearCastWhenWillAvailable(); void RemoveSpellCooldown(uint32 spell_id, bool update /* = false */); - void _SaveSpellCooldowns(SQLTransaction& trans, bool logout); - void _SaveAuras(SQLTransaction& trans, bool logout); - void _SaveSpells(SQLTransaction& trans); + void _SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout); + void _SaveAuras(CharacterDatabaseTransaction trans, bool logout); + void _SaveSpells(CharacterDatabaseTransaction trans); void _LoadSpellCooldowns(PreparedQueryResult result); void _LoadAuras(PreparedQueryResult result, uint32 timediff); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6ae77c6b8..955e58746 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -56,6 +56,7 @@ #include "Player.h" #include "PoolMgr.h" #include "QuestDef.h" +#include "QueryHolder.h" #include "ReputationMgr.h" #include "revision.h" #include "Realm.h" @@ -1666,7 +1667,7 @@ void Player::Update(uint32 p_time) if (GetSession()->m_muteTime && GetSession()->m_muteTime < now) { GetSession()->m_muteTime = 0; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); stmt->setInt64(0, 0); // Set the mute time to 0 stmt->setString(1, ""); stmt->setString(2, ""); @@ -1853,7 +1854,7 @@ void Player::Update(uint32 p_time) { if (m_additionalSaveTimer <= p_time) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (m_additionalSaveMask & ADDITIONAL_SAVING_INVENTORY_AND_GOLD) SaveInventoryAndGoldToDB(trans); @@ -3404,7 +3405,7 @@ void Player::GiveLevel(uint8 level) if (mailReward && sScriptMgr->CanGiveMailRewardAtGiveLevel(this, level)) { //- TODO: Poor design of mail system - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); MailDraft(mailReward->mailTemplateId).SendMailTo(trans, this, MailSender(MAIL_CREATURE, mailReward->senderEntry)); CharacterDatabase.CommitTransaction(trans); } @@ -3733,7 +3734,7 @@ void Player::UpdateNextMailTimeAndUnreads() //Update the next delivery time and unread mails time_t cTime = time(nullptr); //Get the next delivery time - PreparedStatement* stmtNextDeliveryTime = CharacterDatabase.GetPreparedStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME); + CharacterDatabasePreparedStatement* stmtNextDeliveryTime = CharacterDatabase.GetPreparedStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME); stmtNextDeliveryTime->setUInt32(0, GetGUID().GetCounter()); stmtNextDeliveryTime->setUInt64(1, cTime); PreparedQueryResult resultNextDeliveryTime = CharacterDatabase.Query(stmtNextDeliveryTime); @@ -3744,7 +3745,7 @@ void Player::UpdateNextMailTimeAndUnreads() } //Get unread mails count - PreparedStatement* stmtUnreadAmount = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH); + CharacterDatabasePreparedStatement* stmtUnreadAmount = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH); stmtUnreadAmount->setUInt32(0, GetGUID().GetCounter()); stmtUnreadAmount->setUInt64(1, cTime); PreparedQueryResult resultUnreadAmount = CharacterDatabase.Query(stmtUnreadAmount); @@ -4480,9 +4481,9 @@ void Player::_LoadSpellCooldowns(PreparedQueryResult result) } } -void Player::_SaveSpellCooldowns(SQLTransaction& trans, bool logout) +void Player::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -4868,14 +4869,14 @@ void Player::DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool up // Remove signs from petitions (also remove petitions if owner); RemovePetitionsAndSigns(playerGuid, 10); - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; switch (charDelete_method) { // Completely remove from the database case CHAR_DELETE_REMOVE: { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL); stmt->setUInt32(0, lowGuid); @@ -5192,7 +5193,7 @@ void Player::DeleteOldCharacters(uint32 keepDays) LOG_INFO("server.loading", "Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays); LOG_INFO("server.loading", " "); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_OLD_CHARS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_OLD_CHARS); stmt->setUInt32(0, uint32(time(nullptr) - time_t(keepDays * DAY))); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -5394,10 +5395,10 @@ void Player::KillPlayer() //UpdateObjectVisibility(); // pussywizard: not needed } -void Player::OfflineResurrect(ObjectGuid const guid, SQLTransaction& trans) +void Player::OfflineResurrect(ObjectGuid const guid, CharacterDatabaseTransaction trans) { Corpse::DeleteFromDB(guid, trans); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_RESURRECT)); stmt->setUInt64(1, guid.GetCounter()); CharacterDatabase.ExecuteOrAppend(trans, stmt); @@ -5489,7 +5490,7 @@ void Player::RemoveCorpse() GetCorpse()->RemoveFromWorld(); } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); Corpse::DeleteFromDB(GetGUID(), trans); CharacterDatabase.CommitTransaction(trans); } @@ -5501,10 +5502,10 @@ void Player::SpawnCorpseBones(bool triggerSave /*= true*/) if (triggerSave && !GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player { // prevent loading as ghost without corpse - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // pussywizard: update only ghost flag instead of whole character table entry! data integrity is crucial - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_REMOVE_GHOST); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_REMOVE_GHOST); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -7599,7 +7600,7 @@ void Player::SetArenaPoints(uint32 value) AddKnownCurrency(ITEM_ARENA_POINTS_ID); } -void Player::ModifyHonorPoints(int32 value, SQLTransaction* trans /*=nullptr*/) +void Player::ModifyHonorPoints(int32 value, CharacterDatabaseTransaction trans) { int32 newValue = int32(GetHonorPoints()) + value; if (newValue < 0) @@ -7608,14 +7609,14 @@ void Player::ModifyHonorPoints(int32 value, SQLTransaction* trans /*=nullptr*/) if (trans) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_HONOR_POINTS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_HONOR_POINTS); stmt->setUInt32(0, newValue); stmt->setUInt32(1, GetGUID().GetCounter()); - (*trans)->Append(stmt); + trans->Append(stmt); } } -void Player::ModifyArenaPoints(int32 value, SQLTransaction* trans /*=nullptr*/) +void Player::ModifyArenaPoints(int32 value, CharacterDatabaseTransaction trans) { int32 newValue = int32(GetArenaPoints()) + value; if (newValue < 0) @@ -7624,10 +7625,10 @@ void Player::ModifyArenaPoints(int32 value, SQLTransaction* trans /*=nullptr*/) if (trans) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_ARENA_POINTS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_ARENA_POINTS); stmt->setUInt32(0, newValue); stmt->setUInt32(1, GetGUID().GetCounter()); - (*trans)->Append(stmt); + trans->Append(stmt); } } @@ -7660,7 +7661,7 @@ uint32 Player::GetArenaTeamIdFromStorage(ObjectGuid::LowType guid, uint8 slot) uint32 Player::GetArenaTeamIdFromDB(ObjectGuid guid, uint8 type) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt8(1, type); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -7676,7 +7677,7 @@ uint32 Player::GetZoneIdFromDB(ObjectGuid guid) { ObjectGuid::LowType guidLow = guid.GetCounter(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ZONE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ZONE); stmt->setUInt32(0, guidLow); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -12781,7 +12782,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& dest, uint32 item, bool update for (++itr; itr != allowedLooters.end(); ++itr) ss << ' ' << (*itr).GetCounter(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE); stmt->setUInt32(0, pItem->GetGUID().GetCounter()); stmt->setString(1, ss.str()); CharacterDatabase.Execute(stmt); @@ -13253,7 +13254,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); stmt->setUInt32(0, pItem->GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); } @@ -16044,7 +16045,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, // Xinef: send items that couldn't be added properly by mail if (!problematicItems.empty()) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); MailSender sender(MAIL_CREATURE, 34337 /* The Postmaster */ ); MailDraft draft("Recovered Item", "We recovered a lost item in the twisting nether and noted that it was yours.$B$BPlease find said object enclosed."); // This is the text used in Cataclysm, it probably wasn't changed. @@ -16123,7 +16124,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, if (uint32 mail_template_id = quest->GetRewMailTemplateId()) { //- TODO: Poor design of mail system - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (quest->GetRewMailSenderEntry() != 0) MailDraft(mail_template_id).SendMailTo(trans, this, quest->GetRewMailSenderEntry(), MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs()); else @@ -17839,7 +17840,7 @@ void Player::_LoadEntryPointData(PreparedQueryResult result) bool Player::LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight, ObjectGuid::LowType guid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_POSITION); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_POSITION); stmt->setUInt32(0, guid); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -17865,7 +17866,7 @@ void Player::SetHomebind(WorldLocation const& loc, uint32 areaId) m_homebindAreaId = areaId; // update sql homebind - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND); stmt->setUInt16(0, m_homebindMapId); stmt->setUInt16(1, m_homebindAreaId); stmt->setFloat (2, m_homebindX); @@ -17897,7 +17898,7 @@ bool Player::isBeingLoaded() const return GetSession()->PlayerLoading(); } -bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) +bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder const& holder) { //// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 //QueryResult* result = CharacterDatabase.PQuery("SELECT guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, " @@ -17909,7 +17910,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) //"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, " // 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 //"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels FROM characters WHERE guid = '%u'", guid); - PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM); + PreparedQueryResult result = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM); if (!result) { @@ -17929,7 +17930,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) return false; } - if (holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANNED)) + if (holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANNED)) { LOG_ERROR("entities.player", "Player (%s) is banned, can't load.", playerGuid.ToString().c_str()); return false; @@ -17945,7 +17946,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS || (AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()) && sObjectMgr->IsReservedName(m_name))) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_RENAME)); stmt->setUInt32(1, guid); CharacterDatabase.Execute(stmt); @@ -17979,7 +17980,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria) - m_achievementMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS)); + m_achievementMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS)); uint32 money = fields[8].GetUInt32(); if (money > MAX_MONEY_AMOUNT) @@ -18028,7 +18029,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) sInstanceSaveMgr->PlayerCreateBoundInstancesMaps(playerGuid); // load home bind and check in same time class/race pair, it used later for restore broken positions - if (!_LoadHomeBind(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND))) + if (!_LoadHomeBind(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND))) return false; InitPrimaryProfessions(); // to max set before any spell loaded @@ -18065,8 +18066,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[49].GetUInt16()); SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[50].GetUInt16()); - _LoadInstanceTimeRestrictions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES)); - _LoadEntryPointData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ENTRY_POINT)); + _LoadInstanceTimeRestrictions(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES)); + _LoadEntryPointData(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ENTRY_POINT)); GetSession()->SetPlayer(this); MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); @@ -18381,7 +18382,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) } // load skills after InitStatsForLevel because it triggering aura apply also - _LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS)); + _LoadSkills(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS)); UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load // apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods() @@ -18395,12 +18396,12 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) LearnDefaultSkills(); LearnCustomSpells(); - _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); - _LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); + _LoadSpells(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); + _LoadTalents(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS)); - _LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS)); + _LoadGlyphs(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS)); _LoadGlyphAuras(); - _LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), time_diff); + _LoadAuras(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), time_diff); // add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura) if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) { @@ -18412,32 +18413,32 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP); // after spell load, learn rewarded spell if need also - _LoadQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS)); - _LoadQuestStatusRewarded(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW)); - _LoadDailyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS)); - _LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS)); - _LoadSeasonalQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS)); - _LoadMonthlyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS)); - _LoadRandomBGStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG)); + _LoadQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS)); + _LoadQuestStatusRewarded(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW)); + _LoadDailyQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS)); + _LoadWeeklyQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS)); + _LoadSeasonalQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS)); + _LoadMonthlyQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS)); + _LoadRandomBGStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG)); // after spell and quest load InitTalentForLevel(); // must be before inventory (some items required reputation check) - m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION)); + m_reputationMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION)); // xinef: load mails before inventory, so problematic items can be added to already loaded mails // unread mails and next delivery time, actual mails not loaded - _LoadMailInit(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE)); + _LoadMailInit(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE)); - _LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff); + _LoadInventory(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff); // update items with duration and realtime UpdateItemDuration(time_diff, true); - _LoadActions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACTIONS)); + _LoadActions(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACTIONS)); - m_social = sSocialMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST), GetGUID()); + m_social = sSocialMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST), GetGUID()); // check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES // note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded @@ -18450,7 +18451,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) // has to be called after last Relocate() in Player::LoadFromDB SetFallInformation(time(nullptr), GetPositionZ()); - _LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS)); + _LoadSpellCooldowns(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS)); // Spell code allow apply any auras to dead character in load time in aura/spell/item loading // Do now before stats re-calculation cleanup for ghost state unexpected auras @@ -18543,13 +18544,13 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder) if (m_grantableLevels > 0) SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01); - _LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES)); + _LoadDeclinedNames(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES)); //m_achievementMgr->CheckAllAchievementCriteria(); // pussywizard: ZOMG! disabled this bullshit - _LoadEquipmentSets(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS)); + _LoadEquipmentSets(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS)); - _LoadBrewOfTheMonth(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH)); + _LoadBrewOfTheMonth(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH)); // Players are immune to taunt ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); @@ -18810,7 +18811,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) std::map bagMap; // fast guid lookup for bags std::map invalidBagMap; // fast guid lookup for bags std::list problematicItems; - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // Prevent items from being added to the queue while loading m_itemUpdateQueueBlocked = true; @@ -18927,7 +18928,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) _ApplyAllItemMods(); } -Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields) +Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields) { Item* item = nullptr; ObjectGuid::LowType itemGuid = fields[13].GetUInt32(); @@ -18938,7 +18939,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F item = NewItemOrBag(proto); if (item->LoadFromDB(itemGuid, GetGUID(), fields, itemEntry)) { - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; // Do not allow to have item limited to another map/zone in alive state if (IsAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zoneId)) @@ -19058,7 +19059,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F void Player::_LoadMailedItems(Mail* mail) { // data needs to be at first place for Item::LoadFromDB - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS); stmt->setUInt32(0, mail->messageID); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) @@ -19101,7 +19102,7 @@ void Player::_LoadMailedItems(Mail* mail) item->FSetState(ITEM_REMOVED); - SQLTransaction temp = SQLTransaction(nullptr); + CharacterDatabaseTransaction temp = CharacterDatabaseTransaction(nullptr); item->SaveToDB(temp); // it also deletes item object ! continue; } @@ -19141,7 +19142,7 @@ void Player::_LoadMail() if (m_mailsUpdated) { //Save changed data to the sql before refreshing so we always get up to date data - SQLTransaction saveTransaction = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction saveTransaction = CharacterDatabase.BeginTransaction(); _SaveMail(saveTransaction); CharacterDatabase.CommitTransaction(saveTransaction); } @@ -19165,7 +19166,7 @@ void Player::_LoadMail() //Now load the new ones m_mailCache.clear(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt64(1, time(nullptr)); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -19991,7 +19992,7 @@ bool Player::_LoadHomeBind(PreparedQueryResult result) ok = true; else { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND); stmt->setUInt32(0, GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); } @@ -20005,7 +20006,7 @@ bool Player::_LoadHomeBind(PreparedQueryResult result) m_homebindY = info->positionY; m_homebindZ = info->positionZ; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt16(1, m_homebindMapId); stmt->setUInt16(2, m_homebindAreaId); @@ -20025,6 +20026,15 @@ bool Player::_LoadHomeBind(PreparedQueryResult result) /*********************************************************/ void Player::SaveToDB(bool create, bool logout) +{ + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + SaveToDB(trans, create, logout); + + CharacterDatabase.CommitTransaction(trans); +} + +void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create, bool logout) { // delay auto save at any saves (manual, in code, or autosave) m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE); @@ -20049,8 +20059,6 @@ void Player::SaveToDB(bool create, bool logout) if (!create) sScriptMgr->OnPlayerSave(this); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - _SaveCharacter(create, trans); if (m_mailsUpdated) //save mails only when needed @@ -20081,8 +20089,6 @@ void Player::SaveToDB(bool create, bool logout) 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, logout); @@ -20099,23 +20105,23 @@ void Player::SaveToDB(bool create, bool logout) } // fast save function for item/money cheating preventing - save only inventory and money state -void Player::SaveInventoryAndGoldToDB(SQLTransaction& trans) +void Player::SaveInventoryAndGoldToDB(CharacterDatabaseTransaction trans) { _SaveInventory(trans); SaveGoldToDB(trans); } -void Player::SaveGoldToDB(SQLTransaction& trans) +void Player::SaveGoldToDB(CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_MONEY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_MONEY); stmt->setUInt32(0, GetMoney()); stmt->setUInt32(1, GetGUID().GetCounter()); trans->Append(stmt); } -void Player::_SaveActions(SQLTransaction& trans) +void Player::_SaveActions(CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; for (ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end();) { @@ -20161,9 +20167,9 @@ void Player::_SaveActions(SQLTransaction& trans) } } -void Player::_SaveAuras(SQLTransaction& trans, bool logout) +void Player::_SaveAuras(CharacterDatabaseTransaction trans, bool logout) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -20219,9 +20225,9 @@ void Player::_SaveAuras(SQLTransaction& trans, bool logout) } } -void Player::_SaveInventory(SQLTransaction& trans) +void Player::_SaveInventory(CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; // force items in buyback slots to new state // and remove those that aren't already for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i) @@ -20357,14 +20363,14 @@ void Player::_SaveInventory(SQLTransaction& trans) m_itemUpdateQueue.clear(); } -void Player::_SaveMail(SQLTransaction& trans) +void Player::_SaveMail(CharacterDatabaseTransaction trans) { if (!GetMailCacheSize() || !m_mailsUpdated) { return; } - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end(); ++itr) { @@ -20434,7 +20440,7 @@ void Player::_SaveMail(SQLTransaction& trans) m_mailsUpdated = false; } -void Player::_SaveQuestStatus(SQLTransaction& trans) +void Player::_SaveQuestStatus(CharacterDatabaseTransaction trans) { bool isTransaction = static_cast(trans); if (!isTransaction) @@ -20442,7 +20448,7 @@ void Player::_SaveQuestStatus(SQLTransaction& trans) QuestStatusSaveMap::iterator saveItr; QuestStatusMap::iterator statusItr; - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; bool keepAbandoned = !(sWorld->GetCleaningFlags() & CharacterDatabaseCleaner::CLEANING_FLAG_QUESTSTATUS); @@ -20501,7 +20507,7 @@ void Player::_SaveQuestStatus(SQLTransaction& trans) CharacterDatabase.CommitTransaction(trans); } -void Player::_SaveDailyQuestStatus(SQLTransaction& trans) +void Player::_SaveDailyQuestStatus(CharacterDatabaseTransaction trans) { if (!m_DailyQuestChanged) return; @@ -20511,7 +20517,7 @@ void Player::_SaveDailyQuestStatus(SQLTransaction& trans) // save last daily quest time for all quests: we need only mostly reset time for reset check anyway // we don't need transactions here. - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) @@ -20539,13 +20545,13 @@ void Player::_SaveDailyQuestStatus(SQLTransaction& trans) } } -void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans) +void Player::_SaveWeeklyQuestStatus(CharacterDatabaseTransaction trans) { if (!m_WeeklyQuestChanged || m_weeklyquests.empty()) return; // we don't need transactions here. - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -20562,13 +20568,13 @@ void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans) m_WeeklyQuestChanged = false; } -void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans) +void Player::_SaveSeasonalQuestStatus(CharacterDatabaseTransaction trans) { if (!m_SeasonalQuestChanged || m_seasonalquests.empty()) return; // we don't need transactions here. - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -20590,13 +20596,13 @@ void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans) m_SeasonalQuestChanged = false; } -void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans) +void Player::_SaveMonthlyQuestStatus(CharacterDatabaseTransaction trans) { if (!m_MonthlyQuestChanged || m_monthlyquests.empty()) return; // we don't need transactions here. - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -20612,9 +20618,9 @@ void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans) m_MonthlyQuestChanged = false; } -void Player::_SaveSkills(SQLTransaction& trans) +void Player::_SaveSkills(CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; // we don't need transactions here. for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end();) { @@ -20668,9 +20674,9 @@ void Player::_SaveSkills(SQLTransaction& trans) } } -void Player::_SaveSpells(SQLTransaction& trans) +void Player::_SaveSpells(CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end();) { @@ -20715,13 +20721,13 @@ void Player::_SaveSpells(SQLTransaction& trans) // save player stats -- only for external usage // real stats will be recalculated on player login -void Player::_SaveStats(SQLTransaction& trans) +void Player::_SaveStats(CharacterDatabaseTransaction trans) { // check if stat saving is enabled and if char level is high enough if (!sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE)) return; - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_STATS); stmt->setUInt32(0, GetGUID().GetCounter()); @@ -20826,7 +20832,7 @@ void Player::SendAttackSwingNotInRange() void Player::SavePositionInDB(uint32 mapid, float x, float y, float z, float o, uint32 zone, ObjectGuid guid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION); stmt->setFloat(0, x); stmt->setFloat(1, y); @@ -20839,6 +20845,21 @@ void Player::SavePositionInDB(uint32 mapid, float x, float y, float z, float o, CharacterDatabase.Execute(stmt); } +void Player::SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, CharacterDatabaseTransaction trans) +{ + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION); + + stmt->setFloat(0, loc.GetPositionX()); + stmt->setFloat(1, loc.GetPositionY()); + stmt->setFloat(2, loc.GetPositionZ()); + stmt->setFloat(3, loc.GetOrientation()); + stmt->setUInt16(4, uint16(loc.GetMapId())); + stmt->setUInt16(5, zoneId); + stmt->setUInt32(6, guid.GetCounter()); + + CharacterDatabase.ExecuteOrAppend(trans, stmt); +} + void Player::SetUInt32ValueInArray(Tokenizer& tokens, uint16 index, uint32 value) { char buf[11]; @@ -20850,20 +20871,18 @@ void Player::SetUInt32ValueInArray(Tokenizer& tokens, uint16 index, uint32 value tokens[index] = buf; } -void Player::Customize(ObjectGuid guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair) +void Player::Customize(CharacterCustomizeInfo const* customizeInfo, CharacterDatabaseTransaction trans) { - // xinef: zomg! sync query - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GENDER_AND_APPEARANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GENDER_AND_APPEARANCE); + stmt->setUInt8(0, customizeInfo->Gender); + stmt->setUInt8(1, customizeInfo->Skin); + stmt->setUInt8(2, customizeInfo->Face); + stmt->setUInt8(3, customizeInfo->HairStyle); + stmt->setUInt8(4, customizeInfo->HairColor); + stmt->setUInt8(5, customizeInfo->FacialHair); + stmt->setUInt32(6, customizeInfo->Guid.GetCounter()); - stmt->setUInt8(0, gender); - stmt->setUInt8(1, skin); - stmt->setUInt8(2, face); - stmt->setUInt8(3, hairStyle); - stmt->setUInt8(4, hairColor); - stmt->setUInt8(5, facialHair); - stmt->setUInt32(6, guid.GetCounter()); - - CharacterDatabase.Execute(stmt); + CharacterDatabase.ExecuteOrAppend(trans, stmt); } void Player::SendAttackSwingDeadTarget() @@ -21895,22 +21914,22 @@ void Player::RemovePetitionsAndSigns(ObjectGuid guid, uint32 type) if (type == 10) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PETITION_SIGNATURES); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PETITION_SIGNATURES); stmt->setUInt32(0, guid.GetCounter()); CharacterDatabase.Execute(stmt); } else { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt8(1, uint8(type)); CharacterDatabase.Execute(stmt); } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (type == 10) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER); stmt->setUInt32(0, guid.GetCounter()); trans->Append(stmt); @@ -21923,7 +21942,7 @@ void Player::RemovePetitionsAndSigns(ObjectGuid guid, uint32 type) } else { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt8(1, uint8(type)); trans->Append(stmt); @@ -21942,7 +21961,7 @@ void Player::RemovePetitionsAndSigns(ObjectGuid guid, uint32 type) void Player::LeaveAllArenaTeams(ObjectGuid guid) { // xinef: zomg! sync query - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ARENA_TEAMS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ARENA_TEAMS); stmt->setUInt32(0, guid.GetCounter()); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -23169,7 +23188,7 @@ void Player::LeaveBattleground(Battleground* bg) { if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt8(1, BG_DESERTION_TYPE_LEAVE_BG); CharacterDatabase.Execute(stmt); @@ -24468,7 +24487,7 @@ void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/) else { MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); offItem->DeleteFromInventoryDB(trans); // deletes item from character's inventory offItem->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone @@ -25955,7 +25974,7 @@ void Player::_LoadSkills(PreparedQueryResult result) { LOG_ERROR("entities.player", "Character %s has skill %u with value 0. Will be deleted.", GetGUID().ToString().c_str(), skill); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt16(1, skill); @@ -26753,13 +26772,13 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset) eqslot.state = old_state == EQUIPMENT_SET_NEW ? EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED; } -void Player::_SaveEquipmentSets(SQLTransaction& trans) +void Player::_SaveEquipmentSets(CharacterDatabaseTransaction trans) { for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();) { uint32 index = itr->first; EquipmentSet& eqset = itr->second; - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; uint8 j = 0; switch (eqset.state) { @@ -26804,14 +26823,14 @@ void Player::_SaveEquipmentSets(SQLTransaction& trans) } } -void Player::_SaveEntryPoint(SQLTransaction& trans) +void Player::_SaveEntryPoint(CharacterDatabaseTransaction trans) { // xinef: dont save joinpos with invalid mapid MapEntry const* mEntry = sMapStore.LookupEntry(m_entryPointData.joinPos.GetMapId()); if (!mEntry) return; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_ENTRY_POINT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_ENTRY_POINT); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -26856,7 +26875,7 @@ void Player::RemoveAtLoginFlag(AtLoginFlags flags, bool persist /*= false*/) if (persist) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(flags)); stmt->setUInt32(1, GetGUID().GetCounter()); @@ -26894,9 +26913,9 @@ void Player::SetMap(Map* map) m_mapRef.link(map, this); } -void Player::_SaveCharacter(bool create, SQLTransaction& trans) +void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; uint8 index = 0; auto finiteAlways = [](float f) { return std::isfinite(f) ? f : 0.0f; }; @@ -27189,12 +27208,12 @@ void Player::_LoadGlyphs(PreparedQueryResult result) } while (result->NextRow()); } -void Player::_SaveGlyphs(SQLTransaction& trans) +void Player::_SaveGlyphs(CharacterDatabaseTransaction trans) { if (!NeedToSaveGlyphs()) return; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -27236,9 +27255,9 @@ void Player::_LoadTalents(PreparedQueryResult result) } } -void Player::_SaveTalents(SQLTransaction& trans) +void Player::_SaveTalents(CharacterDatabaseTransaction trans) { - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; for (PlayerTalentMap::iterator itr = m_talents.begin(); itr != m_talents.end();) { @@ -27290,8 +27309,8 @@ void Player::UpdateSpecCount(uint8 count) if (m_activeSpec >= count) ActivateSpec(0); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = nullptr; + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = nullptr; // Copy spec data if (count > curCount) @@ -27342,7 +27361,7 @@ void Player::ActivateSpec(uint8 spec) InterruptNonMeleeSpells(false); // xinef: save current actions order - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); _SaveActions(trans); CharacterDatabase.CommitTransaction(trans); @@ -27429,11 +27448,22 @@ void Player::ActivateSpec(uint8 spec) m_usedTalentCount = spentTalents; InitTalentForLevel(); - // xinef: optimization, use callback to read the data - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC); - stmt->setUInt32(0, GetGUID().GetCounter()); - stmt->setUInt8(1, m_activeSpec); - GetSession()->_loadActionsSwitchSpecCallback = CharacterDatabase.AsyncQuery(stmt); // FutureResult + // load them asynchronously + { + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC); + stmt->setUInt32(0, GetGUID().GetCounter()); + stmt->setUInt8(1, m_activeSpec); + + WorldSession* mySess = GetSession(); + mySess->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback([mySess](PreparedQueryResult result) + { + // safe callback, we can't pass this pointer directly + // in case player logs out before db response (player would be deleted in that case) + if (Player* thisPlayer = mySess->GetPlayer()) + thisPlayer->LoadActions(result); + })); + } // xinef: reset power Powers pw = getPowerType(); @@ -27469,6 +27499,14 @@ void Player::ActivateSpec(uint8 spec) } } +void Player::LoadActions(PreparedQueryResult result) +{ + if (result) + _LoadActions(result); + + SendActionButtons(1); +} + void Player::GetTalentTreePoints(uint8 (&specPoints)[3]) const { const PlayerTalentMap& talentMap = GetTalentMap(); @@ -27833,7 +27871,7 @@ void Player::RefundItem(Item* item) uint32 moneyRefund = item->GetPaidMoney(); // item-> will be invalidated in DestroyItem // Save all relevant data to DB to prevent desynchronisation exploits - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // Delete any references to the refund data item->SetNotRefundable(this, true, &trans); @@ -27862,11 +27900,11 @@ void Player::RefundItem(Item* item) // Grant back Honor points if (uint32 honorRefund = iece->reqhonorpoints) - ModifyHonorPoints(honorRefund, &trans); + ModifyHonorPoints(honorRefund, trans); // Grant back Arena points if (uint32 arenaRefund = iece->reqarenapoints) - ModifyArenaPoints(arenaRefund, &trans); + ModifyArenaPoints(arenaRefund, trans); SaveInventoryAndGoldToDB(trans); @@ -27878,7 +27916,7 @@ void Player::SetRandomWinner(bool isWinner) m_IsBGRandomWinner = isWinner; if (m_IsBGRandomWinner) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BATTLEGROUND_RANDOM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BATTLEGROUND_RANDOM); stmt->setUInt32(0, GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); } @@ -27975,13 +28013,13 @@ void Player::_LoadBrewOfTheMonth(PreparedQueryResult result) if (lastEventId != eventId && IsEventActive(eventId) && HasAchieved(2796 /* Brew of the Month*/)) { // Send Mail - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); MailSender sender(MAIL_CREATURE, 27487 /*NPC_BREW_OF_THE_MONTH_CLUB*/); MailDraft draft(uint16(212 + month)); // 212 is starting template id draft.SendMailTo(trans, MailReceiver(this, GetGUID().GetCounter()), sender); // Update Event Id - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_BREW_OF_THE_MONTH); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_BREW_OF_THE_MONTH); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt32(1, uint32(eventId)); trans->Append(stmt); @@ -27990,12 +28028,12 @@ void Player::_LoadBrewOfTheMonth(PreparedQueryResult result) } } -void Player::_SaveInstanceTimeRestrictions(SQLTransaction& trans) +void Player::_SaveInstanceTimeRestrictions(CharacterDatabaseTransaction trans) { if (_instanceResetTimes.empty()) return; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES); stmt->setUInt32(0, GetSession()->GetAccountId()); trans->Append(stmt); @@ -28267,14 +28305,14 @@ void Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy bool Player::IsPetDismissed() { /* - * Check PET_SAVE_NOT_IN_SLOT means the pet is dismissed. If someone ever - * Changes the slot flag, they will break this validation. - */ - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT); + * Check PET_SAVE_NOT_IN_SLOT means the pet is dismissed. If someone ever + * Changes the slot flag, they will break this validation. + */ + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt8(1, uint8(PET_SAVE_NOT_IN_SLOT)); - if (PreparedQueryResult result = CharacterDatabase.AsyncQuery(stmt)) + if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) return true; return false; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index ca777c749..f20721726 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -8,6 +8,7 @@ #define _PLAYER_H #include "Battleground.h" +#include "DatabaseEnvFwd.h" #include "DBCStores.h" #include "GroupReference.h" #include "InstanceSaveMgr.h" @@ -1557,7 +1558,7 @@ public: /*** LOAD SYSTEM ***/ /*********************************************************/ - bool LoadFromDB(ObjectGuid guid, SQLQueryHolder* holder); + bool LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& holder); [[nodiscard]] bool isBeingLoaded() const override; void Initialize(ObjectGuid::LowType guid); @@ -1574,13 +1575,15 @@ public: /*********************************************************/ void SaveToDB(bool create, bool logout); - void SaveInventoryAndGoldToDB(SQLTransaction& trans); // fast save function for item/money cheating preventing - void SaveGoldToDB(SQLTransaction& trans); + void SaveToDB(CharacterDatabaseTransaction trans, bool create, bool logout); + void SaveInventoryAndGoldToDB(CharacterDatabaseTransaction trans); // fast save function for item/money cheating preventing + void SaveGoldToDB(CharacterDatabaseTransaction trans); static void SetUInt32ValueInArray(Tokenizer& data, uint16 index, uint32 value); static void SetFloatValueInArray(Tokenizer& data, uint16 index, float value); - static void Customize(ObjectGuid guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair); + static void Customize(CharacterCustomizeInfo const* customizeInfo, CharacterDatabaseTransaction trans); static void SavePositionInDB(uint32 mapid, float x, float y, float z, float o, uint32 zone, ObjectGuid guid); + static void SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, CharacterDatabaseTransaction trans); static void DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool updateRealmChars, bool deleteFinally); static void DeleteOldCharacters(); @@ -1739,6 +1742,7 @@ public: [[nodiscard]] uint8 GetSpecsCount() const { return m_specsCount; } void SetSpecsCount(uint8 count) { m_specsCount = count; } void ActivateSpec(uint8 spec); + void LoadActions(PreparedQueryResult result); void GetTalentTreePoints(uint8 (&specPoints)[3]) const; [[nodiscard]] uint8 GetMostPointsTalentTree() const; bool HasTankSpec(); @@ -1811,7 +1815,7 @@ public: void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false); void RemoveAllSpellCooldown(); void _LoadSpellCooldowns(PreparedQueryResult result); - void _SaveSpellCooldowns(SQLTransaction& trans, bool logout); + void _SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout); uint32 GetLastPotionId() { return m_lastPotionId; } void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; } void UpdatePotionCooldown(Spell* spell = nullptr); @@ -2043,7 +2047,7 @@ public: Corpse* CreateCorpse(); void RemoveCorpse(); void KillPlayer(); - static void OfflineResurrect(ObjectGuid const guid, SQLTransaction& trans); + static void OfflineResurrect(ObjectGuid const guid, CharacterDatabaseTransaction trans); bool HasCorpse() const { return _corpseLocation.GetMapId() != MAPID_INVALID; } WorldLocation GetCorpseLocation() const { return _corpseLocation; } uint32 GetResurrectionSpellId(); @@ -2158,8 +2162,8 @@ public: bool RewardHonor(Unit* victim, uint32 groupsize, int32 honor = -1, bool awardXP = true); [[nodiscard]] uint32 GetHonorPoints() const { return GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY); } [[nodiscard]] uint32 GetArenaPoints() const { return GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY); } - void ModifyHonorPoints(int32 value, SQLTransaction* trans = nullptr); //! If trans is specified, honor save query will be added to trans - void ModifyArenaPoints(int32 value, SQLTransaction* trans = nullptr); //! If trans is specified, arena point save query will be added to trans + void ModifyHonorPoints(int32 value, CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr)); //! If trans is specified, honor save query will be added to trans + void ModifyArenaPoints(int32 value, CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr)); //! If trans is specified, arena point save query will be added to trans [[nodiscard]] uint32 GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) const; void SetHonorPoints(uint32 value); void SetArenaPoints(uint32 value); @@ -2734,24 +2738,24 @@ protected: /*** SAVE SYSTEM ***/ /*********************************************************/ - void _SaveActions(SQLTransaction& trans); - void _SaveAuras(SQLTransaction& trans, bool logout); - void _SaveInventory(SQLTransaction& trans); - void _SaveMail(SQLTransaction& trans); - void _SaveQuestStatus(SQLTransaction& trans); - void _SaveDailyQuestStatus(SQLTransaction& trans); - void _SaveWeeklyQuestStatus(SQLTransaction& trans); - void _SaveMonthlyQuestStatus(SQLTransaction& trans); - void _SaveSeasonalQuestStatus(SQLTransaction& trans); - void _SaveSkills(SQLTransaction& trans); - void _SaveSpells(SQLTransaction& trans); - void _SaveEquipmentSets(SQLTransaction& trans); - void _SaveEntryPoint(SQLTransaction& trans); - void _SaveGlyphs(SQLTransaction& trans); - void _SaveTalents(SQLTransaction& trans); - void _SaveStats(SQLTransaction& trans); - void _SaveCharacter(bool create, SQLTransaction& trans); - void _SaveInstanceTimeRestrictions(SQLTransaction& trans); + void _SaveActions(CharacterDatabaseTransaction trans); + void _SaveAuras(CharacterDatabaseTransaction trans, bool logout); + void _SaveInventory(CharacterDatabaseTransaction trans); + void _SaveMail(CharacterDatabaseTransaction trans); + void _SaveQuestStatus(CharacterDatabaseTransaction trans); + void _SaveDailyQuestStatus(CharacterDatabaseTransaction trans); + void _SaveWeeklyQuestStatus(CharacterDatabaseTransaction trans); + void _SaveMonthlyQuestStatus(CharacterDatabaseTransaction trans); + void _SaveSeasonalQuestStatus(CharacterDatabaseTransaction trans); + void _SaveSkills(CharacterDatabaseTransaction trans); + void _SaveSpells(CharacterDatabaseTransaction trans); + void _SaveEquipmentSets(CharacterDatabaseTransaction trans); + void _SaveEntryPoint(CharacterDatabaseTransaction trans); + void _SaveGlyphs(CharacterDatabaseTransaction trans); + void _SaveTalents(CharacterDatabaseTransaction trans); + void _SaveStats(CharacterDatabaseTransaction trans); + void _SaveCharacter(bool create, CharacterDatabaseTransaction trans); + void _SaveInstanceTimeRestrictions(CharacterDatabaseTransaction trans); /*********************************************************/ /*** ENVIRONMENTAL SYSTEM ***/ @@ -2926,7 +2930,7 @@ private: InventoryResult CanStoreItem_InBag(uint8 bag, ItemPosCountVec& dest, ItemTemplate const* pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const; InventoryResult CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemTemplate const* pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const; Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update); - Item* _LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields); + Item* _LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields); typedef GuidSet RefundableItemsSet; RefundableItemsSet m_refundableItems; diff --git a/src/server/game/Entities/Player/SocialMgr.cpp b/src/server/game/Entities/Player/SocialMgr.cpp index 7db1ef182..ee2a5ae93 100644 --- a/src/server/game/Entities/Player/SocialMgr.cpp +++ b/src/server/game/Entities/Player/SocialMgr.cpp @@ -37,7 +37,7 @@ bool PlayerSocial::AddToSocialList(ObjectGuid friendGuid, SocialFlag flag) { itr->second.Flags |= flag; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS); stmt->setUInt8(0, itr->second.Flags); stmt->setUInt32(1, GetPlayerGUID().GetCounter()); @@ -49,7 +49,7 @@ bool PlayerSocial::AddToSocialList(ObjectGuid friendGuid, SocialFlag flag) { m_playerSocialMap[friendGuid].Flags |= flag; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_SOCIAL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_SOCIAL); stmt->setUInt32(0, GetPlayerGUID().GetCounter()); stmt->setUInt32(1, friendGuid.GetCounter()); @@ -70,7 +70,7 @@ void PlayerSocial::RemoveFromSocialList(ObjectGuid friendGuid, SocialFlag flag) if (itr->second.Flags == 0) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SOCIAL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SOCIAL); stmt->setUInt32(0, GetPlayerGUID().GetCounter()); stmt->setUInt32(1, friendGuid.GetCounter()); @@ -81,7 +81,7 @@ void PlayerSocial::RemoveFromSocialList(ObjectGuid friendGuid, SocialFlag flag) } else { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS); stmt->setUInt8(0, flag); stmt->setUInt32(1, GetPlayerGUID().GetCounter()); @@ -99,7 +99,7 @@ void PlayerSocial::SetFriendNote(ObjectGuid friendGuid, std::string note) utf8truncate(note, 48); // DB and client size limitation - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_SOCIAL_NOTE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_SOCIAL_NOTE); stmt->setString(0, note); stmt->setUInt32(1, GetPlayerGUID().GetCounter()); diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index b090218aa..7e8665114 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -196,8 +196,8 @@ void GameEventMgr::StopEvent(uint16 event_id, bool overwrite) for (itr = data.conditions.begin(); itr != data.conditions.end(); ++itr) itr->second.done = 0; - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GAME_EVENT_CONDITION_SAVE); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GAME_EVENT_CONDITION_SAVE); stmt->setUInt8(0, uint8(event_id)); trans->Append(stmt); @@ -1635,9 +1635,9 @@ void GameEventMgr::HandleQuestComplete(uint32 quest_id) if (citr->second.done > citr->second.reqNum) citr->second.done = citr->second.reqNum; // save the change to db - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_CONDITION_SAVE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_CONDITION_SAVE); stmt->setUInt8(0, uint8(event_id)); stmt->setUInt32(1, condition); trans->Append(stmt); @@ -1680,9 +1680,9 @@ bool GameEventMgr::CheckOneGameEventConditions(uint16 event_id) void GameEventMgr::SaveWorldEventStateToDB(uint16 event_id) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_SAVE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_SAVE); stmt->setUInt8(0, uint8(event_id)); trans->Append(stmt); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index c297d2a8f..8458bf02c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1687,7 +1687,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid if (!linkedGuidLow) // we're removing the linking { _linkedRespawnStore.erase(guid); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CRELINKED_RESPAWN); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CRELINKED_RESPAWN); stmt->setUInt32(0, guidLow); WorldDatabase.Execute(stmt); return true; @@ -1716,7 +1716,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid ObjectGuid linkedGuid = ObjectGuid::Create(slave->id, linkedGuidLow); _linkedRespawnStore[guid] = linkedGuid; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN); stmt->setUInt32(0, guidLow); stmt->setUInt32(1, linkedGuidLow); WorldDatabase.Execute(stmt); @@ -1956,7 +1956,7 @@ void ObjectMgr::LoadCreatures() uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ); uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA); stmt->setUInt32(0, zoneId); stmt->setUInt32(1, areaId); @@ -2257,7 +2257,7 @@ void ObjectMgr::LoadGameobjects() uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ); uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA); stmt->setUInt32(0, zoneId); stmt->setUInt32(1, areaId); @@ -5192,7 +5192,7 @@ void ObjectMgr::LoadWaypointScripts() for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr) actionSet.insert(itr->first); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION); PreparedQueryResult result = WorldDatabase.Query(stmt); if (result) @@ -5680,7 +5680,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp) time_t curTime = time(nullptr); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL); stmt->setUInt32(0, curTime); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) @@ -7628,7 +7628,7 @@ void ObjectMgr::AddReservedPlayerName(std::string const& name) _reservedNamesStore.insert(wstr); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RESERVED_PLAYER_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RESERVED_PLAYER_NAME); stmt->setString(0, name); CharacterDatabase.Execute(stmt); } @@ -8142,7 +8142,7 @@ bool ObjectMgr::AddGameTele(GameTele& tele) _gameTeleStore[new_id] = tele; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAME_TELE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAME_TELE); stmt->setUInt32(0, new_id); stmt->setFloat(1, tele.position_x); @@ -8171,7 +8171,7 @@ bool ObjectMgr::DeleteGameTele(const std::string& name) { if (itr->second.wnameLow == wname) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_TELE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_TELE); stmt->setString(0, itr->second.name); @@ -8366,7 +8366,7 @@ void ObjectMgr::LoadTrainerSpell() int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set* skip_vendors) { // find all items from the reference vendor - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF); stmt->setUInt32(0, uint32(item)); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -8569,7 +8569,7 @@ void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32 if (persist) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR); stmt->setUInt32(0, entry); stmt->setUInt32(1, item); @@ -8592,7 +8592,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru if (persist) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_NPC_VENDOR); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_NPC_VENDOR); stmt->setUInt32(0, entry); stmt->setUInt32(1, item); diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 143d3194a..79ae24910 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -124,7 +124,7 @@ bool Group::Create(Player* leader) m_raidDifficulty = leader->GetRaidDifficulty(); // Store group in database - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GROUP); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GROUP); uint8 index = 0; @@ -168,8 +168,8 @@ bool Group::LoadGroupFromDB(Field* fields) // group leader not exist if (!sObjectMgr->GetPlayerNameByGUID(fields[0].GetUInt32(), m_leaderName)) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP); stmt->setUInt32(0, groupLowGuid); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER_ALL); @@ -221,7 +221,7 @@ void Group::LoadMemberFromDB(ObjectGuid::LowType guidLow, uint8 memberFlags, uin // skip non-existed member if (!sObjectMgr->GetPlayerNameByGUID(member.guid.GetCounter(), member.name)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER); stmt->setUInt32(0, guidLow); stmt->setUInt32(1, GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); @@ -247,7 +247,7 @@ void Group::ConvertToLFG() m_lootMethod = NEED_BEFORE_GREED; if (!isBGGroup() && !isBFGroup()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE); stmt->setUInt8(0, uint8(m_groupType)); stmt->setUInt32(1, GetGUID().GetCounter()); @@ -266,7 +266,7 @@ void Group::ConvertToRaid() if (!isBGGroup() && !isBFGroup()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE); stmt->setUInt8(0, uint8(m_groupType)); stmt->setUInt32(1, GetGUID().GetCounter()); @@ -413,7 +413,7 @@ bool Group::AddMember(Player* player) if (!isBGGroup() && !isBFGroup()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GROUP_MEMBER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GROUP_MEMBER); stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt32(1, member.guid.GetCounter()); stmt->setUInt8(2, member.flags); @@ -559,7 +559,7 @@ bool Group::RemoveMember(ObjectGuid guid, const RemoveMethod& method /*= GROUP_R // Remove player from group in DB if (!isBGGroup() && !isBFGroup()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt32(1, GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); @@ -687,9 +687,9 @@ void Group::ChangeLeader(ObjectGuid newLeaderGuid) if (!isBGGroup() && !isBFGroup()) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // Update the group leader - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_LEADER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_LEADER); stmt->setUInt32(0, newLeader->GetGUID().GetCounter()); stmt->setUInt32(1, GetGUID().GetCounter()); trans->Append(stmt); @@ -776,9 +776,9 @@ void Group::Disband(bool hideDestroy /* = false */) if (!isBGGroup() && !isBFGroup()) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP); stmt->setUInt32(0, GetGUID().GetCounter()); trans->Append(stmt); @@ -1728,7 +1728,7 @@ void Group::ChangeMembersGroup(ObjectGuid guid, uint8 group) // Preserve new sub group in database for non-raid groups if (!isBGGroup() && !isBFGroup()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_SUBGROUP); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_SUBGROUP); stmt->setUInt8(0, group); stmt->setUInt32(1, guid.GetCounter()); @@ -1927,7 +1927,7 @@ void Group::SetDungeonDifficulty(Difficulty difficulty) m_dungeonDifficulty = difficulty; if (!isBGGroup() && !isBFGroup()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_DIFFICULTY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_DIFFICULTY); stmt->setUInt8(0, uint8(m_dungeonDifficulty)); stmt->setUInt32(1, GetGUID().GetCounter()); @@ -1948,7 +1948,7 @@ void Group::SetRaidDifficulty(Difficulty difficulty) m_raidDifficulty = difficulty; if (!isBGGroup() && !isBFGroup()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY); stmt->setUInt8(0, uint8(m_raidDifficulty)); stmt->setUInt32(1, GetGUID().GetCounter()); @@ -2262,7 +2262,7 @@ void Group::SetGroupMemberFlag(ObjectGuid guid, bool apply, GroupMemberFlags fla ToggleGroupMemberFlag(slot, flag, apply); // Preserve the new setting in the db - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_FLAG); stmt->setUInt8(0, slot->flags); stmt->setUInt32(1, guid.GetCounter()); diff --git a/src/server/game/Groups/GroupMgr.cpp b/src/server/game/Groups/GroupMgr.cpp index 8f7daa92a..84e5fa6c3 100644 --- a/src/server/game/Groups/GroupMgr.cpp +++ b/src/server/game/Groups/GroupMgr.cpp @@ -7,6 +7,7 @@ #include "Common.h" #include "DBCStores.h" #include "GroupMgr.h" +#include "Log.h" #include "InstanceSaveMgr.h" #include "World.h" diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index fe7842299..31b0ff3a6 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -128,7 +128,7 @@ inline void Guild::LogHolder::LoadEvent(LogEntry* entry) // Adds new event happened in game. // If maximum number of events is reached, oldest event is removed from collection. -inline void Guild::LogHolder::AddEvent(SQLTransaction& trans, LogEntry* entry) +inline void Guild::LogHolder::AddEvent(CharacterDatabaseTransaction trans, LogEntry* entry) { // Check max records limit if (m_log.size() >= m_maxRecords) @@ -164,9 +164,9 @@ inline uint32 Guild::LogHolder::GetNextGUID() } // EventLogEntry -void Guild::EventLogEntry::SaveToDB(SQLTransaction& trans) const +void Guild::EventLogEntry::SaveToDB(CharacterDatabaseTransaction trans) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG); stmt->setUInt32(0, m_guildId); stmt->setUInt32(1, m_guid); CharacterDatabase.ExecuteOrAppend(trans, stmt); @@ -200,11 +200,11 @@ void Guild::EventLogEntry::WritePacket(WorldPacket& data) const } // BankEventLogEntry -void Guild::BankEventLogEntry::SaveToDB(SQLTransaction& trans) const +void Guild::BankEventLogEntry::SaveToDB(CharacterDatabaseTransaction trans) const { uint8 index = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG); stmt->setUInt32( index, m_guildId); stmt->setUInt32(++index, m_guid); stmt->setUInt8 (++index, m_bankTabId); @@ -260,9 +260,9 @@ void Guild::RankInfo::LoadFromDB(Field* fields) m_rights |= GR_RIGHT_ALL; } -void Guild::RankInfo::SaveToDB(SQLTransaction& trans) const +void Guild::RankInfo::SaveToDB(CharacterDatabaseTransaction trans) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_RANK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_RANK); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_rankId); stmt->setString(2, m_name); @@ -271,7 +271,7 @@ void Guild::RankInfo::SaveToDB(SQLTransaction& trans) const CharacterDatabase.ExecuteOrAppend(trans, stmt); } -void Guild::RankInfo::CreateMissingTabsIfNeeded(uint8 tabs, SQLTransaction& trans, bool logOnCreate /* = false */) +void Guild::RankInfo::CreateMissingTabsIfNeeded(uint8 tabs, CharacterDatabaseTransaction trans, bool logOnCreate /* = false */) { for (uint8 i = 0; i < tabs; ++i) { @@ -286,7 +286,7 @@ void Guild::RankInfo::CreateMissingTabsIfNeeded(uint8 tabs, SQLTransaction& tran if (logOnCreate) LOG_ERROR("guild", "Guild %u has broken Tab %u for rank %u. Created default tab.", m_guildId, i, m_rankId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT); stmt->setUInt32(0, m_guildId); stmt->setUInt8(1, i); stmt->setUInt8(2, m_rankId); @@ -317,7 +317,7 @@ void Guild::RankInfo::SetName(std::string const& name) m_name = name; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_NAME); stmt->setString(0, m_name); stmt->setUInt8 (1, m_rankId); stmt->setUInt32(2, m_guildId); @@ -334,7 +334,7 @@ void Guild::RankInfo::SetRights(uint32 rights) m_rights = rights; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_RIGHTS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_RIGHTS); stmt->setUInt32(0, m_rights); stmt->setUInt8 (1, m_rankId); stmt->setUInt32(2, m_guildId); @@ -351,7 +351,7 @@ void Guild::RankInfo::SetBankMoneyPerDay(uint32 money) m_bankMoneyPerDay = money; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY); stmt->setUInt32(0, money); stmt->setUInt8 (1, m_rankId); stmt->setUInt32(2, m_guildId); @@ -368,7 +368,7 @@ void Guild::RankInfo::SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAnd if (saveToDB) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, guildBR.GetTabId()); stmt->setUInt8 (2, m_rankId); @@ -409,7 +409,7 @@ bool Guild::BankTab::LoadItemFromDB(Field* fields) { LOG_ERROR("guild", "Item (GUID %u, id: %u) not found in item_instance, deleting from guild bank!", itemGuid, itemEntry); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_tabId); stmt->setUInt8 (2, slotId); @@ -425,7 +425,7 @@ bool Guild::BankTab::LoadItemFromDB(Field* fields) } // Deletes contents of the tab from the world (and from DB if necessary) -void Guild::BankTab::Delete(SQLTransaction& trans, bool removeItemsFromDB) +void Guild::BankTab::Delete(CharacterDatabaseTransaction trans, bool removeItemsFromDB) { for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId) if (Item* pItem = m_items[slotId]) @@ -503,7 +503,7 @@ void Guild::BankTab::SetInfo(std::string const& name, std::string const& icon) m_name = name; m_icon = icon; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_INFO); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_INFO); stmt->setString(0, m_name); stmt->setString(1, m_icon); stmt->setUInt32(2, m_guildId); @@ -519,7 +519,7 @@ void Guild::BankTab::SetText(std::string const& text) m_text = text; utf8truncate(m_text, MAX_GUILD_BANK_TAB_TEXT_LEN); // DB and client size limitation - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT); stmt->setString(0, m_text); stmt->setUInt32(1, m_guildId); stmt->setUInt8 (2, m_tabId); @@ -528,14 +528,14 @@ void Guild::BankTab::SetText(std::string const& text) // Sets/removes contents of specified slot. // If pItem == nullptr contents are removed. -bool Guild::BankTab::SetItem(SQLTransaction& trans, uint8 slotId, Item* item) +bool Guild::BankTab::SetItem(CharacterDatabaseTransaction trans, uint8 slotId, Item* item) { if (slotId >= GUILD_BANK_MAX_SLOTS) return false; m_items[slotId] = item; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEM); stmt->setUInt32(0, m_guildId); stmt->setUInt8 (1, m_tabId); stmt->setUInt8 (2, slotId); @@ -605,7 +605,7 @@ void Guild::Member::SetPublicNote(std::string const& publicNote) m_publicNote = publicNote; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_PNOTE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_PNOTE); stmt->setString(0, publicNote); stmt->setUInt32(1, m_guid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -618,7 +618,7 @@ void Guild::Member::SetOfficerNote(std::string const& officerNote) m_officerNote = officerNote; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_OFFNOTE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_OFFNOTE); stmt->setString(0, officerNote); stmt->setUInt32(1, m_guid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -632,15 +632,15 @@ void Guild::Member::ChangeRank(uint8 newRank) if (Player* player = FindPlayer()) player->SetRank(newRank); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK); stmt->setUInt8 (0, newRank); stmt->setUInt32(1, m_guid.GetCounter()); CharacterDatabase.Execute(stmt); } -void Guild::Member::SaveToDB(SQLTransaction& trans) const +void Guild::Member::SaveToDB(CharacterDatabaseTransaction trans) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER); stmt->setUInt32(0, m_guildId); stmt->setUInt32(1, m_guid.GetCounter()); stmt->setUInt8 (2, m_rankId); @@ -722,11 +722,11 @@ void Guild::Member::WritePacket(WorldPacket& data, bool sendOfficerNote) const // Decreases amount of money/slots left for today. // If (tabId == GUILD_BANK_MAX_TABS) decrease money amount. // Otherwise decrease remaining items amount for specified tab. -void Guild::Member::UpdateBankWithdrawValue(SQLTransaction& trans, uint8 tabId, uint32 amount) +void Guild::Member::UpdateBankWithdrawValue(CharacterDatabaseTransaction trans, uint8 tabId, uint32 amount) { m_bankWithdraw[tabId] += amount; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER_WITHDRAW); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER_WITHDRAW); stmt->setUInt32(0, m_guid.GetCounter()); for (uint8 i = 0; i <= GUILD_BANK_MAX_TABS;) { @@ -781,7 +781,7 @@ void EmblemInfo::WritePacket(WorldPacket& data) const void EmblemInfo::SaveToDB(uint32 guildId) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_EMBLEM_INFO); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_EMBLEM_INFO); stmt->setUInt32(0, m_style); stmt->setUInt32(1, m_color); stmt->setUInt32(2, m_borderStyle); @@ -860,7 +860,7 @@ bool Guild::PlayerMoveItemData::InitItem() return (m_pItem != nullptr); } -void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* /*pOther*/, uint32 splitedAmount) +void Guild::PlayerMoveItemData::RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* /*pOther*/, uint32 splitedAmount) { if (splitedAmount) { @@ -876,7 +876,7 @@ void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* } } -Item* Guild::PlayerMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem) +Item* Guild::PlayerMoveItemData::StoreItem(CharacterDatabaseTransaction trans, Item* pItem) { ASSERT(pItem); m_pPlayer->MoveItemToInventory(m_vec, pItem, true); @@ -884,7 +884,7 @@ Item* Guild::PlayerMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem) return pItem; } -void Guild::PlayerMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const +void Guild::PlayerMoveItemData::LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const { ASSERT(pFrom); // Bank -> Char @@ -927,7 +927,7 @@ bool Guild::BankMoveItemData::HasWithdrawRights(MoveItemData* pOther) const return slots != 0; } -void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount) +void Guild::BankMoveItemData::RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount) { ASSERT(m_pItem); if (splitedAmount) @@ -946,7 +946,7 @@ void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pO m_pGuild->_UpdateMemberWithdrawSlots(trans, m_pPlayer->GetGUID(), m_container); } -Item* Guild::BankMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem) +Item* Guild::BankMoveItemData::StoreItem(CharacterDatabaseTransaction trans, Item* pItem) { if (!pItem) return nullptr; @@ -968,7 +968,7 @@ Item* Guild::BankMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem) return pLastItem; } -void Guild::BankMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const +void Guild::BankMoveItemData::LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const { ASSERT(pFrom->GetItem()); if (pFrom->IsBank()) @@ -986,7 +986,7 @@ void Guild::BankMoveItemData::LogAction(MoveItemData* pFrom) const MoveItemData::LogAction(pFrom); } -Item* Guild::BankMoveItemData::_StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const +Item* Guild::BankMoveItemData::_StoreItem(CharacterDatabaseTransaction trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const { uint8 slotId = uint8(pos.pos); uint32 count = pos.count; @@ -1120,7 +1120,7 @@ Guild::Guild(): Guild::~Guild() { - SQLTransaction temp(nullptr); + CharacterDatabaseTransaction temp(nullptr); _DeleteBankItems(temp); // Cleanup @@ -1161,10 +1161,11 @@ bool Guild::Create(Player* pLeader, std::string const& name) _CreateLogHolders(); LOG_DEBUG("guild", "GUILD: creating guild [%s] for leader %s (%s)", - name.c_str(), pLeader->GetName().c_str(), m_leaderGuid.ToString().c_str()); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + name.c_str(), pLeader->GetName().c_str(), m_leaderGuid.ToString().c_str()); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBERS); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBERS); stmt->setUInt32(0, m_id); trans->Append(stmt); @@ -1213,9 +1214,9 @@ void Guild::Disband() DeleteMember(itr->second->GetGUID(), true); } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD); stmt->setUInt32(0, m_id); trans->Append(stmt); @@ -1335,7 +1336,7 @@ void Guild::HandleSetMOTD(WorldSession* session, std::string const& motd) sScriptMgr->OnGuildMOTDChanged(this, motd); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MOTD); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MOTD); stmt->setString(0, motd); stmt->setUInt32(1, m_id); CharacterDatabase.Execute(stmt); @@ -1356,7 +1357,7 @@ void Guild::HandleSetInfo(WorldSession* session, std::string const& info) sScriptMgr->OnGuildInfoChanged(this, info); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_INFO); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_INFO); stmt->setString(0, info); stmt->setUInt32(1, m_id); CharacterDatabase.Execute(stmt); @@ -1684,7 +1685,7 @@ void Guild::HandleRemoveRank(WorldSession* session, uint8 rankId) return; // Delete bank rights for rank - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK); stmt->setUInt32(0, m_id); stmt->setUInt8(1, rankId); CharacterDatabase.Execute(stmt); @@ -1706,7 +1707,7 @@ void Guild::HandleMemberDepositMoney(WorldSession* session, uint32 amount) // Call script after validation and before money transfer. sScriptMgr->OnGuildMemberDepositMoney(this, player, amount); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); _ModifyBankMoney(trans, amount, true); player->ModifyMoney(-int32(amount)); @@ -1743,7 +1744,7 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool // Call script after validation and before money transfer. sScriptMgr->OnGuildMemberWitdrawMoney(this, player, amount, repair); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // Add money to player (if required) if (!repair) { @@ -2068,7 +2069,7 @@ bool Guild::Validate() } else { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); rankInfo->CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans, true); CharacterDatabase.CommitTransaction(trans); } @@ -2215,7 +2216,7 @@ bool Guild::AddMember(ObjectGuid guid, uint8 rankId) bool ok = false; // xinef: zomg! sync query // Player must exist - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD); stmt->setUInt32(0, guid.GetCounter()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) { @@ -2240,7 +2241,7 @@ bool Guild::AddMember(ObjectGuid guid, uint8 rankId) sWorld->UpdateGlobalPlayerGuild(guid.GetCounter(), m_id); } - SQLTransaction trans(nullptr); + CharacterDatabaseTransaction trans(nullptr); member->SaveToDB(trans); _UpdateAccountsNumber(); @@ -2378,9 +2379,9 @@ void Guild::_CreateNewBankTab() uint8 tabId = _GetPurchasedTabsSize(); // Next free id m_bankTabs.push_back(new BankTab(m_id, tabId)); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TAB); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TAB); stmt->setUInt32(0, m_id); stmt->setUInt8 (1, tabId); trans->Append(stmt); @@ -2399,7 +2400,7 @@ void Guild::_CreateNewBankTab() void Guild::_CreateDefaultGuildRanks(LocaleConstant loc) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS); stmt->setUInt32(0, m_id); CharacterDatabase.Execute(stmt); @@ -2424,7 +2425,7 @@ bool Guild::_CreateRank(std::string const& name, uint32 rights) RankInfo info(m_id, newRankId, name, rights, 0); m_ranks.push_back(info); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); info.CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans); info.SaveToDB(trans); CharacterDatabase.CommitTransaction(trans); @@ -2456,7 +2457,7 @@ bool Guild::_IsLeader(Player* player) const return false; } -void Guild::_DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB) +void Guild::_DeleteBankItems(CharacterDatabaseTransaction trans, bool removeItemsFromDB) { for (uint8 tabId = 0; tabId < _GetPurchasedTabsSize(); ++tabId) { @@ -2467,7 +2468,7 @@ void Guild::_DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB) m_bankTabs.clear(); } -bool Guild::_ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add) +bool Guild::_ModifyBankMoney(CharacterDatabaseTransaction trans, uint64 amount, bool add) { if (add) m_bankMoney += amount; @@ -2479,7 +2480,7 @@ bool Guild::_ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add) m_bankMoney -= amount; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_MONEY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_MONEY); stmt->setUInt64(0, m_bankMoney); stmt->setUInt32(1, m_id); trans->Append(stmt); @@ -2494,7 +2495,7 @@ void Guild::_SetLeaderGUID(Member* pLeader) m_leaderGuid = pLeader->GetGUID(); pLeader->ChangeRank(GR_GUILDMASTER); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_LEADER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_LEADER); stmt->setUInt32(0, m_leaderGuid.GetCounter()); stmt->setUInt32(1, m_id); CharacterDatabase.Execute(stmt); @@ -2586,7 +2587,7 @@ inline int32 Guild::_GetMemberRemainingMoney(Member const* member) const return 0; } -inline void Guild::_UpdateMemberWithdrawSlots(SQLTransaction& trans, ObjectGuid guid, uint8 tabId) +inline void Guild::_UpdateMemberWithdrawSlots(CharacterDatabaseTransaction trans, ObjectGuid guid, uint8 tabId) { if (Member* member = GetMember(guid)) { @@ -2612,7 +2613,7 @@ inline bool Guild::_MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 righ // Add new event log record inline void Guild::_LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2, uint8 newRank) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); m_eventLog->AddEvent(trans, new EventLogEntry(m_id, m_eventLog->GetNextGUID(), eventType, playerGuid1, playerGuid2, newRank)); CharacterDatabase.CommitTransaction(trans); @@ -2620,7 +2621,7 @@ inline void Guild::_LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid } // Add new bank event log record -void Guild::_LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid guid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) +void Guild::_LogBankEvent(CharacterDatabaseTransaction trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid guid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId) { if (tabId > GUILD_BANK_MAX_TABS) return; @@ -2648,7 +2649,7 @@ inline Item* Guild::_GetItem(uint8 tabId, uint8 slotId) const return nullptr; } -inline void Guild::_RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId) +inline void Guild::_RemoveItem(CharacterDatabaseTransaction trans, uint8 tabId, uint8 slotId) { if (BankTab* pTab = GetBankTab(tabId)) pTab->SetItem(trans, slotId, nullptr); @@ -2727,7 +2728,7 @@ bool Guild::_DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError if (swap) pSrc->LogAction(pDest); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // 3. Log bank events pDest->LogBankEvent(trans, pSrc, pSrcItem->GetCount()); if (swap) diff --git a/src/server/game/Guilds/Guild.h b/src/server/game/Guilds/Guild.h index 7968522d1..55960f2a1 100644 --- a/src/server/game/Guilds/Guild.h +++ b/src/server/game/Guilds/Guild.h @@ -302,7 +302,7 @@ public: // pussywizard: public class Member void ResetFlags() { m_flags = GUILDMEMBER_STATUS_NONE; } bool LoadFromDB(Field* fields); - void SaveToDB(SQLTransaction& trans) const; + void SaveToDB(CharacterDatabaseTransaction trans) const; void WritePacket(WorldPacket& data, bool sendOfficerNote) const; ObjectGuid GetGUID() const { return m_guid; } @@ -326,7 +326,7 @@ public: // pussywizard: public class Member inline bool IsRankNotLower(uint8 rankId) const { return m_rankId <= rankId; } inline bool IsSamePlayer(ObjectGuid guid) const { return m_guid == guid; } - void UpdateBankWithdrawValue(SQLTransaction& trans, uint8 tabId, uint32 amount); + void UpdateBankWithdrawValue(CharacterDatabaseTransaction trans, uint8 tabId, uint32 amount); int32 GetBankWithdrawValue(uint8 tabId) const; void ResetValues(); @@ -384,7 +384,7 @@ private: ObjectGuid::LowType GetGUID() const { return m_guid; } uint64 GetTimestamp() const { return m_timestamp; } - virtual void SaveToDB(SQLTransaction& trans) const = 0; + virtual void SaveToDB(CharacterDatabaseTransaction trans) const = 0; virtual void WritePacket(WorldPacket& data) const = 0; protected: @@ -405,7 +405,7 @@ private: ~EventLogEntry() override { } - void SaveToDB(SQLTransaction& trans) const override; + void SaveToDB(CharacterDatabaseTransaction trans) const override; void WritePacket(WorldPacket& data) const override; private: @@ -437,7 +437,7 @@ private: ~BankEventLogEntry() override { } - void SaveToDB(SQLTransaction& trans) const override; + void SaveToDB(CharacterDatabaseTransaction trans) const override; void WritePacket(WorldPacket& data) const override; private: @@ -465,7 +465,7 @@ private: // Adds event from DB to collection void LoadEvent(LogEntry* entry); // Adds new event to collection and saves it to DB - void AddEvent(SQLTransaction& trans, LogEntry* entry); + void AddEvent(CharacterDatabaseTransaction trans, LogEntry* entry); // Writes information about all events to packet void WritePacket(WorldPacket& data) const; uint32 GetNextGUID(); @@ -488,7 +488,7 @@ private: m_bankMoneyPerDay(rankId != GR_GUILDMASTER ? money : GUILD_WITHDRAW_MONEY_UNLIMITED) { } void LoadFromDB(Field* fields); - void SaveToDB(SQLTransaction& trans) const; + void SaveToDB(CharacterDatabaseTransaction trans) const; void WritePacket(WorldPacket& data) const; uint8 GetId() const { return m_rankId; } @@ -514,7 +514,7 @@ private: } void SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAndSlots, bool saveToDB); - void CreateMissingTabsIfNeeded(uint8 ranks, SQLTransaction& trans, bool logOnCreate = false); + void CreateMissingTabsIfNeeded(uint8 ranks, CharacterDatabaseTransaction trans, bool logOnCreate = false); private: uint32 m_guildId; @@ -536,7 +536,7 @@ private: void LoadFromDB(Field* fields); bool LoadItemFromDB(Field* fields); - void Delete(SQLTransaction& trans, bool removeItemsFromDB = false); + void Delete(CharacterDatabaseTransaction trans, bool removeItemsFromDB = false); void WritePacket(WorldPacket& data) const; bool WriteSlotPacket(WorldPacket& data, uint8 slotId, bool ignoreEmpty = true) const; @@ -551,7 +551,7 @@ private: void SendText(const Guild* guild, WorldSession* session) const; inline Item* GetItem(uint8 slotId) const { return slotId < GUILD_BANK_MAX_SLOTS ? m_items[slotId] : nullptr; } - bool SetItem(SQLTransaction& trans, uint8 slotId, Item* pItem); + bool SetItem(CharacterDatabaseTransaction trans, uint8 slotId, Item* pItem); private: uint32 m_guildId; @@ -585,11 +585,11 @@ private: // Clones stored item bool CloneItem(uint32 count); // Remove item from container (if splited update items fields) - virtual void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) = 0; + virtual void RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount = 0) = 0; // Saves item to container - virtual Item* StoreItem(SQLTransaction& trans, Item* pItem) = 0; + virtual Item* StoreItem(CharacterDatabaseTransaction trans, Item* pItem) = 0; // Log bank event - virtual void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const = 0; + virtual void LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const = 0; // Log GM action virtual void LogAction(MoveItemData* pFrom) const; // Copy slots id from position vector @@ -619,9 +619,9 @@ private: bool IsBank() const override { return false; } bool InitItem() override; - void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) override; - Item* StoreItem(SQLTransaction& trans, Item* pItem) override; - void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override; + void RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount = 0) override; + Item* StoreItem(CharacterDatabaseTransaction trans, Item* pItem) override; + void LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const override; protected: InventoryResult CanStore(Item* pItem, bool swap) override; }; @@ -636,16 +636,16 @@ private: bool InitItem() override; bool HasStoreRights(MoveItemData* pOther) const override; bool HasWithdrawRights(MoveItemData* pOther) const override; - void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount) override; - Item* StoreItem(SQLTransaction& trans, Item* pItem) override; - void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override; + void RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount) override; + Item* StoreItem(CharacterDatabaseTransaction trans, Item* pItem) override; + void LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const override; void LogAction(MoveItemData* pFrom) const override; protected: InventoryResult CanStore(Item* pItem, bool swap) override; private: - Item* _StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const; + Item* _StoreItem(CharacterDatabaseTransaction trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const; bool _ReserveSpace(uint8 slotId, Item* pItem, Item* pItemDest, uint32& count); void CanStoreItemInTab(Item* pItem, uint8 skipSlotId, bool merge, uint32& count); }; @@ -756,7 +756,7 @@ public: void ResetTimes(); - [[nodiscard]] bool ModifyBankMoney(SQLTransaction& trans, const uint64& amount, bool add) { return _ModifyBankMoney(trans, amount, add); } + [[nodiscard]] bool ModifyBankMoney(CharacterDatabaseTransaction trans, const uint64& amount, bool add) { return _ModifyBankMoney(trans, amount, add); } [[nodiscard]] uint32 GetMemberSize() const { return m_members.size(); } protected: @@ -799,7 +799,7 @@ private: inline void _DeleteMemberFromDB(ObjectGuid::LowType lowguid) const { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBER); stmt->setUInt32(0, lowguid); CharacterDatabase.Execute(stmt); } @@ -815,8 +815,8 @@ private: // Update account number when member added/removed from guild void _UpdateAccountsNumber(); bool _IsLeader(Player* player) const; - void _DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB = false); - bool _ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add); + void _DeleteBankItems(CharacterDatabaseTransaction trans, bool removeItemsFromDB = false); + bool _ModifyBankMoney(CharacterDatabaseTransaction trans, uint64 amount, bool add); void _SetLeaderGUID(Member* pLeader); void _SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay); @@ -829,14 +829,14 @@ private: int32 _GetMemberRemainingSlots(Member const* member, uint8 tabId) const; int32 _GetMemberRemainingMoney(Member const* member) const; - void _UpdateMemberWithdrawSlots(SQLTransaction& trans, ObjectGuid guid, uint8 tabId); + void _UpdateMemberWithdrawSlots(CharacterDatabaseTransaction trans, ObjectGuid guid, uint8 tabId); bool _MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const; void _LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2 = ObjectGuid::Empty, uint8 newRank = 0); - void _LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid playerGuid, uint32 itemOrMoney, uint16 itemStackCount = 0, uint8 destTabId = 0); + void _LogBankEvent(CharacterDatabaseTransaction trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid playerGuid, uint32 itemOrMoney, uint16 itemStackCount = 0, uint8 destTabId = 0); Item* _GetItem(uint8 tabId, uint8 slotId) const; - void _RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId); + void _RemoveItem(CharacterDatabaseTransaction trans, uint8 tabId, uint8 slotId); void _MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAmount); bool _DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError, uint32 splitedAmount = 0); diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 33309461d..8bc0400b9 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -300,7 +300,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) _player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); item->DeleteFromInventoryDB(trans); item->SaveToDB(trans); AH->SaveToDB(trans); @@ -348,7 +348,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) { _player->MoveItemFromInventory(item2->GetBagSlot(), item2->GetSlot(), true); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); item2->DeleteFromInventoryDB(trans); item2->DeleteFromDB(trans); CharacterDatabase.CommitTransaction(trans); @@ -361,13 +361,13 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) _player->ItemRemovedQuestCheck(item2->GetEntry(), count[j]); item2->SendUpdateToPlayer(_player); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); item2->SaveToDB(trans); CharacterDatabase.CommitTransaction(trans); } } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); newItem->SaveToDB(trans); AH->SaveToDB(trans); _player->SaveInventoryAndGoldToDB(trans); @@ -446,7 +446,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) return; } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (price < auction->buyout || auction->buyout == 0) { @@ -468,7 +468,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) auction->bid = price; GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID); stmt->setUInt32(0, auction->bidder.GetCounter()); stmt->setUInt32(1, auction->bid); stmt->setUInt32(2, auction->Id); @@ -533,7 +533,7 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData) AuctionEntry* auction = auctionHouse->GetAuction(auctionId); Player* player = GetPlayer(); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (auction && auction->owner == player->GetGUID()) { Item* pItem = sAuctionMgr->GetAItem(auction->item_guid); diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 928ff9f96..e3bbe26c9 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -464,7 +464,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData) { if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt8(1, BG_DESERTION_TYPE_LEAVE_QUEUE); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index d0225c5e2..f64049fe1 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -329,7 +329,7 @@ void WorldSession::HandleCalendarAddEvent(WorldPacket& recvData) throw; } - SQLTransaction trans; + CharacterDatabaseTransaction trans; if (inviteCount > 1) trans = CharacterDatabase.BeginTransaction(); @@ -489,7 +489,7 @@ void WorldSession::HandleCalendarCopyEvent(WorldPacket& recvData) sCalendarMgr->AddEvent(newEvent, CALENDAR_SENDTYPE_COPY); CalendarInviteStore invites = sCalendarMgr->GetEventInvites(eventId); - SQLTransaction trans; + CharacterDatabaseTransaction trans; if (invites.size() > 1) trans = CharacterDatabase.BeginTransaction(); @@ -794,7 +794,7 @@ void WorldSession::HandleSetSavedInstanceExtend(WorldPacket& recvData) instanceBind->extended = (bool)toggleExtendOn; // update in db - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE_EXTENDED); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE_EXTENDED); stmt->setUInt8(0, toggleExtendOn ? 1 : 0); stmt->setUInt32(1, GetPlayer()->GetGUID().GetCounter()); stmt->setUInt32(2, instanceBind->save->GetInstanceId()); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 76ff50649..c9620f650 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -38,12 +38,15 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "QueryHolder.h" +#include "StringConvert.h" +#include "Tokenize.h" #ifdef ELUNA #include "LuaEngine.h" #endif -class LoginQueryHolder : public SQLQueryHolder +class LoginQueryHolder : public CharacterDatabaseQueryHolder { private: uint32 m_accountId; @@ -51,6 +54,7 @@ private: public: LoginQueryHolder(uint32 accountId, ObjectGuid guid) : m_accountId(accountId), m_guid(guid) { } + ObjectGuid GetGuid() const { return m_guid; } uint32 GetAccountId() const { return m_accountId; } bool Initialize(); @@ -63,7 +67,7 @@ bool LoginQueryHolder::Initialize() bool res = true; ObjectGuid::LowType lowGuid = m_guid.GetCounter(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER); stmt->setUInt32(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_FROM, stmt); @@ -229,11 +233,7 @@ void WorldSession::HandleCharEnum(PreparedQueryResult result) void WorldSession::HandleCharEnumOpcode(WorldPacket& /*recvData*/) { - // remove expired bans - // pussywizard: moved to world update to do it once >_> - // PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_BANS); - // CharacterDatabase.Execute(stmt); - PreparedStatement* stmt; + CharacterDatabasePreparedStatement* stmt = nullptr; /// get all the data necessary for loading all characters (along with their pets) on the account @@ -245,430 +245,346 @@ void WorldSession::HandleCharEnumOpcode(WorldPacket& /*recvData*/) stmt->setUInt8(0, PET_SAVE_AS_CURRENT); stmt->setUInt32(1, GetAccountId()); - _charEnumCallback = CharacterDatabase.AsyncQuery(stmt); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleCharEnum, this, std::placeholders::_1))); } void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) { - std::string name; - uint8 race_, class_; + std::shared_ptr createInfo = std::make_shared(); - recvData >> name; - - recvData >> race_; - recvData >> class_; - - // extract other data required for player creating - uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId; - recvData >> gender >> skin >> face; - recvData >> hairStyle >> hairColor >> facialHair >> outfitId; - - WorldPacket data(SMSG_CHAR_CREATE, 1); // returned with diff.values in all cases + recvData >> createInfo->Name + >> createInfo->Race + >> createInfo->Class + >> createInfo->Gender + >> createInfo->Skin + >> createInfo->Face + >> createInfo->HairStyle + >> createInfo->HairColor + >> createInfo->FacialHair + >> createInfo->OutfitId; if (AccountMgr::IsPlayerAccount(GetSecurity())) { if (uint32 mask = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED)) { - if (mask & (1 << Player::TeamIdForRace(race_))) + if (mask & (1 << Player::TeamIdForRace(createInfo->Race))) { - data << (uint8)CHAR_CREATE_DISABLED; - SendPacket(&data); + SendCharCreate(CHAR_CREATE_DISABLED); return; } } } - ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_); + ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(createInfo->Class); if (!classEntry) { - data << (uint8)CHAR_CREATE_FAILED; - SendPacket(&data); - LOG_ERROR("network.opcode", "Class (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", class_, GetAccountId()); + SendCharCreate(CHAR_CREATE_FAILED); + LOG_ERROR("network.opcode", "Class (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo->Class, GetAccountId()); return; } - ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_); + ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(createInfo->Race); if (!raceEntry) { - data << (uint8)CHAR_CREATE_FAILED; - SendPacket(&data); - LOG_ERROR("network.opcode", "Race (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", race_, GetAccountId()); + SendCharCreate(CHAR_CREATE_FAILED); + LOG_ERROR("network.opcode", "Race (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo->Race, GetAccountId()); return; } // prevent character creating Expansion race without Expansion account if (raceEntry->expansion > Expansion()) { - data << (uint8)CHAR_CREATE_EXPANSION; - LOG_ERROR("network.opcode", "Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, race_); - SendPacket(&data); + SendCharCreate(CHAR_CREATE_EXPANSION); + LOG_ERROR("network.opcode", "Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, createInfo->Race); return; } // prevent character creating Expansion class without Expansion account if (classEntry->expansion > Expansion()) { - data << (uint8)CHAR_CREATE_EXPANSION_CLASS; - LOG_ERROR("network.opcode", "Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, class_); - SendPacket(&data); + SendCharCreate(CHAR_CREATE_EXPANSION_CLASS); + LOG_ERROR("network.opcode", "Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, createInfo->Class); return; } if (AccountMgr::IsPlayerAccount(GetSecurity())) { uint32 raceMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK); - if ((1 << (race_ - 1)) & raceMaskDisabled) + if ((1 << (createInfo->Race - 1)) & raceMaskDisabled) { - data << uint8(CHAR_CREATE_DISABLED); - SendPacket(&data); + SendCharCreate(CHAR_CREATE_DISABLED); return; } uint32 classMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK); - if ((1 << (class_ - 1)) & classMaskDisabled) + if ((1 << (createInfo->Class - 1)) & classMaskDisabled) { - data << uint8(CHAR_CREATE_DISABLED); - SendPacket(&data); + SendCharCreate(CHAR_CREATE_DISABLED); return; } } // prevent character creating with invalid name - if (!normalizePlayerName(name)) + if (!normalizePlayerName(createInfo->Name)) { - data << (uint8)CHAR_NAME_NO_NAME; - SendPacket(&data); + SendCharCreate(CHAR_NAME_NO_NAME); LOG_ERROR("network.opcode", "Account:[%d] but tried to Create character with empty [name] ", GetAccountId()); return; } // check name limitations - uint8 res = ObjectMgr::CheckPlayerName(name, true); + uint8 res = ObjectMgr::CheckPlayerName(createInfo->Name, true); if (res != CHAR_NAME_SUCCESS) { - data << uint8(res); - SendPacket(&data); + SendCharCreate(ResponseCodes(res)); return; } - if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(name)) + if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(createInfo->Name)) { - data << (uint8)CHAR_NAME_RESERVED; - SendPacket(&data); + SendCharCreate(CHAR_NAME_RESERVED); return; } // speedup check for heroic class disabled case uint32 heroic_free_slots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); - if (heroic_free_slots == 0 && AccountMgr::IsPlayerAccount(GetSecurity()) && class_ == CLASS_DEATH_KNIGHT) + if (heroic_free_slots == 0 && AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT) { - data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT; - SendPacket(&data); + SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); return; } // speedup check for heroic class disabled case uint32 req_level_for_heroic = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); - if (AccountMgr::IsPlayerAccount(GetSecurity()) && class_ == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) + if (AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { - data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT; - SendPacket(&data); + SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT); return; } - delete _charCreateCallback.GetParam(); // Delete existing if any, to make the callback chain reset to stage 0 - _charCreateCallback.SetParam(new CharacterCreateInfo(name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId, recvData)); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); - stmt->setString(0, name); - _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); -} + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); + stmt->setString(0, createInfo->Name); -void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, CharacterCreateInfo* createInfo) -{ - /** This is a series of callbacks executed consecutively as a result from the database becomes available. - This is much more efficient than synchronous requests on packet handler, and much less DoS prone. - It also prevents data syncrhonisation errors. - */ - switch (_charCreateCallback.GetStage()) + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result) { - case 0: + if (result) + { + SendCharCreate(CHAR_CREATE_NAME_IN_USE); + return; + } + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SUM_REALM_CHARACTERS); + stmt->setUInt32(0, GetAccountId()); + queryCallback.SetNextQuery(LoginDatabase.AsyncQuery(stmt)); + }) + .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result) + { + uint64 acctCharCount = 0; + if (result) + { + Field* fields = result->Fetch(); + acctCharCount = uint64(fields[0].GetDouble()); + } + + if (acctCharCount >= static_cast(sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT))) + { + SendCharCreate(CHAR_CREATE_ACCOUNT_LIMIT); + return; + } + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); + stmt->setUInt32(0, GetAccountId()); + queryCallback.SetNextQuery(CharacterDatabase.AsyncQuery(stmt)); + }) + .WithChainingPreparedCallback([this, createInfo](QueryCallback& queryCallback, PreparedQueryResult result) + { + if (result) + { + Field* fields = result->Fetch(); + createInfo->CharCount = uint8(fields[0].GetUInt64()); // SQL's COUNT() returns uint64 but it will always be less than uint8.Max + + if (createInfo->CharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) { - if (result) - { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_NAME_IN_USE); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - - ASSERT(_charCreateCallback.GetParam() == createInfo); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SUM_REALM_CHARACTERS); - stmt->setUInt32(0, GetAccountId()); - - _charCreateCallback.FreeResult(); - _charCreateCallback.SetFutureResult(LoginDatabase.AsyncQuery(stmt)); - _charCreateCallback.NextStage(); + SendCharCreate(CHAR_CREATE_SERVER_LIMIT); + return; } - break; - case 1: + } + + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); + uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); + + std::function finalizeCharacterCreation = [this, createInfo](PreparedQueryResult result) + { + bool haveSameRace = false; + uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); + bool hasHeroicReqLevel = (heroicReqLevel == 0); + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); + uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); + bool checkDeathKnightReqs = AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT; + + if (result) { - uint16 acctCharCount = 0; - if (result) + TeamId teamId = Player::TeamIdForRace(createInfo->Race); + uint32 freeDeathKnightSlots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); + + Field* field = result->Fetch(); + uint8 accRace = field[1].GetUInt8(); + + if (checkDeathKnightReqs) { - Field* fields = result->Fetch(); - // SELECT SUM(x) is MYSQL_TYPE_NEWDECIMAL - needs to be read as string - const char* ch = fields[0].GetCString(); - if (ch) - acctCharCount = atoi(ch); - } - - if (acctCharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) - { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_ACCOUNT_LIMIT); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - - ASSERT(_charCreateCallback.GetParam() == createInfo); - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); - stmt->setUInt32(0, GetAccountId()); - - _charCreateCallback.FreeResult(); - _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); - _charCreateCallback.NextStage(); - } - break; - case 2: - { - if (result) - { - Field* fields = result->Fetch(); - createInfo->CharCount = uint8(fields[0].GetUInt64()); // SQL's COUNT() returns uint64 but it will always be less than uint8.Max - - if (createInfo->CharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) + uint8 accClass = field[2].GetUInt8(); + if (accClass == CLASS_DEATH_KNIGHT) { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_SERVER_LIMIT); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); + if (freeDeathKnightSlots > 0) + --freeDeathKnightSlots; + + if (freeDeathKnightSlots == 0) + { + SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); + return; + } + } + + if (!hasHeroicReqLevel) + { + uint8 accLevel = field[0].GetUInt8(); + if (accLevel >= heroicReqLevel) + hasHeroicReqLevel = true; + } + } + + // need to check team only for first character + /// @todo what to if account already has characters of both races? + if (!allowTwoSideAccounts) + { + uint32 accTeam = 0; + if (accRace > 0) + accTeam = Player::TeamIdForRace(accRace); + + if (accTeam != teamId) + { + SendCharCreate(CHAR_CREATE_PVP_TEAMS_VIOLATION); return; } } - bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); - uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); - - _charCreateCallback.FreeResult(); - - if (!allowTwoSideAccounts || skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT) + // search same race for cinematic or same class if need + /// @todo check if cinematic already shown? (already logged in?; cinematic field) + while ((skipCinematics == 1 && !haveSameRace) || createInfo->Class == CLASS_DEATH_KNIGHT) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CREATE_INFO); - stmt->setUInt32(0, GetAccountId()); - stmt->setUInt32(1, (skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT) ? 10 : 1); - _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); - _charCreateCallback.NextStage(); - return; - } + if (!result->NextRow()) + break; - _charCreateCallback.NextStage(); - HandleCharCreateCallback(PreparedQueryResult(nullptr), createInfo); // Will jump to case 3 - } - break; - case 3: - { - bool haveSameRace = false; - uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); - bool hasHeroicReqLevel = (heroicReqLevel == 0); - bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); - uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); + field = result->Fetch(); + accRace = field[1].GetUInt8(); - if (result) - { - TeamId teamId = Player::TeamIdForRace(createInfo->Race); - uint32 freeHeroicSlots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); + if (!haveSameRace) + haveSameRace = createInfo->Race == accRace; - Field* field = result->Fetch(); - uint8 accRace = field[1].GetUInt8(); - - if (AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT) + if (checkDeathKnightReqs) { - uint8 accClass = field[2].GetUInt8(); - if (accClass == CLASS_DEATH_KNIGHT) + uint8 acc_class = field[2].GetUInt8(); + if (acc_class == CLASS_DEATH_KNIGHT) { - if (freeHeroicSlots > 0) - --freeHeroicSlots; + if (freeDeathKnightSlots > 0) + --freeDeathKnightSlots; - if (freeHeroicSlots == 0) + if (freeDeathKnightSlots == 0) { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_UNIQUE_CLASS_LIMIT); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); + SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); return; } } if (!hasHeroicReqLevel) { - uint8 accLevel = field[0].GetUInt8(); - if (accLevel >= heroicReqLevel) + uint8 acc_level = field[0].GetUInt8(); + if (acc_level >= heroicReqLevel) hasHeroicReqLevel = true; } } - - // need to check team only for first character - // TODO: what to if account already has characters of both races? - if (!allowTwoSideAccounts) - { - uint32 accTeamId = TEAM_NEUTRAL; - if (accRace > 0) - accTeamId = Player::TeamIdForRace(accRace); - - if (accTeamId != teamId) - { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_PVP_TEAMS_VIOLATION); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - } - - // search same race for cinematic or same class if need - // TODO: check if cinematic already shown? (already logged in?; cinematic field) - while ((skipCinematics == 1 && !haveSameRace) || createInfo->Class == CLASS_DEATH_KNIGHT) - { - if (!result->NextRow()) - break; - - field = result->Fetch(); - accRace = field[1].GetUInt8(); - - if (!haveSameRace) - haveSameRace = createInfo->Race == accRace; - - if (AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT) - { - uint8 acc_class = field[2].GetUInt8(); - if (acc_class == CLASS_DEATH_KNIGHT) - { - if (freeHeroicSlots > 0) - --freeHeroicSlots; - - if (freeHeroicSlots == 0) - { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_UNIQUE_CLASS_LIMIT); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - } - - if (!hasHeroicReqLevel) - { - uint8 acc_level = field[0].GetUInt8(); - if (acc_level >= heroicReqLevel) - hasHeroicReqLevel = true; - } - } - } } - - if (AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT && !hasHeroicReqLevel) - { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_LEVEL_REQUIREMENT); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - - if (createInfo->Data.rpos() < createInfo->Data.wpos()) - { - uint8 unk; - createInfo->Data >> unk; - LOG_DEBUG("network", "Character creation %s (account %u) has unhandled tail data: [%u]", createInfo->Name.c_str(), GetAccountId(), unk); - } - - // pussywizard: - if (sWorld->GetGlobalPlayerGUID(createInfo->Name)) - { - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_NAME_IN_USE); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - - Player newChar(this); - newChar.GetMotionMaster()->Initialize(); - if (!newChar.Create(sObjectMgr->GetGenerator().Generate(), createInfo)) - { - // Player not create (race/class/etc problem?) - newChar.CleanupsBeforeDelete(); - - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - - if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2) - newChar.setCinematic(1); // not show intro - - newChar.SetAtLoginFlag(AT_LOGIN_FIRST); // First login - - // Player created, save it now - newChar.SaveToDB(true, false); - createInfo->CharCount += 1; - - SQLTransaction trans = LoginDatabase.BeginTransaction(); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); - stmt->setUInt32(0, GetAccountId()); - stmt->setUInt32(1, realm.Id.Realm); - trans->Append(stmt); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS); - stmt->setUInt32(0, createInfo->CharCount); - stmt->setUInt32(1, GetAccountId()); - stmt->setUInt32(2, realm.Id.Realm); - trans->Append(stmt); - - LoginDatabase.CommitTransaction(trans); - - WorldPacket data(SMSG_CHAR_CREATE, 1); - data << uint8(CHAR_CREATE_SUCCESS); - SendPacket(&data); - - std::string IP_str = GetRemoteAddress(); - LOG_DEBUG("network.opcode", "Account: %d (IP: %s) Create Character:[%s] (%s)", GetAccountId(), IP_str.c_str(), createInfo->Name.c_str(), newChar.GetGUID().ToString().c_str()); - LOG_INFO("entities.player", "Account: %d (IP: %s) Create Character:[%s] (%s)", GetAccountId(), IP_str.c_str(), createInfo->Name.c_str(), newChar.GetGUID().ToString().c_str()); - sScriptMgr->OnPlayerCreate(&newChar); - sWorld->AddGlobalPlayerData(newChar.GetGUID().GetCounter(), GetAccountId(), newChar.GetName(), newChar.getGender(), newChar.getRace(), newChar.getClass(), newChar.getLevel(), 0, 0); - - newChar.CleanupsBeforeDelete(); - delete createInfo; - _charCreateCallback.Reset(); } - break; - } + + if (checkDeathKnightReqs && !hasHeroicReqLevel) + { + SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT); + return; + } + + // Check name uniqueness in the same step as saving to database + if (sWorld->GetGlobalPlayerGUID(createInfo->Name)) + { + SendCharCreate(CHAR_CREATE_NAME_IN_USE); + return; + } + + std::shared_ptr newChar(new Player(this), [](Player* ptr) + { + ptr->CleanupsBeforeDelete(); + delete ptr; + }); + + newChar->GetMotionMaster()->Initialize(); + if (!newChar->Create(sObjectMgr->GetGenerator().Generate(), createInfo.get())) + { + // Player not create (race/class/etc problem?) + SendCharCreate(CHAR_CREATE_ERROR); + return; + } + + if ((haveSameRace && skipCinematics == 1) || skipCinematics == 2) + newChar->setCinematic(1); // not show intro + + newChar->SetAtLoginFlag(AT_LOGIN_FIRST); // First login + + CharacterDatabaseTransaction characterTransaction = CharacterDatabase.BeginTransaction(); + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); + + // Player created, save it now + newChar->SaveToDB(characterTransaction, true, false); + createInfo->CharCount++; + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); + stmt->setUInt32(0, GetAccountId()); + stmt->setUInt32(1, realm.Id.Realm); + trans->Append(stmt); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS); + stmt->setUInt32(0, createInfo->CharCount); + stmt->setUInt32(1, GetAccountId()); + stmt->setUInt32(2, realm.Id.Realm); + trans->Append(stmt); + + LoginDatabase.CommitTransaction(trans); + + AddTransactionCallback(CharacterDatabase.AsyncCommitTransaction(characterTransaction)).AfterComplete([this, newChar = std::move(newChar)](bool success) + { + if (success) + { + 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()); + sWorld->AddGlobalPlayerData(newChar->GetGUID().GetCounter(), GetAccountId(), newChar->GetName(), newChar->getGender(), newChar->getRace(), newChar->getClass(), newChar->getLevel(), 0, 0); + + SendCharCreate(CHAR_CREATE_SUCCESS); + } + else + SendCharCreate(CHAR_CREATE_ERROR); + }); + }; + + if (allowTwoSideAccounts && !skipCinematics && createInfo->Class != CLASS_DEATH_KNIGHT) + { + finalizeCharacterCreation(PreparedQueryResult(nullptr)); + return; + } + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CREATE_INFO); + stmt->setUInt32(0, GetAccountId()); + stmt->setUInt32(1, (skipCinematics == 1 || createInfo->Class == CLASS_DEATH_KNIGHT) ? 10 : 1); + queryCallback.WithPreparedCallback(std::move(finalizeCharacterCreation)).SetNextQuery(CharacterDatabase.AsyncQuery(stmt)); + })); } void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) @@ -683,22 +599,18 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) if (ObjectAccessor::FindConnectedPlayer(guid) || sWorld->FindOfflineSessionForCharacterGUID(guid.GetCounter())) { sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_FAILED; - SendPacket(&data); return; } uint32 accountId = 0; + uint8 level = 0; std::string name; // is guild leader if (sGuildMgr->GetGuildByLeader(guid)) { sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_FAILED_GUILD_LEADER; - SendPacket(&data); + SendCharDelete(CHAR_DELETE_FAILED_GUILD_LEADER); return; } @@ -706,16 +618,15 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) if (sArenaTeamMgr->GetArenaTeamByCaptain(guid)) { sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_FAILED_ARENA_CAPTAIN; - SendPacket(&data); + SendCharDelete(CHAR_DELETE_FAILED_ARENA_CAPTAIN); return; } if (GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(guid.GetCounter())) { - accountId = playerData->accountId; - name = playerData->name; + accountId = playerData->accountId; + name = playerData->name; + level = playerData->level; } // prevent deleting other players' characters using cheating tools @@ -725,9 +636,7 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) return; } - std::string IP_str = GetRemoteAddress(); - LOG_DEBUG("network.opcode", "Account: %d (IP: %s) Delete Character:[%s] (%s)", GetAccountId(), IP_str.c_str(), name.c_str(), guid.ToString().c_str()); - LOG_INFO("entities.player", "Account: %d (IP: %s) Delete Character:[%s] (%s)", GetAccountId(), IP_str.c_str(), name.c_str(), guid.ToString().c_str()); + LOG_INFO("entities.player.character", "Account: %d, IP: %s deleted character: %s, %s, Level: %u", accountId, GetRemoteAddress().c_str(), name.c_str(), guid.ToString().c_str(), level); // To prevent hook failure, place hook before removing reference from DB sScriptMgr->OnPlayerDelete(guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete. @@ -737,44 +646,48 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) { std::string dump; if (PlayerDumpWriter().GetDump(guid.GetCounter(), dump)) - LOG_CHAR_DUMP(dump.c_str(), GetAccountId(), guid.GetRawValue(), name.c_str()); + LOG_CHAR_DUMP(dump.c_str(), accountId, guid.GetRawValue(), name.c_str()); } sCalendarMgr->RemoveAllPlayerEventsAndInvites(guid); Player::DeleteFromDB(guid.GetCounter(), GetAccountId(), true, false); sWorld->DeleteGlobalPlayerData(guid.GetCounter(), name); - WorldPacket data(SMSG_CHAR_DELETE, 1); - data << (uint8)CHAR_DELETE_SUCCESS; - SendPacket(&data); + SendCharDelete(CHAR_DELETE_SUCCESS); } void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData) { if (PlayerLoading() || GetPlayer() != nullptr) { - LOG_ERROR("network.opcode", "Player tries to login again, AccountId = %d", GetAccountId()); - KickPlayer("Player tries to login again"); + LOG_ERROR("network", "Player tries to login again, AccountId = %d", GetAccountId()); + KickPlayer("WorldSession::HandlePlayerLoginOpcode Another client logging in"); return; } + m_playerLoading = true; ObjectGuid playerGuid; recvData >> playerGuid; if (!playerGuid.IsPlayer() || !IsLegitCharacterForAccount(playerGuid)) { - LOG_ERROR("network.opcode", "Account (%u) can't login with that character (%s).", GetAccountId(), playerGuid.ToString().c_str()); + LOG_ERROR("network", "Account (%u) can't login with that character (%s).", GetAccountId(), playerGuid.ToString().c_str()); KickPlayer("Account can't login with this character"); return; } + auto SendCharLogin = [&](ResponseCodes result) + { + WorldPacket data(SMSG_CHARACTER_LOGIN_FAILED, 1); + data << uint8(result); + SendPacket(&data); + }; + // pussywizard: if (WorldSession* sess = sWorld->FindOfflineSessionForCharacterGUID(playerGuid.GetCounter())) if (sess->GetAccountId() != GetAccountId()) { - WorldPacket data(SMSG_CHARACTER_LOGIN_FAILED, 1); - data << (uint8)CHAR_LOGIN_DUPLICATE_CHARACTER; - SendPacket(&data); + SendCharLogin(CHAR_LOGIN_DUPLICATE_CHARACTER); return; } // pussywizard: @@ -783,9 +696,7 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData) Player* p = sess->GetPlayer(); if (!p || sess->IsKicked()) { - WorldPacket data(SMSG_CHARACTER_LOGIN_FAILED, 1); - data << (uint8)CHAR_LOGIN_DUPLICATE_CHARACTER; - SendPacket(&data); + SendCharLogin(CHAR_LOGIN_DUPLICATE_CHARACTER); return; } @@ -796,9 +707,7 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData) // pussywizard: players stay ingame no matter what (prevent abuse), but allow to turn it off to stop crashing if (!sWorld->getBoolConfig(CONFIG_ENABLE_LOGIN_AFTER_DC)) { - WorldPacket data(SMSG_CHARACTER_LOGIN_FAILED, 1); - data << (uint8)CHAR_LOGIN_DUPLICATE_CHARACTER; - SendPacket(&data); + SendCharLogin(CHAR_LOGIN_DUPLICATE_CHARACTER); return; } @@ -826,9 +735,11 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData) LOG_INFO("misc", "HandlePlayerLoginOpcode C"); break; } + Player* plMover = sess->GetPlayer()->m_mover->ToPlayer(); if (!plMover) break; + WorldPacket pkt(MSG_MOVE_TELEPORT_ACK, 20); pkt << plMover->GetPackGUID(); pkt << uint32(0); // flags @@ -838,9 +749,7 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData) } if (!p->FindMap() || !p->IsInWorld() || sess->IsKicked()) { - WorldPacket data(SMSG_CHARACTER_LOGIN_FAILED, 1); - data << (uint8)CHAR_LOGIN_DUPLICATE_CHARACTER; - SendPacket(&data); + SendCharLogin(CHAR_LOGIN_DUPLICATE_CHARACTER); return; } @@ -854,34 +763,33 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData) } } - m_playerLoading = true; - - LoginQueryHolder* holder = new LoginQueryHolder(GetAccountId(), playerGuid); + std::shared_ptr holder = std::make_shared(GetAccountId(), playerGuid); if (!holder->Initialize()) { - delete holder; // delete all unprocessed queries m_playerLoading = false; return; } - _charLoginCallback = CharacterDatabase.DelayQueryHolder((SQLQueryHolder*)holder); + AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)).AfterComplete([this](SQLQueryHolderBase const& holder) + { + HandlePlayerLoginFromDB(static_cast(holder)); + }); } -void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) +void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder) { - ObjectGuid playerGuid = holder->GetGuid(); + ObjectGuid playerGuid = holder.GetGuid(); Player* pCurrChar = new Player(this); // for send server info and strings (config) - ChatHandler chH = ChatHandler(this); + ChatHandler chH = ChatHandler(pCurrChar->GetSession()); // "GetAccountId() == db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools) if (!pCurrChar->LoadFromDB(playerGuid, holder)) { SetPlayer(nullptr); - KickPlayer("HandlePlayerLoginFromDB"); // disconnect client, player no set to session and it will not deleted or saved at kick - delete pCurrChar; // delete it manually - delete holder; // delete all unprocessed queries + KickPlayer("WorldSession::HandlePlayerLogin Player::LoadFromDB failed"); // disconnect client, player no set to session and it will not deleted or saved at kick + delete pCurrChar; // delete it manually m_playerLoading = false; return; } @@ -898,11 +806,11 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) SendPacket(&data); // load player specific part before send times - LoadAccountData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_DATA), PER_CHARACTER_CACHE_MASK); + LoadAccountData(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_DATA), PER_CHARACTER_CACHE_MASK); SendAccountDataTimes(PER_CHARACTER_CACHE_MASK); data.Initialize(SMSG_FEATURE_SYSTEM_STATUS, 2); // added in 2.2.0 - data << uint8(2); // unknown value + data << uint8(2); // 2 - COMPLAINT_ENABLED_WITH_AUTO_IGNORE data << uint8(0); // enable(1)/disable(0) voice chat interface in client SendPacket(&data); @@ -913,8 +821,6 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) // send server info if (sWorld->getIntConfig(CONFIG_ENABLE_SINFO_LOGIN) == 1) chH.PSendSysMessage("%s", GitRevision::GetFullVersion()); - - LOG_DEBUG("network.opcode", "WORLD: Sent server info"); } if (uint32 guildId = Player::GetGuildIdFromStorage(pCurrChar->GetGUID().GetCounter())) @@ -980,21 +886,20 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) pCurrChar->SendInitialPacketsAfterAddToMap(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ONLINE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ONLINE); stmt->setUInt32(0, pCurrChar->GetGUID().GetCounter()); CharacterDatabase.Execute(stmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_ONLINE); - stmt->setUInt32(0, realm.Id.Realm); - stmt->setUInt32(1, GetAccountId()); - LoginDatabase.Execute(stmt); + LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_ONLINE); + loginStmt->setUInt32(0, realm.Id.Realm); + loginStmt->setUInt32(1, GetAccountId()); + LoginDatabase.Execute(loginStmt); pCurrChar->SetInGameTime(World::GetGameTimeMS()); // announce group about member online (must be after add to player list to receive announce to self) if (Group* group = pCurrChar->GetGroup()) { - //pCurrChar->groupInfo.group->SendInit(this); // useless group->SendUpdate(); group->ResetMaxEnchantingLevel(); } @@ -1022,16 +927,16 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) sSocialMgr->SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUID(), true); // Place character in world (and load zone) before some object loading - pCurrChar->LoadCorpse(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION)); + pCurrChar->LoadCorpse(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION)); // setting Ghost+speed if dead if (pCurrChar->m_deathState != ALIVE) { // not blizz like, we must correctly save and load player instead... if (pCurrChar->getRace() == RACE_NIGHTELF) - pCurrChar->CastSpell(pCurrChar, 20584, true, 0);// auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) - pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) + pCurrChar->CastSpell(pCurrChar, 20584, true, 0); // auras SPELL_AURA_INCREASE_SPEED(+speed in wisp form), SPELL_AURA_INCREASE_SWIM_SPEED(+swim speed in wisp form), SPELL_AURA_TRANSFORM (to wisp form) + pCurrChar->CastSpell(pCurrChar, 8326, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?) pCurrChar->SetMovement(MOVE_WATER_WALK); } @@ -1066,43 +971,29 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) if (sWorld->getBoolConfig(CONFIG_START_ALL_REP)) { ReputationMgr& repMgr = pCurrChar->GetReputationMgr(); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(942), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(935), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(936), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(1011), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(970), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(967), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(989), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(932), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(934), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(1038), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(1077), 42999, false); + + auto SendFullReputation = [&repMgr](std::initializer_list factionsList) + { + for (auto const& itr : factionsList) + { + repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(itr), 42999, false); + } + }; + + SendFullReputation({ 942, 935, 936, 1011, 970, 967, 989, 932, 934, 1038, 1077 }); switch (pCurrChar->getFaction()) { case ALLIANCE: - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(72), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(47), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(69), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(930), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(730), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(978), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(54), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(946), 42999, false); + SendFullReputation({ 72, 47, 69, 930, 730, 978, 54, 946 }); break; case HORDE: - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(76), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(68), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(81), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(911), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(729), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(941), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(530), 42999, false); - repMgr.SetOneFactionReputation(sFactionStore.LookupEntry(947), 42999, false); + SendFullReputation({ 76, 68, 81, 911, 729, 941, 530, 947 }); break; default: break; } + repMgr.SendStates(); } @@ -1164,14 +1055,15 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) { bool isReferrer = pCurrChar->GetSession()->IsARecruiter(); - for (SessionMap::const_iterator itr = sWorld->GetAllSessions().begin(); itr != sWorld->GetAllSessions().end(); ++itr) + for (auto const& [accID, session] : sWorld->GetAllSessions()) { - if (!itr->second->GetRecruiterId() && !itr->second->IsARecruiter()) + if (!session->GetRecruiterId() && !session->IsARecruiter()) continue; - if ((isReferrer && pCurrChar->GetSession()->GetAccountId() == itr->second->GetRecruiterId()) || (!isReferrer && pCurrChar->GetSession()->GetRecruiterId() == itr->second->GetAccountId())) + + if ((isReferrer && pCurrChar->GetSession()->GetAccountId() == session->GetRecruiterId()) || (!isReferrer && pCurrChar->GetSession()->GetRecruiterId() == session->GetAccountId())) { - Player* rf = itr->second->GetPlayer(); - if (rf != nullptr) + Player* rf = session->GetPlayer(); + if (rf) { pCurrChar->SendUpdateToPlayer(rf); rf->SendUpdateToPlayer(pCurrChar); @@ -1187,8 +1079,6 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder* holder) pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST); sScriptMgr->OnFirstLogin(pCurrChar); } - - delete holder; } void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar) @@ -1313,7 +1203,7 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar) m_playerLoading = false; } -void WorldSession::HandlePlayerLoginToCharOutOfWorld(Player* /*pCurrChar*/) +void WorldSession::HandlePlayerLoginToCharOutOfWorld(Player* /*pCurrChar*/) { ABORT(); } @@ -1400,63 +1290,49 @@ void WorldSession::HandleShowingCloakOpcode(WorldPacket& recvData) void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) { - ObjectGuid guid; - std::string newName; + std::shared_ptr renameInfo = std::make_shared(); - recvData >> guid; - recvData >> newName; + recvData >> renameInfo->Guid + >> renameInfo->Name; // prevent character rename to invalid name - if (!normalizePlayerName(newName)) + if (!normalizePlayerName(renameInfo->Name)) { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_NAME_NO_NAME); - SendPacket(&data); + SendCharRename(CHAR_NAME_NO_NAME, renameInfo.get()); return; } - uint8 res = ObjectMgr::CheckPlayerName(newName, true); + uint8 res = ObjectMgr::CheckPlayerName(renameInfo->Name, true); if (res != CHAR_NAME_SUCCESS) { - WorldPacket data(SMSG_CHAR_RENAME, 1 + 8 + (newName.size() + 1)); - data << uint8(res); - data << guid; - data << newName; - SendPacket(&data); + SendCharRename(ResponseCodes(res), renameInfo.get()); return; } // check name limitations - if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(newName)) + if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(renameInfo->Name)) { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_NAME_RESERVED); - SendPacket(&data); + SendCharRename(CHAR_NAME_RESERVED, renameInfo.get()); return; } // Ensure that the character belongs to the current account, that rename at login is enabled // and that there is no character with the desired new name - _charRenameCallback.SetParam(newName); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_FREE_NAME); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_FREE_NAME); - - stmt->setUInt32(0, guid.GetCounter()); + stmt->setUInt32(0, renameInfo->Guid.GetCounter()); stmt->setUInt32(1, GetAccountId()); - stmt->setUInt16(2, AT_LOGIN_RENAME); - stmt->setUInt16(3, AT_LOGIN_RENAME); - stmt->setString(4, newName); + stmt->setString(2, renameInfo->Name); - _charRenameCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleCharRenameCallBack, this, renameInfo, std::placeholders::_1))); } -void WorldSession::HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult result, std::string const& newName) +void WorldSession::HandleCharRenameCallBack(std::shared_ptr renameInfo, PreparedQueryResult result) { if (!result) { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharRename(CHAR_CREATE_ERROR, renameInfo.get()); return; } @@ -1464,48 +1340,45 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult resu ObjectGuid::LowType guidLow = fields[0].GetUInt32(); std::string oldName = fields[1].GetString(); + uint16 atLoginFlags = fields[2].GetUInt16(); - ObjectGuid guid = ObjectGuid::Create(guidLow); + if (!(atLoginFlags & AT_LOGIN_RENAME)) + { + SendCharRename(CHAR_CREATE_ERROR, renameInfo.get()); + return; + } + + atLoginFlags &= ~AT_LOGIN_RENAME; // pussywizard: - if (ObjectAccessor::FindConnectedPlayer(guid) || sWorld->FindOfflineSessionForCharacterGUID(guidLow)) + if (ObjectAccessor::FindConnectedPlayer(ObjectGuid::Create(guidLow)) || sWorld->FindOfflineSessionForCharacterGUID(guidLow)) { - WorldPacket data(SMSG_CHAR_RENAME, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharRename(CHAR_CREATE_ERROR, renameInfo.get()); return; } // Update name and at_login flag in the db - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_NAME); - - stmt->setString(0, newName); - stmt->setUInt16(1, AT_LOGIN_RENAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); + stmt->setString(0, renameInfo->Name); + stmt->setUInt16(1, atLoginFlags); stmt->setUInt32(2, guidLow); - CharacterDatabase.Execute(stmt); // Removed declined name from db if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_DECLINED_NAME); - stmt->setUInt32(0, guidLow); - CharacterDatabase.Execute(stmt); } - LOG_INFO("entities.player", "Account: %d (IP: %s), Character [%s] (guid: %u) Changed name to: %s", GetAccountId(), GetRemoteAddress().c_str(), oldName.c_str(), guidLow, newName.c_str()); + LOG_INFO("entities.player.character", "Account: %d (IP: %s), Character [%s] (guid: %u) Changed name to: %s", GetAccountId(), GetRemoteAddress().c_str(), oldName.c_str(), guidLow, renameInfo->Name.c_str()); - WorldPacket data(SMSG_CHAR_RENAME, 1 + 8 + (newName.size() + 1)); - data << uint8(RESPONSE_SUCCESS); - data << guid; - data << newName; - SendPacket(&data); + SendCharRename(RESPONSE_SUCCESS, renameInfo.get()); // xinef: update global data - sWorld->UpdateGlobalNameData(guidLow, oldName, newName); - sWorld->UpdateGlobalPlayerData(guidLow, PLAYER_UPDATE_DATA_NAME, newName); + sWorld->UpdateGlobalNameData(guidLow, oldName, renameInfo->Name); + sWorld->UpdateGlobalPlayerData(guidLow, PLAYER_UPDATE_DATA_NAME, renameInfo->Name); } void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recvData) @@ -1515,36 +1388,26 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recvData) return; ObjectGuid guid; - recvData >> guid; // not accept declined names for unsupported languages std::string name; if (!sObjectMgr->GetPlayerNameByGUID(guid.GetCounter(), name)) { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(1); - data << guid; - SendPacket(&data); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); return; } std::wstring wname; if (!Utf8toWStr(name, wname)) { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(1); - data << guid; - SendPacket(&data); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); return; } if (!isCyrillicCharacter(wname[0])) // name already stored as only single alphabet using { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(1); - data << guid; - SendPacket(&data); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); return; } @@ -1555,10 +1418,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recvData) if (name2 != name) // character have different name { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(1); - data << guid; - SendPacket(&data); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); return; } @@ -1567,29 +1427,23 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recvData) recvData >> declinedname.name[i]; if (!normalizePlayerName(declinedname.name[i])) { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(1); - data << guid; - SendPacket(&data); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); return; } } if (!ObjectMgr::CheckDeclinedNames(wname, declinedname)) { - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(1); - data << guid; - SendPacket(&data); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_ERROR, guid); return; } for (int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) CharacterDatabase.EscapeString(declinedname.name[i]); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME); stmt->setUInt32(0, guid.GetCounter()); trans->Append(stmt); @@ -1603,10 +1457,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recvData) CharacterDatabase.CommitTransaction(trans); - WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); - data << uint32(0); // OK - data << guid; - SendPacket(&data); + SendSetPlayerDeclinedNamesResult(DECLINED_NAMES_RESULT_SUCCESS, guid); } void WorldSession::HandleAlterAppearance(WorldPacket& recvData) @@ -1705,21 +1556,21 @@ void WorldSession::HandleRemoveGlyph(WorldPacket& recvData) void WorldSession::HandleCharCustomize(WorldPacket& recvData) { - ObjectGuid guid; - std::string newName; + std::shared_ptr customizeInfo = std::make_shared(); - recvData >> guid; - if (!IsLegitCharacterForAccount(guid)) + recvData >> customizeInfo->Guid; + + if (!IsLegitCharacterForAccount(customizeInfo->Guid)) { - LOG_ERROR("network.opcode", "Account %u, IP: %s tried to customise character %s, but it does not belong to their account!", - GetAccountId(), GetRemoteAddress().c_str(), guid.ToString().c_str()); + LOG_ERROR("entities.player.cheat", "Account %u, IP: %s tried to customise %s, but it does not belong to their account!", + GetAccountId(), GetRemoteAddress().c_str(), customizeInfo->Guid.ToString().c_str()); recvData.rfinish(); - KickPlayer("HandleCharCustomize"); + KickPlayer("WorldSession::HandleCharCustomize Trying to customise character of another account"); return; } // pussywizard: - if (ObjectAccessor::FindConnectedPlayer(guid) || sWorld->FindOfflineSessionForCharacterGUID(guid.GetCounter())) + if (ObjectAccessor::FindConnectedPlayer(customizeInfo->Guid) || sWorld->FindOfflineSessionForCharacterGUID(customizeInfo->Guid.GetCounter())) { recvData.rfinish(); WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); @@ -1728,123 +1579,119 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) return; } - recvData >> newName; + recvData >> customizeInfo->Name + >> customizeInfo->Gender + >> customizeInfo->Skin + >> customizeInfo->HairColor + >> customizeInfo->HairStyle + >> customizeInfo->FacialHair + >> customizeInfo->Face; - uint8 gender, skin, face, hairStyle, hairColor, facialHair; - recvData >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face; + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO); + stmt->setUInt32(0, customizeInfo->Guid.GetCounter()); - // xinef: zomg! sync query - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_AT_LOGIN); - - stmt->setUInt32(0, guid.GetCounter()); - - PreparedQueryResult result = CharacterDatabase.Query(stmt); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleCharCustomizeCallback, this, customizeInfo, std::placeholders::_1))); +} +void WorldSession::HandleCharCustomizeCallback(std::shared_ptr customizeInfo, PreparedQueryResult result) +{ if (!result) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo.get()); return; } // get the players old (at this moment current) race - GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(guid.GetCounter()); + GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(customizeInfo->Guid.GetCounter()); if (!playerData) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo.get()); return; } Field* fields = result->Fetch(); - uint32 at_loginFlags = fields[0].GetUInt16(); + std::string oldName = fields[0].GetString(); + //uint8 plrRace = fields[1].GetUInt8(); + //uint8 plrClass = fields[2].GetUInt8(); + //uint8 plrGender = fields[3].GetUInt8(); + uint32 atLoginFlags = fields[4].GetUInt16(); - if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE)) + if (!(atLoginFlags & AT_LOGIN_CUSTOMIZE)) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo.get()); return; } + atLoginFlags &= ~AT_LOGIN_CUSTOMIZE; + // prevent character rename to invalid name - if (!normalizePlayerName(newName)) + if (!normalizePlayerName(customizeInfo->Name)) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_NAME_NO_NAME); - SendPacket(&data); + SendCharCustomize(CHAR_NAME_NO_NAME, customizeInfo.get()); return; } - uint8 res = ObjectMgr::CheckPlayerName(newName, true); + ResponseCodes res = static_cast(ObjectMgr::CheckPlayerName(customizeInfo->Name, true)); if (res != CHAR_NAME_SUCCESS) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(res); - SendPacket(&data); + SendCharCustomize(res, customizeInfo.get()); return; } // check name limitations - if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(newName)) + if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(customizeInfo->Name)) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_NAME_RESERVED); - SendPacket(&data); + SendCharCustomize(CHAR_NAME_RESERVED, customizeInfo.get()); return; } // character with this name already exist - if (ObjectGuid newguid = sObjectMgr->GetPlayerGUIDByName(newName)) + if (ObjectGuid newguid = sObjectMgr->GetPlayerGUIDByName(customizeInfo->Name)) { - if (newguid != guid) + if (newguid != customizeInfo->Guid) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1); - data << uint8(CHAR_CREATE_NAME_IN_USE); - SendPacket(&data); + SendCharCustomize(CHAR_CREATE_NAME_IN_USE, customizeInfo.get()); return; } } - LOG_INFO("entities.player", "Account: %d (IP: %s), Character [%s] (%s) Customized to: %s", - GetAccountId(), GetRemoteAddress().c_str(), playerData->name.c_str(), guid.ToString().c_str(), newName.c_str()); + CharacterDatabasePreparedStatement* stmt = nullptr; + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair); + ObjectGuid::LowType lowGuid = customizeInfo->Guid.GetCounter(); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); + /// Customize + Player::Customize(customizeInfo.get(), trans); - stmt->setString(0, newName); - stmt->setUInt16(1, uint16(AT_LOGIN_CUSTOMIZE)); - stmt->setUInt32(2, guid.GetCounter()); - - CharacterDatabase.Execute(stmt); - - if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) + /// Name Change and update atLogin flags { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_DECLINED_NAME); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); + stmt->setString(0, customizeInfo->Name); + stmt->setUInt16(1, atLoginFlags); + stmt->setUInt32(2, lowGuid); - stmt->setUInt32(0, guid.GetCounter()); + trans->Append(stmt); - CharacterDatabase.Execute(stmt); + if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_DECLINED_NAME); + stmt->setUInt32(0, lowGuid); + + trans->Append(stmt); + } } - // xinef: update global data - sWorld->UpdateGlobalNameData(guid.GetCounter(), playerData->name, newName); - sWorld->UpdateGlobalPlayerData(guid.GetCounter(), PLAYER_UPDATE_DATA_NAME | PLAYER_UPDATE_DATA_GENDER, newName, 0, gender); + CharacterDatabase.CommitTransaction(trans); - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1 + 8 + (newName.size() + 1) + 6); - data << uint8(RESPONSE_SUCCESS); - data << guid; - data << newName; - data << uint8(gender); - data << uint8(skin); - data << uint8(face); - data << uint8(hairStyle); - data << uint8(hairColor); - data << uint8(facialHair); - SendPacket(&data); + // xinef: update global data + sWorld->UpdateGlobalNameData(lowGuid, playerData->name, customizeInfo->Name); + sWorld->UpdateGlobalPlayerData(lowGuid, PLAYER_UPDATE_DATA_NAME | PLAYER_UPDATE_DATA_GENDER, customizeInfo->Name, 0, customizeInfo->Gender); + + SendCharCustomize(RESPONSE_SUCCESS, customizeInfo.get()); + + LOG_INFO("entities.player.character", "Account: %d (IP: %s), Character[%s] (%s) Customized to: %s", + GetAccountId(), GetRemoteAddress().c_str(), oldName.c_str(), customizeInfo->Guid.ToString().c_str(), customizeInfo->Name.c_str()); } void WorldSession::HandleEquipmentSetSave(WorldPacket& recvData) @@ -2002,263 +1849,246 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData) void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) { - ObjectGuid guid; - std::string newname; - uint8 gender, skin, face, hairStyle, hairColor, facialHair, race; - recvData >> guid; + std::shared_ptr factionChangeInfo = std::make_shared(); - if (!IsLegitCharacterForAccount(guid)) + recvData >> factionChangeInfo->Guid; + + if (!IsLegitCharacterForAccount(factionChangeInfo->Guid)) { - LOG_ERROR("network.opcode", "Account %u, IP: %s tried to factionchange character %s, but it does not belong to their account!", - GetAccountId(), GetRemoteAddress().c_str(), guid.ToString().c_str()); + LOG_ERROR("entities.player.cheat", "Account %u, IP: %s tried to factionchange character %s, but it does not belong to their account!", + GetAccountId(), GetRemoteAddress().c_str(), factionChangeInfo->Guid.ToString().c_str()); recvData.rfinish(); - KickPlayer("HandleCharFactionOrRaceChange"); + KickPlayer("WorldSession::HandleCharFactionOrRaceChange Trying to change faction of character of another account"); return; } + recvData >> factionChangeInfo->Name + >> factionChangeInfo->Gender + >> factionChangeInfo->Skin + >> factionChangeInfo->HairColor + >> factionChangeInfo->HairStyle + >> factionChangeInfo->FacialHair + >> factionChangeInfo->Face + >> factionChangeInfo->Race; + // pussywizard: - if (ObjectAccessor::FindConnectedPlayer(guid) || sWorld->FindOfflineSessionForCharacterGUID(guid.GetCounter())) + if (ObjectAccessor::FindConnectedPlayer(factionChangeInfo->Guid) || sWorld->FindOfflineSessionForCharacterGUID(factionChangeInfo->Guid.GetCounter())) { - recvData.rfinish(); - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << (uint8)CHAR_CREATE_ERROR; - SendPacket(&data); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } - recvData >> newname; - recvData >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face >> race; + factionChangeInfo->FactionChange = (recvData.GetOpcode() == CMSG_CHAR_FACTION_CHANGE); - ObjectGuid::LowType lowGuid = guid.GetCounter(); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS); + stmt->setUInt32(0, factionChangeInfo->Guid.GetCounter()); + + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleCharFactionOrRaceChangeCallback, this, factionChangeInfo, std::placeholders::_1))); +} + +void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr factionChangeInfo, PreparedQueryResult result) +{ + if (!result) + { + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); + return; + } + + ObjectGuid::LowType lowGuid = factionChangeInfo->Guid.GetCounter(); // get the players old (at this moment current) race GlobalPlayerData const* playerData = sWorld->GetGlobalPlayerData(lowGuid); if (!playerData) // pussywizard: restoring character via www spoils nameData (it's not restored so it may be null) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << (uint8)CHAR_CREATE_ERROR; - SendPacket(&data); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } - // xinef: add some safety checks - if (recvData.GetOpcode() == CMSG_CHAR_FACTION_CHANGE) - { - // if player is in a guild - if (playerData->guildId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) - { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << (uint8)CHAR_CREATE_CHARACTER_IN_GUILD; - SendPacket(&data); - return; - } - - // is arena team captain - if (sArenaTeamMgr->GetArenaTeamByCaptain(guid)) - { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << (uint8)CHAR_CREATE_CHARACTER_ARENA_LEADER; - SendPacket(&data); - return; - } - - // check mailbox - if (playerData->mailCount) - { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << (uint8)CHAR_CREATE_CHARACTER_DELETE_MAIL; - SendPacket(&data); - return; - } - - // check auctions, current packet is processed single-threaded way, so not a problem - bool has_auctions = false; - for (uint8 i = 0; i < 2; ++i) // check both neutral and faction-specific AH - { - AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(i == 0 ? 0 : (((1 << (playerData->race - 1))&RACEMASK_ALLIANCE) ? 12 : 29)); - AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin(); - AuctionHouseObject::AuctionEntryMap::const_iterator _end = auctionHouse->GetAuctionsEnd(); - for (; itr != _end; ++itr) - { - AuctionEntry* Aentry = itr->second; - if (Aentry && (Aentry->owner == guid || Aentry->bidder == guid)) - { - has_auctions = true; - break; - } - } - if (has_auctions) - break; - } - if (has_auctions) - { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << (uint8)CHAR_CREATE_ERROR; - SendPacket(&data); - return; - } - } - uint8 oldRace = playerData->race; uint8 playerClass = playerData->playerClass; uint8 level = playerData->level; - // xinef: zomg! sync query - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY); - stmt->setUInt32(0, lowGuid); - PreparedQueryResult result = CharacterDatabase.Query(stmt); - - if (!result) + if (!sObjectMgr->GetPlayerInfo(factionChangeInfo->Race, playerClass)) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } Field* fields = result->Fetch(); - uint32 at_loginFlags = fields[0].GetUInt16(); - char const* knownTitlesStr = fields[1].GetCString(); + uint32 atLoginFlags = fields[0].GetUInt16(); + std::string knownTitlesStr = fields[1].GetString(); uint32 money = fields[2].GetUInt32(); - uint32 used_loginFlag = ((recvData.GetOpcode() == CMSG_CHAR_RACE_CHANGE) ? AT_LOGIN_CHANGE_RACE : AT_LOGIN_CHANGE_FACTION); + + uint32 usedLoginFlag = (factionChangeInfo->FactionChange ? AT_LOGIN_CHANGE_FACTION : AT_LOGIN_CHANGE_RACE); + if (!(atLoginFlags & usedLoginFlag)) + { + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); + return; + } + + // xinef: add some safety checks + if (factionChangeInfo->FactionChange) + { + // if player is in a guild + if (playerData->guildId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) + { + SendCharFactionChange(CHAR_CREATE_CHARACTER_IN_GUILD, factionChangeInfo.get()); + return; + } + + // is arena team captain + if (sArenaTeamMgr->GetArenaTeamByCaptain(factionChangeInfo->Guid)) + { + SendCharFactionChange(CHAR_CREATE_CHARACTER_ARENA_LEADER, factionChangeInfo.get()); + return; + } + + // check mailbox + if (playerData->mailCount) + { + SendCharFactionChange(CHAR_CREATE_CHARACTER_DELETE_MAIL, factionChangeInfo.get()); + return; + } + + // check auctions, current packet is processed single-threaded way, so not a problem + bool has_auctions = false; + + for (uint8 i = 0; i < 2; ++i) // check both neutral and faction-specific AH + { + AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(i == 0 ? 0 : (((1 << (playerData->race - 1)) & RACEMASK_ALLIANCE) ? 12 : 29)); + + for (auto const& [auID, Aentry] : auctionHouse->GetAuctions()) + { + if (Aentry && (Aentry->owner == factionChangeInfo->Guid || Aentry->bidder == factionChangeInfo->Guid)) + { + has_auctions = true; + break; + } + } + + if (has_auctions) + break; + } + + if (has_auctions) + { + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); + return; + } + } + + TeamId newTeam = Player::TeamIdForRace(factionChangeInfo->Race); + if (factionChangeInfo->FactionChange == (Player::TeamIdForRace(oldRace) == newTeam)) + { + SendCharFactionChange(factionChangeInfo->FactionChange ? CHAR_CREATE_CHARACTER_SWAP_FACTION : CHAR_CREATE_CHARACTER_RACE_ONLY, factionChangeInfo.get()); + return; + } // xinef: check money - bool valid = Player::TeamIdForRace(oldRace) == Player::TeamIdForRace(race); - if ((level < 10 && money <= 0) || (level > 10 && level <= 30 && money <= 3000000 ) || (level > 30 && level <= 50 && money <= 10000000) || - (level > 50 && level <= 70 && money <= 50000000) || (level > 70 && money <= 200000000)) + bool valid = Player::TeamIdForRace(oldRace) == Player::TeamIdForRace(factionChangeInfo->Race); + if ((level < 10 && money <= 0) || (level > 10 && level <= 30 && money <= 3000000) || (level > 30 && level <= 50 && money <= 10000000) || + (level > 50 && level <= 70 && money <= 50000000) || (level > 70 && money <= 200000000)) valid = true; + if (!valid) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_CHARACTER_GOLD_LIMIT); - SendPacket(&data); + SendCharFactionChange(CHAR_CREATE_CHARACTER_GOLD_LIMIT, factionChangeInfo.get()); return; } // pussywizard: check titles here to prevent return while building queries const uint32 ktcount = KNOWN_TITLES_SIZE * 2; - Tokenizer tokensTitles(knownTitlesStr, ' ', ktcount); - if (recvData.GetOpcode() == CMSG_CHAR_FACTION_CHANGE) - { - if (tokensTitles.size() != ktcount) - { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); - return; - } - } + std::vector tokens = Acore::Tokenize(knownTitlesStr, ' ', false); - if (!sObjectMgr->GetPlayerInfo(race, playerClass)) + if (factionChangeInfo->FactionChange && tokens.size() != ktcount) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); - return; - } - - if (!(at_loginFlags & used_loginFlag)) - { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } if (AccountMgr::IsPlayerAccount(GetSecurity())) { uint32 raceMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK); - if ((1 << (race - 1)) & raceMaskDisabled) + if ((1 << (factionChangeInfo->Race - 1)) & raceMaskDisabled) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } } // prevent character rename to invalid name - if (!normalizePlayerName(newname)) + if (!normalizePlayerName(factionChangeInfo->Name)) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_NAME_NO_NAME); - SendPacket(&data); + SendCharFactionChange(CHAR_NAME_NO_NAME, factionChangeInfo.get()); return; } - uint8 res = ObjectMgr::CheckPlayerName(newname, true); + ResponseCodes res = static_cast(ObjectMgr::CheckPlayerName(factionChangeInfo->Name, true)); if (res != CHAR_NAME_SUCCESS) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(res); - SendPacket(&data); + SendCharFactionChange(res, factionChangeInfo.get()); return; } // check name limitations - if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(newname)) + if (AccountMgr::IsPlayerAccount(GetSecurity()) && sObjectMgr->IsReservedName(factionChangeInfo->Name)) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_NAME_RESERVED); - SendPacket(&data); + SendCharFactionChange(CHAR_NAME_RESERVED, factionChangeInfo.get()); return; } // character with this name already exist - if (ObjectGuid newguid = sObjectMgr->GetPlayerGUIDByName(newname)) + if (ObjectGuid newguid = sObjectMgr->GetPlayerGUIDByName(factionChangeInfo->Name)) { - if (newguid != guid) + if (newguid != factionChangeInfo->Guid) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_NAME_IN_USE); - SendPacket(&data); + SendCharFactionChange(CHAR_CREATE_NAME_IN_USE, factionChangeInfo.get()); return; } } - CharacterDatabase.EscapeString(newname); - Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair); - - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = nullptr; + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // resurrect the character in case he's dead - Player::OfflineResurrect(guid, trans); + Player::OfflineResurrect(factionChangeInfo->Guid, trans); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_FACTION_OR_RACE); - stmt->setString(0, newname); - stmt->setUInt8(1, race); - stmt->setUInt16(2, used_loginFlag); - stmt->setUInt32(3, lowGuid); - trans->Append(stmt); + // Name Change and update atLogin flags + { + CharacterDatabase.EscapeString(factionChangeInfo->Name); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME); - stmt->setUInt32(0, lowGuid); - trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); + stmt->setString(0, factionChangeInfo->Name); + stmt->setUInt16(1, uint16((atLoginFlags | AT_LOGIN_RESURRECT) & ~usedLoginFlag)); + stmt->setUInt32(2, lowGuid); + trans->Append(stmt); - LOG_INFO("entities.player", "Account: %d (IP: %s), Character [%s] (guid: %u) Changed Race/Faction to: %s", GetAccountId(), GetRemoteAddress().c_str(), playerData->name.c_str(), lowGuid, newname.c_str()); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME); + stmt->setUInt32(0, lowGuid); + trans->Append(stmt); + } + + // Customize + Player::Customize(factionChangeInfo.get(), trans); + + // Race Change + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_RACE); + stmt->setUInt8(0, factionChangeInfo->Race); + stmt->setUInt32(1, lowGuid); + trans->Append(stmt); + } + + LOG_INFO("entities.player.character", "Account: %d (IP: %s), Character [%s] (guid: %u) Changed Race/Faction to: %s", + GetAccountId(), GetRemoteAddress().c_str(), playerData->name.c_str(), lowGuid, factionChangeInfo->Name.c_str()); // xinef: update global data - sWorld->UpdateGlobalNameData(lowGuid, playerData->name, newname); - sWorld->UpdateGlobalPlayerData(lowGuid, PLAYER_UPDATE_DATA_NAME | PLAYER_UPDATE_DATA_RACE | PLAYER_UPDATE_DATA_GENDER, newname, 0, gender, race); + sWorld->UpdateGlobalNameData(lowGuid, playerData->name, factionChangeInfo->Name); + sWorld->UpdateGlobalPlayerData(lowGuid, PLAYER_UPDATE_DATA_NAME | PLAYER_UPDATE_DATA_RACE | PLAYER_UPDATE_DATA_GENDER, factionChangeInfo->Name, 0, factionChangeInfo->Gender, factionChangeInfo->Race); - if (oldRace != race) + if (oldRace != factionChangeInfo->Race) { - TeamId team = TEAM_ALLIANCE; - - // Search each faction is targeted - switch (race) - { - case RACE_ORC: - case RACE_TAUREN: - case RACE_UNDEAD_PLAYER: - case RACE_TROLL: - case RACE_BLOODELF: - team = TEAM_HORDE; - break; - default: - break; - } - // Switch Languages // delete all languages first stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SKILL_LANGUAGES); @@ -2270,7 +2100,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) stmt->setUInt32(0, lowGuid); // Faction specific languages - if (team == TEAM_HORDE) + if (newTeam == TEAM_HORDE) stmt->setUInt16(1, 109); else stmt->setUInt16(1, 98); @@ -2278,43 +2108,43 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) trans->Append(stmt); // Race specific languages - if (race != RACE_ORC && race != RACE_HUMAN) + if (factionChangeInfo->Race != RACE_ORC && factionChangeInfo->Race != RACE_HUMAN) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_SKILL_LANGUAGE); stmt->setUInt32(0, lowGuid); - switch (race) + switch (factionChangeInfo->Race) { - case RACE_DWARF: - stmt->setUInt16(1, 111); - break; - case RACE_DRAENEI: - stmt->setUInt16(1, 759); - break; - case RACE_GNOME: - stmt->setUInt16(1, 313); - break; - case RACE_NIGHTELF: - stmt->setUInt16(1, 113); - break; - case RACE_UNDEAD_PLAYER: - stmt->setUInt16(1, 673); - break; - case RACE_TAUREN: - stmt->setUInt16(1, 115); - break; - case RACE_TROLL: - stmt->setUInt16(1, 315); - break; - case RACE_BLOODELF: - stmt->setUInt16(1, 137); - break; + case RACE_DWARF: + stmt->setUInt16(1, 111); + break; + case RACE_DRAENEI: + stmt->setUInt16(1, 759); + break; + case RACE_GNOME: + stmt->setUInt16(1, 313); + break; + case RACE_NIGHTELF: + stmt->setUInt16(1, 113); + break; + case RACE_UNDEAD_PLAYER: + stmt->setUInt16(1, 673); + break; + case RACE_TAUREN: + stmt->setUInt16(1, 115); + break; + case RACE_TROLL: + stmt->setUInt16(1, 315); + break; + case RACE_BLOODELF: + stmt->setUInt16(1, 137); + break; } trans->Append(stmt); } - if (recvData.GetOpcode() == CMSG_CHAR_FACTION_CHANGE) + if (factionChangeInfo->FactionChange) { // Delete all Flypaths stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_TAXI_PATH); @@ -2327,43 +2157,25 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) // this doesn't seem to be 100% blizzlike... but it can't really be helped. std::ostringstream taximaskstream; uint32 numFullTaximasks = level / 7; + if (numFullTaximasks > 11) numFullTaximasks = 11; - if (team == TEAM_ALLIANCE) + + TaxiMask const& factionMask = newTeam == TEAM_HORDE ? sHordeTaxiNodesMask : sAllianceTaxiNodesMask; + for (uint8 i = 0; i < numFullTaximasks; ++i) { - if (playerClass != CLASS_DEATH_KNIGHT) - { - for (uint8 i = 0; i < numFullTaximasks; ++i) - taximaskstream << uint32(sAllianceTaxiNodesMask[i]) << ' '; - } - else - { - for (uint8 i = 0; i < numFullTaximasks; ++i) - taximaskstream << uint32(sAllianceTaxiNodesMask[i] | sDeathKnightTaxiNodesMask[i]) << ' '; - } - } - else - { - if (playerClass != CLASS_DEATH_KNIGHT) - { - for (uint8 i = 0; i < numFullTaximasks; ++i) - taximaskstream << uint32(sHordeTaxiNodesMask[i]) << ' '; - } - else - { - for (uint8 i = 0; i < numFullTaximasks; ++i) - taximaskstream << uint32(sHordeTaxiNodesMask[i] | sDeathKnightTaxiNodesMask[i]) << ' '; - } + uint8 deathKnightExtraNode = (playerClass == CLASS_DEATH_KNIGHT) ? sDeathKnightTaxiNodesMask[i] : 0; + taximaskstream << uint32(factionMask[i] | deathKnightExtraNode) << ' '; } uint32 numEmptyTaximasks = 11 - numFullTaximasks; for (uint8 i = 0; i < numEmptyTaximasks; ++i) taximaskstream << "0 "; + taximaskstream << '0'; - std::string taximask = taximaskstream.str(); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_TAXIMASK); - stmt->setString(0, taximask); + stmt->setString(0, taximaskstream.str()); stmt->setUInt32(1, lowGuid); trans->Append(stmt); } @@ -2373,7 +2185,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) { if (uint32 guildId = playerData->guildId) if (Guild* guild = sGuildMgr->GetGuildById(guildId)) - guild->DeleteMember(guid, false, false, true); + guild->DeleteMember(factionChangeInfo->Guid, false, false, true); } if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND)) @@ -2389,7 +2201,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) } // Leave Arena Teams - Player::LeaveAllArenaTeams(guid); + Player::LeaveAllArenaTeams(factionChangeInfo->Guid); // Reset homebind and position stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND); @@ -2398,80 +2210,77 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND); stmt->setUInt32(0, lowGuid); - if (team == TEAM_ALLIANCE) + + WorldLocation loc; + uint16 zoneId = 0; + + if (newTeam == TEAM_ALLIANCE) { - stmt->setUInt16(1, 0); - stmt->setUInt16(2, 1519); - stmt->setFloat (3, -8867.68f); - stmt->setFloat (4, 673.373f); - stmt->setFloat (5, 97.9034f); - Player::SavePositionInDB(0, -8867.68f, 673.373f, 97.9034f, 0.0f, 1519, guid); + loc.WorldRelocate(0, -8867.68f, 673.373f, 97.9034f, 0.0f); + zoneId = 1519; } else { - stmt->setUInt16(1, 1); - stmt->setUInt16(2, 1637); - stmt->setFloat (3, 1633.33f); - stmt->setFloat (4, -4439.11f); - stmt->setFloat (5, 15.7588f); - Player::SavePositionInDB(1, 1633.33f, -4439.11f, 15.7588f, 0.0f, 1637, guid); + loc.WorldRelocate(1, 1633.33f, -4439.11f, 15.7588f, 0.0f); + zoneId = 1637; } + + stmt->setUInt16(1, loc.GetMapId()); + stmt->setUInt16(2, zoneId); + stmt->setFloat(3, loc.GetPositionX()); + stmt->setFloat(4, loc.GetPositionY()); + stmt->setFloat(5, loc.GetPositionZ()); trans->Append(stmt); - // Achievement conversion - for (std::map::const_iterator it = sObjectMgr->FactionChangeAchievements.begin(); it != sObjectMgr->FactionChangeAchievements.end(); ++it) - { - uint32 achiev_alliance = it->first; - uint32 achiev_horde = it->second; + Player::SavePositionInDB(loc, zoneId, factionChangeInfo->Guid, trans); + // Achievement conversion + for (auto const& [achiev_alliance, achiev_horde] : sObjectMgr->FactionChangeAchievements) + { stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT); - stmt->setUInt16(0, uint16(team == TEAM_ALLIANCE ? achiev_alliance : achiev_horde)); + stmt->setUInt16(0, uint16(newTeam == TEAM_ALLIANCE ? achiev_alliance : achiev_horde)); stmt->setUInt32(1, lowGuid); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ACHIEVEMENT); - stmt->setUInt16(0, uint16(team == TEAM_ALLIANCE ? achiev_alliance : achiev_horde)); - stmt->setUInt16(1, uint16(team == TEAM_ALLIANCE ? achiev_horde : achiev_alliance)); + stmt->setUInt16(0, uint16(newTeam == TEAM_ALLIANCE ? achiev_alliance : achiev_horde)); + stmt->setUInt16(1, uint16(newTeam == TEAM_ALLIANCE ? achiev_horde : achiev_alliance)); stmt->setUInt32(2, lowGuid); trans->Append(stmt); } // Item conversion - for (std::map::const_iterator it = sObjectMgr->FactionChangeItems.begin(); it != sObjectMgr->FactionChangeItems.end(); ++it) + for (auto const& [item_alliance, item_horde] : sObjectMgr->FactionChangeItems) { - uint32 item_alliance = it->first; - uint32 item_horde = it->second; - uint32 new_entry = (team == TEAM_ALLIANCE ? item_alliance : item_horde); - uint32 old_entry = (team == TEAM_ALLIANCE ? item_horde : item_alliance); + uint32 new_entry = (newTeam == TEAM_ALLIANCE ? item_alliance : item_horde); + uint32 old_entry = (newTeam == TEAM_ALLIANCE ? item_horde : item_alliance); + if (old_entry == 45978 /*Solid Gold Coin*/ || old_entry == 2589 /*Linen Cloth*/ || old_entry == 5976 /*Guild Tabard*/) continue; stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INVENTORY_FACTION_CHANGE); stmt->setUInt32(0, new_entry); stmt->setUInt32(1, old_entry); - stmt->setUInt32(2, guid.GetCounter()); + stmt->setUInt32(2, lowGuid); trans->Append(stmt); } // Delete all current quests stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS); - stmt->setUInt32(0, guid.GetCounter()); + stmt->setUInt32(0, lowGuid); trans->Append(stmt); // Quest conversion - for (std::map::const_iterator it = sObjectMgr->FactionChangeQuests.begin(); it != sObjectMgr->FactionChangeQuests.end(); ++it) + for (auto const& [quest_alliance, quest_horde] : sObjectMgr->FactionChangeQuests) { - uint32 quest_alliance = it->first; - uint32 quest_horde = it->second; - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST); stmt->setUInt32(0, lowGuid); - stmt->setUInt32(1, (team == TEAM_ALLIANCE ? quest_alliance : quest_horde)); + stmt->setUInt32(1, (newTeam == TEAM_ALLIANCE ? quest_alliance : quest_horde)); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE); - stmt->setUInt32(0, (team == TEAM_ALLIANCE ? quest_alliance : quest_horde)); - stmt->setUInt32(1, (team == TEAM_ALLIANCE ? quest_horde : quest_alliance)); + stmt->setUInt32(0, (newTeam == TEAM_ALLIANCE ? quest_alliance : quest_horde)); + stmt->setUInt32(1, (newTeam == TEAM_ALLIANCE ? quest_horde : quest_alliance)); stmt->setUInt32(2, lowGuid); trans->Append(stmt); } @@ -2482,64 +2291,50 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) trans->Append(stmt); // Disable all old-faction specific quests + for (auto const& [questID, quest] : sObjectMgr->GetQuestTemplates()) { - ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates(); - for (ObjectMgr::QuestMap::const_iterator iter = questTemplates.begin(); iter != questTemplates.end(); ++iter) + uint32 newRaceMask = (newTeam == TEAM_ALLIANCE) ? RACEMASK_ALLIANCE : RACEMASK_HORDE; + + if (quest->GetAllowableRaces() && !(quest->GetAllowableRaces() & newRaceMask)) { - Quest const* quest = iter->second; - uint32 newRaceMask = (team == TEAM_ALLIANCE) ? RACEMASK_ALLIANCE : RACEMASK_HORDE; - if (quest->GetAllowableRaces() && !(quest->GetAllowableRaces() & newRaceMask)) - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST); - stmt->setUInt32(0, quest->GetQuestId()); - stmt->setUInt32(1, lowGuid); - trans->Append(stmt); - } + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST); + stmt->setUInt32(0, quest->GetQuestId()); + stmt->setUInt32(1, lowGuid); + trans->Append(stmt); } } // Spell conversion - for (std::map::const_iterator it = sObjectMgr->FactionChangeSpells.begin(); it != sObjectMgr->FactionChangeSpells.end(); ++it) + for (auto const& [spell_alliance, spell_horde] : sObjectMgr->FactionChangeSpells) { - uint32 spell_alliance = it->first; - uint32 spell_horde = it->second; - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_BY_SPELL); stmt->setUInt32(0, lowGuid); - stmt->setUInt32(1, (team == TEAM_ALLIANCE ? spell_alliance : spell_horde)); + stmt->setUInt32(1, (newTeam == TEAM_ALLIANCE ? spell_alliance : spell_horde)); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_SPELL_FACTION_CHANGE); - stmt->setUInt32(0, (team == TEAM_ALLIANCE ? spell_alliance : spell_horde)); - stmt->setUInt32(1, (team == TEAM_ALLIANCE ? spell_horde : spell_alliance)); + stmt->setUInt32(0, (newTeam == TEAM_ALLIANCE ? spell_alliance : spell_horde)); + stmt->setUInt32(1, (newTeam == TEAM_ALLIANCE ? spell_horde : spell_alliance)); stmt->setUInt32(2, lowGuid); trans->Append(stmt); } // Reputation conversion - for (std::map::const_iterator it = sObjectMgr->FactionChangeReputation.begin(); it != sObjectMgr->FactionChangeReputation.end(); ++it) + for (auto const& [reputation_alliance, reputation_horde] : sObjectMgr->FactionChangeReputation) { - uint32 reputation_alliance = it->first; - uint32 reputation_horde = it->second; - uint32 newReputation = (team == TEAM_ALLIANCE) ? reputation_alliance : reputation_horde; - uint32 oldReputation = (team == TEAM_ALLIANCE) ? reputation_horde : reputation_alliance; + uint32 newReputation = (newTeam == TEAM_ALLIANCE) ? reputation_alliance : reputation_horde; + uint32 oldReputation = (newTeam == TEAM_ALLIANCE) ? reputation_horde : reputation_alliance; // select old standing set in db stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_REP_BY_FACTION); stmt->setUInt32(0, oldReputation); stmt->setUInt32(1, lowGuid); + PreparedQueryResult result = CharacterDatabase.Query(stmt); - if (!result) - { - continue; // pussywizard - /*WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1); - data << uint8(CHAR_CREATE_ERROR); - SendPacket(&data); - return;*/ - } + continue; - Field* fields = result->Fetch(); + fields = result->Fetch(); int32 oldDBRep = fields[0].GetInt32(); FactionEntry const* factionEntry = sFactionStore.LookupEntry(oldReputation); @@ -2547,7 +2342,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) int32 oldBaseRep = sObjectMgr->GetBaseReputationOf(factionEntry, oldRace, playerClass); // new base reputation - int32 newBaseRep = sObjectMgr->GetBaseReputationOf(sFactionStore.LookupEntry(newReputation), race, playerClass); + int32 newBaseRep = sObjectMgr->GetBaseReputationOf(sFactionStore.LookupEntry(newReputation), factionChangeInfo->Race, playerClass); // final reputation shouldnt change int32 FinalRep = oldDBRep + oldBaseRep; @@ -2567,30 +2362,40 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) } // Title conversion - if (knownTitlesStr) + if (!knownTitlesStr.empty()) { - uint32 knownTitles[ktcount]; + std::array knownTitles; - //if (tokens.size() != ktcount) // pussywizard: checked at the beginning - // return; - - for (uint32 index = 0; index < ktcount; ++index) - knownTitles[index] = atol(tokensTitles[index]); - - for (std::map::const_iterator it = sObjectMgr->FactionChangeTitles.begin(); it != sObjectMgr->FactionChangeTitles.end(); ++it) + for (uint32 index = 0; index < knownTitles.size(); ++index) { - uint32 title_alliance = it->first; - uint32 title_horde = it->second; + Optional thisMask; + if (index < tokens.size()) + thisMask = Acore::StringTo(tokens[index]); + if (thisMask) + knownTitles[index] = *thisMask; + else + { + LOG_WARN("entities.player", "%s has invalid title data '%s' at index %u - skipped, this may result in titles being lost", + GetPlayerInfo().c_str(), (index < tokens.size()) ? std::string(tokens[index]).c_str() : "", index); + + knownTitles[index] = 0; + } + } + + for (auto const& [title_alliance, title_horde] : sObjectMgr->FactionChangeTitles) + { CharTitlesEntry const* atitleInfo = sCharTitlesStore.LookupEntry(title_alliance); CharTitlesEntry const* htitleInfo = sCharTitlesStore.LookupEntry(title_horde); + // new team - if (team == TEAM_ALLIANCE) + if (newTeam == TEAM_ALLIANCE) { uint32 bitIndex = htitleInfo->bit_index; uint32 index = bitIndex / 32; uint32 old_flag = 1 << (bitIndex % 32); uint32 new_flag = 1 << (atitleInfo->bit_index % 32); + if (knownTitles[index] & old_flag) { knownTitles[index] &= ~old_flag; @@ -2604,6 +2409,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) uint32 index = bitIndex / 32; uint32 old_flag = 1 << (bitIndex % 32); uint32 new_flag = 1 << (htitleInfo->bit_index % 32); + if (knownTitles[index] & old_flag) { knownTitles[index] &= ~old_flag; @@ -2613,8 +2419,8 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) } std::ostringstream ss; - for (uint32 index = 0; index < ktcount; ++index) - ss << knownTitles[index] << ' '; + for (uint32 mask : knownTitles) + ss << mask << ' '; stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE); stmt->setString(0, ss.str().c_str()); @@ -2632,26 +2438,78 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) CharacterDatabase.CommitTransaction(trans); - if (recvData.GetOpcode() == CMSG_CHAR_FACTION_CHANGE) - { - PreparedStatement* stmnt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); - stmnt->setUInt16(0, uint16(AT_LOGIN_CHECK_ACHIEVS)); - stmnt->setUInt32(1, lowGuid); - CharacterDatabase.Execute(stmnt); - } + LOG_DEBUG("entities.player", "%s (IP: %s) changed race from %u to %u", GetPlayerInfo().c_str(), GetRemoteAddress().c_str(), oldRace, factionChangeInfo->Race); - std::string IP_str = GetRemoteAddress(); + SendCharFactionChange(RESPONSE_SUCCESS, factionChangeInfo.get()); +} - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1 + 8 + (newname.size() + 1) + 1 + 1 + 1 + 1 + 1 + 1 + 1); - data << uint8(RESPONSE_SUCCESS); - data << guid; - data << newname; - data << uint8(gender); - data << uint8(skin); - data << uint8(face); - data << uint8(hairStyle); - data << uint8(hairColor); - data << uint8(facialHair); - data << uint8(race); +void WorldSession::SendCharCreate(ResponseCodes result) +{ + WorldPacket data(SMSG_CHAR_CREATE, 1); + data << uint8(result); + SendPacket(&data); +} + +void WorldSession::SendCharDelete(ResponseCodes result) +{ + WorldPacket data(SMSG_CHAR_DELETE, 1); + data << uint8(result); + SendPacket(&data); +} + +void WorldSession::SendCharRename(ResponseCodes result, CharacterRenameInfo const* renameInfo) +{ + WorldPacket data(SMSG_CHAR_RENAME, 1 + 8 + renameInfo->Name.size() + 1); + data << uint8(result); + if (result == RESPONSE_SUCCESS) + { + data << renameInfo->Guid; + data << renameInfo->Name; + } + SendPacket(&data); +} + +void WorldSession::SendCharFactionChange(ResponseCodes result, CharacterFactionChangeInfo const* factionChangeInfo) +{ + WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1 + 8 + factionChangeInfo->Name.size() + 1 + 7); + data << uint8(result); + if (result == RESPONSE_SUCCESS) + { + data << factionChangeInfo->Guid; + data << factionChangeInfo->Name; + data << uint8(factionChangeInfo->Gender); + data << uint8(factionChangeInfo->Skin); + data << uint8(factionChangeInfo->Face); + data << uint8(factionChangeInfo->HairStyle); + data << uint8(factionChangeInfo->HairColor); + data << uint8(factionChangeInfo->FacialHair); + data << uint8(factionChangeInfo->Race); + } + SendPacket(&data); +} + +void WorldSession::SendCharCustomize(ResponseCodes result, CharacterCustomizeInfo const* customizeInfo) +{ + WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1 + 8 + customizeInfo->Name.size() + 1 + 6); + data << uint8(result); + if (result == RESPONSE_SUCCESS) + { + data << customizeInfo->Guid; + data << customizeInfo->Name; + data << uint8(customizeInfo->Gender); + data << uint8(customizeInfo->Skin); + data << uint8(customizeInfo->Face); + data << uint8(customizeInfo->HairStyle); + data << uint8(customizeInfo->HairColor); + data << uint8(customizeInfo->FacialHair); + } + SendPacket(&data); +} + +void WorldSession::SendSetPlayerDeclinedNamesResult(DeclinedNameResult result, ObjectGuid guid) +{ + WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4 + 8); + data << uint32(result); + data << guid; SendPacket(&data); } diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index b184fb846..d7246790d 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -775,7 +775,7 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) { if (sWorld->getBoolConfig(CONFIG_ITEMDELETE_VENDOR)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RECOVERY_ITEM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RECOVERY_ITEM); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt32(1, pItem->GetEntry()); stmt->setUInt32(2, pItem->GetCount()); @@ -1308,9 +1308,9 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) return; } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GIFT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GIFT); stmt->setUInt32(0, item->GetOwnerGUID().GetCounter()); stmt->setUInt32(1, item->GetGUID().GetCounter()); stmt->setUInt32(2, item->GetEntry()); @@ -1664,7 +1664,7 @@ bool WorldSession::recoveryItem(Item* pItem) && pItem->GetTemplate()->Quality >= sWorld->getIntConfig(CONFIG_ITEMDELETE_QUALITY) && pItem->GetTemplate()->ItemLevel >= sWorld->getIntConfig(CONFIG_ITEMDELETE_ITEM_LEVEL)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RECOVERY_ITEM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RECOVERY_ITEM); stmt->setUInt32(0, pItem->GetOwnerGUID().GetCounter()); stmt->setUInt32(1, pItem->GetTemplate()->ItemId); diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 7c8185934..e79a6a2c7 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -268,7 +268,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) MailDraft draft(subject, body); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (items_count > 0 || money > 0) { @@ -393,9 +393,9 @@ void WorldSession::HandleMailReturnToSender(WorldPacket& recvData) } //we can return mail now //so firstly delete the old one - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID); stmt->setUInt32(0, mailId); trans->Append(stmt); @@ -484,7 +484,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket& recvData) uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false); if (msg == EQUIP_ERR_OK) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); m->RemoveItem(itemLowGuid); m->removedItems.push_back(itemLowGuid); @@ -570,7 +570,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket& recvData) player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK); // save money and mail to prevent cheating - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); player->SaveGoldToDB(trans); player->_SaveMail(trans); CharacterDatabase.CommitTransaction(trans); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index d07a4e600..e84119c8b 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -615,7 +615,7 @@ void WorldSession::HandleBugOpcode(WorldPacket& recv_data) LOG_DEBUG("network", "%s", type.c_str()); LOG_DEBUG("network", "%s", content.c_str()); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BUG_REPORT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BUG_REPORT); stmt->setString(0, type); stmt->setString(1, content); @@ -1138,7 +1138,7 @@ void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data) uint32 accid = player->GetSession()->GetAccountId(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_WHOIS); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_WHOIS); stmt->setUInt32(0, accid); diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index ac82d11df..47ccdf5a1 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -500,17 +500,16 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData) void WorldSession::SendStablePet(ObjectGuid guid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT); stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT); - _sendStabledPetCallback.SetParam(guid); - _sendStabledPetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::SendStablePetCallback, this, guid, std::placeholders::_1))); } -void WorldSession::SendStablePetCallback(PreparedQueryResult result, ObjectGuid guid) +void WorldSession::SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result) { if (!GetPlayer()) return; @@ -518,16 +517,12 @@ void WorldSession::SendStablePetCallback(PreparedQueryResult result, ObjectGuid LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS Send."); WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size - data << guid; - - Pet* pet = _player->GetPet(); - size_t wpos = data.wpos(); data << uint8(0); // place holder for slot show number - data << uint8(GetPlayer()->m_stableSlots); + Pet* pet = _player->GetPet(); uint8 num = 0; // counter for place holder // not let move dead pet in slot @@ -542,11 +537,11 @@ void WorldSession::SendStablePetCallback(PreparedQueryResult result, ObjectGuid } else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt8(1, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT)); - if (PreparedQueryResult _result = CharacterDatabase.AsyncQuery(stmt)) + if (PreparedQueryResult _result = CharacterDatabase.Query(stmt)) { Field* fields = _result->Fetch(); @@ -630,13 +625,13 @@ void WorldSession::HandleStablePet(WorldPacket& recvData) } } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT); stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT); - _stablePetCallback = CharacterDatabase.AsyncQuery(stmt); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1))); } void WorldSession::HandleStablePetCallback(PreparedQueryResult result) @@ -672,16 +667,11 @@ void WorldSession::HandleStablePetCallback(PreparedQueryResult result) return; } - // change pet slot directly in database - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT); stmt->setUInt8(0, freeSlot); stmt->setUInt32(1, _player->GetGUID().GetCounter()); stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT)); - trans->Append(stmt); - - CharacterDatabase.CommitTransaction(trans); + CharacterDatabase.Execute(stmt); _player->SetTemporaryUnsummonedPetNumber(0); SendStableResult(STABLE_SUCCESS_STABLE); @@ -709,18 +699,17 @@ void WorldSession::HandleUnstablePet(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt32(1, petnumber); stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT); stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT); - _unstablePetCallback.SetParam(petnumber); - _unstablePetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleUnstablePetCallback, this, petnumber, std::placeholders::_1))); } -void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32 petId) +void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result) { if (!GetPlayer()) return; @@ -772,16 +761,13 @@ void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32 SendStableResult(STABLE_ERR_STABLE); return; } - // change pet slot directly in database - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT); stmt->setUInt8(0, slot); stmt->setUInt32(1, _player->GetGUID().GetCounter()); stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT)); - trans->Append(stmt); + CharacterDatabase.Execute(stmt); - CharacterDatabase.CommitTransaction(trans); _player->SetTemporaryUnsummonedPetNumber(0); } @@ -870,16 +856,15 @@ void WorldSession::HandleStableSwapPet(WorldPacket& recvData) } // Find swapped pet slot in stable - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt32(1, petId); - _stableSwapCallback.SetParam(petId); - _stableSwapCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableSwapPetCallback, this, petId, std::placeholders::_1))); } -void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint32 petId) +void WorldSession::HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result) { if (!GetPlayer()) return; @@ -891,7 +876,6 @@ void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint3 } Field* fields = result->Fetch(); - uint32 slot = fields[0].GetUInt8(); uint32 petEntry = fields[1].GetUInt32(); @@ -919,16 +903,12 @@ void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint3 _player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED); else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber()) { - // change pet slot directly in database - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT); stmt->setUInt8(0, slot); stmt->setUInt32(1, _player->GetGUID().GetCounter()); stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT)); - trans->Append(stmt); + CharacterDatabase.Execute(stmt); - CharacterDatabase.CommitTransaction(trans); _player->SetTemporaryUnsummonedPetNumber(0); } diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index ed58076f6..55cb3940f 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -22,61 +22,75 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "QueryHolder.h" -class LoadPetFromDBQueryHolder : public SQLQueryHolder +class LoadPetFromDBQueryHolder : public CharacterDatabaseQueryHolder { -private: - const uint32 m_petNumber; - const bool m_current; - const uint32 m_diffTime; - const std::string m_actionBar; - const uint32 m_savedHealth; - const uint32 m_savedMana; - public: LoadPetFromDBQueryHolder(uint32 petNumber, bool current, uint32 diffTime, std::string&& actionBar, uint32 health, uint32 mana) - : m_petNumber(petNumber), m_current(current), m_diffTime(diffTime), m_actionBar(std::move(actionBar)), - m_savedHealth(health), m_savedMana(mana) { } + : _petNumber(petNumber), + _current(current), + _diffTime(diffTime), + _actionBar(std::move(actionBar)), + _savedHealth(health), + _savedMana(mana) { } + + uint32 GetPetNumber() const { return _petNumber; } + uint32 GetDiffTime() const { return _diffTime; } + bool GetCurrent() const { return _current; } + uint32 GetSavedHealth() const { return _savedHealth; } + uint32 GetSavedMana() const { return _savedMana; } + std::string GetActionBar() const { return _actionBar; } - uint32 GetPetNumber() const { return m_petNumber; } - uint32 GetDiffTime() const { return m_diffTime; } - bool GetCurrent() const { return m_current; } - uint32 GetSavedHealth() const { return m_savedHealth; } - uint32 GetSavedMana() const { return m_savedMana; } - std::string GetActionBar() const { return m_actionBar; } bool Initialize(); +private: + enum + { + AURAS, + SPELLS, + COOLDOWNS, + + MAX + }; + + const uint32 _petNumber; + const bool _current; + const uint32 _diffTime; + const std::string _actionBar; + const uint32 _savedHealth; + const uint32 _savedMana; }; bool LoadPetFromDBQueryHolder::Initialize() { - SetSize(MAX_PET_LOAD_QUERY); + SetSize(MAX); bool res = true; - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA); - stmt->setUInt32(0, m_petNumber); - res &= SetPreparedQuery(PET_LOAD_QUERY_LOADAURAS, stmt); + stmt->setUInt32(0, _petNumber); + res &= SetPreparedQuery(AURAS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL); - stmt->setUInt32(0, m_petNumber); - res &= SetPreparedQuery(PET_LOAD_QUERY_LOADSPELLS, stmt); + stmt->setUInt32(0, _petNumber); + res &= SetPreparedQuery(SPELLS, stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_COOLDOWN); - stmt->setUInt32(0, m_petNumber); - res &= SetPreparedQuery(PET_LOAD_QUERY_LOADSPELLCOOLDOWN, stmt); + stmt->setUInt32(0, _petNumber); + res &= SetPreparedQuery(COOLDOWNS, stmt); return res; } uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, uint8 asynchLoadType) { - if (!GetPlayer() || GetPlayer()->GetPet() || GetPlayer()->GetVehicle() || GetPlayer()->IsSpectator()) - return PET_LOAD_ERROR; - if (!result) return PET_LOAD_NO_RESULT; + if (!GetPlayer() || GetPlayer()->GetPet() || GetPlayer()->GetVehicle() || GetPlayer()->IsSpectator()) + return PET_LOAD_ERROR; + Field* fields = result->Fetch(); // Xinef: this can happen if fetch is called twice, impossibru. @@ -135,11 +149,10 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, return PET_LOAD_ERROR; } - LoadPetFromDBQueryHolder* holder = new LoadPetFromDBQueryHolder(pet_number, current, uint32(time(nullptr) - fields[14].GetUInt32()), fields[13].GetString(), savedhealth, savedmana); + std::shared_ptr holder = std::make_shared(pet_number, current, uint32(time(nullptr) - fields[14].GetUInt32()), fields[13].GetString(), savedhealth, savedmana); if (!holder->Initialize()) { delete pet; - delete holder; return PET_LOAD_ERROR; } @@ -150,7 +163,6 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, LOG_ERROR("network.opcode", "Pet (%s, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)", pet->GetGUID().ToString().c_str(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY()); delete pet; - delete holder; return PET_LOAD_ERROR; } @@ -164,7 +176,6 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, { map->AddToMap(pet->ToCreature(), true); pet->SetLoading(false); // xinef, mine - delete holder; return PET_LOAD_OK; } @@ -172,11 +183,12 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, pet->GetCharmInfo()->SetPetNumber(pet_number, pet->IsPermanentPetFor(owner)); // Show pet details tab (Shift+P) only for hunter pets, demons or undead else pet->GetCharmInfo()->SetPetNumber(pet_number, false); + pet->SetDisplayId(fields[3].GetUInt32()); pet->SetNativeDisplayId(fields[3].GetUInt32()); - uint32 petlevel = fields[4].GetUInt16(); pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); pet->SetName(fields[8].GetString()); + uint32 petlevel = fields[4].GetUInt16(); switch (pet->getPetType()) { @@ -195,7 +207,6 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); // class = warrior, gender = none, power = focus pet->SetSheath(SHEATH_STATE_MELEE); pet->SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED); - pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); // this enables popup window (pet abandon, cancel) pet->SetMaxPower(POWER_HAPPINESS, pet->GetCreatePowers(POWER_HAPPINESS)); @@ -214,9 +225,7 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, pet->InitStatsForLevel(petlevel); pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32()); - pet->SynchronizeLevelWithOwner(); - pet->SetReactState(ReactStates(fields[6].GetUInt8())); pet->SetCanModifyStats(true); @@ -226,9 +235,9 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, // PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning)) if (petSlot) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID); stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT)); stmt->setUInt32(1, owner->GetGUID().GetCounter()); stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT)); @@ -261,10 +270,12 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, // do it as early as possible! pet->InitTalentForLevel(); // set original talents points before spell loading + if (!is_temporary_summoned) pet->GetCharmInfo()->InitPetActionBar(); map->AddToMap(pet->ToCreature(), true); + if (pet->getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA)); else @@ -275,20 +286,15 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, pet->SetAsynchLoadType(asynchLoadType); - // xinef: clear any old result - if (_loadPetFromDBSecondCallback.ready()) + AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)).AfterComplete([this](SQLQueryHolderBase const& holder) { - SQLQueryHolder* param; - _loadPetFromDBSecondCallback.get(param); - delete param; - } - _loadPetFromDBSecondCallback.cancel(); + HandleLoadPetFromDBSecondCallback(static_cast(holder)); + }); - _loadPetFromDBSecondCallback = CharacterDatabase.DelayQueryHolder((SQLQueryHolder*)holder); return PET_LOAD_OK; } -void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* holder) +void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder const& holder) { if (!GetPlayer()) return; @@ -298,8 +304,8 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h if (!pet) return; - pet->_LoadAuras(holder->GetPreparedResult(PET_LOAD_QUERY_LOADAURAS), holder->GetDiffTime()); - bool current = holder->GetCurrent(); + pet->_LoadAuras(holder.GetPreparedResult(PET_LOAD_QUERY_LOADAURAS), holder.GetDiffTime()); + bool current = holder.GetCurrent(); uint32 summon_spell_id = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summon_spell_id); // CANT BE nullptr bool is_temporary_summoned = spellInfo && spellInfo->GetDuration() > 0; @@ -307,26 +313,26 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h // load action bar, if data broken will fill later by default spells. if (!is_temporary_summoned) { - pet->_LoadSpells(holder->GetPreparedResult(PET_LOAD_QUERY_LOADSPELLS)); + pet->_LoadSpells(holder.GetPreparedResult(PET_LOAD_QUERY_LOADSPELLS)); pet->InitTalentForLevel(); // re-init to check talent count - pet->_LoadSpellCooldowns(holder->GetPreparedResult(PET_LOAD_QUERY_LOADSPELLCOOLDOWN)); + pet->_LoadSpellCooldowns(holder.GetPreparedResult(PET_LOAD_QUERY_LOADSPELLCOOLDOWN)); pet->LearnPetPassives(); pet->InitLevelupSpellsForLevel(); pet->CastPetAuras(current); - - pet->GetCharmInfo()->LoadPetActionBar(holder->GetActionBar()); // action bar stored in already read string + pet->GetCharmInfo()->LoadPetActionBar(holder.GetActionBar()); // action bar stored in already read string } pet->CleanupActionBar(); // remove unknown spells from action bar after load owner->PetSpellInitialize(); owner->SendTalentsInfoData(true); + if (owner->GetGroup()) owner->SetGroupUpdateFlag(GROUP_UPDATE_PET); //set last used pet number (for use in BG's) if (owner->GetTypeId() == TYPEID_PLAYER && pet->isControlled() && !pet->isTemporarySummoned() && (pet->getPetType() == SUMMON_PET || pet->getPetType() == HUNTER_PET)) { - owner->ToPlayer()->SetLastPetNumber(holder->GetPetNumber()); + owner->ToPlayer()->SetLastPetNumber(holder.GetPetNumber()); owner->SetLastPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL)); } @@ -337,12 +343,12 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h } else { - if (!holder->GetSavedHealth() && pet->getPetType() == HUNTER_PET && pet->GetAsynchLoadType() != PET_LOAD_SUMMON_DEAD_PET) + if (!holder.GetSavedHealth() && pet->getPetType() == HUNTER_PET && pet->GetAsynchLoadType() != PET_LOAD_SUMMON_DEAD_PET) pet->setDeathState(JUST_DIED); else { - pet->SetHealth(holder->GetSavedHealth() > pet->GetMaxHealth() ? pet->GetMaxHealth() : holder->GetSavedHealth()); - pet->SetPower(POWER_MANA, holder->GetSavedMana() > pet->GetMaxPower(POWER_MANA) ? pet->GetMaxPower(POWER_MANA) : holder->GetSavedMana()); + pet->SetHealth(holder.GetSavedHealth() > pet->GetMaxHealth() ? pet->GetMaxHealth() : holder.GetSavedHealth()); + pet->SetPower(POWER_MANA, holder.GetSavedMana() > pet->GetMaxPower(POWER_MANA) ? pet->GetMaxPower(POWER_MANA) : holder.GetSavedMana()); } } @@ -357,7 +363,6 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h } pet->HandleAsynchLoadSucceed(); - return; } void WorldSession::HandleDismissCritter(WorldPacket& recvData) @@ -1185,12 +1190,12 @@ void WorldSession::HandlePetRename(WorldPacket& recvData) } } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (isdeclined) { if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME); stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber()); trans->Append(stmt); @@ -1204,7 +1209,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData) } } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_NAME); stmt->setString(0, name); stmt->setUInt32(1, _player->GetGUID().GetCounter()); stmt->setUInt32(2, pet->GetCharmInfo()->GetPetNumber()); diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index 91e4ef173..fa41c0fe8 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -185,21 +185,22 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData) Petition const* petition = sPetitionMgr->GetPetitionByOwnerWithType(_player->GetGUID(), type); CharacterDatabase.EscapeString(name); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); if (petition) { LOG_DEBUG("network", "Invalid petition: %s", petition->petitionGuid.ToString().c_str()); - trans->PAppend("DELETE FROM petition WHERE petitionguid = %u", petition->petitionGuid); - trans->PAppend("DELETE FROM petition_sign WHERE petitionguid = %u", petition->petitionGuid); + trans->PAppend("DELETE FROM petition WHERE petitionguid = %u", petition->petitionGuid.GetCounter()); + trans->PAppend("DELETE FROM petition_sign WHERE petitionguid = %u", petition->petitionGuid.GetCounter()); + // xinef: clear petition store sPetitionMgr->RemovePetition(petition->petitionGuid); } // xinef: petition pointer is invalid from now on - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt32(1, charter->GetGUID().GetCounter()); stmt->setString(2, name); @@ -359,7 +360,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket& recvData) } } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME); stmt->setString(0, newName); stmt->setUInt32(1, petitionGuid.GetCounter()); @@ -481,7 +482,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData) return; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE); stmt->setUInt32(0, petition->ownerGuid.GetCounter()); stmt->setUInt32(1, petitionGuid.GetCounter()); @@ -772,9 +773,9 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket& recvData) } } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID); stmt->setUInt32(0, petitionGuid.GetCounter()); trans->Append(stmt); diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 9504bf7e4..e22396178 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -227,20 +227,18 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED))// wrapped? { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM); - + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM); stmt->setUInt32(0, item->GetGUID().GetCounter()); - - _openWrappedItemCallback.SetFirstParam(bagIndex); - _openWrappedItemCallback.SetSecondParam(slot); - _openWrappedItemCallback.SetThirdParam(item->GetGUID().GetCounter()); - _openWrappedItemCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleOpenWrappedItemCallback, this, bagIndex, slot, item->GetGUID().GetCounter(), std::placeholders::_1))); } else + { pUser->SendLoot(item->GetGUID(), LOOT_CORPSE); + } } -void WorldSession::HandleOpenWrappedItemCallback(PreparedQueryResult result, uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID) +void WorldSession::HandleOpenWrappedItemCallback(uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID, PreparedQueryResult result) { if (!GetPlayer()) return; @@ -254,12 +252,12 @@ void WorldSession::HandleOpenWrappedItemCallback(PreparedQueryResult result, uin if (!result) { - LOG_ERROR("network.opcode", "Wrapped item %s don't have record in character_gifts table and will deleted", item->GetGUID().ToString().c_str()); + LOG_ERROR("network", "Wrapped item %s don't have record in character_gifts table and will deleted", item->GetGUID().ToString().c_str()); GetPlayer()->DestroyItem(item->GetBagSlot(), item->GetSlot(), true); return; } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); Field* fields = result->Fetch(); uint32 entry = fields[0].GetUInt32(); @@ -273,7 +271,7 @@ void WorldSession::HandleOpenWrappedItemCallback(PreparedQueryResult result, uin item->SetState(ITEM_CHANGED, GetPlayer()); GetPlayer()->SaveInventoryAndGoldToDB(trans); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT); stmt->setUInt32(0, item->GetGUID().GetCounter()); trans->Append(stmt); diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index 212f58bff..335be835b 100644 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -114,7 +114,7 @@ void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket& recv_data) GMTicketResponse response = GMTICKET_RESPONSE_UPDATE_ERROR; if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) { - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetMessage(message); ticket->SaveToDB(trans); @@ -175,7 +175,7 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data) recv_data >> mainSurvey; std::unordered_set surveyIds; - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // sub_survey1, r1, comment1, sub_survey2, r2, comment2, sub_survey3, r3, comment3, sub_survey4, r4, comment4, sub_survey5, r5, comment5, sub_survey6, r6, comment6, sub_survey7, r7, comment7, sub_survey8, r8, comment8, sub_survey9, r9, comment9, sub_survey10, r10, comment10, for (uint8 i = 0; i < 10; i++) { @@ -193,7 +193,7 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data) if (!surveyIds.insert(subSurveyId).second) continue; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY); stmt->setUInt32(0, nextSurveyID); stmt->setUInt32(1, subSurveyId); stmt->setUInt32(2, rank); @@ -204,7 +204,7 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data) std::string comment; // just a guess recv_data >> comment; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY); stmt->setUInt32(0, GetPlayer()->GetGUID().GetCounter()); stmt->setUInt32(1, nextSurveyID); stmt->setUInt32(2, mainSurvey); @@ -227,7 +227,7 @@ void WorldSession::HandleReportLag(WorldPacket& recv_data) recv_data >> y; recv_data >> z; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LAG_REPORT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LAG_REPORT); stmt->setUInt32(0, GetPlayer()->GetGUID().GetCounter()); stmt->setUInt8 (1, lagType); stmt->setUInt16(2, mapId); diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp index 248da5188..663f7b347 100644 --- a/src/server/game/Handlers/TradeHandler.cpp +++ b/src/server/game/Handlers/TradeHandler.cpp @@ -480,7 +480,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) trader->m_trade = nullptr; // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); _player->SaveInventoryAndGoldToDB(trans); trader->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); diff --git a/src/server/game/Handlers/VoiceChatHandler.cpp b/src/server/game/Handlers/VoiceChatHandler.cpp index e8ae937fd..d69b62e16 100644 --- a/src/server/game/Handlers/VoiceChatHandler.cpp +++ b/src/server/game/Handlers/VoiceChatHandler.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2005-2009 MaNGOS */ +#include "Log.h" #include "Opcodes.h" #include "WorldPacket.h" #include "WorldSession.h" diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index 1b06f92c5..e34e73b05 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -117,7 +117,7 @@ bool InstanceSaveManager::DeleteInstanceSaveIfNeeded(InstanceSave* save, bool sk // delete save from db: // character_instance is deleted when unbinding a certain player - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INSTANCE_BY_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INSTANCE_BY_INSTANCE); stmt->setUInt32(0, save->GetInstanceId()); CharacterDatabase.Execute(stmt); @@ -163,7 +163,7 @@ void InstanceSave::InsertToDB() } } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_INSTANCE_SAVE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_INSTANCE_SAVE); stmt->setUInt32(0, m_instanceid); stmt->setUInt16(1, GetMapId()); stmt->setUInt32(2, uint32(GetResetTimeForDB())); @@ -377,7 +377,7 @@ void InstanceSaveManager::LoadCharacterBinds() { if (bind.perm) // already loaded perm -> delete currently checked one from db { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt32(1, instanceId); CharacterDatabase.Execute(stmt); @@ -385,7 +385,7 @@ void InstanceSaveManager::LoadCharacterBinds() } else // override temp bind by newest one { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt32(1, bind.save->GetInstanceId()); CharacterDatabase.Execute(stmt); @@ -470,7 +470,7 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator& itr) if (pList.empty()) { // delete character_instance per id, delete instance per id - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE); stmt->setUInt32(0, itr->second->GetInstanceId()); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INSTANCE_BY_INSTANCE); @@ -487,8 +487,8 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator& itr) else { // delete character_instance per id where extended = 0, transtaction with set extended = 0, transaction is used to avoid mysql thread races - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_NOT_EXTENDED); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_NOT_EXTENDED); stmt->setUInt32(0, itr->second->GetInstanceId()); trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE_SET_NOT_EXTENDED); @@ -535,7 +535,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b ScheduleReset(time_t(next_reset - 3600), InstResetEvent(1, mapid, difficulty)); // update it in the DB - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME); stmt->setUInt32(0, next_reset); stmt->setUInt16(1, uint16(mapid)); stmt->setUInt8(2, uint8(difficulty)); @@ -591,7 +591,7 @@ InstancePlayerBind* InstanceSaveManager::PlayerBindToInstance(ObjectGuid guid, I { bind.extended = false; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE); stmt->setUInt32(0, save->GetInstanceId()); stmt->setBool(1, permanent); stmt->setUInt32(2, guid.GetCounter()); @@ -605,9 +605,9 @@ InstancePlayerBind* InstanceSaveManager::PlayerBindToInstance(ObjectGuid guid, I // pussywizard: CHANGED MY MIND! DON'T SLOW DOWN THIS QUERY! HANDLE ONLY DURING LOADING FROM DB! // example: enter instance -> bind -> update old id to new id -> exit -> delete new id // if delete by new id is executed before update, then we end up with shit in db - /*SQLTransaction trans = CharacterDatabase.BeginTransaction(); + /*CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // ensure any shit for that map+difficulty is deleted! - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_GUID_MAP_DIFF); // DELETE ci FROM character_instance ci JOIN instance i ON ci.instance = i.id WHERE ci.guid = ? AND i.map = ? AND i.difficulty = ? + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_GUID_MAP_DIFF); // DELETE ci FROM character_instance ci JOIN instance i ON ci.instance = i.id WHERE ci.guid = ? AND i.map = ? AND i.difficulty = ? stmt->setUInt32(0, guidLow); stmt->setUInt16(1, uint16(save->GetMapId())); stmt->setUInt8(2, uint8(save->GetDifficulty())); @@ -619,7 +619,7 @@ InstancePlayerBind* InstanceSaveManager::PlayerBindToInstance(ObjectGuid guid, I trans->Append(stmt); CharacterDatabase.CommitTransaction(trans);*/ - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_INSTANCE); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt32(1, save->GetInstanceId()); stmt->setBool(2, permanent); @@ -657,7 +657,7 @@ void InstanceSaveManager::PlayerUnbindInstance(ObjectGuid guid, uint32 mapid, Di { if (deleteFromDB) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID); stmt->setUInt32(0, guid.GetCounter()); stmt->setUInt32(1, itr->second.save->GetInstanceId()); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 98314a848..5c6de5e39 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -29,7 +29,7 @@ void InstanceScript::SaveToDB() if (save) save->SetInstanceData(data); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_INSTANCE_SAVE_DATA); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_INSTANCE_SAVE_DATA); stmt->setString(0, data); stmt->setUInt32(1, instance->GetInstanceId()); CharacterDatabase.Execute(stmt); @@ -399,7 +399,7 @@ void InstanceScript::SetCompletedEncountersMask(uint32 newMask, bool save) if (iSave) iSave->SetCompletedEncounterMask(completedEncounters); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_INSTANCE_SAVE_ENCOUNTERMASK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_INSTANCE_SAVE_ENCOUNTERMASK); stmt->setUInt32(0, completedEncounters); stmt->setUInt32(1, instance->GetInstanceId()); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Loot/LootItemStorage.cpp b/src/server/game/Loot/LootItemStorage.cpp index 68d57b81d..3edda5a09 100644 --- a/src/server/game/Loot/LootItemStorage.cpp +++ b/src/server/game/Loot/LootItemStorage.cpp @@ -3,6 +3,7 @@ Xinef */ #include "LootItemStorage.h" +#include "DatabaseEnv.h" #include "ObjectMgr.h" #include "PreparedStatement.h" @@ -25,7 +26,7 @@ void LootItemStorage::LoadStorageFromDB() uint32 oldMSTime = getMSTime(); lootItemStore.clear(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_ITEMS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ITEMCONTAINER_ITEMS); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) { @@ -52,9 +53,9 @@ void LootItemStorage::LoadStorageFromDB() void LootItemStorage::RemoveEntryFromDB(ObjectGuid containerGUID, uint32 itemid, uint32 count) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_SINGLE_ITEM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_SINGLE_ITEM); stmt->setUInt32(0, containerGUID.GetCounter()); stmt->setUInt32(1, itemid); stmt->setUInt32(2, count); @@ -71,8 +72,8 @@ void LootItemStorage::AddNewStoredLoot(Loot* loot, Player* /*player*/) return; } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = nullptr; + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt = nullptr; StoredLootItemList& itemList = lootItemStore[loot->containerGUID]; @@ -257,9 +258,9 @@ void LootItemStorage::RemoveStoredLoot(ObjectGuid containerGUID) { lootItemStore.erase(containerGUID); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_CONTAINER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_CONTAINER); stmt->setUInt32(0, containerGUID.GetCounter()); trans->Append(stmt); diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index 01b9c563b..2df948461 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -84,7 +84,7 @@ MailDraft& MailDraft::AddItem(Item* item) return *this; } -void MailDraft::prepareItems(Player* receiver, SQLTransaction& trans) +void MailDraft::prepareItems(Player* receiver, CharacterDatabaseTransaction trans) { if (!m_mailTemplateId || !m_mailTemplateItemsNeed) return; @@ -110,7 +110,7 @@ void MailDraft::prepareItems(Player* receiver, SQLTransaction& trans) } } -void MailDraft::deleteIncludedItems(SQLTransaction& trans, bool inDB /*= false*/ ) +void MailDraft::deleteIncludedItems(CharacterDatabaseTransaction trans, bool inDB /*= false*/ ) { for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter) { @@ -118,7 +118,7 @@ void MailDraft::deleteIncludedItems(SQLTransaction& trans, bool inDB /*= false*/ if (inDB) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); stmt->setUInt32(0, item->GetGUID().GetCounter()); trans->Append(stmt); } @@ -129,7 +129,7 @@ void MailDraft::deleteIncludedItems(SQLTransaction& trans, bool inDB /*= false*/ m_items.clear(); } -void MailDraft::SendReturnToSender(uint32 /*sender_acc*/, ObjectGuid::LowType sender_guid, ObjectGuid::LowType receiver_guid, SQLTransaction& trans) +void MailDraft::SendReturnToSender(uint32 /*sender_acc*/, ObjectGuid::LowType sender_guid, ObjectGuid::LowType receiver_guid, CharacterDatabaseTransaction trans) { Player* receiver = ObjectAccessor::FindPlayerByLowGUID(receiver_guid); @@ -154,7 +154,7 @@ void MailDraft::SendReturnToSender(uint32 /*sender_acc*/, ObjectGuid::LowType se Item* item = mailItemIter->second; item->SaveToDB(trans); // item not in inventory and can be save standalone // owner in data will set at mail receive and item extracting - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER); stmt->setUInt32(0, receiver_guid); stmt->setUInt32(1, item->GetGUID().GetCounter()); trans->Append(stmt); @@ -167,7 +167,7 @@ void MailDraft::SendReturnToSender(uint32 /*sender_acc*/, ObjectGuid::LowType se SendMailTo(trans, MailReceiver(receiver, receiver_guid), MailSender(MAIL_NORMAL, sender_guid), MAIL_CHECK_MASK_RETURNED, 0); } -void MailDraft::SendMailTo(SQLTransaction& trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay, uint32 custom_expiration, bool deleteMailItemsFromDB, bool sendMail) +void MailDraft::SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay, uint32 custom_expiration, bool deleteMailItemsFromDB, bool sendMail) { sScriptMgr->OnBeforeMailDraftSendMailTo(this, receiver, sender, checked, deliver_delay, custom_expiration, deleteMailItemsFromDB, sendMail); @@ -211,7 +211,7 @@ void MailDraft::SendMailTo(SQLTransaction& trans, MailReceiver const& receiver, // Add to DB uint8 index = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_MAIL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_MAIL); stmt->setUInt32( index, mailId); stmt->setUInt8 (++index, uint8(sender.GetMailMessageType())); stmt->setInt8 (++index, int8(sender.GetStationery())); @@ -281,7 +281,7 @@ void MailDraft::SendMailTo(SQLTransaction& trans, MailReceiver const& receiver, } else if (!m_items.empty()) { - SQLTransaction temp = SQLTransaction(nullptr); + CharacterDatabaseTransaction temp = CharacterDatabaseTransaction(nullptr); deleteIncludedItems(temp); } } diff --git a/src/server/game/Mails/Mail.h b/src/server/game/Mails/Mail.h index 88cfb5d69..d2c9d1e13 100644 --- a/src/server/game/Mails/Mail.h +++ b/src/server/game/Mails/Mail.h @@ -126,12 +126,12 @@ public: // modifiers MailDraft& AddCOD(uint32 COD) { m_COD = COD; return *this; } public: // finishers - void SendReturnToSender(uint32 sender_acc, ObjectGuid::LowType sender_guid, ObjectGuid::LowType receiver_guid, SQLTransaction& trans); - void SendMailTo(SQLTransaction& trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0, uint32 custom_expiration = 0, bool deleteMailItemsFromDB = false, bool sendMail = true); + void SendReturnToSender(uint32 sender_acc, ObjectGuid::LowType sender_guid, ObjectGuid::LowType receiver_guid, CharacterDatabaseTransaction trans); + void SendMailTo(CharacterDatabaseTransaction trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0, uint32 custom_expiration = 0, bool deleteMailItemsFromDB = false, bool sendMail = true); private: - void deleteIncludedItems(SQLTransaction& trans, bool inDB = false); - void prepareItems(Player* receiver, SQLTransaction& trans); // called from SendMailTo for generate mailTemplateBase items + void deleteIncludedItems(CharacterDatabaseTransaction trans, bool inDB = false); + void prepareItems(Player* receiver, CharacterDatabaseTransaction trans); // called from SendMailTo for generate mailTemplateBase items uint16 m_mailTemplateId; bool m_mailTemplateItemsNeed; diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 6acaa2816..6041c9c8a 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -3126,7 +3126,7 @@ void Map::SaveCreatureRespawnTime(ObjectGuid::LowType spawnId, time_t& respawnTi _creatureRespawnTimes[spawnId] = respawnTime; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CREATURE_RESPAWN); stmt->setUInt32(0, spawnId); stmt->setUInt32(1, uint32(respawnTime)); stmt->setUInt16(2, GetId()); @@ -3138,7 +3138,7 @@ void Map::RemoveCreatureRespawnTime(ObjectGuid::LowType spawnId) { _creatureRespawnTimes.erase(spawnId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); stmt->setUInt32(0, spawnId); stmt->setUInt16(1, GetId()); stmt->setUInt32(2, GetInstanceId()); @@ -3160,7 +3160,7 @@ void Map::SaveGORespawnTime(ObjectGuid::LowType spawnId, time_t& respawnTime) _goRespawnTimes[spawnId] = respawnTime; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GO_RESPAWN); stmt->setUInt32(0, spawnId); stmt->setUInt32(1, uint32(respawnTime)); stmt->setUInt16(2, GetId()); @@ -3172,7 +3172,7 @@ void Map::RemoveGORespawnTime(ObjectGuid::LowType spawnId) { _goRespawnTimes.erase(spawnId); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GO_RESPAWN); stmt->setUInt32(0, spawnId); stmt->setUInt16(1, GetId()); stmt->setUInt32(2, GetInstanceId()); @@ -3181,7 +3181,7 @@ void Map::RemoveGORespawnTime(ObjectGuid::LowType spawnId) void Map::LoadRespawnTimes() { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CREATURE_RESPAWNS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CREATURE_RESPAWNS); stmt->setUInt16(0, GetId()); stmt->setUInt32(1, GetInstanceId()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) @@ -3222,7 +3222,7 @@ void Map::DeleteRespawnTimes() void Map::DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE); stmt->setUInt16(0, mapId); stmt->setUInt32(1, instanceId); CharacterDatabase.Execute(stmt); @@ -3387,7 +3387,7 @@ Corpse* Map::ConvertCorpseToBones(ObjectGuid const ownerGuid, bool insignia /*= RemoveCorpse(corpse); // remove corpse from DB - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); corpse->DeleteFromDB(trans); CharacterDatabase.CommitTransaction(trans); @@ -3752,7 +3752,7 @@ bool Map::CheckCollisionAndGetValidCoords(const WorldObject* source, float start void Map::LoadCorpseData() { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSES); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSES); stmt->setUInt32(0, GetId()); stmt->setUInt32(1, GetInstanceId()); @@ -3788,7 +3788,7 @@ void Map::LoadCorpseData() void Map::DeleteCorpseData() { // DELETE FROM corpse WHERE mapId = ? AND instanceId = ? - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSES_FROM_MAP); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSES_FROM_MAP); stmt->setUInt32(0, GetId()); stmt->setUInt32(1, GetInstanceId()); CharacterDatabase.Execute(stmt); diff --git a/src/server/game/Misc/BanManager.cpp b/src/server/game/Misc/BanManager.cpp index fa346b3f7..f543badc4 100644 --- a/src/server/game/Misc/BanManager.cpp +++ b/src/server/game/Misc/BanManager.cpp @@ -32,10 +32,10 @@ BanReturn BanManager::BanAccount(std::string const& AccountName, std::string con return BAN_NOTFOUND; ///- Disconnect all affected players (for IP it can be several) - SQLTransaction trans = LoginDatabase.BeginTransaction(); + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); // pussywizard: check existing ban to prevent overriding by a shorter one! >_> - PreparedStatement* stmtAccountBanned = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); + LoginDatabasePreparedStatement* stmtAccountBanned = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); stmtAccountBanned->setUInt32(0, AccountID); PreparedQueryResult banresult = LoginDatabase.Query(stmtAccountBanned); @@ -43,7 +43,7 @@ BanReturn BanManager::BanAccount(std::string const& AccountName, std::string con return BAN_LONGER_EXISTS; // make sure there is only one active ban - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); stmt->setUInt32(0, AccountID); trans->Append(stmt); @@ -94,10 +94,10 @@ BanReturn BanManager::BanAccountByPlayerName(std::string const& CharacterName, s return BAN_NOTFOUND; ///- Disconnect all affected players (for IP it can be several) - SQLTransaction trans = LoginDatabase.BeginTransaction(); + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); // pussywizard: check existing ban to prevent overriding by a shorter one! >_> - PreparedStatement* stmtAccountBanned = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); + LoginDatabasePreparedStatement* stmtAccountBanned = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); stmtAccountBanned->setUInt32(0, AccountID); PreparedQueryResult banresult = LoginDatabase.Query(stmtAccountBanned); @@ -105,7 +105,7 @@ BanReturn BanManager::BanAccountByPlayerName(std::string const& CharacterName, s return BAN_LONGER_EXISTS; // make sure there is only one active ban - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); stmt->setUInt32(0, AccountID); trans->Append(stmt); @@ -156,7 +156,7 @@ BanReturn BanManager::BanIP(std::string const& IP, std::string const& Duration, uint32 DurationSecs = TimeStringToSecs(Duration); // No SQL injection with prepared statements - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_IP); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_IP); stmt->setString(0, IP); PreparedQueryResult resultAccounts = LoginDatabase.Query(stmt); @@ -184,7 +184,7 @@ BanReturn BanManager::BanIP(std::string const& IP, std::string const& Duration, return BAN_SUCCESS; ///- Disconnect all affected players (for IP it can be several) - SQLTransaction trans = LoginDatabase.BeginTransaction(); + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); do { @@ -223,7 +223,7 @@ BanReturn BanManager::BanCharacter(std::string const& CharacterName, std::string TargetGUID = target->GetGUID(); // make sure there is only one active ban - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_BAN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_BAN); stmt->setUInt32(0, TargetGUID.GetCounter()); CharacterDatabase.Execute(stmt); @@ -261,7 +261,7 @@ bool BanManager::RemoveBanAccount(std::string const& AccountName) return false; // NO SQL injection as account is uint32 - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); stmt->setUInt32(0, AccountID); LoginDatabase.Execute(stmt); @@ -276,7 +276,7 @@ bool BanManager::RemoveBanAccountByPlayerName(std::string const& CharacterName) return false; // NO SQL injection as account is uint32 - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED); stmt->setUInt32(0, AccountID); LoginDatabase.Execute(stmt); @@ -286,7 +286,7 @@ bool BanManager::RemoveBanAccountByPlayerName(std::string const& CharacterName) /// Remove a ban from an account bool BanManager::RemoveBanIP(std::string const& IP) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_IP_NOT_BANNED); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_IP_NOT_BANNED); stmt->setString(0, IP); LoginDatabase.Execute(stmt); @@ -308,7 +308,7 @@ bool BanManager::RemoveBanCharacter(std::string const& CharacterName) if (!guid) return false; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_BAN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_BAN); stmt->setUInt32(0, guid.GetCounter()); CharacterDatabase.Execute(stmt); return true; diff --git a/src/server/game/Misc/GameGraveyard.cpp b/src/server/game/Misc/GameGraveyard.cpp index 932264ce2..ad177d804 100644 --- a/src/server/game/Misc/GameGraveyard.cpp +++ b/src/server/game/Misc/GameGraveyard.cpp @@ -231,7 +231,7 @@ bool Graveyard::AddGraveyardLink(uint32 id, uint32 zoneId, TeamId teamId, bool p // add link to DB if (persist) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GRAVEYARD_ZONE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GRAVEYARD_ZONE); stmt->setUInt32(0, id); stmt->setUInt32(1, zoneId); @@ -282,7 +282,7 @@ void Graveyard::RemoveGraveyardLink(uint32 id, uint32 zoneId, TeamId teamId, boo // remove link from DB if (persist) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GRAVEYARD_ZONE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GRAVEYARD_ZONE); stmt->setUInt32(0, id); stmt->setUInt32(1, zoneId); diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index a91299744..dd4bc1035 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -101,7 +101,7 @@ void WaypointMgr::ReloadPath(uint32 id) _waypointStore.erase(itr); } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID); stmt->setUInt32(0, id); diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp index 40ea3445b..7e1ddc79a 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -152,7 +152,7 @@ bool OPvPCapturePoint::DelCreature(uint32 type) //if (Map* map = sMapMgr->FindMap(cr->GetMapId())) // map->Remove(cr, false); // delete respawn time for this creature - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); stmt->setUInt32(0, spawnId); stmt->setUInt16(1, m_PvP->GetMap()->GetId()); stmt->setUInt32(2, 0); // instance id, always 0 for world maps diff --git a/src/server/game/Pools/PoolMgr.cpp b/src/server/game/Pools/PoolMgr.cpp index a91f196f8..917e3337d 100644 --- a/src/server/game/Pools/PoolMgr.cpp +++ b/src/server/game/Pools/PoolMgr.cpp @@ -450,7 +450,7 @@ void PoolGroup::SpawnObject(ActivePoolData& spawns, uint32 limit, uint32 // load state from db if (!triggerFrom) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_POOL_QUEST_SAVE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_POOL_QUEST_SAVE); stmt->setUInt32(0, poolId); @@ -816,7 +816,7 @@ void PoolMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_QUEST_POOLS); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_QUEST_POOLS); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) @@ -956,7 +956,7 @@ void PoolMgr::LoadQuestPools() void PoolMgr::SaveQuestsToDB(bool daily, bool weekly, bool other) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); // pussywizard: mysql thread races, change only what is meant to be changed std::set deletedPools; @@ -973,7 +973,7 @@ void PoolMgr::SaveQuestsToDB(bool daily, bool weekly, bool other) if (!other && !quest->IsDaily() && !quest->IsWeekly()) continue; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_POOL_SAVE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_POOL_SAVE); stmt->setUInt32(0, itr->second.GetPoolId()); trans->Append(stmt); deletedPools.insert(itr->second.GetPoolId()); @@ -983,7 +983,7 @@ void PoolMgr::SaveQuestsToDB(bool daily, bool weekly, bool other) if (deletedPools.find(itr->second) != deletedPools.end()) if (IsSpawnedObject(itr->first)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_POOL_SAVE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_QUEST_POOL_SAVE); stmt->setUInt32(0, itr->second); stmt->setUInt32(1, itr->first); trans->Append(stmt); diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index c536bc358..e7138eae8 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -561,13 +561,13 @@ void ReputationMgr::LoadFromDB(PreparedQueryResult result) } } -void ReputationMgr::SaveToDB(SQLTransaction& trans) +void ReputationMgr::SaveToDB(CharacterDatabaseTransaction trans) { for (FactionStateList::iterator itr = _factions.begin(); itr != _factions.end(); ++itr) { if (itr->second.needSave) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_REPUTATION_BY_FACTION); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_REPUTATION_BY_FACTION); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt16(1, uint16(itr->second.ID)); trans->Append(stmt); diff --git a/src/server/game/Reputation/ReputationMgr.h b/src/server/game/Reputation/ReputationMgr.h index 73d2a65ef..798f72768 100644 --- a/src/server/game/Reputation/ReputationMgr.h +++ b/src/server/game/Reputation/ReputationMgr.h @@ -56,7 +56,7 @@ public: // constructors and glob _visibleFactionCount(0), _honoredFactionCount(0), _reveredFactionCount(0), _exaltedFactionCount(0), _sendFactionIncreased(false) {} ~ReputationMgr() {} - void SaveToDB(SQLTransaction& trans); + void SaveToDB(CharacterDatabaseTransaction trans); void LoadFromDB(PreparedQueryResult result); public: // statics static const int32 PointsInRank[MAX_REPUTATION_RANK]; diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index dac2942a1..3e3446e35 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -1682,12 +1682,12 @@ void ScriptMgr::OnCriteriaProgress(Player* player, AchievementCriteriaEntry cons FOREACH_SCRIPT(PlayerScript)->OnCriteriaProgress(player, criteria); } -void ScriptMgr::OnAchievementSave(SQLTransaction& trans, Player* player, uint16 achiId, CompletedAchievementData achiData) +void ScriptMgr::OnAchievementSave(CharacterDatabaseTransaction trans, Player* player, uint16 achiId, CompletedAchievementData achiData) { FOREACH_SCRIPT(PlayerScript)->OnAchiSave(trans, player, achiId, achiData); } -void ScriptMgr::OnCriteriaSave(SQLTransaction& trans, Player* player, uint16 critId, CriteriaProgress criteriaData) +void ScriptMgr::OnCriteriaSave(CharacterDatabaseTransaction trans, Player* player, uint16 critId, CriteriaProgress criteriaData) { FOREACH_SCRIPT(PlayerScript)->OnCriteriaSave(trans, player, critId, criteriaData); } @@ -1975,7 +1975,7 @@ void ScriptMgr::OnGroupDisband(Group* group) } // Global -void ScriptMgr::OnGlobalItemDelFromDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid) +void ScriptMgr::OnGlobalItemDelFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid) { ASSERT(trans); ASSERT(itemGuid); @@ -2364,7 +2364,7 @@ bool ScriptMgr::CanGiveMailRewardAtGiveLevel(Player* player, uint8 level) return ret; } -void ScriptMgr::OnDeleteFromDB(SQLTransaction& trans, uint32 guid) +void ScriptMgr::OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid) { FOREACH_SCRIPT(PlayerScript)->OnDeleteFromDB(trans, guid); } diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 87dfc3651..4e629635d 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -839,10 +839,10 @@ public: virtual bool OnBeforeCriteriaProgress(Player* /*player*/, AchievementCriteriaEntry const* /*criteria*/) { return true; } // Called when an Achievement is saved to DB - virtual void OnAchiSave(SQLTransaction& /*trans*/, Player* /*player*/, uint16 /*achId*/, CompletedAchievementData /*achiData*/) { } + virtual void OnAchiSave(CharacterDatabaseTransaction /*trans*/, Player* /*player*/, uint16 /*achId*/, CompletedAchievementData /*achiData*/) { } // Called when an Criteria is saved to DB - virtual void OnCriteriaSave(SQLTransaction& /*trans*/, Player* /*player*/, uint16 /*achId*/, CriteriaProgress /*criteriaData*/) { } + virtual void OnCriteriaSave(CharacterDatabaseTransaction /*trans*/, Player* /*player*/, uint16 /*achId*/, CriteriaProgress /*criteriaData*/) { } // Called when a player selects an option in a player gossip window virtual void OnGossipSelect(Player* /*player*/, uint32 /*menu_id*/, uint32 /*sender*/, uint32 /*action*/) { } @@ -947,7 +947,7 @@ public: [[nodiscard]] virtual bool CanGiveMailRewardAtGiveLevel(Player* /*player*/, uint8 /*level*/) { return true; } - virtual void OnDeleteFromDB(SQLTransaction& /*trans*/, uint32 /*guid*/) { } + virtual void OnDeleteFromDB(CharacterDatabaseTransaction /*trans*/, uint32 /*guid*/) { } [[nodiscard]] virtual bool CanRepopAtGraveyard(Player* /*player*/) { return true; } @@ -1139,7 +1139,7 @@ protected: public: // items - virtual void OnItemDelFromDB(SQLTransaction& /*trans*/, ObjectGuid::LowType /*itemGuid*/) { } + virtual void OnItemDelFromDB(CharacterDatabaseTransaction /*trans*/, ObjectGuid::LowType /*itemGuid*/) { } virtual void OnMirrorImageDisplayItem(const Item* /*item*/, uint32& /*display*/) { } // loot @@ -1606,8 +1606,8 @@ public: /* PlayerScript */ bool OnBeforeAchievementComplete(Player* player, AchievementEntry const* achievement); void OnCriteriaProgress(Player* player, AchievementCriteriaEntry const* criteria); bool OnBeforeCriteriaProgress(Player* player, AchievementCriteriaEntry const* criteria); - void OnAchievementSave(SQLTransaction& trans, Player* player, uint16 achiId, CompletedAchievementData achiData); - void OnCriteriaSave(SQLTransaction& trans, Player* player, uint16 critId, CriteriaProgress criteriaData); + void OnAchievementSave(CharacterDatabaseTransaction trans, Player* player, uint16 achiId, CompletedAchievementData achiData); + void OnCriteriaSave(CharacterDatabaseTransaction trans, Player* player, uint16 critId, CriteriaProgress criteriaData); void OnGossipSelect(Player* player, uint32 menu_id, uint32 sender, uint32 action); void OnGossipSelectCode(Player* player, uint32 menu_id, uint32 sender, uint32 action, const char* code); void OnPlayerBeingCharmed(Player* player, Unit* charmer, uint32 oldFactionId, uint32 newFactionId); @@ -1651,7 +1651,7 @@ public: /* PlayerScript */ void PetitionShowList(Player* player, Creature* creature, uint32& CharterEntry, uint32& CharterDispayID, uint32& CharterCost); void OnRewardKillRewarder(Player* player, bool isDungeon, float& rate); bool CanGiveMailRewardAtGiveLevel(Player* player, uint8 level); - void OnDeleteFromDB(SQLTransaction& trans, uint32 guid); + void OnDeleteFromDB(CharacterDatabaseTransaction trans, uint32 guid); bool CanRepopAtGraveyard(Player* player); void OnGetMaxSkillValue(Player* player, uint32 skill, int32& result, bool IsPure); bool CanAreaExploreAndOutdoor(Player* player); @@ -1731,7 +1731,7 @@ public: /* GroupScript */ void OnCreate(Group* group, Player* leader); public: /* GlobalScript */ - void OnGlobalItemDelFromDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid); + void OnGlobalItemDelFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid); void OnGlobalMirrorImageDisplayItem(const Item* item, uint32& display); void OnBeforeUpdateArenaPoints(ArenaTeam* at, std::map& ap); void OnAfterRefCount(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, uint32& maxcount, LootStore const& store); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 4fc9bf92d..01a6e1ed2 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -35,6 +35,7 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "QueryHolder.h" #include "zlib.h" #ifdef ELUNA @@ -139,8 +140,6 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 ResetTimeOutTime(false); LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); } - - InitializeQueryCallbackParameters(); } /// WorldSession destructor @@ -569,7 +568,7 @@ void WorldSession::LogoutPlayer(bool save) { if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK); stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt8(1, BG_DESERTION_TYPE_INVITE_LOGOUT); CharacterDatabase.Execute(stmt); @@ -668,7 +667,7 @@ void WorldSession::LogoutPlayer(bool save) LOG_DEBUG("network", "SESSION: Sent SMSG_LOGOUT_COMPLETE Message"); //! Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_ONLINE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_ONLINE); stmt->setUInt32(0, GetAccountId()); CharacterDatabase.Execute(stmt); } @@ -772,7 +771,7 @@ void WorldSession::SendAuthWaitQue(uint32 position) void WorldSession::LoadGlobalAccountData() { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_DATA); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_DATA); stmt->setUInt32(0, GetAccountId()); LoadAccountData(CharacterDatabase.Query(stmt), GLOBAL_CACHE_MASK); } @@ -810,7 +809,7 @@ void WorldSession::LoadAccountData(PreparedQueryResult result, uint32 mask) void WorldSession::SetAccountData(AccountDataType type, time_t tm, std::string const& data) { uint32 id = 0; - uint32 index = 0; + CharacterDatabaseStatements index; if ((1 << type) & GLOBAL_CACHE_MASK) { id = GetAccountId(); @@ -826,7 +825,7 @@ void WorldSession::SetAccountData(AccountDataType type, time_t tm, std::string c index = CHAR_REP_PLAYER_ACCOUNT_DATA; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(index); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(index); stmt->setUInt32(0, id); stmt->setUInt8(1, type); stmt->setUInt32(2, uint32(tm)); @@ -853,7 +852,7 @@ void WorldSession::LoadTutorialsData() { memset(m_Tutorials, 0, sizeof(uint32) * MAX_ACCOUNT_TUTORIAL_VALUES); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_TUTORIALS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_TUTORIALS); stmt->setUInt32(0, GetAccountId()); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) for (uint8 i = 0; i < MAX_ACCOUNT_TUTORIAL_VALUES; ++i) @@ -870,12 +869,12 @@ void WorldSession::SendTutorialsData() SendPacket(&data); } -void WorldSession::SaveTutorialsData(SQLTransaction& trans) +void WorldSession::SaveTutorialsData(CharacterDatabaseTransaction trans) { if (!m_TutorialsChanged) return; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_HAS_TUTORIALS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_HAS_TUTORIALS); stmt->setUInt32(0, GetAccountId()); bool hasTutorials = bool(CharacterDatabase.Query(stmt)); @@ -1198,181 +1197,21 @@ void WorldSession::SetPlayer(Player* player) m_GUIDLow = _player->GetGUID().GetCounter(); } -void WorldSession::InitializeQueryCallbackParameters() -{ - // Callback parameters that have pointers in them should be properly - // initialized to nullptr here. - _charCreateCallback.SetParam(nullptr); - _loadPetFromDBFirstCallback.SetFirstParam(0); - _loadPetFromDBFirstCallback.SetSecondParam(nullptr); -} - void WorldSession::ProcessQueryCallbacks() { - ProcessQueryCallbackPlayer(); - ProcessQueryCallbackPet(); - ProcessQueryCallbackLogin(); + _queryProcessor.ProcessReadyCallbacks(); + _transactionCallbacks.ProcessReadyCallbacks(); + _queryHolderProcessor.ProcessReadyCallbacks(); } -void WorldSession::ProcessQueryCallbackPlayer() +TransactionCallback& WorldSession::AddTransactionCallback(TransactionCallback&& callback) { - PreparedQueryResult result; - - //- HandleCharRenameOpcode - if (_charRenameCallback.IsReady()) - { - std::string param = _charRenameCallback.GetParam(); - _charRenameCallback.GetResult(result); - HandleChangePlayerNameOpcodeCallBack(result, param); - _charRenameCallback.FreeResult(); - } - - //- HandleOpenItemOpcode - if (_openWrappedItemCallback.IsReady()) - { - uint8 bagIndex = _openWrappedItemCallback.GetFirstParam(); - uint8 slot = _openWrappedItemCallback.GetSecondParam(); - ObjectGuid::LowType itemLowGUID = _openWrappedItemCallback.GetThirdParam(); - _openWrappedItemCallback.GetResult(result); - HandleOpenWrappedItemCallback(result, bagIndex, slot, itemLowGUID); - _openWrappedItemCallback.FreeResult(); - } - - //- Player - ActivateSpec - if (_loadActionsSwitchSpecCallback.ready()) - { - _loadActionsSwitchSpecCallback.get(result); - HandleLoadActionsSwitchSpec(result); - _loadActionsSwitchSpecCallback.cancel(); - } + return _transactionCallbacks.AddCallback(std::move(callback)); } -void WorldSession::ProcessQueryCallbackPet() +SQLQueryHolderCallback& WorldSession::AddQueryHolderCallback(SQLQueryHolderCallback&& callback) { - PreparedQueryResult result; - - //- SendStabledPet - if (_sendStabledPetCallback.IsReady()) - { - ObjectGuid param = _sendStabledPetCallback.GetParam(); - _sendStabledPetCallback.GetResult(result); - SendStablePetCallback(result, param); - _sendStabledPetCallback.FreeResult(); - return; - } - - //- HandleStablePet - if (_stablePetCallback.ready()) - { - _stablePetCallback.get(result); - HandleStablePetCallback(result); - _stablePetCallback.cancel(); - return; - } - - //- HandleUnstablePet - if (_unstablePetCallback.IsReady()) - { - uint32 param = _unstablePetCallback.GetParam(); - _unstablePetCallback.GetResult(result); - HandleUnstablePetCallback(result, param); - _unstablePetCallback.FreeResult(); - return; - } - - //- HandleStableSwapPet - if (_stableSwapCallback.IsReady()) - { - uint32 param = _stableSwapCallback.GetParam(); - _stableSwapCallback.GetResult(result); - HandleStableSwapPetCallback(result, param); - _stableSwapCallback.FreeResult(); - return; - } - - //- LoadPetFromDB first part - if (_loadPetFromDBFirstCallback.IsReady()) - { - Player* player = GetPlayer(); - if (!player) - { - if (AsynchPetSummon* info = _loadPetFromDBFirstCallback.GetSecondParam()) - delete info; - _loadPetFromDBFirstCallback.Reset(); - return; - } - // process only if player is in world (teleport crashes?) - // otherwise wait with result till he logs in - if (player->IsInWorld()) - { - uint8 asynchLoadType = _loadPetFromDBFirstCallback.GetFirstParam(); - _loadPetFromDBFirstCallback.GetResult(result); - - uint8 loadResult = HandleLoadPetFromDBFirstCallback(result, asynchLoadType); - if (loadResult != PET_LOAD_OK) - Pet::HandleAsynchLoadFailed(_loadPetFromDBFirstCallback.GetSecondParam(), player, asynchLoadType, loadResult); - - if (AsynchPetSummon* info = _loadPetFromDBFirstCallback.GetSecondParam()) - delete info; - _loadPetFromDBFirstCallback.Reset(); - } - return; - } - - //- LoadPetFromDB second part - if (_loadPetFromDBSecondCallback.ready()) - { - Player* player = GetPlayer(); - if (!player) - { - SQLQueryHolder* param; - _loadPetFromDBSecondCallback.get(param); - delete param; - _loadPetFromDBSecondCallback.cancel(); - } - else if (!player->IsInWorld()) - { - // wait - } - else - { - SQLQueryHolder* param; - _loadPetFromDBSecondCallback.get(param); - HandleLoadPetFromDBSecondCallback((LoadPetFromDBQueryHolder*)param); - delete param; - _loadPetFromDBSecondCallback.cancel(); - } - return; - } -} - -void WorldSession::ProcessQueryCallbackLogin() -{ - PreparedQueryResult result; - - //! HandleCharEnumOpcode - if (_charEnumCallback.ready()) - { - _charEnumCallback.get(result); - HandleCharEnum(result); - _charEnumCallback.cancel(); - } - - if (_charCreateCallback.IsReady()) - { - _charCreateCallback.GetResult(result); - HandleCharCreateCallback(result, _charCreateCallback.GetParam()); - // Don't call FreeResult() here, the callback handler will do that depending on the events in the callback chain - } - - //! HandlePlayerLoginOpcode - if (_charLoginCallback.ready()) - { - SQLQueryHolder* param; - _charLoginCallback.get(param); - HandlePlayerLoginFromDB((LoginQueryHolder*)param); - _charLoginCallback.cancel(); - } + return _queryHolderProcessor.AddCallback(std::move(callback)); } void WorldSession::InitWarden(SessionKey const& k, std::string const& os) diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 239dac08f..06b3b356a 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -112,6 +112,12 @@ enum ChatRestrictionType ERR_YELL_RESTRICTED = 3 }; +enum DeclinedNameResult +{ + DECLINED_NAMES_RESULT_SUCCESS = 0, + DECLINED_NAMES_RESULT_ERROR = 1 +}; + enum CharterTypes { GUILD_CHARTER_TYPE = 9, @@ -165,29 +171,53 @@ class CharacterCreateInfo friend class Player; protected: - CharacterCreateInfo(std::string name, uint8 race, uint8 cclass, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId, - WorldPacket& data) : Name(std::move(name)), Race(race), Class(cclass), Gender(gender), Skin(skin), Face(face), HairStyle(hairStyle), HairColor(hairColor), FacialHair(facialHair), - OutfitId(outfitId), Data(data), CharCount(0) - {} - /// User specified variables std::string Name; - uint8 Race; - uint8 Class; - uint8 Gender; - uint8 Skin; - uint8 Face; - uint8 HairStyle; - uint8 HairColor; - uint8 FacialHair; - uint8 OutfitId; - WorldPacket Data; + uint8 Race = 0; + uint8 Class = 0; + uint8 Gender = GENDER_NONE; + uint8 Skin = 0; + uint8 Face = 0; + uint8 HairStyle = 0; + uint8 HairColor = 0; + uint8 FacialHair = 0; + uint8 OutfitId = 0; /// Server side data - uint8 CharCount; + uint8 CharCount = 0; +}; -private: - virtual ~CharacterCreateInfo() = default; +struct CharacterRenameInfo +{ + friend class WorldSession; + +protected: + ObjectGuid Guid; + std::string Name; +}; + +struct CharacterCustomizeInfo : public CharacterRenameInfo +{ + friend class Player; + friend class WorldSession; + +protected: + uint8 Gender = GENDER_NONE; + uint8 Skin = 0; + uint8 Face = 0; + uint8 HairStyle = 0; + uint8 HairColor = 0; + uint8 FacialHair = 0; +}; + +struct CharacterFactionChangeInfo : public CharacterCustomizeInfo +{ + friend class Player; + friend class WorldSession; + +protected: + uint8 Race = 0; + bool FactionChange = false; }; struct PacketCounter @@ -303,7 +333,7 @@ public: // Pet void SendPetNameQuery(ObjectGuid guid, uint32 petnumber); void SendStablePet(ObjectGuid guid); - void SendStablePetCallback(PreparedQueryResult result, ObjectGuid guid); + void SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result); void SendStableResult(uint8 guid); bool CheckStableMaster(ObjectGuid guid); @@ -316,7 +346,7 @@ public: void LoadTutorialsData(); void SendTutorialsData(); - void SaveTutorialsData(SQLTransaction& trans); + void SaveTutorialsData(CharacterDatabaseTransaction trans); uint32 GetTutorialInt(uint8 index) const { return m_Tutorials[index]; } void SetTutorialInt(uint8 index, uint32 value) { @@ -403,13 +433,20 @@ public: // opcodes handlers void HandleCharEnumOpcode(WorldPacket& recvPacket); void HandleCharDeleteOpcode(WorldPacket& recvPacket); void HandleCharCreateOpcode(WorldPacket& recvPacket); - void HandleCharCreateCallback(PreparedQueryResult result, CharacterCreateInfo* createInfo); void HandlePlayerLoginOpcode(WorldPacket& recvPacket); void HandleCharEnum(PreparedQueryResult result); - void HandlePlayerLoginFromDB(LoginQueryHolder* holder); + void HandlePlayerLoginFromDB(LoginQueryHolder const& holder); void HandlePlayerLoginToCharInWorld(Player* pCurrChar); void HandlePlayerLoginToCharOutOfWorld(Player* pCurrChar); void HandleCharFactionOrRaceChange(WorldPacket& recvData); + void HandleCharFactionOrRaceChangeCallback(std::shared_ptr factionChangeInfo, PreparedQueryResult result); + + void SendCharCreate(ResponseCodes result); + void SendCharDelete(ResponseCodes result); + void SendCharRename(ResponseCodes result, CharacterRenameInfo const* renameInfo); + void SendCharCustomize(ResponseCodes result, CharacterCustomizeInfo const* customizeInfo); + void SendCharFactionChange(ResponseCodes result, CharacterFactionChangeInfo const* factionChangeInfo); + void SendSetPlayerDeclinedNamesResult(DeclinedNameResult result, ObjectGuid guid); // played time void HandlePlayedTime(WorldPacket& recvPacket); @@ -592,16 +629,16 @@ public: // opcodes handlers void HandleStablePet(WorldPacket& recvPacket); void HandleStablePetCallback(PreparedQueryResult result); void HandleUnstablePet(WorldPacket& recvPacket); - void HandleUnstablePetCallback(PreparedQueryResult result, uint32 petId); + void HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result); void HandleBuyStableSlot(WorldPacket& recvPacket); void HandleStableRevivePet(WorldPacket& recvPacket); void HandleStableSwapPet(WorldPacket& recvPacket); - void HandleStableSwapPetCallback(PreparedQueryResult result, uint32 petId); - void HandleOpenWrappedItemCallback(PreparedQueryResult result, uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID); + void HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result); + void HandleOpenWrappedItemCallback(uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID, PreparedQueryResult result); void HandleLoadActionsSwitchSpec(PreparedQueryResult result); void HandleCharacterAuraFrozen(PreparedQueryResult result); uint8 HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, uint8 asynchLoadType); - void HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* holder); + void HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder const& holder); void HandleDuelAcceptedOpcode(WorldPacket& recvPacket); void HandleDuelCancelledOpcode(WorldPacket& recvPacket); @@ -753,7 +790,7 @@ public: // opcodes handlers void HandleSetActionBarToggles(WorldPacket& recvData); void HandleCharRenameOpcode(WorldPacket& recvData); - void HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult result, std::string const& newName); + void HandleCharRenameCallBack(std::shared_ptr renameInfo, PreparedQueryResult result); void HandleSetPlayerDeclinedNames(WorldPacket& recvData); void HandleTotemDestroyed(WorldPacket& recvData); @@ -903,6 +940,7 @@ public: // opcodes handlers void HandleAlterAppearance(WorldPacket& recvData); void HandleRemoveGlyph(WorldPacket& recvData); void HandleCharCustomize(WorldPacket& recvData); + void HandleCharCustomizeCallback(std::shared_ptr customizeInfo, PreparedQueryResult result); void HandleQueryInspectAchievements(WorldPacket& recvData); void HandleEquipmentSetSave(WorldPacket& recvData); void HandleEquipmentSetDelete(WorldPacket& recvData); @@ -915,9 +953,6 @@ public: // opcodes handlers void HandleEnterPlayerVehicle(WorldPacket& data); void HandleUpdateProjectilePosition(WorldPacket& recvPacket); - // _loadPetFromDBFirstCallback helpers - //QueryCallback GetLoadPetFromDBFirstCallback() { return _loadPetFromDBFirstCallback; } - uint32 _lastAuctionListItemsMSTime; uint32 _lastAuctionListOwnerItemsMSTime; @@ -931,28 +966,20 @@ public: // opcodes handlers bool GetShouldSetOfflineInDB() const { return _shouldSetOfflineInDB; } bool IsSocketClosed() const; - /*** - CALLBACKS - ***/ + /* + * CALLBACKS + */ + + QueryCallbackProcessor& GetQueryProcessor() { return _queryProcessor; } + TransactionCallback& AddTransactionCallback(TransactionCallback&& callback); + SQLQueryHolderCallback& AddQueryHolderCallback(SQLQueryHolderCallback&& callback); + private: - void InitializeQueryCallbackParameters(); void ProcessQueryCallbacks(); - void ProcessQueryCallbackPlayer(); - void ProcessQueryCallbackPet(); - void ProcessQueryCallbackLogin(); - PreparedQueryResultFuture _charEnumCallback; - PreparedQueryResultFuture _stablePetCallback; - QueryCallback _charRenameCallback; - QueryCallback _unstablePetCallback; - QueryCallback _stableSwapCallback; - QueryCallback _sendStabledPetCallback; - QueryCallback _charCreateCallback; - - QueryResultHolderFuture _charLoginCallback; - - QueryResultHolderFuture _loadPetFromDBSecondCallback; - QueryCallback_3 _openWrappedItemCallback; + QueryCallbackProcessor _queryProcessor; + AsyncCallbackProcessor _transactionCallbacks; + AsyncCallbackProcessor _queryHolderProcessor; friend class World; protected: @@ -984,15 +1011,6 @@ protected: DosProtection& operator=(DosProtection const& right) = delete; } AntiDOS; -public: - // xinef: those must be public, requires calls out of worldsession :( - QueryCallback_2 _loadPetFromDBFirstCallback; - PreparedQueryResultFuture _loadActionsSwitchSpecCallback; - PreparedQueryResultFuture _CharacterAuraFrozenCallback; - - /*** - END OF CALLBACKS - ***/ private: // private trade methods void moveItems(Item* myItems[], Item* hisItems[]); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index d67692682..9c85ab374 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -441,7 +441,7 @@ void DeleteSpellFromAllPlayers(uint32 spellId) CharacterDatabaseStatements stmts[2] = {CHAR_DEL_INVALID_SPELL_SPELLS, CHAR_DEL_INVALID_SPELL_TALENTS}; for (uint8 i = 0; i < 2; i++) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmts[i]); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(stmts[i]); stmt->setUInt32(0, spellId); CharacterDatabase.Execute(stmt); } diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp index d96fa61db..294b61dc7 100644 --- a/src/server/game/Texts/CreatureTextMgr.cpp +++ b/src/server/game/Texts/CreatureTextMgr.cpp @@ -76,7 +76,7 @@ void CreatureTextMgr::LoadCreatureTexts() mTextMap.clear(); // for reload case mTextRepeatMap.clear(); //reset all currently used temp texts - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEXT); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEXT); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index 3b51940d9..311764c81 100644 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -62,12 +62,12 @@ bool GmTicket::LoadFromDB(Field* fields) return true; } -void GmTicket::SaveToDB(SQLTransaction& trans) const +void GmTicket::SaveToDB(CharacterDatabaseTransaction trans) const { // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // id, type, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy uint8 index = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_TICKET); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_TICKET); stmt->setUInt32( index, _id); stmt->setUInt8 (++index, uint8(_type)); stmt->setUInt32(++index, _playerGuid.GetCounter()); @@ -94,7 +94,7 @@ void GmTicket::SaveToDB(SQLTransaction& trans) const void GmTicket::DeleteFromDB() { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_TICKET); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GM_TICKET); stmt->setUInt32(0, _id); CharacterDatabase.Execute(stmt); } @@ -272,7 +272,7 @@ void TicketMgr::ResetTickets() _lastTicketId = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_TICKETS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GM_TICKETS); CharacterDatabase.Execute(stmt); } @@ -288,7 +288,7 @@ void TicketMgr::LoadTickets() _lastTicketId = 0; _openTicketCount = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_TICKETS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GM_TICKETS); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) { @@ -341,7 +341,7 @@ void TicketMgr::AddTicket(GmTicket* ticket) _ticketList[ticket->GetId()] = ticket; if (!ticket->IsClosed()) ++_openTicketCount; - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SaveToDB(trans); } @@ -349,7 +349,7 @@ void TicketMgr::CloseTicket(uint32 ticketId, ObjectGuid source) { if (GmTicket* ticket = GetTicket(ticketId)) { - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetClosedBy(source); if (source) --_openTicketCount; @@ -371,7 +371,7 @@ void TicketMgr::ResolveAndCloseTicket(uint32 ticketId, ObjectGuid source) { if (GmTicket* ticket = GetTicket(ticketId)) { - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetClosedBy(source); ticket->SetResolvedBy(source); if (source) diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h index d12439bb2..9085702ab 100644 --- a/src/server/game/Tickets/TicketMgr.h +++ b/src/server/game/Tickets/TicketMgr.h @@ -130,7 +130,7 @@ public: void AppendResponse(std::string const& response) { _response += response; } bool LoadFromDB(Field* fields); - void SaveToDB(SQLTransaction& trans) const; + void SaveToDB(CharacterDatabaseTransaction trans) const; void DeleteFromDB(); void WritePacket(WorldPacket& data) const; diff --git a/src/server/game/Tools/CharacterDatabaseCleaner.cpp b/src/server/game/Tools/CharacterDatabaseCleaner.cpp index e99c0ee86..bc3fb95d9 100644 --- a/src/server/game/Tools/CharacterDatabaseCleaner.cpp +++ b/src/server/game/Tools/CharacterDatabaseCleaner.cpp @@ -8,6 +8,7 @@ #include "Common.h" #include "Database/DatabaseEnv.h" #include "DBCStores.h" +#include "Log.h" #include "SpellMgr.h" #include "World.h" diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index a065dc7eb..62ba79dbe 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -99,7 +99,7 @@ bool findnth(std::string& str, int n, std::string::size_type& s, std::string::si std::string gettablename(std::string& str) { std::string::size_type s = 13; - std::string::size_type e = str.find(_TABLE_SIM_, s); + std::string::size_type e = str.find("`", s); if (e == std::string::npos) return ""; @@ -187,7 +187,7 @@ std::string CreateDumpString(char const* tableName, QueryResult result) { if (!tableName || !result) return ""; std::ostringstream ss; - ss << "INSERT INTO " << _TABLE_SIM_ << tableName << _TABLE_SIM_ << " VALUES ("; + ss << "INSERT INTO `" << tableName << "` VALUES ("; Field* fields = result->Fetch(); for (uint32 i = 0; i < result->GetFieldCount(); ++i) { @@ -416,7 +416,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s bool incHighest = true; if (guid != 0 && guid < sObjectMgr->GetGenerator().GetNextAfterMaxUsed()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_GUID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_GUID); stmt->setUInt32(0, guid); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -434,7 +434,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s if (ObjectMgr::CheckPlayerName(name, true) == CHAR_NAME_SUCCESS) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); stmt->setString(0, name); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -464,7 +464,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s uint8 playerClass = 0; uint8 level = 1; - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); while (!feof(fin)) { if (!fgets(buf, 32000, fin)) @@ -544,7 +544,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s // check if the original name already exists name = getnth(line, 3); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); stmt->setString(0, name); PreparedQueryResult result = CharacterDatabase.Query(stmt); diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index b562066a9..5ee43f23e 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -5,7 +5,7 @@ #ifndef AZEROTHCORE_IWORLD_H #define AZEROTHCORE_IWORLD_H -#include "Callback.h" +#include "AsyncCallbackProcessor.h" #include "Common.h" #include "ObjectGuid.h" #include "QueryResult.h" diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 46855ff04..d0b6ac199 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2117,7 +2117,7 @@ void World::LoadAutobroadcasts() m_AutobroadcastsWeights.clear(); uint32 realmId = sConfigMgr->GetOption("RealmID", 0); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST); stmt->setInt32(0, realmId); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -2176,7 +2176,7 @@ void World::Update(uint32 diff) m_timers[WUPDATE_5_SECS].Reset(); // moved here from HandleCharEnumOpcode - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_BANS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_BANS); CharacterDatabase.Execute(stmt); // copy players hashmapholder to avoid mutex @@ -2250,7 +2250,7 @@ void World::Update(uint32 diff) { m_timers[WUPDATE_CLEANDB].Reset(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_OLD_LOGS); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_OLD_LOGS); stmt->setUInt32(0, sWorld->getIntConfig(CONFIG_LOGDB_CLEARTIME)); stmt->setUInt32(1, uint32(time(0))); @@ -2291,7 +2291,7 @@ void World::Update(uint32 diff) m_timers[WUPDATE_UPTIME].Reset(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_UPTIME_PLAYERS); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_UPTIME_PLAYERS); stmt->setUInt32(0, tmpDiff); stmt->setUInt16(1, uint16(maxOnlinePlayers)); @@ -2804,11 +2804,9 @@ void World::SendAutoBroadcast() void World::UpdateRealmCharCount(uint32 accountId) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_COUNT); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_COUNT); stmt->setUInt32(0, accountId); - stmt->setUInt32(1, accountId); - PreparedQueryResultFuture result = CharacterDatabase.AsyncQuery(stmt); - m_realmCharCallbacks.insert(result); + _queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&World::_UpdateRealmCharCount, this, std::placeholders::_1))); } void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount) @@ -2819,9 +2817,9 @@ void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount) uint32 accountId = fields[0].GetUInt32(); uint8 charCount = uint8(fields[1].GetUInt64()); - SQLTransaction trans = LoginDatabase.BeginTransaction(); + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); stmt->setUInt32(0, accountId); stmt->setUInt32(1, realm.Id.Realm); trans->Append(stmt); @@ -2940,7 +2938,7 @@ void World::InitGuildResetTime() void World::ResetDailyQuests() { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY); CharacterDatabase.Execute(stmt); for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) @@ -2956,7 +2954,7 @@ void World::ResetDailyQuests() void World::LoadDBAllowedSecurityLevel() { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL); stmt->setInt32(0, int32(realm.Id.Realm)); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -2975,7 +2973,7 @@ void World::SetPlayerSecurityLimit(AccountTypes _sec) void World::ResetWeeklyQuests() { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY); CharacterDatabase.Execute(stmt); for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) @@ -2993,7 +2991,7 @@ void World::ResetMonthlyQuests() { LOG_INFO("server.worldserver", "Monthly quests reset for all characters."); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY); CharacterDatabase.Execute(stmt); for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) @@ -3006,7 +3004,7 @@ void World::ResetMonthlyQuests() void World::ResetEventSeasonalQuests(uint16 event_id) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL); stmt->setUInt16(0, event_id); CharacterDatabase.Execute(stmt); @@ -3019,7 +3017,7 @@ void World::ResetRandomBG() { LOG_DEBUG("server.worldserver", "Random BG status reset for all characters."); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_BATTLEGROUND_RANDOM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_BATTLEGROUND_RANDOM); CharacterDatabase.Execute(stmt); for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr) @@ -3154,7 +3152,7 @@ void World::setWorldState(uint32 index, uint64 value) WorldStatesMap::const_iterator it = m_worldstates.find(index); if (it != m_worldstates.end()) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_WORLDSTATE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_WORLDSTATE); stmt->setUInt32(0, uint32(value)); stmt->setUInt32(1, index); @@ -3163,7 +3161,7 @@ void World::setWorldState(uint32 index, uint64 value) } else { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_WORLDSTATE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_WORLDSTATE); stmt->setUInt32(0, index); stmt->setUInt32(1, uint32(value)); @@ -3181,22 +3179,7 @@ uint64 World::getWorldState(uint32 index) const void World::ProcessQueryCallbacks() { - PreparedQueryResult result; - - while (!m_realmCharCallbacks.is_empty()) - { - ACE_Future lResult; - ACE_Time_Value timeout = ACE_Time_Value::zero; - if (m_realmCharCallbacks.next_readable(lResult, &timeout) != 1) - break; - - if (lResult.ready()) - { - lResult.get(result); - _UpdateRealmCharCount(result); - lResult.cancel(); - } - } + _queryProcessor.ProcessReadyCallbacks(); } void World::LoadGlobalPlayerDataStore() @@ -3364,7 +3347,7 @@ GlobalPlayerData const* World::GetGlobalPlayerData(ObjectGuid::LowType guid) con return &itr->second; // Player is not in the global storage, try to get it from the Database - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_DATA_BY_GUID); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_DATA_BY_GUID); stmt->setUInt32(0, guid); @@ -3412,7 +3395,7 @@ ObjectGuid World::GetGlobalPlayerGUID(std::string const& name) const return ObjectGuid::Create(itr->second); // Player is not in the global storage, try to get it from the Database - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_DATA_BY_NAME); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_DATA_BY_NAME); stmt->setString(0, name); @@ -3467,3 +3450,15 @@ bool World::IsFFAPvPRealm() const { return getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; } + +uint32 World::GetNextWhoListUpdateDelaySecs() +{ + if (m_timers[WUPDATE_5_SECS].Passed()) + return 1; + + uint32 t = m_timers[WUPDATE_5_SECS].GetInterval() - m_timers[WUPDATE_5_SECS].GetCurrent(); + t = std::min(t, (uint32)m_timers[WUPDATE_5_SECS].GetInterval()); + + return uint32(ceil(t / 1000.0f)); +} + diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index edbfd8dfd..bb432e417 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -11,8 +11,6 @@ #ifndef __WORLD_H #define __WORLD_H -#include "Callback.h" -#include "Common.h" #include "IWorld.h" #include "LockedQueue.h" #include "ObjectGuid.h" @@ -337,14 +335,7 @@ public: static float GetMaxVisibleDistanceInBGArenas() { return m_MaxVisibleDistanceInBGArenas; } // our: needed for arena spectator subscriptions - uint32 GetNextWhoListUpdateDelaySecs() - { - if (m_timers[WUPDATE_5_SECS].Passed()) - return 1; - uint32 t = m_timers[WUPDATE_5_SECS].GetInterval() - m_timers[WUPDATE_5_SECS].GetCurrent(); - t = std::min(t, (uint32)m_timers[WUPDATE_5_SECS].GetInterval()); - return uint32(ceil(t / 1000.0f)); - } + uint32 GetNextWhoListUpdateDelaySecs(); // xinef: Global Player Data Storage system void LoadGlobalPlayerDataStore(); @@ -493,7 +484,7 @@ private: AutobroadcastsWeightMap m_AutobroadcastsWeights; void ProcessQueryCallbacks(); - ACE_Future_Set m_realmCharCallbacks; + QueryCallbackProcessor _queryProcessor; }; std::unique_ptr& getWorldInstance(); diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index c5a1056c4..0564fae04 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -254,7 +254,7 @@ public: return false; } - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); stmt->setUInt8(0, uint8(expansion)); stmt->setUInt32(1, accountId); @@ -375,7 +375,7 @@ public: static bool HandleAccountOnlineListCommand(ChatHandler* handler, char const* /*args*/) { ///- Get the list of accounts ID logged to the realm - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ONLINE); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ONLINE); PreparedQueryResult result = CharacterDatabase.Query(stmt); @@ -399,9 +399,9 @@ public: ///- Get the username, last IP and GM level of each account // No SQL injection. account is uint32. - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO); - stmt->setUInt32(0, account); - PreparedQueryResult resultLogin = LoginDatabase.Query(stmt); + LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO); + loginStmt->setUInt32(0, account); + PreparedQueryResult resultLogin = LoginDatabase.Query(loginStmt); if (resultLogin) { @@ -517,7 +517,7 @@ public: if (!param.empty()) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_LOCK); if (param == "on") { @@ -743,7 +743,7 @@ public: if (expansion < 0 || uint8(expansion) > sWorld->getIntConfig(CONFIG_EXPANSION)) return false; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPANSION); stmt->setUInt8(0, expansion); stmt->setUInt32(1, accountId); @@ -822,7 +822,7 @@ public: // Check and abort if the target gm has a higher rank on one of the realms and the new realm is -1 if (gmRealmID == -1 && !AccountMgr::IsConsoleAccount(playerSecurity)) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST); stmt->setUInt32(0, targetAccountId); stmt->setUInt8(1, uint8(gm)); @@ -846,18 +846,16 @@ public: } // If gmRealmID is -1, delete all values for the account id, else, insert values for the specific realm.Id.Realm - PreparedStatement* stmt; + LoginDatabasePreparedStatement* stmt; if (gmRealmID == -1) { stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); - stmt->setUInt32(0, targetAccountId); } else { stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM); - stmt->setUInt32(0, targetAccountId); stmt->setUInt32(1, realm.Id.Realm); } diff --git a/src/server/scripts/Commands/cs_ban.cpp b/src/server/scripts/Commands/cs_ban.cpp index 20c673a56..08a2e3afa 100644 --- a/src/server/scripts/Commands/cs_ban.cpp +++ b/src/server/scripts/Commands/cs_ban.cpp @@ -319,8 +319,9 @@ public: else targetGuid = target->GetGUID(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BANINFO); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BANINFO); stmt->setUInt32(0, targetGuid.GetCounter()); + PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) { @@ -378,7 +379,7 @@ public: static bool HandleBanListAccountCommand(ChatHandler* handler, char const* args) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS); LoginDatabase.Execute(stmt); char* filterStr = strtok((char*)args, " "); @@ -388,12 +389,12 @@ public: if (filter.empty()) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED_ALL); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED_ALL); result = LoginDatabase.Query(stmt); } else { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME); stmt->setString(0, filter); result = LoginDatabase.Query(stmt); } @@ -495,7 +496,7 @@ public: return false; std::string filter(filterStr); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_BY_NAME_FILTER); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_BY_NAME_FILTER); stmt->setString(0, filter); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) @@ -512,8 +513,9 @@ public: do { Field* fields = result->Fetch(); - PreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BANNED_NAME); + CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BANNED_NAME); stmt2->setUInt32(0, fields[0].GetUInt32()); + PreparedQueryResult banResult = CharacterDatabase.Query(stmt2); if (banResult) handler->PSendSysMessage("%s", (*banResult)[0].GetCString()); @@ -533,8 +535,9 @@ public: std::string char_name = fields[1].GetString(); - PreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BANINFO_LIST); + CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BANINFO_LIST); stmt2->setUInt32(0, fields[0].GetUInt32()); + PreparedQueryResult banInfo = CharacterDatabase.Query(stmt2); if (banInfo) { @@ -572,7 +575,7 @@ public: static bool HandleBanListIPCommand(ChatHandler* handler, char const* args) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS); LoginDatabase.Execute(stmt); char* filterStr = strtok((char*)args, " "); @@ -583,12 +586,12 @@ public: if (filter.empty()) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED_ALL); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED_ALL); result = LoginDatabase.Query(stmt); } else { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED_BY_IP); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED_BY_IP); stmt->setString(0, filter); result = LoginDatabase.Query(stmt); } diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index ba523d1cd..0873ecde4 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -93,7 +93,7 @@ public: static bool GetDeletedCharacterInfoList(DeletedInfoList& foundList, std::string searchString) { PreparedQueryResult result; - PreparedStatement* stmt; + CharacterDatabasePreparedStatement* stmt = nullptr; if (!searchString.empty()) { // search by GUID @@ -211,7 +211,7 @@ public: return; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_RESTORE_DELETE_INFO); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_RESTORE_DELETE_INFO); stmt->setString(0, delInfo.name); stmt->setUInt32(1, delInfo.accountId); stmt->setUInt32(2, delInfo.lowGuid); @@ -244,7 +244,7 @@ public: else { // Update level and reset XP, everything else will be updated at login - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_LEVEL); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_LEVEL); stmt->setUInt8(0, uint8(newLevel)); stmt->setUInt32(1, playerGuid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -336,7 +336,7 @@ public: std::string oldNameLink = handler->playerLink(targetName); handler->PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldNameLink.c_str(), targetGuid.GetCounter()); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_RENAME)); stmt->setUInt32(1, targetGuid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -399,7 +399,7 @@ public: if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName)) return false; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_CUSTOMIZE)); if (target) { @@ -427,7 +427,7 @@ public: if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName)) return false; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_FACTION)); if (target) { @@ -454,7 +454,7 @@ public: if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName)) return false; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_CHANGE_RACE)); if (target) { diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp index 01de59d89..2c044c55e 100644 --- a/src/server/scripts/Commands/cs_disable.cpp +++ b/src/server/scripts/Commands/cs_disable.cpp @@ -148,7 +148,7 @@ public: break; } - PreparedStatement* stmt = nullptr; + WorldDatabasePreparedStatement* stmt = nullptr; stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_DISABLES); stmt->setUInt32(0, entry); stmt->setUInt8(1, disableType); @@ -263,7 +263,7 @@ public: break; } - PreparedStatement* stmt = nullptr; + WorldDatabasePreparedStatement* stmt = nullptr; stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_DISABLES); stmt->setUInt32(0, entry); stmt->setUInt8(1, disableType); diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp index 899d6cfad..b54d5856d 100644 --- a/src/server/scripts/Commands/cs_gm.cpp +++ b/src/server/scripts/Commands/cs_gm.cpp @@ -155,7 +155,7 @@ public: static bool HandleGMListFullCommand(ChatHandler* handler, char const* /*args*/) { ///- Get the accounts with GM Level >0 - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_GM_ACCOUNTS); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_GM_ACCOUNTS); stmt->setUInt8(0, uint8(SEC_MODERATOR)); stmt->setInt32(1, int32(realm.Id.Realm)); PreparedQueryResult result = LoginDatabase.Query(stmt); diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index a0c3b7550..026e2265f 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -101,7 +101,7 @@ public: { std::string name = param1; WorldDatabase.EscapeString(name); - whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name " _LIKE_ " '" << name << '\''; + whereClause << ", creature_template WHERE creature.id = creature_template.entry AND creature_template.name LIKE '" << name << '\''; } else whereClause << "WHERE guid = '" << guid << '\''; diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index a56f505c5..7d0def796 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -224,9 +224,9 @@ public: std::string name = id; WorldDatabase.EscapeString(name); result = WorldDatabase.PQuery( - "SELECT guid, id, position_x, position_y, position_z, orientation, map, phaseMask, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ " - "FROM gameobject, gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name " _LIKE_ " " _CONCAT3_("'%%'", "'%s'", "'%%'") " ORDER BY order_ ASC LIMIT 1", - player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), name.c_str()); + "SELECT guid, id, position_x, position_y, position_z, orientation, map, phaseMask, (POW(position_x - %f, 2) + POW(position_y - %f, 2) + POW(position_z - %f, 2)) AS order_ " + "FROM gameobject, gameobject_template WHERE gameobject_template.entry = gameobject.id AND map = %i AND name LIKE '%%%s%%' ORDER BY order_ ASC LIMIT 1", + player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), name.c_str()); } } else @@ -545,7 +545,7 @@ public: Player* player = handler->GetSession()->GetPlayer(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_GAMEOBJECT_NEAREST); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_GAMEOBJECT_NEAREST); stmt->setFloat(0, player->GetPositionX()); stmt->setFloat(1, player->GetPositionY()); stmt->setFloat(2, player->GetPositionZ()); @@ -555,8 +555,8 @@ public: stmt->setFloat(6, player->GetPositionZ()); stmt->setFloat(7, distance * distance); stmt->setUInt32(8, player->GetPhaseMask()); - PreparedQueryResult result = WorldDatabase.Query(stmt); + PreparedQueryResult result = WorldDatabase.Query(stmt); if (result) { do diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp index 437ac5e3a..060615807 100644 --- a/src/server/scripts/Commands/cs_list.cpp +++ b/src/server/scripts/Commands/cs_list.cpp @@ -149,7 +149,7 @@ public: // inventory case uint32 inventoryCount = 0; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_INVENTORY_COUNT_ITEM); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_INVENTORY_COUNT_ITEM); stmt->setUInt32(0, itemId); result = CharacterDatabase.Query(stmt); diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp index 315db5b21..a9dab2fcb 100644 --- a/src/server/scripts/Commands/cs_lookup.cpp +++ b/src/server/scripts/Commands/cs_lookup.cpp @@ -1333,7 +1333,7 @@ public: limit = limitStr ? atoi(limitStr) : -1; } - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_IP); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_IP); stmt->setString(0, ip); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -1353,7 +1353,7 @@ public: (account)) return false; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME); stmt->setString(0, account); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -1369,7 +1369,7 @@ public: char* limitStr = strtok(nullptr, " "); int32 limit = limitStr ? atoi(limitStr) : -1; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL); stmt->setString(0, email); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -1404,7 +1404,7 @@ public: uint32 accountId = fields[0].GetUInt32(); std::string accountName = fields[1].GetString(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_GUID_NAME_BY_ACC); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_GUID_NAME_BY_ACC); stmt->setUInt32(0, accountId); PreparedQueryResult result2 = CharacterDatabase.Query(stmt); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index bb4d3bfe8..1561c9fa8 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -927,7 +927,7 @@ public: } else { - SQLTransaction trans(nullptr); + CharacterDatabaseTransaction trans(nullptr); Player::OfflineResurrect(targetGuid, trans); } @@ -1491,7 +1491,7 @@ public: { std::string itemName = itemNameStr + 1; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME); stmt->setString(0, itemName); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -1787,7 +1787,8 @@ public: Player* target; ObjectGuid targetGuid; std::string targetName; - PreparedStatement* stmt = nullptr; + CharacterDatabasePreparedStatement* stmt = nullptr; + LoginDatabasePreparedStatement* loginStmt = nullptr; ObjectGuid parseGUID = ObjectGuid::Create(atol((char*)args)); @@ -1907,11 +1908,11 @@ public: } // Query the prepared statement for login data - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO); - stmt->setInt32(0, int32(realm.Id.Realm)); - stmt->setUInt32(1, accId); - PreparedQueryResult accInfoResult = LoginDatabase.Query(stmt); + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO); + loginStmt->setInt32(0, int32(realm.Id.Realm)); + loginStmt->setUInt32(1, accId); + PreparedQueryResult accInfoResult = LoginDatabase.Query(loginStmt); if (accInfoResult) { Field* fields = accInfoResult->Fetch(); @@ -1952,8 +1953,9 @@ public: std::string nameLink = handler->playerLink(targetName); // Returns banType, banTime, bannedBy, banreason - PreparedStatement* banQuery = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO_BANS); + LoginDatabasePreparedStatement* banQuery = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO_BANS); banQuery->setUInt32(0, accId); + PreparedQueryResult accBannedResult = LoginDatabase.Query(banQuery); if (!accBannedResult) { @@ -1972,10 +1974,10 @@ public: } // Can be used to query data from World database - PreparedStatement* xpQuery = WorldDatabase.GetPreparedStatement(WORLD_SEL_REQ_XP); + WorldDatabasePreparedStatement* xpQuery = WorldDatabase.GetPreparedStatement(WORLD_SEL_REQ_XP); xpQuery->setUInt8(0, level); - PreparedQueryResult xpResult = WorldDatabase.Query(xpQuery); + PreparedQueryResult xpResult = WorldDatabase.Query(xpQuery); if (xpResult) { Field* fields = xpResult->Fetch(); @@ -1983,10 +1985,10 @@ public: } // Can be used to query data from Characters database - PreparedStatement* charXpQuery = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PINFO_XP); + CharacterDatabasePreparedStatement* charXpQuery = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PINFO_XP); charXpQuery->setUInt32(0, lowguid); - PreparedQueryResult charXpResult = CharacterDatabase.Query(charXpQuery); + PreparedQueryResult charXpResult = CharacterDatabase.Query(charXpQuery); if (charXpResult) { Field* fields = charXpResult->Fetch(); @@ -1995,8 +1997,9 @@ public: if (gguid != 0) { - PreparedStatement* guildQuery = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED); + CharacterDatabasePreparedStatement* guildQuery = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED); guildQuery->setUInt32(0, lowguid); + PreparedQueryResult guildInfoResult = CharacterDatabase.Query(guildQuery); if (guildInfoResult) { @@ -2170,8 +2173,9 @@ public: // Mail Data - an own query, because it may or may not be useful. // SQL: "SELECT SUM(CASE WHEN (checked & 1) THEN 1 ELSE 0 END) AS 'readmail', COUNT(*) AS 'totalmail' FROM mail WHERE `receiver` = ?" - PreparedStatement* mailQuery = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PINFO_MAILS); + CharacterDatabasePreparedStatement* mailQuery = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PINFO_MAILS); mailQuery->setUInt32(0, lowguid); + PreparedQueryResult mailInfoResult = CharacterDatabase.Query(mailQuery); if (mailInfoResult) { @@ -2250,7 +2254,7 @@ public: if (handler->HasLowerSecurity (target, targetGuid, true)) return false; - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); std::string muteBy = ""; if (handler->GetSession()) muteBy = handler->GetSession()->GetPlayerName(); @@ -2336,7 +2340,7 @@ public: target->GetSession()->m_muteTime = 0; } - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME); stmt->setInt64(0, 0); stmt->setString(1, ""); stmt->setString(2, ""); @@ -2384,7 +2388,7 @@ public: // helper for mutehistory static bool HandleMuteInfoHelper(uint32 accountId, char const* accountName, ChatHandler* handler) { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO); stmt->setUInt16(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); @@ -2671,7 +2675,7 @@ public: MailSender sender(MAIL_NORMAL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetGUID().GetCounter() : 0, MAIL_STATIONERY_GM); //- TODO: Fix poor design - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); MailDraft(subject, text) .SendMailTo(trans, MailReceiver(target, targetGuid.GetCounter()), sender); @@ -2771,7 +2775,7 @@ public: // fill mail MailDraft draft(subject, text); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); for (ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr) { @@ -2828,7 +2832,7 @@ public: // from console show not existed sender MailSender sender(MAIL_NORMAL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetGUID().GetCounter() : 0, MAIL_STATIONERY_GM); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); MailDraft(subject, text) .AddMoney(money) @@ -3092,7 +3096,7 @@ public: { if (ObjectGuid playerGUID = sWorld->GetGlobalPlayerGUID(name)) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA_FROZEN); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA_FROZEN); stmt->setUInt32(0, playerGUID.GetCounter()); CharacterDatabase.Execute(stmt); handler->PSendSysMessage(LANG_COMMAND_UNFREEZE, name.c_str()); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index dea3417b9..433e7f997 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -354,8 +354,7 @@ public: wait = 0; // Update movement type - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE); stmt->setUInt8(0, uint8(WAYPOINT_MOTION_TYPE)); stmt->setUInt32(1, spawnId); @@ -569,8 +568,7 @@ public: } // ..and DB - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_FACTION); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_FACTION); stmt->setUInt16(0, uint16(factionId)); stmt->setUInt32(1, creature->GetEntry()); @@ -639,8 +637,7 @@ public: creature->SetUInt32Value(UNIT_NPC_FLAGS, npcFlags); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_NPCFLAG); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_NPCFLAG); stmt->setUInt32(0, npcFlags); stmt->setUInt32(1, creature->GetEntry()); @@ -761,7 +758,7 @@ public: Player* player = handler->GetSession()->GetPlayer(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_NEAREST); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_NEAREST); stmt->setFloat(0, player->GetPositionX()); stmt->setFloat(1, player->GetPositionY()); stmt->setFloat(2, player->GetPositionZ()); @@ -771,8 +768,8 @@ public: stmt->setFloat(6, player->GetPositionZ()); stmt->setFloat(7, distance * distance); stmt->setUInt32(8, player->GetPhaseMask()); - PreparedQueryResult result = WorldDatabase.Query(stmt); + PreparedQueryResult result = WorldDatabase.Query(stmt); if (result) { do @@ -874,8 +871,7 @@ public: } } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_POSITION); stmt->setFloat(0, x); stmt->setFloat(1, y); stmt->setFloat(2, z); @@ -1143,8 +1139,7 @@ public: creature->Respawn(); } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_WANDER_DISTANCE); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_WANDER_DISTANCE); stmt->setFloat(0, option); stmt->setUInt8(1, uint8(mtype)); stmt->setUInt32(2, guidLow); @@ -1183,8 +1178,7 @@ public: else return false; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_SPAWN_TIME_SECS); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_SPAWN_TIME_SECS); stmt->setUInt32(0, uint32(spawnTime)); stmt->setUInt32(1, guidLow); @@ -1456,8 +1450,7 @@ public: sFormationMgr->CreatureGroupMap[lowguid] = group_member; creature->SearchFormation(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE_FORMATION); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_CREATURE_FORMATION); stmt->setUInt32(0, leaderGUID); stmt->setUInt32(1, lowguid); stmt->setFloat(2, group_member->follow_dist); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 17c0070b8..dfe4baaf7 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -30,6 +30,8 @@ EndScriptData */ #include "TicketMgr.h" #include "WardenCheckMgr.h" #include "WaypointManager.h" +#include "StringConvert.h" +#include "Tokenize.h" class reload_commandscript : public CommandScript { @@ -410,13 +412,11 @@ public: if (!*args) return false; - Tokenizer entries(std::string(args), ' '); - - for (Tokenizer::const_iterator itr = entries.begin(); itr != entries.end(); ++itr) + for (std::string_view entryStr : Acore::Tokenize(args, ' ', false)) { - uint32 entry = uint32(atoi(*itr)); + uint32 entry = Acore::StringTo(entryStr).value_or(0); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE); stmt->setUInt32(0, entry); PreparedQueryResult result = WorldDatabase.Query(stmt); diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp index d09805364..26596f0ec 100644 --- a/src/server/scripts/Commands/cs_reset.cpp +++ b/src/server/scripts/Commands/cs_reset.cpp @@ -160,7 +160,7 @@ public: } else { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_RESET_SPELLS)); stmt->setUInt32(1, targetGuid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -234,7 +234,7 @@ public: } else if (targetGuid) { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->setUInt16(0, uint16(AT_LOGIN_NONE | AT_LOGIN_RESET_PET_TALENTS)); stmt->setUInt32(1, targetGuid.GetCounter()); CharacterDatabase.Execute(stmt); @@ -280,7 +280,7 @@ public: return false; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS); stmt->setUInt16(0, uint16(atLogin)); CharacterDatabase.Execute(stmt); diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp index d47a7cbef..fbbd5ea35 100644 --- a/src/server/scripts/Commands/cs_tele.cpp +++ b/src/server/scripts/Commands/cs_tele.cpp @@ -121,7 +121,7 @@ public: target->TeleportTo(target->m_homebindMapId, target->m_homebindX, target->m_homebindY, target->m_homebindZ, target->GetOrientation()); else { - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_HOMEBIND); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_HOMEBIND); stmt->setUInt32(0, target_guid.GetCounter()); PreparedQueryResult resultDB = CharacterDatabase.Query(stmt); diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp index cefc7758c..2a2c5ea40 100644 --- a/src/server/scripts/Commands/cs_ticket.cpp +++ b/src/server/scripts/Commands/cs_ticket.cpp @@ -111,7 +111,7 @@ public: } // Assign ticket - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetAssignedTo(targetGuid, AccountMgr::IsAdminAccount(targetGmLevel)); ticket->SaveToDB(trans); sTicketMgr->UpdateLastChange(); @@ -187,7 +187,7 @@ public: return true; } - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetComment(comment); ticket->SaveToDB(trans); sTicketMgr->UpdateLastChange(); @@ -243,7 +243,7 @@ public: Player* gm = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr; - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetCompleted(); ticket->SetResolvedBy(gm ? gm->GetGUID() : ObjectGuid::Empty); ticket->SaveToDB(trans); @@ -396,7 +396,7 @@ public: } std::string assignedTo = ticket->GetAssignedToName(); // copy assignedto name because we need it after the ticket has been unnassigned - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetUnassigned(); ticket->SaveToDB(trans); sTicketMgr->UpdateLastChange(); @@ -421,7 +421,7 @@ public: return true; } - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetViewed(); ticket->SaveToDB(trans); @@ -460,7 +460,7 @@ public: return true; } - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->SetViewed(); ticket->SaveToDB(trans); @@ -496,7 +496,7 @@ public: return true; } - SQLTransaction trans = SQLTransaction(nullptr); + CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr); ticket->AppendResponse(response); if (newLine) ticket->AppendResponse("\n"); diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index f641a7a04..293a9eb9f 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -80,7 +80,7 @@ public: pathid = target->GetWaypointPath(); else { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_MAX_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_MAX_ID); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -101,7 +101,7 @@ public: return true; } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_MAX_POINT); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_MAX_POINT); stmt->setUInt32(0, pathid); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -168,7 +168,7 @@ public: guidLow = target->GetSpawnId(); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_ADDON_BY_GUID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_ADDON_BY_GUID); stmt->setUInt32(0, guidLow); @@ -237,8 +237,7 @@ public: { if (target->GetCreatureAddon()->path_id != 0) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE_ADDON); stmt->setUInt32(0, guildLow); WorldDatabase.Execute(stmt); @@ -246,7 +245,6 @@ public: target->UpdateWaypointID(0); stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE); - stmt->setUInt8(0, uint8(IDLE_MOTION_TYPE)); stmt->setUInt32(1, guildLow); @@ -286,13 +284,13 @@ public: if (id) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID); stmt->setUInt32(0, id); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_WAYPOINT_SCRIPT); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_WAYPOINT_SCRIPT); stmt->setUInt32(0, id); @@ -305,7 +303,7 @@ public: } else { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPTS_MAX_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPTS_MAX_ID); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -337,7 +335,7 @@ public: float a8, a9, a10, a11; char const* a7; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID); stmt->setUInt32(0, id); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -377,7 +375,7 @@ public: id = atoi(arg_id); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID); stmt->setUInt32(0, id); @@ -385,7 +383,7 @@ public: if (result) { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_WAYPOINT_SCRIPT); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_WAYPOINT_SCRIPT); stmt->setUInt32(0, id); @@ -448,7 +446,7 @@ public: uint32 newid = atoi(arg_3); handler->PSendSysMessage("%s%s|r|cff00ffff%u|r|cff00ff00%s|r|cff00ffff%u|r", "|cff00ff00", "Wp Event: Wypoint scipt guid: ", newid, " id changed: ", id); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_ID); stmt->setUInt32(0, newid); stmt->setUInt32(1, id); @@ -459,7 +457,7 @@ public: } else { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID); stmt->setUInt32(0, id); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -471,7 +469,7 @@ public: if (arg_str_2 == "posx") { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_X); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_X); stmt->setFloat(0, float(atof(arg_3))); stmt->setUInt32(1, id); @@ -483,7 +481,7 @@ public: } else if (arg_str_2 == "posy") { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_Y); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_Y); stmt->setFloat(0, float(atof(arg_3))); stmt->setUInt32(1, id); @@ -495,7 +493,7 @@ public: } else if (arg_str_2 == "posz") { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_Z); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_Z); stmt->setFloat(0, float(atof(arg_3))); stmt->setUInt32(1, id); @@ -507,7 +505,7 @@ public: } else if (arg_str_2 == "orientation") { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_O); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_SCRIPT_O); stmt->setFloat(0, float(atof(arg_3))); stmt->setUInt32(1, id); @@ -580,7 +578,7 @@ public: // User did select a visual waypoint? // Check the creature - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_WPGUID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_WPGUID); stmt->setUInt32(0, wpSpawnId); PreparedQueryResult result = WorldDatabase.Query(stmt); @@ -595,7 +593,7 @@ public: // See also: http://dev.mysql.com/doc/refman/5.0/en/problems-with-float.html std::string maxDiff = "0.01"; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_POS); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_BY_POS); stmt->setFloat(0, target->GetPositionX()); stmt->setString(1, maxDiff); stmt->setFloat(2, target->GetPositionY()); @@ -641,15 +639,13 @@ public: wpCreature->AddObjectToRemoveList(); } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_WAYPOINT_DATA); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_WAYPOINT_DATA); stmt->setUInt32(0, pathid); stmt->setUInt32(1, point); WorldDatabase.Execute(stmt); stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_POINT); - stmt->setUInt32(0, pathid); stmt->setUInt32(1, point); @@ -700,7 +696,7 @@ public: //sMapMgr->GetMap(npcCreature->GetMapId())->Add(wpCreature2); } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_POSITION); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_POSITION); stmt->setFloat(0, chr->GetPositionX()); stmt->setFloat(1, chr->GetPositionY()); @@ -792,7 +788,7 @@ public: return false; } - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ALL_BY_WPGUID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ALL_BY_WPGUID); stmt->setUInt32(0, target->GetSpawnId()); @@ -827,7 +823,7 @@ public: if (show == "on") { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID); stmt->setUInt32(0, pathid); @@ -863,8 +859,7 @@ public: handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid); hasError = true; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); stmt->setUInt32(0, wpguid); WorldDatabase.Execute(stmt); @@ -908,8 +903,7 @@ public: } // Set "wpguid" column to the visual waypoint - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID); - + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID); stmt->setInt32(0, int32(wpCreature->GetSpawnId())); stmt->setUInt32(1, pathid); stmt->setUInt32(2, point); @@ -941,10 +935,10 @@ public: { handler->PSendSysMessage("|cff00ff00DEBUG: wp first, GUID: %u|r", pathid); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID); stmt->setUInt32(0, pathid); - PreparedQueryResult result = WorldDatabase.Query(stmt); + PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) { handler->PSendSysMessage(LANG_WAYPOINT_NOTFOUND, pathid); @@ -991,16 +985,17 @@ public: { handler->PSendSysMessage("|cff00ff00DEBUG: wp last, PathID: |r|cff00ffff%u|r", pathid); - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID); stmt->setUInt32(0, pathid); - PreparedQueryResult result = WorldDatabase.Query(stmt); + PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) { handler->PSendSysMessage(LANG_WAYPOINT_NOTFOUNDLAST, pathid); handler->SetSentErrorMessage(true); return false; } + Field* fields = result->Fetch(); float x = fields[0].GetFloat(); float y = fields[1].GetFloat(); @@ -1038,17 +1033,19 @@ public: if (show == "off") { - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_BY_ID); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_BY_ID); stmt->setUInt32(0, 1); - PreparedQueryResult result = WorldDatabase.Query(stmt); + PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) { handler->SendSysMessage(LANG_WAYPOINT_VP_NOTFOUND); handler->SetSentErrorMessage(true); return false; } + bool hasError = false; + do { Field* fields = result->Fetch(); @@ -1059,7 +1056,7 @@ public: handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); hasError = true; - PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); stmt->setUInt32(0, guid); @@ -1072,11 +1069,11 @@ public: creature->AddObjectToRemoveList(); } } while (result->NextRow()); + // set "wpguid" column to "empty" - no visual waypoint spawned stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID); WorldDatabase.Execute(stmt); - //WorldDatabase.PExecute("UPDATE creature_movement SET wpguid = '0' WHERE wpguid <> '0'"); if (hasError) { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h index d3f1c88cb..08b3ed578 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/blackrock_spire.h @@ -4,8 +4,6 @@ * Copyright (C) 2005-2009 MaNGOS */ -#include "CreatureAIImpl.h" - #ifndef DEF_BLACKROCK_SPIRE_H #define DEF_BLACKROCK_SPIRE_H diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h index 9af08227d..0425ea350 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h @@ -16,8 +16,6 @@ * with this program. If not, see . */ -#include "CreatureAIImpl.h" - #ifndef DEF_KARAZHAN_H #define DEF_KARAZHAN_H diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h index 8605bc582..7ee85ab1e 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.h @@ -2,8 +2,6 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ -#include "CreatureAIImpl.h" - #ifndef DEF_CULLING_OF_STRATHOLME_H #define DEF_CULLING_OF_STRATHOLME_H diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h index 3588ac21c..a2af68943 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.h @@ -2,8 +2,6 @@ * Originally written by Xinef - Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 */ -#include "CreatureAIImpl.h" - #ifndef DEF_OBSIDIAN_SANCTUM_H #define DEF_OBSIDIAN_SANCTUM_H diff --git a/src/server/scripts/Northrend/Nexus/Nexus/nexus.h b/src/server/scripts/Northrend/Nexus/Nexus/nexus.h index 2648c5e4c..977302d19 100644 --- a/src/server/scripts/Northrend/Nexus/Nexus/nexus.h +++ b/src/server/scripts/Northrend/Nexus/Nexus/nexus.h @@ -7,8 +7,6 @@ #include "CreatureAIImpl.h" #include "GameEventMgr.h" -#include "GameEventMgr.h" -#include "GridNotifiers.h" #include "GridNotifiers.h" #include "PassiveAI.h" #include "Player.h" diff --git a/src/server/scripts/Northrend/zone_dalaran.cpp b/src/server/scripts/Northrend/zone_dalaran.cpp index d97c87347..8ecccc4e2 100644 --- a/src/server/scripts/Northrend/zone_dalaran.cpp +++ b/src/server/scripts/Northrend/zone_dalaran.cpp @@ -545,7 +545,7 @@ public: void SendMailToPlayer(Player* player) { - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); int16 deliverDelay = irand(MAIL_DELIVER_DELAY_MIN, MAIL_DELIVER_DELAY_MAX); MailDraft(MAIL_MINIGOB_ENTRY, true).SendMailTo(trans, MailReceiver(player), MailSender(MAIL_CREATURE, me->GetEntry()), MAIL_CHECK_MASK_NONE, deliverDelay); CharacterDatabase.CommitTransaction(trans); diff --git a/src/server/scripts/World/action_ip_logger.cpp b/src/server/scripts/World/action_ip_logger.cpp index 065d73d2f..6ba19b3f5 100644 --- a/src/server/scripts/World/action_ip_logger.cpp +++ b/src/server/scripts/World/action_ip_logger.cpp @@ -128,7 +128,7 @@ public: { // As we can assume most account actions are NOT failed login, so this is the more accurate check. // For those, we need last_ip... - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING); stmt->setUInt32(0, playerGuid); stmt->setUInt32(1, characterGuid); @@ -139,7 +139,7 @@ public: } else // ... but for failed login, we query last_attempt_ip from account table. Which we do with an unique query { - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FACL_IP_LOGGING); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FACL_IP_LOGGING); stmt->setUInt32(0, playerGuid); stmt->setUInt32(1, characterGuid); @@ -224,7 +224,7 @@ public: } // Once we have done everything, we can insert the new log. - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_CHAR_IP_LOGGING); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_CHAR_IP_LOGGING); stmt->setUInt32(0, playerGuid); stmt->setUInt32(1, characterGuid); @@ -287,8 +287,7 @@ public: } // Once we have done everything, we can insert the new log. - PreparedStatement* stmt2 = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING); - + LoginDatabasePreparedStatement* stmt2 = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING); stmt2->setUInt32(0, playerGuid); stmt2->setUInt32(1, characterGuid); stmt2->setUInt8(2, aType); diff --git a/src/server/shared/Realms/RealmList.cpp b/src/server/shared/Realms/RealmList.cpp index 1bdeaee1f..909c79e69 100644 --- a/src/server/shared/Realms/RealmList.cpp +++ b/src/server/shared/Realms/RealmList.cpp @@ -123,7 +123,7 @@ void RealmList::UpdateRealms() { LOG_DEBUG("server.authserver", "Updating Realm List..."); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALMLIST); PreparedQueryResult result = LoginDatabase.Query(stmt); std::map existingRealms; diff --git a/src/server/worldserver/Master.cpp b/src/server/worldserver/Master.cpp index f09284a78..ce103b0ec 100644 --- a/src/server/worldserver/Master.cpp +++ b/src/server/worldserver/Master.cpp @@ -32,6 +32,7 @@ #include "WorldSocketMgr.h" #include "DatabaseLoader.h" #include "Optional.h" +#include "MySQLThreading.h" #include "SecretMgr.h" #include "ProcessPriority.h" #include @@ -306,7 +307,7 @@ bool Master::_StartDB() MySQL::Library_Init(); // Load databases - DatabaseLoader loader; + DatabaseLoader loader("server.worldserver"); loader .AddDatabase(LoginDatabase, "Login") .AddDatabase(CharacterDatabase, "Character") diff --git a/src/server/worldserver/RemoteAccess/RASession.cpp b/src/server/worldserver/RemoteAccess/RASession.cpp index f2824c585..33355d004 100644 --- a/src/server/worldserver/RemoteAccess/RASession.cpp +++ b/src/server/worldserver/RemoteAccess/RASession.cpp @@ -116,8 +116,8 @@ bool RASession::CheckAccessLevel(const std::string& user) auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ACCESS); stmt->setString(0, safeUser); - PreparedQueryResult result = LoginDatabase.Query(stmt); + PreparedQueryResult result = LoginDatabase.Query(stmt); if (!result) { LOG_INFO("commands.ra", "User %s does not exist in database", user.c_str()); diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp index f04455078..2a956cbe7 100644 --- a/src/tools/vmap4_extractor/vmapexport.cpp +++ b/src/tools/vmap4_extractor/vmapexport.cpp @@ -15,7 +15,6 @@ #ifdef WIN32 #include -#include #include #define mkdir _mkdir #endif