diff --git a/src/db/ClientManagerLogin.cpp b/src/db/ClientManagerLogin.cpp index ad092c1..3078ae8 100644 --- a/src/db/ClientManagerLogin.cpp +++ b/src/db/ClientManagerLogin.cpp @@ -7,12 +7,242 @@ #include "Config.h" #include "QID.h" #include "Cache.h" +#include "libsql/Statement.h" extern std::string g_stLocale; extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab); extern int g_test_server; extern int g_log; +namespace +{ +bool PreparePlayerStmt(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 LoadPlayerIndexTable(uint32_t accountId, TAccountTable* accountTable) +{ + CStmt stmt; + const std::string query = std::string("SELECT pid1, pid2, pid3, pid4, empire FROM player_index") + GetTablePostfix() + " WHERE id=?"; + uint32_t playerIds[PLAYER_PER_ACCOUNT] = {}; + uint8_t empire = 0; + + if (!PreparePlayerStmt(stmt, query)) + return false; + + if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)) + return false; + + for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i) + { + if (!stmt.BindResult(MYSQL_TYPE_LONG, &playerIds[i])) + return false; + } + + if (!stmt.BindResult(MYSQL_TYPE_TINY, &empire)) + return false; + + if (!stmt.Execute() || !stmt.Fetch()) + return false; + + for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i) + accountTable->players[i].dwID = playerIds[i]; + + accountTable->bEmpire = empire; + return true; +} + +bool CreatePlayerIndexRow(uint32_t accountId) +{ + CStmt stmt; + const std::string query = std::string("INSERT INTO player_index") + GetTablePostfix() + " (id) VALUES(?)"; + + if (!PreparePlayerStmt(stmt, query)) + return false; + + if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)) + return false; + + return stmt.Execute(); +} + +void ApplyPlayerSummary(TAccountTable* accountTable, uint32_t playerId, const char* name, uint32_t job, uint32_t level, uint32_t progressValue, + int32_t st, int32_t ht, int32_t dx, int32_t iq, uint32_t mainPart, uint32_t hairPart, int32_t x, int32_t y, uint32_t skillGroup, uint32_t changeName) +{ + for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i) + { + if (accountTable->players[i].dwID != playerId) + continue; + + CPlayerTableCache* cache = CClientManager::instance().GetPlayerCache(playerId); + TPlayerTable* playerTable = cache ? cache->Get(false) : NULL; + + if (playerTable) + { + strlcpy(accountTable->players[i].szName, playerTable->name, sizeof(accountTable->players[i].szName)); + accountTable->players[i].byJob = playerTable->job; + accountTable->players[i].byLevel = playerTable->level; + accountTable->players[i].dwPlayMinutes = playerTable->playtime; + accountTable->players[i].byST = playerTable->st; + accountTable->players[i].byHT = playerTable->ht; + accountTable->players[i].byDX = playerTable->dx; + accountTable->players[i].byIQ = playerTable->iq; + accountTable->players[i].wMainPart = playerTable->parts[PART_MAIN]; + accountTable->players[i].wHairPart = playerTable->parts[PART_HAIR]; + accountTable->players[i].x = playerTable->x; + accountTable->players[i].y = playerTable->y; + accountTable->players[i].skill_group = playerTable->skill_group; + accountTable->players[i].bChangeName = 0; + } + else + { + strlcpy(accountTable->players[i].szName, name, sizeof(accountTable->players[i].szName)); + accountTable->players[i].byJob = static_cast(job); + accountTable->players[i].byLevel = static_cast(level); + accountTable->players[i].dwPlayMinutes = progressValue; + accountTable->players[i].byST = static_cast(st); + accountTable->players[i].byHT = static_cast(ht); + accountTable->players[i].byDX = static_cast(dx); + accountTable->players[i].byIQ = static_cast(iq); + accountTable->players[i].wMainPart = static_cast(mainPart); + accountTable->players[i].wHairPart = static_cast(hairPart); + accountTable->players[i].x = x; + accountTable->players[i].y = y; + accountTable->players[i].skill_group = static_cast(skillGroup); + accountTable->players[i].bChangeName = static_cast(changeName); + } + + sys_log(0, "%s %lu %lu hair %u", + accountTable->players[i].szName, + accountTable->players[i].x, + accountTable->players[i].y, + accountTable->players[i].wHairPart); + return; + } +} + +bool LoadAccountPlayerSummaries(uint32_t accountId, TAccountTable* accountTable) +{ + CStmt stmt; + std::string query; + uint32_t playerId = 0; + char name[CHARACTER_NAME_MAX_LEN + 1] = {}; + uint32_t job = 0; + uint32_t level = 0; + uint32_t progressValue = 0; + int32_t st = 0; + int32_t ht = 0; + int32_t dx = 0; + int32_t iq = 0; + uint32_t mainPart = 0; + uint32_t hairPart = 0; + int32_t x = 0; + int32_t y = 0; + uint32_t skillGroup = 0; + uint32_t changeName = 0; + + if (g_stLocale == "gb2312") + { + query = std::string("SELECT id, name, job, level, alignment, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player") + + GetTablePostfix() + " WHERE account_id=?"; + } + else + { + query = std::string("SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player") + + GetTablePostfix() + " WHERE account_id=?"; + } + + if (!PreparePlayerStmt(stmt, query)) + return false; + + if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)) + return false; + + if (!stmt.BindResult(MYSQL_TYPE_LONG, &playerId) + || !stmt.BindResult(MYSQL_TYPE_STRING, name, sizeof(name)) + || !stmt.BindResult(MYSQL_TYPE_LONG, &job) + || !stmt.BindResult(MYSQL_TYPE_LONG, &level) + || !stmt.BindResult(MYSQL_TYPE_LONG, &progressValue) + || !stmt.BindResult(MYSQL_TYPE_LONG, &st) + || !stmt.BindResult(MYSQL_TYPE_LONG, &ht) + || !stmt.BindResult(MYSQL_TYPE_LONG, &dx) + || !stmt.BindResult(MYSQL_TYPE_LONG, &iq) + || !stmt.BindResult(MYSQL_TYPE_LONG, &mainPart) + || !stmt.BindResult(MYSQL_TYPE_LONG, &hairPart) + || !stmt.BindResult(MYSQL_TYPE_LONG, &x) + || !stmt.BindResult(MYSQL_TYPE_LONG, &y) + || !stmt.BindResult(MYSQL_TYPE_LONG, &skillGroup) + || !stmt.BindResult(MYSQL_TYPE_LONG, &changeName)) + { + return false; + } + + if (!stmt.Execute()) + return false; + + while (stmt.Fetch()) + { + size_t nameLen = stmt.GetResultLength(1); + if (nameLen >= sizeof(name)) + nameLen = sizeof(name) - 1; + name[nameLen] = '\0'; + + ApplyPlayerSummary(accountTable, playerId, name, job, level, progressValue, st, ht, dx, iq, mainPart, hairPart, x, y, skillGroup, changeName); + } + + return true; +} + +bool CountPlayerNames(const char* playerName, uint32_t playerId, uint32_t* count) +{ + CStmt stmt; + std::string query; + + if (g_stLocale == "sjis") + query = std::string("SELECT COUNT(*) FROM player") + GetTablePostfix() + " WHERE name=? collate sjis_japanese_ci AND id <> ?"; + else + query = std::string("SELECT COUNT(*) FROM player") + GetTablePostfix() + " WHERE name=? AND id <> ?"; + + if (!PreparePlayerStmt(stmt, query)) + return false; + + if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast(playerName), CHARACTER_NAME_MAX_LEN + 1) + || !stmt.BindParam(MYSQL_TYPE_LONG, &playerId) + || !stmt.BindResult(MYSQL_TYPE_LONG, count)) + { + return false; + } + + return stmt.Execute() && stmt.Fetch(); +} + +bool UpdatePlayerName(uint32_t playerId, const char* playerName) +{ + CStmt stmt; + const std::string query = std::string("UPDATE player") + GetTablePostfix() + " SET name=?, change_name=0 WHERE id=?"; + + if (!PreparePlayerStmt(stmt, query)) + return false; + + if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast(playerName), CHARACTER_NAME_MAX_LEN + 1) + || !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)) + { + return false; + } + + return stmt.Execute(); +} +} + bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP) { char szLogin[LOGIN_MAX_LEN + 1]; @@ -110,23 +340,51 @@ void CClientManager::QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketG return; } - TAccountTable * pkTab = new TAccountTable; - memset(pkTab, 0, sizeof(TAccountTable)); + TAccountTable accountTable = {}; + accountTable.id = r.id; + trim_and_lower(r.login, accountTable.login, sizeof(accountTable.login)); + strlcpy(accountTable.passwd, r.passwd, sizeof(accountTable.passwd)); + strlcpy(accountTable.social_id, r.social_id, sizeof(accountTable.social_id)); + strlcpy(accountTable.status, "OK", sizeof(accountTable.status)); - pkTab->id = r.id; - trim_and_lower(r.login, pkTab->login, sizeof(pkTab->login)); - strlcpy(pkTab->passwd, r.passwd, sizeof(pkTab->passwd)); - strlcpy(pkTab->social_id, r.social_id, sizeof(pkTab->social_id)); - strlcpy(pkTab->status, "OK", sizeof(pkTab->status)); + sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, p->szIP); - ClientHandleInfo * info = new ClientHandleInfo(dwHandle); - info->pAccountTable = pkTab; - strlcpy(info->ip, p->szIP, sizeof(info->ip)); + if (!LoadPlayerIndexTable(accountTable.id, &accountTable)) + { + sys_log(0, "LOGIN_BY_KEY missing player_index for account %u", accountTable.id); - sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, info->ip); - char szQuery[QUERY_MAX_LEN]; - snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id); - CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info); + CreatePlayerIndexRow(accountTable.id); + + if (!LoadPlayerIndexTable(accountTable.id, &accountTable)) + { + pkPeer->EncodeReturn(DG::LOGIN_NOT_EXIST, dwHandle); + return; + } + } + + if (!LoadAccountPlayerSummaries(accountTable.id, &accountTable)) + { + pkPeer->EncodeReturn(DG::LOGIN_NOT_EXIST, dwHandle); + return; + } + + if (!InsertLogonAccount(accountTable.login, pkPeer->GetHandle(), p->szIP)) + { + sys_log(0, "RESULT_LOGIN: already logon %s", accountTable.login); + + TPacketDGLoginAlready packet; + strlcpy(packet.szLogin, accountTable.login, sizeof(packet.szLogin)); + + pkPeer->EncodeHeader(DG::LOGIN_ALREADY, dwHandle, sizeof(TPacketDGLoginAlready)); + pkPeer->Encode(&packet, sizeof(packet)); + return; + } + + if (CLoginData* loginData = GetLoginDataByLogin(accountTable.login)) + memcpy(&loginData->GetAccountRef(), &accountTable, sizeof(TAccountTable)); + + pkPeer->EncodeHeader(DG::LOGIN_SUCCESS, dwHandle, sizeof(TAccountTable)); + pkPeer->Encode(&accountTable, sizeof(TAccountTable)); } void CClientManager::RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg) @@ -464,44 +722,25 @@ void CClientManager::QUERY_LOGOUT(CPeer * peer, DWORD dwHandle,const char * data void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p) { - char queryStr[QUERY_MAX_LEN]; + uint32_t duplicateCount = 0; - if (g_stLocale == "sjis") - snprintf(queryStr, sizeof(queryStr), - "SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci AND id <> %u", - GetTablePostfix(), p->name, p->pid); - else - snprintf(queryStr, sizeof(queryStr), - "SELECT COUNT(*) as count FROM player%s WHERE name='%s' AND id <> %u", GetTablePostfix(), p->name, p->pid); - - auto pMsg = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER); - - if (pMsg->Get()->uiNumRows) - { - if (!pMsg->Get()->pSQLResult) - { - peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0); - return; - } - - MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); - - if (*row[0] != '0') - { - peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0); - return; - } - } - else + if (!CountPlayerNames(p->name, p->pid, &duplicateCount)) { peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0); return; } - snprintf(queryStr, sizeof(queryStr), - "UPDATE player%s SET name='%s',change_name=0 WHERE id=%u", GetTablePostfix(), p->name, p->pid); + if (duplicateCount != 0) + { + peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0); + return; + } - auto pMsg0 = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER); + if (!UpdatePlayerName(p->pid, p->name)) + { + peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0); + return; + } TPacketDGChangeName pdg; peer->EncodeHeader(DG::CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName)); @@ -509,4 +748,3 @@ void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDCh strlcpy(pdg.name, p->name, sizeof(pdg.name)); peer->Encode(&pdg, sizeof(TPacketDGChangeName)); } - diff --git a/src/db/DBManager.cpp b/src/db/DBManager.cpp index f69eeed..35da369 100644 --- a/src/db/DBManager.cpp +++ b/src/db/DBManager.cpp @@ -128,6 +128,12 @@ std::unique_ptr CDBManager::DirectQuery(const char* c_pszQuery, int iSlo return msg; } +CAsyncSQL* CDBManager::GetDirectSQL(int iSlot) +{ + assert(iSlot < SQL_MAX_NUM); + return m_directSQL[iSlot].get(); +} + extern CPacketInfo g_query_info; extern int g_query_count[2]; @@ -182,4 +188,3 @@ void CDBManager::QueryLocaleSet() m_asyncSQL[n]->QueryLocaleSet(); } } - diff --git a/src/db/DBManager.h b/src/db/DBManager.h index 649ae0c..ea8aba2 100644 --- a/src/db/DBManager.h +++ b/src/db/DBManager.h @@ -43,6 +43,7 @@ class CDBManager : public singleton void ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * pvData, int iSlot = SQL_PLAYER); void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER); std::unique_ptr DirectQuery(const char* c_pszQuery, int iSlot = SQL_PLAYER); + CAsyncSQL* GetDirectSQL(int iSlot = SQL_PLAYER); SQLMsg * PopResult(); SQLMsg * PopResult(eSQL_SLOT slot );