From 44720d06912f29ad4966723463fcb6844821654f Mon Sep 17 00:00:00 2001 From: server Date: Mon, 13 Apr 2026 21:17:19 +0200 Subject: [PATCH] db: prepare safebox and player lookups --- src/db/ClientManager.cpp | 506 +++++++++++++++++++++++---------------- 1 file changed, 300 insertions(+), 206 deletions(-) diff --git a/src/db/ClientManager.cpp b/src/db/ClientManager.cpp index 56b9e4e..b61c65f 100644 --- a/src/db/ClientManager.cpp +++ b/src/db/ClientManager.cpp @@ -10,6 +10,7 @@ #include "Config.h" #include "DBManager.h" #include "QID.h" +#include "libsql/Statement.h" #include "GuildManager.h" #include "PrivManager.h" #include "MoneyLog.h" @@ -37,6 +38,186 @@ CPacketInfo g_item_info; int g_item_count = 0; int g_query_count[2]; +namespace +{ +bool PrepareClientPlayerStmt(CStmt& stmt, const std::string& query) +{ + CAsyncSQL* sql = CDBManager::instance().GetDirectSQL(SQL_PLAYER); + + if (!sql) + { + sys_err("player SQL handle is not initialized"); + return false; + } + + return stmt.Prepare(sql, query.c_str()); +} + +bool LoadSafeboxPassword(uint32_t accountId, char* password, size_t passwordSize, bool* foundRow) +{ + CStmt stmt; + const std::string query = std::string("SELECT password FROM safebox") + GetTablePostfix() + " WHERE account_id=?"; + + if (!PrepareClientPlayerStmt(stmt, query)) + return false; + + password[0] = '\0'; + *foundRow = false; + + if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId) + || !stmt.BindResult(MYSQL_TYPE_STRING, password, static_cast(passwordSize))) + { + return false; + } + + if (!stmt.Execute()) + return false; + + if (stmt.iRows == 0) + return true; + + if (!stmt.Fetch()) + return false; + + size_t passwordLen = stmt.GetResultLength(0); + if (passwordLen >= passwordSize) + passwordLen = passwordSize - 1; + + password[passwordLen] = '\0'; + *foundRow = true; + return true; +} + +bool LoadSafeboxTable(uint32_t accountId, const char* providedPassword, bool isMall, TSafeboxTable* safebox, bool* wrongPassword) +{ + CStmt stmt; + const std::string query = std::string("SELECT account_id, size, password FROM safebox") + GetTablePostfix() + " WHERE account_id=?"; + uint32_t loadedAccountId = 0; + uint32_t loadedSize = 0; + char storedPassword[SAFEBOX_PASSWORD_MAX_LEN + 1] = {}; + + if (!PrepareClientPlayerStmt(stmt, query)) + return false; + + *wrongPassword = false; + memset(safebox, 0, sizeof(TSafeboxTable)); + + if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId) + || !stmt.BindResult(MYSQL_TYPE_LONG, &loadedAccountId) + || !stmt.BindResult(MYSQL_TYPE_LONG, &loadedSize) + || !stmt.BindResult(MYSQL_TYPE_STRING, storedPassword, sizeof(storedPassword))) + { + return false; + } + + if (!stmt.Execute()) + return false; + + if (stmt.iRows == 0) + { + safebox->dwID = accountId; + *wrongPassword = strcmp("000000", providedPassword) != 0; + return true; + } + + if (!stmt.Fetch()) + return false; + + size_t passwordLen = stmt.GetResultLength(2); + if (passwordLen >= sizeof(storedPassword)) + passwordLen = sizeof(storedPassword) - 1; + + storedPassword[passwordLen] = '\0'; + + if (((passwordLen == 0) && strcmp("000000", providedPassword)) + || ((passwordLen != 0) && strcmp(storedPassword, providedPassword))) + { + *wrongPassword = true; + return true; + } + + safebox->dwID = loadedAccountId == 0 ? accountId : loadedAccountId; + safebox->bSize = static_cast(loadedSize); + + if (isMall) + { + safebox->bSize = 1; + sys_log(0, "MALL id[%u] size[%u]", safebox->dwID, safebox->bSize); + } + else + { + sys_log(0, "SAFEBOX id[%u] size[%u]", safebox->dwID, safebox->bSize); + } + + return true; +} + +void QueueSafeboxItemsLoad(CPeer* peer, CClientManager::ClientHandleInfo* info) +{ + char query[512]; + snprintf(query, sizeof(query), + "SELECT id, window+0, pos, count, vnum, socket0, socket1, socket2, " + "attrtype0, attrvalue0, " + "attrtype1, attrvalue1, " + "attrtype2, attrvalue2, " + "attrtype3, attrvalue3, " + "attrtype4, attrvalue4, " + "attrtype5, attrvalue5, " + "attrtype6, attrvalue6 " + "FROM item%s WHERE owner_id=%d AND window='%s'", + GetTablePostfix(), info->account_id, info->ip[0] == 0 ? "SAFEBOX" : "MALL"); + + CDBManager::instance().ReturnQuery(query, QID_SAFEBOX_LOAD, peer->GetHandle(), info); +} + +bool UpdateSafeboxPassword(uint32_t accountId, const char* password) +{ + CStmt stmt; + const std::string query = std::string("UPDATE safebox") + GetTablePostfix() + " SET password=? WHERE account_id=?"; + + if (!PrepareClientPlayerStmt(stmt, query)) + return false; + + if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast(password), SAFEBOX_PASSWORD_MAX_LEN + 1) + || !stmt.BindParam(MYSQL_TYPE_LONG, &accountId)) + { + return false; + } + + return stmt.Execute(); +} + +bool FindPlayerIdByName(const char* playerName, uint32_t* playerId) +{ + CStmt stmt; + std::string query; + + if (g_stLocale == "sjis") + query = std::string("SELECT id FROM player") + GetTablePostfix() + " WHERE name=? collate sjis_japanese_ci"; + else + query = std::string("SELECT id FROM player") + GetTablePostfix() + " WHERE name=?"; + + if (!PrepareClientPlayerStmt(stmt, query)) + return false; + + *playerId = 0; + + if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast(playerName), CHARACTER_NAME_MAX_LEN + 1) + || !stmt.BindResult(MYSQL_TYPE_LONG, playerId)) + { + return false; + } + + if (!stmt.Execute()) + return false; + + if (stmt.iRows == 0) + return true; + + return stmt.Fetch(); +} +} + CClientManager::CClientManager() : m_pkAuthPeer(NULL), m_iPlayerIDStart(0), @@ -451,15 +632,29 @@ void CClientManager::QUERY_SAFEBOX_LOAD(CPeer * pkPeer, DWORD dwHandle, TSafebox pi->ip[0] = bMall ? 1 : 0; strlcpy(pi->login, packet->szLogin, sizeof(pi->login)); - char szQuery[QUERY_MAX_LEN]; - snprintf(szQuery, sizeof(szQuery), - "SELECT account_id, size, password FROM safebox%s WHERE account_id=%u", - GetTablePostfix(), packet->dwID); - if (g_log) sys_log(0, "GD::SAFEBOX_LOAD (handle: %d account.id %u is_mall %d)", dwHandle, packet->dwID, bMall ? 1 : 0); - CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi); + TSafeboxTable* safebox = new TSafeboxTable; + bool wrongPassword = false; + + if (!LoadSafeboxTable(packet->dwID, pi->safebox_password, bMall, safebox, &wrongPassword)) + { + delete safebox; + delete pi; + return; + } + + if (wrongPassword) + { + delete safebox; + delete pi; + pkPeer->EncodeHeader(DG::SAFEBOX_WRONG_PASSWORD, dwHandle, 0); + return; + } + + pi->pSafebox = safebox; + QueueSafeboxItemsLoad(pkPeer, pi); } void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg) @@ -467,177 +662,89 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg) CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData; DWORD dwHandle = pi->dwHandle; - - // 여기에서 사용하는 account_index는 쿼리 순서를 말한다. - // 첫번째 패스워드 알아내기 위해 하는 쿼리가 0 - // 두번째 실제 데이터를 얻어놓는 쿼리가 1 - - if (pi->account_index == 0) + if (!pi->pSafebox) { - char szSafeboxPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; - strlcpy(szSafeboxPassword, pi->safebox_password, sizeof(szSafeboxPassword)); - - TSafeboxTable * pSafebox = new TSafeboxTable; - memset(pSafebox, 0, sizeof(TSafeboxTable)); - - SQLResult * res = msg->Get(); - - if (res->uiNumRows == 0) - { - if (strcmp("000000", szSafeboxPassword)) - { - pkPeer->EncodeHeader(DG::SAFEBOX_WRONG_PASSWORD, dwHandle, 0); - delete pSafebox; - delete pi; - return; - } - } - else - { - MYSQL_ROW row = mysql_fetch_row(res->pSQLResult); - - // 비밀번호가 틀리면.. - if (((!row[2] || !*row[2]) && strcmp("000000", szSafeboxPassword)) || - ((row[2] && *row[2]) && strcmp(row[2], szSafeboxPassword))) - { - pkPeer->EncodeHeader(DG::SAFEBOX_WRONG_PASSWORD, dwHandle, 0); - delete pSafebox; - delete pi; - return; - } - - if (!row[0]) - pSafebox->dwID = 0; - else - str_to_number(pSafebox->dwID, row[0]); - - if (!row[1]) - pSafebox->bSize = 0; - else - str_to_number(pSafebox->bSize, row[1]); - /* - if (!row[3]) - pSafebox->dwGold = 0; - else - pSafebox->dwGold = atoi(row[3]); - */ - if (pi->ip[0] == 1) - { - pSafebox->bSize = 1; - sys_log(0, "MALL id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize); - } - else - sys_log(0, "SAFEBOX id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize); - } - - if (0 == pSafebox->dwID) - pSafebox->dwID = pi->account_id; - - pi->pSafebox = pSafebox; - - char szQuery[512]; - snprintf(szQuery, sizeof(szQuery), - "SELECT id, window+0, pos, count, vnum, socket0, socket1, socket2, " - "attrtype0, attrvalue0, " - "attrtype1, attrvalue1, " - "attrtype2, attrvalue2, " - "attrtype3, attrvalue3, " - "attrtype4, attrvalue4, " - "attrtype5, attrvalue5, " - "attrtype6, attrvalue6 " - "FROM item%s WHERE owner_id=%d AND window='%s'", - GetTablePostfix(), pi->account_id, pi->ip[0] == 0 ? "SAFEBOX" : "MALL"); - - pi->account_index = 1; - - CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi); + sys_err("null safebox pointer!"); + delete pi; + return; } - else + + + // 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼 + // 보이기 때문에 창고가 아얘 안열리는게 나음 + if (!msg->Get()->pSQLResult) + { + sys_err("null safebox result"); + delete pi; + return; + } + + static std::vector s_items; + CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id); + + std::set * pSet = ItemAwardManager::instance().GetByLogin(pi->login); + + if (pSet && !m_vec_itemTable.empty()) { - if (!pi->pSafebox) + CGrid grid(5, MAX(1, pi->pSafebox->bSize) * 9); + bool bEscape = false; + + for (DWORD i = 0; i < s_items.size(); ++i) { - sys_err("null safebox pointer!"); - delete pi; - return; - } + TPlayerItem & r = s_items[i]; + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(r.vnum); - // 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼 - // 보이기 때문에 창고가 아얘 안열리는게 나음 - if (!msg->Get()->pSQLResult) - { - sys_err("null safebox result"); - delete pi; - return; - } - - static std::vector s_items; - CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id); - - std::set * pSet = ItemAwardManager::instance().GetByLogin(pi->login); - - if (pSet && !m_vec_itemTable.empty()) - { - - CGrid grid(5, MAX(1, pi->pSafebox->bSize) * 9); - bool bEscape = false; - - for (DWORD i = 0; i < s_items.size(); ++i) + if (it == m_map_itemTableByVnum.end()) { - TPlayerItem & r = s_items[i]; + bEscape = true; + sys_err("invalid item vnum %u in safebox: login %s", r.vnum, pi->login); + break; + } - itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(r.vnum); + grid.Put(r.pos, 1, it->second->bSize); + } + + if (!bEscape) + { + std::vector > vec_dwFinishedAwardID; + + __typeof(pSet->begin()) it = pSet->begin(); + + char szQuery[512]; + + while (it != pSet->end()) + { + TItemAward * pItemAward = *(it++); + const DWORD& dwItemVnum = pItemAward->dwVnum; + + if (pItemAward->bTaken) + continue; + + if (pi->ip[0] == 0 && pItemAward->bMall) + continue; + + if (pi->ip[0] == 1 && !pItemAward->bMall) + continue; + + itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(pItemAward->dwVnum); if (it == m_map_itemTableByVnum.end()) { - bEscape = true; - sys_err("invalid item vnum %u in safebox: login %s", r.vnum, pi->login); - break; + sys_err("invalid item vnum %u in item_award: login %s", pItemAward->dwVnum, pi->login); + continue; } - grid.Put(r.pos, 1, it->second->bSize); - } + TItemTable * pItemTable = it->second; - if (!bEscape) - { - std::vector > vec_dwFinishedAwardID; + int iPos; - __typeof(pSet->begin()) it = pSet->begin(); + if ((iPos = grid.FindBlank(1, it->second->bSize)) == -1) + break; - char szQuery[512]; - - while (it != pSet->end()) - { - TItemAward * pItemAward = *(it++); - const DWORD& dwItemVnum = pItemAward->dwVnum; - - if (pItemAward->bTaken) - continue; - - if (pi->ip[0] == 0 && pItemAward->bMall) - continue; - - if (pi->ip[0] == 1 && !pItemAward->bMall) - continue; - - itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(pItemAward->dwVnum); - - if (it == m_map_itemTableByVnum.end()) - { - sys_err("invalid item vnum %u in item_award: login %s", pItemAward->dwVnum, pi->login); - continue; - } - - TItemTable * pItemTable = it->second; - - int iPos; - - if ((iPos = grid.FindBlank(1, it->second->bSize)) == -1) - break; - - TPlayerItem item; - memset(&item, 0, sizeof(TPlayerItem)); + TPlayerItem item; + memset(&item, 0, sizeof(TPlayerItem)); // DWORD dwSocket2 = 0; DWORD dwSocket2 = pItemAward->dwSocket2; //Fix @@ -766,22 +873,21 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg) grid.Put(iPos, 1, it->second->bSize); } - for (DWORD i = 0; i < vec_dwFinishedAwardID.size(); ++i) - ItemAwardManager::instance().Taken(vec_dwFinishedAwardID[i].first, vec_dwFinishedAwardID[i].second); - } + for (DWORD i = 0; i < vec_dwFinishedAwardID.size(); ++i) + ItemAwardManager::instance().Taken(vec_dwFinishedAwardID[i].first, vec_dwFinishedAwardID[i].second); } - - pi->pSafebox->wItemCount = s_items.size(); - - pkPeer->EncodeHeader(pi->ip[0] == 0 ? DG::SAFEBOX_LOAD : DG::MALL_LOAD, dwHandle, sizeof(TSafeboxTable) + sizeof(TPlayerItem) * s_items.size()); - - pkPeer->Encode(pi->pSafebox, sizeof(TSafeboxTable)); - - if (!s_items.empty()) - pkPeer->Encode(&s_items[0], sizeof(TPlayerItem) * s_items.size()); - - delete pi; } + + pi->pSafebox->wItemCount = s_items.size(); + + pkPeer->EncodeHeader(pi->ip[0] == 0 ? DG::SAFEBOX_LOAD : DG::MALL_LOAD, dwHandle, sizeof(TSafeboxTable) + sizeof(TPlayerItem) * s_items.size()); + + pkPeer->Encode(pi->pSafebox, sizeof(TSafeboxTable)); + + if (!s_items.empty()) + pkPeer->Encode(&s_items[0], sizeof(TPlayerItem) * s_items.size()); + + delete pi; } void CClientManager::QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p) @@ -817,15 +923,23 @@ void CClientManager::RESULT_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, SQLMsg * msg) void CClientManager::QUERY_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangePasswordPacket * p) { - ClientHandleInfo * pi = new ClientHandleInfo(dwHandle); - strlcpy(pi->safebox_password, p->szNewPassword, sizeof(pi->safebox_password)); - strlcpy(pi->login, p->szOldPassword, sizeof(pi->login)); - pi->account_id = p->dwID; + BYTE result = 0; + char storedPassword[SAFEBOX_PASSWORD_MAX_LEN + 1]; + bool foundRow = false; - char szQuery[QUERY_MAX_LEN]; - snprintf(szQuery, sizeof(szQuery), "SELECT password FROM safebox%s WHERE account_id=%u", GetTablePostfix(), p->dwID); + if (LoadSafeboxPassword(p->dwID, storedPassword, sizeof(storedPassword), &foundRow)) + { + const bool oldPasswordMatches = + (foundRow + && ((storedPassword[0] && !strcasecmp(storedPassword, p->szOldPassword)) + || (!storedPassword[0] && !strcmp("000000", p->szOldPassword)))); - CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD, pkPeer->GetHandle(), pi); + if (oldPasswordMatches && UpdateSafeboxPassword(p->dwID, p->szNewPassword)) + result = 1; + } + + pkPeer->EncodeHeader(DG::SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE)); + pkPeer->EncodeBYTE(result); } void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg) @@ -833,29 +947,20 @@ void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg CQueryInfo * qi = (CQueryInfo *) msg->pvUserData; ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData; DWORD dwHandle = p->dwHandle; + BYTE result = 0; if (msg->Get()->uiNumRows > 0) { MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult); if ((row[0] && *row[0] && !strcasecmp(row[0], p->login)) || ((!row[0] || !*row[0]) && !strcmp("000000", p->login))) - { - char szQuery[QUERY_MAX_LEN]; - char escape_pwd[64]; - CDBManager::instance().EscapeString(escape_pwd, p->safebox_password, strlen(p->safebox_password)); - - snprintf(szQuery, sizeof(szQuery), "UPDATE safebox%s SET password='%s' WHERE account_id=%u", GetTablePostfix(), escape_pwd, p->account_id); - - CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD_SECOND, pkPeer->GetHandle(), p); - return; - } + result = UpdateSafeboxPassword(p->account_id, p->safebox_password) ? 1 : 0; } delete p; - // Wrong old password pkPeer->EncodeHeader(DG::SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE)); - pkPeer->EncodeBYTE(0); + pkPeer->EncodeBYTE(result); } void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(CPeer * pkPeer, SQLMsg * msg) @@ -1841,21 +1946,10 @@ void CClientManager::UpdateLand(DWORD * pdw) // BLOCK_CHAT void CClientManager::BlockChat(TPacketBlockChat* p) { - char szQuery[256]; + DWORD pid = 0; - if (g_stLocale == "sjis") - snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player%s WHERE name = '%s' collate sjis_japanese_ci", GetTablePostfix(), p->szName); - else - snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player%s WHERE name = '%s'", GetTablePostfix(), p->szName); - - auto pmsg = CDBManager::instance().DirectQuery(szQuery); - SQLResult * pRes = pmsg->Get(); - - if (pRes->uiNumRows) + if (FindPlayerIdByName(p->szName, &pid) && pid != 0) { - MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); - DWORD pid = strtoul(row[0], NULL, 10); - TPacketGDAddAffect pa; pa.dwPID = pid; pa.elem.dwType = 223;