db: prepare player create and delete queries

This commit is contained in:
server
2026-04-13 22:41:36 +02:00
parent 30eefceabf
commit 77ba7381b5

View File

@@ -8,6 +8,7 @@
#include "ItemAwardManager.h"
#include "HB.h"
#include "Cache.h"
#include "libsql/Statement.h"
extern bool g_bHotBackup;
@@ -15,6 +16,22 @@ extern std::string g_stLocale;
extern int g_test_server;
extern int g_log;
namespace
{
bool PreparePlayerStmt(CStmt& stmt, const std::string& query, int slot = SQL_PLAYER)
{
CAsyncSQL* sql = CDBManager::instance().GetDirectSQL(slot);
if (!sql)
{
sys_err("player SQL handle is not initialized");
return false;
}
return stmt.Prepare(sql, query.c_str());
}
}
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!! IMPORTANT !!!!!!!!!!!!
@@ -810,9 +827,7 @@ static time_by_id_map_t s_createTimeByAccountID;
*/
void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerCreatePacket* packet)
{
char queryStr[QUERY_MAX_LEN];
int queryLen;
int player_id;
uint32_t player_id = 0;
// 한 계정에 X초 내로 캐릭터 생성을 할 수 없다.
time_by_id_map_t::iterator it = s_createTimeByAccountID.find(packet->account_id);
@@ -828,81 +843,53 @@ void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerC
}
}
queryLen = snprintf(queryStr, sizeof(queryStr),
"SELECT pid%u FROM player_index%s WHERE id=%d", packet->account_index + 1, GetTablePostfix(), packet->account_id);
uint32_t existingPid = 0;
CStmt playerIndexStmt;
const std::string playerIndexQuery = "SELECT pid" + std::to_string(packet->account_index + 1)
+ " FROM player_index" + std::string(GetTablePostfix()) + " WHERE id=?";
auto pMsg0 = CDBManager::instance().DirectQuery(queryStr);
if (pMsg0->Get()->uiNumRows != 0)
{
if (!pMsg0->Get()->pSQLResult)
{
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
MYSQL_ROW row = mysql_fetch_row(pMsg0->Get()->pSQLResult);
DWORD dwPID = 0; str_to_number(dwPID, row[0]);
if (row[0] && dwPID > 0)
{
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
sys_log(0, "ALREADY EXIST AccountChrIdx %d ID %d", packet->account_index, dwPID);
return;
}
}
else
if (!PreparePlayerStmt(playerIndexStmt, playerIndexQuery)
|| !playerIndexStmt.BindParam(MYSQL_TYPE_LONG, &packet->account_id)
|| !playerIndexStmt.BindResult(MYSQL_TYPE_LONG, &existingPid)
|| !playerIndexStmt.Execute()
|| playerIndexStmt.iRows == 0
|| !playerIndexStmt.Fetch())
{
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
if (existingPid > 0)
{
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
sys_log(0, "ALREADY EXIST AccountChrIdx %d ID %d", packet->account_index, existingPid);
return;
}
unsigned long long nameCount = 0;
CStmt playerNameStmt;
std::string playerNameQuery = "SELECT COUNT(*) FROM player" + std::string(GetTablePostfix()) + " WHERE name=?";
if (g_stLocale == "sjis")
snprintf(queryStr, sizeof(queryStr),
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci",
GetTablePostfix(), packet->player_table.name);
else
snprintf(queryStr, sizeof(queryStr),
"SELECT COUNT(*) as count FROM player%s WHERE name='%s'", GetTablePostfix(), packet->player_table.name);
playerNameQuery += " collate sjis_japanese_ci";
auto pMsg1 = CDBManager::instance().DirectQuery(queryStr);
if (pMsg1->Get()->uiNumRows)
{
if (!pMsg1->Get()->pSQLResult)
{
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
MYSQL_ROW row = mysql_fetch_row(pMsg1->Get()->pSQLResult);
if (*row[0] != '0')
{
sys_log(0, "ALREADY EXIST name %s, row[0] %s query %s", packet->player_table.name, row[0], queryStr);
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
return;
}
}
else
if (!PreparePlayerStmt(playerNameStmt, playerNameQuery)
|| !playerNameStmt.BindParam(MYSQL_TYPE_STRING, packet->player_table.name, sizeof(packet->player_table.name))
|| !playerNameStmt.BindResult(MYSQL_TYPE_LONGLONG, &nameCount)
|| !playerNameStmt.Execute()
|| playerNameStmt.iRows == 0
|| !playerNameStmt.Fetch())
{
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
queryLen = snprintf(queryStr, sizeof(queryStr),
"INSERT INTO player%s "
"(id, account_id, name, level, st, ht, dx, iq, "
"job, voice, dir, x, y, z, "
"hp, mp, random_hp, random_sp, stat_point, stamina, part_base, part_main, part_hair, gold, playtime, "
"skill_level, quickslot) "
"VALUES(0, %u, '%s', %d, %d, %d, %d, %d, "
"%d, %d, %d, %d, %d, %d, %d, "
"%d, %d, %d, %d, %d, %d, %d, 0, %d, 0, ",
GetTablePostfix(),
packet->account_id, packet->player_table.name, packet->player_table.level, packet->player_table.st, packet->player_table.ht, packet->player_table.dx, packet->player_table.iq,
packet->player_table.job, packet->player_table.voice, packet->player_table.dir, packet->player_table.x, packet->player_table.y, packet->player_table.z,
packet->player_table.hp, packet->player_table.sp, packet->player_table.sRandomHP, packet->player_table.sRandomSP, packet->player_table.stat_point, packet->player_table.stamina, packet->player_table.part_base, packet->player_table.part_base, packet->player_table.gold);
if (nameCount > 0)
{
sys_log(0, "ALREADY EXIST name %s", packet->player_table.name);
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
return;
}
sys_log(0, "PlayerCreate accountid %d name %s level %d gold %d, st %d ht %d job %d",
packet->account_id,
@@ -913,39 +900,77 @@ void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerC
packet->player_table.ht,
packet->player_table.job);
static char text[8192 + 1];
CStmt createPlayerStmt;
uint16_t partHair = 0;
int32_t playtime = 0;
const std::string createPlayerQuery = "INSERT INTO player" + std::string(GetTablePostfix()) +
" (id, account_id, name, level, st, ht, dx, iq, "
"job, voice, dir, x, y, z, "
"hp, mp, random_hp, random_sp, stat_point, stamina, part_base, part_main, part_hair, gold, playtime, "
"skill_level, quickslot) "
"VALUES(0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
CDBManager::instance().EscapeString(text, packet->player_table.skills, sizeof(packet->player_table.skills));
queryLen += snprintf(queryStr + queryLen, sizeof(queryStr) - queryLen, "'%s', ", text);
if (g_test_server)
sys_log(0, "Create_Player queryLen[%d] TEXT[%s]", queryLen, text);
CDBManager::instance().EscapeString(text, packet->player_table.quickslot, sizeof(packet->player_table.quickslot));
queryLen += snprintf(queryStr + queryLen, sizeof(queryStr) - queryLen, "'%s')", text);
auto pMsg2 = CDBManager::instance().DirectQuery(queryStr);
if (g_test_server)
sys_log(0, "Create_Player queryLen[%d] TEXT[%s]", queryLen, text);
if (pMsg2->Get()->uiAffectedRows <= 0)
if (!PreparePlayerStmt(createPlayerStmt, createPlayerQuery)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->account_id)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_STRING, packet->player_table.name, sizeof(packet->player_table.name))
|| !createPlayerStmt.BindParam(MYSQL_TYPE_TINY, &packet->player_table.level)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &packet->player_table.st)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &packet->player_table.ht)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &packet->player_table.dx)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &packet->player_table.iq)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &packet->player_table.job)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_TINY, &packet->player_table.voice)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_TINY, &packet->player_table.dir)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.x)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.y)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.z)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.hp)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.sp)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.sRandomHP)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.sRandomSP)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &packet->player_table.stat_point)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &packet->player_table.stamina)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_TINY, &packet->player_table.part_base)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_TINY, &packet->player_table.part_base)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_SHORT, &partHair)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &packet->player_table.gold)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_LONG, &playtime)
|| !createPlayerStmt.BindParam(MYSQL_TYPE_BLOB, packet->player_table.skills, sizeof(packet->player_table.skills))
|| !createPlayerStmt.BindParam(MYSQL_TYPE_BLOB, packet->player_table.quickslot, sizeof(packet->player_table.quickslot)))
{
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
sys_log(0, "ALREADY EXIST3 query: %s AffectedRows %lu", queryStr, pMsg2->Get()->uiAffectedRows);
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
player_id = pMsg2->Get()->uiInsertID;
snprintf(queryStr, sizeof(queryStr), "UPDATE player_index%s SET pid%d=%d WHERE id=%d",
GetTablePostfix(), packet->account_index + 1, player_id, packet->account_id);
auto pMsg3 = CDBManager::instance().DirectQuery(queryStr);
if (pMsg3->Get()->uiAffectedRows <= 0)
if (!createPlayerStmt.Execute() || createPlayerStmt.GetAffectedRows() <= 0)
{
sys_err("QUERY_ERROR: %s", queryStr);
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
sys_log(0, "ALREADY EXIST3 name %s affected_rows %llu", packet->player_table.name, createPlayerStmt.GetAffectedRows());
return;
}
snprintf(queryStr, sizeof(queryStr), "DELETE FROM player%s WHERE id=%d", GetTablePostfix(), player_id);
CDBManager::instance().DirectQuery(queryStr);
player_id = static_cast<uint32_t>(createPlayerStmt.GetInsertId());
CStmt updatePlayerIndexStmt;
const std::string updatePlayerIndexQuery = "UPDATE player_index" + std::string(GetTablePostfix())
+ " SET pid" + std::to_string(packet->account_index + 1) + "=? WHERE id=?";
if (!PreparePlayerStmt(updatePlayerIndexStmt, updatePlayerIndexQuery)
|| !updatePlayerIndexStmt.BindParam(MYSQL_TYPE_LONG, &player_id)
|| !updatePlayerIndexStmt.BindParam(MYSQL_TYPE_LONG, &packet->account_id)
|| !updatePlayerIndexStmt.Execute()
|| updatePlayerIndexStmt.GetAffectedRows() <= 0)
{
sys_err("QUERY_ERROR: failed to update player_index for account %u pid %u", packet->account_id, player_id);
CStmt rollbackDeleteStmt;
const std::string rollbackDeleteQuery = "DELETE FROM player" + std::string(GetTablePostfix()) + " WHERE id=?";
if (PreparePlayerStmt(rollbackDeleteStmt, rollbackDeleteQuery))
{
rollbackDeleteStmt.BindParam(MYSQL_TYPE_LONG, &player_id);
rollbackDeleteStmt.Execute();
}
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
@@ -1082,12 +1107,15 @@ void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
}
char queryStr[QUERY_MAX_LEN];
CStmt archivePlayerStmt;
const std::string archivePlayerQuery = "INSERT INTO player" + std::string(GetTablePostfix())
+ "_deleted SELECT * FROM player" + std::string(GetTablePostfix()) + " WHERE id=?";
snprintf(queryStr, sizeof(queryStr), "INSERT INTO player%s_deleted SELECT * FROM player%s WHERE id=%d",
GetTablePostfix(), GetTablePostfix(), pi->player_id);
auto pIns = CDBManager::instance().DirectQuery(queryStr);
if (pIns->Get()->uiAffectedRows == 0 || pIns->Get()->uiAffectedRows == (uint32_t)-1)
if (!PreparePlayerStmt(archivePlayerStmt, archivePlayerQuery)
|| !archivePlayerStmt.BindParam(MYSQL_TYPE_LONG, &pi->player_id)
|| !archivePlayerStmt.Execute()
|| archivePlayerStmt.GetAffectedRows() == 0
|| archivePlayerStmt.GetAffectedRows() == static_cast<unsigned long long>(-1))
{
sys_log(0, "PLAYER_DELETE FAILED %u CANNOT INSERT TO player%s_deleted", dwPID, GetTablePostfix());
@@ -1099,10 +1127,6 @@ void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
// 삭제 성공
sys_log(0, "PLAYER_DELETE SUCCESS %u", dwPID);
char account_index_string[16];
snprintf(account_index_string, sizeof(account_index_string), "player_id%d", m_iPlayerIDStart + pi->account_index);
// 플레이어 테이블을 캐쉬에서 삭제한다.
CPlayerTableCache * pkPlayerCache = GetPlayerCache(pi->player_id);
@@ -1131,15 +1155,15 @@ void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
m_map_pkItemCacheSetPtr.erase(pi->player_id);
}
snprintf(queryStr, sizeof(queryStr), "UPDATE player_index%s SET pid%u=0 WHERE pid%u=%d",
GetTablePostfix(),
pi->account_index + 1,
pi->account_index + 1,
pi->player_id);
CStmt resetPlayerIndexStmt;
const std::string resetPlayerIndexQuery = "UPDATE player_index" + std::string(GetTablePostfix())
+ " SET pid" + std::to_string(pi->account_index + 1) + "=0 WHERE pid" + std::to_string(pi->account_index + 1) + "=?";
auto pMsg = CDBManager::instance().DirectQuery(queryStr);
if (pMsg->Get()->uiAffectedRows == 0 || pMsg->Get()->uiAffectedRows == (uint32_t)-1)
if (!PreparePlayerStmt(resetPlayerIndexStmt, resetPlayerIndexQuery)
|| !resetPlayerIndexStmt.BindParam(MYSQL_TYPE_LONG, &pi->player_id)
|| !resetPlayerIndexStmt.Execute()
|| resetPlayerIndexStmt.GetAffectedRows() == 0
|| resetPlayerIndexStmt.GetAffectedRows() == static_cast<unsigned long long>(-1))
{
sys_log(0, "PLAYER_DELETE FAIL WHEN UPDATE account table");
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, pi->dwHandle, 1);
@@ -1147,11 +1171,26 @@ void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
return;
}
snprintf(queryStr, sizeof(queryStr), "DELETE FROM player%s WHERE id=%d", GetTablePostfix(), pi->player_id);
CDBManager::instance().DirectQuery(queryStr);
CStmt deletePlayerStmt;
const std::string deletePlayerQuery = "DELETE FROM player" + std::string(GetTablePostfix()) + " WHERE id=?";
if (PreparePlayerStmt(deletePlayerStmt, deletePlayerQuery))
{
deletePlayerStmt.BindParam(MYSQL_TYPE_LONG, &pi->player_id);
deletePlayerStmt.Execute();
}
snprintf(queryStr, sizeof(queryStr), "DELETE FROM item%s WHERE owner_id=%d AND (window < %d or window = %d)", GetTablePostfix(), pi->player_id, SAFEBOX, DRAGON_SOUL_INVENTORY);
CDBManager::instance().DirectQuery(queryStr);
CStmt deleteItemStmt;
int32_t safeboxWindow = SAFEBOX;
int32_t dragonSoulInventory = DRAGON_SOUL_INVENTORY;
const std::string deleteItemQuery = "DELETE FROM item" + std::string(GetTablePostfix())
+ " WHERE owner_id=? AND (window < ? or window = ?)";
if (PreparePlayerStmt(deleteItemStmt, deleteItemQuery))
{
deleteItemStmt.BindParam(MYSQL_TYPE_LONG, &pi->player_id);
deleteItemStmt.BindParam(MYSQL_TYPE_LONG, &safeboxWindow);
deleteItemStmt.BindParam(MYSQL_TYPE_LONG, &dragonSoulInventory);
deleteItemStmt.Execute();
}
snprintf(queryStr, sizeof(queryStr), "DELETE FROM quest%s WHERE dwPID=%d", GetTablePostfix(), pi->player_id);
CDBManager::instance().AsyncQuery(queryStr);
@@ -1372,4 +1411,3 @@ void CClientManager::FlushPlayerCacheSet(DWORD pid)
delete c;
}
}