Files
m2dev-server-src/src/db/ClientManagerPlayer.cpp
2026-04-13 23:42:49 +02:00

1425 lines
44 KiB
C++

#include "stdafx.h"
#include "ClientManager.h"
#include "Main.h"
#include "QID.h"
#include "ItemAwardManager.h"
#include "HB.h"
#include "Cache.h"
#include "libsql/Statement.h"
extern bool g_bHotBackup;
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());
}
bool LoadHighscoreValue(const char* board, uint32_t playerId, int& value, bool& found)
{
CStmt stmt;
const std::string query =
"SELECT value FROM highscore" + std::string(GetTablePostfix()) + " WHERE board=? AND pid=?";
found = false;
value = 0;
if (!PreparePlayerStmt(stmt, query))
return false;
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(board))
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|| !stmt.BindResult(MYSQL_TYPE_LONG, &value))
{
return false;
}
if (!stmt.Execute())
return false;
if (stmt.iRows == 0)
return true;
if (!stmt.Fetch())
return false;
found = true;
return true;
}
bool WriteHighscoreValue(const char* board, uint32_t playerId, int value, bool replaceExisting)
{
CStmt stmt;
const std::string query = std::string(replaceExisting ? "REPLACE INTO highscore" : "INSERT INTO highscore")
+ std::string(GetTablePostfix()) + " (board, pid, value) VALUES(?, ?, ?)";
if (!PreparePlayerStmt(stmt, query))
return false;
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(board))
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|| !stmt.BindParam(MYSQL_TYPE_LONG, &value))
{
return false;
}
return stmt.Execute();
}
}
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!! IMPORTANT !!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Check all SELECT syntax on item table before change this function!!!
//
bool CreateItemTableFromRes(MYSQL_RES * res, std::vector<TPlayerItem> * pVec, DWORD dwPID)
{
if (!res)
{
pVec->clear();
return true;
}
int rows;
if ((rows = mysql_num_rows(res)) <= 0) // 데이터 없음
{
pVec->clear();
return true;
}
pVec->resize(rows);
for (int i = 0; i < rows; ++i)
{
MYSQL_ROW row = mysql_fetch_row(res);
TPlayerItem & item = pVec->at(i);
int cur = 0;
// Check all SELECT syntax on item table before change this function!!!
// Check all SELECT syntax on item table before change this function!!!
// Check all SELECT syntax on item table before change this function!!!
str_to_number(item.id, row[cur++]);
str_to_number(item.window, row[cur++]);
str_to_number(item.pos, row[cur++]);
str_to_number(item.count, row[cur++]);
str_to_number(item.vnum, row[cur++]);
str_to_number(item.alSockets[0], row[cur++]);
str_to_number(item.alSockets[1], row[cur++]);
str_to_number(item.alSockets[2], row[cur++]);
for (int j = 0; j < ITEM_ATTRIBUTE_MAX_NUM; j++)
{
str_to_number(item.aAttr[j].bType, row[cur++]);
str_to_number(item.aAttr[j].sValue, row[cur++]);
}
item.owner = dwPID;
}
return true;
}
size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * pkTab)
{
size_t queryLen;
const std::string escapedIp = CDBManager::instance().EscapeStringCopy(pkTab->ip, strnlen(pkTab->ip, sizeof(pkTab->ip)));
const std::string escapedSkillLevel = CDBManager::instance().EscapeStringCopy(
reinterpret_cast<const char*>(pkTab->skills), sizeof(pkTab->skills));
const std::string escapedQuickslot = CDBManager::instance().EscapeStringCopy(
reinterpret_cast<const char*>(pkTab->quickslot), sizeof(pkTab->quickslot));
queryLen = snprintf(pszQuery, querySize,
"UPDATE player%s SET "
"job = %d, "
"voice = %d, "
"dir = %d, "
"x = %d, "
"y = %d, "
"z = %d, "
"map_index = %d, "
"exit_x = %ld, "
"exit_y = %ld, "
"exit_map_index = %ld, "
"hp = %d, "
"mp = %d, "
"stamina = %d, "
"random_hp = %d, "
"random_sp = %d, "
"playtime = %d, "
"level = %d, "
"level_step = %d, "
"st = %d, "
"ht = %d, "
"dx = %d, "
"iq = %d, "
"gold = %d, "
"exp = %u, "
"stat_point = %d, "
"skill_point = %d, "
"sub_skill_point = %d, "
"stat_reset_count = %d, "
"ip = '%s', "
"part_main = %d, "
"part_hair = %d, "
"last_play = NOW(), "
"skill_group = %d, "
"alignment = %ld, "
"horse_level = %d, "
"horse_riding = %d, "
"horse_hp = %d, "
"horse_hp_droptime = %u, "
"horse_stamina = %d, "
"horse_skill_point = %d, "
,
GetTablePostfix(),
pkTab->job,
pkTab->voice,
pkTab->dir,
pkTab->x,
pkTab->y,
pkTab->z,
pkTab->lMapIndex,
static_cast<long>(pkTab->lExitX),
static_cast<long>(pkTab->lExitY),
static_cast<long>(pkTab->lExitMapIndex),
pkTab->hp,
pkTab->sp,
pkTab->stamina,
pkTab->sRandomHP,
pkTab->sRandomSP,
pkTab->playtime,
pkTab->level,
pkTab->level_step,
pkTab->st,
pkTab->ht,
pkTab->dx,
pkTab->iq,
pkTab->gold,
pkTab->exp,
pkTab->stat_point,
pkTab->skill_point,
pkTab->sub_skill_point,
pkTab->stat_reset_count,
escapedIp.c_str(),
pkTab->parts[PART_MAIN],
pkTab->parts[PART_HAIR],
pkTab->skill_group,
static_cast<long>(pkTab->lAlignment),
pkTab->horse.bLevel,
pkTab->horse.bRiding,
pkTab->horse.sHealth,
pkTab->horse.dwHorseHealthDropTime,
pkTab->horse.sStamina,
pkTab->horse_skill_point);
queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, "skill_level = '%s', ", escapedSkillLevel.c_str());
queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, "quickslot = '%s' ", escapedQuickslot.c_str());
queryLen += snprintf(pszQuery + queryLen, querySize - queryLen, " WHERE id=%d", pkTab->id);
return queryLen;
}
CPlayerTableCache * CClientManager::GetPlayerCache(DWORD id)
{
TPlayerTableCacheMap::iterator it = m_map_playerCache.find(id);
if (it == m_map_playerCache.end())
return NULL;
TPlayerTable* pTable = it->second->Get(false);
pTable->logoff_interval = GetCurrentTime() - it->second->GetLastUpdateTime();
return it->second;
}
void CClientManager::PutPlayerCache(TPlayerTable * pNew)
{
CPlayerTableCache * c;
c = GetPlayerCache(pNew->id);
if (!c)
{
c = new CPlayerTableCache;
m_map_playerCache.insert(TPlayerTableCacheMap::value_type(pNew->id, c));
}
if (g_bHotBackup)
PlayerHB::instance().Put(pNew->id);
c->Put(pNew);
}
/*
* PLAYER LOAD
*/
void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoadPacket * packet)
{
CPlayerTableCache * c;
TPlayerTable * pTab;
//
// 한 계정에 속한 모든 캐릭터들 캐쉬처리
//
CLoginData * pLoginData = GetLoginDataByAID(packet->account_id);
if (pLoginData)
{
for (int n = 0; n < PLAYER_PER_ACCOUNT; ++n)
if (pLoginData->GetAccountRef().players[n].dwID != 0)
DeleteLogoutPlayer(pLoginData->GetAccountRef().players[n].dwID);
}
//----------------------------------------------------------------
// 1. 유저정보가 DBCache 에 존재 : DBCache에서
// 2. 유저정보가 DBCache 에 없음 : DB에서
// ---------------------------------------------------------------
//----------------------------------
// 1. 유저정보가 DBCache 에 존재 : DBCache에서
//----------------------------------
if ((c = GetPlayerCache(packet->player_id)))
{
CLoginData * pkLD = GetLoginDataByAID(packet->account_id);
if (!pkLD || pkLD->IsPlay())
{
sys_log(0, "PLAYER_LOAD_ERROR: LoginData %p IsPlay %d", pkLD, pkLD ? pkLD->IsPlay() : 0);
peer->EncodeHeader(DG::PLAYER_LOAD_FAILED, dwHandle, 0);
return;
}
pTab = c->Get();
pkLD->SetPlay(true);
thecore_memcpy(pTab->aiPremiumTimes, pkLD->GetPremiumPtr(), sizeof(pTab->aiPremiumTimes));
peer->EncodeHeader(DG::PLAYER_LOAD_SUCCESS, dwHandle, sizeof(TPlayerTable));
peer->Encode(pTab, sizeof(TPlayerTable));
if (packet->player_id != pkLD->GetLastPlayerID())
{
TPacketNeedLoginLogInfo logInfo;
logInfo.dwPlayerID = packet->player_id;
pkLD->SetLastPlayerID( packet->player_id );
peer->EncodeHeader( DG::NEED_LOGIN_LOG, dwHandle, sizeof(TPacketNeedLoginLogInfo) );
peer->Encode( &logInfo, sizeof(TPacketNeedLoginLogInfo) );
}
char szQuery[1024] = { 0, };
TItemCacheSet * pSet = GetItemCacheSet(pTab->id);
sys_log(0, "[PLAYER_LOAD] ID %s pid %d gold %d ", pTab->name, pTab->id, pTab->gold);
//--------------------------------------------
// 아이템 & AFFECT & QUEST 로딩 :
//--------------------------------------------
// 1) 아이템이 DBCache 에 존재 : DBCache 에서 가져옴
// 2) 아이템이 DBCache 에 없음 : DB 에서 가져옴
/////////////////////////////////////////////
// 1) 아이템이 DBCache 에 존재 : DBCache 에서 가져옴
/////////////////////////////////////////////
if (pSet)
{
static std::vector<TPlayerItem> s_items;
s_items.resize(pSet->size());
DWORD dwCount = 0;
TItemCacheSet::iterator it = pSet->begin();
while (it != pSet->end())
{
CItemCache * c = *it++;
TPlayerItem * p = c->Get();
if (p->vnum) // vnum이 없으면 삭제된 아이템이다.
thecore_memcpy(&s_items[dwCount++], p, sizeof(TPlayerItem));
}
if (g_test_server)
sys_log(0, "ITEM_CACHE: HIT! %s count: %u", pTab->name, dwCount);
peer->EncodeHeader(DG::ITEM_LOAD, dwHandle, sizeof(DWORD) + sizeof(TPlayerItem) * dwCount);
peer->EncodeDWORD(dwCount);
if (dwCount)
peer->Encode(&s_items[0], sizeof(TPlayerItem) * dwCount);
// Quest
snprintf(szQuery, sizeof(szQuery),
"SELECT dwPID,szName,szState,lValue FROM quest%s WHERE dwPID=%d AND lValue<>0",
GetTablePostfix(), pTab->id);
CDBManager::instance().ReturnQuery(szQuery, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle, 0, packet->account_id));
// Affect
snprintf(szQuery, sizeof(szQuery),
"SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d",
GetTablePostfix(), pTab->id);
CDBManager::instance().ReturnQuery(szQuery, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id));
}
/////////////////////////////////////////////
// 2) 아이템이 DBCache 에 없음 : DB 에서 가져옴
/////////////////////////////////////////////
else
{
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 < %d or window = %d)",
GetTablePostfix(), pTab->id, SAFEBOX, DRAGON_SOUL_INVENTORY);
CDBManager::instance().ReturnQuery(szQuery,
QID_ITEM,
peer->GetHandle(),
new ClientHandleInfo(dwHandle, pTab->id));
snprintf(szQuery, sizeof(szQuery),
"SELECT dwPID, szName, szState, lValue FROM quest%s WHERE dwPID=%d",
GetTablePostfix(), pTab->id);
CDBManager::instance().ReturnQuery(szQuery,
QID_QUEST,
peer->GetHandle(),
new ClientHandleInfo(dwHandle, pTab->id));
snprintf(szQuery, sizeof(szQuery),
"SELECT dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost FROM affect%s WHERE dwPID=%d",
GetTablePostfix(), pTab->id);
CDBManager::instance().ReturnQuery(szQuery,
QID_AFFECT,
peer->GetHandle(),
new ClientHandleInfo(dwHandle, pTab->id));
}
//ljw
//return;
}
//----------------------------------
// 2. 유저정보가 DBCache 에 없음 : DB에서
//----------------------------------
else
{
sys_log(0, "[PLAYER_LOAD] Load from PlayerDB pid[%d]", packet->player_id);
char queryStr[QUERY_MAX_LEN];
//--------------------------------------------------------------
// 캐릭터 정보 얻어오기 : 무조건 DB에서
//--------------------------------------------------------------
snprintf(queryStr, sizeof(queryStr),
"SELECT "
"id,name,job,voice,dir,x,y,z,map_index,exit_x,exit_y,exit_map_index,hp,mp,stamina,random_hp,random_sp,playtime,"
"gold,level,level_step,st,ht,dx,iq,exp,"
"stat_point,skill_point,sub_skill_point,stat_reset_count,part_base,part_hair,"
"skill_level,quickslot,skill_group,alignment,mobile,horse_level,horse_riding,horse_hp,horse_hp_droptime,horse_stamina,"
"UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_play),horse_skill_point FROM player%s WHERE id=%d",
GetTablePostfix(), packet->player_id);
ClientHandleInfo * pkInfo = new ClientHandleInfo(dwHandle, packet->player_id);
pkInfo->account_id = packet->account_id;
CDBManager::instance().ReturnQuery(queryStr, QID_PLAYER, peer->GetHandle(), pkInfo);
//--------------------------------------------------------------
// 아이템 가져오기
//--------------------------------------------------------------
snprintf(queryStr, sizeof(queryStr),
"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 < %d or window = %d)",
GetTablePostfix(), packet->player_id, SAFEBOX, DRAGON_SOUL_INVENTORY);
CDBManager::instance().ReturnQuery(queryStr, QID_ITEM, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id));
//--------------------------------------------------------------
// QUEST 가져오기
//--------------------------------------------------------------
snprintf(queryStr, sizeof(queryStr),
"SELECT dwPID,szName,szState,lValue FROM quest%s WHERE dwPID=%d",
GetTablePostfix(), packet->player_id);
CDBManager::instance().ReturnQuery(queryStr, QID_QUEST, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id,packet->account_id));
//독일 선물 기능에서 item_award테이블에서 login 정보를 얻기위해 account id도 넘겨준다
//--------------------------------------------------------------
// AFFECT 가져오기
//--------------------------------------------------------------
snprintf(queryStr, sizeof(queryStr),
"SELECT dwPID,bType,bApplyOn,lApplyValue,dwFlag,lDuration,lSPCost FROM affect%s WHERE dwPID=%d",
GetTablePostfix(), packet->player_id);
CDBManager::instance().ReturnQuery(queryStr, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id));
}
}
void CClientManager::ItemAward(CPeer * peer,char* login)
{
char login_t[LOGIN_MAX_LEN + 1] = "";
strlcpy(login_t,login,LOGIN_MAX_LEN + 1);
std::set<TItemAward *> * pSet = ItemAwardManager::instance().GetByLogin(login_t);
if(pSet == NULL)
return;
__typeof(pSet->begin()) it = pSet->begin(); //taken_time이 NULL인것들 읽어옴
while(it != pSet->end() )
{
TItemAward * pItemAward = *(it++);
char* whyStr = pItemAward->szWhy; //why 콜룸 읽기
char cmdStr[100] = ""; //why콜룸에서 읽은 값을 임시 문자열에 복사해둠
strcpy(cmdStr, whyStr); //명령어 얻는 과정에서 토큰쓰면 원본도 토큰화 되기 때문
char command[20] = "";
strcpy(command,GetCommand(cmdStr)); // command 얻기
if (!(strcmp(command,"GIFT"))) // command 가 GIFT이면
{
TPacketItemAwardInfromer giftData;
strcpy(giftData.login, pItemAward->szLogin); //로그인 아이디 복사
strcpy(giftData.command, command); //명령어 복사
giftData.vnum = pItemAward->dwVnum; //아이템 vnum도 복사
ForwardPacket(DG::ITEMAWARD_INFORMER, &giftData, sizeof(TPacketItemAwardInfromer));
}
}
}
char* CClientManager::GetCommand(char* str)
{
static char command[20] = "";
char* tok;
if (str[0] == '[')
{
tok = strtok(str, "]");
strlcpy(command, &tok[1], sizeof(command));
}
else
{
command[0] = '\0';
}
return command;
}
bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab)
{
if (mysql_num_rows(res) == 0) // 데이터 없음
return false;
memset(pkTab, 0, sizeof(TPlayerTable));
MYSQL_ROW row = mysql_fetch_row(res);
int col = 0;
// "id,name,job,voice,dir,x,y,z,map_index,exit_x,exit_y,exit_map_index,hp,mp,stamina,random_hp,random_sp,playtime,"
// "gold,level,level_step,st,ht,dx,iq,exp,"
// "stat_point,skill_point,sub_skill_point,stat_reset_count,part_base,part_hair,"
// "skill_level,quickslot,skill_group,alignment,mobile,horse_level,horse_riding,horse_hp,horse_stamina FROM player%s WHERE id=%d",
str_to_number(pkTab->id, row[col++]);
strlcpy(pkTab->name, row[col++], sizeof(pkTab->name));
str_to_number(pkTab->job, row[col++]);
str_to_number(pkTab->voice, row[col++]);
str_to_number(pkTab->dir, row[col++]);
str_to_number(pkTab->x, row[col++]);
str_to_number(pkTab->y, row[col++]);
str_to_number(pkTab->z, row[col++]);
str_to_number(pkTab->lMapIndex, row[col++]);
str_to_number(pkTab->lExitX, row[col++]);
str_to_number(pkTab->lExitY, row[col++]);
str_to_number(pkTab->lExitMapIndex, row[col++]);
str_to_number(pkTab->hp, row[col++]);
str_to_number(pkTab->sp, row[col++]);
str_to_number(pkTab->stamina, row[col++]);
str_to_number(pkTab->sRandomHP, row[col++]);
str_to_number(pkTab->sRandomSP, row[col++]);
str_to_number(pkTab->playtime, row[col++]);
str_to_number(pkTab->gold, row[col++]);
str_to_number(pkTab->level, row[col++]);
str_to_number(pkTab->level_step, row[col++]);
str_to_number(pkTab->st, row[col++]);
str_to_number(pkTab->ht, row[col++]);
str_to_number(pkTab->dx, row[col++]);
str_to_number(pkTab->iq, row[col++]);
str_to_number(pkTab->exp, row[col++]);
str_to_number(pkTab->stat_point, row[col++]);
str_to_number(pkTab->skill_point, row[col++]);
str_to_number(pkTab->sub_skill_point, row[col++]);
str_to_number(pkTab->stat_reset_count, row[col++]);
str_to_number(pkTab->part_base, row[col++]);
str_to_number(pkTab->parts[PART_HAIR], row[col++]);
if (row[col])
thecore_memcpy(pkTab->skills, row[col], sizeof(pkTab->skills));
else
memset(&pkTab->skills, 0, sizeof(pkTab->skills));
col++;
if (row[col])
thecore_memcpy(pkTab->quickslot, row[col], sizeof(pkTab->quickslot));
else
memset(pkTab->quickslot, 0, sizeof(pkTab->quickslot));
col++;
str_to_number(pkTab->skill_group, row[col++]);
str_to_number(pkTab->lAlignment, row[col++]);
col++; // skip mobile (removed feature)
str_to_number(pkTab->horse.bLevel, row[col++]);
str_to_number(pkTab->horse.bRiding, row[col++]);
str_to_number(pkTab->horse.sHealth, row[col++]);
str_to_number(pkTab->horse.dwHorseHealthDropTime, row[col++]);
str_to_number(pkTab->horse.sStamina, row[col++]);
str_to_number(pkTab->logoff_interval, row[col++]);
str_to_number(pkTab->horse_skill_point, row[col++]);
// reset sub_skill_point
{
pkTab->skills[123].bLevel = 0; // SKILL_CREATE
if (pkTab->level > 9)
{
int max_point = pkTab->level - 9;
int skill_point =
MIN(20, pkTab->skills[121].bLevel) + // SKILL_LEADERSHIP 통솔력
MIN(20, pkTab->skills[124].bLevel) + // SKILL_MINING 채광
MIN(10, pkTab->skills[131].bLevel) + // SKILL_HORSE_SUMMON 말소환
MIN(20, pkTab->skills[141].bLevel) + // SKILL_ADD_HP HP보강
MIN(20, pkTab->skills[142].bLevel); // SKILL_RESIST_PENETRATE 관통저항
pkTab->sub_skill_point = max_point - skill_point;
}
else
pkTab->sub_skill_point = 0;
}
return true;
}
void CClientManager::RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD dwQID)
{
CQueryInfo * qi = (CQueryInfo *) pMsg->pvUserData;
std::unique_ptr<ClientHandleInfo> info((ClientHandleInfo *) qi->pvData);
MYSQL_RES * pSQLResult = pMsg->Get()->pSQLResult;
if (!pSQLResult)
{
sys_err("null MYSQL_RES QID %u", dwQID);
return;
}
switch (dwQID)
{
case QID_PLAYER:
sys_log(0, "QID_PLAYER %u %u", info->dwHandle, info->player_id);
RESULT_PLAYER_LOAD(peer, pSQLResult, info.get());
break;
case QID_ITEM:
sys_log(0, "QID_ITEM %u", info->dwHandle);
RESULT_ITEM_LOAD(peer, pSQLResult, info->dwHandle, info->player_id);
break;
case QID_QUEST:
{
sys_log(0, "QID_QUEST %u", info->dwHandle);
RESULT_QUEST_LOAD(peer, pSQLResult, info->dwHandle, info->player_id);
//aid얻기
ClientHandleInfo* temp1 = info.get();
if (temp1 == NULL)
break;
CLoginData* pLoginData1 = GetLoginDataByAID(temp1->account_id);
if (pLoginData1 == NULL)
break;
//독일 선물 기능
if (pLoginData1->GetAccountRef().login[0] == '\0')
break;
sys_log(0,"info of pLoginData1 before call ItemAwardfunction %d",pLoginData1);
ItemAward(peer,pLoginData1->GetAccountRef().login);
}
break;
case QID_AFFECT:
sys_log(0, "QID_AFFECT %u", info->dwHandle);
// MR-8: Fix "when_login" being loaded before character affects
if (!mysql_num_rows(pSQLResult))
{
TPacketAffectElement pAffElem{};
DWORD dwCount = 0;
peer->EncodeHeader(DG::AFFECT_LOAD, info->dwHandle, sizeof(DWORD) + sizeof(DWORD) + sizeof(TPacketAffectElement) * dwCount);
peer->Encode(&info->player_id, sizeof(DWORD));
peer->Encode(&dwCount, sizeof(DWORD));
peer->Encode(&pAffElem, sizeof(TPacketAffectElement) * dwCount);
break;
}
// MR-8: -- END OF -- Fix "when_login" being loaded before character affects
RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle);
break;
/*
case QID_PLAYER_ITEM_QUEST_AFFECT:
sys_log(0, "QID_PLAYER_ITEM_QUEST_AFFECT %u", info->dwHandle);
RESULT_PLAYER_LOAD(peer, pSQLResult, info->dwHandle);
if (!pMsg->Next())
{
sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: ITEM FAILED");
return;
}
case QID_ITEM_QUEST_AFFECT:
sys_log(0, "QID_ITEM_QUEST_AFFECT %u", info->dwHandle);
RESULT_ITEM_LOAD(peer, pSQLResult, info->dwHandle, info->player_id);
if (!pMsg->Next())
{
sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: QUEST FAILED");
return;
}
case QID_QUEST_AFFECT:
sys_log(0, "QID_QUEST_AFFECT %u", info->dwHandle);
RESULT_QUEST_LOAD(peer, pSQLResult, info->dwHandle);
if (!pMsg->Next())
sys_err("RESULT_COMPOSITE_PLAYER: QID_PLAYER_ITEM_QUEST_AFFECT: AFFECT FAILED");
else
RESULT_AFFECT_LOAD(peer, pSQLResult, info->dwHandle);
break;
*/
}
}
void CClientManager::RESULT_PLAYER_LOAD(CPeer * peer, MYSQL_RES * pRes, ClientHandleInfo * pkInfo)
{
TPlayerTable tab;
if (!CreatePlayerTableFromRes(pRes, &tab))
{
peer->EncodeHeader(DG::PLAYER_LOAD_FAILED, pkInfo->dwHandle, 0);
return;
}
CLoginData * pkLD = GetLoginDataByAID(pkInfo->account_id);
if (!pkLD || pkLD->IsPlay())
{
sys_log(0, "PLAYER_LOAD_ERROR: LoginData %p IsPlay %d", pkLD, pkLD ? pkLD->IsPlay() : 0);
peer->EncodeHeader(DG::PLAYER_LOAD_FAILED, pkInfo->dwHandle, 0);
return;
}
pkLD->SetPlay(true);
thecore_memcpy(tab.aiPremiumTimes, pkLD->GetPremiumPtr(), sizeof(tab.aiPremiumTimes));
peer->EncodeHeader(DG::PLAYER_LOAD_SUCCESS, pkInfo->dwHandle, sizeof(TPlayerTable));
peer->Encode(&tab, sizeof(TPlayerTable));
if (tab.id != pkLD->GetLastPlayerID())
{
TPacketNeedLoginLogInfo logInfo;
logInfo.dwPlayerID = tab.id;
pkLD->SetLastPlayerID( tab.id );
peer->EncodeHeader( DG::NEED_LOGIN_LOG, pkInfo->dwHandle, sizeof(TPacketNeedLoginLogInfo) );
peer->Encode( &logInfo, sizeof(TPacketNeedLoginLogInfo) );
}
}
void CClientManager::RESULT_ITEM_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD dwPID)
{
static std::vector<TPlayerItem> s_items;
//DB에서 아이템 정보를 읽어온다.
CreateItemTableFromRes(pRes, &s_items, dwPID);
DWORD dwCount = s_items.size();
peer->EncodeHeader(DG::ITEM_LOAD, dwHandle, sizeof(DWORD) + sizeof(TPlayerItem) * dwCount);
peer->EncodeDWORD(dwCount);
//CacheSet을 만든다
CreateItemCacheSet(dwPID);
// ITEM_LOAD_LOG_ATTACH_PID
sys_log(0, "ITEM_LOAD: count %u pid %u", dwCount, dwPID);
// END_OF_ITEM_LOAD_LOG_ATTACH_PID
if (dwCount)
{
peer->Encode(&s_items[0], sizeof(TPlayerItem) * dwCount);
for (DWORD i = 0; i < dwCount; ++i)
PutItemCache(&s_items[i], true); // 로드한 것은 따로 저장할 필요 없으므로, 인자 bSkipQuery에 true를 넣는다.
}
}
void CClientManager::RESULT_AFFECT_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle)
{
int iNumRows;
if ((iNumRows = mysql_num_rows(pRes)) == 0) // 데이터 없음
return;
static std::vector<TPacketAffectElement> s_elements;
s_elements.resize(iNumRows);
DWORD dwPID = 0;
MYSQL_ROW row;
for (int i = 0; i < iNumRows; ++i)
{
TPacketAffectElement & r = s_elements[i];
row = mysql_fetch_row(pRes);
if (dwPID == 0)
str_to_number(dwPID, row[0]);
str_to_number(r.dwType, row[1]);
str_to_number(r.bApplyOn, row[2]);
str_to_number(r.lApplyValue, row[3]);
str_to_number(r.dwFlag, row[4]);
str_to_number(r.lDuration, row[5]);
str_to_number(r.lSPCost, row[6]);
}
sys_log(0, "AFFECT_LOAD: count %d PID %u", s_elements.size(), dwPID);
DWORD dwCount = s_elements.size();
peer->EncodeHeader(DG::AFFECT_LOAD, dwHandle, sizeof(DWORD) + sizeof(DWORD) + sizeof(TPacketAffectElement) * dwCount);
peer->Encode(&dwPID, sizeof(DWORD));
peer->Encode(&dwCount, sizeof(DWORD));
peer->Encode(&s_elements[0], sizeof(TPacketAffectElement) * dwCount);
}
void CClientManager::RESULT_QUEST_LOAD(CPeer * peer, MYSQL_RES * pRes, DWORD dwHandle, DWORD pid)
{
int iNumRows;
if ((iNumRows = mysql_num_rows(pRes)) == 0)
{
DWORD dwCount = 0;
peer->EncodeHeader(DG::QUEST_LOAD, dwHandle, sizeof(DWORD));
peer->Encode(&dwCount, sizeof(DWORD));
return;
}
static std::vector<TQuestTable> s_table;
s_table.resize(iNumRows);
MYSQL_ROW row;
for (int i = 0; i < iNumRows; ++i)
{
TQuestTable & r = s_table[i];
row = mysql_fetch_row(pRes);
str_to_number(r.dwPID, row[0]);
strlcpy(r.szName, row[1], sizeof(r.szName));
strlcpy(r.szState, row[2], sizeof(r.szState));
str_to_number(r.lValue, row[3]);
}
sys_log(0, "QUEST_LOAD: count %d PID %u", s_table.size(), pid);
DWORD dwCount = s_table.size();
peer->EncodeHeader(DG::QUEST_LOAD, dwHandle, sizeof(DWORD) + sizeof(TQuestTable) * dwCount);
peer->Encode(&dwCount, sizeof(DWORD));
peer->Encode(&s_table[0], sizeof(TQuestTable) * dwCount);
}
/*
* PLAYER SAVE
*/
void CClientManager::QUERY_PLAYER_SAVE(CPeer * peer, DWORD dwHandle, TPlayerTable * pkTab)
{
if (g_test_server)
sys_log(0, "PLAYER_SAVE: %s", pkTab->name);
PutPlayerCache(pkTab);
}
typedef std::map<DWORD, time_t> time_by_id_map_t;
static time_by_id_map_t s_createTimeByAccountID;
/*
* PLAYER CREATE
*/
void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerCreatePacket* packet)
{
uint32_t player_id = 0;
// 한 계정에 X초 내로 캐릭터 생성을 할 수 없다.
time_by_id_map_t::iterator it = s_createTimeByAccountID.find(packet->account_id);
if (it != s_createTimeByAccountID.end())
{
time_t curtime = time(0);
if (curtime - it->second < 30)
{
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
return;
}
}
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=?";
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")
playerNameQuery += " collate sjis_japanese_ci";
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;
}
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,
packet->player_table.name,
packet->player_table.level,
packet->player_table.gold,
packet->player_table.st,
packet->player_table.ht,
packet->player_table.job);
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, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
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_FAILED, dwHandle, 0);
return;
}
if (!createPlayerStmt.Execute() || createPlayerStmt.GetAffectedRows() <= 0)
{
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;
}
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;
}
TPacketDGCreateSuccess pack;
memset(&pack, 0, sizeof(pack));
pack.bAccountCharacterIndex = packet->account_index;
pack.player.dwID = player_id;
strlcpy(pack.player.szName, packet->player_table.name, sizeof(pack.player.szName));
pack.player.byJob = packet->player_table.job;
pack.player.byLevel = 1;
pack.player.dwPlayMinutes = 0;
pack.player.byST = packet->player_table.st;
pack.player.byHT = packet->player_table.ht;
pack.player.byDX = packet->player_table.dx;
pack.player.byIQ = packet->player_table.iq;
pack.player.wMainPart = packet->player_table.part_base;
pack.player.x = packet->player_table.x;
pack.player.y = packet->player_table.y;
peer->EncodeHeader(DG::PLAYER_CREATE_SUCCESS, dwHandle, sizeof(TPacketDGCreateSuccess));
peer->Encode(&pack, sizeof(TPacketDGCreateSuccess));
sys_log(0, "7 name %s job %d", pack.player.szName, pack.player.byJob);
s_createTimeByAccountID[packet->account_id] = time(0);
}
/*
* PLAYER DELETE
*/
void CClientManager::__QUERY_PLAYER_DELETE(CPeer* peer, DWORD dwHandle, TPlayerDeletePacket* packet)
{
if (!packet->login[0] || !packet->player_id || packet->account_index >= PLAYER_PER_ACCOUNT)
return;
CLoginData * ld = GetLoginDataByLogin(packet->login);
if (!ld)
{
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, dwHandle, 1);
peer->EncodeBYTE(packet->account_index);
return;
}
TAccountTable & r = ld->GetAccountRef();
// block for japan
if (g_stLocale != "sjis")
{
if (!IsChinaEventServer())
{
if (strlen(r.social_id) < 7 || strncmp(packet->private_code, r.social_id + strlen(r.social_id) - 7, 7))
{
sys_log(0, "PLAYER_DELETE FAILED len(%d)", strlen(r.social_id));
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, dwHandle, 1);
peer->EncodeBYTE(packet->account_index);
return;
}
CPlayerTableCache * pkPlayerCache = GetPlayerCache(packet->player_id);
if (pkPlayerCache)
{
TPlayerTable * pTab = pkPlayerCache->Get();
if (pTab->level >= m_iPlayerDeleteLevelLimit)
{
sys_log(0, "PLAYER_DELETE FAILED LEVEL %u >= DELETE LIMIT %d", pTab->level, m_iPlayerDeleteLevelLimit);
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, dwHandle, 1);
peer->EncodeBYTE(packet->account_index);
return;
}
if (pTab->level < m_iPlayerDeleteLevelLimitLower)
{
sys_log(0, "PLAYER_DELETE FAILED LEVEL %u < DELETE LIMIT %d", pTab->level, m_iPlayerDeleteLevelLimitLower);
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, dwHandle, 1);
peer->EncodeBYTE(packet->account_index);
return;
}
}
}
}
char szQuery[128];
snprintf(szQuery, sizeof(szQuery), "SELECT p.id, p.level, p.name FROM player_index%s AS i, player%s AS p WHERE pid%u=%u AND pid%u=p.id",
GetTablePostfix(), GetTablePostfix(), packet->account_index + 1, packet->player_id, packet->account_index + 1);
ClientHandleInfo * pi = new ClientHandleInfo(dwHandle, packet->player_id);
pi->account_index = packet->account_index;
sys_log(0, "PLAYER_DELETE TRY: %s %d pid%d", packet->login, packet->player_id, packet->account_index + 1);
CDBManager::instance().ReturnQuery(szQuery, QID_PLAYER_DELETE, peer->GetHandle(), pi);
}
//
// @version 05/06/10 Bang2ni - 플레이어 삭제시 가격정보 리스트 삭제 추가.
//
void CClientManager::__RESULT_PLAYER_DELETE(CPeer *peer, SQLMsg* msg)
{
CQueryInfo * qi = (CQueryInfo *) msg->pvUserData;
ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData;
if (msg->Get() && msg->Get()->uiNumRows)
{
MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult);
DWORD dwPID = 0;
str_to_number(dwPID, row[0]);
int deletedLevelLimit = 0;
str_to_number(deletedLevelLimit, row[1]);
char szName[64];
strlcpy(szName, row[2], sizeof(szName));
if (deletedLevelLimit >= m_iPlayerDeleteLevelLimit && !IsChinaEventServer())
{
sys_log(0, "PLAYER_DELETE FAILED LEVEL %u >= DELETE LIMIT %d", deletedLevelLimit, m_iPlayerDeleteLevelLimit);
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, pi->dwHandle, 1);
peer->EncodeBYTE(pi->account_index);
return;
}
if (deletedLevelLimit < m_iPlayerDeleteLevelLimitLower)
{
sys_log(0, "PLAYER_DELETE FAILED LEVEL %u < DELETE LIMIT %d", deletedLevelLimit, m_iPlayerDeleteLevelLimitLower);
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, pi->dwHandle, 1);
peer->EncodeBYTE(pi->account_index);
return;
}
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=?";
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());
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, pi->dwHandle, 1);
peer->EncodeBYTE(pi->account_index);
return;
}
// 삭제 성공
sys_log(0, "PLAYER_DELETE SUCCESS %u", dwPID);
// 플레이어 테이블을 캐쉬에서 삭제한다.
CPlayerTableCache * pkPlayerCache = GetPlayerCache(pi->player_id);
if (pkPlayerCache)
{
m_map_playerCache.erase(pi->player_id);
delete pkPlayerCache;
}
// 아이템들을 캐쉬에서 삭제한다.
TItemCacheSet * pSet = GetItemCacheSet(pi->player_id);
if (pSet)
{
TItemCacheSet::iterator it = pSet->begin();
while (it != pSet->end())
{
CItemCache * pkItemCache = *it++;
DeleteItemCache(pkItemCache->Get()->id);
}
pSet->clear();
delete pSet;
m_map_pkItemCacheSetPtr.erase(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) + "=?";
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);
peer->EncodeBYTE(pi->account_index);
return;
}
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();
}
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);
snprintf(queryStr, sizeof(queryStr), "DELETE FROM affect%s WHERE dwPID=%d", GetTablePostfix(), pi->player_id);
CDBManager::instance().AsyncQuery(queryStr);
snprintf(queryStr, sizeof(queryStr), "DELETE FROM guild_member%s WHERE pid=%d", GetTablePostfix(), pi->player_id);
CDBManager::instance().AsyncQuery(queryStr);
// MYSHOP_PRICE_LIST
snprintf(queryStr, sizeof(queryStr), "DELETE FROM myshop_pricelist%s WHERE owner_id=%d", GetTablePostfix(), pi->player_id);
CDBManager::instance().AsyncQuery(queryStr);
// END_OF_MYSHOP_PRICE_LIST
const std::string escapedPlayerName = CDBManager::instance().EscapeStringCopy(szName, strnlen(szName, sizeof(szName)));
snprintf(queryStr, sizeof(queryStr), "DELETE FROM messenger_list%s WHERE account='%s' OR companion='%s'",
GetTablePostfix(), escapedPlayerName.c_str(), escapedPlayerName.c_str());
CDBManager::instance().AsyncQuery(queryStr);
peer->EncodeHeader(DG::PLAYER_DELETE_SUCCESS, pi->dwHandle, 1);
peer->EncodeBYTE(pi->account_index);
}
else
{
// 삭제 실패
sys_log(0, "PLAYER_DELETE FAIL NO ROW");
peer->EncodeHeader(DG::PLAYER_DELETE_FAILED, pi->dwHandle, 1);
peer->EncodeBYTE(pi->account_index);
}
}
void CClientManager::QUERY_ADD_AFFECT(CPeer * peer, TPacketGDAddAffect * p)
{
char queryStr[QUERY_MAX_LEN];
/*
snprintf(queryStr, sizeof(queryStr),
"INSERT INTO affect%s (dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost) "
"VALUES(%u, %u, %u, %d, %u, %d, %d) "
"ON DUPLICATE KEY UPDATE lApplyValue=%d, dwFlag=%u, lDuration=%d, lSPCost=%d",
GetTablePostfix(),
p->dwPID,
p->elem.dwType,
p->elem.bApplyOn,
p->elem.lApplyValue,
p->elem.dwFlag,
p->elem.lDuration,
p->elem.lSPCost,
p->elem.lApplyValue,
p->elem.dwFlag,
p->elem.lDuration,
p->elem.lSPCost);
*/
snprintf(queryStr, sizeof(queryStr),
"REPLACE INTO affect%s (dwPID, bType, bApplyOn, lApplyValue, dwFlag, lDuration, lSPCost) "
"VALUES(%u, %u, %u, %ld, %u, %ld, %ld)",
GetTablePostfix(),
p->dwPID,
p->elem.dwType,
p->elem.bApplyOn,
static_cast<long>(p->elem.lApplyValue),
p->elem.dwFlag,
static_cast<long>(p->elem.lDuration),
static_cast<long>(p->elem.lSPCost));
CDBManager::instance().AsyncQuery(queryStr);
}
void CClientManager::QUERY_REMOVE_AFFECT(CPeer * peer, TPacketGDRemoveAffect * p)
{
char queryStr[QUERY_MAX_LEN];
snprintf(queryStr, sizeof(queryStr),
"DELETE FROM affect%s WHERE dwPID=%u AND bType=%u AND bApplyOn=%u",
GetTablePostfix(), p->dwPID, p->dwType, p->bApplyOn);
CDBManager::instance().AsyncQuery(queryStr);
}
void CClientManager::QUERY_HIGHSCORE_REGISTER(CPeer* peer, TPacketGDHighscore * data)
{
(void)peer;
sys_log(0, "GD::HIGHSCORE_REGISTER: PID %u", data->dwPID);
char board[sizeof(data->szBoard)];
int currentValue = 0;
bool found = false;
const bool higherIsBetter = data->cDir > 0;
const int value = static_cast<int>(data->lValue);
strlcpy(board, data->szBoard, sizeof(board));
if (!LoadHighscoreValue(board, data->dwPID, currentValue, found))
{
sys_err("failed to load highscore for board %s pid %u", board, data->dwPID);
return;
}
if (found)
{
if ((higherIsBetter && currentValue >= value) || (!higherIsBetter && currentValue <= value))
return;
}
if (!WriteHighscoreValue(board, data->dwPID, value, found))
sys_err("failed to write highscore for board %s pid %u", board, data->dwPID);
}
void CClientManager::InsertLogoutPlayer(DWORD pid)
{
TLogoutPlayerMap::iterator it = m_map_logout.find(pid);
// 존재하지 않을경우 추가
if (it != m_map_logout.end())
{
// 존재할경우 시간만 갱신
if (g_log)
sys_log(0, "LOGOUT: Update player time pid(%d)", pid);
it->second->time = time(0);
return;
}
TLogoutPlayer * pLogout = new TLogoutPlayer;
pLogout->pid = pid;
pLogout->time = time(0);
m_map_logout.insert(std::make_pair(pid, pLogout));
if (g_log)
sys_log(0, "LOGOUT: Insert player pid(%d)", pid);
}
void CClientManager::DeleteLogoutPlayer(DWORD pid)
{
TLogoutPlayerMap::iterator it = m_map_logout.find(pid);
if (it != m_map_logout.end())
{
delete it->second;
m_map_logout.erase(it);
}
}
extern int g_iLogoutSeconds;
void CClientManager::UpdateLogoutPlayer()
{
time_t now = time(0);
TLogoutPlayerMap::iterator it = m_map_logout.begin();
while (it != m_map_logout.end())
{
TLogoutPlayer* pLogout = it->second;
if (now - g_iLogoutSeconds > pLogout->time)
{
FlushItemCacheSet(pLogout->pid);
FlushPlayerCacheSet(pLogout->pid);
delete pLogout;
m_map_logout.erase(it++);
}
else
++it;
}
}
void CClientManager::FlushPlayerCacheSet(DWORD pid)
{
TPlayerTableCacheMap::iterator it = m_map_playerCache.find(pid);
if (it != m_map_playerCache.end())
{
CPlayerTableCache * c = it->second;
m_map_playerCache.erase(it);
c->Flush();
delete c;
}
}