diff --git a/src/db/ClientManagerLogin.cpp b/src/db/ClientManagerLogin.cpp index ad092c1..d222430 100644 --- a/src/db/ClientManagerLogin.cpp +++ b/src/db/ClientManagerLogin.cpp @@ -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,44 +514,24 @@ 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) - { - peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0); - return; - } - - MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); - - if (*row[0] != '0') - { - peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0); - return; - } - } - else + unsigned long long count = 0; + if (!CountPlayersByNameExcludingId(p->name, p->pid, &count)) { 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); + if (count != 0) + { + peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0); + return; + } - auto pMsg0 = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER); + if (!UpdatePlayerName(p->pid, p->name)) + { + peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } TPacketDGChangeName pdg; peer->EncodeHeader(DG::CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName)); @@ -509,4 +539,3 @@ void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDCh strlcpy(pdg.name, p->name, sizeof(pdg.name)); peer->Encode(&pdg, sizeof(TPacketDGChangeName)); } - diff --git a/src/db/DBManager.h b/src/db/DBManager.h index 649ae0c..0b439f3 100644 --- a/src/db/DBManager.h +++ b/src/db/DBManager.h @@ -43,6 +43,7 @@ class CDBManager : public singleton 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 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 ); diff --git a/src/game/db.h b/src/game/db.h index 731a7b1..b716ab3 100644 --- a/src/game/db.h +++ b/src/game/db.h @@ -74,6 +74,7 @@ class DBManager : public singleton std::unique_ptr 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); diff --git a/src/game/guild_manager.cpp b/src/game/guild_manager.cpp index f8f93c3..636a3eb 100644 --- a/src/game/guild_manager.cpp +++ b/src/game/guild_manager.cpp @@ -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"); } - diff --git a/src/game/questlua_pc.cpp b/src/game/questlua_pc.cpp index 72940ea..b86d854 100644 --- a/src/game/questlua_pc.cpp +++ b/src/game/questlua_pc.cpp @@ -22,6 +22,7 @@ #include "utils.h" #include "unique_item.h" #include "mob_manager.h" +#include "libsql/Statement.h" #include #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,23 +2157,18 @@ 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); + lua_pushnumber(L, 5); + return 1; + } - int count = 0; - str_to_number(count, row[0]); - - // 이미 해당 이름을 가진 캐릭터가 있음 - if ( count != 0 ) - { - lua_pushnumber(L, 3); - return 1; - } + // 이미 해당 이름을 가진 캐릭터가 있음 + if (count != 0) + { + lua_pushnumber(L, 3); + return 1; } DWORD pid = ch->GetPlayerID(); @@ -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); diff --git a/src/libsql/Statement.cpp b/src/libsql/Statement.cpp index 3863c51..7a3e605 100644 --- a/src/libsql/Statement.cpp +++ b/src/libsql/Statement.cpp @@ -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,13 +139,31 @@ int CStmt::Execute() return 0; } - if (mysql_stmt_store_result(m_pkStmt)) + if (!m_vec_result.empty()) { - Error("mysql_store_result"); - return 0; + 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_stmt_store_result"); + return 0; + } + + iRows = mysql_stmt_num_rows(m_pkStmt); + return true; } - iRows = mysql_stmt_num_rows(m_pkStmt); + iRows = 0; return true; } @@ -156,4 +171,3 @@ bool CStmt::Fetch() { return !mysql_stmt_fetch(m_pkStmt); } - diff --git a/src/libsql/Statement.h b/src/libsql/Statement.h index 681fdfb..b869af1 100644 --- a/src/libsql/Statement.h +++ b/src/libsql/Statement.h @@ -32,10 +32,11 @@ class CStmt std::vector m_vec_param; unsigned int m_uiParamCount; - long unsigned int * m_puiParamLen; + std::vector m_vecParamLen; std::vector m_vec_result; unsigned int m_uiResultCount; + std::vector m_vecResultLen; }; #endif