fix: __INTELLISENSE__ version import (VSC).

change: Format all /db files, db/Main, db/DBManager, db/Monarch.
add: PgConnectionPool, PgAsyncQuery.
This commit is contained in:
WildEgo 2025-06-07 20:38:53 +01:00
parent 4bc4a79a4b
commit 93d8f2a0be
52 changed files with 4633 additions and 4324 deletions

72
.vscode/settings.json vendored
View File

@ -13,10 +13,78 @@
"xstring": "cpp", "xstring": "cpp",
"xtree": "cpp", "xtree": "cpp",
"xutility": "cpp", "xutility": "cpp",
"sstream": "cpp" "sstream": "cpp",
"fstream": "cpp",
"atomic": "cpp",
"hash_map": "cpp",
"bit": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"complex": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"exception": "cpp",
"expected": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"ratio": "cpp",
"source_location": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"format": "cpp",
"future": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"ranges": "cpp",
"semaphore": "cpp",
"shared_mutex": "cpp",
"span": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
}, },
"editor.formatOnSave": false, // "editor.formatOnSave": false,
"vcpkg.target.useManifest": false, "vcpkg.target.useManifest": false,
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"cmake.configureArgs": [ "cmake.configureArgs": [
"-DVCPKG_MANIFEST_MODE=OFF", "-DVCPKG_MANIFEST_MODE=OFF",
"-DVCPKG_APPLOCAL_DEPS=ON", "-DVCPKG_APPLOCAL_DEPS=ON",

View File

@ -1,5 +1,7 @@
# The list # The list
- [ ] Database initialization - [ ] Database initialization
- [x] Main (Most important duh)
- [ ] ClientManager - [ ] ClientManager
- [ ] ClientManagerBoot - [ ] ClientManagerBoot
- [ ] ClientManagerEventFlag - [ ] ClientManagerEventFlag
@ -7,9 +9,9 @@
- [ ] ClientManagerHorseName - [ ] ClientManagerHorseName
- [ ] ClientManagerLogin - [ ] ClientManagerLogin
- [ ] ClientManagerPlayer - [ ] ClientManagerPlayer
- [ ] DBManager - [x] DBManager
- [ ] GuildManager - [ ] GuildManager
- [ ] ItemAwardManager - [ ] ItemAwardManager
- [ ] ItemIDRangeManager - [ ] ItemIDRangeManager
- [ ] Marriage - [ ] Marriage
- [ ] Monarch - [x] Monarch

View File

@ -34,7 +34,6 @@ class CPlayerTableCache : public cache<TPlayerTable>
class CItemPriceListTableCache : public cache<TItemPriceListTable> class CItemPriceListTableCache : public cache<TItemPriceListTable>
{ {
public: public:
/// Constructor /// Constructor
/** /**
* . * .
@ -54,7 +53,6 @@ class CItemPriceListTableCache : public cache< TItemPriceListTable >
virtual void OnFlush(void); virtual void OnFlush(void);
private: private:
static const int s_nMinFlushSec; ///< Minimum cache expire time static const int s_nMinFlushSec; ///< Minimum cache expire time
}; };
// END_OF_MYSHOP_PRICE_LIST // END_OF_MYSHOP_PRICE_LIST

View File

@ -58,7 +58,8 @@ static void AcceptConnection(
bufferevent_enable(bev, EV_READ | EV_WRITE); bufferevent_enable(bev, EV_READ | EV_WRITE);
} }
static void AcceptError(evconnlistener *listener, void *ctx) { static void AcceptError(evconnlistener *listener, void *ctx)
{
struct event_base *base = evconnlistener_get_base(listener); struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR(); int err = EVUTIL_SOCKET_ERROR();
SPDLOG_CRITICAL("Got an error {} ({}) on the listener. Shutting down.", err, evutil_socket_error_to_string(err)); SPDLOG_CRITICAL("Got an error {} ({}) on the listener. Shutting down.", err, evutil_socket_error_to_string(err));
@ -66,7 +67,8 @@ static void AcceptError(evconnlistener *listener, void *ctx) {
event_base_loopexit(base, nullptr); event_base_loopexit(base, nullptr);
} }
static void DescReadHandler(bufferevent *bev, void *ctx) { static void DescReadHandler(bufferevent *bev, void *ctx)
{
auto *peer = (CPeer *)ctx; auto *peer = (CPeer *)ctx;
if (peer == CClientManager::Instance().GetAuthPeer()) if (peer == CClientManager::Instance().GetAuthPeer())
@ -75,14 +77,16 @@ static void DescReadHandler(bufferevent *bev, void *ctx) {
CClientManager::Instance().ProcessPackets(peer); CClientManager::Instance().ProcessPackets(peer);
} }
static void DescWriteHandler(bufferevent *bev, void *ctx) { static void DescWriteHandler(bufferevent *bev, void *ctx)
{
auto *peer = (CPeer *)ctx; auto *peer = (CPeer *)ctx;
if (peer == CClientManager::Instance().GetAuthPeer()) if (peer == CClientManager::Instance().GetAuthPeer())
SPDLOG_TRACE("AUTH_PEER_WRITE: size {}", peer->GetSendLength()); SPDLOG_TRACE("AUTH_PEER_WRITE: size {}", peer->GetSendLength());
} }
static void DescEventHandler(bufferevent *bev, short events, void *ctx) { static void DescEventHandler(bufferevent *bev, short events, void *ctx)
{
auto *peer = (CPeer *)ctx; auto *peer = (CPeer *)ctx;
if (events & BEV_EVENT_ERROR) if (events & BEV_EVENT_ERROR)
@ -96,8 +100,7 @@ static void DescEventHandler(bufferevent *bev, short events, void *ctx) {
CClientManager::Instance().RemovePeer(peer); CClientManager::Instance().RemovePeer(peer);
} }
CClientManager::CClientManager() : CClientManager::CClientManager() : m_pkAuthPeer(NULL),
m_pkAuthPeer(NULL),
m_iPlayerIDStart(0), m_iPlayerIDStart(0),
m_iPlayerDeleteLevelLimit(0), m_iPlayerDeleteLevelLimit(0),
m_iPlayerDeleteLevelLimitLower(0), m_iPlayerDeleteLevelLimitLower(0),
@ -137,12 +140,14 @@ void CClientManager::Destroy()
m_peerList.clear(); m_peerList.clear();
// Free the libevent resources // Free the libevent resources
if (m_listener) { if (m_listener)
{
evconnlistener_free(m_listener); evconnlistener_free(m_listener);
m_listener = nullptr; m_listener = nullptr;
} }
if (m_base) { if (m_base)
{
event_base_free(m_base); event_base_free(m_base);
m_base = nullptr; m_base = nullptr;
} }
@ -187,7 +192,8 @@ bool CClientManager::Initialize()
// Create a new libevent base and listen for new connections // Create a new libevent base and listen for new connections
m_base = event_base_new(); m_base = event_base_new();
if (!m_base) { if (!m_base)
{
SPDLOG_ERROR("Libevent base initialization FAILED!"); SPDLOG_ERROR("Libevent base initialization FAILED!");
return false; return false;
} }
@ -204,9 +210,9 @@ bool CClientManager::Initialize()
m_base, m_base,
AcceptConnection, this, AcceptConnection, this,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
(const sockaddr*)&sin, sizeof(sin) (const sockaddr *)&sin, sizeof(sin));
); if (!m_listener)
if (!m_listener) { {
SPDLOG_ERROR("Libevent listener initialization FAILED!"); SPDLOG_ERROR("Libevent listener initialization FAILED!");
return false; return false;
} }
@ -240,7 +246,6 @@ bool CClientManager::Initialize()
SPDLOG_DEBUG("CHINA_EVENT_SERVER {}", CClientManager::instance().IsChinaEventServer() ? "true" : "false"); SPDLOG_DEBUG("CHINA_EVENT_SERVER {}", CClientManager::instance().IsChinaEventServer() ? "true" : "false");
LoadEventFlag(); LoadEventFlag();
// database character-set을 강제로 맞춤 // database character-set을 강제로 맞춤
@ -288,7 +293,6 @@ void CClientManager::MainLoop()
} }
m_map_playerCache.clear(); m_map_playerCache.clear();
itertype(m_map_itemCache) it2 = m_map_itemCache.begin(); itertype(m_map_itemCache) it2 = m_map_itemCache.begin();
// 아이템 플러쉬 // 아이템 플러쉬
while (it2 != m_map_itemCache.end()) while (it2 != m_map_itemCache.end())
@ -471,7 +475,8 @@ void CClientManager::QUERY_BOOT(CPeer* peer, TPacketGDBoot * p)
size_t num_monarch_candidacy = CMonarch::instance().MonarchCandidacySize(); size_t num_monarch_candidacy = CMonarch::instance().MonarchCandidacySize();
peer->EncodeWORD(sizeof(MonarchCandidacy)); peer->EncodeWORD(sizeof(MonarchCandidacy));
peer->EncodeWORD(num_monarch_candidacy); peer->EncodeWORD(num_monarch_candidacy);
if (num_monarch_candidacy != 0) { if (num_monarch_candidacy != 0)
{
peer->Encode(&rVecMonarchCandidacy[0], sizeof(MonarchCandidacy) * num_monarch_candidacy); peer->Encode(&rVecMonarchCandidacy[0], sizeof(MonarchCandidacy) * num_monarch_candidacy);
} }
// END_MONARCE // END_MONARCE
@ -660,7 +665,6 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
return; return;
} }
// 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼 // 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼
// 보이기 때문에 창고가 아얘 안열리는게 나음 // 보이기 때문에 창고가 아얘 안열리는게 나음
if (!msg->Get()->pSQLResult) if (!msg->Get()->pSQLResult)
@ -769,12 +773,22 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
{ {
switch (dwItemVnum) switch (dwItemVnum)
{ {
case 72723: case 72724: case 72725: case 72726: case 72723:
case 72727: case 72728: case 72729: case 72730: case 72724:
case 72725:
case 72726:
case 72727:
case 72728:
case 72729:
case 72730:
// 무시무시하지만 이전에 하던 걸 고치기는 무섭고... // 무시무시하지만 이전에 하던 걸 고치기는 무섭고...
// 그래서 그냥 하드 코딩. 선물 상자용 자동물약 아이템들. // 그래서 그냥 하드 코딩. 선물 상자용 자동물약 아이템들.
case 76004: case 76005: case 76021: case 76022: case 76004:
case 79012: case 79013: case 76005:
case 76021:
case 76022:
case 79012:
case 79013:
if (pItemAward->dwSocket2 == 0) if (pItemAward->dwSocket2 == 0)
{ {
dwSocket2 = pItemTable->alValues[0]; dwSocket2 = pItemTable->alValues[0];
@ -2045,15 +2059,13 @@ void CClientManager::MyshopPricelistRequest(CPeer* peer, DWORD dwHandle, DWORD d
TPacketMyshopPricelistHeader header = TPacketMyshopPricelistHeader header =
{ {
pTable->dwOwnerID, pTable->dwOwnerID,
pTable->byCount pTable->byCount};
};
size_t sizePriceListSize = sizeof(TItemPriceInfo) * pTable->byCount; size_t sizePriceListSize = sizeof(TItemPriceInfo) * pTable->byCount;
peer->EncodeHeader(HEADER_DG_MYSHOP_PRICELIST_RES, dwHandle, sizeof(header) + sizePriceListSize); peer->EncodeHeader(HEADER_DG_MYSHOP_PRICELIST_RES, dwHandle, sizeof(header) + sizePriceListSize);
peer->Encode(&header, sizeof(header)); peer->Encode(&header, sizeof(header));
peer->Encode(pTable->aPriceInfo, sizePriceListSize); peer->Encode(pTable->aPriceInfo, sizePriceListSize);
} }
else else
{ {
@ -2475,7 +2487,8 @@ CPeer * CClientManager::AddPeer(bufferevent* bufev, sockaddr* addr)
{ {
auto *pPeer = new CPeer; auto *pPeer = new CPeer;
if (!pPeer->Accept(bufev, addr)) { if (!pPeer->Accept(bufev, addr))
{
delete pPeer; delete pPeer;
return nullptr; return nullptr;
} }
@ -2773,7 +2786,6 @@ int CClientManager::Process()
m_iCacheFlushCount = 0; m_iCacheFlushCount = 0;
// 플레이어 플러쉬 // 플레이어 플러쉬
UpdatePlayerCache(); UpdatePlayerCache();
// 아이템 플러쉬 // 아이템 플러쉬
@ -3544,7 +3556,6 @@ void CClientManager::Election(CPeer * peer, DWORD dwHandle, const char* data)
peer->Encode(&Success, sizeof(int)); peer->Encode(&Success, sizeof(int));
return; return;
} }
} }
void CClientManager::Candidacy(CPeer *peer, DWORD dwHandle, const char *data) void CClientManager::Candidacy(CPeer *peer, DWORD dwHandle, const char *data)
{ {
@ -3623,7 +3634,6 @@ void CClientManager::AddMonarchMoney(CPeer * peer, DWORD dwHandle, const char *
p->Encode(&Empire, sizeof(int)); p->Encode(&Empire, sizeof(int));
p->Encode(&Money, sizeof(int)); p->Encode(&Money, sizeof(int));
} }
} }
} }
void CClientManager::DecMonarchMoney(CPeer *peer, DWORD dwHandle, const char *data) void CClientManager::DecMonarchMoney(CPeer *peer, DWORD dwHandle, const char *data)
@ -3673,6 +3683,7 @@ void CClientManager::TakeMonarchMoney(CPeer * peer, DWORD dwHandle, const char *
SPDLOG_TRACE("[MONARCH] Take money Empire({}) Money({})", Empire, Money); SPDLOG_TRACE("[MONARCH] Take money Empire({}) Money({})", Empire, Money);
// TODO The if seems redundant no reason to be so
if (CMonarch::instance().TakeMoney(Empire, pid, Money) == true) if (CMonarch::instance().TakeMoney(Empire, pid, Money) == true)
{ {
peer->EncodeHeader(HEADER_DG_TAKE_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int)); peer->EncodeHeader(HEADER_DG_TAKE_MONARCH_MONEY, dwHandle, sizeof(int) + sizeof(int));
@ -3909,16 +3920,17 @@ void CClientManager::DeleteAwardId(TPacketDeleteAwardID *data)
{ {
SPDLOG_DEBUG("DELETE_AWARDID : could not find the id: {}", data->dwID); SPDLOG_DEBUG("DELETE_AWARDID : could not find the id: {}", data->dwID);
} }
} }
void CClientManager::UpdateChannelStatus(TChannelStatus *pData) void CClientManager::UpdateChannelStatus(TChannelStatus *pData)
{ {
TChannelStatusMap::iterator it = m_mChannelStatus.find(pData->nPort); TChannelStatusMap::iterator it = m_mChannelStatus.find(pData->nPort);
if (it != m_mChannelStatus.end()) { if (it != m_mChannelStatus.end())
{
it->second = pData->bStatus; it->second = pData->bStatus;
} }
else { else
{
m_mChannelStatus.insert(TChannelStatusMap::value_type(pData->nPort, pData->bStatus)); m_mChannelStatus.insert(TChannelStatusMap::value_type(pData->nPort, pData->bStatus));
} }
} }
@ -3928,7 +3940,8 @@ void CClientManager::RequestChannelStatus(CPeer* peer, DWORD dwHandle)
const int nSize = m_mChannelStatus.size(); const int nSize = m_mChannelStatus.size();
peer->EncodeHeader(HEADER_DG_RESPOND_CHANNELSTATUS, dwHandle, sizeof(TChannelStatus) * nSize + sizeof(int)); peer->EncodeHeader(HEADER_DG_RESPOND_CHANNELSTATUS, dwHandle, sizeof(TChannelStatus) * nSize + sizeof(int));
peer->Encode(&nSize, sizeof(int)); peer->Encode(&nSize, sizeof(int));
for (TChannelStatusMap::iterator it = m_mChannelStatus.begin(); it != m_mChannelStatus.end(); it++) { for (TChannelStatusMap::iterator it = m_mChannelStatus.begin(); it != m_mChannelStatus.end(); it++)
{
peer->Encode(&it->first, sizeof(short)); peer->Encode(&it->first, sizeof(short));
peer->Encode(&it->second, sizeof(BYTE)); peer->Encode(&it->second, sizeof(BYTE));
} }

View File

@ -150,12 +150,10 @@ class CClientManager : public singleton<CClientManager>
*/ */
void PutItemPriceListCache(const TItemPriceListTable *pItemPriceList); void PutItemPriceListCache(const TItemPriceListTable *pItemPriceList);
/// Flush 시간이 만료된 아이템 가격정보 리스트 캐시를 Flush 해주고 캐시에서 삭제한다. /// Flush 시간이 만료된 아이템 가격정보 리스트 캐시를 Flush 해주고 캐시에서 삭제한다.
void UpdateItemPriceListCache(void); void UpdateItemPriceListCache(void);
// END_OF_MYSHOP_PRICE_LIST // END_OF_MYSHOP_PRICE_LIST
void SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable); void SendGuildSkillUsable(DWORD guild_id, DWORD dwSkillVnum, bool bUsable);
void SetCacheFlushCountLimit(int iLimit); void SetCacheFlushCountLimit(int iLimit);
@ -289,7 +287,6 @@ class CClientManager : public singleton<CClientManager>
void QUERY_ITEM_DESTROY(CPeer *pkPeer, const char *c_pData); void QUERY_ITEM_DESTROY(CPeer *pkPeer, const char *c_pData);
void QUERY_ITEM_FLUSH(CPeer *pkPeer, const char *c_pData); void QUERY_ITEM_FLUSH(CPeer *pkPeer, const char *c_pData);
void QUERY_QUEST_SAVE(CPeer *pkPeer, TQuestTable *, DWORD dwLen); void QUERY_QUEST_SAVE(CPeer *pkPeer, TQuestTable *, DWORD dwLen);
void QUERY_ADD_AFFECT(CPeer *pkPeer, TPacketGDAddAffect *p); void QUERY_ADD_AFFECT(CPeer *pkPeer, TPacketGDAddAffect *p);
void QUERY_REMOVE_AFFECT(CPeer *pkPeer, TPacketGDRemoveAffect *p); void QUERY_REMOVE_AFFECT(CPeer *pkPeer, TPacketGDRemoveAffect *p);
@ -486,7 +483,6 @@ class CClientManager : public singleton<CClientManager>
bool __GetHostInfo(std::vector<std::string> &rIPVec); bool __GetHostInfo(std::vector<std::string> &rIPVec);
// END_ADMIN_MANAGER // END_ADMIN_MANAGER
// RELOAD_ADMIN // RELOAD_ADMIN
void ReloadAdmin(CPeer *peer, TPacketReloadAdmin *p); void ReloadAdmin(CPeer *peer, TPacketReloadAdmin *p);
// END_RELOAD_ADMIN // END_RELOAD_ADMIN
@ -523,7 +519,6 @@ class CClientManager : public singleton<CClientManager>
void SetMonarch(CPeer *peer, DWORD dwHandle, const char *p); void SetMonarch(CPeer *peer, DWORD dwHandle, const char *p);
void RMMonarch(CPeer *peer, DWORD dwHandle, const char *p); void RMMonarch(CPeer *peer, DWORD dwHandle, const char *p);
void DecMonarchMoney(CPeer *peer, DWORD dwHandle, const char *p); void DecMonarchMoney(CPeer *peer, DWORD dwHandle, const char *p);
// END_MONARCH // END_MONARCH

View File

@ -97,7 +97,6 @@ bool CClientManager::InitializeTables()
return false; return false;
} }
return true; return true;
} }
@ -193,7 +192,6 @@ bool CClientManager::InitializeMobTable()
// 5) (최종) 게임 클라이언트에서 제대로 작동 하는지. // 5) (최종) 게임 클라이언트에서 제대로 작동 하는지.
//_______________________________________________// //_______________________________________________//
//===============================================// //===============================================//
// 1) 'mob_names.txt' 파일을 읽어서 (a)[localMap] 맵을 만든다. // 1) 'mob_names.txt' 파일을 읽어서 (a)[localMap] 맵을 만든다.
//<(a)localMap 맵 생성> //<(a)localMap 맵 생성>
@ -205,15 +203,17 @@ bool CClientManager::InitializeMobTable()
{ {
SPDLOG_ERROR("mob_names.txt Failed to read the file"); SPDLOG_ERROR("mob_names.txt Failed to read the file");
isNameFile = false; isNameFile = false;
} else { }
else
{
nameData.Next(); // 설명row 생략. nameData.Next(); // 설명row 생략.
while(nameData.Next()) { while (nameData.Next())
{
localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1); localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1);
} }
} }
//________________________________________________// //________________________________________________//
//===============================================// //===============================================//
// 2) 'mob_proto_test.txt'파일과 (a)localMap 맵으로 // 2) 'mob_proto_test.txt'파일과 (a)localMap 맵으로
// (b)[test_map_mobTableByVnum](vnum:TMobTable) 맵을 생성한다. // (b)[test_map_mobTableByVnum](vnum:TMobTable) 맵을 생성한다.
@ -229,7 +229,8 @@ bool CClientManager::InitializeMobTable()
} }
// 2. (c)[test_map_mobTableByVnum](vnum:TMobTable) 맵 생성. // 2. (c)[test_map_mobTableByVnum](vnum:TMobTable) 맵 생성.
map<DWORD, TMobTable *> test_map_mobTableByVnum; map<DWORD, TMobTable *> test_map_mobTableByVnum;
if (isTestFile) { if (isTestFile)
{
test_data.Next(); // 설명 로우 넘어가기. test_data.Next(); // 설명 로우 넘어가기.
// ㄱ. 테스트 몬스터 테이블 생성. // ㄱ. 테스트 몬스터 테이블 생성.
@ -239,7 +240,8 @@ bool CClientManager::InitializeMobTable()
memset(test_mob_table, 0, sizeof(TMobTable) * test_MobTableSize); memset(test_mob_table, 0, sizeof(TMobTable) * test_MobTableSize);
// ㄴ. 테스트 몬스터 테이블에 값을 넣고, 맵에까지 넣기. // ㄴ. 테스트 몬스터 테이블에 값을 넣고, 맵에까지 넣기.
while(test_data.Next()) { while (test_data.Next())
{
if (!Set_Proto_Mob_Table(test_mob_table, test_data, localMap)) if (!Set_Proto_Mob_Table(test_mob_table, test_data, localMap))
{ {
@ -248,10 +250,8 @@ bool CClientManager::InitializeMobTable()
test_map_mobTableByVnum.insert(std::map<DWORD, TMobTable *>::value_type(test_mob_table->dwVnum, test_mob_table)); test_map_mobTableByVnum.insert(std::map<DWORD, TMobTable *>::value_type(test_mob_table->dwVnum, test_mob_table));
++test_mob_table; ++test_mob_table;
} }
} }
// 3) 'mob_proto.txt' 파일과 (a)[localMap] 맵으로 // 3) 'mob_proto.txt' 파일과 (a)[localMap] 맵으로
@ -263,7 +263,8 @@ bool CClientManager::InitializeMobTable()
// 1. 파일 읽기. // 1. 파일 읽기.
cCsvTable data; cCsvTable data;
if(!data.Load("mob_proto.txt",'\t')) { if (!data.Load("mob_proto.txt", '\t'))
{
SPDLOG_ERROR("mob_proto.txt Failed to read the file"); SPDLOG_ERROR("mob_proto.txt Failed to read the file");
return false; return false;
} }
@ -271,11 +272,13 @@ bool CClientManager::InitializeMobTable()
// 2. (!)[mob_table] 생성하기 // 2. (!)[mob_table] 생성하기
// 2.1 새로 추가되는 갯수를 파악 // 2.1 새로 추가되는 갯수를 파악
int addNumber = 0; int addNumber = 0;
while(data.Next()) { while (data.Next())
{
int vnum = atoi(data.AsStringByIndex(0)); int vnum = atoi(data.AsStringByIndex(0));
std::map<DWORD, TMobTable *>::iterator it_map_mobTable; std::map<DWORD, TMobTable *>::iterator it_map_mobTable;
it_map_mobTable = test_map_mobTableByVnum.find(vnum); it_map_mobTable = test_map_mobTableByVnum.find(vnum);
if(it_map_mobTable != test_map_mobTableByVnum.end()) { if (it_map_mobTable != test_map_mobTableByVnum.end())
{
addNumber++; addNumber++;
} }
} }
@ -304,11 +307,13 @@ bool CClientManager::InitializeMobTable()
bool isSameRow = true; bool isSameRow = true;
std::map<DWORD, TMobTable *>::iterator it_map_mobTable; std::map<DWORD, TMobTable *>::iterator it_map_mobTable;
it_map_mobTable = test_map_mobTableByVnum.find(atoi(data.AsStringByIndex(col))); it_map_mobTable = test_map_mobTableByVnum.find(atoi(data.AsStringByIndex(col)));
if(it_map_mobTable == test_map_mobTableByVnum.end()) { if (it_map_mobTable == test_map_mobTableByVnum.end())
{
isSameRow = false; isSameRow = false;
} }
// 같은 row 가 있으면 (b)에서 읽어온다. // 같은 row 가 있으면 (b)에서 읽어온다.
if(isSameRow) { if (isSameRow)
{
TMobTable *tempTable = it_map_mobTable->second; TMobTable *tempTable = it_map_mobTable->second;
mob_table->dwVnum = tempTable->dwVnum; mob_table->dwVnum = tempTable->dwVnum;
@ -357,7 +362,6 @@ bool CClientManager::InitializeMobTable()
mob_table->dwDrainSP = tempTable->dwDrainSP; mob_table->dwDrainSP = tempTable->dwDrainSP;
mob_table->dwPolymorphItemVnum = tempTable->dwPolymorphItemVnum; mob_table->dwPolymorphItemVnum = tempTable->dwPolymorphItemVnum;
mob_table->Skills[0].bLevel = tempTable->Skills[0].bLevel; mob_table->Skills[0].bLevel = tempTable->Skills[0].bLevel;
mob_table->Skills[0].dwVnum = tempTable->Skills[0].dwVnum; mob_table->Skills[0].dwVnum = tempTable->Skills[0].dwVnum;
mob_table->Skills[1].bLevel = tempTable->Skills[1].bLevel; mob_table->Skills[1].bLevel = tempTable->Skills[1].bLevel;
@ -374,27 +378,24 @@ bool CClientManager::InitializeMobTable()
mob_table->bGodSpeedPoint = tempTable->bGodSpeedPoint; mob_table->bGodSpeedPoint = tempTable->bGodSpeedPoint;
mob_table->bDeathBlowPoint = tempTable->bDeathBlowPoint; mob_table->bDeathBlowPoint = tempTable->bDeathBlowPoint;
mob_table->bRevivePoint = tempTable->bRevivePoint; mob_table->bRevivePoint = tempTable->bRevivePoint;
} else { }
else
{
if (!Set_Proto_Mob_Table(mob_table, data, localMap)) if (!Set_Proto_Mob_Table(mob_table, data, localMap))
{ {
SPDLOG_ERROR("Mob proto table setup failed."); SPDLOG_ERROR("Mob proto table setup failed.");
} }
} }
// 셋에 vnum 추가 // 셋에 vnum 추가
vnumSet.insert(mob_table->dwVnum); vnumSet.insert(mob_table->dwVnum);
SPDLOG_TRACE("MOB #{:<5} {:24} {:24} level: {:<3} rank: {} empire: {}", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire); SPDLOG_TRACE("MOB #{:<5} {:24} {:24} level: {:<3} rank: {} empire: {}", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire);
++mob_table; ++mob_table;
} }
//_____________________________________________________// //_____________________________________________________//
// 4) (b)[test_map_mobTableByVnum]의 row중, (!)[mob_table]에 없는 것을 추가한다. // 4) (b)[test_map_mobTableByVnum]의 row중, (!)[mob_table]에 없는 것을 추가한다.
// 파일 다시 읽어오기. // 파일 다시 읽어오기.
test_data.Destroy(); test_data.Destroy();
@ -405,7 +406,8 @@ bool CClientManager::InitializeMobTable()
SPDLOG_ERROR("No test file exists, proceed as is."); SPDLOG_ERROR("No test file exists, proceed as is.");
isTestFile = false; isTestFile = false;
} }
if(isTestFile) { if (isTestFile)
{
test_data.Next(); // 설명 로우 넘어가기. test_data.Next(); // 설명 로우 넘어가기.
while (test_data.Next()) // 테스트 데이터 각각을 훑어나가며,새로운 것을 추가한다. while (test_data.Next()) // 테스트 데이터 각각을 훑어나가며,새로운 것을 추가한다.
@ -413,7 +415,8 @@ bool CClientManager::InitializeMobTable()
// 중복되는 부분이면 넘어간다. // 중복되는 부분이면 넘어간다.
set<int>::iterator itVnum; set<int>::iterator itVnum;
itVnum = vnumSet.find(atoi(test_data.AsStringByIndex(0))); itVnum = vnumSet.find(atoi(test_data.AsStringByIndex(0)));
if (itVnum != vnumSet.end()) { if (itVnum != vnumSet.end())
{
continue; continue;
} }
@ -424,7 +427,6 @@ bool CClientManager::InitializeMobTable()
SPDLOG_DEBUG("MOB #{:<5} {:24} {:24} level: {:<3} rank: {} empire: {}", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire); SPDLOG_DEBUG("MOB #{:<5} {:24} {:24} level: {:<3} rank: {} empire: {}", mob_table->dwVnum, mob_table->szName, mob_table->szLocaleName, mob_table->bLevel, mob_table->bRank, mob_table->bEmpire);
++mob_table; ++mob_table;
} }
} }
sort(m_vec_mobTable.begin(), m_vec_mobTable.end(), FCompareVnum()); sort(m_vec_mobTable.begin(), m_vec_mobTable.end(), FCompareVnum());
@ -595,8 +597,6 @@ bool CClientManager::InitializeItemTable()
// 5) (Final) Verify that it works correctly in the game client. // 5) (Final) Verify that it works correctly in the game client.
//_______________________________________________// //_______________________________________________//
//=================================================================================// //=================================================================================//
// 1) Read the 'item_names.txt' file to create (a) [localMap] (vnum:name) map. // 1) Read the 'item_names.txt' file to create (a) [localMap] (vnum:name) map.
//=================================================================================// //=================================================================================//
@ -607,9 +607,12 @@ bool CClientManager::InitializeItemTable()
{ {
SPDLOG_ERROR("item_names.txt Failed to read the file"); SPDLOG_ERROR("item_names.txt Failed to read the file");
isNameFile = false; isNameFile = false;
} else { }
else
{
nameData.Next(); nameData.Next();
while(nameData.Next()) { while (nameData.Next())
{
localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1); localMap[atoi(nameData.AsStringByIndex(0))] = nameData.AsStringByIndex(1);
} }
} }
@ -626,7 +629,9 @@ bool CClientManager::InitializeItemTable()
{ {
SPDLOG_ERROR("item_proto_test.txt Failed to read the file"); SPDLOG_ERROR("item_proto_test.txt Failed to read the file");
// return false; // return false;
} else { }
else
{
test_data.Next(); // 설명 로우 넘어가기. test_data.Next(); // 설명 로우 넘어가기.
// 2. Create the test item table. // 2. Create the test item table.
@ -636,8 +641,8 @@ bool CClientManager::InitializeItemTable()
memset(test_item_table, 0, sizeof(TItemTable) * test_itemTableSize); memset(test_item_table, 0, sizeof(TItemTable) * test_itemTableSize);
// 3. Insert values into the test item table and populate the map. // 3. Insert values into the test item table and populate the map.
while(test_data.Next()) { while (test_data.Next())
{
if (!Set_Proto_Item_Table(test_item_table, test_data, localMap)) if (!Set_Proto_Item_Table(test_item_table, test_data, localMap))
{ {
@ -646,12 +651,10 @@ bool CClientManager::InitializeItemTable()
test_map_itemTableByVnum.insert(std::map<DWORD, TItemTable *>::value_type(test_item_table->dwVnum, test_item_table)); test_map_itemTableByVnum.insert(std::map<DWORD, TItemTable *>::value_type(test_item_table->dwVnum, test_item_table));
test_item_table++; test_item_table++;
} }
} }
//______________________________________________________________________// //______________________________________________________________________//
//========================================================================// //========================================================================//
// 3) Using the 'item_proto.txt' file and the (a) [localMap] map, // 3) Using the 'item_proto.txt' file and the (a) [localMap] map,
// create the (!) [item_table] and <m_map_itemTableByVnum>. // create the (!) [item_table] and <m_map_itemTableByVnum>.
@ -683,11 +686,13 @@ bool CClientManager::InitializeItemTable()
//===== Create item table =====// //===== Create item table =====//
// Determine the number of newly added items. // Determine the number of newly added items.
int addNumber = 0; int addNumber = 0;
while(data.Next()) { while (data.Next())
{
int vnum = atoi(data.AsStringByIndex(0)); int vnum = atoi(data.AsStringByIndex(0));
std::map<DWORD, TItemTable *>::iterator it_map_itemTable; std::map<DWORD, TItemTable *>::iterator it_map_itemTable;
it_map_itemTable = test_map_itemTableByVnum.find(vnum); it_map_itemTable = test_map_itemTableByVnum.find(vnum);
if(it_map_itemTable != test_map_itemTableByVnum.end()) { if (it_map_itemTable != test_map_itemTableByVnum.end())
{
addNumber++; addNumber++;
} }
} }
@ -712,17 +717,17 @@ bool CClientManager::InitializeItemTable()
std::map<DWORD, TItemTable *>::iterator it_map_itemTable; std::map<DWORD, TItemTable *>::iterator it_map_itemTable;
it_map_itemTable = test_map_itemTableByVnum.find(atoi(data.AsStringByIndex(col))); it_map_itemTable = test_map_itemTableByVnum.find(atoi(data.AsStringByIndex(col)));
if(it_map_itemTable == test_map_itemTableByVnum.end()) { if (it_map_itemTable == test_map_itemTableByVnum.end())
{
// Store data for each column // Store data for each column
if (!Set_Proto_Item_Table(item_table, data, localMap)) if (!Set_Proto_Item_Table(item_table, data, localMap))
{ {
SPDLOG_ERROR("Item proto table setup failed."); SPDLOG_ERROR("Item proto table setup failed.");
} }
}
else
{ // $$$$$$$$$$$$$$$$$$$$$ There is test item information!
} else { // $$$$$$$$$$$$$$$$$$$$$ There is test item information!
TItemTable *tempTable = it_map_itemTable->second; TItemTable *tempTable = it_map_itemTable->second;
item_table->dwVnum = tempTable->dwVnum; item_table->dwVnum = tempTable->dwVnum;
@ -770,7 +775,6 @@ bool CClientManager::InitializeItemTable()
item_table->sAddonType = tempTable->sAddonType; item_table->sAddonType = tempTable->sAddonType;
item_table->bWeight = tempTable->bWeight; item_table->bWeight = tempTable->bWeight;
} }
vnumSet.insert(item_table->dwVnum); vnumSet.insert(item_table->dwVnum);
m_map_itemTableByVnum.insert(std::map<DWORD, TItemTable *>::value_type(item_table->dwVnum, item_table)); m_map_itemTableByVnum.insert(std::map<DWORD, TItemTable *>::value_type(item_table->dwVnum, item_table));
@ -786,7 +790,9 @@ bool CClientManager::InitializeItemTable()
{ {
SPDLOG_ERROR("item_proto_test.txt Failed to read the file"); SPDLOG_ERROR("item_proto_test.txt Failed to read the file");
// return false; // return false;
} else { }
else
{
test_data.Next(); // Skip the description row. test_data.Next(); // Skip the description row.
while (test_data.Next()) // Iterate through each test data entry and add new ones. while (test_data.Next()) // Iterate through each test data entry and add new ones.
@ -794,7 +800,8 @@ bool CClientManager::InitializeItemTable()
// Skip if its a duplicate. // Skip if its a duplicate.
set<int>::iterator itVnum; set<int>::iterator itVnum;
itVnum = vnumSet.find(atoi(test_data.AsStringByIndex(0))); itVnum = vnumSet.find(atoi(test_data.AsStringByIndex(0)));
if (itVnum != vnumSet.end()) { if (itVnum != vnumSet.end())
{
continue; continue;
} }
@ -803,16 +810,12 @@ bool CClientManager::InitializeItemTable()
SPDLOG_ERROR("Item proto table setup failed."); SPDLOG_ERROR("Item proto table setup failed.");
} }
m_map_itemTableByVnum.insert(std::map<DWORD, TItemTable *>::value_type(item_table->dwVnum, item_table)); m_map_itemTableByVnum.insert(std::map<DWORD, TItemTable *>::value_type(item_table->dwVnum, item_table));
item_table++; item_table++;
} }
} }
// QUEST_ITEM_PROTO_DISABLE // QUEST_ITEM_PROTO_DISABLE
// InitializeQuestItemTable(); // InitializeQuestItemTable();
// END_OF_QUEST_ITEM_PROTO_DISABLE // END_OF_QUEST_ITEM_PROTO_DISABLE
@ -848,7 +851,6 @@ bool CClientManager::InitializeItemTable()
return true; return true;
} }
bool CClientManager::InitializeSkillTable() bool CClientManager::InitializeSkillTable()
{ {
char query[4096]; char query[4096];
@ -1400,8 +1402,7 @@ bool CClientManager::MirrorMobTableIntoDB()
t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel, t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel,
t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel, t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel,
t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint);
);
} }
else else
{ {
@ -1452,8 +1453,7 @@ bool CClientManager::MirrorMobTableIntoDB()
t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel, t.Skills[0].dwVnum, t.Skills[0].bLevel, t.Skills[1].dwVnum, t.Skills[1].bLevel, t.Skills[2].dwVnum, t.Skills[2].bLevel,
t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel, t.Skills[3].dwVnum, t.Skills[3].bLevel, t.Skills[4].dwVnum, t.Skills[4].bLevel,
t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint t.bBerserkPoint, t.bStoneSkinPoint, t.bGodSpeedPoint, t.bDeathBlowPoint, t.bRevivePoint);
);
} }
CDBManager::instance().AsyncQuery(query); CDBManager::instance().AsyncQuery(query);

