rendered paste bodyIndex: 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<uint8>(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<string>& data,uint64 guid);
static uint32 GetUInt32ValueFromArray(vector<string> const& data, uint16 index);
static float GetFloatValueFromArray(vector<string> 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();
+
/// </ul>
///- 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<CliCommandHolder*, ZThread::FastMutex> 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 <queue>
-
class SqlTransaction;
typedef HM_NAMESPACE::hash_map<ZThread::ThreadImpl*, SqlTransaction*> TransactionQueues;
+typedef HM_NAMESPACE::hash_map<ZThread::ThreadImpl*, SqlResultQueue*> 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<class Class>
+ 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<Class>(object, method), itr->second));
+ return true;
+ }
+
+ template<class Class, typename ParamType1>
+ 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<Class, ParamType1>(object, method, param1), itr->second));
+ return true;
+ }
+
+ template<class Class>
+ 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<class Class, typename ParamType1>
+ 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 <queue>
/// ---- 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<SqlResultHolder*, ZThread::FastMutex>
+{
+ public:
+ SqlResultQueue() : ZThread::LockedQueue<SqlResultHolder*, ZThread::FastMutex>() {}
+ 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 Class>
+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 Class, typename ParamType1>
+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