db: prepare safebox and player lookups

This commit is contained in:
server
2026-04-13 21:17:19 +02:00
parent ccc1a8899d
commit 44720d0691

View File

@@ -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<int>(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<BYTE>(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<char*>(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<char*>(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<TPlayerItem> s_items;
CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id);
std::set<TItemAward *> * 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<TPlayerItem> s_items;
CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id);
std::set<TItemAward *> * 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<std::pair<DWORD, DWORD> > 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<std::pair<DWORD, DWORD> > 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;