db: prepare player create flow
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include "ItemAwardManager.h"
|
#include "ItemAwardManager.h"
|
||||||
#include "HB.h"
|
#include "HB.h"
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
|
#include "libsql/Statement.h"
|
||||||
|
|
||||||
extern bool g_bHotBackup;
|
extern bool g_bHotBackup;
|
||||||
|
|
||||||
@@ -15,6 +16,181 @@ extern std::string g_stLocale;
|
|||||||
extern int g_test_server;
|
extern int g_test_server;
|
||||||
extern int g_log;
|
extern int g_log;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool PreparePlayerStmt(CStmt& stmt, const char* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoadPlayerIndexSlot(DWORD accountId, BYTE accountIndex, DWORD* playerId, bool* foundRow)
|
||||||
|
{
|
||||||
|
char query[QUERY_MAX_LEN];
|
||||||
|
snprintf(query, sizeof(query), "SELECT pid%u FROM player_index%s WHERE id = ?", accountIndex + 1, GetTablePostfix());
|
||||||
|
|
||||||
|
*playerId = 0;
|
||||||
|
*foundRow = false;
|
||||||
|
|
||||||
|
CStmt stmt;
|
||||||
|
if (!PreparePlayerStmt(stmt, query))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindResult(MYSQL_TYPE_LONG, playerId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.Execute())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stmt.iRows == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!stmt.Fetch())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*foundRow = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CountPlayersByName(const char* playerName, 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",
|
||||||
|
GetTablePostfix());
|
||||||
|
else
|
||||||
|
snprintf(query, sizeof(query),
|
||||||
|
"SELECT COUNT(*) FROM player%s WHERE name = ?",
|
||||||
|
GetTablePostfix());
|
||||||
|
|
||||||
|
*count = 0;
|
||||||
|
|
||||||
|
CStmt stmt;
|
||||||
|
if (!PreparePlayerStmt(stmt, query))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(playerName), CHARACTER_NAME_MAX_LEN + 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindResult(MYSQL_TYPE_LONGLONG, count))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.Execute())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stmt.iRows == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return stmt.Fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InsertPlayerRecord(const TPlayerCreatePacket* packet, DWORD* playerId)
|
||||||
|
{
|
||||||
|
char query[QUERY_MAX_LEN];
|
||||||
|
snprintf(query, sizeof(query),
|
||||||
|
"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, ?, %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.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);
|
||||||
|
|
||||||
|
CStmt stmt;
|
||||||
|
if (!PreparePlayerStmt(stmt, query))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(packet->player_table.name), CHARACTER_NAME_MAX_LEN + 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_BLOB, const_cast<TPlayerSkill*>(packet->player_table.skills), sizeof(packet->player_table.skills)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_BLOB, const_cast<TQuickslot*>(packet->player_table.quickslot), sizeof(packet->player_table.quickslot)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.Execute())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stmt.GetAffectedRows() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*playerId = static_cast<DWORD>(stmt.GetInsertId());
|
||||||
|
return *playerId != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UpdatePlayerIndexSlot(DWORD accountId, BYTE accountIndex, DWORD playerId)
|
||||||
|
{
|
||||||
|
char query[QUERY_MAX_LEN];
|
||||||
|
snprintf(query, sizeof(query), "UPDATE player_index%s SET pid%u = ? WHERE id = ?", GetTablePostfix(), accountIndex + 1);
|
||||||
|
|
||||||
|
CStmt stmt;
|
||||||
|
if (!PreparePlayerStmt(stmt, query))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_LONG, &playerId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!stmt.Execute())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return stmt.GetAffectedRows() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePlayerById(DWORD playerId)
|
||||||
|
{
|
||||||
|
char query[QUERY_MAX_LEN];
|
||||||
|
snprintf(query, sizeof(query), "DELETE FROM player%s WHERE id = ?", GetTablePostfix());
|
||||||
|
|
||||||
|
CStmt stmt;
|
||||||
|
if (!PreparePlayerStmt(stmt, query))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!stmt.BindParam(MYSQL_TYPE_LONG, &playerId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
stmt.Execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
// !!!!!!!!!!! IMPORTANT !!!!!!!!!!!!
|
// !!!!!!!!!!! IMPORTANT !!!!!!!!!!!!
|
||||||
@@ -810,9 +986,7 @@ static time_by_id_map_t s_createTimeByAccountID;
|
|||||||
*/
|
*/
|
||||||
void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerCreatePacket* packet)
|
void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerCreatePacket* packet)
|
||||||
{
|
{
|
||||||
char queryStr[QUERY_MAX_LEN];
|
DWORD player_id = 0;
|
||||||
int queryLen;
|
|
||||||
int player_id;
|
|
||||||
|
|
||||||
// 한 계정에 X초 내로 캐릭터 생성을 할 수 없다.
|
// 한 계정에 X초 내로 캐릭터 생성을 할 수 없다.
|
||||||
time_by_id_map_t::iterator it = s_createTimeByAccountID.find(packet->account_id);
|
time_by_id_map_t::iterator it = s_createTimeByAccountID.find(packet->account_id);
|
||||||
@@ -828,81 +1002,40 @@ void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queryLen = snprintf(queryStr, sizeof(queryStr),
|
DWORD existingPlayerId = 0;
|
||||||
"SELECT pid%u FROM player_index%s WHERE id=%d", packet->account_index + 1, GetTablePostfix(), packet->account_id);
|
bool foundPlayerIndexRow = false;
|
||||||
|
if (!LoadPlayerIndexSlot(packet->account_id, packet->account_index, &existingPlayerId, &foundPlayerIndexRow))
|
||||||
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
|
|
||||||
{
|
{
|
||||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_stLocale == "sjis")
|
if (!foundPlayerIndexRow)
|
||||||
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);
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
queryLen = snprintf(queryStr, sizeof(queryStr),
|
if (existingPlayerId > 0)
|
||||||
"INSERT INTO player%s "
|
{
|
||||||
"(id, account_id, name, level, st, ht, dx, iq, "
|
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
|
||||||
"job, voice, dir, x, y, z, "
|
sys_log(0, "ALREADY EXIST AccountChrIdx %d ID %d", packet->account_index, existingPlayerId);
|
||||||
"hp, mp, random_hp, random_sp, stat_point, stamina, part_base, part_main, part_hair, gold, playtime, "
|
return;
|
||||||
"skill_level, quickslot) "
|
}
|
||||||
"VALUES(0, %u, '%s', %d, %d, %d, %d, %d, "
|
|
||||||
"%d, %d, %d, %d, %d, %d, %d, "
|
unsigned long long playerCount = 0;
|
||||||
"%d, %d, %d, %d, %d, %d, %d, 0, %d, 0, ",
|
if (!CountPlayersByName(packet->player_table.name, &playerCount))
|
||||||
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,
|
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||||
packet->player_table.job, packet->player_table.voice, packet->player_table.dir, packet->player_table.x, packet->player_table.y, packet->player_table.z,
|
return;
|
||||||
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 (playerCount != 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",
|
sys_log(0, "PlayerCreate accountid %d name %s level %d gold %d, st %d ht %d job %d",
|
||||||
packet->account_id,
|
packet->account_id,
|
||||||
@@ -913,40 +1046,16 @@ void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerC
|
|||||||
packet->player_table.ht,
|
packet->player_table.ht,
|
||||||
packet->player_table.job);
|
packet->player_table.job);
|
||||||
|
|
||||||
static char text[8192 + 1];
|
if (!InsertPlayerRecord(packet, &player_id))
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
|
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
|
||||||
sys_log(0, "ALREADY EXIST3 query: %s AffectedRows %lu", queryStr, pMsg2->Get()->uiAffectedRows);
|
sys_log(0, "ALREADY EXIST3 name %s", packet->player_table.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
player_id = pMsg2->Get()->uiInsertID;
|
if (!UpdatePlayerIndexSlot(packet->account_id, packet->account_index, player_id))
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
sys_err("QUERY_ERROR: %s", queryStr);
|
DeletePlayerById(player_id);
|
||||||
|
|
||||||
snprintf(queryStr, sizeof(queryStr), "DELETE FROM player%s WHERE id=%d", GetTablePostfix(), player_id);
|
|
||||||
CDBManager::instance().DirectQuery(queryStr);
|
|
||||||
|
|
||||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1372,4 +1481,3 @@ void CClientManager::FlushPlayerCacheSet(DWORD pid)
|
|||||||
delete c;
|
delete c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -171,3 +171,19 @@ bool CStmt::Fetch()
|
|||||||
{
|
{
|
||||||
return !mysql_stmt_fetch(m_pkStmt);
|
return !mysql_stmt_fetch(m_pkStmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long long CStmt::GetInsertId() const
|
||||||
|
{
|
||||||
|
if (!m_pkStmt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mysql_stmt_insert_id(m_pkStmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long CStmt::GetAffectedRows() const
|
||||||
|
{
|
||||||
|
if (!m_pkStmt)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mysql_stmt_affected_rows(m_pkStmt);
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class CStmt
|
|||||||
bool BindResult(enum_field_types type, void * p, int iMaxLen=0);
|
bool BindResult(enum_field_types type, void * p, int iMaxLen=0);
|
||||||
int Execute();
|
int Execute();
|
||||||
bool Fetch();
|
bool Fetch();
|
||||||
|
unsigned long long GetInsertId() const;
|
||||||
|
unsigned long long GetAffectedRows() const;
|
||||||
|
|
||||||
void Error(const char * c_pszMsg);
|
void Error(const char * c_pszMsg);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user