knightonline/server/LogInServer/LoginServer.cpp

288 lines
7.3 KiB
C++

#include "stdafx.h"
#include <sstream>
#include "../shared/Ini.h"
#include "../shared/DateTime.h"
extern bool g_bRunning;
std::vector<Thread *> g_timerThreads;
LoginServer::LoginServer() : m_sLastVersion(__VERSION), m_fpLoginServer(nullptr)
{
}
bool LoginServer::Startup()
{
GetInfoFromIni();
DateTime time;
printf("LevoKO LoginServer started in %02d-%02d-%04d at %02d:%02d\n\n", time.GetDay(), time.GetMonth(), time.GetYear(), time.GetHour(), time.GetMinute());
printf("Project : %d\n", GetVersion());
printf("Edited by Levent\n\n");
#ifdef _WIN32
CreateDirectory("Logs",NULL);
#else
mkdir("Logs", 0755);
#endif
m_fpLoginServer = fopen("./Logs/LoginServer.log", "a");
if (m_fpLoginServer == nullptr)
{
printf("ERROR: Unable to open log file.\n");
return false;
}
m_fpUser = fopen(string_format("./Logs/Login_%d_%d_%d.log",time.GetDay(),time.GetMonth(),time.GetYear()).c_str(), "a");
if (m_fpUser == nullptr)
{
printf("ERROR: Unable to open user log file.\n");
return false;
}
if (!m_DBProcess.Connect(m_ODBCName, m_ODBCLogin, m_ODBCPwd))
{
printf("ERROR: Unable to connect to the database using the details configured.\n");
return false;
}
printf("Databaseye Basarili Sekilde Baglanildi.\n");
if (!m_DBProcess.LoadVersionList())
{
printf("ERROR: Unable to load the version list.\n");
return false;
}
printf("LevoKO Server Versionu: %d\n", GetVersion());
InitPacketHandlers();
for(int i=0; i<10 ; i++)
if (!m_socketMgr[i].Listen(m_LoginServerPort+i, MAX_USER))
{
printf("ERROR: Failed to listen on server port.\n");
return false;
}
for(int i=0; i<10 ; i++)
m_socketMgr[i].RunServer();
g_timerThreads.push_back(new Thread(Timer_UpdateUserCount));
return true;
}
uint32 LoginServer::Timer_UpdateUserCount(void * lpParam)
{
while (g_bRunning)
{
g_pMain->UpdateServerList();
sleep(60 * SECOND);
}
return 0;
}
void LoginServer::GetServerList(Packet & result)
{
Guard lock(m_serverListLock);
result.append(m_serverListPacket.contents(), m_serverListPacket.size());
}
void LoginServer::UpdateServerList()
{
// Update the user counts first
m_DBProcess.LoadUserCountList();
Guard lock(m_serverListLock);
Packet & result = m_serverListPacket;
result.clear();
result << uint8(m_ServerList.size());
foreach (itr, m_ServerList)
{
_SERVER_INFO *pServer = *itr;
result << pServer->strServerIP;
result << pServer->strLanIP;
result << pServer->strServerName;
if (pServer->sUserCount <= pServer->sPlayerCap)
result << pServer->sUserCount;
else
result << int16(-1);
result << pServer->sServerID << pServer->sGroupID;
result << pServer->sPlayerCap << pServer->sFreePlayerCap;
result << uint8(0);
// we read all this stuff from ini, TODO: make this more versatile.
result << pServer->strKarusKingName << pServer->strKarusNotice
<< pServer->strElMoradKingName << pServer->strElMoradNotice;
}
}
void LoginServer::GetInfoFromIni()
{
CIni ini(CONF_LOGIN_SERVER);
ini.GetString("DOWNLOAD", "URL", "ftp.yoursite.net", m_strFtpUrl, false);
ini.GetString("DOWNLOAD", "PATH", "/", m_strFilePath, false);
ini.GetString("ODBC", "DSN", "KO_MAIN", m_ODBCName, false);
ini.GetString("ODBC", "UID", "username", m_ODBCLogin, false);
ini.GetString("ODBC", "PWD", "password", m_ODBCPwd, false);
m_LoginServerPort = ini.GetInt("SETTINGS","PORT", 15100);
int nServerCount = ini.GetInt("SERVER_LIST", "COUNT", 1);
if (nServerCount <= 0)
nServerCount = 1;
char key[20];
_SERVER_INFO* pInfo = nullptr;
m_ServerList.reserve(nServerCount);
// TODO: Replace this nonsense with something a little more versatile
for (int i = 0; i < nServerCount; i++)
{
pInfo = new _SERVER_INFO;
_snprintf(key, sizeof(key), "SERVER_%02d", i);
ini.GetString("SERVER_LIST", key, "127.0.0.1", pInfo->strServerIP, false);
_snprintf(key, sizeof(key), "LANIP_%02d", i);
ini.GetString("SERVER_LIST", key, "127.0.0.1", pInfo->strLanIP, false);
_snprintf(key, sizeof(key), "NAME_%02d", i);
ini.GetString("SERVER_LIST", key, "TEST|Server 1", pInfo->strServerName, false);
_snprintf(key, sizeof(key), "ID_%02d", i);
pInfo->sServerID = ini.GetInt("SERVER_LIST", key, 1);
_snprintf(key, sizeof(key), "GROUPID_%02d", i);
pInfo->sGroupID = ini.GetInt("SERVER_LIST", key, 1);
_snprintf(key, sizeof(key), "PREMLIMIT_%02d", i);
pInfo->sPlayerCap = ini.GetInt("SERVER_LIST", key, MAX_USER);
_snprintf(key, sizeof(key), "FREELIMIT_%02d", i);
pInfo->sFreePlayerCap = ini.GetInt("SERVER_LIST", key, MAX_USER);
_snprintf(key, sizeof(key), "KING1_%02d", i);
ini.GetString("SERVER_LIST", key, "", pInfo->strKarusKingName);
_snprintf(key, sizeof(key), "KING2_%02d", i);
ini.GetString("SERVER_LIST", key, "", pInfo->strElMoradKingName);
_snprintf(key, sizeof(key), "KINGMSG1_%02d", i);
ini.GetString("SERVER_LIST", key, "", pInfo->strKarusNotice);
_snprintf(key, sizeof(key), "KINGMSG2_%02d", i);
ini.GetString("SERVER_LIST", key, "", pInfo->strElMoradNotice);
m_ServerList.push_back(pInfo);
}
// Read news from INI (max 3 blocks)
#define BOX_START '#' << uint8(0) << '\n'
#define LINE_ENDING uint8(0) << '\n'
#define BOX_END BOX_START << LINE_ENDING
m_news.Size = 0;
std::stringstream ss;
for (int i = 0; i < 3; i++)
{
string title, message;
_snprintf(key, sizeof(key), "TITLE_%02d", i);
ini.GetString("NEWS", key, "", title);
if (title.empty())
continue;
_snprintf(key, sizeof(key), "MESSAGE_%02d", i);
ini.GetString("NEWS", key, "", message);
if (message.empty())
continue;
size_t oldPos = 0, pos = 0;
ss << title << BOX_START;
// potentially support multiline by making | act as linebreaks (same as the TBL afaik, so at least we're conformant).
//replace(messages[i].begin(), messages[i].end(), '|', '\n');
//while ((pos = message.find('\r', pos)) != string::npos)
// message.erase(pos, 1);
//Remove \n for now, perhaps re-implement later
//while ((pos = message.find('\n', pos)) != string::npos)
// message.erase(pos, 1);
ss << message << LINE_ENDING << BOX_END;
}
m_news.Size = ss.str().size();
if (m_news.Size)
memcpy(&m_news.Content, ss.str().c_str(), m_news.Size);
}
void LoginServer::WriteLogFile(string & logMessage)
{
Guard lock(m_lock);
fwrite(logMessage.c_str(), logMessage.length(), 1, m_fpLoginServer);
fflush(m_fpLoginServer);
}
void LoginServer::WriteUserLogFile(string & logMessage)
{
Guard lock(m_lock);
fwrite(logMessage.c_str(), logMessage.length(), 1, m_fpUser);
fflush(m_fpUser);
}
void LoginServer::ReportSQLError(OdbcError *pError)
{
if (pError == nullptr)
return;
// This is *very* temporary.
string errorMessage = string_format(_T("ODBC error occurred.\r\nSource: %s\r\nError: %s\r\nDescription: %s\n"),
pError->Source.c_str(), pError->ExtendedErrorMessage.c_str(), pError->ErrorMessage.c_str());
TRACE("%s", errorMessage.c_str());
WriteLogFile(errorMessage);
delete pError;
}
LoginServer::~LoginServer()
{
printf("Waiting for timer threads to exit...");
foreach (itr, g_timerThreads)
{
(*itr)->waitForExit();
delete (*itr);
}
printf(" exited.\n");
foreach (itr, m_ServerList)
delete *itr;
m_ServerList.clear();
foreach (itr, m_VersionList)
delete itr->second;
m_VersionList.clear();
if (m_fpLoginServer != nullptr)
fclose(m_fpLoginServer);
if (m_fpUser != nullptr)
fclose(m_fpUser);
printf("Shutting down socket system...");
for(int i=0; i<10 ; i++)
m_socketMgr[i].Shutdown();
printf(" done.\n");
}