View File

@ -74,4 +74,3 @@ void CClientManager::SendEventFlagsOnSetup(CPeer* peer)
peer->Encode(&p, sizeof(TPacketSetEventFlag)); peer->Encode(&p, sizeof(TPacketSetEventFlag));
} }
} }

View File

@ -7,7 +7,6 @@
#include "QID.h" #include "QID.h"
#include "GuildManager.h" #include "GuildManager.h"
void CClientManager::GuildCreate(CPeer *peer, DWORD dwGuildID) void CClientManager::GuildCreate(CPeer *peer, DWORD dwGuildID)
{ {
SPDLOG_DEBUG("GuildCreate {}", dwGuildID); SPDLOG_DEBUG("GuildCreate {}", dwGuildID);
@ -241,4 +240,3 @@ void CClientManager::GuildChangeMaster(TPacketChangeGuildMaster* p)
ForwardPacket(HEADER_DG_ACK_CHANGE_GUILD_MASTER, &packet, sizeof(packet)); ForwardPacket(HEADER_DG_ACK_CHANGE_GUILD_MASTER, &packet, sizeof(packet));
} }
} }

View File

@ -37,4 +37,3 @@ void CClientManager::AckHorseName(DWORD dwPID, CPeer* peer)
peer->EncodeHeader(HEADER_DG_ACK_HORSE_NAME, 0, sizeof(TPacketUpdateHorseName)); peer->EncodeHeader(HEADER_DG_ACK_HORSE_NAME, 0, sizeof(TPacketUpdateHorseName));
peer->Encode(&packet, sizeof(TPacketUpdateHorseName)); peer->Encode(&packet, sizeof(TPacketUpdateHorseName));
} }

