#include "stdafx.h" #include "Config.h" #include "Peer.h" #include "DBManager.h" #include "ClientManager.h" #include "GuildManager.h" #include "ItemAwardManager.h" #include "HB.h" #include "PrivManager.h" #include "MoneyLog.h" #include "Marriage.h" #include "ItemIDRangeManager.h" #include #undef OS_FREEBSD void SetPlayerDBName(const char* c_pszPlayerDBName); void SetTablePostfix(const char* c_pszTablePostfix); int Start(); std::string g_stTablePostfix; std::string g_stLocaleNameColumn = "name"; std::string g_stLocale = "euckr"; std::string g_stPlayerDBName = ""; bool g_bHotBackup = false; BOOL g_test_server = false; //단위 초 int g_iPlayerCacheFlushSeconds = 60*7; int g_iItemCacheFlushSeconds = 60*5; //g_iLogoutSeconds 수치는 g_iPlayerCacheFlushSeconds 와 g_iItemCacheFlushSeconds 보다 길어야 한다. int g_iLogoutSeconds = 60*10; int g_log = 1; // MYSHOP_PRICE_LIST int g_iItemPriceListTableCacheFlushSeconds = 540; // END_OF_MYSHOP_PRICE_LIST #ifdef OS_FREEBSD extern const char * _malloc_options; #endif extern void WriteVersion(); namespace { struct SQLConnectionConfig { char host[64]; char database[64]; char user[64]; char password[64]; int port; }; const char* BoolState(bool value) { return value ? "on" : "off"; } const char* EmptyToLabel(const std::string& value, const char* fallback) { return value.empty() ? fallback : value.c_str(); } bool CopyEnvString(const char* env_name, char* dest, size_t dest_size) { const char* value = std::getenv(env_name); if (!value) return false; strlcpy(dest, value, dest_size); return true; } bool CopyEnvInt(const char* env_name, int* dest) { const char* value = std::getenv(env_name); if (!value) return false; str_to_number(*dest, value); return true; } bool HasSQLConfig(const SQLConnectionConfig& config) { return config.host[0] && config.database[0] && config.user[0] && config.password[0]; } bool ParseSQLConfig(const char* value, SQLConnectionConfig* config, const char* label) { int token_count = sscanf( value, " %63s %63s %63s %63s %d ", config->host, config->database, config->user, config->password, &config->port); if (token_count < 4 || !HasSQLConfig(*config)) { fprintf(stderr, "%s syntax: \n", label); return false; } return true; } bool LoadSQLConfig(const char* key, const char* env_prefix, SQLConnectionConfig* config) { char line[256 + 1]; bool loaded_from_file = false; if (CConfig::instance().GetValue(key, line, sizeof(line) - 1)) { if (!ParseSQLConfig(line, config, key)) return false; loaded_from_file = true; } bool overridden = false; const std::string prefix = env_prefix; overridden |= CopyEnvString((prefix + "_HOST").c_str(), config->host, sizeof(config->host)); overridden |= CopyEnvString((prefix + "_DB").c_str(), config->database, sizeof(config->database)); overridden |= CopyEnvString((prefix + "_USER").c_str(), config->user, sizeof(config->user)); overridden |= CopyEnvString((prefix + "_PASSWORD").c_str(), config->password, sizeof(config->password)); overridden |= CopyEnvInt((prefix + "_PORT").c_str(), &config->port); if (overridden) sys_log(0, "CONFIG: %s overridden from environment", key); if (!loaded_from_file && !overridden) { sys_err("%s not configured", key); return false; } if (!HasSQLConfig(*config)) { sys_err("%s incomplete after applying config/environment overrides", key); return false; } return true; } bool ConnectDatabase(int slot, const SQLConnectionConfig& config, const char* label) { sys_log(0, "connecting to MySQL server (%s)", label); int retry_count = 5; do { if (CDBManager::instance().Connect(slot, config.host, config.port, config.database, config.user, config.password)) { sys_log(0, " OK"); return true; } sys_log(0, " failed, retrying in 5 seconds"); fprintf(stderr, " failed, retrying in 5 seconds"); sleep(5); } while (retry_count--); return false; } void LogStartupSummary(int heart_fps, int player_id_start) { sys_log(0, "[STARTUP] locale=%s table_postfix=%s player_db=%s player_id_start=%d heart_fps=%d test_server=%s log=%s hotbackup=%s", EmptyToLabel(g_stLocale, ""), EmptyToLabel(g_stTablePostfix, ""), EmptyToLabel(g_stPlayerDBName, ""), player_id_start, heart_fps, BoolState(g_test_server), BoolState(g_log != 0), BoolState(g_bHotBackup) ); sys_log(0, "[STARTUP] cache_flush player=%d item=%d pricelist=%d logout=%d locale_name_column=%s", g_iPlayerCacheFlushSeconds, g_iItemCacheFlushSeconds, g_iItemPriceListTableCacheFlushSeconds, g_iLogoutSeconds, EmptyToLabel(g_stLocaleNameColumn, "") ); } } void emergency_sig(int sig) { if (sig == SIGSEGV) sys_log(0, "SIGNAL: SIGSEGV"); else if (sig == SIGUSR1) sys_log(0, "SIGNAL: SIGUSR1"); if (sig == SIGSEGV) abort(); } int main() { log_init(); WriteVersion(); #ifdef OS_FREEBSD _malloc_options = "A"; #endif CConfig Config; CNetPoller poller; CDBManager DBManager; CClientManager ClientManager; PlayerHB player_hb; CGuildManager GuildManager; CPrivManager PrivManager; CMoneyLog MoneyLog; ItemAwardManager ItemAwardManager; marriage::CManager MarriageManager; CItemIDRangeManager ItemIDRangeManager; if (!Start()) return 1; GuildManager.Initialize(); MarriageManager.Initialize(); ItemIDRangeManager.Build(); sys_log(0, "Metin2DBCacheServer Start\n"); CClientManager::instance().MainLoop(); signal_timer_disable(); sys_log(0, "[SHUTDOWN] DB main loop finished, quitting SQL workers"); DBManager.Quit(); sys_log(0, "[SHUTDOWN] DB SQL workers stopped"); int iCount; while (1) { iCount = 0; iCount += CDBManager::instance().CountReturnQuery(SQL_PLAYER); iCount += CDBManager::instance().CountAsyncQuery(SQL_PLAYER); if (iCount == 0) break; usleep(1000); sys_log(0, "WAITING_QUERY_COUNT %d", iCount); } sys_log(0, "[SHUTDOWN] DB process exiting cleanly"); log_destroy(); return 0; } void emptybeat(LPHEART heart, int pulse) { if (!(pulse % heart->passes_per_sec)) // 1초에 한번 { } } // // @version 05/06/13 Bang2ni - 아이템 가격정보 캐시 flush timeout 설정 추가. // int Start() { if (!CConfig::instance().LoadFile("conf/db.txt")) { fprintf(stderr, "Loading conf/db.txt failed.\n"); return false; } if (!CConfig::instance().GetValue("TEST_SERVER", &g_test_server)) { fprintf(stderr, "Real Server\n"); } else fprintf(stderr, "Test Server\n"); if (!CConfig::instance().GetValue("LOG", &g_log)) { fprintf(stderr, "Log Off"); g_log= 0; } else { g_log = 1; fprintf(stderr, "Log On"); } int tmpValue; int heart_beat = 50; if (!CConfig::instance().GetValue("CLIENT_HEART_FPS", &heart_beat)) { fprintf(stderr, "Cannot find CLIENT_HEART_FPS configuration.\n"); return false; } thecore_init(heart_beat, emptybeat); signal_timer_enable(60); char szBuf[256+1]; if (CConfig::instance().GetValue("LOCALE", szBuf, 256)) { g_stLocale = szBuf; sys_log(0, "LOCALE set to %s", g_stLocale.c_str()); // CHINA_DISABLE_HOTBACKUP if ("gb2312" == g_stLocale) { sys_log(0, "CIBN_LOCALE: DISABLE_HOTBACKUP"); g_bHotBackup = false; } // END_OF_CHINA_DISABLE_HOTBACKUP } int iDisableHotBackup; if (CConfig::instance().GetValue("DISABLE_HOTBACKUP", &iDisableHotBackup)) { if (iDisableHotBackup) { sys_log(0, "CONFIG: DISABLE_HOTBACKUP"); g_bHotBackup = false; } } if (!CConfig::instance().GetValue("TABLE_POSTFIX", szBuf, 256)) { sys_log(0, "CONFIG: TABLE_POSTFIX not configured, using default table names"); szBuf[0] = '\0'; } SetTablePostfix(szBuf); if (CConfig::instance().GetValue("PLAYER_CACHE_FLUSH_SECONDS", szBuf, 256)) { str_to_number(g_iPlayerCacheFlushSeconds, szBuf); sys_log(0, "PLAYER_CACHE_FLUSH_SECONDS: %d", g_iPlayerCacheFlushSeconds); } if (CConfig::instance().GetValue("ITEM_CACHE_FLUSH_SECONDS", szBuf, 256)) { str_to_number(g_iItemCacheFlushSeconds, szBuf); sys_log(0, "ITEM_CACHE_FLUSH_SECONDS: %d", g_iItemCacheFlushSeconds); } // MYSHOP_PRICE_LIST if (CConfig::instance().GetValue("ITEM_PRICELIST_CACHE_FLUSH_SECONDS", szBuf, 256)) { str_to_number(g_iItemPriceListTableCacheFlushSeconds, szBuf); sys_log(0, "ITEM_PRICELIST_CACHE_FLUSH_SECONDS: %d", g_iItemPriceListTableCacheFlushSeconds); } // END_OF_MYSHOP_PRICE_LIST // if (CConfig::instance().GetValue("CACHE_FLUSH_LIMIT_PER_SECOND", szBuf, 256)) { DWORD dwVal = 0; str_to_number(dwVal, szBuf); CClientManager::instance().SetCacheFlushCountLimit(dwVal); } int iIDStart; if (!CConfig::instance().GetValue("PLAYER_ID_START", &iIDStart)) { sys_err("PLAYER_ID_START not configured"); return false; } CClientManager::instance().SetPlayerIDStart(iIDStart); if (CConfig::instance().GetValue("NAME_COLUMN", szBuf, 256)) { fprintf(stderr, "%s %s", g_stLocaleNameColumn.c_str(), szBuf); g_stLocaleNameColumn = szBuf; } SQLConnectionConfig player_sql = {}; SQLConnectionConfig account_sql = {}; SQLConnectionConfig common_sql = {}; SQLConnectionConfig hotbackup_sql = {}; if (!LoadSQLConfig("SQL_PLAYER", "METIN2_PLAYER_SQL", &player_sql)) return false; if (!ConnectDatabase(SQL_PLAYER, player_sql, "player")) return false; fprintf(stderr, "Success PLAYER\n"); SetPlayerDBName(player_sql.database); if (!LoadSQLConfig("SQL_ACCOUNT", "METIN2_ACCOUNT_SQL", &account_sql)) return false; if (!ConnectDatabase(SQL_ACCOUNT, account_sql, "account")) return false; fprintf(stderr, "Success ACCOUNT\n"); if (!LoadSQLConfig("SQL_COMMON", "METIN2_COMMON_SQL", &common_sql)) return false; if (!ConnectDatabase(SQL_COMMON, common_sql, "common")) return false; fprintf(stderr, "Success COMMON\n"); if (!LoadSQLConfig("SQL_HOTBACKUP", "METIN2_HOTBACKUP_SQL", &hotbackup_sql)) return false; if (!ConnectDatabase(SQL_HOTBACKUP, hotbackup_sql, "hotbackup")) return false; fprintf(stderr, "Success HOTBACKUP\n"); if (!CNetPoller::instance().Create()) { sys_err("Cannot create network poller"); return false; } sys_log(0, "ClientManager initialization.. "); if (!CClientManager::instance().Initialize()) { sys_log(0, " failed"); return false; } sys_log(0, " OK"); if (!PlayerHB::instance().Initialize()) { sys_err("cannot initialize player hotbackup"); return false; } LogStartupSummary(heart_beat, iIDStart); #ifndef OS_WINDOWS signal(SIGUSR1, emergency_sig); #endif signal(SIGSEGV, emergency_sig); return true; } void SetTablePostfix(const char* c_pszTablePostfix) { if (!c_pszTablePostfix || !*c_pszTablePostfix) g_stTablePostfix = ""; else g_stTablePostfix = c_pszTablePostfix; } const char * GetTablePostfix() { return g_stTablePostfix.c_str(); } void SetPlayerDBName(const char* c_pszPlayerDBName) { if (! c_pszPlayerDBName || ! *c_pszPlayerDBName) g_stPlayerDBName = ""; else { g_stPlayerDBName = c_pszPlayerDBName; g_stPlayerDBName += "."; } } const char * GetPlayerDBName() { return g_stPlayerDBName.c_str(); }