From fce6268129935dfd532412bf511fcf38fc07d385 Mon Sep 17 00:00:00 2001 From: server Date: Mon, 13 Apr 2026 22:55:56 +0200 Subject: [PATCH] db: prepare client manager queries --- src/db/ClientManager.cpp | 248 +++++++++++++++++++++++++-------------- 1 file changed, 160 insertions(+), 88 deletions(-) diff --git a/src/db/ClientManager.cpp b/src/db/ClientManager.cpp index 1035956..fff7e06 100644 --- a/src/db/ClientManager.cpp +++ b/src/db/ClientManager.cpp @@ -40,19 +40,39 @@ int g_query_count[2]; namespace { -bool PrepareClientPlayerStmt(CStmt& stmt, const std::string& query) +bool PrepareClientStmt(CStmt& stmt, const std::string& query, int slot) { - CAsyncSQL* sql = CDBManager::instance().GetDirectSQL(SQL_PLAYER); + CAsyncSQL* sql = CDBManager::instance().GetDirectSQL(slot); if (!sql) { - sys_err("player SQL handle is not initialized"); + sys_err("SQL handle is not initialized for slot %d", slot); return false; } return stmt.Prepare(sql, query.c_str()); } +bool PrepareClientPlayerStmt(CStmt& stmt, const std::string& query) +{ + return PrepareClientStmt(stmt, query, SQL_PLAYER); +} + +bool PrepareClientCommonStmt(CStmt& stmt, const std::string& query) +{ + return PrepareClientStmt(stmt, query, SQL_COMMON); +} + +template +void NullTerminateClientResult(char (&buffer)[N], unsigned long length) +{ + size_t finalLength = length; + if (finalLength >= N) + finalLength = N - 1; + + buffer[finalLength] = '\0'; +} + bool LoadSafeboxPassword(uint32_t accountId, char* password, size_t passwordSize, bool* foundRow) { CStmt stmt; @@ -711,8 +731,10 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg) std::vector > vec_dwFinishedAwardID; __typeof(pSet->begin()) it = pSet->begin(); - - char szQuery[512]; + const char* itemWindowName = pi->ip[0] == 0 ? "SAFEBOX" : "MALL"; + const std::string insertAwardItemQuery = std::string( + "INSERT INTO item") + GetTablePostfix() + + " (id, owner_id, window, pos, vnum, count, socket0, socket1, socket2) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"; while (it != pSet->end()) { @@ -841,25 +863,37 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg) } } - snprintf(szQuery, sizeof(szQuery), - "INSERT INTO item%s (id, owner_id, window, pos, vnum, count, socket0, socket1, socket2) " - "VALUES(%u, %u, '%s', %d, %u, %u, %u, %u, %u)", - GetTablePostfix(), - GainItemID(), - pi->account_id, - pi->ip[0] == 0 ? "SAFEBOX" : "MALL", - iPos, - pItemAward->dwVnum, pItemAward->dwCount, pItemAward->dwSocket0, pItemAward->dwSocket1, dwSocket2); + uint32_t itemId = GainItemID(); + int32_t pos = iPos; + uint32_t ownerId = pi->account_id; + uint32_t vnum = pItemAward->dwVnum; + uint32_t count = pItemAward->dwCount; + uint32_t socket0 = pItemAward->dwSocket0; + uint32_t socket1 = pItemAward->dwSocket1; + uint32_t socket2 = dwSocket2; + CStmt insertAwardItemStmt; + + if (!PrepareClientPlayerStmt(insertAwardItemStmt, insertAwardItemQuery) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &itemId) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &ownerId) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_STRING, const_cast(itemWindowName), static_cast(strlen(itemWindowName))) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &pos) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &vnum) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &count) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &socket0) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &socket1) + || !insertAwardItemStmt.BindParam(MYSQL_TYPE_LONG, &socket2) + || !insertAwardItemStmt.Execute()) + { + break; + } + + sys_log(0, "SAFEBOX insert: owner=%u window=%s pos=%d vnum=%u count=%u id=%u", + ownerId, itemWindowName, pos, vnum, count, itemId); + + item.id = itemId; } - auto pmsg = CDBManager::instance().DirectQuery(szQuery); - SQLResult * pRes = pmsg->Get(); - sys_log(0, "SAFEBOX Query : [%s]", szQuery); - - if (pRes->uiAffectedRows == 0 || pRes->uiInsertID == 0 || pRes->uiAffectedRows == (uint32_t)-1) - break; - - item.id = pmsg->Get()->uiInsertID; item.window = pi->ip[0] == 0 ? SAFEBOX : MALL, item.pos = iPos; item.count = pItemAward->dwCount; @@ -1100,7 +1134,6 @@ void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpire } else if (playerIndexStmt.iRows && playerIndexStmt.Fetch()) { - CStmt moveEmpirePlayerStmt; const std::string moveEmpirePlayerQuery = std::string("UPDATE player") + GetTablePostfix() + " SET map_index=?,x=?,y=? WHERE id=?"; @@ -1121,11 +1154,7 @@ void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpire { 969600, 278400 } // 진노국 }; - if (!PrepareClientPlayerStmt(moveEmpirePlayerStmt, moveEmpirePlayerQuery)) - { - sys_err("EmpireSelect: failed to prepare player move query"); - } - else for (int i = 0; i < 3; ++i) + for (int i = 0; i < 3; ++i) { sys_log(0, "EMPIRE PIDS[%u]", pids[i]); @@ -1138,8 +1167,10 @@ void CClientManager::QUERY_EMPIRE_SELECT(CPeer * pkPeer, DWORD dwHandle, TEmpire uint32_t x = g_start_position[p->bEmpire][0]; uint32_t y = g_start_position[p->bEmpire][1]; uint32_t playerId = pids[i]; + CStmt moveEmpirePlayerStmt; - if (!moveEmpirePlayerStmt.BindParam(MYSQL_TYPE_LONG, &mapIndex) + if (!PrepareClientPlayerStmt(moveEmpirePlayerStmt, moveEmpirePlayerQuery) + || !moveEmpirePlayerStmt.BindParam(MYSQL_TYPE_LONG, &mapIndex) || !moveEmpirePlayerStmt.BindParam(MYSQL_TYPE_LONG, &x) || !moveEmpirePlayerStmt.BindParam(MYSQL_TYPE_LONG, &y) || !moveEmpirePlayerStmt.BindParam(MYSQL_TYPE_LONG, &playerId) @@ -1883,15 +1914,21 @@ void CClientManager::CreateObject(TPacketGDCreateObject * p) { using namespace building; - char szQuery[512]; + CStmt stmt; + const std::string query = std::string("INSERT INTO object") + GetTablePostfix() + + " (land_id, vnum, map_index, x, y, x_rot, y_rot, z_rot) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; - snprintf(szQuery, sizeof(szQuery), - "INSERT INTO object%s (land_id, vnum, map_index, x, y, x_rot, y_rot, z_rot) VALUES(%u, %u, %d, %d, %d, %f, %f, %f)", - GetTablePostfix(), p->dwLandID, p->dwVnum, p->lMapIndex, p->x, p->y, p->xRot, p->yRot, p->zRot); - - auto pmsg = CDBManager::instance().DirectQuery(szQuery); - - if (pmsg->Get()->uiInsertID == 0) + if (!PrepareClientPlayerStmt(stmt, query) + || !stmt.BindParam(MYSQL_TYPE_LONG, &p->dwLandID) + || !stmt.BindParam(MYSQL_TYPE_LONG, &p->dwVnum) + || !stmt.BindParam(MYSQL_TYPE_LONG, &p->lMapIndex) + || !stmt.BindParam(MYSQL_TYPE_LONG, &p->x) + || !stmt.BindParam(MYSQL_TYPE_LONG, &p->y) + || !stmt.BindParam(MYSQL_TYPE_FLOAT, &p->xRot) + || !stmt.BindParam(MYSQL_TYPE_FLOAT, &p->yRot) + || !stmt.BindParam(MYSQL_TYPE_FLOAT, &p->zRot) + || !stmt.Execute() + || stmt.GetInsertId() == 0) { sys_err("cannot insert object"); return; @@ -1901,7 +1938,7 @@ void CClientManager::CreateObject(TPacketGDCreateObject * p) memset(pkObj, 0, sizeof(TObject)); - pkObj->dwID = pmsg->Get()->uiInsertID; + pkObj->dwID = static_cast(stmt.GetInsertId()); pkObj->dwVnum = p->dwVnum; pkObj->dwLandID = p->dwLandID; pkObj->lMapIndex = p->lMapIndex; @@ -1919,13 +1956,14 @@ void CClientManager::CreateObject(TPacketGDCreateObject * p) void CClientManager::DeleteObject(DWORD dwID) { - char szQuery[128]; + CStmt stmt; + const std::string query = std::string("DELETE FROM object") + GetTablePostfix() + " WHERE id=?"; - snprintf(szQuery, sizeof(szQuery), "DELETE FROM object%s WHERE id=%u", GetTablePostfix(), dwID); - - auto pmsg = CDBManager::instance().DirectQuery(szQuery); - - if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) + if (!PrepareClientPlayerStmt(stmt, query) + || !stmt.BindParam(MYSQL_TYPE_LONG, &dwID) + || !stmt.Execute() + || stmt.GetAffectedRows() == 0 + || stmt.GetAffectedRows() == static_cast(-1)) { sys_err("no object by id %u", dwID); return; @@ -3067,29 +3105,31 @@ DWORD CClientManager::GetItemID() bool CClientManager::InitializeLocalization() { - char szQuery[512]; - snprintf(szQuery, sizeof(szQuery), "SELECT mValue, mKey FROM locale"); - auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + CStmt stmt; + tLocale locale; + const std::string query = "SELECT COALESCE(mValue, ''), COALESCE(mKey, '') FROM locale"; - if (pMsg->Get()->uiNumRows == 0) + memset(&locale, 0, sizeof(locale)); + + if (!PrepareClientCommonStmt(stmt, query) + || !stmt.BindResult(MYSQL_TYPE_STRING, locale.szValue, sizeof(locale.szValue)) + || !stmt.BindResult(MYSQL_TYPE_STRING, locale.szKey, sizeof(locale.szKey)) + || !stmt.Execute() + || stmt.iRows == 0) { - sys_err("InitializeLocalization() ==> DirectQuery failed(%s)", szQuery); + sys_err("InitializeLocalization() ==> DirectQuery failed(%s)", query.c_str()); return false; } - sys_log(0, "InitializeLocalization() - LoadLocaleTable(count:%d)", pMsg->Get()->uiNumRows); + sys_log(0, "InitializeLocalization() - LoadLocaleTable(count:%d)", stmt.iRows); m_vec_Locale.clear(); + m_vec_Locale.reserve(stmt.iRows); - MYSQL_ROW row = NULL; - - for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n) + while (stmt.Fetch()) { - int col = 0; - tLocale locale; - - strlcpy(locale.szValue, row[col++], sizeof(locale.szValue)); - strlcpy(locale.szKey, row[col++], sizeof(locale.szKey)); + NullTerminateClientResult(locale.szValue, stmt.GetResultLength(0)); + NullTerminateClientResult(locale.szKey, stmt.GetResultLength(1)); //DB_NAME_COLUMN Setting if (strcmp(locale.szKey, "LOCALE") == 0) @@ -3450,7 +3490,9 @@ bool CClientManager::InitializeLocalization() { sys_log(0, "locale[UNKNOWN_KEY(%s)] = %s", locale.szKey, locale.szValue); } + m_vec_Locale.push_back(locale); + memset(&locale, 0, sizeof(locale)); } return true; @@ -3461,33 +3503,52 @@ bool CClientManager::InitializeLocalization() bool CClientManager::__GetAdminInfo(const char *szIP, std::vector & rAdminVec) { //szIP == NULL 일경우 모든서버에 운영자 권한을 갖는다. - char szQuery[512]; - snprintf(szQuery, sizeof(szQuery), - "SELECT mID,mAccount,mName,mContactIP,mServerIP,mAuthority FROM gmlist WHERE mServerIP='ALL' or mServerIP='%s'", - szIP ? szIP : "ALL"); + CStmt stmt; + int32_t adminId = 0; + char account[sizeof(tAdminInfo::m_szAccount)] = {}; + char name[sizeof(tAdminInfo::m_szName)] = {}; + char contactIP[sizeof(tAdminInfo::m_szContactIP)] = {}; + char serverIP[sizeof(tAdminInfo::m_szServerIP)] = {}; + char authority[32] = {}; + const char* requestedServerIp = szIP ? szIP : "ALL"; + const std::string query = + "SELECT mID, COALESCE(mAccount, ''), COALESCE(mName, ''), COALESCE(mContactIP, ''), COALESCE(mServerIP, ''), COALESCE(mAuthority, '') " + "FROM gmlist WHERE mServerIP='ALL' or mServerIP=?"; - auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); - - if (pMsg->Get()->uiNumRows == 0) + if (!PrepareClientCommonStmt(stmt, query) + || !stmt.BindParam(MYSQL_TYPE_STRING, const_cast(requestedServerIp), static_cast(strlen(requestedServerIp))) + || !stmt.BindResult(MYSQL_TYPE_LONG, &adminId) + || !stmt.BindResult(MYSQL_TYPE_STRING, account, sizeof(account)) + || !stmt.BindResult(MYSQL_TYPE_STRING, name, sizeof(name)) + || !stmt.BindResult(MYSQL_TYPE_STRING, contactIP, sizeof(contactIP)) + || !stmt.BindResult(MYSQL_TYPE_STRING, serverIP, sizeof(serverIP)) + || !stmt.BindResult(MYSQL_TYPE_STRING, authority, sizeof(authority)) + || !stmt.Execute() + || stmt.iRows == 0) { - sys_err("__GetAdminInfo() ==> DirectQuery failed(%s)", szQuery); + sys_err("__GetAdminInfo() ==> DirectQuery failed(%s)", query.c_str()); return false; } - MYSQL_ROW row; - rAdminVec.reserve(pMsg->Get()->uiNumRows); + rAdminVec.reserve(stmt.iRows); - while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + while (stmt.Fetch()) { - int idx = 0; tAdminInfo Info; + memset(&Info, 0, sizeof(Info)); - str_to_number(Info.m_ID, row[idx++]); - trim_and_lower(row[idx++], Info.m_szAccount, sizeof(Info.m_szAccount)); - strlcpy(Info.m_szName, row[idx++], sizeof(Info.m_szName)); - strlcpy(Info.m_szContactIP, row[idx++], sizeof(Info.m_szContactIP)); - strlcpy(Info.m_szServerIP, row[idx++], sizeof(Info.m_szServerIP)); - std::string stAuth = row[idx++]; + NullTerminateClientResult(account, stmt.GetResultLength(1)); + NullTerminateClientResult(name, stmt.GetResultLength(2)); + NullTerminateClientResult(contactIP, stmt.GetResultLength(3)); + NullTerminateClientResult(serverIP, stmt.GetResultLength(4)); + NullTerminateClientResult(authority, stmt.GetResultLength(5)); + + Info.m_ID = adminId; + trim_and_lower(account, Info.m_szAccount, sizeof(Info.m_szAccount)); + strlcpy(Info.m_szName, name, sizeof(Info.m_szName)); + strlcpy(Info.m_szContactIP, contactIP, sizeof(Info.m_szContactIP)); + strlcpy(Info.m_szServerIP, serverIP, sizeof(Info.m_szServerIP)); + std::string stAuth = authority; if (!stAuth.compare("IMPLEMENTOR")) Info.m_Authority = GM_IMPLEMENTOR; @@ -3506,6 +3567,12 @@ bool CClientManager::__GetAdminInfo(const char *szIP, std::vector & sys_log(0, "GM: PID %u Login %s Character %s ContactIP %s ServerIP %s Authority %d[%s]", Info.m_ID, Info.m_szAccount, Info.m_szName, Info.m_szContactIP, Info.m_szServerIP, Info.m_Authority, stAuth.c_str()); + + memset(account, 0, sizeof(account)); + memset(name, 0, sizeof(name)); + memset(contactIP, 0, sizeof(contactIP)); + memset(serverIP, 0, sizeof(serverIP)); + memset(authority, 0, sizeof(authority)); } return true; @@ -3513,27 +3580,32 @@ bool CClientManager::__GetAdminInfo(const char *szIP, std::vector & bool CClientManager::__GetHostInfo(std::vector & rIPVec) { - char szQuery[512]; - snprintf(szQuery, sizeof(szQuery), "SELECT mIP FROM gmhost"); - auto pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_COMMON); + CStmt stmt; + char hostIP[16] = {}; + const std::string query = "SELECT COALESCE(mIP, '') FROM gmhost"; - if (pMsg->Get()->uiNumRows == 0) + if (!PrepareClientCommonStmt(stmt, query) + || !stmt.BindResult(MYSQL_TYPE_STRING, hostIP, sizeof(hostIP)) + || !stmt.Execute() + || stmt.iRows == 0) { - sys_err("__GetHostInfo() ==> DirectQuery failed(%s)", szQuery); + sys_err("__GetHostInfo() ==> DirectQuery failed(%s)", query.c_str()); return false; } - rIPVec.reserve(pMsg->Get()->uiNumRows); + rIPVec.reserve(stmt.iRows); - MYSQL_ROW row; - - while ((row = mysql_fetch_row(pMsg->Get()->pSQLResult))) + while (stmt.Fetch()) { - if (row[0] && *row[0]) + NullTerminateClientResult(hostIP, stmt.GetResultLength(0)); + + if (hostIP[0]) { - rIPVec.push_back(row[0]); - sys_log(0, "GMHOST: %s", row[0]); + rIPVec.push_back(hostIP); + sys_log(0, "GMHOST: %s", hostIP); } + + memset(hostIP, 0, sizeof(hostIP)); } return true;