db: prepare guild and rename queries
This commit is contained in:
@@ -7,12 +7,62 @@
|
||||
#include "Config.h"
|
||||
#include "QID.h"
|
||||
#include "Cache.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
extern std::string g_stLocale;
|
||||
extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab);
|
||||
extern int g_test_server;
|
||||
extern int g_log;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool CountPlayersByNameExcludingId(const char* player_name, DWORD player_id, unsigned long long* count)
|
||||
{
|
||||
char query[QUERY_MAX_LEN];
|
||||
|
||||
if (g_stLocale == "sjis")
|
||||
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM player%s WHERE name = ? COLLATE sjis_japanese_ci AND id <> ?", GetTablePostfix());
|
||||
else
|
||||
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM player%s WHERE name = ? AND id <> ?", GetTablePostfix());
|
||||
|
||||
CStmt stmt;
|
||||
if (!stmt.Prepare(CDBManager::instance().GetDirectSQL(SQL_PLAYER), query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &player_id))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindResult(MYSQL_TYPE_LONGLONG, count))
|
||||
return false;
|
||||
|
||||
if (!stmt.Execute() || !stmt.Fetch())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdatePlayerName(DWORD player_id, const char* player_name)
|
||||
{
|
||||
char query[QUERY_MAX_LEN];
|
||||
snprintf(query, sizeof(query), "UPDATE player%s SET name = ?, change_name = 0 WHERE id = ?", GetTablePostfix());
|
||||
|
||||
CStmt stmt;
|
||||
if (!stmt.Prepare(CDBManager::instance().GetDirectSQL(SQL_PLAYER), query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &player_id))
|
||||
return false;
|
||||
|
||||
return stmt.Execute() != 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP)
|
||||
{
|
||||
char szLogin[LOGIN_MAX_LEN + 1];
|
||||
@@ -464,49 +514,28 @@ void CClientManager::QUERY_LOGOUT(CPeer * peer, DWORD dwHandle,const char * data
|
||||
|
||||
void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p)
|
||||
{
|
||||
char queryStr[QUERY_MAX_LEN];
|
||||
|
||||
if (g_stLocale == "sjis")
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci AND id <> %u",
|
||||
GetTablePostfix(), p->name, p->pid);
|
||||
else
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' AND id <> %u", GetTablePostfix(), p->name, p->pid);
|
||||
|
||||
auto pMsg = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER);
|
||||
|
||||
if (pMsg->Get()->uiNumRows)
|
||||
{
|
||||
if (!pMsg->Get()->pSQLResult)
|
||||
unsigned long long count = 0;
|
||||
if (!CountPlayersByNameExcludingId(p->name, p->pid, &count))
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
|
||||
if (*row[0] != '0')
|
||||
if (count != 0)
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!UpdatePlayerName(p->pid, p->name))
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"UPDATE player%s SET name='%s',change_name=0 WHERE id=%u", GetTablePostfix(), p->name, p->pid);
|
||||
|
||||
auto pMsg0 = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER);
|
||||
|
||||
TPacketDGChangeName pdg;
|
||||
peer->EncodeHeader(DG::CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName));
|
||||
pdg.pid = p->pid;
|
||||
strlcpy(pdg.name, p->name, sizeof(pdg.name));
|
||||
peer->Encode(&pdg, sizeof(TPacketDGChangeName));
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ class CDBManager : public singleton<CDBManager>
|
||||
void ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * pvData, int iSlot = SQL_PLAYER);
|
||||
void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER);
|
||||
std::unique_ptr<SQLMsg> DirectQuery(const char* c_pszQuery, int iSlot = SQL_PLAYER);
|
||||
CAsyncSQL* GetDirectSQL(int iSlot = SQL_PLAYER) { return m_directSQL[iSlot].get(); }
|
||||
|
||||
SQLMsg * PopResult();
|
||||
SQLMsg * PopResult(eSQL_SLOT slot );
|
||||
|
||||
@@ -74,6 +74,7 @@ class DBManager : public singleton<DBManager>
|
||||
|
||||
std::unique_ptr<SQLMsg> DirectQuery(const char* c_pszFormat, ...);
|
||||
void ReturnQuery(int iType, DWORD dwIdent, void* pvData, const char * c_pszFormat, ...);
|
||||
CAsyncSQL* GetDirectSQL() { return &m_sql_direct; }
|
||||
|
||||
void Process();
|
||||
void AnalyzeReturnQuery(SQLMsg * pmsg);
|
||||
|
||||
@@ -15,9 +15,30 @@
|
||||
#include "locale_service.h"
|
||||
#include "guild_manager.h"
|
||||
#include "MarkManager.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool CountGuildsByName(const char* guild_name, unsigned long long* count)
|
||||
{
|
||||
char query[256];
|
||||
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM guild%s WHERE name = ?", get_table_postfix());
|
||||
|
||||
CStmt stmt;
|
||||
if (!stmt.Prepare(DBManager::instance().GetDirectSQL(), query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) guild_name, GUILD_NAME_MAX_LEN))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindResult(MYSQL_TYPE_LONGLONG, count))
|
||||
return false;
|
||||
|
||||
if (!stmt.Execute() || !stmt.Fetch())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct FGuildNameSender
|
||||
{
|
||||
@@ -81,25 +102,19 @@ DWORD CGuildManager::CreateGuild(TGuildCreateParameter& gcp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto pmsg = DBManager::instance().DirectQuery("SELECT COUNT(*) FROM guild%s WHERE name = '%s'",
|
||||
get_table_postfix(), gcp.name);
|
||||
|
||||
if (pmsg->Get()->uiNumRows > 0)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
|
||||
|
||||
if (!(row[0] && row[0][0] == '0'))
|
||||
{
|
||||
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 이미 같은 이름의 길드가 있습니다."));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
unsigned long long guild_count = 0;
|
||||
if (!CountGuildsByName(gcp.name, &guild_count))
|
||||
{
|
||||
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드를 생성할 수 없습니다."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (guild_count != 0)
|
||||
{
|
||||
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 이미 같은 이름의 길드가 있습니다."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// new CGuild(gcp) queries guild tables and tell dbcache to notice other game servers.
|
||||
// other game server calls CGuildManager::LoadGuild to load guild.
|
||||
CGuild * pg = M2_NEW CGuild(gcp);
|
||||
@@ -956,4 +971,3 @@ void CGuildManager::ChangeMaster(DWORD dwGID)
|
||||
"SELECT 1");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "utils.h"
|
||||
#include "unique_item.h"
|
||||
#include "mob_manager.h"
|
||||
#include "libsql/Statement.h"
|
||||
#include <cctype>
|
||||
|
||||
#undef sys_err
|
||||
@@ -33,6 +34,48 @@ const int ITEM_BROKEN_METIN_VNUM = 28960;
|
||||
|
||||
namespace quest
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool CountPlayersByName(const char* player_name, unsigned long long* count)
|
||||
{
|
||||
char query[256];
|
||||
snprintf(query, sizeof(query), "SELECT COUNT(*) FROM player%s WHERE name = ?", get_table_postfix());
|
||||
|
||||
CStmt stmt;
|
||||
if (!stmt.Prepare(DBManager::instance().GetDirectSQL(), query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindResult(MYSQL_TYPE_LONGLONG, count))
|
||||
return false;
|
||||
|
||||
if (!stmt.Execute() || !stmt.Fetch())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdatePlayerName(DWORD player_id, const char* player_name)
|
||||
{
|
||||
char query[256];
|
||||
snprintf(query, sizeof(query), "UPDATE player%s SET name = ?, change_name = 0 WHERE id = ?", get_table_postfix());
|
||||
|
||||
CStmt stmt;
|
||||
if (!stmt.Prepare(DBManager::instance().GetDirectSQL(), query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, (void*) player_name, CHARACTER_NAME_MAX_LEN))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &player_id))
|
||||
return false;
|
||||
|
||||
return stmt.Execute() != 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// "pc" Lua functions
|
||||
//
|
||||
@@ -2114,24 +2157,19 @@ teleport_area:
|
||||
return 1;
|
||||
}
|
||||
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM player%s WHERE name='%s'", get_table_postfix(), szName);
|
||||
auto pmsg = DBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
if ( pmsg->Get()->uiNumRows > 0 )
|
||||
unsigned long long count = 0;
|
||||
if (!CountPlayersByName(szName, &count))
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
|
||||
|
||||
int count = 0;
|
||||
str_to_number(count, row[0]);
|
||||
lua_pushnumber(L, 5);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 이미 해당 이름을 가진 캐릭터가 있음
|
||||
if ( count != 0 )
|
||||
if (count != 0)
|
||||
{
|
||||
lua_pushnumber(L, 3);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD pid = ch->GetPlayerID();
|
||||
// db_clientdesc->DBPacketHeader(GD::FLUSH_CACHE, 0, sizeof(DWORD));
|
||||
@@ -2144,8 +2182,11 @@ teleport_area:
|
||||
/* change_name_log */
|
||||
LogManager::instance().ChangeNameLog(pid, ch->GetName(), szName, ch->GetDesc()->GetHostName());
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE player%s SET name='%s' WHERE id=%u", get_table_postfix(), szName, pid);
|
||||
DBManager::instance().DirectQuery(szQuery);
|
||||
if (!UpdatePlayerName(pid, szName))
|
||||
{
|
||||
lua_pushnumber(L, 5);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch->SetNewName(szName);
|
||||
lua_pushnumber(L, 4);
|
||||
|
||||
@@ -9,7 +9,6 @@ CStmt::CStmt()
|
||||
m_uiParamCount = 0;
|
||||
m_uiResultCount = 0;
|
||||
iRows = 0;
|
||||
m_puiParamLen = NULL;
|
||||
}
|
||||
|
||||
CStmt::~CStmt()
|
||||
@@ -25,11 +24,13 @@ void CStmt::Destroy()
|
||||
m_pkStmt = NULL;
|
||||
}
|
||||
|
||||
if (m_puiParamLen)
|
||||
{
|
||||
free(m_puiParamLen);
|
||||
m_puiParamLen = NULL;
|
||||
}
|
||||
m_vec_param.clear();
|
||||
m_vecParamLen.clear();
|
||||
m_vec_result.clear();
|
||||
m_vecResultLen.clear();
|
||||
m_uiParamCount = 0;
|
||||
m_uiResultCount = 0;
|
||||
iRows = 0;
|
||||
}
|
||||
|
||||
void CStmt::Error(const char * c_pszMsg)
|
||||
@@ -39,6 +40,7 @@ void CStmt::Error(const char * c_pszMsg)
|
||||
|
||||
bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery)
|
||||
{
|
||||
Destroy();
|
||||
m_pkStmt = mysql_stmt_init(sql->GetSQLHandle());
|
||||
m_stQuery = c_pszQuery;
|
||||
|
||||
@@ -48,27 +50,20 @@ bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery)
|
||||
return false;
|
||||
}
|
||||
|
||||
int iParamCount = 0;
|
||||
|
||||
for (unsigned int i = 0; i < m_stQuery.length(); ++i)
|
||||
if (c_pszQuery[i] == '?')
|
||||
++iParamCount;
|
||||
|
||||
if (iParamCount)
|
||||
const unsigned int param_count = mysql_stmt_param_count(m_pkStmt);
|
||||
if (param_count)
|
||||
{
|
||||
m_vec_param.resize(iParamCount);
|
||||
memset(&m_vec_param[0], 0, sizeof(MYSQL_BIND) * iParamCount);
|
||||
|
||||
m_puiParamLen = (long unsigned int *) calloc(iParamCount, sizeof(long unsigned int));
|
||||
m_vec_param.resize(param_count);
|
||||
memset(&m_vec_param[0], 0, sizeof(MYSQL_BIND) * param_count);
|
||||
m_vecParamLen.resize(param_count, 0);
|
||||
}
|
||||
|
||||
m_vec_result.resize(48);
|
||||
memset(&m_vec_result[0], 0, sizeof(MYSQL_BIND) * 48);
|
||||
|
||||
if (mysql_stmt_bind_result(m_pkStmt, &m_vec_result[0]))
|
||||
const unsigned int result_count = mysql_stmt_field_count(m_pkStmt);
|
||||
if (result_count)
|
||||
{
|
||||
Error("mysql_stmt_bind_result");
|
||||
return 0;
|
||||
m_vec_result.resize(result_count);
|
||||
memset(&m_vec_result[0], 0, sizeof(MYSQL_BIND) * result_count);
|
||||
m_vecResultLen.resize(result_count, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -87,16 +82,8 @@ bool CStmt::BindParam(enum_field_types type, void * p, int iMaxLen)
|
||||
bind->buffer_type = type;
|
||||
bind->buffer = (void *) p;
|
||||
bind->buffer_length = iMaxLen;
|
||||
bind->length = m_puiParamLen + m_uiParamCount;
|
||||
|
||||
if (++m_uiParamCount == m_vec_param.size())
|
||||
{
|
||||
if (mysql_stmt_bind_param(m_pkStmt, &m_vec_param[0]))
|
||||
{
|
||||
Error("mysql_stmt_bind_param");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bind->length = m_vecParamLen.empty() ? NULL : &m_vecParamLen[m_uiParamCount];
|
||||
++m_uiParamCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -114,6 +101,7 @@ bool CStmt::BindResult(enum_field_types type, void * p, int iMaxLen)
|
||||
bind->buffer_type = type;
|
||||
bind->buffer = (void *) p;
|
||||
bind->buffer_length = iMaxLen;
|
||||
bind->length = m_vecResultLen.empty() ? NULL : &m_vecResultLen[m_uiResultCount - 1];
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -131,9 +119,18 @@ int CStmt::Execute()
|
||||
|
||||
if (bind->buffer_type == MYSQL_TYPE_STRING)
|
||||
{
|
||||
*(m_puiParamLen + i) = strlen((const char *) bind->buffer);
|
||||
sys_log(0, "param %d len %d buf %s", i, *m_puiParamLen, (const char *) bind->buffer);
|
||||
m_vecParamLen[i] = strlen((const char *) bind->buffer);
|
||||
}
|
||||
else if (bind->buffer_type == MYSQL_TYPE_BLOB)
|
||||
{
|
||||
m_vecParamLen[i] = bind->buffer_length;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_vec_param.empty() && mysql_stmt_bind_param(m_pkStmt, &m_vec_param[0]))
|
||||
{
|
||||
Error("mysql_stmt_bind_param");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mysql_stmt_execute(m_pkStmt))
|
||||
@@ -142,18 +139,35 @@ int CStmt::Execute()
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!m_vec_result.empty())
|
||||
{
|
||||
if (m_uiResultCount != m_vec_result.size())
|
||||
{
|
||||
sys_log(0, "Result count mismatch %u, expected %zu query: %s", m_uiResultCount, m_vec_result.size(), m_stQuery.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mysql_stmt_bind_result(m_pkStmt, &m_vec_result[0]))
|
||||
{
|
||||
Error("mysql_stmt_bind_result");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mysql_stmt_store_result(m_pkStmt))
|
||||
{
|
||||
Error("mysql_store_result");
|
||||
Error("mysql_stmt_store_result");
|
||||
return 0;
|
||||
}
|
||||
|
||||
iRows = mysql_stmt_num_rows(m_pkStmt);
|
||||
return true;
|
||||
}
|
||||
|
||||
iRows = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CStmt::Fetch()
|
||||
{
|
||||
return !mysql_stmt_fetch(m_pkStmt);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,10 +32,11 @@ class CStmt
|
||||
|
||||
std::vector<MYSQL_BIND> m_vec_param;
|
||||
unsigned int m_uiParamCount;
|
||||
long unsigned int * m_puiParamLen;
|
||||
std::vector<unsigned long> m_vecParamLen;
|
||||
|
||||
std::vector<MYSQL_BIND> m_vec_result;
|
||||
unsigned int m_uiResultCount;
|
||||
std::vector<unsigned long> m_vecResultLen;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user