diff options
21 files changed, 550 insertions, 816 deletions
diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index 0dbf7841e07..5c3ed477ace 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -169,8 +169,7 @@ void AuthSession::Start() stmt->setString(0, ip_address); stmt->setUInt32(1, inet_addr(ip_address.c_str())); - _queryCallback = std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1); - _queryFuture = LoginDatabase.AsyncQuery(stmt); + _queryCallback = std::move(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1))); } bool AuthSession::Update() @@ -178,12 +177,8 @@ bool AuthSession::Update() if (!AuthSocket::Update()) return false; - if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - auto callback = _queryCallback; - _queryCallback = nullptr; - callback(_queryFuture.get()); - } + if (_queryCallback && _queryCallback->InvokeIfReady() == QueryCallback::Completed) + _queryCallback = boost::none; return true; } @@ -323,8 +318,7 @@ bool AuthSession::HandleLogonChallenge() PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE); stmt->setString(0, login); - _queryCallback = std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1); - _queryFuture = LoginDatabase.AsyncQuery(stmt); + _queryCallback = std::move(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1))); return true; } @@ -737,8 +731,7 @@ bool AuthSession::HandleReconnectChallenge() PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE); stmt->setString(0, login); - _queryCallback = std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1); - _queryFuture = LoginDatabase.AsyncQuery(stmt); + _queryCallback = std::move(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1))); return true; } @@ -819,8 +812,7 @@ bool AuthSession::HandleRealmList() PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS); stmt->setUInt32(0, _accountInfo.Id); - _queryCallback = std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1); - _queryFuture = LoginDatabase.AsyncQuery(stmt); + _queryCallback = std::move(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1))); return true; } diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h index 1bc349dde4d..6b7124038e8 100644 --- a/src/server/authserver/Server/AuthSession.h +++ b/src/server/authserver/Server/AuthSession.h @@ -102,8 +102,7 @@ private: uint16 _build; uint8 _expversion; - PreparedQueryResultFuture _queryFuture; - std::function<void(PreparedQueryResult)> _queryCallback; + Optional<QueryCallback> _queryCallback; }; #pragma pack(push, 1) diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp index b770e30fc8c..9a0acedb0fe 100644 --- a/src/server/database/Database/DatabaseWorkerPool.cpp +++ b/src/server/database/Database/DatabaseWorkerPool.cpp @@ -17,6 +17,7 @@ #include "DatabaseWorkerPool.h" #include "DatabaseEnv.h" +#include "QueryCallback.h" #define MIN_MYSQL_SERVER_VERSION 50100u #define MIN_MYSQL_CLIENT_VERSION 50100u @@ -146,23 +147,23 @@ PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement* stmt) } template <class T> -QueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(const char* sql) +QueryCallback DatabaseWorkerPool<T>::AsyncQuery(const char* sql) { 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 result; + return QueryCallback(std::move(result)); } template <class T> -PreparedQueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(PreparedStatement* stmt) +QueryCallback DatabaseWorkerPool<T>::AsyncQuery(PreparedStatement* stmt) { 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 result; + return QueryCallback(std::move(result)); } template <class T> diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h index a082a9b6b0e..b195d2d6a28 100644 --- a/src/server/database/Database/DatabaseWorkerPool.h +++ b/src/server/database/Database/DatabaseWorkerPool.h @@ -19,7 +19,6 @@ #define _DATABASEWORKERPOOL_H #include "Common.h" -#include "QueryCallback.h" #include "MySQLConnection.h" #include "Transaction.h" #include "DatabaseWorker.h" @@ -34,6 +33,8 @@ #include <memory> #include <array> +class QueryCallback; + class PingOperation : public SQLOperation { //! Operation for idle delaythreads @@ -192,12 +193,12 @@ class DatabaseWorkerPool //! 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); + QueryCallback 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<typename Format, typename... Args> - QueryResultFuture AsyncPQuery(Format&& sql, Args&&... args) + QueryCallback AsyncPQuery(Format&& sql, Args&&... args) { return AsyncQuery(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str()); } @@ -205,7 +206,7 @@ class DatabaseWorkerPool //! 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. diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 301f3cf7d67..ecf3b52e8d9 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -51,7 +51,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "cb.guid, 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_GUID_RACE_ACC_BY_NAME, "SELECT guid, race, account FROM characters WHERE name = ?", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_CHAR_LEVEL, "SELECT level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH); @@ -162,7 +162,6 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_NAME_CLASS, "SELECT name, class FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT account, COUNT(guid) FROM characters WHERE account = ? GROUP BY account", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_NAME, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_NAME_BY_GUID, "UPDATE characters SET name = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); @@ -399,7 +398,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_INSTANCE_RESETTIME, "UPDATE instance SET resettime = ? WHERE id = ?", 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_GUID, "DELETE FROM character_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); @@ -429,7 +428,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_AT_LOGIN_TITLES, "SELECT 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 FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PERM_BIND_BY_INSTANCE, "SELECT guid FROM character_instance WHERE instance = ? and permanent = 1", 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); @@ -477,7 +477,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); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 3373daea2f0..5998b5f584c 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -148,7 +148,6 @@ enum CharacterDatabaseStatements CHAR_SEL_CHARACTER_NAME_CLASS, CHAR_SEL_MATCH_MAKER_RATING, CHAR_SEL_CHARACTER_COUNT, - CHAR_UPD_NAME, CHAR_UPD_NAME_BY_GUID, CHAR_DEL_DECLINED_NAME, @@ -360,7 +359,8 @@ enum CharacterDatabaseStatements CHAR_SEL_POOL_QUEST_SAVE, CHAR_SEL_CHARACTER_AT_LOGIN, CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, - CHAR_SEL_CHAR_AT_LOGIN_TITLES, + CHAR_SEL_CHAR_CUSTOMIZE_INFO, + CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, CHAR_SEL_INSTANCE, CHAR_SEL_PERM_BIND_BY_INSTANCE, CHAR_SEL_CHAR_COD_ITEM_MAIL, @@ -403,7 +403,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, diff --git a/src/server/database/Database/QueryCallback.cpp b/src/server/database/Database/QueryCallback.cpp index b77036b8e6a..8850f87b20a 100644 --- a/src/server/database/Database/QueryCallback.cpp +++ b/src/server/database/Database/QueryCallback.cpp @@ -18,115 +18,186 @@ #include "QueryCallback.h" template<typename T> +inline void Construct(T& t) +{ + new (&t) T(); +} + +template<typename T> inline void Destroy(T& t) { t.~T(); } template<typename T> -void DestroyActiveMember(T& obj) +void ConstructActiveMember(T* obj) +{ + if (!obj->_isPrepared) + Construct(obj->_string); + else + Construct(obj->_prepared); +} + +template<typename T> +void DestroyActiveMember(T* obj) { - if (!obj._isPrepared) - Destroy(obj._string); + if (!obj->_isPrepared) + Destroy(obj->_string); else - Destroy(obj._prepared); + Destroy(obj->_prepared); } template<typename T> -void MoveFrom(T& to, T&& from) +void MoveFrom(T* to, T&& from) { - DestroyActiveMember(to); - to._isPrepared = from._isPrepared; - if (!to._isPrepared) - to._string = std::move(from._string); + ASSERT(to->_isPrepared == from._isPrepared); + + if (!to->_isPrepared) + to->_string = std::move(from._string); else - to._prepared = std::move(from._prepared); + to->_prepared = std::move(from._prepared); } -struct QueryCallbackNew::QueryCallbackData +struct QueryCallback::QueryCallbackData { public: - friend class QueryCallbackNew; + friend class QueryCallback; - QueryCallbackData(std::function<void(QueryResult)>&& callback) : _string(std::move(callback)), _isPrepared(false) { } - QueryCallbackData(std::function<void(PreparedQueryResult)>&& callback) : _prepared(std::move(callback)), _isPrepared(true) { } - QueryCallbackData(QueryCallbackData&& right) { MoveFrom(*this, std::move(right)); } - QueryCallbackData& operator=(QueryCallbackData&& right) { MoveFrom(*this, std::move(right)); } - ~QueryCallbackData() { DestroyActiveMember(*this); } + QueryCallbackData(std::function<void(QueryCallback&, QueryResult)>&& callback) : _string(std::move(callback)), _isPrepared(false) { TC_LOG_ERROR(LOGGER_ROOT, "QueryCallbackData [%p]: Constructing from string query", (void*)this); } + QueryCallbackData(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback) : _prepared(std::move(callback)), _isPrepared(true) { TC_LOG_ERROR(LOGGER_ROOT, "QueryCallbackData [%p]: Constructing from prepared query", (void*)this); } + 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<typename T> friend void MoveFrom(T& to, T&& from); - template<typename T> friend void DestroyActiveMember(T& obj); + template<typename T> friend void ConstructActiveMember(T* obj); + template<typename T> friend void DestroyActiveMember(T* obj); + template<typename T> friend void MoveFrom(T* to, T&& from); union { - std::function<void(QueryResult)> _string; - std::function<void(PreparedQueryResult)> _prepared; + std::function<void(QueryCallback&, QueryResult)> _string; + std::function<void(QueryCallback&, PreparedQueryResult)> _prepared; }; bool _isPrepared; }; -QueryCallbackNew::QueryCallbackNew(std::future<QueryResult>&& result) : _string(std::move(result)), _isPrepared(false) +QueryCallback::QueryCallback(std::future<QueryResult>&& result) : _string(std::move(result)), _isPrepared(false) { } -QueryCallbackNew::QueryCallbackNew(std::future<PreparedQueryResult>&& result) : _prepared(std::move(result)), _isPrepared(true) +QueryCallback::QueryCallback(std::future<PreparedQueryResult>&& result) : _prepared(std::move(result)), _isPrepared(true) { } -QueryCallbackNew::QueryCallbackNew(QueryCallbackNew&& right) +QueryCallback::QueryCallback(QueryCallback&& right) { - MoveFrom(*this, std::move(right)); + _isPrepared = right._isPrepared; + ConstructActiveMember(this); + MoveFrom(this, std::move(right)); _callbacks = std::move(right._callbacks); } -QueryCallbackNew& QueryCallbackNew::operator=(QueryCallbackNew&& right) +QueryCallback& QueryCallback::operator=(QueryCallback&& right) { - MoveFrom(*this, std::move(right)); - _callbacks = std::move(right._callbacks); + 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; } -QueryCallbackNew::~QueryCallbackNew() +QueryCallback::~QueryCallback() { - DestroyActiveMember(*this); + DestroyActiveMember(this); } -QueryCallbackNew&& QueryCallbackNew::WithCallback(std::function<void(QueryResult)>&& callback) +QueryCallback&& QueryCallback::WithCallback(std::function<void(QueryResult)>&& callback) +{ + return WithChainingCallback([callback](QueryCallback& /*this*/, QueryResult result) { callback(std::move(result)); }); +} + +QueryCallback&& QueryCallback::WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback) +{ + return WithChainingPreparedCallback([callback](QueryCallback& /*this*/, PreparedQueryResult result) { callback(std::move(result)); }); +} + +QueryCallback&& QueryCallback::WithChainingCallback(std::function<void(QueryCallback&, QueryResult)>&& 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); } -QueryCallbackNew&& QueryCallbackNew::WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback) +QueryCallback&& QueryCallback::WithChainingPreparedCallback(std::function<void(QueryCallback&, PreparedQueryResult)>&& 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); } -QueryCallbackNew::Status QueryCallbackNew::InvokeIfReady() +void QueryCallback::SetNextQuery(QueryCallback&& next) +{ + MoveFrom(this, std::move(next)); +} + +QueryCallback::Status QueryCallback::InvokeIfReady() { QueryCallbackData& callback = _callbacks.front(); + auto checkStateAndReturnCompletion = [this]() + { + _callbacks.pop(); + bool hasNext = !_isPrepared ? _string.valid() : _prepared.valid(); + if (_callbacks.empty()) + { + ASSERT(!hasNext); + return Completed; + } + + // abort chain + if (!hasNext) + return Completed; + + ASSERT(_isPrepared == _callbacks.front()._isPrepared); + return NextStep; + }; + if (!_isPrepared) { if (_string.valid() && _string.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - std::function<void(QueryResult)> cb(std::move(callback._string)); - cb(_string.get()); - _callbacks.pop(); - if (_callbacks.empty()) - { - ASSERT(!_isPrepared ? !_string.valid() : !_prepared.valid()); - return Completed; - } - - ASSERT(_isPrepared == _callbacks.front()._isPrepared); - return NextStep; + std::future<QueryResult> f(std::move(_string)); + std::function<void(QueryCallback&, QueryResult)> cb(std::move(callback._string)); + cb(*this, f.get()); + return checkStateAndReturnCompletion(); } } else @@ -134,17 +205,9 @@ QueryCallbackNew::Status QueryCallbackNew::InvokeIfReady() if (_prepared.valid() && _prepared.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { std::future<PreparedQueryResult> f(std::move(_prepared)); - std::function<void(PreparedQueryResult)> cb(std::move(callback._prepared)); - cb(_prepared.get()); - _callbacks.pop(); - if (_callbacks.empty()) - { - ASSERT(!_isPrepared ? !_string.valid() : !_prepared.valid()); - return Completed; - } - - ASSERT(_isPrepared == _callbacks.front()._isPrepared); - return NextStep; + std::function<void(QueryCallback&, PreparedQueryResult)> cb(std::move(callback._prepared)); + cb(*this, f.get()); + return checkStateAndReturnCompletion(); } } diff --git a/src/server/database/Database/QueryCallback.h b/src/server/database/Database/QueryCallback.h index 6c4a8a81b4e..f9c93000da7 100644 --- a/src/server/database/Database/QueryCallback.h +++ b/src/server/database/Database/QueryCallback.h @@ -18,27 +18,26 @@ #ifndef _QUERY_CALLBACK_H #define _QUERY_CALLBACK_H -#include <future> #include "QueryResult.h" +#include <future> -typedef std::future<QueryResult> QueryResultFuture; -typedef std::promise<QueryResult> QueryResultPromise; -typedef std::future<PreparedQueryResult> PreparedQueryResultFuture; -typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise; - -#define CALLBACK_STAGE_INVALID uint8(-1) - -class TC_DATABASE_API QueryCallbackNew +class TC_DATABASE_API QueryCallback { public: - explicit QueryCallbackNew(std::future<QueryResult>&& result); - explicit QueryCallbackNew(std::future<PreparedQueryResult>&& result); - QueryCallbackNew(QueryCallbackNew&& right); - QueryCallbackNew& operator=(QueryCallbackNew&& right); - ~QueryCallbackNew(); + explicit QueryCallback(std::future<QueryResult>&& result); + explicit QueryCallback(std::future<PreparedQueryResult>&& result); + QueryCallback(QueryCallback&& right); + QueryCallback& operator=(QueryCallback&& right); + ~QueryCallback(); + + QueryCallback&& WithCallback(std::function<void(QueryResult)>&& callback); + QueryCallback&& WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback); + + QueryCallback&& WithChainingCallback(std::function<void(QueryCallback&, QueryResult)>&& callback); + QueryCallback&& WithChainingPreparedCallback(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback); - QueryCallbackNew&& WithCallback(std::function<void(QueryResult)>&& callback); - QueryCallbackNew&& WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback); + // Moves std::future from next to this object + void SetNextQuery(QueryCallback&& next); enum Status { @@ -50,11 +49,12 @@ public: Status InvokeIfReady(); private: - QueryCallbackNew(QueryCallbackNew const& right) = delete; - QueryCallbackNew& operator=(QueryCallbackNew const& right) = delete; + QueryCallback(QueryCallback const& right) = delete; + QueryCallback& operator=(QueryCallback const& right) = delete; - template<typename T> friend void MoveFrom(T& to, T&& from); - template<typename T> friend void DestroyActiveMember(T& obj); + template<typename T> friend void ConstructActiveMember(T* obj); + template<typename T> friend void DestroyActiveMember(T* obj); + template<typename T> friend void MoveFrom(T* to, T&& from); union { @@ -67,182 +67,4 @@ private: std::queue<QueryCallbackData, std::list<QueryCallbackData>> _callbacks; }; -template <typename Result, typename ParamType, bool chain = false> -class QueryCallback -{ - public: - QueryCallback() : _param(), _stage(chain ? 0 : CALLBACK_STAGE_INVALID) { } - - //! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery - void SetFutureResult(std::future<Result> value) - { - _result = std::move(value); - } - - std::future<Result>& GetFutureResult() - { - return _result; - } - - bool IsReady() - { - return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready; - } - - void GetResult(Result& res) - { - res = _result.get(); - } - - void FreeResult() - { - // Nothing to do here, the constructor of std::future will take care of the cleanup - } - - void SetParam(ParamType value) - { - _param = value; - } - - ParamType GetParam() - { - return _param; - } - - //! Resets the stage of the callback chain - void ResetStage() - { - if (!chain) - return; - - _stage = 0; - } - - //! Advances the callback chain to the next stage, so upper level code can act on its results accordingly - void NextStage() - { - if (!chain) - return; - - ++_stage; - } - - //! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid) - uint8 GetStage() - { - return _stage; - } - - //! Resets all underlying variables (param, result and stage) - void Reset() - { - SetParam(ParamType()); - FreeResult(); - ResetStage(); - } - - private: - std::future<Result> _result; - ParamType _param; - uint8 _stage; - - QueryCallback(QueryCallback const& right) = delete; - QueryCallback& operator=(QueryCallback const& right) = delete; -}; - -template <typename Result, typename ParamType1, typename ParamType2, bool chain = false> -class QueryCallback_2 -{ - public: - QueryCallback_2() : _stage(chain ? 0 : CALLBACK_STAGE_INVALID) { } - - //! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery - void SetFutureResult(std::future<Result> value) - { - _result = std::move(value); - } - - std::future<Result>& GetFutureResult() - { - return _result; - } - - bool IsReady() - { - return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready; - } - - void GetResult(Result& res) - { - res = _result.get(); - } - - void FreeResult() - { - // Nothing to do here, the constructor of std::future will take care of the cleanup - } - - void SetFirstParam(ParamType1 value) - { - _param_1 = value; - } - - void SetSecondParam(ParamType2 value) - { - _param_2 = value; - } - - ParamType1 GetFirstParam() - { - return _param_1; - } - - ParamType2 GetSecondParam() - { - return _param_2; - } - - //! Resets the stage of the callback chain - void ResetStage() - { - if (!chain) - return; - - _stage = 0; - } - - //! Advances the callback chain to the next stage, so upper level code can act on its results accordingly - void NextStage() - { - if (!chain) - return; - - ++_stage; - } - - //! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid) - uint8 GetStage() - { - return _stage; - } - - //! Resets all underlying variables (param, result and stage) - void Reset() - { - SetFirstParam(NULL); - SetSecondParam(NULL); - FreeResult(); - ResetStage(); - } - - private: - std::future<Result> _result; - ParamType1 _param_1; - ParamType2 _param_2; - uint8 _stage; - - QueryCallback_2(QueryCallback_2 const& right) = delete; - QueryCallback_2& operator=(QueryCallback_2 const& right) = delete; -}; - #endif // _QUERY_CALLBACK_H diff --git a/src/server/database/Database/QueryCallbackProcessor.cpp b/src/server/database/Database/QueryCallbackProcessor.cpp index 0eaa1c2352c..9f8b8fc4ad9 100644 --- a/src/server/database/Database/QueryCallbackProcessor.cpp +++ b/src/server/database/Database/QueryCallbackProcessor.cpp @@ -27,15 +27,22 @@ QueryCallbackProcessor::~QueryCallbackProcessor() { } -void QueryCallbackProcessor::AddQuery(QueryCallbackNew&& query) +void QueryCallbackProcessor::AddQuery(QueryCallback&& query) { _callbacks.emplace_back(std::move(query)); } void QueryCallbackProcessor::ProcessReadyQueries() { - _callbacks.erase(std::remove_if(_callbacks.begin(), _callbacks.end(), [](QueryCallbackNew& callback) + if (_callbacks.empty()) + return; + + std::vector<QueryCallback> updateCallbacks{ std::move(_callbacks) }; + + updateCallbacks.erase(std::remove_if(updateCallbacks.begin(), updateCallbacks.end(), [](QueryCallback& callback) { - return callback.InvokeIfReady() == QueryCallbackNew::Completed; - }), _callbacks.end()); + return callback.InvokeIfReady() == QueryCallback::Completed; + }), updateCallbacks.end()); + + _callbacks.insert(_callbacks.end(), std::make_move_iterator(updateCallbacks.begin()), std::make_move_iterator(updateCallbacks.end())); } diff --git a/src/server/database/Database/QueryCallbackProcessor.h b/src/server/database/Database/QueryCallbackProcessor.h index 08ae3d15693..0b06fd8fc27 100644 --- a/src/server/database/Database/QueryCallbackProcessor.h +++ b/src/server/database/Database/QueryCallbackProcessor.h @@ -21,7 +21,7 @@ #include "Define.h" #include <vector> -class QueryCallbackNew; +class QueryCallback; class TC_DATABASE_API QueryCallbackProcessor { @@ -29,14 +29,14 @@ public: QueryCallbackProcessor(); ~QueryCallbackProcessor(); - void AddQuery(QueryCallbackNew&& query); + void AddQuery(QueryCallback&& query); void ProcessReadyQueries(); private: QueryCallbackProcessor(QueryCallbackProcessor const&) = delete; QueryCallbackProcessor& operator=(QueryCallbackProcessor const&) = delete; - std::vector<QueryCallbackNew> _callbacks; + std::vector<QueryCallback> _callbacks; }; #endif // QueryCallbackProcessor_h__ diff --git a/src/server/game/Accounts/RBAC.cpp b/src/server/game/Accounts/RBAC.cpp index 378a66aa989..951223a9257 100644 --- a/src/server/game/Accounts/RBAC.cpp +++ b/src/server/game/Accounts/RBAC.cpp @@ -18,6 +18,7 @@ #include "RBAC.h" #include "AccountMgr.h" #include "Log.h" +#include "QueryCallback.h" namespace rbac { @@ -182,7 +183,7 @@ void RBACData::LoadFromDB() LoadFromDBCallback(LoginDatabase.Query(stmt)); } -PreparedQueryResultFuture RBACData::LoadFromDBAsync() +QueryCallback RBACData::LoadFromDBAsync() { ClearData(); diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index c998fd4274a..9b83b6492ed 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -918,7 +918,7 @@ class TC_GAME_API RBACData /// Loads all permissions assigned to current account void LoadFromDB(); - PreparedQueryResultFuture LoadFromDBAsync(); + QueryCallback LoadFromDBAsync(); void LoadFromDBCallback(PreparedQueryResult result); /// Sets security level diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index cab80d23cba..0d6d750c73f 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -35,6 +35,7 @@ #include "Pet.h" #include "PlayerDump.h" #include "Player.h" +#include "QueryCallback.h" #include "ReputationMgr.h" #include "GitRevision.h" #include "ScriptMgr.h" @@ -261,23 +262,23 @@ void WorldSession::HandleCharEnumOpcode(WorldPacket& /*recvData*/) stmt->setUInt8(0, PET_SAVE_AS_CURRENT); stmt->setUInt32(1, GetAccountId()); - _charEnumCallback = CharacterDatabase.AsyncQuery(stmt); + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleCharEnum, this, std::placeholders::_1))); } void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) { - CharacterCreateInfo createInfo; - - recvData >> createInfo.Name - >> createInfo.Race - >> createInfo.Class - >> createInfo.Gender - >> createInfo.Skin - >> createInfo.Face - >> createInfo.HairStyle - >> createInfo.HairColor - >> createInfo.FacialHair - >> createInfo.OutfitId; + std::shared_ptr<CharacterCreateInfo> createInfo = std::make_shared<CharacterCreateInfo>(); + + recvData >> createInfo->Name + >> createInfo->Race + >> createInfo->Class + >> createInfo->Gender + >> createInfo->Skin + >> createInfo->Face + >> createInfo->HairStyle + >> createInfo->HairColor + >> createInfo->FacialHair + >> createInfo->OutfitId; if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_TEAMMASK)) { @@ -285,7 +286,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) { bool disabled = false; - switch (Player::TeamForRace(createInfo.Race)) + switch (Player::TeamForRace(createInfo->Race)) { case ALLIANCE: disabled = (mask & (1 << 0)) != 0; @@ -303,18 +304,18 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } } - ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(createInfo.Class); + ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(createInfo->Class); if (!classEntry) { - TC_LOG_ERROR("network", "Class (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo.Class, GetAccountId()); + TC_LOG_ERROR("network", "Class (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo->Class, GetAccountId()); SendCharCreate(CHAR_CREATE_FAILED); return; } - ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(createInfo.Race); + ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(createInfo->Race); if (!raceEntry) { - TC_LOG_ERROR("network", "Race (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo.Race, GetAccountId()); + TC_LOG_ERROR("network", "Race (%u) not found in DBC while creating new char for account (ID: %u): wrong DBC files or cheater?", createInfo->Race, GetAccountId()); SendCharCreate(CHAR_CREATE_FAILED); return; } @@ -322,7 +323,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) // prevent character creating Expansion race without Expansion account if (raceEntry->expansion > Expansion()) { - TC_LOG_ERROR("entities.player.cheat", "Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, createInfo.Race); + TC_LOG_ERROR("entities.player.cheat", "Expansion %u account:[%d] tried to Create character with expansion %u race (%u)", Expansion(), GetAccountId(), raceEntry->expansion, createInfo->Race); SendCharCreate(CHAR_CREATE_EXPANSION); return; } @@ -330,7 +331,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) // prevent character creating Expansion class without Expansion account if (classEntry->expansion > Expansion()) { - TC_LOG_ERROR("entities.player.cheat", "Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, createInfo.Class); + TC_LOG_ERROR("entities.player.cheat", "Expansion %u account:[%d] tried to Create character with expansion %u class (%u)", Expansion(), GetAccountId(), classEntry->expansion, createInfo->Class); SendCharCreate(CHAR_CREATE_EXPANSION_CLASS); return; } @@ -338,7 +339,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RACEMASK)) { uint32 raceMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK); - if ((1 << (createInfo.Race - 1)) & raceMaskDisabled) + if ((1 << (createInfo->Race - 1)) & raceMaskDisabled) { SendCharCreate(CHAR_CREATE_DISABLED); return; @@ -348,7 +349,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_CLASSMASK)) { uint32 classMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK); - if ((1 << (createInfo.Class - 1)) & classMaskDisabled) + if ((1 << (createInfo->Class - 1)) & classMaskDisabled) { SendCharCreate(CHAR_CREATE_DISABLED); return; @@ -356,7 +357,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } // prevent character creating with invalid name - if (!normalizePlayerName(createInfo.Name)) + if (!normalizePlayerName(createInfo->Name)) { TC_LOG_ERROR("entities.player.cheat", "Account:[%d] but tried to Create character with empty [name] ", GetAccountId()); SendCharCreate(CHAR_NAME_NO_NAME); @@ -364,20 +365,20 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } // check name limitations - ResponseCodes res = ObjectMgr::CheckPlayerName(createInfo.Name, GetSessionDbcLocale(), true); + ResponseCodes res = ObjectMgr::CheckPlayerName(createInfo->Name, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { SendCharCreate(res); return; } - if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(createInfo.Name)) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(createInfo->Name)) { SendCharCreate(CHAR_NAME_RESERVED); return; } - if (createInfo.Class == CLASS_DEATH_KNIGHT && !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_HEROIC_CHARACTER)) + if (createInfo->Class == CLASS_DEATH_KNIGHT && !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_HEROIC_CHARACTER)) { // speedup check for heroic class disabled case uint32 heroic_free_slots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); @@ -397,104 +398,58 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME); - stmt->setString(0, createInfo.Name); + stmt->setString(0, createInfo->Name); - delete _charCreateCallback.GetParam(); // Delete existing if any, to make the callback chain reset to stage 0 - _charCreateCallback.SetParam(new CharacterCreateInfo(std::move(createInfo))); - _charCreateCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); -} - -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.AddQuery(CharacterDatabase.AsyncQuery(stmt) + .WithChainingPreparedCallback([this](QueryCallback& queryCallback, PreparedQueryResult result) { - case 0: + if (result) { - if (result) - { - SendCharCreate(CHAR_CREATE_NAME_IN_USE); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - - ASSERT(_charCreateCallback.GetParam() == createInfo); + SendCharCreate(CHAR_CREATE_NAME_IN_USE); + return; + } - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SUM_REALM_CHARACTERS); - stmt->setUInt32(0, GetAccountId()); + PreparedStatement* 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()); + } - _charCreateCallback.FreeResult(); - _charCreateCallback.SetFutureResult(LoginDatabase.AsyncQuery(stmt)); - _charCreateCallback.NextStage(); - break; + if (acctCharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) + { + SendCharCreate(CHAR_CREATE_ACCOUNT_LIMIT); + return; } - case 1: + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); + stmt->setUInt32(0, GetAccountId()); + queryCallback.SetNextQuery(LoginDatabase.AsyncQuery(stmt)); + }) + .WithChainingPreparedCallback([this, createInfo](QueryCallback& queryCallback, PreparedQueryResult result) + { + if (result) { - uint64 acctCharCount = 0; - if (result) - { - Field* fields = result->Fetch(); - acctCharCount = uint64(fields[0].GetDouble()); - } + 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 (acctCharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_ACCOUNT)) + if (createInfo->CharCount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) { - SendCharCreate(CHAR_CREATE_ACCOUNT_LIMIT); - delete createInfo; - _charCreateCallback.Reset(); + SendCharCreate(CHAR_CREATE_SERVER_LIMIT); 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)) - { - SendCharCreate(CHAR_CREATE_SERVER_LIMIT); - delete createInfo; - _charCreateCallback.Reset(); - return; - } - } - - bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || HasPermission(rbac::RBAC_PERM_TWO_SIDE_CHARACTER_CREATION); - uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); - - _charCreateCallback.FreeResult(); + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || HasPermission(rbac::RBAC_PERM_TWO_SIDE_CHARACTER_CREATION); + uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); - if (!allowTwoSideAccounts || skipCinematics == 1 || 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; - } - - _charCreateCallback.NextStage(); - HandleCharCreateCallback(PreparedQueryResult(NULL), createInfo); // Will jump to case 3 - break; - } - case 3: + std::function<void(PreparedQueryResult)> finalizeCharacterCreation = [this, createInfo](PreparedQueryResult result) { bool haveSameRace = false; uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); @@ -509,7 +464,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte uint32 freeHeroicSlots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); Field* field = result->Fetch(); - uint8 accRace = field[1].GetUInt8(); + uint8 accRace = field[1].GetUInt8(); if (checkHeroicReqs) { @@ -522,8 +477,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (freeHeroicSlots == 0) { SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); - delete createInfo; - _charCreateCallback.Reset(); return; } } @@ -547,8 +500,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (accTeam != team) { SendCharCreate(CHAR_CREATE_PVP_TEAMS_VIOLATION); - delete createInfo; - _charCreateCallback.Reset(); return; } } @@ -577,8 +528,6 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (freeHeroicSlots == 0) { SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); - delete createInfo; - _charCreateCallback.Reset(); return; } } @@ -596,22 +545,18 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte if (checkHeroicReqs && !hasHeroicReqLevel) { SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT); - delete createInfo; - _charCreateCallback.Reset(); return; } Player newChar(this); newChar.GetMotionMaster()->Initialize(); - if (!newChar.Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(), createInfo)) + if (!newChar.Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(), createInfo.get())) { // Player not create (race/class/etc problem?) newChar.CleanupsBeforeDelete(); SendCharCreate(CHAR_CREATE_ERROR); - delete createInfo; - _charCreateCallback.Reset(); return; } @@ -620,7 +565,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte newChar.SetAtLoginFlag(AT_LOGIN_FIRST); // First login - // Player created, save it now + // Player created, save it now newChar.SaveToDB(true); createInfo->CharCount += 1; @@ -646,11 +591,19 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER), newChar.getRace(), newChar.getClass(), newChar.getLevel()); newChar.CleanupsBeforeDelete(); - delete createInfo; - _charCreateCallback.Reset(); - break; + }; + + if (allowTwoSideAccounts && !skipCinematics && createInfo->Class != CLASS_DEATH_KNIGHT) + { + finalizeCharacterCreation(PreparedQueryResult(nullptr)); + return; } - } + + 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); + queryCallback.WithPreparedCallback(std::move(finalizeCharacterCreation)).SetNextQuery(CharacterDatabase.AsyncQuery(stmt)); + })); } void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) @@ -1084,29 +1037,29 @@ void WorldSession::HandleShowingCloakOpcode(WorldPacket& recvData) void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) { - CharacterRenameInfo renameInfo; + std::shared_ptr<CharacterRenameInfo> renameInfo = std::make_shared<CharacterRenameInfo>(); - recvData >> renameInfo.Guid - >> renameInfo.Name; + recvData >> renameInfo->Guid + >> renameInfo->Name; // prevent character rename to invalid name - if (!normalizePlayerName(renameInfo.Name)) + if (!normalizePlayerName(renameInfo->Name)) { - SendCharRename(CHAR_NAME_NO_NAME, renameInfo); + SendCharRename(CHAR_NAME_NO_NAME, renameInfo.get()); return; } - ResponseCodes res = ObjectMgr::CheckPlayerName(renameInfo.Name, GetSessionDbcLocale(), true); + ResponseCodes res = ObjectMgr::CheckPlayerName(renameInfo->Name, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { - SendCharRename(res, renameInfo); + SendCharRename(res, renameInfo.get()); return; } // check name limitations - if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(renameInfo.Name)) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(renameInfo->Name)) { - SendCharRename(CHAR_NAME_RESERVED, renameInfo); + SendCharRename(CHAR_NAME_RESERVED, renameInfo.get()); return; } @@ -1114,35 +1067,43 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) // and that there is no character with the desired new name PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_FREE_NAME); - stmt->setUInt32(0, renameInfo.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, renameInfo.Name); + stmt->setString(2, renameInfo->Name); - delete _charRenameCallback.GetParam(); - _charRenameCallback.SetParam(new CharacterRenameInfo(std::move(renameInfo))); - _charRenameCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleCharRenameCallback, this, renameInfo, std::placeholders::_1))); } -void WorldSession::HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult result, CharacterRenameInfo const* renameInfo) +void WorldSession::HandleCharRenameCallback(std::shared_ptr<CharacterRenameInfo> renameInfo, PreparedQueryResult result) { if (!result) { - SendCharRename(CHAR_CREATE_ERROR, *renameInfo); + SendCharRename(CHAR_CREATE_ERROR, renameInfo.get()); return; } Field* fields = result->Fetch(); - ObjectGuid::LowType guidLow = fields[0].GetUInt32(); + ObjectGuid::LowType guidLow = fields[0].GetUInt32(); std::string oldName = fields[1].GetString(); + uint16 atLoginFlags = fields[2].GetUInt16(); + + if (!(atLoginFlags & AT_LOGIN_RENAME)) + { + SendCharRename(CHAR_CREATE_ERROR, renameInfo.get()); + return; + } + + atLoginFlags &= ~AT_LOGIN_RENAME; + + SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Update name and at_login flag in the db - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_NAME); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); stmt->setString(0, renameInfo->Name); - stmt->setUInt16(1, AT_LOGIN_RENAME); + stmt->setUInt16(1, atLoginFlags); stmt->setUInt32(2, guidLow); CharacterDatabase.Execute(stmt); @@ -1156,7 +1117,7 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult resu TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s) Character:[%s] (%s) Changed name to: %s", GetAccountId(), GetRemoteAddress().c_str(), oldName.c_str(), renameInfo->Guid.ToString().c_str(), renameInfo->Name.c_str()); - SendCharRename(RESPONSE_SUCCESS, *renameInfo); + SendCharRename(RESPONSE_SUCCESS, renameInfo.get()); sWorld->UpdateCharacterInfo(renameInfo->Guid, renameInfo->Name); } @@ -1331,131 +1292,131 @@ void WorldSession::HandleRemoveGlyph(WorldPacket& recvData) void WorldSession::HandleCharCustomize(WorldPacket& recvData) { - CharacterCustomizeInfo customizeInfo; + std::shared_ptr<CharacterCustomizeInfo> customizeInfo = std::make_shared<CharacterCustomizeInfo>(); - recvData >> customizeInfo.Guid; - if (!IsLegitCharacterForAccount(customizeInfo.Guid)) + recvData >> customizeInfo->Guid; + if (!IsLegitCharacterForAccount(customizeInfo->Guid)) { TC_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()); + GetAccountId(), GetRemoteAddress().c_str(), customizeInfo->Guid.ToString().c_str()); recvData.rfinish(); KickPlayer(); return; } - recvData >> customizeInfo.Name - >> customizeInfo.Gender - >> customizeInfo.Skin - >> customizeInfo.HairColor - >> customizeInfo.HairStyle - >> customizeInfo.FacialHair - >> customizeInfo.Face; + recvData >> customizeInfo->Name + >> customizeInfo->Gender + >> customizeInfo->Skin + >> customizeInfo->HairColor + >> customizeInfo->HairStyle + >> customizeInfo->FacialHair + >> customizeInfo->Face; + + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO); + stmt->setUInt32(0, customizeInfo->Guid.GetCounter()); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME_DATA); - stmt->setUInt32(0, customizeInfo.Guid.GetCounter()); - PreparedQueryResult result = CharacterDatabase.Query(stmt); + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleCharCustomizeCallback, this, customizeInfo, std::placeholders::_1))); +} +void WorldSession::HandleCharCustomizeCallback(std::shared_ptr<CharacterCustomizeInfo> customizeInfo, PreparedQueryResult result) +{ if (!result) { - SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo); + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo.get()); return; } Field* fields = result->Fetch(); - uint8 plrRace = fields[0].GetUInt8(); - uint8 plrClass = fields[1].GetUInt8(); - uint8 plrGender = fields[2].GetUInt8(); - std::string oldName = fields[4].GetString(); + std::string oldName = fields[0].GetString(); + uint8 plrRace = fields[1].GetUInt8(); + uint8 plrClass = fields[2].GetUInt8(); + uint8 plrGender = fields[3].GetUInt8(); + uint16 atLoginFlags = fields[4].GetUInt16(); - if (!Player::ValidateAppearance(plrRace, plrClass, plrGender, customizeInfo.HairStyle, customizeInfo.HairColor, customizeInfo.Face, customizeInfo.FacialHair, customizeInfo.Skin, true)) + if (!Player::ValidateAppearance(plrRace, plrClass, plrGender, customizeInfo->HairStyle, customizeInfo->HairColor, customizeInfo->Face, customizeInfo->FacialHair, customizeInfo->Skin, true)) { - SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo); + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo.get()); return; } - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_AT_LOGIN); - - stmt->setUInt32(0, customizeInfo.Guid.GetCounter()); - // TODO: Make async with callback - result = CharacterDatabase.Query(stmt); - - if (!result) + if (!(atLoginFlags & AT_LOGIN_CUSTOMIZE)) { - SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo); + SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo.get()); return; } - fields = result->Fetch(); - uint32 at_loginFlags = fields[0].GetUInt16(); - - if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE)) - { - SendCharCustomize(CHAR_CREATE_ERROR, customizeInfo); - return; - } + atLoginFlags &= ~AT_LOGIN_CUSTOMIZE; // prevent character rename - if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (customizeInfo.Name != oldName)) + if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (customizeInfo->Name != oldName)) { - SendCharCustomize(CHAR_NAME_FAILURE, customizeInfo); + SendCharCustomize(CHAR_NAME_FAILURE, customizeInfo.get()); return; } // prevent character rename to invalid name - if (!normalizePlayerName(customizeInfo.Name)) + if (!normalizePlayerName(customizeInfo->Name)) { - SendCharCustomize(CHAR_NAME_NO_NAME, customizeInfo); + SendCharCustomize(CHAR_NAME_NO_NAME, customizeInfo.get()); return; } - ResponseCodes res = ObjectMgr::CheckPlayerName(customizeInfo.Name, GetSessionDbcLocale(), true); + ResponseCodes res = ObjectMgr::CheckPlayerName(customizeInfo->Name, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { - SendCharCustomize(res, customizeInfo); + SendCharCustomize(res, customizeInfo.get()); return; } // check name limitations - if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(customizeInfo.Name)) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(customizeInfo->Name)) { - SendCharCustomize(CHAR_NAME_RESERVED, customizeInfo); + SendCharCustomize(CHAR_NAME_RESERVED, customizeInfo.get()); return; } // character with this name already exist - if (ObjectGuid newGuid = sObjectMgr->GetPlayerGUIDByName(customizeInfo.Name)) + if (ObjectGuid newGuid = sObjectMgr->GetPlayerGUIDByName(customizeInfo->Name)) { - if (newGuid != customizeInfo.Guid) + if (newGuid != customizeInfo->Guid) { - SendCharCustomize(CHAR_CREATE_NAME_IN_USE, customizeInfo); + SendCharCustomize(CHAR_CREATE_NAME_IN_USE, customizeInfo.get()); return; } } + PreparedStatement* stmt = nullptr; SQLTransaction trans = CharacterDatabase.BeginTransaction(); - Player::Customize(&customizeInfo, trans); + ObjectGuid::LowType lowGuid = customizeInfo->Guid.GetCounter(); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); - stmt->setString(0, customizeInfo.Name); - stmt->setUInt16(1, uint16(AT_LOGIN_CUSTOMIZE)); - stmt->setUInt32(2, customizeInfo.Guid.GetCounter()); + /// Customize + Player::Customize(customizeInfo.get(), trans); - trans->Append(stmt); + /// Name Change and update atLogin flags + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN); + stmt->setString(0, customizeInfo->Name); + stmt->setUInt16(1, atLoginFlags); + stmt->setUInt32(2, lowGuid); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_DECLINED_NAME); - stmt->setUInt32(0, customizeInfo.Guid.GetCounter()); + trans->Append(stmt); - trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_DECLINED_NAME); + stmt->setUInt32(0, lowGuid); + + trans->Append(stmt); + } CharacterDatabase.CommitTransaction(trans); - sWorld->UpdateCharacterInfo(customizeInfo.Guid, customizeInfo.Name, customizeInfo.Gender); + sWorld->UpdateCharacterInfo(customizeInfo->Guid, customizeInfo->Name, customizeInfo->Gender); - SendCharCustomize(RESPONSE_SUCCESS, customizeInfo); + SendCharCustomize(RESPONSE_SUCCESS, customizeInfo.get()); TC_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()); + GetAccountId(), GetRemoteAddress().c_str(), oldName.c_str(), customizeInfo->Guid.ToString().c_str(), customizeInfo->Name.c_str()); } void WorldSession::HandleEquipmentSetSave(WorldPacket& recvData) @@ -1585,157 +1546,174 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket& recvData) void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) { - CharacterFactionChangeInfo factionChangeInfo; - recvData >> factionChangeInfo.Guid; + std::shared_ptr<CharacterFactionChangeInfo> factionChangeInfo = std::make_shared<CharacterFactionChangeInfo>(); + recvData >> factionChangeInfo->Guid; - if (!IsLegitCharacterForAccount(factionChangeInfo.Guid)) + if (!IsLegitCharacterForAccount(factionChangeInfo->Guid)) { TC_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()); + GetAccountId(), GetRemoteAddress().c_str(), factionChangeInfo->Guid.ToString().c_str()); recvData.rfinish(); KickPlayer(); return; } - recvData >> factionChangeInfo.Name - >> factionChangeInfo.Gender - >> factionChangeInfo.Skin - >> factionChangeInfo.HairColor - >> factionChangeInfo.HairStyle - >> factionChangeInfo.FacialHair - >> factionChangeInfo.Face - >> factionChangeInfo.Race; + recvData >> factionChangeInfo->Name + >> factionChangeInfo->Gender + >> factionChangeInfo->Skin + >> factionChangeInfo->HairColor + >> factionChangeInfo->HairStyle + >> factionChangeInfo->FacialHair + >> factionChangeInfo->Face + >> factionChangeInfo->Race; - ObjectGuid::LowType lowGuid = factionChangeInfo.Guid.GetCounter(); + factionChangeInfo->FactionChange = (recvData.GetOpcode() == CMSG_CHAR_FACTION_CHANGE); - // get the players old (at this moment current) race - CharacterInfo const* nameData = sWorld->GetCharacterInfo(factionChangeInfo.Guid); - if (!nameData) + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS); + stmt->setUInt32(0, factionChangeInfo->Guid.GetCounter()); + + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleCharFactionOrRaceChangeCallback, this, factionChangeInfo, std::placeholders::_1))); +} + +void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr<CharacterFactionChangeInfo> factionChangeInfo, PreparedQueryResult result) +{ + if (!result) { - SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } - std::string oldName = nameData->Name; - uint8 oldRace = nameData->Race; - uint8 playerClass = nameData->Class; - uint8 level = nameData->Level; + // get the players old (at this moment current) race + CharacterInfo const* characterInfo = sWorld->GetCharacterInfo(factionChangeInfo->Guid); + if (!characterInfo) + { + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); + return; + } - // TO Do: Make async - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES); - stmt->setUInt32(0, lowGuid); - PreparedQueryResult result = CharacterDatabase.Query(stmt); + uint8 oldRace = characterInfo->Race; + uint8 playerClass = characterInfo->Class; + uint8 level = characterInfo->Level; - if (!result) + if (!sObjectMgr->GetPlayerInfo(factionChangeInfo->Race, playerClass)) { - SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } - Field* fields = result->Fetch(); - uint32 at_loginFlags = fields[0].GetUInt16(); + Field* fields = result->Fetch(); + uint32 atLoginFlags = fields[0].GetUInt16(); std::string knownTitlesStr = fields[1].GetString(); - uint32 used_loginFlag = ((recvData.GetOpcode() == CMSG_CHAR_RACE_CHANGE) ? AT_LOGIN_CHANGE_RACE : AT_LOGIN_CHANGE_FACTION); - if (!sObjectMgr->GetPlayerInfo(factionChangeInfo.Race, playerClass)) + uint32 usedLoginFlag = (factionChangeInfo->FactionChange ? AT_LOGIN_CHANGE_FACTION : AT_LOGIN_CHANGE_RACE); + if (!(atLoginFlags & usedLoginFlag)) { - SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } - if (!(at_loginFlags & used_loginFlag)) + uint32 newTeam = Player::TeamForRace(factionChangeInfo->Race); + if (factionChangeInfo->FactionChange == (Player::TeamForRace(oldRace) == newTeam)) { - SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo); + SendCharFactionChange(factionChangeInfo->FactionChange ? CHAR_CREATE_CHARACTER_SWAP_FACTION : CHAR_CREATE_CHARACTER_RACE_ONLY, factionChangeInfo.get()); return; } if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RACEMASK)) { uint32 raceMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK); - if ((1 << (factionChangeInfo.Race - 1)) & raceMaskDisabled) + if ((1 << (factionChangeInfo->Race - 1)) & raceMaskDisabled) { - SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } } // prevent character rename - if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (factionChangeInfo.Name != oldName)) + if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (factionChangeInfo->Name != characterInfo->Name)) { - SendCharFactionChange(CHAR_NAME_FAILURE, factionChangeInfo); + SendCharFactionChange(CHAR_NAME_FAILURE, factionChangeInfo.get()); return; } // prevent character rename to invalid name - if (!normalizePlayerName(factionChangeInfo.Name)) + if (!normalizePlayerName(factionChangeInfo->Name)) { - SendCharFactionChange(CHAR_NAME_NO_NAME, factionChangeInfo); + SendCharFactionChange(CHAR_NAME_NO_NAME, factionChangeInfo.get()); return; } - ResponseCodes res = ObjectMgr::CheckPlayerName(factionChangeInfo.Name, GetSessionDbcLocale(), true); + ResponseCodes res = ObjectMgr::CheckPlayerName(factionChangeInfo->Name, GetSessionDbcLocale(), true); if (res != CHAR_NAME_SUCCESS) { - SendCharFactionChange(res, factionChangeInfo); + SendCharFactionChange(res, factionChangeInfo.get()); return; } // check name limitations - if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(factionChangeInfo.Name)) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) && sObjectMgr->IsReservedName(factionChangeInfo->Name)) { - SendCharFactionChange(CHAR_NAME_RESERVED, factionChangeInfo); + SendCharFactionChange(CHAR_NAME_RESERVED, factionChangeInfo.get()); return; } // character with this name already exist - if (ObjectGuid newGuid = sObjectMgr->GetPlayerGUIDByName(factionChangeInfo.Name)) + ObjectGuid newGuid = sObjectMgr->GetPlayerGUIDByName(factionChangeInfo->Name); + if (!newGuid.IsEmpty()) { - if (newGuid != factionChangeInfo.Guid) + if (newGuid != factionChangeInfo->Guid) { - SendCharFactionChange(CHAR_CREATE_NAME_IN_USE, factionChangeInfo); + SendCharFactionChange(CHAR_CREATE_NAME_IN_USE, factionChangeInfo.get()); return; } } + if (sArenaTeamMgr->GetArenaTeamByCaptain(factionChangeInfo->Guid)) + { + SendCharFactionChange(CHAR_CREATE_CHARACTER_ARENA_LEADER, factionChangeInfo.get()); + return; + } + + // All checks are fine, deal with race change now + ObjectGuid::LowType lowGuid = factionChangeInfo->Guid.GetCounter(); + + PreparedStatement* stmt = nullptr; SQLTransaction trans = CharacterDatabase.BeginTransaction(); // resurrect the character in case he's dead - Player::OfflineResurrect(factionChangeInfo.Guid, trans); + Player::OfflineResurrect(factionChangeInfo->Guid, trans); - CharacterDatabase.EscapeString(factionChangeInfo.Name); - Player::Customize(&factionChangeInfo, trans); + // Name Change and update atLogin flags + { + CharacterDatabase.EscapeString(factionChangeInfo->Name); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_FACTION_OR_RACE); - stmt->setString(0, factionChangeInfo.Name); - stmt->setUInt8(1, factionChangeInfo.Race); - stmt->setUInt16(2, used_loginFlag); - stmt->setUInt32(3, 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); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME); - stmt->setUInt32(0, lowGuid); - trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_DECLINED_NAME); + stmt->setUInt32(0, lowGuid); + trans->Append(stmt); + } - sWorld->UpdateCharacterInfo(factionChangeInfo.Guid, factionChangeInfo.Name, factionChangeInfo.Gender, factionChangeInfo.Race); + // Customize + Player::Customize(factionChangeInfo.get(), trans); - if (oldRace != factionChangeInfo.Race) + // Race Change { - TeamId team = TEAM_ALLIANCE; + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_RACE); + stmt->setUInt8(0, factionChangeInfo->Race); + stmt->setUInt32(1, lowGuid); + trans->Append(stmt); + } - // Search each faction is targeted - switch (factionChangeInfo.Race) - { - case RACE_ORC: - case RACE_TAUREN: - case RACE_UNDEAD_PLAYER: - case RACE_TROLL: - case RACE_BLOODELF: - team = TEAM_HORDE; - break; - default: - break; - } + sWorld->UpdateCharacterInfo(factionChangeInfo->Guid, factionChangeInfo->Name, factionChangeInfo->Gender, factionChangeInfo->Race); + if (oldRace != factionChangeInfo->Race) + { // Switch Languages // delete all languages first stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SKILL_LANGUAGES); @@ -1747,7 +1725,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) stmt->setUInt32(0, lowGuid); // Faction specific languages - if (team == TEAM_HORDE) + if (newTeam == HORDE) stmt->setUInt16(1, 109); else stmt->setUInt16(1, 98); @@ -1755,12 +1733,12 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) trans->Append(stmt); // Race specific languages - if (factionChangeInfo.Race != RACE_ORC && factionChangeInfo.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 (factionChangeInfo.Race) + switch (factionChangeInfo->Race) { case RACE_DWARF: stmt->setUInt16(1, 111); @@ -1791,7 +1769,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) trans->Append(stmt); } - if (recvData.GetOpcode() == CMSG_CHAR_FACTION_CHANGE) + if (factionChangeInfo->FactionChange) { // Delete all Flypaths stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_TAXI_PATH); @@ -1806,41 +1784,21 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) uint32 numFullTaximasks = level / 7; if (numFullTaximasks > 11) numFullTaximasks = 11; - if (team == TEAM_ALLIANCE) - { - 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 + + TaxiMask const& factionMask = newTeam == HORDE ? sHordeTaxiNodesMask : sAllianceTaxiNodesMask; + for (uint8 i = 0; i < numFullTaximasks; ++i) { - 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); } @@ -1853,9 +1811,9 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) stmt->setUInt32(0, lowGuid); if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) if (Guild* guild = sGuildMgr->GetGuildById((result->Fetch()[0]).GetUInt32())) - guild->DeleteMember(trans, factionChangeInfo.Guid, false, false, true); + guild->DeleteMember(trans, factionChangeInfo->Guid, false, false, true); - Player::LeaveAllArenaTeams(factionChangeInfo.Guid); + Player::LeaveAllArenaTeams(factionChangeInfo->Guid); } if (!HasPermission(rbac::RBAC_PERM_TWO_SIDE_ADD_FRIEND)) @@ -1880,7 +1838,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) WorldLocation loc; uint16 zoneId = 0; - if (team == TEAM_ALLIANCE) + if (newTeam == ALLIANCE) { loc.WorldRelocate(0, -8867.68f, 673.373f, 97.9034f, 0.0f); zoneId = 1519; @@ -1898,7 +1856,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) stmt->setFloat(5, loc.GetPositionZ()); trans->Append(stmt); - Player::SavePositionInDB(loc, zoneId, factionChangeInfo.Guid, trans); + Player::SavePositionInDB(loc, zoneId, factionChangeInfo->Guid, trans); // Achievement conversion for (std::map<uint32, uint32>::const_iterator it = sObjectMgr->FactionChangeAchievements.begin(); it != sObjectMgr->FactionChangeAchievements.end(); ++it) @@ -1907,13 +1865,13 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) uint32 achiev_horde = it->second; 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 == 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 == ALLIANCE ? achiev_alliance : achiev_horde)); + stmt->setUInt16(1, uint16(newTeam == ALLIANCE ? achiev_horde : achiev_alliance)); stmt->setUInt32(2, lowGuid); trans->Append(stmt); } @@ -1925,8 +1883,8 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) uint32 item_horde = it->second; stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INVENTORY_FACTION_CHANGE); - stmt->setUInt32(0, (team == TEAM_ALLIANCE ? item_alliance : item_horde)); - stmt->setUInt32(1, (team == TEAM_ALLIANCE ? item_horde : item_alliance)); + stmt->setUInt32(0, (newTeam == ALLIANCE ? item_alliance : item_horde)); + stmt->setUInt32(1, (newTeam == ALLIANCE ? item_horde : item_alliance)); stmt->setUInt32(2, lowGuid); trans->Append(stmt); } @@ -1944,12 +1902,12 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) 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 == 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 == ALLIANCE ? quest_alliance : quest_horde)); + stmt->setUInt32(1, (newTeam == ALLIANCE ? quest_horde : quest_alliance)); stmt->setUInt32(2, lowGuid); trans->Append(stmt); } @@ -1965,7 +1923,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) for (ObjectMgr::QuestMap::const_iterator iter = questTemplates.begin(); iter != questTemplates.end(); ++iter) { Quest const* quest = iter->second; - uint32 newRaceMask = (team == TEAM_ALLIANCE) ? RACEMASK_ALLIANCE : RACEMASK_HORDE; + uint32 newRaceMask = (newTeam == ALLIANCE) ? RACEMASK_ALLIANCE : RACEMASK_HORDE; if (!(quest->GetAllowableRaces() & newRaceMask)) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST); @@ -1983,13 +1941,13 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) uint32 spell_horde = it->second; stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_BY_SPELL); - stmt->setUInt32(0, (team == TEAM_ALLIANCE ? spell_alliance : spell_horde)); + stmt->setUInt32(0, (newTeam == ALLIANCE ? spell_alliance : spell_horde)); stmt->setUInt32(1, lowGuid); 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 == ALLIANCE ? spell_alliance : spell_horde)); + stmt->setUInt32(1, (newTeam == ALLIANCE ? spell_horde : spell_alliance)); stmt->setUInt32(2, lowGuid); trans->Append(stmt); } @@ -1999,8 +1957,8 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) { 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 == ALLIANCE) ? reputation_alliance : reputation_horde; + uint32 oldReputation = (newTeam == ALLIANCE) ? reputation_horde : reputation_alliance; // select old standing set in db stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_REP_BY_FACTION); @@ -2017,7 +1975,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) int32 oldBaseRep = sObjectMgr->GetBaseReputationOf(factionEntry, oldRace, playerClass); // new base reputation - int32 newBaseRep = sObjectMgr->GetBaseReputationOf(sFactionStore.LookupEntry(newReputation), factionChangeInfo.Race, playerClass); + int32 newBaseRep = sObjectMgr->GetBaseReputationOf(sFactionStore.LookupEntry(newReputation), factionChangeInfo->Race, playerClass); // final reputation shouldnt change int32 FinalRep = oldDBRep + oldBaseRep; @@ -2040,13 +1998,13 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) // Title conversion if (!knownTitlesStr.empty()) { - const uint32 ktcount = KNOWN_TITLES_SIZE * 2; + uint32 const ktcount = KNOWN_TITLES_SIZE * 2; uint32 knownTitles[ktcount]; Tokenizer tokens(knownTitlesStr, ' ', ktcount); if (tokens.size() != ktcount) { - SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo); + SendCharFactionChange(CHAR_CREATE_ERROR, factionChangeInfo.get()); return; } @@ -2061,7 +2019,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) CharTitlesEntry const* atitleInfo = sCharTitlesStore.LookupEntry(title_alliance); CharTitlesEntry const* htitleInfo = sCharTitlesStore.LookupEntry(title_horde); // new team - if (team == TEAM_ALLIANCE) + if (newTeam == ALLIANCE) { uint32 bitIndex = htitleInfo->bit_index; uint32 index = bitIndex / 32; @@ -2108,9 +2066,9 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) CharacterDatabase.CommitTransaction(trans); - TC_LOG_DEBUG("entities.player", "%s (IP: %s) changed race from %u to %u", GetPlayerInfo().c_str(), GetRemoteAddress().c_str(), oldRace, factionChangeInfo.Race); + TC_LOG_DEBUG("entities.player", "%s (IP: %s) changed race from %u to %u", GetPlayerInfo().c_str(), GetRemoteAddress().c_str(), oldRace, factionChangeInfo->Race); - SendCharFactionChange(RESPONSE_SUCCESS, factionChangeInfo); + SendCharFactionChange(RESPONSE_SUCCESS, factionChangeInfo.get()); } void WorldSession::SendCharCreate(ResponseCodes result) @@ -2127,51 +2085,51 @@ void WorldSession::SendCharDelete(ResponseCodes result) SendPacket(&data); } -void WorldSession::SendCharRename(ResponseCodes result, CharacterRenameInfo const& renameInfo) +void WorldSession::SendCharRename(ResponseCodes result, CharacterRenameInfo const* renameInfo) { - WorldPacket data(SMSG_CHAR_RENAME, 1 + 8 + renameInfo.Name.size() + 1); + WorldPacket data(SMSG_CHAR_RENAME, 1 + 8 + renameInfo->Name.size() + 1); data << uint8(result); if (result == RESPONSE_SUCCESS) { - data << renameInfo.Guid; - data << renameInfo.Name; + data << renameInfo->Guid; + data << renameInfo->Name; } SendPacket(&data); } -void WorldSession::SendCharCustomize(ResponseCodes result, CharacterCustomizeInfo const& customizeInfo) +void WorldSession::SendCharCustomize(ResponseCodes result, CharacterCustomizeInfo const* customizeInfo) { - WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1 + 8 + customizeInfo.Name.size() + 1 + 6); + 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); + 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::SendCharFactionChange(ResponseCodes result, CharacterFactionChangeInfo const& factionChangeInfo) +void WorldSession::SendCharFactionChange(ResponseCodes result, CharacterFactionChangeInfo const* factionChangeInfo) { - WorldPacket data(SMSG_CHAR_FACTION_CHANGE, 1 + 8 + factionChangeInfo.Name.size() + 1 + 7); + 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); + 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); } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 7290acdbe43..055ad830f7b 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -19,6 +19,7 @@ #include "Common.h" #include "Language.h" #include "DatabaseEnv.h" +#include "QueryCallback.h" #include "WorldPacket.h" #include "WorldSession.h" #include "Opcodes.h" @@ -469,11 +470,10 @@ void WorldSession::SendStablePet(ObjectGuid guid) 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.AddQuery(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; @@ -571,7 +571,7 @@ void WorldSession::HandleStablePet(WorldPacket& recvData) stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT); stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT); - _stablePetCallback = CharacterDatabase.AsyncQuery(stmt); + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1))); } void WorldSession::HandleStablePetCallback(PreparedQueryResult result) @@ -633,11 +633,10 @@ void WorldSession::HandleUnstablePet(WorldPacket& recvData) 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.AddQuery(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; @@ -760,11 +759,10 @@ void WorldSession::HandleStableSwapPet(WorldPacket& recvData) stmt->setUInt32(0, _player->GetGUID().GetCounter()); stmt->setUInt32(1, petId); - _stableSwapCallback.SetParam(petId); - _stableSwapCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddQuery(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; diff --git a/src/server/game/Handlers/SocialHandler.cpp b/src/server/game/Handlers/SocialHandler.cpp index cd9968bf84e..2b13cfdbd9b 100644 --- a/src/server/game/Handlers/SocialHandler.cpp +++ b/src/server/game/Handlers/SocialHandler.cpp @@ -18,6 +18,7 @@ #include "WorldSession.h" #include "Player.h" +#include "QueryCallback.h" #include "SocialMgr.h" #include "ObjectMgr.h" @@ -42,11 +43,11 @@ void WorldSession::HandleAddFriendOpcode(WorldPacket& recvData) PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_RACE_ACC_BY_NAME); stmt->setString(0, friendName); - _addFriendCallback.SetParam(friendNote); - _addFriendCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt) + .WithPreparedCallback(std::bind(&WorldSession::HandleAddFriendOpcodeCallback, this, std::move(friendNote), std::placeholders::_1))); } -void WorldSession::HandleAddFriendOpcodeCallback(PreparedQueryResult result, std::string const& friendNote) +void WorldSession::HandleAddFriendOpcodeCallback(std::string const& friendNote, PreparedQueryResult result) { if (!GetPlayer()) return; @@ -116,7 +117,7 @@ void WorldSession::HandleAddIgnoreOpcode(WorldPacket& recvData) PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUID_BY_NAME); stmt->setString(0, ignoreName); - _addIgnoreCallback = CharacterDatabase.AsyncQuery(stmt); + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleAddIgnoreOpcodeCallback, this, std::placeholders::_1))); } void WorldSession::HandleAddIgnoreOpcodeCallback(PreparedQueryResult result) diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index a7dc3685171..4664d2efb8f 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -24,6 +24,7 @@ #include "Config.h" #include "Common.h" #include "DatabaseEnv.h" +#include "QueryCallback.h" #include "AccountMgr.h" #include "Log.h" #include "Opcodes.h" @@ -137,7 +138,6 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldS LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query } - InitializeQueryCallbackParameters(); } /// WorldSession destructor @@ -1085,99 +1085,16 @@ 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 NULL here. - _charCreateCallback.SetParam(NULL); -} - void WorldSession::ProcessQueryCallbacks() { - PreparedQueryResult result; + _queryProcessor.ProcessReadyQueries(); if (_realmAccountLoginCallback.valid() && _realmAccountLoginCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) InitializeSessionCallback(_realmAccountLoginCallback.get()); - //! HandleCharEnumOpcode - if (_charEnumCallback.valid() && _charEnumCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - result = _charEnumCallback.get(); - HandleCharEnum(result); - } - - if (_charCreateCallback.IsReady()) - { - _charCreateCallback.GetResult(result); - HandleCharCreateCallback(result, _charCreateCallback.GetParam()); - } - //! HandlePlayerLoginOpcode if (_charLoginCallback.valid() && _charLoginCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - SQLQueryHolder* param = _charLoginCallback.get(); - HandlePlayerLogin((LoginQueryHolder*)param); - } - - //! HandleAddFriendOpcode - if (_addFriendCallback.IsReady()) - { - std::string param = _addFriendCallback.GetParam(); - _addFriendCallback.GetResult(result); - HandleAddFriendOpcodeCallback(result, param); - _addFriendCallback.FreeResult(); - } - - //- HandleCharRenameOpcode - if (_charRenameCallback.IsReady()) - { - _charRenameCallback.GetResult(result); - CharacterRenameInfo* renameInfo = _charRenameCallback.GetParam(); - HandleChangePlayerNameOpcodeCallBack(result, renameInfo); - delete renameInfo; - _charRenameCallback.Reset(); - } - - //- HandleCharAddIgnoreOpcode - if (_addIgnoreCallback.valid() && _addIgnoreCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - result = _addIgnoreCallback.get(); - HandleAddIgnoreOpcodeCallback(result); - } - - //- SendStabledPet - if (_sendStabledPetCallback.IsReady()) - { - ObjectGuid param = _sendStabledPetCallback.GetParam(); - _sendStabledPetCallback.GetResult(result); - SendStablePetCallback(result, param); - _sendStabledPetCallback.FreeResult(); - } - - //- HandleStablePet - if (_stablePetCallback.valid() && _stablePetCallback.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - result = _stablePetCallback.get(); - HandleStablePetCallback(result); - } - - //- HandleUnstablePet - if (_unstablePetCallback.IsReady()) - { - uint32 param = _unstablePetCallback.GetParam(); - _unstablePetCallback.GetResult(result); - HandleUnstablePetCallback(result, param); - _unstablePetCallback.FreeResult(); - } - - //- HandleStableSwapPet - if (_stableSwapCallback.IsReady()) - { - uint32 param = _stableSwapCallback.GetParam(); - _stableSwapCallback.GetResult(result); - HandleStableSwapPetCallback(result, param); - _stableSwapCallback.FreeResult(); - } + HandlePlayerLogin(reinterpret_cast<LoginQueryHolder*>(_charLoginCallback.get())); } void WorldSession::InitWarden(BigNumber* k, std::string const& os) @@ -1204,7 +1121,7 @@ void WorldSession::LoadPermissions() _RBACData->LoadFromDB(); } -PreparedQueryResultFuture WorldSession::LoadPermissionsAsync() +QueryCallback WorldSession::LoadPermissionsAsync() { uint32 id = GetAccountId(); uint8 secLevel = GetSecurity(); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index d4320923a59..a95aa75cccf 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -239,6 +239,7 @@ struct CharacterFactionChangeInfo : public CharacterCustomizeInfo protected: uint8 Race = 0; + bool FactionChange = false; }; struct PacketCounter @@ -284,7 +285,7 @@ class TC_GAME_API WorldSession rbac::RBACData* GetRBACData(); bool HasPermission(uint32 permissionId); void LoadPermissions(); - PreparedQueryResultFuture LoadPermissionsAsync(); + QueryCallback LoadPermissionsAsync(); void InvalidateRBACData(); // Used to force LoadPermissions at next HasPermission check AccountTypes GetSecurity() const { return _security; } @@ -356,7 +357,7 @@ class TC_GAME_API WorldSession // 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); @@ -449,16 +450,23 @@ class TC_GAME_API WorldSession 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 HandlePlayerLogin(LoginQueryHolder * holder); + void HandlePlayerLogin(LoginQueryHolder* holder); void HandleCharFactionOrRaceChange(WorldPacket& recvData); + void HandleCharFactionOrRaceChangeCallback(std::shared_ptr<CharacterFactionChangeInfo> factionChangeInfo, PreparedQueryResult result); + void HandleCharRenameOpcode(WorldPacket& recvData); + void HandleCharRenameCallback(std::shared_ptr<CharacterRenameInfo> renameInfo, PreparedQueryResult result); + void HandleSetPlayerDeclinedNames(WorldPacket& recvData); + void HandleAlterAppearance(WorldPacket& recvData); + void HandleCharCustomize(WorldPacket& recvData); + void HandleCharCustomizeCallback(std::shared_ptr<CharacterCustomizeInfo> customizeInfo, 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 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); void SendBarberShopResult(BarberShopResult result); @@ -528,7 +536,7 @@ class TC_GAME_API WorldSession // Social void HandleContactListOpcode(WorldPacket& recvPacket); void HandleAddFriendOpcode(WorldPacket& recvPacket); - void HandleAddFriendOpcodeCallback(PreparedQueryResult result, std::string const& friendNote); + void HandleAddFriendOpcodeCallback(std::string const& friendNote, PreparedQueryResult result); void HandleDelFriendOpcode(WorldPacket& recvPacket); void HandleAddIgnoreOpcode(WorldPacket& recvPacket); void HandleAddIgnoreOpcodeCallback(PreparedQueryResult result); @@ -651,11 +659,11 @@ class TC_GAME_API WorldSession 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 HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result); void HandleDuelAcceptedOpcode(WorldPacket& recvPacket); void HandleDuelCancelledOpcode(WorldPacket& recvPacket); @@ -805,10 +813,6 @@ class TC_GAME_API WorldSession void HandleSetActionBarToggles(WorldPacket& recvData); - void HandleCharRenameOpcode(WorldPacket& recvData); - void HandleChangePlayerNameOpcodeCallBack(PreparedQueryResult result, CharacterRenameInfo const* renameInfo); - void HandleSetPlayerDeclinedNames(WorldPacket& recvData); - void HandleTotemDestroyed(WorldPacket& recvData); void HandleDismissCritter(WorldPacket& recvData); @@ -952,9 +956,7 @@ class TC_GAME_API WorldSession void HandleSpellClick(WorldPacket& recvData); void HandleMirrorImageDataRequest(WorldPacket& recvData); - void HandleAlterAppearance(WorldPacket& recvData); void HandleRemoveGlyph(WorldPacket& recvData); - void HandleCharCustomize(WorldPacket& recvData); void HandleQueryInspectAchievements(WorldPacket& recvData); void HandleEquipmentSetSave(WorldPacket& recvData); void HandleEquipmentSetDelete(WorldPacket& recvData); @@ -969,21 +971,13 @@ class TC_GAME_API WorldSession void HandleUpdateMissileTrajectory(WorldPacket& recvPacket); private: - void InitializeQueryCallbackParameters(); void ProcessQueryCallbacks(); QueryResultHolderFuture _realmAccountLoginCallback; - PreparedQueryResultFuture _charEnumCallback; - PreparedQueryResultFuture _addIgnoreCallback; - PreparedQueryResultFuture _stablePetCallback; - QueryCallback<PreparedQueryResult, CharacterRenameInfo*> _charRenameCallback; - QueryCallback<PreparedQueryResult, std::string> _addFriendCallback; - QueryCallback<PreparedQueryResult, uint32> _unstablePetCallback; - QueryCallback<PreparedQueryResult, uint32> _stableSwapCallback; - QueryCallback<PreparedQueryResult, ObjectGuid> _sendStabledPetCallback; - QueryCallback<PreparedQueryResult, CharacterCreateInfo*, true> _charCreateCallback; QueryResultHolderFuture _charLoginCallback; + QueryCallbackProcessor _queryProcessor; + friend class World; protected: class DosProtection diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index fae31751358..08fb36dafb5 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -19,6 +19,7 @@ #include "WorldSocket.h" #include "BigNumber.h" #include "Opcodes.h" +#include "QueryCallback.h" #include "ScriptMgr.h" #include "SHA1.h" #include "PacketLog.h" @@ -51,8 +52,7 @@ void WorldSocket::Start() stmt->setString(0, ip_address); stmt->setUInt32(1, inet_addr(ip_address.c_str())); - _queryCallback = std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1); - _queryFuture = LoginDatabase.AsyncQuery(stmt); + _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1))); } void WorldSocket::CheckIpCallback(PreparedQueryResult result) @@ -125,12 +125,7 @@ bool WorldSocket::Update() if (!BaseSocket::Update()) return false; - if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - auto callback = _queryCallback; - _queryCallback = nullptr; - callback(_queryFuture.get()); - } + _queryProcessor.ProcessReadyQueries(); return true; } @@ -444,8 +439,7 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) stmt->setInt32(0, int32(realm.Id.Realm)); stmt->setString(1, authSession->Account); - _queryCallback = std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1); - _queryFuture = LoginDatabase.AsyncQuery(stmt); + _queryProcessor.AddQuery(LoginDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1))); } void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSession, PreparedQueryResult result) @@ -603,8 +597,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes if (wardenActive) _worldSession->InitWarden(&account.SessionKey, account.OS); - _queryCallback = std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1); - _queryFuture = _worldSession->LoadPermissionsAsync(); + _queryProcessor.AddQuery(_worldSession->LoadPermissionsAsync().WithPreparedCallback(std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1))); AsyncRead(); } diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index b5f2dee2006..36ce6528beb 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -111,8 +111,7 @@ private: MessageBuffer _packetBuffer; MPSCQueue<EncryptablePacket> _bufferQueue; - PreparedQueryResultFuture _queryFuture; - std::function<void(PreparedQueryResult&&)> _queryCallback; + QueryCallbackProcessor _queryProcessor; std::string _ipCountry; }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index e4e09935970..363e0284632 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -39,6 +39,7 @@ #include "DisableMgr.h" #include "GameEventMgr.h" #include "GameObjectModel.h" +#include "GitRevision.h" #include "GridNotifiersImpl.h" #include "GroupMgr.h" #include "GuildMgr.h" @@ -52,7 +53,7 @@ #include "OutdoorPvPMgr.h" #include "Player.h" #include "PoolMgr.h" -#include "GitRevision.h" +#include "QueryCallback.h" #include "ScriptMgr.h" #include "ScriptReloadMgr.h" #include "SkillDiscovery.h" @@ -2909,7 +2910,7 @@ void World::UpdateRealmCharCount(uint32 accountId) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_COUNT); stmt->setUInt32(0, accountId); - m_realmCharCallbacks.push_back(CharacterDatabase.AsyncQuery(stmt)); + _queryProcessor.AddQuery(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&World::_UpdateRealmCharCount, this, std::placeholders::_1))); } void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount) @@ -3271,20 +3272,7 @@ uint64 World::getWorldState(uint32 index) const void World::ProcessQueryCallbacks() { - PreparedQueryResult result; - - for (std::deque<std::future<PreparedQueryResult>>::iterator itr = m_realmCharCallbacks.begin(); itr != m_realmCharCallbacks.end(); ) - { - if ((*itr).wait_for(std::chrono::seconds(0)) != std::future_status::ready) - { - ++itr; - continue; - } - - result = (*itr).get(); - _UpdateRealmCharCount(result); - itr = m_realmCharCallbacks.erase(itr); - } + _queryProcessor.ProcessReadyQueries(); } CharacterInfo const* World::GetCharacterInfo(ObjectGuid const& guid) const diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 77e06f62a53..87f5c9415b8 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -28,7 +28,7 @@ #include "Timer.h" #include "SharedDefines.h" #include "QueryResult.h" -#include "QueryCallback.h" +#include "QueryCallbackProcessor.h" #include "Realm/Realm.h" #include <atomic> @@ -882,7 +882,7 @@ class TC_GAME_API World void LoadCharacterInfoStore(); void ProcessQueryCallbacks(); - std::deque<std::future<PreparedQueryResult>> m_realmCharCallbacks; + QueryCallbackProcessor _queryProcessor; }; TC_GAME_API extern Realm realm; |