View File

@ -427,7 +427,6 @@ void CClientManager::RESULT_LOGIN(CPeer * peer, SQLMsg * msg)
// END_PREVENT_COPY_ITEM // END_PREVENT_COPY_ITEM
peer->EncodeHeader(HEADER_DG_LOGIN_SUCCESS, info->dwHandle, sizeof(TAccountTable)); peer->EncodeHeader(HEADER_DG_LOGIN_SUCCESS, info->dwHandle, sizeof(TAccountTable));
peer->Encode(info->pAccountTable, sizeof(TAccountTable)); peer->Encode(info->pAccountTable, sizeof(TAccountTable));
} }
delete info->pAccountTable; delete info->pAccountTable;
@ -518,4 +517,3 @@ void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDCh
strlcpy(pdg.name, p->name, sizeof(pdg.name)); strlcpy(pdg.name, p->name, sizeof(pdg.name));
peer->Encode(&pdg, sizeof(TPacketDGChangeName)); peer->Encode(&pdg, sizeof(TPacketDGChangeName));
} }

View File

@ -112,8 +112,7 @@ size_t CreatePlayerSaveQuery(char * pszQuery, size_t querySize, TPlayerTable * p
"horse_hp = %d, " "horse_hp = %d, "
"horse_hp_droptime = %u, " "horse_hp_droptime = %u, "
"horse_stamina = %d, " "horse_stamina = %d, "
"horse_skill_point = %d, " "horse_skill_point = %d, ",
,
GetTablePostfix(), GetTablePostfix(),
pkTab->job, pkTab->job,
pkTab->voice, pkTab->voice,
@ -253,7 +252,9 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
peer->Encode(&logInfo, sizeof(TPacketNeedLoginLogInfo)); peer->Encode(&logInfo, sizeof(TPacketNeedLoginLogInfo));
} }
char szQuery[1024] = { 0, }; char szQuery[1024] = {
0,
};
TItemCacheSet *pSet = GetItemCacheSet(pTab->id); TItemCacheSet *pSet = GetItemCacheSet(pTab->id);
@ -390,8 +391,6 @@ void CClientManager::QUERY_PLAYER_LOAD(CPeer * peer, DWORD dwHandle, TPlayerLoad
GetTablePostfix(), packet->player_id); GetTablePostfix(), packet->player_id);
CDBManager::instance().ReturnQuery(queryStr, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id)); CDBManager::instance().ReturnQuery(queryStr, QID_AFFECT, peer->GetHandle(), new ClientHandleInfo(dwHandle, packet->player_id));
} }
} }
void CClientManager::ItemAward(CPeer *peer, char *login) void CClientManager::ItemAward(CPeer *peer, char *login)
{ {
@ -612,7 +611,6 @@ void CClientManager::RESULT_COMPOSITE_PLAYER(CPeer * peer, SQLMsg * pMsg, DWORD
break; break;
*/ */
} }
} }
void CClientManager::RESULT_PLAYER_LOAD(CPeer *peer, MYSQL_RES *pRes, ClientHandleInfo *pkInfo) void CClientManager::RESULT_PLAYER_LOAD(CPeer *peer, MYSQL_RES *pRes, ClientHandleInfo *pkInfo)
@ -807,7 +805,8 @@ void CClientManager::__QUERY_PLAYER_CREATE(CPeer *peer, DWORD dwHandle, TPlayerC
MYSQL_ROW row = mysql_fetch_row(pMsg0->Get()->pSQLResult); MYSQL_ROW row = mysql_fetch_row(pMsg0->Get()->pSQLResult);
DWORD dwPID = 0; str_to_number(dwPID, row[0]); DWORD dwPID = 0;
str_to_number(dwPID, row[0]);
if (row[0] && dwPID > 0) if (row[0] && dwPID > 0)
{ {
peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0); peer->EncodeHeader(HEADER_DG_PLAYER_CREATE_ALREADY, dwHandle, 0);
@ -1262,4 +1261,3 @@ void CClientManager::FlushPlayerCacheSet(DWORD pid)
delete c; delete c;
} }
} }

View File

@ -138,12 +138,10 @@ bool CConfig::LoadFile(const char* filename)
GetLine(fp, szTmp); GetLine(fp, szTmp);
m_valueMap.insert(TValueMap::value_type(comment, szTmp)); m_valueMap.insert(TValueMap::value_type(comment, szTmp));
mode = 0; mode = 0;
} }
// ITEM_ID_RANGE_END // ITEM_ID_RANGE_END
} }
// 파일 닫는 부분. // 파일 닫는 부분.
fclose(fp); fclose(fp);
return true; return true;
@ -187,7 +185,6 @@ const char * CConfig::Get(const char* key)
return pstStr->c_str(); return pstStr->c_str();
} }
bool CConfig::GetValue(const char *key, int *dest) bool CConfig::GetValue(const char *key, int *dest)
{ {
if (!Search(key)) if (!Search(key))
@ -247,4 +244,3 @@ bool CConfig::GetTwoValue(const char* key, DWORD * dest1, DWORD *dest2)
return true; return true;
} }

View File

@ -107,14 +107,17 @@ bool cCsvFile::Load(const char* fileName, const char seperator, const char quote
Assert(seperator != quote); Assert(seperator != quote);
std::ifstream file(fileName, std::ios::in); std::ifstream file(fileName, std::ios::in);
if (!file) return false; if (!file)
return false;
Destroy(); // 기존의 데이터를 삭제 Destroy(); // 기존의 데이터를 삭제
cCsvRow *row = NULL; cCsvRow *row = NULL;
ParseState state = STATE_NORMAL; ParseState state = STATE_NORMAL;
std::string token = ""; std::string token = "";
char buf[2048+1] = {0,}; char buf[2048 + 1] = {
0,
};
while (file.good()) while (file.good())
{ {
@ -122,7 +125,8 @@ bool cCsvFile::Load(const char* fileName, const char seperator, const char quote
buf[sizeof(buf) - 1] = 0; buf[sizeof(buf) - 1] = 0;
std::string line(Trim(buf)); std::string line(Trim(buf));
if (line.empty() || (state == STATE_NORMAL && line[0] == '#')) continue; if (line.empty() || (state == STATE_NORMAL && line[0] == '#'))
continue;
std::string text = std::string(line) + " "; // 파싱 lookahead 때문에 붙여준다. std::string text = std::string(line) + " "; // 파싱 lookahead 때문에 붙여준다.
size_t cur = 0; size_t cur = 0;
@ -223,11 +227,18 @@ bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quot
// 출력 모드에 따라 파일을 적당한 플래그로 생성한다. // 출력 모드에 따라 파일을 적당한 플래그로 생성한다.
std::ofstream file; std::ofstream file;
if (append) { file.open(fileName, std::ios::out | std::ios::app); } if (append)
else { file.open(fileName, std::ios::out | std::ios::trunc); } {
file.open(fileName, std::ios::out | std::ios::app);
}
else
{
file.open(fileName, std::ios::out | std::ios::trunc);
}
// 파일을 열지 못했다면, false를 리턴한다. // 파일을 열지 못했다면, false를 리턴한다.
if (!file) return false; if (!file)
return false;
char special_chars[5] = {seperator, quote, '\r', '\n', 0}; char special_chars[5] = {seperator, quote, '\r', '\n', 0};
char quote_escape_string[3] = {quote, quote, 0}; char quote_escape_string[3] = {quote, quote, 0};
@ -258,15 +269,20 @@ bool cCsvFile::Save(const char* fileName, bool append, char seperator, char quot
for (size_t k = 0; k < token.size(); k++) for (size_t k = 0; k < token.size(); k++)
{ {
if (token[k] == quote) line += quote_escape_string; if (token[k] == quote)
else line += token[k]; line += quote_escape_string;
else
line += token[k];
} }
line += quote; line += quote;
} }
// 마지막 셀이 아니라면 ','를 토큰의 뒤에다 붙여줘야한다. // 마지막 셀이 아니라면 ','를 토큰의 뒤에다 붙여줘야한다.
if (j != row.size() - 1) { line += seperator; } if (j != row.size() - 1)
{
line += seperator;
}
} }
// 라인을 출력한다. // 라인을 출력한다.
@ -427,4 +443,3 @@ const cCsvRow* const cCsvTable::CurRow() const
return m_File[m_CurRow]; return m_File[m_CurRow];
} }

View File

@ -50,7 +50,6 @@ private:
NAME2INDEX_MAP m_Name2Index; ///< 셀 인덱스 대신으로 사용하기 위한 이름들 NAME2INDEX_MAP m_Name2Index; ///< 셀 인덱스 대신으로 사용하기 위한 이름들
INDEX2NAME_MAP m_Index2Name; ///< 잘못된 alias를 검사하기 위한 추가적인 맵 INDEX2NAME_MAP m_Index2Name; ///< 잘못된 alias를 검사하기 위한 추가적인 맵
public: public:
/// \brief 생성자 /// \brief 생성자
cCsvAlias() {} cCsvAlias() {}
@ -58,7 +57,6 @@ public:
/// \brief 소멸자 /// \brief 소멸자
virtual ~cCsvAlias() {} virtual ~cCsvAlias() {}
public: public:
/// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다. /// \brief 셀을 액세스할 때, 숫자 대신 사용할 이름을 등록한다.
void AddAlias(const char *name, size_t index); void AddAlias(const char *name, size_t index);
@ -72,7 +70,6 @@ public:
/// \brief 이름을 숫자 인덱스로 변환한다. /// \brief 이름을 숫자 인덱스로 변환한다.
size_t operator[](const char *name) const; size_t operator[](const char *name) const;
private: private:
/// \brief 복사 생성자 금지 /// \brief 복사 생성자 금지
cCsvAlias(const cCsvAlias &) {} cCsvAlias(const cCsvAlias &) {}
@ -81,7 +78,6 @@ private:
const cCsvAlias &operator=(const cCsvAlias &) { return *this; } const cCsvAlias &operator=(const cCsvAlias &) { return *this; }
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// \class cCsvRow /// \class cCsvRow
/// \brief CSV 파일의 한 행을 캡슐화한 클래스 /// \brief CSV 파일의 한 행을 캡슐화한 클래스
@ -117,7 +113,6 @@ public:
/// \brief 소멸자 /// \brief 소멸자
~cCsvRow() {} ~cCsvRow() {}
public: public:
/// \brief 해당 셀의 데이터를 int 형으로 반환한다. /// \brief 해당 셀의 데이터를 int 형으로 반환한다.
int AsInt(size_t index) const { return atoi(at(index).c_str()); } int AsInt(size_t index) const { return atoi(at(index).c_str()); }
@ -129,21 +124,23 @@ public:
const char *AsString(size_t index) const { return at(index).c_str(); } const char *AsString(size_t index) const { return at(index).c_str(); }
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다. /// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
int AsInt(const char* name, const cCsvAlias& alias) const { int AsInt(const char *name, const cCsvAlias &alias) const
{
return atoi(at(alias[name]).c_str()); return atoi(at(alias[name]).c_str());
} }
/// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다. /// \brief 해당하는 이름의 셀 데이터를 int 형으로 반환한다.
double AsDouble(const char* name, const cCsvAlias& alias) const { double AsDouble(const char *name, const cCsvAlias &alias) const
{
return atof(at(alias[name]).c_str()); return atof(at(alias[name]).c_str());
} }
/// \brief 해당하는 이름의 셀 데이터를 문자열로 반환한다. /// \brief 해당하는 이름의 셀 데이터를 문자열로 반환한다.
const char* AsString(const char* name, const cCsvAlias& alias) const { const char *AsString(const char *name, const cCsvAlias &alias) const
{
return at(alias[name]).c_str(); return at(alias[name]).c_str();
} }
private: private:
/// \brief 복사 생성자 금지 /// \brief 복사 생성자 금지
cCsvRow(const cCsvRow &) {} cCsvRow(const cCsvRow &) {}
@ -152,7 +149,6 @@ private:
const cCsvRow &operator=(const cCsvRow &) { return *this; } const cCsvRow &operator=(const cCsvRow &) { return *this; }
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// \class cCsvFile /// \class cCsvFile
/// \brief CSV(Comma Seperated Values) 파일을 read/write하기 위한 클래스 /// \brief CSV(Comma Seperated Values) 파일을 read/write하기 위한 클래스
@ -190,7 +186,6 @@ private:
ROWS m_Rows; ///< 행 컬렉션 ROWS m_Rows; ///< 행 컬렉션
public: public:
/// \brief 생성자 /// \brief 생성자
cCsvFile() {} cCsvFile() {}
@ -198,7 +193,6 @@ public:
/// \brief 소멸자 /// \brief 소멸자
virtual ~cCsvFile() { Destroy(); } virtual ~cCsvFile() { Destroy(); }
public: public:
/// \brief 지정된 이름의 CSV 파일을 로드한다. /// \brief 지정된 이름의 CSV 파일을 로드한다.
bool Load(const char *fileName, const char seperator = ',', const char quote = '"'); bool Load(const char *fileName, const char seperator = ',', const char quote = '"');
@ -218,7 +212,6 @@ public:
/// \brief 행의 갯수를 반환한다. /// \brief 행의 갯수를 반환한다.
size_t GetRowCount() const { return m_Rows.size(); } size_t GetRowCount() const { return m_Rows.size(); }
private: private:
/// \brief 복사 생성자 금지 /// \brief 복사 생성자 금지
cCsvFile(const cCsvFile &) {} cCsvFile(const cCsvFile &) {}
@ -227,7 +220,6 @@ private:
const cCsvFile &operator=(const cCsvFile &) { return *this; } const cCsvFile &operator=(const cCsvFile &) { return *this; }
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// \class cCsvTable /// \class cCsvTable
/// \brief CSV 파일을 이용해 테이블 데이터를 로드하는 경우가 많은데, 이 클래스는 /// \brief CSV 파일을 이용해 테이블 데이터를 로드하는 경우가 많은데, 이 클래스는
@ -264,7 +256,6 @@ private:
cCsvAlias m_Alias; ///< 문자열을 셀 인덱스로 변환하기 위한 객체 cCsvAlias m_Alias; ///< 문자열을 셀 인덱스로 변환하기 위한 객체
int m_CurRow; ///< 현재 횡단 중인 행 번호 int m_CurRow; ///< 현재 횡단 중인 행 번호
public: public:
/// \brief 생성자 /// \brief 생성자
cCsvTable(); cCsvTable();
@ -272,7 +263,6 @@ public:
/// \brief 소멸자 /// \brief 소멸자
virtual ~cCsvTable(); virtual ~cCsvTable();
public: public:
/// \brief 지정된 이름의 CSV 파일을 로드한다. /// \brief 지정된 이름의 CSV 파일을 로드한다.
bool Load(const char *fileName, const char seperator = ',', const char quote = '"'); bool Load(const char *fileName, const char seperator = ',', const char quote = '"');
@ -307,7 +297,6 @@ public:
/// \brief alias를 포함해 모든 데이터를 삭제한다. /// \brief alias를 포함해 모든 데이터를 삭제한다.
void Destroy(); void Destroy();
private: private:
/// \brief 현재 행을 반환한다. /// \brief 현재 행을 반환한다.
const cCsvRow *const CurRow() const; const cCsvRow *const CurRow() const;

View File

