Files
m2dev-server-src/src/db/Main.cpp
server 6d24172c51
Some checks failed
build / Linux asan (push) Has been cancelled
build / Linux release (push) Has been cancelled
build / FreeBSD build (push) Has been cancelled
db: treat empty table postfix as expected default
2026-04-14 16:37:34 +02:00

493 lines
11 KiB
C++

#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 <signal.h>
#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: <host db user password [port]>\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, "<unset>"),
EmptyToLabel(g_stTablePostfix, "<none>"),
EmptyToLabel(g_stPlayerDBName, "<unset>"),
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, "<unset>")
);
}
}
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();
}