/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "LogonStdAfx.h"
#include
#include "../shared/svn_revision.h"
#ifndef WIN32
#include
#endif
#include "../shared/ascent_getopt.h"
#define BANNER "Ascent r%u/%s-%s (%s) :: Logon Server"
#ifndef WIN32
#include
#endif
//#include
//#include
// Database impl
Database * sLogonSQL;
initialiseSingleton(LogonServer);
bool mrunning = true;
Mutex _authSocketLock;
set _authSockets;
/*** Signal Handler ***/
void _OnSignal(int s)
{
switch (s)
{
#ifndef WIN32
case SIGHUP:
{
sLog.outString("Received SIGHUP signal, reloading accounts.");
AccountMgr::getSingleton().ReloadAccounts(true);
}break;
#endif
case SIGINT:
case SIGTERM:
case SIGABRT:
#ifdef _WIN32
case SIGBREAK:
#endif
mrunning = false;
break;
}
signal(s, _OnSignal);
}
int main(int argc, char** argv)
{
#ifndef WIN32
rlimit rl;
if (getrlimit(RLIMIT_CORE, &rl) == -1)
printf("getrlimit failed. This could be problem.\n");
else
{
rl.rlim_cur = rl.rlim_max;
if (setrlimit(RLIMIT_CORE, &rl) == -1)
printf("setrlimit failed. Server may not save core.dump files.\n");
}
#endif
new LogonServer;
// Run!
LogonServer::getSingleton( ).Run(argc, argv);
delete LogonServer::getSingletonPtr();
}
bool startdb()
{
string lhostname, lusername, lpassword, ldatabase;
int lport = 0;
int ltype = 1;
// Configure Main Database
bool result;
// Configure Logon Database...
result = Config.MainConfig.GetString("LogonDatabase", "Username", &lusername);
result = !result ? result : Config.MainConfig.GetString("LogonDatabase", "Password", &lpassword);
result = !result ? result : Config.MainConfig.GetString("LogonDatabase", "Hostname", &lhostname);
result = !result ? result : Config.MainConfig.GetString("LogonDatabase", "Name", &ldatabase);
result = !result ? result : Config.MainConfig.GetInt("LogonDatabase", "Port", &lport);
result = !result ? result : Config.MainConfig.GetInt("LogonDatabase", "Type", <ype);
if(result == false)
{
sLog.outString("sql: Logon database parameters not found.");
return false;
}
sLog.SetScreenLoggingLevel(Config.MainConfig.GetIntDefault("LogLevel", "Screen", 0));
sLogonSQL = CreateDatabaseInterface((DatabaseType)ltype);
// Initialize it
if(!sLogonSQL->Initialize(lhostname.c_str(), (unsigned int)lport, lusername.c_str(),
lpassword.c_str(), ldatabase.c_str(), Config.MainConfig.GetIntDefault("LogonDatabase", "ConnectionCount", 1),
16384))
{
sLog.outError("sql: Logon database initialization failed. Exiting.");
return false;
}
return true;
}
#define DEF_VALUE_NOT_SET 0xDEADBEEF
void LogonServer::Run(int argc, char ** argv)
{
UNIXTIME = time(NULL);
#ifdef WIN32
char * config_file = "logonserver.conf";
#else
char * config_file = CONFDIR "/logonserver.conf";
#endif
int file_log_level = DEF_VALUE_NOT_SET;
int screen_log_level = DEF_VALUE_NOT_SET;
int do_check_conf = 0;
int do_version = 0;
struct ascent_option longopts[] =
{
{ "checkconf", ascent_no_argument, &do_check_conf, 1 },
{ "screenloglevel", ascent_required_argument, &screen_log_level, 1 },
{ "fileloglevel", ascent_required_argument, &file_log_level, 1 },
{ "version", ascent_no_argument, &do_version, 1 },
{ "conf", ascent_required_argument, NULL, 'c' },
{ 0, 0, 0, 0 }
};
char c;
while ((c = ascent_getopt_long_only(argc, argv, ":f:", longopts, NULL)) != -1)
{
switch (c)
{
case 'c':
/* Log filename was set */
config_file = new char[strlen(ascent_optarg)];
strcpy(config_file,ascent_optarg);
break;
case 0:
break;
default:
sLog.m_fileLogLevel = -1;
sLog.m_screenLogLevel = 3;
printf("Usage: %s [--checkconf] [--screenloglevel ] [--fileloglevel ] [--conf ] [--version]\n", argv[0]);
return;
}
}
// Startup banner
if(!do_version && !do_check_conf)
{
sLog.Init(-1, 3);
}
else
{
sLog.m_fileLogLevel = -1;
sLog.m_screenLogLevel = 3;
}
sLog.outString(BANNER, g_getRevision(), CONFIG, PLATFORM_TEXT, ARCH);
sLog.outString("==============================================================================");
sLog.outString("");
if(do_version)
return;
if(do_check_conf)
{
printf("Checking config file: %s\n", config_file);
if(Config.MainConfig.SetSource(config_file, true))
printf(" Passed without errors.\n");
else
printf(" Encountered one or more errors.\n");
/* test for die variables */
string die;
if(Config.MainConfig.GetString("die", "msg", &die) || Config.MainConfig.GetString("die2", "msg", &die))
printf("Die directive received: %s", die.c_str());
return;
}
sLog.outString("The key combination will safely shut down the server at any time.");
sLog.outString("");
Log.Notice("System","Initializing Random Number Generators...");
Log.Notice("Config", "Loading Config Files...");
if(Config.MainConfig.SetSource(config_file))
{
Log.Success("Config", ">> logonserver.conf", config_file);
}
else
{
Log.Error("Config", ">> logonserver.conf", config_file);
return;
}
Log.Notice("ThreadMgr", "Starting...");
new ThreadMgr;
ThreadMgr::getSingleton( ).Initialize();
if(!startdb())
return;
Log.Notice("AccountMgr", "Starting...");
new AccountMgr;
new IPBanner;
Log.Notice("InfoCore", "Starting...");
new InformationCore;
Log.Notice("AccountMgr", "Precaching accounts...");
sAccountMgr.ReloadAccounts(true);
Log.Notice("AccountMgr", "%u accounts are loaded and ready.", sAccountMgr.GetCount());
Log.Line();
// Spawn periodic function caller thread for account reload every 10mins
int time = Config.MainConfig.GetIntDefault("Rates", "AccountRefresh",600);
time *= 1000;
//SpawnPeriodicCallThread(AccountMgr, AccountMgr::getSingletonPtr(), &AccountMgr::ReloadAccountsCallback, time);
PeriodicFunctionCaller * pfc = new PeriodicFunctionCaller(AccountMgr::getSingletonPtr(),
&AccountMgr::ReloadAccountsCallback, time);
launch_thread(pfc);
// Load conf settings..
uint32 cport = Config.MainConfig.GetIntDefault("Listen", "RealmListPort", 3724);
uint32 sport = Config.MainConfig.GetIntDefault("Listen", "ServerPort", 8093);
//uint32 threadcount = Config.MainConfig.GetIntDefault("Network", "ThreadCount", 5);
//uint32 threaddelay = Config.MainConfig.GetIntDefault("Network", "ThreadDelay", 20);
string host = Config.MainConfig.GetStringDefault("Listen", "Host", "0.0.0.0");
string shost = Config.MainConfig.GetStringDefault("Listen", "ISHost", host.c_str());
min_build = Config.MainConfig.GetIntDefault("Client", "MinBuild", 6180);
max_build = Config.MainConfig.GetIntDefault("Client", "MaxBuild", 6999);
string logon_pass = Config.MainConfig.GetStringDefault("LogonServer", "RemotePassword", "r3m0t3b4d");
Sha1Hash hash;
hash.UpdateData(logon_pass);
hash.Finalize();
memcpy(sql_hash, hash.GetDigest(), 20);
launch_thread(new LogonConsoleThread);
new SocketMgr;
new SocketGarbageCollector;
sSocketMgr.SpawnWorkerThreads();
ListenSocket * cl = new ListenSocket(host.c_str(), cport);
ListenSocket * sl = new ListenSocket(shost.c_str(), sport);
// Spawn auth listener
// Spawn interserver listener
bool authsockcreated = cl->IsOpen();
bool intersockcreated = sl->IsOpen();
// hook signals
sLog.outString("Hooking signals...");
signal(SIGINT, _OnSignal);
signal(SIGTERM, _OnSignal);
signal(SIGABRT, _OnSignal);
#ifdef _WIN32
signal(SIGBREAK, _OnSignal);
#else
signal(SIGHUP, _OnSignal);
#endif
/* write pid file */
FILE * fPid = fopen("logonserver.pid", "w");
if(fPid)
{
uint32 pid;
#ifdef WIN32
pid = GetCurrentProcessId();
#else
pid = getpid();
#endif
fprintf(fPid, "%u", (unsigned int)pid);
fclose(fPid);
}
uint32 loop_counter = 0;
while(mrunning && authsockcreated && intersockcreated)
{
if(!(++loop_counter % 400)) // 20 seconds
CheckForDeadSockets();
cl->Update();
sl->Update();
sInfoCore.TimeoutSockets();
sSocketGarbageCollector.Update();
CheckForDeadSockets(); // Flood Protection
Sleep(50);
}
sLog.outString("Shutting down...");
pfc->kill();
delete pfc;
cl->Close();
sl->Close();
delete sl;
delete cl;
sSocketMgr.CloseAll();
#ifdef WIN32
sSocketMgr.ShutdownThreads();
#endif
sLogonConsole.Kill();
delete LogonConsole::getSingletonPtr();
// kill db
sThreadMgr.RemoveThread((MySQLDatabase*)sLogonSQL);
((MySQLDatabase*)sLogonSQL)->SetThreadState(THREADSTATE_TERMINATE);
// send a query to wake up condition
((MySQLDatabase*)sLogonSQL)->Execute("SELECT count(acct) from accounts");
sLog.outString("Waiting for database to close..");
while(((MySQLDatabase*)sLogonSQL)->ThreadRunning)
Sleep(100);
DestroyDatabaseInterface(sLogonSQL);
sThreadMgr.Shutdown();
// delete pid file
remove("logonserver.pid");
delete AccountMgr::getSingletonPtr();
delete InformationCore::getSingletonPtr();
delete ThreadMgr::getSingletonPtr();
delete IPBanner::getSingletonPtr();
delete SocketMgr::getSingletonPtr();
delete SocketGarbageCollector::getSingletonPtr();
printf("Shutdown complete.\n");
}
void OnCrash(bool Terminate)
{
}
void LogonServer::CheckForDeadSockets()
{
_authSocketLock.Acquire();
time_t t = time(NULL);
time_t diff;
set::iterator itr = _authSockets.begin();
set::iterator it2;
AuthSocket * s;
for(itr = _authSockets.begin(); itr != _authSockets.end();)
{
it2 = itr;
s = (*it2);
++itr;
diff = t - s->GetLastRecv();
if(diff > 240) // More than 4mins -> kill the socket.
{
_authSockets.erase(it2);
s->removedFromSet = true;
s->Disconnect();
}
}
_authSocketLock.Release();
}