@ -6,183 +6,179 @@ extern std::string g_stLocale;
CDBManager::CDBManager() CDBManager::CDBManager()
{ {
Initialize(); StartWorkerThread();
} }
CDBManager::~CDBManager() CDBManager::~CDBManager()
{ {
Destroy(); Quit();
} }
void CDBManager::Initialize() int CDBManager::Connect(const char *db_address, int db_port, const char *db_name, const char *user, const char *pwd)
{ {
for (int i = 0; i < SQL_MAX_NUM; ++i) if (!db_address || !db_name || !user || !pwd)
return false;
try
{ {
m_mainSQL[i] = NULL; m_connPool = std::make_shared<PgConnectionPool>(
m_directSQL[i] = NULL; db_address, db_port, db_name, user, pwd,
m_asyncSQL[i] = NULL; 10, // poolSize
60 // idleTimeoutSeconds
);
// Optionally, do a test connection:
{
auto conn = m_connPool->acquire();
if (!conn || !conn->is_open())
{
SPDLOG_ERROR("Failed to open test connection to DB");
m_connPool.reset();
return false;
} }
} }
void CDBManager::Destroy() SPDLOG_INFO("Connected to PostgreSQL at {}:{}, DB: {}", db_address, db_port, db_name);
return true;
}
catch (const std::exception &ex)
{ {
Clear(); SPDLOG_ERROR("Exception while connecting to DB: {}", ex.what());
return false;
}
} }
void CDBManager::Clear() void CDBManager::Clear()
{ {
for (int i = 0; i < SQL_MAX_NUM; ++i) std::lock_guard<std::mutex> lock(m_queueMutex);
{ std::queue<std::shared_ptr<PgAsyncQuery>> empty;
if (m_mainSQL[i]) m_queryQueue.swap(empty);
{
delete m_mainSQL[i];
m_mainSQL[i] = NULL;
}
if (m_directSQL[i])
{
delete m_directSQL[i];
m_directSQL[i] = NULL;
}
if (m_asyncSQL[i])
{
delete m_asyncSQL[i];
m_asyncSQL[i] = NULL;
}
}
Initialize();
} }
void CDBManager::Quit() void CDBManager::Quit()
{ {
for (int i = 0; i < SQL_MAX_NUM; ++i) if (m_quit.exchange(1) == 0)
{ {
if (m_mainSQL[i]) StopWorkerThread();
m_mainSQL[i]->Quit(); if (m_connPool)
if (m_asyncSQL[i])
m_asyncSQL[i]->Quit();
if (m_directSQL[i])
m_directSQL[i]->Quit();
}
}
SQLMsg * CDBManager::PopResult()
{ {
SQLMsg * p; m_connPool.reset();
}
for (int i = 0; i < SQL_MAX_NUM; ++i) }
if (m_mainSQL[i] && m_mainSQL[i]->PopResult(&p))
return p;
return NULL;
} }
SQLMsg * CDBManager::PopResult(eSQL_SLOT slot) std::shared_ptr<PgConnectionPool> CDBManager::GetConnectionPool()
{ {
SQLMsg * p; return m_connPool;
if (m_mainSQL[slot] && m_mainSQL[slot]->PopResult(&p))
return p;
return NULL;
} }
int CDBManager::Connect(int iSlot, const char * db_address, const int db_port, const char * db_name, const char * user, const char * pwd)
void CDBManager::AsyncQuery(const std::string &query, const pqxx::params &params)
{ {
if (db_address == NULL || db_name == NULL) auto msg = std::make_shared<PgAsyncQuery>(++m_msgCounter, query, params);
return false;
if (iSlot < 0 || iSlot >= SQL_MAX_NUM)
return false;
SPDLOG_INFO("CREATING DIRECT_SQL");
m_directSQL[iSlot] = new CAsyncSQL2;
if (!m_directSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), true, db_port))
{ {
Clear(); std::lock_guard<std::mutex> lock(m_queueMutex);
return false; m_queryQueue.push(msg);
} }
m_queueCondition.notify_one();
}
SPDLOG_INFO("CREATING MAIN_SQL"); size_t CDBManager::GetPendingQueryCount() const
m_mainSQL[iSlot] = new CAsyncSQL2;
if (!m_mainSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port))
{ {
Clear(); std::lock_guard<std::mutex> lock(m_queueMutex);
return false; return m_queryQueue.size();
} }
SPDLOG_INFO("CREATING ASYNC_SQL"); size_t CDBManager::GetCompletedQueryCount() const
m_asyncSQL[iSlot] = new CAsyncSQL2;
if (!m_asyncSQL[iSlot]->Setup(db_address, user, pwd, db_name, g_stLocale.c_str(), false, db_port))
{ {
Clear(); return m_completedQueries.load();
return false;
} }
return true; void CDBManager::ResetQueryStats()
}
SQLMsg * CDBManager::DirectQuery(const char * c_pszQuery, int iSlot)
{ {
return m_directSQL[iSlot]->DirectQuery(c_pszQuery); m_completedQueries = 0;
} }
extern CPacketInfo g_query_info; void CDBManager::StartWorkerThread()
extern int g_query_count[2];
void CDBManager::ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * udata, int iSlot)
{ {
assert(iSlot < SQL_MAX_NUM); if (!m_workerRunning.exchange(true))
//SPDLOG_DEBUG("ReturnQuery {}", c_pszQuery);
CQueryInfo * p = new CQueryInfo;
p->iType = iType;
p->dwIdent = dwIdent;
p->pvData = udata;
m_mainSQL[iSlot]->ReturnQuery(c_pszQuery, p);
//g_query_info.Add(iType);
++g_query_count[0];
}
void CDBManager::AsyncQuery(const char * c_pszQuery, int iSlot)
{ {
assert(iSlot < SQL_MAX_NUM); m_workerThread = std::thread(&CDBManager::WorkerLoop, this);
m_asyncSQL[iSlot]->AsyncQuery(c_pszQuery);
++g_query_count[1];
}
unsigned int CDBManager::EscapeString(void *to, const void *from, unsigned int length, int iSlot)
{
assert(iSlot < SQL_MAX_NUM);
return mysql_real_escape_string(m_directSQL[iSlot]->GetSQLHandle(), (char *) to, (const char *) from, length);
}
void CDBManager::SetLocale(const char * szLocale)
{
const std::string stLocale(szLocale);
SPDLOG_DEBUG("SetLocale start");
for (int n = 0; n < SQL_MAX_NUM; ++n)
{
m_mainSQL[n]->SetLocale(stLocale);
m_directSQL[n]->SetLocale(stLocale);
m_asyncSQL[n]->SetLocale(stLocale);
}
SPDLOG_DEBUG("End setlocale {}", szLocale);
}
void CDBManager::QueryLocaleSet()
{
for (int n = 0; n < SQL_MAX_NUM; ++n)
{
m_mainSQL[n]->QueryLocaleSet();
m_directSQL[n]->QueryLocaleSet();
m_asyncSQL[n]->QueryLocaleSet();
} }
} }
void CDBManager::StopWorkerThread()
{
if (m_workerRunning.exchange(false))
{
m_queueCondition.notify_all();
if (m_workerThread.joinable())
{
m_workerThread.join();
}
}
}
void CDBManager::WorkerLoop()
{
SPDLOG_INFO("AsyncQuery worker thread started");
while (m_workerRunning)
{
std::shared_ptr<PgAsyncQuery> msg;
{
std::unique_lock<std::mutex> lock(m_queueMutex);
m_queueCondition.wait(lock, [this]
{ return !m_queryQueue.empty() || !m_workerRunning; });
if (!m_workerRunning && m_queryQueue.empty())
{
break;
}
if (!m_queryQueue.empty())
{
msg = m_queryQueue.front();
m_queryQueue.pop();
}
}
if (msg)
{
ProcessQuery(msg);
}
}
SPDLOG_INFO("AsyncQuery worker thread stopped");
}
void CDBManager::ProcessQuery(std::shared_ptr<PgAsyncQuery> msg)
{
try
{
if (!m_connPool)
{
throw std::runtime_error("No database connection available");
}
auto conn = m_connPool->acquire();
if (!conn)
{
throw std::runtime_error("Failed to get connection from pool");
}
pqxx::work txn(*conn);
txn.exec_params(msg->query, msg->parameters);
txn.commit();
SPDLOG_TRACE("AsyncQuery completed: {} (ID: {})", msg->query, msg->id);
}
catch (const std::exception &e)
{
SPDLOG_ERROR("AsyncQuery failed: {} (query: {}, ID: {})", e.what(), msg->query, msg->id);
}
++m_completedQueries;
}

View File

@ -1,97 +1,48 @@
// vim:ts=8 sw=4
#ifndef __INC_METIN2_DB_DBMANAGER_H__ #ifndef __INC_METIN2_DB_DBMANAGER_H__
#define __INC_METIN2_DB_DBMANAGER_H__ #define __INC_METIN2_DB_DBMANAGER_H__
// 디비 커넥션 클래스의 목적은... 디비에 접속해서 쿼리보내고 결과 받아오는 #include <libsql/include/PgConnectionPool.h>
// 모든 일들을 처리한다. #include <libsql/include/PgAsyncQuery.h>
// 코드 by 꼬붕 후로그래머 아노아~ = _=)b #include <memory>
#include <libsql/include/CAsyncSQL.h>
#define SQL_SAFE_LENGTH(size) (size * 2 + 1)
#define QUERY_SAFE_LENGTH(size) (1024 + SQL_SAFE_LENGTH(size))
class CQueryInfo
{
public:
int iType;
DWORD dwIdent;
void * pvData;
};
enum eSQL_SLOT
{
SQL_PLAYER,
SQL_ACCOUNT,
SQL_COMMON,
SQL_MAX_NUM,
};
class CDBManager : public singleton<CDBManager> class CDBManager : public singleton<CDBManager>
{ {
protected:
void Initialize();
void Destroy();
public: public:
CDBManager(); CDBManager();
virtual ~CDBManager(); virtual ~CDBManager();
int Connect(const char *host, int port, const char *dbname, const char *user, const char *pass);
void Clear(); void Clear();
void Quit(); void Quit();
int Connect(int iSlot, const char * host, int port, const char* dbname, const char* user, const char* pass); std::shared_ptr<PgConnectionPool> GetConnectionPool();
void ReturnQuery(const char * c_pszQuery, int iType, DWORD dwIdent, void * pvData, int iSlot = SQL_PLAYER); // Async Query
void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER);
SQLMsg * DirectQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER);
SQLMsg * PopResult(); void AsyncQuery(const std::string &query, const pqxx::params &params = pqxx::params{});
SQLMsg * PopResult(eSQL_SLOT slot );
unsigned int EscapeString(void * to, const void * from, unsigned int length, int iSlot = SQL_PLAYER); size_t GetPendingQueryCount() const;
size_t GetCompletedQueryCount() const;
DWORD CountReturnQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQuery() : 0; } void ResetQueryStats();
DWORD CountReturnResult(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountResult() : 0; }
DWORD CountReturnQueryFinished(int i) { return m_mainSQL[i] ? m_mainSQL[i]->CountQueryFinished() : 0; }
DWORD CountReturnCopiedQuery(int i) { return m_mainSQL[i] ? m_mainSQL[i]->GetCopiedQueryCount() : 0; }
DWORD CountAsyncQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQuery() : 0; }
DWORD CountAsyncResult(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountResult() : 0; }
DWORD CountAsyncQueryFinished(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->CountQueryFinished() : 0; }
DWORD CountAsyncCopiedQuery(int i) { return m_asyncSQL[i] ? m_asyncSQL[i]->GetCopiedQueryCount() : 0; }
void ResetCounter()
{
for (int i = 0; i < SQL_MAX_NUM; ++i)
{
if (m_mainSQL[i])
{
m_mainSQL[i]->ResetQueryFinished();
m_mainSQL[i]->ResetCopiedQueryCount();
}
if (m_asyncSQL[i])
{
m_asyncSQL[i]->ResetQueryFinished();
m_asyncSQL[i]->ResetCopiedQueryCount();
}
}
}
private: private:
CAsyncSQL2 * m_mainSQL[SQL_MAX_NUM]; std::shared_ptr<PgConnectionPool> m_connPool;
CAsyncSQL2 * m_directSQL[SQL_MAX_NUM]; std::atomic<int> m_quit{0};
CAsyncSQL2 * m_asyncSQL[SQL_MAX_NUM];
int m_quit; // looping flag // Async Query
//CHARSET void StartWorkerThread();
public: void StopWorkerThread();
void SetLocale(const char * szLocale ); void WorkerLoop();
void QueryLocaleSet(); void ProcessQuery(std::shared_ptr<PgAsyncQuery> msg);
private:
//END_CHARSET std::queue<std::shared_ptr<PgAsyncQuery>> m_queryQueue;
mutable std::mutex m_queueMutex;
std::condition_variable m_queueCondition;
std::thread m_workerThread;
std::atomic<int> m_msgCounter{0};
std::atomic<size_t> m_completedQueries{0};
std::atomic<bool> m_workerRunning{false};
}; };
#endif #endif

View File

@ -14,15 +14,24 @@ bool isEurope()
{ {
do do
{ {
if (g_stLocale.compare("germany") == 0) break; if (g_stLocale.compare("germany") == 0)
if (g_stLocale.compare("france") == 0) break; break;
if (g_stLocale.compare("italy") == 0) break; if (g_stLocale.compare("france") == 0)
if (g_stLocale.compare("spain") == 0) break; break;
if (g_stLocale.compare("uk") == 0) break; if (g_stLocale.compare("italy") == 0)
if (g_stLocale.compare("turkey") == 0) break; break;
if (g_stLocale.compare("poland") == 0) break; if (g_stLocale.compare("spain") == 0)
if (g_stLocale.compare("portugal") == 0) break; break;
if (g_stLocale.compare("greek") == 0) break; if (g_stLocale.compare("uk") == 0)
break;
if (g_stLocale.compare("turkey") == 0)
break;
if (g_stLocale.compare("poland") == 0)
break;
if (g_stLocale.compare("portugal") == 0)
break;
if (g_stLocale.compare("greek") == 0)
break;
return false; return false;
} while (false); } while (false);
@ -35,8 +44,10 @@ DWORD GetGuildWarWaitStartDuration()
// const int GUILD_WAR_WAIT_START_DURATION = 60; // const int GUILD_WAR_WAIT_START_DURATION = 60;
// const int GUILD_WAR_WAIT_START_DURATION = 5; // const int GUILD_WAR_WAIT_START_DURATION = 5;
if (isEurope() == true) return 60; if (isEurope() == true)
else return 5; return 60;
else
return 5;
} }
DWORD GetGuildWarReserveSeconds() DWORD GetGuildWarReserveSeconds()
@ -44,8 +55,10 @@ DWORD GetGuildWarReserveSeconds()
// const int GUILD_WAR_RESERVE_SECONDS = 180; // const int GUILD_WAR_RESERVE_SECONDS = 180;
// const int GUILD_WAR_RESERVE_SECONDS = 10; // const int GUILD_WAR_RESERVE_SECONDS = 10;
if (isEurope() == true) return 180; if (isEurope() == true)
else return 10; return 180;
else
return 10;
} }
namespace namespace
@ -228,8 +241,10 @@ void CGuildManager::ResultRanking(MYSQL_RES * pRes)
while ((row = mysql_fetch_row(pRes))) while ((row = mysql_fetch_row(pRes)))
{ {
DWORD dwGID = 0; str_to_number(dwGID, row[0]); DWORD dwGID = 0;
int iLadderPoint = 0; str_to_number(iLadderPoint, row[2]); str_to_number(dwGID, row[0]);
int iLadderPoint = 0;
str_to_number(iLadderPoint, row[2]);
if (iLadderPoint != iLastLadderPoint) if (iLadderPoint != iLastLadderPoint)
++iRank; ++iRank;
@ -896,8 +911,7 @@ void CGuildManager::BootReserveWar()
const char *c_apszQuery[2] = const char *c_apszQuery[2] =
{ {
"SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=1 AND winner=-1", "SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=1 AND winner=-1",
"SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=0" "SELECT id, guild1, guild2, UNIX_TIMESTAMP(time), type, warprice, initscore, bet_from, bet_to, power1, power2, handicap FROM guild_war_reservation WHERE started=0"};
};
for (int i = 0; i < 2; ++i) for (int i = 0; i < 2; ++i)
{ {
@ -967,7 +981,8 @@ int GetAverageGuildMemberLevel(DWORD dwGID)
MYSQL_ROW row; MYSQL_ROW row;
row = mysql_fetch_row(msg->Get()->pSQLResult); row = mysql_fetch_row(msg->Get()->pSQLResult);
int nAverageLevel = 0; str_to_number(nAverageLevel, row[0]); int nAverageLevel = 0;
str_to_number(nAverageLevel, row[0]);
return nAverageLevel; return nAverageLevel;
} }
@ -982,7 +997,8 @@ int GetGuildMemberCount(DWORD dwGID)
MYSQL_ROW row; MYSQL_ROW row;
row = mysql_fetch_row(msg->Get()->pSQLResult); row = mysql_fetch_row(msg->Get()->pSQLResult);
DWORD dwCount = 0; str_to_number(dwCount, row[0]); DWORD dwCount = 0;
str_to_number(dwCount, row[0]);
return dwCount; return dwCount;
} }
@ -1488,4 +1504,3 @@ void CGuildWarReserve::End(int iScoreFrom, int iScoreTo)
break; break;
} }
} }

View File

@ -6,8 +6,6 @@
#include "ClientManager.h" #include "ClientManager.h"
DWORD g_dwLastCachedItemAwardID = 0; DWORD g_dwLastCachedItemAwardID = 0;
ItemAwardManager::ItemAwardManager() ItemAwardManager::ItemAwardManager()
{ {

View File

@ -33,6 +33,7 @@ class ItemAwardManager : public singleton<ItemAwardManager>
// gift notify // gift notify
std::map<DWORD, TItemAward *> &GetMapAward(); std::map<DWORD, TItemAward *> &GetMapAward();
std::map<std::string, std::set<TItemAward *>> &GetMapkSetAwardByLogin(); std::map<std::string, std::set<TItemAward *>> &GetMapkSetAwardByLogin();
private: private:
// ID, ItemAward pair // ID, ItemAward pair
std::map<DWORD, TItemAward *> m_map_award; std::map<DWORD, TItemAward *> m_map_award;

View File

@ -74,7 +74,8 @@ TItemIDRangeTable CItemIDRangeManager::GetRange()
FCheckCollision f(ret); FCheckCollision f(ret);
CClientManager::instance().for_each_peer(f); CClientManager::instance().for_each_peer(f);
if (f.hasCollision == false) return ret; if (f.hasCollision == false)
return ret;
} }
} }
@ -162,4 +163,3 @@ void CItemIDRangeManager::UpdateRange(DWORD dwMin, DWORD dwMax)
m_listData.push_back(range); m_listData.push_back(range);
} }
} }

View File

