Index: src/game/CharacterHandler.cpp =================================================================== --- src/game/CharacterHandler.cpp (revision 4123) +++ src/game/CharacterHandler.cpp (working copy) @@ -38,27 +38,31 @@ // check used symbols in player name at creating and rename std::string notAllowedChars = "\t\v\b\f\a\n\r\\\"\'\? <>[](){}_=+-|/!@#$%^&*~`.,0123456789\0"; -void WorldSession::HandleCharEnumOpcode( WorldPacket & recv_data ) +class CharacterHandler { - // keys can be non cleared if player open realm list and close it by 'cancel' - loginDatabase.PExecute("UPDATE `account` SET `v` = '0', `s` = '0' WHERE `id` = '%u'", GetAccountId()); + public: + void HandleCharEnum(QueryResult * result, uint32 account); +} chrHandler; +void CharacterHandler::HandleCharEnum(QueryResult * result, uint32 account) +{ + WorldSession * session = sWorld.FindSession(account); + if(!session) return; + WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size - - QueryResult *result = sDatabase.PQuery("SELECT `guid` FROM `character` WHERE `account` = '%u' ORDER BY `guid`", GetAccountId()); - + uint8 num = 0; data << num; if( result ) { - Player *plr = new Player(this); + Player *plr = new Player(session); do { - sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),GetAccountId()); + sLog.outDetail("Loading char guid %u from account %u.",(*result)[0].GetUInt32(),session->GetAccountId()); - if(plr->MinimalLoadFromDB( (*result)[0].GetUInt32() )) + if(plr->MinimalLoadFromDB( result, (*result)[0].GetUInt32() )) { plr->BuildEnumData( &data ); num++; @@ -72,9 +76,17 @@ data.put(0, num); - SendPacket( &data ); + session->SendPacket( &data ); } +void WorldSession::HandleCharEnumOpcode( WorldPacket & recv_data ) +{ + // keys can be non cleared if player open realm list and close it by 'cancel' + loginDatabase.PExecute("UPDATE `account` SET `v` = '0', `s` = '0' WHERE `id` = '%u'", GetAccountId()); + sDatabase.AsyncPQuery(&chrHandler, &CharacterHandler::HandleCharEnum, GetAccountId(), + "SELECT `data`,`name`,`position_x`,`position_y`,`position_z`,`map`,`totaltime`,`leveltime`,`rename` FROM `character` WHERE `account` = '%u' ORDER BY `guid`", GetAccountId()); +} + void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data ) { // in Player::Create: Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 4123) +++ src/game/Player.cpp (working copy) @@ -1084,7 +1084,7 @@ uint32 petFamily = 0; // show pet at selection character in character list only for non-ghost character - if(isAlive()&&(pClass==CLASS_WARLOCK||pClass==CLASS_HUNTER)) + /*if(isAlive()&&(pClass==CLASS_WARLOCK||pClass==CLASS_HUNTER)) { QueryResult *result = sDatabase.PQuery("SELECT `entry`,`modelid`,`level` FROM `character_pet` WHERE `owner` = '%u' AND `slot` = '0'", GetGUIDLow() ); if(result) @@ -1101,14 +1101,14 @@ } delete result; } - } + }*/ *p_data << (uint32)petDisplayId; *p_data << (uint32)petLevel; *p_data << (uint32)petFamily; } - ItemPrototype const *items[EQUIPMENT_SLOT_END]; + /*ItemPrototype const *items[EQUIPMENT_SLOT_END]; for (int i = 0; i < EQUIPMENT_SLOT_END; i++) items[i] = NULL; @@ -1131,14 +1131,18 @@ } } while (result->NextRow()); delete result; - } + }*/ - for (int i = 0; i < EQUIPMENT_SLOT_END; i++) + for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++) { - if (items[i] != NULL) + int VisibleBase = PLAYER_VISIBLE_ITEM_1_0 + (slot * 16); + uint32 item_id = GetUInt32Value(VisibleBase); + const ItemPrototype * proto = objmgr.GetItemPrototype(item_id); + + if (proto != NULL) { - *p_data << (uint32)items[i]->DisplayInfoID; - *p_data << (uint8)items[i]->InventoryType; + *p_data << (uint32)proto->DisplayInfoID; + *p_data << (uint8)proto->InventoryType; } else { @@ -11506,19 +11510,23 @@ /*** LOAD SYSTEM ***/ /*********************************************************/ -bool Player::MinimalLoadFromDB( uint32 guid ) +bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid ) { - // 0 1 2 3 4 5 6 7 8 - QueryResult *result = sDatabase.PQuery("SELECT `data`,`name`,`position_x`,`position_y`,`position_z`,`map`,`totaltime`,`leveltime`,`rename` FROM `character` WHERE `guid` = '%u'",guid); + bool delete_query = true; if(!result) - return false; + { + // 0 1 2 3 4 5 6 7 8 + QueryResult *result = sDatabase.PQuery("SELECT `data`,`name`,`position_x`,`position_y`,`position_z`,`map`,`totaltime`,`leveltime`,`rename` FROM `character` WHERE `guid` = '%u'",guid); + if(!result) return false; + } + else delete_query = false; Field *fields = result->Fetch(); if(!LoadValues( fields[0].GetString())) { sLog.outError("ERROR: Player #%d have broken data in `data` field. Can't be loaded.",GUID_LOPART(guid)); - delete result; + if(delete_query) delete result; return false; } @@ -11536,7 +11544,7 @@ _LoadBoundInstances(); - delete result; + if (delete_query) delete result; for (int i = 0; i < PLAYER_SLOTS_COUNT; i++) m_items[i] = NULL; Index: src/game/Player.h =================================================================== --- src/game/Player.h (revision 4123) +++ src/game/Player.h (working copy) @@ -898,7 +898,7 @@ /*********************************************************/ bool LoadFromDB(uint32 guid); - bool MinimalLoadFromDB(uint32 guid); + bool MinimalLoadFromDB(QueryResult *result, uint32 guid); static bool LoadValuesArrayFromDB(vector& data,uint64 guid); static uint32 GetUInt32ValueFromArray(vector const& data, uint16 index); static float GetFloatValueFromArray(vector const& data, uint16 index); Index: src/game/World.cpp =================================================================== --- src/game/World.cpp (revision 4123) +++ src/game/World.cpp (working copy) @@ -815,6 +815,9 @@ sBattleGroundMgr.Update(diff); } + // execute callbacks from sql queries that were queued recently + UpdateResultQueue(); + /// ///- Move all creatures with "delayed move" and remove and delete all objects with "delayed remove" ObjectAccessor::Instance().DoDelayedMovesAndRemoves(); @@ -1348,3 +1351,15 @@ // print the console message here so it looks right zprintf("mangos>"); } + +void World::InitResultQueue() +{ + m_resultQueue = new SqlResultQueue; + sDatabase.SetResultQueue(m_resultQueue); +} + +void World::UpdateResultQueue() +{ + m_resultQueue->Update(); +} + Index: src/game/World.h =================================================================== --- src/game/World.h (revision 4123) +++ src/game/World.h (working copy) @@ -36,6 +36,7 @@ struct ScriptAction; struct ScriptInfo; class CliCommandHolder; +class SqlResultQueue; /// Timers for different object refresh rates enum WorldTimers @@ -313,6 +314,9 @@ void ProcessCliCommands(); void QueueCliCommand(CliCommandHolder* command) { cliCmdQueue.add(command); } + void UpdateResultQueue(); + void InitResultQueue(); + protected: void _UpdateGameTime(); void ScriptsProcess(); @@ -355,6 +359,7 @@ // CLI command holder to be thread safe ZThread::LockedQueue cliCmdQueue; + SqlResultQueue *m_resultQueue; }; extern uint32 realmID; Index: src/mangosd/WorldRunnable.cpp =================================================================== --- src/mangosd/WorldRunnable.cpp (revision 4123) +++ src/mangosd/WorldRunnable.cpp (working copy) @@ -35,6 +35,7 @@ { ///- Init new SQL thread for the world database sDatabase.ThreadStart(); // let thread do safe mySQL requests (one connection call enough) + sWorld.InitResultQueue(); uint32 realCurrTime = 0; uint32 realPrevTime = getMSTime(); Index: src/shared/Database/Database.cpp =================================================================== --- src/shared/Database/Database.cpp (revision 4123) +++ src/shared/Database/Database.cpp (working copy) @@ -103,4 +103,9 @@ } return Execute(szQuery); +} + +void Database::SetResultQueue(SqlResultQueue * queue) +{ + m_queryQueues[ZThread::ThreadImpl::current()] = queue; } \ No newline at end of file Index: src/shared/Database/Database.h =================================================================== --- src/shared/Database/Database.h (revision 4123) +++ src/shared/Database/Database.h (working copy) @@ -20,14 +20,15 @@ #define DATABASE_H #include "zthread/Thread.h" +#include "../src/zthread/ThreadImpl.h" #include "Utilities/HashMap.h" #include "Database/SqlDelayThread.h" +#include "Database/SqlOperations.h" -#include - class SqlTransaction; typedef HM_NAMESPACE::hash_map TransactionQueues; +typedef HM_NAMESPACE::hash_map QueryQueues; #define MAX_QUERY_LEN 1024 @@ -36,7 +37,8 @@ protected: Database() : m_threadBody(NULL), m_delayThread(NULL) {}; - TransactionQueues m_tranQueues; ///< Transaction queue from diff. threads + TransactionQueues m_tranQueues; ///< Transaction queues from diff. threads + QueryQueues m_queryQueues; ///< Query queues from diff threads SqlDelayThread* m_threadBody; ///< Pointer to delay sql executer ZThread::Thread* m_delayThread; ///< Pointer to executer thread @@ -51,6 +53,68 @@ virtual QueryResult* Query(const char *sql) = 0; virtual QueryResult* PQuery(const char *format,...) = 0; + template + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql) + { + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new _SqlResultHolder(object, method), itr->second)); + return true; + } + + template + bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) + { + if (!sql) return false; + ZThread::ThreadImpl * queryThread = ZThread::ThreadImpl::current(); + QueryQueues::iterator itr = m_queryQueues.find(queryThread); + if (itr == m_queryQueues.end()) return false; + m_threadBody->Delay(new SqlQuery(sql, new _SqlResultHolder1(object, method, param1), itr->second)); + return true; + } + + template + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...) + { + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(object, method, szQuery); + } + + template + bool AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) + { + if(!format) return false; + + va_list ap; + char szQuery [MAX_QUERY_LEN]; + va_start(ap, format); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); + va_end(ap); + + if(res==-1) + { + sLog.outError("SQL Query truncated (and not execute) for format: %s",format); + return false; + } + + return AsyncQuery(object, method, param1, szQuery); + } + virtual bool Execute(const char *sql) = 0; virtual bool PExecute(const char *format,...) = 0; virtual bool DirectExecute(const char* sql) = 0; @@ -80,6 +144,10 @@ virtual void ThreadStart(); // must be called before finish thread run (one time for thread using one from existed Database objects) virtual void ThreadEnd(); + + // sets the result queue of the current thread, be careful what thread you call this from + void SetResultQueue(SqlResultQueue * queue); + private: // 0 - do not log, 1 - log sql commands uint32 m_logSQL; Index: src/shared/Database/DatabaseMysql.cpp =================================================================== --- src/shared/Database/DatabaseMysql.cpp (revision 4123) +++ src/shared/Database/DatabaseMysql.cpp (working copy) @@ -425,4 +425,4 @@ delete m_delayThread; //This also deletes m_threadBody m_delayThread = NULL; m_threadBody = NULL; -} \ No newline at end of file +} Index: src/shared/Database/SqlOperations.cpp =================================================================== --- src/shared/Database/SqlOperations.cpp (revision 4123) +++ src/shared/Database/SqlOperations.cpp (working copy) @@ -37,3 +37,40 @@ } db->DirectExecute("COMMIT"); } + +void SqlQuery::Execute(Database *db) +{ + if(!m_holder || !m_queue) return; + m_holder->SetResult(db->Query(m_sql)); + m_queue->add(m_holder); + m_holder = NULL; +} + +SqlResultHolder::~SqlResultHolder() +{ + delete m_result; +} + + +void SqlResultQueue::Update() +{ + while(!empty()) + { + SqlResultHolder * holder = next(); + holder->CallBack(); + } +} + +void SqlTransaction::Execute(Database *db, size_t nr) +{ + if(m_queue.size() < nr) return; + db->DirectExecute("START TRANSACTION"); + for(size_t i = 0; i < nr; i++) + { + char *sql = (char*)m_queue.front(); + m_queue.pop(); + db->DirectExecute(sql); + free(sql); + } + db->DirectExecute("COMMIT"); +} \ No newline at end of file Index: src/shared/Database/SqlOperations.h =================================================================== --- src/shared/Database/SqlOperations.h (revision 4123) +++ src/shared/Database/SqlOperations.h (working copy) @@ -21,6 +21,7 @@ #include "zthread/LockedQueue.h" #include "zthread/FastMutex.h" +#include "zthread/Thread.h" #include /// ---- BASE --- @@ -30,6 +31,7 @@ class SqlOperation { public: + virtual void OnRemove() { delete this; } virtual void Execute(Database *db) = 0; virtual ~SqlOperation() {} }; @@ -54,5 +56,83 @@ SqlTransaction() {} void DelayExecute(const char *sql) { m_queue.push(strdup(sql)); } void Execute(Database *db); + void Execute(Database *db, size_t nr); }; + +class SqlTransHolder : public SqlOperation +{ + private: + SqlTransaction *m_trans; + size_t m_nr; + public: + SqlTransHolder(SqlTransaction *trans, size_t opNr) : m_trans(trans), m_nr(opNr) {} + void Execute(Database *db) { m_trans->Execute(db, m_nr); } +}; + +// ---- ASYNC QUERIES ---- + +class SqlQuery; +class QueryResult; +class SqlResultHolder; +class SqlResultQueue; + +class SqlResultQueue : public ZThread::LockedQueue +{ + public: + SqlResultQueue() : ZThread::LockedQueue() {} + void Update(); +}; + +class SqlResultHolder +{ + private: + QueryResult *m_result; + public: + SqlResultHolder() : m_result(NULL) {} + void SetResult(QueryResult *result) { m_result = result; } + QueryResult * GetResult() { return m_result; } + virtual ~SqlResultHolder(); + virtual void CallBack() = 0; +}; + +template +class _SqlResultHolder : public SqlResultHolder +{ + private: + typedef void (Class::*Method)(QueryResult*); + Class *m_object; + Method m_method; + public: + _SqlResultHolder(Class *object, Method method) + : m_object(object), m_method(method), SqlResultHolder() {} + void CallBack() { (m_object->*m_method)(GetResult()); } +}; + +template +class _SqlResultHolder1 : public SqlResultHolder +{ + private: + typedef void (Class::*Method)(QueryResult*, ParamType1); + Class *m_object; + Method m_method; + ParamType1 m_param1; + public: + _SqlResultHolder1(Class *object, Method method, ParamType1 param1) + : m_object(object), m_method(method), m_param1(param1), SqlResultHolder() {} + void CallBack() { (m_object->*m_method)(GetResult(), m_param1); } +}; + +class SqlQuery : public SqlOperation +{ + private: + const char *m_sql; + SqlResultHolder * m_holder; + SqlResultQueue * m_queue; + public: + SqlQuery(const char *sql, SqlResultHolder * holder, SqlResultQueue * queue) + : m_sql(strdup(sql)), m_holder(holder), m_queue(queue) {} + ~SqlQuery() { if(m_holder) delete m_holder; free((void*)m_sql); } + void Execute(Database *db); +}; + #endif //__SQLOPERATIONS_H