@ -2,7 +2,8 @@
#ifndef __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__ #ifndef __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__
#define __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__ #define __INC_METIN_II_ITEM_ID_RANGE_MANAGER_H__
namespace { namespace
{
static const uint32_t cs_dwMaxItemID = 4290000000UL; static const uint32_t cs_dwMaxItemID = 4290000000UL;
static const uint32_t cs_dwMinimumRange = 10000000UL; static const uint32_t cs_dwMinimumRange = 10000000UL;
static const uint32_t cs_dwMinimumRemainCount = 10000UL; static const uint32_t cs_dwMinimumRemainCount = 10000UL;

View File

@ -118,4 +118,3 @@ DWORD * CLoginData::GetPremiumPtr()
{ {
return &m_aiPremiumTimes[0]; return &m_aiPremiumTimes[0];
} }

View File

@ -10,7 +10,11 @@
#include "Marriage.h" #include "Marriage.h"
#include "Monarch.h" #include "Monarch.h"
#include "ItemIDRangeManager.h" #include "ItemIDRangeManager.h"
#ifndef __INTELLISENSE__
#include <version.h> #include <version.h>
#else
#include <../../../common/version.h>
#endif
#include <signal.h> #include <signal.h>
void SetTablePostfix(const char *c_pszTablePostfix); void SetTablePostfix(const char *c_pszTablePostfix);
@ -20,7 +24,6 @@ std::string g_stTablePostfix;
std::string g_stLocaleNameColumn = "name"; std::string g_stLocaleNameColumn = "name";
std::string g_stLocale = "euckr"; std::string g_stLocale = "euckr";
BOOL g_test_server = false; BOOL g_test_server = false;
// 단위 초 // 단위 초
@ -30,15 +33,10 @@ int g_iItemCacheFlushSeconds = 60*5;
// g_iLogoutSeconds 수치는 g_iPlayerCacheFlushSeconds 와 g_iItemCacheFlushSeconds 보다 길어야 한다. // g_iLogoutSeconds 수치는 g_iPlayerCacheFlushSeconds 와 g_iItemCacheFlushSeconds 보다 길어야 한다.
int g_iLogoutSeconds = 60 * 10; int g_iLogoutSeconds = 60 * 10;
// MYSHOP_PRICE_LIST // MYSHOP_PRICE_LIST
int g_iItemPriceListTableCacheFlushSeconds = 540; int g_iItemPriceListTableCacheFlushSeconds = 540;
// END_OF_MYSHOP_PRICE_LIST // END_OF_MYSHOP_PRICE_LIST
#ifdef __FreeBSD__
extern const char * _malloc_options;
#endif
void emergency_sig(int sig) void emergency_sig(int sig)
{ {
if (sig == SIGSEGV) if (sig == SIGSEGV)
@ -55,10 +53,6 @@ int main()
WriteVersion(); WriteVersion();
log_init(); log_init();
#ifdef __FreeBSD__
_malloc_options = "A";
#endif
CConfig Config; CConfig Config;
CDBManager DBManager; CDBManager DBManager;
CClientManager ClientManager; CClientManager ClientManager;
@ -82,21 +76,23 @@ int main()
signal_timer_disable(); signal_timer_disable();
DBManager.Quit(); DBManager.Quit();
int iCount;
while (true) // TODO Check if this is really needed, this should just be cleaning up queued queries
{ // int iCount;
iCount = 0;
iCount += CDBManager::instance().CountReturnQuery(SQL_PLAYER); // while (true)
iCount += CDBManager::instance().CountAsyncQuery(SQL_PLAYER); // {
// iCount = 0;
if (iCount == 0) // iCount += CDBManager::instance().CountReturnQuery(SQL_PLAYER);
break; // iCount += CDBManager::instance().CountAsyncQuery(SQL_PLAYER);
usleep(1000); // if (iCount == 0)
SPDLOG_DEBUG("WAITING_QUERY_COUNT {}", iCount); // break;
}
// usleep(1000);
// SPDLOG_DEBUG("WAITING_QUERY_COUNT {}", iCount);
// }
log_destroy(); log_destroy();
@ -185,7 +181,8 @@ int Start()
// //
if (CConfig::instance().GetValue("CACHE_FLUSH_LIMIT_PER_SECOND", szBuf, 256)) if (CConfig::instance().GetValue("CACHE_FLUSH_LIMIT_PER_SECOND", szBuf, 256))
{ {
DWORD dwVal = 0; str_to_number(dwVal, szBuf); DWORD dwVal = 0;
str_to_number(dwVal, szBuf);
CClientManager::instance().SetCacheFlushCountLimit(dwVal); CClientManager::instance().SetCacheFlushCountLimit(dwVal);
} }
@ -208,58 +205,16 @@ int Start()
int iPort; int iPort;
char line[256 + 1]; char line[256 + 1];
if (CConfig::instance().GetValue("SQL_PLAYER", line, 256)) {
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort); sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
SPDLOG_DEBUG("Connecting to MySQL server (player)"); SPDLOG_DEBUG("Connecting to PostgreSQL server");
if (!CDBManager::instance().Connect(SQL_PLAYER, szAddr, iPort, szDB, szUser, szPassword)) { if (!CDBManager::instance().Connect(szAddr, iPort, szDB, szUser, szPassword))
SPDLOG_CRITICAL("Connection to MySQL server (player) failed!");
return false;
}
SPDLOG_INFO("Connected to MySQL server (player)");
}
else {
SPDLOG_CRITICAL("SQL_PLAYER not configured");
return false;
}
if (CConfig::instance().GetValue("SQL_ACCOUNT", line, 256)) {
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
SPDLOG_DEBUG("Connecting to MySQL server (account)");
if (!CDBManager::instance().Connect(SQL_ACCOUNT, szAddr, iPort, szDB, szUser, szPassword)) {
SPDLOG_CRITICAL("Connection to MySQL server (account) failed!");
return false;
}
SPDLOG_INFO("Connected to MySQL server (account)");
}
else
{ {
SPDLOG_CRITICAL("SQL_ACCOUNT not configured"); SPDLOG_CRITICAL("Connection to PostgreSQL server failed!");
return false; return false;
} }
if (CConfig::instance().GetValue("SQL_COMMON", line, 256)) SPDLOG_INFO("Connected to PostgreSQL server");
{
sscanf(line, " %s %s %s %s %d ", szAddr, szDB, szUser, szPassword, &iPort);
SPDLOG_DEBUG("Connecting to MySQL server (common)");
if (!CDBManager::instance().Connect(SQL_COMMON, szAddr, iPort, szDB, szUser, szPassword))
{
SPDLOG_CRITICAL("Connection to MySQL server (common) failed!");
return false;
}
SPDLOG_INFO("Connected to MySQL server (common)");
}
else
{
SPDLOG_CRITICAL("SQL_COMMON not configured");
return false;
}
if (!CClientManager::instance().Initialize()) if (!CClientManager::instance().Initialize())
{ {

View File

@ -2,8 +2,6 @@
#define __INC_MAIN_H__ #define __INC_MAIN_H__
int Start(); int Start();
void End();
const char *GetTablePostfix(); const char *GetTablePostfix();
const char * GetPlayerDBName();
#endif #endif

View File

@ -52,11 +52,16 @@ namespace marriage
{ {
MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult); MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult);
DWORD pid1 = 0; str_to_number(pid1, row[0]); DWORD pid1 = 0;
DWORD pid2 = 0; str_to_number(pid2, row[1]); str_to_number(pid1, row[0]);
int love_point = 0; str_to_number(love_point, row[2]); DWORD pid2 = 0;
DWORD time = 0; str_to_number(time, row[3]); str_to_number(pid2, row[1]);
BYTE is_married = 0; str_to_number(is_married, row[4]); int love_point = 0;
str_to_number(love_point, row[2]);
DWORD time = 0;
str_to_number(time, row[3]);
BYTE is_married = 0;
str_to_number(is_married, row[4]);
const char *name1 = row[5]; const char *name1 = row[5];
const char *name2 = row[6]; const char *name2 = row[6];

View File

@ -27,11 +27,9 @@ bool CMonarch::VoteMonarch(DWORD pid, DWORD selectdpid)
p->selectedpid = selectdpid; p->selectedpid = selectdpid;
m_map_MonarchElection.insert(MAP_MONARCHELECTION::value_type(pid, p)); m_map_MonarchElection.insert(MAP_MONARCHELECTION::value_type(pid, p));
char szQuery[256]; CDBManager::instance().AsyncQuery(
snprintf(szQuery, sizeof(szQuery), "INSERT INTO player.monarch_election(player_id, selected_player_id, election_at) VALUES($1, $2, now())",
"INSERT INTO monarch_election(pid, selectedpid, electiondata) VALUES(%d, %d, now())", pid, selectdpid); pqxx::params(pid, selectdpid));
CDBManager::instance().AsyncQuery(szQuery);
return 1; return 1;
} }
@ -85,11 +83,10 @@ bool CMonarch::AddCandidacy(DWORD pid, const char * name)
strlcpy(info.name, name, sizeof(info.name)); strlcpy(info.name, name, sizeof(info.name));
m_vec_MonarchCandidacy.push_back(info); m_vec_MonarchCandidacy.push_back(info);
char szQuery[256]; CDBManager::instance().AsyncQuery(
snprintf(szQuery, sizeof(szQuery), "INSERT INTO player.monarch_candidacy(player_id, date) VALUES($1, now())",
"INSERT INTO monarch_candidacy(pid, date) VALUES(%d, now())", pid); pqxx::params(pid));
CDBManager::instance().AsyncQuery(szQuery);
return true; return true;
} }
@ -100,17 +97,17 @@ bool CMonarch::DelCandidacy(const char * name)
{ {
if (0 == strncmp(it->name, name, sizeof(it->name))) if (0 == strncmp(it->name, name, sizeof(it->name)))
{ {
char szQuery[256]; CDBManager::instance().AsyncQuery(
snprintf(szQuery, sizeof(szQuery), "DELETE FROM player.monarch_candidacy WHERE player_id=$1",
"DELETE FROM monarch_candidacy WHERE pid=%d ", it->pid); pqxx::params(it->pid));
CDBManager::instance().AsyncQuery(szQuery);
m_vec_MonarchCandidacy.erase(it); m_vec_MonarchCandidacy.erase(it);
return true; return true;
} }
} }
return false;
return false;
} }
bool CMonarch::IsMonarch(int Empire, DWORD pid) bool CMonarch::IsMonarch(int Empire, DWORD pid)
@ -129,10 +126,9 @@ bool CMonarch::AddMoney(int Empire, int64_t Money)
int64_t Money64 = m_MonarchInfo.money[Empire]; int64_t Money64 = m_MonarchInfo.money[Empire];
char szQuery[1024]; CDBManager::instance().AsyncQuery(
snprintf(szQuery, sizeof(szQuery), "UPDATE monarch set money=%ld where empire=%d", Money64, Empire); "UPDATE player.monarch SET money=$1 WHERE empire=$2",
pqxx::params(Money64, Empire));
CDBManager::instance().AsyncQuery(szQuery);
return true; return true;
} }
@ -145,10 +141,10 @@ bool CMonarch::DecMoney(int Empire, int64_t Money)
int64_t Money64 = m_MonarchInfo.money[Empire]; int64_t Money64 = m_MonarchInfo.money[Empire];
char szQuery[1024]; CDBManager::instance().AsyncQuery(
snprintf(szQuery, sizeof(szQuery), "UPDATE monarch set money=%ld where empire=%d", Money64, Empire); "UPDATE player.monarch SET money=$1 WHERE empire=$2",
pqxx::params(Money64, Empire));
CDBManager::instance().AsyncQuery(szQuery);
return true; return true;
} }
@ -162,11 +158,9 @@ bool CMonarch::TakeMoney(int Empire, DWORD pid, int64_t Money)
m_MonarchInfo.money[Empire] -= Money; m_MonarchInfo.money[Empire] -= Money;
char szQuery[1024]; CDBManager::instance().AsyncQuery(
snprintf(szQuery, sizeof(szQuery), "UPDATE player.monarch SET money=$1 WHERE empire=$2",
"UPDATE monarch set money=%ld; where empire=%d", m_MonarchInfo.money[Empire], Empire); pqxx::params(m_MonarchInfo.money[Empire], Empire));
CDBManager::instance().AsyncQuery(szQuery);
SPDLOG_TRACE("[MONARCH] Take money empire({}) money({})", Empire, m_MonarchInfo.money[Empire]); SPDLOG_TRACE("[MONARCH] Take money empire({}) money({})", Empire, m_MonarchInfo.money[Empire]);
return true; return true;
@ -175,120 +169,151 @@ bool CMonarch::TakeMoney(int Empire, DWORD pid, int64_t Money)
bool CMonarch::LoadMonarch() bool CMonarch::LoadMonarch()
{ {
MonarchInfo *p = &m_MonarchInfo; MonarchInfo *p = &m_MonarchInfo;
char szQuery[256];
snprintf(szQuery, sizeof(szQuery), "SELECT empire, pid, name, money, windate FROM monarch a, player%s b where a.pid=b.id", GetTablePostfix());
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER);
if (pMsg->Get()->uiNumRows == 0) auto pool = CDBManager::instance().GetConnectionPool();
auto conn = pool->acquire();
try
{
pqxx::work txn{*conn};
pqxx::result result = txn.exec("SELECT empire, player_id, name, money, win_at FROM player.monarch m, player.player p WHERE m.player_id=p.id");
std::size_t const numRows = std::size(result);
if (numRows == 0)
{ {
delete pMsg;
return false; return false;
} }
MYSQL_ROW row; for (auto row : result)
for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n)
{ {
int idx = 0; int Empire = row[0].as<int>();
int Empire = 0; str_to_number(Empire, row[idx++]); str_to_number(p->pid[Empire], row[1].c_str());
strlcpy(p->name[Empire], row[2].c_str(), sizeof(p->name[Empire]));
str_to_number(p->pid[Empire], row[idx++]); str_to_number(p->money[Empire], row[3].c_str());
strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire])); strlcpy(p->date[Empire], row[4].c_str(), sizeof(p->date[Empire]));
str_to_number(p->money[Empire], row[idx++]);
strlcpy(p->date[Empire], row[idx++], sizeof(p->date[Empire]));
SPDLOG_TRACE("[LOAD_MONARCH] Empire {} pid {} money {} windate {}", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]); SPDLOG_TRACE("[LOAD_MONARCH] Empire {} pid {} money {} windate {}", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]);
} }
delete pMsg; txn.commit();
}
catch (const std::exception &e)
{
SPDLOG_ERROR("[CMonarch::LoadMonarch] Query error: {}", e.what());
return false;
}
return true; return true;
} }
bool CMonarch::SetMonarch(const char *name) bool CMonarch::SetMonarch(const char *name)
{ {
MonarchInfo *p = &m_MonarchInfo; MonarchInfo *p = &m_MonarchInfo;
char szQuery[256];
snprintf(szQuery, sizeof(szQuery), "SELECT empire, pid, name FROM player a where a.name = '%s'", name); auto pool = CDBManager::instance().GetConnectionPool();
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); auto conn = pool->acquire();
if (pMsg->Get()->uiNumRows == 0) try
{
pqxx::work txn{*conn};
// TODO Look at this, this should probably not iterate and just get one value instead
pqxx::result result = txn.exec_params("SELECT empire, player_id, name, money FROM player.player WHERE name=$1", name);
std::size_t const numRows = std::size(result);
if (numRows == 0)
{ {
delete pMsg;
return false; return false;
} }
MYSQL_ROW row;
int Empire = 0; int Empire = 0;
for (int n = 0; (row = mysql_fetch_row(pMsg->Get()->pSQLResult)) != NULL; ++n) for (auto row : result)
{ {
int idx = 0; Empire = row[0].as<int>();
str_to_number(Empire, row[idx++]);
str_to_number(p->pid[Empire], row[idx++]); str_to_number(p->pid[Empire], row[1].c_str());
strlcpy(p->name[Empire], row[idx++], sizeof(p->name[Empire])); strlcpy(p->name[Empire], row[2].c_str(), sizeof(p->name[Empire]));
p->money[Empire] = atoll(row[idx++]); p->money[Empire] = atoll(row[3].c_str());
SPDLOG_TRACE("[Set_MONARCH] Empire {} pid {} money {} windate {}", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]); SPDLOG_TRACE("[Set_MONARCH] Empire {} pid {} money {} windate {}", Empire, p->pid[Empire], p->money[Empire], p->date[Empire]);
} }
delete pMsg;
//db에 입력 txn.commit();
snprintf(szQuery, sizeof(szQuery),
"REPLACE INTO monarch (empire, name, windate, money) VALUES(%d, %d, now(), %ld)", Empire, p->pid[Empire], p->money[Empire]); CDBManager::instance().AsyncQuery(
"REPLACE INTO player.monarch (empire, name, win_at, money) VALUES($1, $2, now(), $3)",
pqxx::params{Empire, p->pid[Empire], p->money[Empire]});
}
catch (const std::exception &e)
{
SPDLOG_ERROR("[CMonarch::SetMonarch] Query error: {}", e.what());
return false;
}
CDBManager::instance().AsyncQuery(szQuery, SQL_PLAYER);
return true; return true;
} }
bool CMonarch::DelMonarch(int Empire) bool CMonarch::DelMonarch(int Empire)
{ {
char szQuery[256]; auto pool = CDBManager::instance().GetConnectionPool();
auto conn = pool->acquire();
snprintf(szQuery, sizeof(szQuery), "DELETE from monarch where empire=%d", Empire); try
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); {
pqxx::work txn{*conn};
if (pMsg->Get()->uiNumRows == 0) pqxx::result result = txn.exec_params("DELETE FROM player.monarch WHERE empire=$1 RETURNING id", Empire);
std::size_t const numRows = std::size(result);
if (numRows == 0)
{ {
delete pMsg;
return false; return false;
} }
}
delete pMsg; catch (const std::exception &e)
{
SPDLOG_ERROR("[CMonarch::DelMonarch] Query error: {}", e.what());
return false;
}
memset(m_MonarchInfo.name[Empire], 0, sizeof(m_MonarchInfo.name[Empire])); memset(m_MonarchInfo.name[Empire], 0, sizeof(m_MonarchInfo.name[Empire]));
m_MonarchInfo.money[Empire] = 0; m_MonarchInfo.money[Empire] = 0;
m_MonarchInfo.pid[Empire] = 0; m_MonarchInfo.pid[Empire] = 0;
return true; return true;
} }
bool CMonarch::DelMonarch(const char *name) bool CMonarch::DelMonarch(const char *name)
{ {
for (int n = 1; n < 4; ++n) auto pool = CDBManager::instance().GetConnectionPool();
{ auto conn = pool->acquire();
if (0 == strncmp(m_MonarchInfo.name[n], name, sizeof(m_MonarchInfo.name[n])))
{
char szQuery[256];
int Empire = n; try
snprintf(szQuery, sizeof(szQuery), "DELETE from monarch where name=%d", Empire); {
SQLMsg * pMsg = CDBManager::instance().DirectQuery(szQuery, SQL_PLAYER); pqxx::work txn{*conn};
pqxx::result result = txn.exec_params("DELETE FROM player.monarch WHERE name=$1 RETURNING empire", name);
if (pMsg->Get()->uiNumRows == 0) std::size_t const numRows = std::size(result);
if (numRows == 0)
{ {
SPDLOG_ERROR(" DirectQuery failed({})", szQuery);
delete pMsg;
return false; return false;
} }
delete pMsg; for (auto row : result)
{
int Empire = row[0].as<int>();
memset(m_MonarchInfo.name[Empire], 0, 32); memset(m_MonarchInfo.name[Empire], 0, sizeof(m_MonarchInfo.name[Empire]));
m_MonarchInfo.money[Empire] = 0; m_MonarchInfo.money[Empire] = 0;
m_MonarchInfo.pid[Empire] = 0; m_MonarchInfo.pid[Empire] = 0;
return true; return true;
} }
} }
catch (const std::exception &e)
{
SPDLOG_ERROR("[CMonarch::DelMonarch(name)] Query error: {}", e.what());
return false;
}
return false; return false;
} }

View File

@ -31,24 +31,6 @@ void CMoneyLog::Save()
} }
m_MoneyLogContainer[bType].clear(); m_MoneyLogContainer[bType].clear();
} }
/*
CPeer* peer = GetPeer();
peer->
for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++)
{
//"INSERT INTO money_log%s VALUES('%s', %d, %d, %d)", CClientManager::instance().GetTablePostfix(),
typeof(m_MoneyLogContainer[bType].begin()) it;
for (it = m_MoneyLogContainer[bType].begin(); it != m_MoneyLogContainer[bType].end(); ++it)
{
typeof(it->second.begin())
}
}
for (BYTE bType = 0; bType < MONEY_LOG_TYPE_MAX_NUM; bType++)
m_MoneyLogContainer[bType].clear()
*/
} }
void CMoneyLog::AddLog(BYTE bType, DWORD dwVnum, int iGold) void CMoneyLog::AddLog(BYTE bType, DWORD dwVnum, int iGold)

View File

@ -68,7 +68,8 @@ bool CPeer::PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWO
return false; return false;
const char *buf = (const char *)GetRecvBuffer(iBytesProceed + 9); const char *buf = (const char *)GetRecvBuffer(iBytesProceed + 9);
if (!buf) { if (!buf)
{
SPDLOG_ERROR("PeekPacket: Failed to get network buffer!"); SPDLOG_ERROR("PeekPacket: Failed to get network buffer!");
return false; return false;
} }
@ -94,7 +95,8 @@ bool CPeer::PeekPacket(int & iBytesProceed, BYTE & header, DWORD & dwHandle, DWO
// Ensure that all the required data is available in a contiguous area // Ensure that all the required data is available in a contiguous area
buf = (const char *)GetRecvBuffer(iBytesProceed + dwLength + 9); buf = (const char *)GetRecvBuffer(iBytesProceed + dwLength + 9);
if (!buf) { if (!buf)
{
SPDLOG_ERROR("PeekPacket: Failed to get network buffer!"); SPDLOG_ERROR("PeekPacket: Failed to get network buffer!");
return false; return false;
} }
@ -160,7 +162,8 @@ void CPeer::SendSpareItemIDRange()
bool CPeer::SetItemIDRange(TItemIDRangeTable itemRange) bool CPeer::SetItemIDRange(TItemIDRangeTable itemRange)
{ {
if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false; if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0)
return false;
m_itemRange = itemRange; m_itemRange = itemRange;
SPDLOG_DEBUG("ItemIDRange: SET {} {} ~ {} start: {}", GetPublicIP(), m_itemRange.dwMin, m_itemRange.dwMax, m_itemRange.dwUsableItemIDMin); SPDLOG_DEBUG("ItemIDRange: SET {} {} ~ {} start: {}", GetPublicIP(), m_itemRange.dwMin, m_itemRange.dwMax, m_itemRange.dwUsableItemIDMin);
@ -170,7 +173,8 @@ bool CPeer::SetItemIDRange(TItemIDRangeTable itemRange)
bool CPeer::SetSpareItemIDRange(TItemIDRangeTable itemRange) bool CPeer::SetSpareItemIDRange(TItemIDRangeTable itemRange)
{ {
if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0) return false; if (itemRange.dwMin == 0 || itemRange.dwMax == 0 || itemRange.dwUsableItemIDMin == 0)
return false;
m_itemSpareRange = itemRange; m_itemSpareRange = itemRange;
SPDLOG_DEBUG("ItemIDRange: SPARE SET {} {} ~ {} start: {}", GetPublicIP(), m_itemSpareRange.dwMin, m_itemSpareRange.dwMax, SPDLOG_DEBUG("ItemIDRange: SPARE SET {} {} ~ {} start: {}", GetPublicIP(), m_itemSpareRange.dwMin, m_itemSpareRange.dwMax,
@ -197,5 +201,3 @@ bool CPeer::CheckItemIDRangeCollision(TItemIDRangeTable itemRange)
return true; return true;
} }

View File

@ -14,7 +14,8 @@ CPeerBase::~CPeerBase()
void CPeerBase::Destroy() void CPeerBase::Destroy()
{ {
if (m_bufferevent) { if (m_bufferevent)
{
bufferevent_free(m_bufferevent); bufferevent_free(m_bufferevent);
m_bufferevent = nullptr; m_bufferevent = nullptr;
} }
@ -22,12 +23,14 @@ void CPeerBase::Destroy()
bool CPeerBase::Accept(bufferevent *bufev, sockaddr *addr) bool CPeerBase::Accept(bufferevent *bufev, sockaddr *addr)
{ {
if (!bufev) { if (!bufev)
{
SPDLOG_ERROR("Cannot accept empty bufferevent!"); SPDLOG_ERROR("Cannot accept empty bufferevent!");
return false; return false;
} }
if (m_bufferevent != nullptr) { if (m_bufferevent != nullptr)
{
SPDLOG_ERROR("Peer is already initialized"); SPDLOG_ERROR("Peer is already initialized");
return false; return false;
} }
@ -39,7 +42,8 @@ bool CPeerBase::Accept(bufferevent* bufev, sockaddr* addr)
sockaddr_in *peer; sockaddr_in *peer;
sockaddr_in6 *peer6; sockaddr_in6 *peer6;
switch (addr->sa_family) { switch (addr->sa_family)
{
case AF_INET: case AF_INET:
peer = (sockaddr_in *)addr; peer = (sockaddr_in *)addr;
inet_ntop(AF_INET, &(peer->sin_addr), m_host, INET_ADDRSTRLEN); inet_ntop(AF_INET, &(peer->sin_addr), m_host, INET_ADDRSTRLEN);
@ -90,7 +94,8 @@ void CPeerBase::Encode(const void* data, size_t size)
return; return;
} }
if (bufferevent_write(m_bufferevent, data, size) != 0) { if (bufferevent_write(m_bufferevent, data, size) != 0)
{
SPDLOG_ERROR("Buffer write error!"); SPDLOG_ERROR("Buffer write error!");
return; return;
} }

View File

@ -3,7 +3,8 @@
#include <event2/bufferevent.h> #include <event2/bufferevent.h>
class CPeerBase { class CPeerBase
{
protected: protected:
virtual void OnAccept() = 0; virtual void OnAccept() = 0;
virtual void OnClose() = 0; virtual void OnClose() = 0;

View File

@ -39,7 +39,8 @@ void CPrivManager::Update()
// ADD_GUILD_PRIV_TIME // ADD_GUILD_PRIV_TIME
// 길드에 중복적으로 보너스가 설정되었을 경우 map 의 value 가 갱신(수정) 되었으므로 // 길드에 중복적으로 보너스가 설정되었을 경우 map 의 value 가 갱신(수정) 되었으므로
// TPrivGuildData 의 포인터가 같을때 실제로 삭제해 주고 게임서버들에게 cast 해 준다. // TPrivGuildData 의 포인터가 같을때 실제로 삭제해 주고 게임서버들에게 cast 해 준다.
if (it != m_aPrivGuild[p->type].end() && it->second == p) { if (it != m_aPrivGuild[p->type].end() && it->second == p)
{
m_aPrivGuild[p->type].erase(it); m_aPrivGuild[p->type].erase(it);
SendChangeGuildPriv(p->guild_id, p->type, 0, 0); SendChangeGuildPriv(p->guild_id, p->type, 0, 0);
// END_OF_ADD_GUILD_PRIV_TIME // END_OF_ADD_GUILD_PRIV_TIME

View File

@ -18,7 +18,8 @@ struct TPrivEmpireData
TPrivEmpireData(BYTE type, int value, BYTE empire, time_t end_time_sec) TPrivEmpireData(BYTE type, int value, BYTE empire, time_t end_time_sec)
: type(type), value(value), bRemoved(false), empire(empire), end_time_sec(end_time_sec) : type(type), value(value), bRemoved(false), empire(empire), end_time_sec(end_time_sec)
{} {
}
// END_OF_ADD_EMPIRE_PRIV_TIME // END_OF_ADD_EMPIRE_PRIV_TIME
}; };
@ -37,7 +38,8 @@ struct TPrivGuildData
TPrivGuildData(BYTE type, int value, DWORD guild_id, time_t _end_time_sec) TPrivGuildData(BYTE type, int value, DWORD guild_id, time_t _end_time_sec)
: type(type), value(value), bRemoved(false), guild_id(guild_id), end_time_sec(_end_time_sec) : type(type), value(value), bRemoved(false), guild_id(guild_id), end_time_sec(_end_time_sec)
{} {
}
// END_OF_ADD_GUILD_PRIV_TIME // END_OF_ADD_GUILD_PRIV_TIME
}; };
@ -49,7 +51,8 @@ struct TPrivCharData
DWORD pid; DWORD pid;
TPrivCharData(BYTE type, int value, DWORD pid) TPrivCharData(BYTE type, int value, DWORD pid)
: type(type), value(value), bRemoved(false), pid(pid) : type(type), value(value), bRemoved(false), pid(pid)
{} {
}
}; };
/** /**
@ -76,7 +79,6 @@ class CPrivManager : public singleton<CPrivManager>
void SendPrivOnSetup(CPeer *peer); void SendPrivOnSetup(CPeer *peer);
private: private:
// ADD_GUILD_PRIV_TIME // ADD_GUILD_PRIV_TIME
void SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec); void SendChangeGuildPriv(DWORD guild_id, BYTE type, int value, time_t end_time_sec);
// END_OF_ADD_GUILD_PRIV_TIME // END_OF_ADD_GUILD_PRIV_TIME

View File

@ -52,11 +52,10 @@ static string* StringSplit(string strOrigin, string strTok)
return strResult; // 결과return return strResult; // 결과return
} }
int get_Item_Type_Value(string inputString) int get_Item_Type_Value(string inputString)
{ {
string arType[] = {"ITEM_NONE", "ITEM_WEAPON", string arType[] = {
"ITEM_NONE", "ITEM_WEAPON",
"ITEM_ARMOR", "ITEM_USE", "ITEM_ARMOR", "ITEM_USE",
"ITEM_AUTOUSE", "ITEM_MATERIAL", "ITEM_AUTOUSE", "ITEM_MATERIAL",
"ITEM_SPECIAL", "ITEM_TOOL", "ITEM_SPECIAL", "ITEM_TOOL",
@ -81,12 +80,13 @@ int get_Item_Type_Value(string inputString)
"ITEM_BELT", // 35개 (EItemTypes 값으로 치면 34) "ITEM_BELT", // 35개 (EItemTypes 값으로 치면 34)
}; };
int retInt = -1; int retInt = -1;
// cout << "Type : " << typeStr << " -> "; // cout << "Type : " << typeStr << " -> ";
for (int j=0;j<sizeof(arType)/sizeof(arType[0]);j++) { for (int j = 0; j < sizeof(arType) / sizeof(arType[0]); j++)
{
string tempString = arType[j]; string tempString = arType[j];
if (inputString.find(tempString)!=string::npos && tempString.find(inputString)!=string::npos) { if (inputString.find(tempString) != string::npos && tempString.find(inputString) != string::npos)
{
// cout << j << " "; // cout << j << " ";
retInt = j; retInt = j;
break; break;
@ -95,7 +95,6 @@ int get_Item_Type_Value(string inputString)
// cout << endl; // cout << endl;
return retInt; return retInt;
} }
int get_Item_SubType_Value(int type_value, string inputString) int get_Item_SubType_Value(int type_value, string inputString)
@ -126,7 +125,8 @@ int get_Item_SubType_Value(int type_value, string inputString)
static string arSub29[] = {"DS_SLOT1", "DS_SLOT2", "DS_SLOT3", "DS_SLOT4", "DS_SLOT5", "DS_SLOT6"}; static string arSub29[] = {"DS_SLOT1", "DS_SLOT2", "DS_SLOT3", "DS_SLOT4", "DS_SLOT5", "DS_SLOT6"};
static string arSub31[] = {"EXTRACT_DRAGON_SOUL", "EXTRACT_DRAGON_HEART"}; static string arSub31[] = {"EXTRACT_DRAGON_SOUL", "EXTRACT_DRAGON_HEART"};
static string* arSubType[] = {0, //0 static string *arSubType[] = {
0, // 0
arSub1, // 1 arSub1, // 1
arSub2, // 2 arSub2, // 2
arSub3, // 3 arSub3, // 3
@ -200,7 +200,6 @@ int get_Item_SubType_Value(int type_value, string inputString)
0, // 34 벨트 0, // 34 벨트
}; };
assert(_countof(arSubType) > type_value && "Subtype rule: Out of range!!"); assert(_countof(arSubType) > type_value && "Subtype rule: Out of range!!");
// assert 안 먹히는 듯.. // assert 안 먹히는 듯..
@ -211,14 +210,16 @@ int get_Item_SubType_Value(int type_value, string inputString)
} }
// 아이템 타입의 서브타입 어레이가 존재하는지 알아보고, 없으면 0 리턴 // 아이템 타입의 서브타입 어레이가 존재하는지 알아보고, 없으면 0 리턴
if (arSubType[type_value]==0) { if (arSubType[type_value] == 0)
{
return 0; return 0;
} }
// //
int retInt = -1; int retInt = -1;
// cout << "SubType : " << subTypeStr << " -> "; // cout << "SubType : " << subTypeStr << " -> ";
for (int j=0;j<arNumberOfSubtype[type_value];j++) { for (int j = 0; j < arNumberOfSubtype[type_value]; j++)
{
string tempString = arSubType[type_value][j]; string tempString = arSubType[type_value][j];
string tempInputString = trim(inputString); string tempInputString = trim(inputString);
if (tempInputString.compare(tempString) == 0) if (tempInputString.compare(tempString) == 0)
@ -233,10 +234,6 @@ int get_Item_SubType_Value(int type_value, string inputString)
return retInt; return retInt;
} }
int get_Item_AntiFlag_Value(string inputString) int get_Item_AntiFlag_Value(string inputString)
{ {
@ -244,15 +241,16 @@ int get_Item_AntiFlag_Value(string inputString)
"ANTI_GET", "ANTI_DROP", "ANTI_SELL", "ANTI_EMPIRE_A", "ANTI_EMPIRE_B", "ANTI_EMPIRE_C", "ANTI_GET", "ANTI_DROP", "ANTI_SELL", "ANTI_EMPIRE_A", "ANTI_EMPIRE_B", "ANTI_EMPIRE_C",
"ANTI_SAVE", "ANTI_GIVE", "ANTI_PKDROP", "ANTI_STACK", "ANTI_MYSHOP", "ANTI_SAFEBOX"}; "ANTI_SAVE", "ANTI_GIVE", "ANTI_PKDROP", "ANTI_STACK", "ANTI_MYSHOP", "ANTI_SAFEBOX"};
int retValue = 0; int retValue = 0;
string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열. string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arAntiFlag)/sizeof(arAntiFlag[0]);i++) { for (int i = 0; i < sizeof(arAntiFlag) / sizeof(arAntiFlag[0]); i++)
{
string tempString = arAntiFlag[i]; string tempString = arAntiFlag[i];
for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩) for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩)
{ {
string tempString2 = arInputString[j]; string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인. if (tempString2.compare(tempString) == 0)
{ // 일치하는지 확인.
retValue = retValue + pow((float)2, (float)i); retValue = retValue + pow((float)2, (float)i);
} }
@ -273,15 +271,16 @@ int get_Item_Flag_Value(string inputString)
"ITEM_MAKECOUNT", "ITEM_IRREMOVABLE", "CONFIRM_WHEN_USE", "QUEST_USE", "QUEST_USE_MULTIPLE", "ITEM_MAKECOUNT", "ITEM_IRREMOVABLE", "CONFIRM_WHEN_USE", "QUEST_USE", "QUEST_USE_MULTIPLE",
"QUEST_GIVE", "ITEM_QUEST", "LOG", "STACKABLE", "SLOW_QUERY", "REFINEABLE", "IRREMOVABLE", "ITEM_APPLICABLE"}; "QUEST_GIVE", "ITEM_QUEST", "LOG", "STACKABLE", "SLOW_QUERY", "REFINEABLE", "IRREMOVABLE", "ITEM_APPLICABLE"};
int retValue = 0; int retValue = 0;
string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열. string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arFlag)/sizeof(arFlag[0]);i++) { for (int i = 0; i < sizeof(arFlag) / sizeof(arFlag[0]); i++)
{
string tempString = arFlag[i]; string tempString = arFlag[i];
for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩) for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩)
{ {
string tempString2 = arInputString[j]; string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인. if (tempString2.compare(tempString) == 0)
{ // 일치하는지 확인.
retValue = retValue + pow((float)2, (float)i); retValue = retValue + pow((float)2, (float)i);
} }
@ -301,15 +300,16 @@ int get_Item_WearFlag_Value(string inputString)
string arWearrFlag[] = {"WEAR_BODY", "WEAR_HEAD", "WEAR_FOOTS", "WEAR_WRIST", "WEAR_WEAPON", "WEAR_NECK", "WEAR_EAR", "WEAR_SHIELD", "WEAR_UNIQUE", string arWearrFlag[] = {"WEAR_BODY", "WEAR_HEAD", "WEAR_FOOTS", "WEAR_WRIST", "WEAR_WEAPON", "WEAR_NECK", "WEAR_EAR", "WEAR_SHIELD", "WEAR_UNIQUE",
"WEAR_ARROW", "WEAR_HAIR", "WEAR_ABILITY"}; "WEAR_ARROW", "WEAR_HAIR", "WEAR_ABILITY"};
int retValue = 0; int retValue = 0;
string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열. string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arWearrFlag)/sizeof(arWearrFlag[0]);i++) { for (int i = 0; i < sizeof(arWearrFlag) / sizeof(arWearrFlag[0]); i++)
{
string tempString = arWearrFlag[i]; string tempString = arWearrFlag[i];
for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩) for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩)
{ {
string tempString2 = arInputString[j]; string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인. if (tempString2.compare(tempString) == 0)
{ // 일치하는지 확인.
retValue = retValue + pow((float)2, (float)i); retValue = retValue + pow((float)2, (float)i);
} }
@ -330,12 +330,14 @@ int get_Item_Immune_Value(string inputString)
int retValue = 0; int retValue = 0;
string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열. string *arInputString = StringSplit(inputString, "|"); // 프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arImmune)/sizeof(arImmune[0]);i++) { for (int i = 0; i < sizeof(arImmune) / sizeof(arImmune[0]); i++)
{
string tempString = arImmune[i]; string tempString = arImmune[i];
for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩) for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩)
{ {
string tempString2 = arInputString[j]; string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인. if (tempString2.compare(tempString) == 0)
{ // 일치하는지 확인.
retValue = retValue + pow((float)2, (float)i); retValue = retValue + pow((float)2, (float)i);
} }
@ -349,16 +351,14 @@ int get_Item_Immune_Value(string inputString)
return retValue; return retValue;
} }
int get_Item_LimitType_Value(string inputString) int get_Item_LimitType_Value(string inputString)
{ {
string arLimitType[] = {"LIMIT_NONE", "LEVEL", "STR", "DEX", "INT", "CON", "PC_BANG", "REAL_TIME", "REAL_TIME_FIRST_USE", "TIMER_BASED_ON_WEAR"}; string arLimitType[] = {"LIMIT_NONE", "LEVEL", "STR", "DEX", "INT", "CON", "PC_BANG", "REAL_TIME", "REAL_TIME_FIRST_USE", "TIMER_BASED_ON_WEAR"};
int retInt = -1; int retInt = -1;
// cout << "LimitType : " << limitTypeStr << " -> "; // cout << "LimitType : " << limitTypeStr << " -> ";
for (int j=0;j<sizeof(arLimitType)/sizeof(arLimitType[0]);j++) { for (int j = 0; j < sizeof(arLimitType) / sizeof(arLimitType[0]); j++)
{
string tempString = arLimitType[j]; string tempString = arLimitType[j];
string tempInputString = trim(inputString); string tempInputString = trim(inputString);
if (tempInputString.compare(tempString) == 0) if (tempInputString.compare(tempString) == 0)
@ -373,31 +373,107 @@ int get_Item_LimitType_Value(string inputString)
return retInt; return retInt;
} }
int get_Item_ApplyType_Value(string inputString) int get_Item_ApplyType_Value(string inputString)
{ {
string arApplyType[] = {"APPLY_NONE", "APPLY_MAX_HP", "APPLY_MAX_SP", "APPLY_CON", "APPLY_INT", "APPLY_STR", "APPLY_DEX", "APPLY_ATT_SPEED", string arApplyType[] = {
"APPLY_MOV_SPEED", "APPLY_CAST_SPEED", "APPLY_HP_REGEN", "APPLY_SP_REGEN", "APPLY_POISON_PCT", "APPLY_STUN_PCT", "APPLY_NONE",
"APPLY_SLOW_PCT", "APPLY_CRITICAL_PCT", "APPLY_PENETRATE_PCT", "APPLY_ATTBONUS_HUMAN", "APPLY_ATTBONUS_ANIMAL", "APPLY_MAX_HP",
"APPLY_ATTBONUS_ORC", "APPLY_ATTBONUS_MILGYO", "APPLY_ATTBONUS_UNDEAD", "APPLY_ATTBONUS_DEVIL", "APPLY_STEAL_HP", "APPLY_MAX_SP",
"APPLY_STEAL_SP", "APPLY_MANA_BURN_PCT", "APPLY_DAMAGE_SP_RECOVER", "APPLY_BLOCK", "APPLY_DODGE", "APPLY_RESIST_SWORD", "APPLY_CON",
"APPLY_RESIST_TWOHAND", "APPLY_RESIST_DAGGER", "APPLY_RESIST_BELL", "APPLY_RESIST_FAN", "APPLY_RESIST_BOW", "APPLY_RESIST_FIRE", "APPLY_INT",
"APPLY_RESIST_ELEC", "APPLY_RESIST_MAGIC", "APPLY_RESIST_WIND", "APPLY_REFLECT_MELEE", "APPLY_REFLECT_CURSE", "APPLY_POISON_REDUCE", "APPLY_STR",
"APPLY_KILL_SP_RECOVER", "APPLY_EXP_DOUBLE_BONUS", "APPLY_GOLD_DOUBLE_BONUS", "APPLY_ITEM_DROP_BONUS", "APPLY_POTION_BONUS", "APPLY_DEX",
"APPLY_KILL_HP_RECOVER", "APPLY_IMMUNE_STUN", "APPLY_IMMUNE_SLOW", "APPLY_IMMUNE_FALL", "APPLY_SKILL", "APPLY_BOW_DISTANCE", "APPLY_ATT_SPEED",
"APPLY_ATT_GRADE_BONUS", "APPLY_DEF_GRADE_BONUS", "APPLY_MAGIC_ATT_GRADE", "APPLY_MAGIC_DEF_GRADE", "APPLY_CURSE_PCT", "APPLY_MOV_SPEED",
"APPLY_MAX_STAMINA", "APPLY_ATTBONUS_WARRIOR", "APPLY_ATTBONUS_ASSASSIN", "APPLY_ATTBONUS_SURA", "APPLY_ATTBONUS_SHAMAN", "APPLY_CAST_SPEED",
"APPLY_ATTBONUS_MONSTER", "APPLY_MALL_ATTBONUS", "APPLY_MALL_DEFBONUS", "APPLY_MALL_EXPBONUS", "APPLY_MALL_ITEMBONUS", "APPLY_HP_REGEN",
"APPLY_MALL_GOLDBONUS", "APPLY_MAX_HP_PCT", "APPLY_MAX_SP_PCT", "APPLY_SKILL_DAMAGE_BONUS", "APPLY_NORMAL_HIT_DAMAGE_BONUS", "APPLY_SP_REGEN",
"APPLY_SKILL_DEFEND_BONUS", "APPLY_NORMAL_HIT_DEFEND_BONUS", "APPLY_PC_BANG_EXP_BONUS", "APPLY_PC_BANG_DROP_BONUS", "APPLY_POISON_PCT",
"APPLY_EXTRACT_HP_PCT", "APPLY_RESIST_WARRIOR", "APPLY_RESIST_ASSASSIN", "APPLY_RESIST_SURA", "APPLY_RESIST_SHAMAN", "APPLY_STUN_PCT",
"APPLY_ENERGY", "APPLY_DEF_GRADE", "APPLY_COSTUME_ATTR_BONUS", "APPLY_MAGIC_ATTBONUS_PER", "APPLY_MELEE_MAGIC_ATTBONUS_PER", "APPLY_SLOW_PCT",
"APPLY_RESIST_ICE", "APPLY_RESIST_EARTH", "APPLY_RESIST_DARK", "APPLY_ANTI_CRITICAL_PCT", "APPLY_ANTI_PENETRATE_PCT", "APPLY_CRITICAL_PCT",
"APPLY_PENETRATE_PCT",
"APPLY_ATTBONUS_HUMAN",
"APPLY_ATTBONUS_ANIMAL",
"APPLY_ATTBONUS_ORC",
"APPLY_ATTBONUS_MILGYO",
"APPLY_ATTBONUS_UNDEAD",
"APPLY_ATTBONUS_DEVIL",
"APPLY_STEAL_HP",
"APPLY_STEAL_SP",
"APPLY_MANA_BURN_PCT",
"APPLY_DAMAGE_SP_RECOVER",
"APPLY_BLOCK",
"APPLY_DODGE",
"APPLY_RESIST_SWORD",
"APPLY_RESIST_TWOHAND",
"APPLY_RESIST_DAGGER",
"APPLY_RESIST_BELL",
"APPLY_RESIST_FAN",
"APPLY_RESIST_BOW",
"APPLY_RESIST_FIRE",
"APPLY_RESIST_ELEC",
"APPLY_RESIST_MAGIC",
"APPLY_RESIST_WIND",
"APPLY_REFLECT_MELEE",
"APPLY_REFLECT_CURSE",
"APPLY_POISON_REDUCE",
"APPLY_KILL_SP_RECOVER",
"APPLY_EXP_DOUBLE_BONUS",
"APPLY_GOLD_DOUBLE_BONUS",
"APPLY_ITEM_DROP_BONUS",
"APPLY_POTION_BONUS",
"APPLY_KILL_HP_RECOVER",
"APPLY_IMMUNE_STUN",
"APPLY_IMMUNE_SLOW",
"APPLY_IMMUNE_FALL",
"APPLY_SKILL",
"APPLY_BOW_DISTANCE",
"APPLY_ATT_GRADE_BONUS",
"APPLY_DEF_GRADE_BONUS",
"APPLY_MAGIC_ATT_GRADE",
"APPLY_MAGIC_DEF_GRADE",
"APPLY_CURSE_PCT",
"APPLY_MAX_STAMINA",
"APPLY_ATTBONUS_WARRIOR",
"APPLY_ATTBONUS_ASSASSIN",
"APPLY_ATTBONUS_SURA",
"APPLY_ATTBONUS_SHAMAN",
"APPLY_ATTBONUS_MONSTER",
"APPLY_MALL_ATTBONUS",
"APPLY_MALL_DEFBONUS",
"APPLY_MALL_EXPBONUS",
"APPLY_MALL_ITEMBONUS",
"APPLY_MALL_GOLDBONUS",
"APPLY_MAX_HP_PCT",
"APPLY_MAX_SP_PCT",
"APPLY_SKILL_DAMAGE_BONUS",
"APPLY_NORMAL_HIT_DAMAGE_BONUS",
"APPLY_SKILL_DEFEND_BONUS",
"APPLY_NORMAL_HIT_DEFEND_BONUS",
"APPLY_PC_BANG_EXP_BONUS",
"APPLY_PC_BANG_DROP_BONUS",
"APPLY_EXTRACT_HP_PCT",
"APPLY_RESIST_WARRIOR",
"APPLY_RESIST_ASSASSIN",
"APPLY_RESIST_SURA",
"APPLY_RESIST_SHAMAN",
"APPLY_ENERGY",
"APPLY_DEF_GRADE",
"APPLY_COSTUME_ATTR_BONUS",
"APPLY_MAGIC_ATTBONUS_PER",
"APPLY_MELEE_MAGIC_ATTBONUS_PER",
"APPLY_RESIST_ICE",
"APPLY_RESIST_EARTH",
"APPLY_RESIST_DARK",
"APPLY_ANTI_CRITICAL_PCT",
"APPLY_ANTI_PENETRATE_PCT",
}; };
int retInt = -1; int retInt = -1;
// cout << "ApplyType : " << applyTypeStr << " -> "; // cout << "ApplyType : " << applyTypeStr << " -> ";
for (int j=0;j<sizeof(arApplyType)/sizeof(arApplyType[0]);j++) { for (int j = 0; j < sizeof(arApplyType) / sizeof(arApplyType[0]); j++)
{
string tempString = arApplyType[j]; string tempString = arApplyType[j];
string tempInputString = trim(inputString); string tempInputString = trim(inputString);
if (tempInputString.compare(tempString) == 0) if (tempInputString.compare(tempString) == 0)
@ -410,20 +486,18 @@ int get_Item_ApplyType_Value(string inputString)
// cout << endl; // cout << endl;
return retInt; return retInt;
} }
// 몬스터 프로토도 읽는다. // 몬스터 프로토도 읽는다.
int get_Mob_Rank_Value(string inputString) int get_Mob_Rank_Value(string inputString)
{ {
string arRank[] = {"PAWN", "S_PAWN", "KNIGHT", "S_KNIGHT", "BOSS", "KING"}; string arRank[] = {"PAWN", "S_PAWN", "KNIGHT", "S_KNIGHT", "BOSS", "KING"};
int retInt = -1; int retInt = -1;
// cout << "Rank : " << rankStr << " -> "; // cout << "Rank : " << rankStr << " -> ";
for (int j=0;j<sizeof(arRank)/sizeof(arRank[0]);j++) { for (int j = 0; j < sizeof(arRank) / sizeof(arRank[0]); j++)
{
string tempString = arRank[j]; string tempString = arRank[j];
string tempInputString = trim(inputString); string tempInputString = trim(inputString);
if (tempInputString.compare(tempString) == 0) if (tempInputString.compare(tempString) == 0)
@ -438,14 +512,14 @@ int get_Mob_Rank_Value(string inputString)
return retInt; return retInt;
} }
int get_Mob_Type_Value(string inputString) int get_Mob_Type_Value(string inputString)
{ {
string arType[] = {"MONSTER", "NPC", "STONE", "WARP", "DOOR", "BUILDING", "PC", "POLYMORPH_PC", "HORSE", "GOTO"}; string arType[] = {"MONSTER", "NPC", "STONE", "WARP", "DOOR", "BUILDING", "PC", "POLYMORPH_PC", "HORSE", "GOTO"};
int retInt = -1; int retInt = -1;
// cout << "Type : " << typeStr << " -> "; // cout << "Type : " << typeStr << " -> ";
for (int j=0;j<sizeof(arType)/sizeof(arType[0]);j++) { for (int j = 0; j < sizeof(arType) / sizeof(arType[0]); j++)
{
string tempString = arType[j]; string tempString = arType[j];
string tempInputString = trim(inputString); string tempInputString = trim(inputString);
if (tempInputString.compare(tempString) == 0) if (tempInputString.compare(tempString) == 0)
@ -466,7 +540,8 @@ int get_Mob_BattleType_Value(string inputString)
int retInt = -1; int retInt = -1;
// cout << "Battle Type : " << battleTypeStr << " -> "; // cout << "Battle Type : " << battleTypeStr << " -> ";
for (int j=0;j<sizeof(arBattleType)/sizeof(arBattleType[0]);j++) { for (int j = 0; j < sizeof(arBattleType) / sizeof(arBattleType[0]); j++)
{
string tempString = arBattleType[j]; string tempString = arBattleType[j];
string tempInputString = trim(inputString); string tempInputString = trim(inputString);
if (tempInputString.compare(tempString) == 0) if (tempInputString.compare(tempString) == 0)
@ -487,7 +562,8 @@ int get_Mob_Size_Value(string inputString)
int retInt = 0; int retInt = 0;
// cout << "Size : " << sizeStr << " -> "; // cout << "Size : " << sizeStr << " -> ";
for (int j=0;j<sizeof(arSize)/sizeof(arSize[0]);j++) { for (int j = 0; j < sizeof(arSize) / sizeof(arSize[0]); j++)
{
string tempString = arSize[j]; string tempString = arSize[j];
string tempInputString = trim(inputString); string tempInputString = trim(inputString);
if (tempInputString.compare(tempString) == 0) if (tempInputString.compare(tempString) == 0)
@ -506,15 +582,16 @@ int get_Mob_AIFlag_Value(string inputString)
{ {
string arAIFlag[] = {"AGGR", "NOMOVE", "COWARD", "NOATTSHINSU", "NOATTCHUNJO", "NOATTJINNO", "ATTMOB", "BERSERK", "STONESKIN", "GODSPEED", "DEATHBLOW", "REVIVE"}; string arAIFlag[] = {"AGGR", "NOMOVE", "COWARD", "NOATTSHINSU", "NOATTCHUNJO", "NOATTJINNO", "ATTMOB", "BERSERK", "STONESKIN", "GODSPEED", "DEATHBLOW", "REVIVE"};
int retValue = 0; int retValue = 0;
string *arInputString = StringSplit(inputString, ","); // 프로토 정보 내용을 단어별로 쪼갠 배열. string *arInputString = StringSplit(inputString, ","); // 프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arAIFlag)/sizeof(arAIFlag[0]);i++) { for (int i = 0; i < sizeof(arAIFlag) / sizeof(arAIFlag[0]); i++)
{
string tempString = arAIFlag[i]; string tempString = arAIFlag[i];
for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩) for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩)
{ {
string tempString2 = arInputString[j]; string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인. if (tempString2.compare(tempString) == 0)
{ // 일치하는지 확인.
retValue = retValue + pow((float)2, (float)i); retValue = retValue + pow((float)2, (float)i);
} }
@ -534,12 +611,14 @@ int get_Mob_RaceFlag_Value(string inputString)
int retValue = 0; int retValue = 0;
string *arInputString = StringSplit(inputString, ","); // 프로토 정보 내용을 단어별로 쪼갠 배열. string *arInputString = StringSplit(inputString, ","); // 프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arRaceFlag)/sizeof(arRaceFlag[0]);i++) { for (int i = 0; i < sizeof(arRaceFlag) / sizeof(arRaceFlag[0]); i++)
{
string tempString = arRaceFlag[i]; string tempString = arRaceFlag[i];
for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩) for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩)
{ {
string tempString2 = arInputString[j]; string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인. if (tempString2.compare(tempString) == 0)
{ // 일치하는지 확인.
retValue = retValue + pow((float)2, (float)i); retValue = retValue + pow((float)2, (float)i);
} }
@ -558,12 +637,14 @@ int get_Mob_ImmuneFlag_Value(string inputString)
int retValue = 0; int retValue = 0;
string *arInputString = StringSplit(inputString, ","); // 프로토 정보 내용을 단어별로 쪼갠 배열. string *arInputString = StringSplit(inputString, ","); // 프로토 정보 내용을 단어별로 쪼갠 배열.
for(int i =0;i<sizeof(arImmuneFlag)/sizeof(arImmuneFlag[0]);i++) { for (int i = 0; i < sizeof(arImmuneFlag) / sizeof(arImmuneFlag[0]); i++)
{
string tempString = arImmuneFlag[i]; string tempString = arImmuneFlag[i];
for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩) for (int j = 0; j < 30; j++) // 최대 30개 단어까지. (하드코딩)
{ {
string tempString2 = arInputString[j]; string tempString2 = arInputString[j];
if (tempString2.compare(tempString)==0) { //일치하는지 확인. if (tempString2.compare(tempString) == 0)
{ // 일치하는지 확인.
retValue = retValue + pow((float)2, (float)i); retValue = retValue + pow((float)2, (float)i);
} }
@ -574,11 +655,9 @@ int get_Mob_ImmuneFlag_Value(string inputString)
delete[] arInputString; delete[] arInputString;
// cout << "Immune Flag : " << immuneFlagStr << " -> " << retValue << endl; // cout << "Immune Flag : " << immuneFlagStr << " -> " << retValue << endl;
return retValue; return retValue;
} }
#ifndef __DUMP_PROTO__ #ifndef __DUMP_PROTO__
// 몹 테이블을 셋팅해준다. // 몹 테이블을 셋팅해준다.
@ -591,10 +670,13 @@ bool Set_Proto_Mob_Table(TMobTable *mobTable, cCsvTable &csvTable,std::map<int,c
// 3. 지역별 이름 넣어주기. // 3. 지역별 이름 넣어주기.
map<int, const char *>::iterator it; map<int, const char *>::iterator it;
it = nameMap.find(mobTable->dwVnum); it = nameMap.find(mobTable->dwVnum);
if (it != nameMap.end()) { if (it != nameMap.end())
{
const char *localeName = it->second; const char *localeName = it->second;
strlcpy(mobTable->szLocaleName, localeName, sizeof(mobTable->szLocaleName)); strlcpy(mobTable->szLocaleName, localeName, sizeof(mobTable->szLocaleName));
} else { }
else
{
strlcpy(mobTable->szLocaleName, mobTable->szName, sizeof(mobTable->szLocaleName)); strlcpy(mobTable->szLocaleName, mobTable->szName, sizeof(mobTable->szLocaleName));
} }
@ -693,42 +775,66 @@ bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map<in
int col = 0; int col = 0;
int dataArray[33]; int dataArray[33];
for (int i=0; i<sizeof(dataArray)/sizeof(dataArray[0]);i++) { for (int i = 0; i < sizeof(dataArray) / sizeof(dataArray[0]); i++)
{
int validCheck = 0; int validCheck = 0;
if (i==2) { if (i == 2)
{
dataArray[i] = get_Item_Type_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_Type_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==3) { }
else if (i == 3)
{
dataArray[i] = get_Item_SubType_Value(dataArray[i - 1], csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_SubType_Value(dataArray[i - 1], csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==5) { }
else if (i == 5)
{
dataArray[i] = get_Item_AntiFlag_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_AntiFlag_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==6) { }
else if (i == 6)
{
dataArray[i] = get_Item_Flag_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_Flag_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==7) { }
else if (i == 7)
{
dataArray[i] = get_Item_WearFlag_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_WearFlag_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==8) { }
else if (i == 8)
{
dataArray[i] = get_Item_Immune_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_Immune_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==14) { }
else if (i == 14)
{
dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==16) { }
else if (i == 16)
{
dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_LimitType_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==18) { }
else if (i == 18)
{
dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==20) { }
else if (i == 20)
{
dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else if (i==22) { }
else if (i == 22)
{
dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col)); dataArray[i] = get_Item_ApplyType_Value(csvTable.AsStringByIndex(col));
validCheck = dataArray[i]; validCheck = dataArray[i];
} else { }
else
{
str_to_number(dataArray[i], csvTable.AsStringByIndex(col)); str_to_number(dataArray[i], csvTable.AsStringByIndex(col));
} }
@ -780,10 +886,13 @@ bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map<in
// 지역별 이름 넣어주기. // 지역별 이름 넣어주기.
map<int, const char *>::iterator it; map<int, const char *>::iterator it;
it = nameMap.find(itemTable->dwVnum); it = nameMap.find(itemTable->dwVnum);
if (it != nameMap.end()) { if (it != nameMap.end())
{
const char *localeName = it->second; const char *localeName = it->second;
strlcpy(itemTable->szLocaleName, localeName, sizeof(itemTable->szLocaleName)); strlcpy(itemTable->szLocaleName, localeName, sizeof(itemTable->szLocaleName));
} else { }
else
{
strlcpy(itemTable->szLocaleName, itemTable->szName, sizeof(itemTable->szLocaleName)); strlcpy(itemTable->szLocaleName, itemTable->szName, sizeof(itemTable->szLocaleName));
} }
itemTable->bType = dataArray[2]; itemTable->bType = dataArray[2];
@ -813,7 +922,6 @@ bool Set_Proto_Item_Table(TItemTable *itemTable, cCsvTable &csvTable,std::map<in
if (LIMIT_TIMER_BASED_ON_WEAR == itemTable->aLimits[i].bType) if (LIMIT_TIMER_BASED_ON_WEAR == itemTable->aLimits[i].bType)
itemTable->cLimitTimerBasedOnWearIndex = (char)i; itemTable->cLimitTimerBasedOnWearIndex = (char)i;
} }
for (i = 0; i < ITEM_APPLY_MAX_NUM; ++i) for (i = 0; i < ITEM_APPLY_MAX_NUM; ++i)

View File

@ -18,7 +18,6 @@ int get_Item_Immune_Value(std::string inputString);
int get_Item_LimitType_Value(std::string inputString); int get_Item_LimitType_Value(std::string inputString);
int get_Item_ApplyType_Value(std::string inputString); int get_Item_ApplyType_Value(std::string inputString);
// 몬스터 프로토도 읽을 수 있다. // 몬스터 프로토도 읽을 수 있다.
int get_Mob_Rank_Value(std::string inputString); int get_Mob_Rank_Value(std::string inputString);
int get_Mob_Type_Value(std::string inputString); int get_Mob_Type_Value(std::string inputString);

View File

@ -126,4 +126,3 @@ unsigned int CGrid::GetSize()
{ {
return m_iWidth * m_iHeight; return m_iWidth * m_iHeight;
} }

View File

@ -1,8 +1,12 @@
#include <fmt/core.h> #include <fmt/core.h>
#ifndef __INTELLISENSE__
#include <version.h> #include <version.h>
#else
#include <../../../common/version.h>
#endif
void WriteVersion() { void WriteVersion()
{
fmt::println("Metin2 DB Cache version {} (rev. {}, date: {})", __COMMIT_TAG__, __REVISION__, __COMMIT_DATE__); fmt::println("Metin2 DB Cache version {} (rev. {}, date: {})", __COMMIT_TAG__, __REVISION__, __COMMIT_DATE__);
fmt::println("OS: {}, target arch: {}, compiler: {}", __OS_NAME__, __CPU_TARGET__, __COMPILER__); fmt::println("OS: {}, target arch: {}, compiler: {}", __OS_NAME__, __CPU_TARGET__, __COMPILER__);
} }

View File

@ -56,7 +56,11 @@
#include "skill_power.h" #include "skill_power.h"
#include "SpeedServer.h" #include "SpeedServer.h"
#include "DragonSoul.h" #include "DragonSoul.h"
#ifndef __INTELLISENSE__
#include <version.h> #include <version.h>
#else
#include <../../../common/version.h>
#endif
#include <event2/event.h> #include <event2/event.h>
#include <event2/listener.h> #include <event2/listener.h>
#include <event2/dns.h> #include <event2/dns.h>
@ -65,20 +69,6 @@
#include <execinfo.h> #include <execinfo.h>
#endif #endif
//extern const char * _malloc_options;
#if defined(__FreeBSD__) && defined(DEBUG_ALLOC)
extern void (*_malloc_message)(const char* p1, const char* p2, const char* p3, const char* p4);
// FreeBSD _malloc_message replacement
void WriteMallocMessage(const char* p1, const char* p2, const char* p3, const char* p4) {
FILE* fp = ::fopen(DBGALLOC_LOG_FILENAME, "a");
if (fp == NULL) {
return;
}
::fprintf(fp, "%s %s %s %s\n", p1, p2, p3, p4);
::fclose(fp);
}
#endif
// TRAFFIC_PROFILER // TRAFFIC_PROFILER
static const DWORD TRAFFIC_PROFILE_FLUSH_CYCLE = 3600; ///< TrafficProfiler 의 Flush cycle. 1시간 간격 static const DWORD TRAFFIC_PROFILE_FLUSH_CYCLE = 3600; ///< TrafficProfiler 의 Flush cycle. 1시간 간격
// END_OF_TRAFFIC_PROFILER // END_OF_TRAFFIC_PROFILER
@ -137,7 +127,8 @@ void ContinueOnFatalError()
std::ostringstream oss; std::ostringstream oss;
oss << std::endl; oss << std::endl;
for (std::size_t i = 0; i < size; ++i) { for (std::size_t i = 0; i < size; ++i)
{
oss << " Stack> " << symbols[i] << std::endl; oss << " Stack> " << symbols[i] << std::endl;
} }
@ -305,7 +296,8 @@ void heartbeat(LPHEART ht, int pulse)
} }
} }
static void CleanUpForEarlyExit() { static void CleanUpForEarlyExit()
{
CancelReloadSpamEvent(); CancelReloadSpamEvent();
} }
@ -368,14 +360,16 @@ int main(int argc, char **argv)
CSpeedServerManager SSManager; CSpeedServerManager SSManager;
DSManager dsManager; DSManager dsManager;
if (!start(argc, argv)) { if (!start(argc, argv))
{
CleanUpForEarlyExit(); CleanUpForEarlyExit();
return 0; return 0;
} }
quest::CQuestManager quest_manager; quest::CQuestManager quest_manager;
if (!quest_manager.Initialize()) { if (!quest_manager.Initialize())
{
CleanUpForEarlyExit(); CleanUpForEarlyExit();
return 0; return 0;
} }
@ -394,7 +388,8 @@ int main(int argc, char **argv)
if (g_bTrafficProfileOn) if (g_bTrafficProfileOn)
TrafficProfiler::instance().Initialize(TRAFFIC_PROFILE_FLUSH_CYCLE, "ProfileLog"); TrafficProfiler::instance().Initialize(TRAFFIC_PROFILE_FLUSH_CYCLE, "ProfileLog");
while (idle()); while (idle())
;
SPDLOG_INFO("<shutdown> Starting..."); SPDLOG_INFO("<shutdown> Starting...");
g_bShutdown = true; g_bShutdown = true;
@ -478,11 +473,6 @@ int start(int argc, char **argv)
{ {
std::string st_localeServiceName; std::string st_localeServiceName;
//_malloc_options = "A";
#if defined(__FreeBSD__) && defined(DEBUG_ALLOC)
_malloc_message = WriteMallocMessage;
#endif
int ch; int ch;
while ((ch = getopt(argc, argv, "n:p:erl:tI:")) != -1) while ((ch = getopt(argc, argv, "n:p:erl:tI:")) != -1)
{ {
@ -574,24 +564,28 @@ int start(int argc, char **argv)
// Initialize the network stack // Initialize the network stack
// Check if the public and internal IP addresses were configured // Check if the public and internal IP addresses were configured
if (g_szInternalIP.empty()) { if (g_szInternalIP.empty())
{
SPDLOG_CRITICAL("Internal IP address could not be automatically detected. Manually set the IP and try again."); SPDLOG_CRITICAL("Internal IP address could not be automatically detected. Manually set the IP and try again.");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (g_szPublicIP.empty()) { if (g_szPublicIP.empty())
{
SPDLOG_CRITICAL("Public IP address could not be automatically detected. Manually set the IP and try again."); SPDLOG_CRITICAL("Public IP address could not be automatically detected. Manually set the IP and try again.");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Create a new libevent base and listen for new connections // Create a new libevent base and listen for new connections
ev_base = event_base_new(); ev_base = event_base_new();
if (!ev_base) { if (!ev_base)
{
SPDLOG_CRITICAL("Libevent base initialization FAILED!"); SPDLOG_CRITICAL("Libevent base initialization FAILED!");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
dns_base = evdns_base_new(ev_base, 1); dns_base = evdns_base_new(ev_base, 1);
if (!dns_base) { if (!dns_base)
{
SPDLOG_CRITICAL("Libevent DNS base initialization FAILED!"); SPDLOG_CRITICAL("Libevent DNS base initialization FAILED!");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -607,9 +601,9 @@ int start(int argc, char **argv)
ev_base, ev_base,
AcceptTCPConnection, nullptr, AcceptTCPConnection, nullptr,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
(const sockaddr*)&sin, sizeof(sin) (const sockaddr *)&sin, sizeof(sin));
); if (!tcp_listener)
if (!tcp_listener) { {
SPDLOG_CRITICAL("TCP listener initialization FAILED!"); SPDLOG_CRITICAL("TCP listener initialization FAILED!");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -625,9 +619,9 @@ int start(int argc, char **argv)
ev_base, ev_base,
AcceptP2PConnection, nullptr, AcceptP2PConnection, nullptr,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
(const sockaddr*)&sin, sizeof(sin) (const sockaddr *)&sin, sizeof(sin));
); if (!p2p_listener)
if (!p2p_listener) { {
SPDLOG_CRITICAL("P2P listener initialization FAILED!"); SPDLOG_CRITICAL("P2P listener initialization FAILED!");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -636,7 +630,8 @@ int start(int argc, char **argv)
// Create client connections // Create client connections
db_clientdesc = DESC_MANAGER::instance().CreateConnectionDesc(ev_base, dns_base, db_addr, db_port, PHASE_DBCLIENT, true); db_clientdesc = DESC_MANAGER::instance().CreateConnectionDesc(ev_base, dns_base, db_addr, db_port, PHASE_DBCLIENT, true);
if (!g_bAuthServer) { if (!g_bAuthServer)
{
db_clientdesc->UpdateChannelStatus(0, true); db_clientdesc->UpdateChannelStatus(0, true);
} }
@ -648,7 +643,6 @@ int start(int argc, char **argv)
g_pkAuthMasterDesc = DESC_MANAGER::instance().CreateConnectionDesc(ev_base, dns_base, g_stAuthMasterIP.c_str(), g_wAuthMasterPort, PHASE_P2P, true); g_pkAuthMasterDesc = DESC_MANAGER::instance().CreateConnectionDesc(ev_base, dns_base, g_stAuthMasterIP.c_str(), g_wAuthMasterPort, PHASE_P2P, true);
P2P_MANAGER::instance().RegisterConnector(g_pkAuthMasterDesc); P2P_MANAGER::instance().RegisterConnector(g_pkAuthMasterDesc);
g_pkAuthMasterDesc->SetP2P(g_wAuthMasterPort, g_bChannel); g_pkAuthMasterDesc->SetP2P(g_wAuthMasterPort, g_bChannel);
} }
else else
{ {
@ -682,22 +676,26 @@ void destroy()
regen_free(); regen_free();
SPDLOG_INFO("<shutdown> Closing network stack..."); SPDLOG_INFO("<shutdown> Closing network stack...");
if (tcp_listener) { if (tcp_listener)
{
evconnlistener_free(tcp_listener); evconnlistener_free(tcp_listener);
tcp_listener = nullptr; tcp_listener = nullptr;
} }
if (p2p_listener) { if (p2p_listener)
{
evconnlistener_free(p2p_listener); evconnlistener_free(p2p_listener);
p2p_listener = nullptr; p2p_listener = nullptr;
} }
if (dns_base) { if (dns_base)
{
evdns_base_free(dns_base, 0); evdns_base_free(dns_base, 0);
dns_base = nullptr; dns_base = nullptr;
} }
if (ev_base) { if (ev_base)
{
event_base_free(ev_base); event_base_free(ev_base);
ev_base = nullptr; ev_base = nullptr;
} }
@ -730,7 +728,8 @@ int idle()
DWORD t; DWORD t;
while (passed_pulses--) { while (passed_pulses--)
{
heartbeat(thecore_heart, ++thecore_heart->pulse); heartbeat(thecore_heart, ++thecore_heart->pulse);
// To reduce the possibility of abort() in checkpointing // To reduce the possibility of abort() in checkpointing
@ -743,7 +742,8 @@ int idle()
s_dwProfiler[PROF_CHR_UPDATE] += (get_dword_time() - t); s_dwProfiler[PROF_CHR_UPDATE] += (get_dword_time() - t);
t = get_dword_time(); t = get_dword_time();
if (!io_loop(ev_base)) return 0; if (!io_loop(ev_base))
return 0;
s_dwProfiler[PROF_IO] += (get_dword_time() - t); s_dwProfiler[PROF_IO] += (get_dword_time() - t);
gettimeofday(&now, (struct timezone *)0); gettimeofday(&now, (struct timezone *)0);
@ -776,7 +776,8 @@ int idle()
return 1; return 1;
} }
static void AcceptError(evconnlistener *listener, void *ctx) { static void AcceptError(evconnlistener *listener, void *ctx)
{
struct event_base *base = evconnlistener_get_base(listener); struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR(); int err = EVUTIL_SOCKET_ERROR();
SPDLOG_CRITICAL("Got an error {} ({}) on the listener. Shutting down.", err, evutil_socket_error_to_string(err)); SPDLOG_CRITICAL("Got an error {} ({}) on the listener. Shutting down.", err, evutil_socket_error_to_string(err));
@ -810,4 +811,3 @@ int io_loop(event_base * base)
return 1; return 1;
} }

View File

@ -1,8 +1,12 @@
#include <fmt/core.h> #include <fmt/core.h>
#ifndef __INTELLISENSE__
#include <version.h> #include <version.h>
#else
#include <../../../common/version.h>
#endif
void WriteVersion() { void WriteVersion()
{
fmt::println("Metin2 Game Server version {} (rev. {}, date: {})", __COMMIT_TAG__, __REVISION__, __COMMIT_DATE__); fmt::println("Metin2 Game Server version {} (rev. {}, date: {})", __COMMIT_TAG__, __REVISION__, __COMMIT_DATE__);
fmt::println("OS: {}, target arch: {}, compiler: {}", __OS_NAME__, __CPU_TARGET__, __COMPILER__); fmt::println("OS: {}, target arch: {}, compiler: {}", __OS_NAME__, __CPU_TARGET__, __COMPILER__);
} }

View File

@ -0,0 +1,15 @@
#pragma once
#include <pqxx/pqxx>
#include <string>
class PgAsyncQuery
{
public:
int id;
std::string query;
pqxx::params parameters;
PgAsyncQuery(int msgId, const std::string &sql, const pqxx::params &params = pqxx::params{})
: id(msgId), query(sql), parameters(params) {}
};

View File

@ -0,0 +1,175 @@
#pragma once
#include <pqxx/pqxx>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <memory>
#include <string>
#include <vector>
#include <chrono>
#include <future>
#include <thread>
#include <atomic>
#include <map>
class PgConnectionPool
{
public:
PgConnectionPool(const char *db_address, int db_port, const char *db_name,
const char *user, const char *pwd,
size_t pool_size = 10,
std::chrono::seconds idle_timeout = std::chrono::seconds(60))
: db_address_(db_address), db_port_(db_port), db_name_(db_name),
user_(user), pwd_(pwd), max_pool_size_(pool_size),
idle_timeout_(idle_timeout), stop_cleaner_(false)
{
for (size_t i = 0; i < max_pool_size_; ++i)
{
auto conn = createConnection();
pool_.push({std::move(conn), std::chrono::steady_clock::now()});
}
// Start cleanup thread
cleaner_thread_ = std::thread(&PgConnectionPool::cleanupIdleConnections, this);
}
~PgConnectionPool()
{
stop_cleaner_ = true;
cond_.notify_all();
if (cleaner_thread_.joinable())
{
cleaner_thread_.join();
}
std::lock_guard<std::mutex> lock(mutex_);
while (!pool_.empty())
{
auto &entry = pool_.front();
if (entry.conn && entry.conn->is_open())
{
entry.conn->close();
}
pool_.pop();
}
}
// Asynchronous acquire
std::future<std::shared_ptr<pqxx::connection>> acquireAsync()
{
return std::async(std::launch::async, [this]()
{ return this->acquire(); });
}
// Blocking acquire
std::shared_ptr<pqxx::connection> acquire()
{
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this]()
{ return !pool_.empty(); });
ConnectionEntry entry = std::move(pool_.front());
pool_.pop();
if (!entry.conn || !entry.conn->is_open())
{
entry.conn = createConnection(); // Reconnect if needed
}
return std::shared_ptr<pqxx::connection>(
entry.conn.release(),
[this](pqxx::connection *p)
{
std::lock_guard<std::mutex> lock(mutex_);
auto now = std::chrono::steady_clock::now();
if (p->is_open())
{
pool_.push({std::unique_ptr<pqxx::connection>(p), now});
}
else
{
delete p;
pool_.push({createConnection(), now});
}
cond_.notify_one();
});
}
private:
struct ConnectionEntry
{
std::unique_ptr<pqxx::connection> conn;
std::chrono::steady_clock::time_point last_used;
};
std::unique_ptr<pqxx::connection> createConnection()
{
std::string conn_str = "host=" + db_address_ +
" port=" + std::to_string(db_port_) +
" dbname=" + db_name_ +
" user=" + user_ +
" password=" + pwd_;
try
{
auto conn = std::make_unique<pqxx::connection>(conn_str);
if (!conn->is_open())
{
throw std::runtime_error("Failed to open DB connection");
}
return conn;
}
catch (const std::exception &e)
{
throw std::runtime_error(std::string("Connection error: ") + e.what());
}
}
void cleanupIdleConnections()
{
while (!stop_cleaner_)
{
std::this_thread::sleep_for(idle_timeout_ / 2); // Clean more frequently
std::lock_guard<std::mutex> lock(mutex_);
size_t current_size = pool_.size();
std::queue<ConnectionEntry> temp;
auto now = std::chrono::steady_clock::now();
while (!pool_.empty())
{
auto entry = std::move(pool_.front());
pool_.pop();
if (std::chrono::duration_cast<std::chrono::seconds>(
now - entry.last_used) > idle_timeout_)
{
if (entry.conn && entry.conn->is_open())
{
entry.conn->close();
}
}
else
{
temp.push(std::move(entry));
}
}
pool_ = std::move(temp);
cond_.notify_all();
}
}
std::string db_address_, db_name_, user_, pwd_;
int db_port_;
size_t max_pool_size_;
std::chrono::seconds idle_timeout_;
std::queue<ConnectionEntry> pool_;
std::mutex mutex_;
std::condition_variable cond_;
std::thread cleaner_thread_;
std::atomic<bool> stop_cleaner_;
};