2010-10-07 15:35:36 +02:00
/*
2014-01-01 00:07:53 +01:00
* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
2010-10-07 15:35:36 +02:00
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* 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 2 of the License, or (at your
* option) 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 <http://www.gnu.org/licenses/>.
2008-10-02 16:23:55 -05:00
*/
2009-10-17 15:51:44 -07:00
2008-10-14 11:57:03 -05:00
/// \addtogroup Trinityd Trinity Daemon
2008-10-02 16:23:55 -05:00
/// @{
/// \file
2009-10-17 15:51:44 -07:00
2009-06-04 21:41:07 +02:00
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
2014-07-04 15:22:06 +02:00
#include <boost/asio/io_service.hpp>
2014-07-06 23:04:38 +02:00
#include <boost/asio/deadline_timer.hpp>
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
#include "Common.h"
2014-07-04 15:22:06 +02:00
#include "DatabaseEnv.h"
#include "AsyncAcceptor.h"
#include "RASession.h"
2010-07-29 01:22:45 +02:00
#include "Configuration/Config.h"
2014-07-04 15:22:06 +02:00
#include "OpenSSLCrypto.h"
#include "ProcessPriority.h"
#include "BigNumber.h"
#include "RealmList.h"
#include "World.h"
#include "MapManager.h"
#include "ObjectAccessor.h"
#include "ScriptMgr.h"
#include "OutdoorPvP/OutdoorPvPMgr.h"
#include "BattlegroundMgr.h"
#include "TCSoap.h"
#include "CliRunnable.h"
#include "SystemConfig.h"
2014-07-07 22:03:41 +02:00
#include "WorldSocket.h"
2009-10-17 15:51:44 -07:00
2014-07-04 15:22:06 +02:00
#define TRINITY_CORE_CONFIG "worldserver.conf"
#define WORLD_SLEEP_CONST 50
2009-10-17 15:51:44 -07:00
2010-06-23 01:47:41 +02:00
#ifdef _WIN32
2008-10-02 16:23:55 -05:00
#include "ServiceWin32.h"
2011-04-12 16:46:37 -04:00
char serviceName [] = "worldserver" ;
char serviceLongName [] = "TrinityCore world service" ;
char serviceDescription [] = "TrinityCore World of Warcraft emulator world service" ;
2008-10-02 16:23:55 -05:00
/*
* -1 - not in service mode
* 0 - stopped
* 1 - running
* 2 - paused
*/
int m_ServiceStatus = - 1 ;
#endif
2009-10-17 15:51:44 -07:00
2014-07-04 15:22:06 +02:00
boost :: asio :: io_service _ioService ;
2014-07-06 23:04:38 +02:00
boost :: asio :: deadline_timer _freezeCheckTimer ( _ioService );
uint32 _worldLoopCounter ( 0 );
uint32 _lastChangeMsTime ( 0 );
2014-07-08 21:17:24 +02:00
uint32 _maxCoreStuckTimeInMs ( 0 );
2014-07-06 23:04:38 +02:00
2010-09-02 17:47:50 +02:00
WorldDatabaseWorkerPool WorldDatabase ; ///< Accessor to the world database
CharacterDatabaseWorkerPool CharacterDatabase ; ///< Accessor to the character database
LoginDatabaseWorkerPool LoginDatabase ; ///< Accessor to the realm/login database
2008-10-02 16:23:55 -05:00
uint32 realmID ; ///< Id of the realm
2009-10-17 15:51:44 -07:00
2014-07-04 15:22:06 +02:00
void usage ( const char * prog );
void SignalHandler ( const boost :: system :: error_code & error , int signalNumber );
2014-07-06 23:04:38 +02:00
void FreezeDetectorHandler ( const boost :: system :: error_code & error );
2014-07-04 15:22:06 +02:00
AsyncAcceptor < RASession >* StartRaSocketAcceptor ( boost :: asio :: io_service & ioService );
bool StartDB ();
void StopDB ();
void WorldUpdateLoop ();
void ClearOnlineAccounts ();
2009-10-17 15:51:44 -07:00
2008-10-14 11:57:03 -05:00
/// Launch the Trinity server
2013-07-28 16:59:07 +01:00
extern int main ( int argc , char ** argv )
2008-10-02 16:23:55 -05:00
{
///- Command line parsing to get the configuration file name
2014-07-04 15:22:06 +02:00
char const * cfg_file = TRINITY_CORE_CONFIG ;
2011-04-12 16:46:37 -04:00
int c = 1 ;
2013-07-28 16:59:07 +01:00
while ( c < argc )
2008-10-02 16:23:55 -05:00
{
2013-07-28 16:59:07 +01:00
if ( ! strcmp ( argv [ c ], "-c" ))
2008-10-02 16:23:55 -05:00
{
2011-04-12 16:46:37 -04:00
if ( ++ c >= argc )
2008-10-02 16:23:55 -05:00
{
2012-08-29 20:02:55 +01:00
printf ( "Runtime-Error: -c option requires an input argument" );
2008-10-02 16:23:55 -05:00
usage ( argv [ 0 ]);
return 1 ;
}
else
cfg_file = argv [ c ];
}
2009-10-17 15:51:44 -07:00
2014-07-09 16:52:41 -05:00
#ifdef _WIN32
2013-07-28 16:59:07 +01:00
if ( strcmp ( argv [ c ], "-s" ) == 0 ) // Services
2008-10-02 16:23:55 -05:00
{
2011-04-12 16:46:37 -04:00
if ( ++ c >= argc )
2008-10-02 16:23:55 -05:00
{
2012-08-29 20:02:55 +01:00
printf ( "Runtime-Error: -s option requires an input argument" );
2008-10-02 16:23:55 -05:00
usage ( argv [ 0 ]);
return 1 ;
}
2013-07-28 16:59:07 +01:00
2011-04-12 16:46:37 -04:00
if ( strcmp ( argv [ c ], "install" ) == 0 )
2008-10-02 16:23:55 -05:00
{
if ( WinServiceInstall ())
2012-10-22 10:37:39 +02:00
printf ( "Installing service \n " );
2008-10-02 16:23:55 -05:00
return 1 ;
}
2011-04-12 16:46:37 -04:00
else if ( strcmp ( argv [ c ], "uninstall" ) == 0 )
2008-10-02 16:23:55 -05:00
{
2011-04-12 16:46:37 -04:00
if ( WinServiceUninstall ())
2012-10-22 10:37:39 +02:00
printf ( "Uninstalling service \n " );
2008-10-02 16:23:55 -05:00
return 1 ;
}
else
{
2012-08-29 20:02:55 +01:00
printf ( "Runtime-Error: unsupported option %s" , argv [ c ]);
2008-10-02 16:23:55 -05:00
usage ( argv [ 0 ]);
return 1 ;
}
}
2013-07-28 16:59:07 +01:00
2011-04-12 16:46:37 -04:00
if ( strcmp ( argv [ c ], "--service" ) == 0 )
2008-10-02 16:23:55 -05:00
WinServiceRun ();
2014-07-09 16:52:41 -05:00
#endif
2008-10-02 16:23:55 -05:00
++ c ;
}
2009-10-17 15:51:44 -07:00
2013-07-15 17:31:44 +02:00
if ( ! sConfigMgr -> LoadInitial ( cfg_file ))
2008-10-02 16:23:55 -05:00
{
2012-10-22 10:37:39 +02:00
printf ( "Invalid or missing configuration file : %s \n " , cfg_file );
printf ( "Verify that the file exists and has \' [worldserver]' written in the top of the file! \n " );
2008-10-02 16:23:55 -05:00
return 1 ;
}
2013-05-13 18:10:20 +02:00
2014-07-08 20:55:25 +02:00
if ( sConfigMgr -> GetBoolDefault ( "Log.Async.Enable" , false ))
{
// If logs are supposed to be handled async then we need to pass the io_service into the Log singleton
Log :: instance ( & _ioService );
}
2014-07-04 15:22:06 +02:00
TC_LOG_INFO ( "server.worldserver" , "%s (worldserver-daemon)" , _FULLVERSION );
TC_LOG_INFO ( "server.worldserver" , "<Ctrl-C> to stop. \n " );
TC_LOG_INFO ( "server.worldserver" , " ______ __" );
TC_LOG_INFO ( "server.worldserver" , "/ \\ __ _ \\ __ __/ \\ \\ __" );
TC_LOG_INFO ( "server.worldserver" , " \\ /_/ \\ \\ / _ __ / \\ _ \\ ___ / \\ _ \\ \\ , _ \\ __ __" );
TC_LOG_INFO ( "server.worldserver" , " \\ \\ \\ / \\ `'__ \\ / \\ \\ /' _ ` \\ / \\ \\ \\ \\ / / \\ \\ / \\ \\ " );
TC_LOG_INFO ( "server.worldserver" , " \\ \\ \\ \\ \\ / \\ \\ \\ / \\ \\ / \\ \\ \\ \\ \\ \\ _ \\ \\ \\ _ \\ \\ " );
TC_LOG_INFO ( "server.worldserver" , " \\ \\ _ \\ \\ _ \\ \\ \\ _ \\ \\ _ \\ \\ _ \\ \\ _ \\ \\ __ \\\\ /`____ \\ " );
TC_LOG_INFO ( "server.worldserver" , " \\ /_/ \\ /_/ \\ /_/ \\ /_/ \\ /_/ \\ /_/ \\ /__/ `/___/> \\ " );
TC_LOG_INFO ( "server.worldserver" , " C O R E / \\ ___/" );
TC_LOG_INFO ( "server.worldserver" , "http://TrinityCore.org \\ /__/ \n " );
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.worldserver" , "Using configuration file %s." , cfg_file );
TC_LOG_INFO ( "server.worldserver" , "Using SSL version: %s (library: %s)" , OPENSSL_VERSION_TEXT , SSLeay_version ( SSLEAY_VERSION ));
2014-07-04 15:22:06 +02:00
TC_LOG_INFO ( "server.worldserver" , "Using Boost version: %i.%i.%i" , BOOST_VERSION / 100000 , BOOST_VERSION / 100 % 1000 , BOOST_VERSION % 100 );
OpenSSLCrypto :: threadsSetup ();
2014-07-09 16:52:41 -05:00
// Seed the OpenSSL's PRNG here.
// That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login
BigNumber seed ;
seed . SetRand ( 16 * 8 );
2014-07-04 15:22:06 +02:00
/// worldserver PID file creation
std :: string pidFile = sConfigMgr -> GetStringDefault ( "PidFile" , "" );
if ( ! pidFile . empty ())
{
if ( uint32 pid = CreatePIDFile ( pidFile ))
TC_LOG_INFO ( "server.worldserver" , "Daemon PID: %u \n " , pid );
else
{
TC_LOG_ERROR ( "server.worldserver" , "Cannot create PID file %s. \n " , pidFile . c_str ());
return 1 ;
}
}
// Set signal handlers (this must be done before starting io_service threads, because otherwise they would unblock and exit)
boost :: asio :: signal_set signals ( _ioService , SIGINT , SIGTERM );
signals . async_wait ( SignalHandler );
// Start the Boost based thread pool
int numThreads = sConfigMgr -> GetIntDefault ( "ThreadPool" , 1 );
std :: vector < std :: thread > threadPool ;
if ( numThreads < 1 )
numThreads = 1 ;
for ( int i = 0 ; i < numThreads ; ++ i )
threadPool . push_back ( std :: thread ( boost :: bind ( & boost :: asio :: io_service :: run , & _ioService )));
// Set process priority according to configuration settings
SetProcessPriority ( "server.worldserver" );
// Start the databases
if ( ! StartDB ())
return 1 ;
// Set server offline (not connectable)
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'" , REALM_FLAG_OFFLINE , REALM_FLAG_INVALID , realmID );
// Initialize the World
sWorld -> SetInitialWorldSettings ();
// Launch CliRunnable thread
std :: thread * cliThread = nullptr ;
#ifdef _WIN32
if ( sConfigMgr -> GetBoolDefault ( "Console.Enable" , true ) && ( m_ServiceStatus == - 1 ) /* need disable console in service mode*/ )
#else
if ( sConfigMgr -> GetBoolDefault ( "Console.Enable" , true ))
#endif
{
cliThread = new std :: thread ( CliThread );
}
// Start the Remote Access port (acceptor) if enabled
AsyncAcceptor < RASession >* raAcceptor = nullptr ;
if ( sConfigMgr -> GetBoolDefault ( "Ra.Enable" , false ))
raAcceptor = StartRaSocketAcceptor ( _ioService );
// Start soap serving thread if enabled
std :: thread * soapThread = nullptr ;
if ( sConfigMgr -> GetBoolDefault ( "SOAP.Enabled" , false ))
{
soapThread = new std :: thread ( TCSoapThread , sConfigMgr -> GetStringDefault ( "SOAP.IP" , "127.0.0.1" ), uint16 ( sConfigMgr -> GetIntDefault ( "SOAP.Port" , 7878 )));
}
// Launch the worldserver listener socket
uint16 worldPort = uint16 ( sWorld -> getIntConfig ( CONFIG_PORT_WORLD ));
2014-07-06 01:26:29 +02:00
std :: string worldListener = sConfigMgr -> GetStringDefault ( "BindIP" , "0.0.0.0" );
2014-07-07 21:37:20 +02:00
bool tcpNoDelay = sConfigMgr -> GetBoolDefault ( "Network.TcpNodelay" , true );
2014-07-04 15:22:06 +02:00
2014-07-07 22:03:41 +02:00
AsyncAcceptor < WorldSocket > worldAcceptor ( _ioService , worldListener , worldPort , tcpNoDelay );
2014-07-06 01:26:29 +02:00
sScriptMgr -> OnStartup ();
2009-10-17 15:51:44 -07:00
2014-07-04 15:22:06 +02:00
// Set server online (allow connecting now)
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'" , REALM_FLAG_INVALID , realmID );
2014-07-06 23:04:38 +02:00
// Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec)
2014-07-08 21:17:24 +02:00
if ( int coreStuckTime = sConfigMgr -> GetIntDefault ( "MaxCoreStuckTime" , 0 ))
2014-07-06 23:04:38 +02:00
{
2014-07-08 21:17:24 +02:00
_maxCoreStuckTimeInMs = coreStuckTime * 1000 ;
2014-07-06 23:04:38 +02:00
_freezeCheckTimer . expires_from_now ( boost :: posix_time :: seconds ( 5 ));
_freezeCheckTimer . async_wait ( FreezeDetectorHandler );
2014-07-08 21:17:24 +02:00
TC_LOG_INFO ( "server.worldserver" , "Starting up anti-freeze thread (%u seconds max stuck time)..." , coreStuckTime );
2014-07-06 23:04:38 +02:00
}
2014-07-04 15:22:06 +02:00
TC_LOG_INFO ( "server.worldserver" , "%s (worldserver-daemon) ready..." , _FULLVERSION );
WorldUpdateLoop ();
// Shutdown starts here
_ioService . stop ();
for ( auto & thread : threadPool )
{
thread . join ();
}
sScriptMgr -> OnShutdown ();
sWorld -> KickAll (); // save and kick all players
sWorld -> UpdateSessions ( 1 ); // real players unload required UpdateSessions call
// unload battleground templates before different singletons destroyed
sBattlegroundMgr -> DeleteAllBattlegrounds ();
sMapMgr -> UnloadAll (); // unload all grids (including locked in memory)
sObjectAccessor -> UnloadAll (); // unload 'i_player2corpse' storage and remove from world
sScriptMgr -> Unload ();
sOutdoorPvPMgr -> Die ();
// set server offline
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag | %u WHERE id = '%d'" , REALM_FLAG_OFFLINE , realmID );
// Clean up threads if any
if ( soapThread != nullptr )
{
soapThread -> join ();
delete soapThread ;
}
if ( raAcceptor != nullptr )
delete raAcceptor ;
///- Clean database before leaving
ClearOnlineAccounts ();
StopDB ();
TC_LOG_INFO ( "server.worldserver" , "Halting process..." );
if ( cliThread != nullptr )
{
#ifdef _WIN32
// this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API)
//_exit(1);
// send keyboard input to safely unblock the CLI thread
INPUT_RECORD b [ 4 ];
HANDLE hStdIn = GetStdHandle ( STD_INPUT_HANDLE );
b [ 0 ]. EventType = KEY_EVENT ;
b [ 0 ]. Event . KeyEvent . bKeyDown = TRUE ;
b [ 0 ]. Event . KeyEvent . uChar . AsciiChar = 'X' ;
b [ 0 ]. Event . KeyEvent . wVirtualKeyCode = 'X' ;
b [ 0 ]. Event . KeyEvent . wRepeatCount = 1 ;
b [ 1 ]. EventType = KEY_EVENT ;
b [ 1 ]. Event . KeyEvent . bKeyDown = FALSE ;
b [ 1 ]. Event . KeyEvent . uChar . AsciiChar = 'X' ;
b [ 1 ]. Event . KeyEvent . wVirtualKeyCode = 'X' ;
b [ 1 ]. Event . KeyEvent . wRepeatCount = 1 ;
b [ 2 ]. EventType = KEY_EVENT ;
b [ 2 ]. Event . KeyEvent . bKeyDown = TRUE ;
b [ 2 ]. Event . KeyEvent . dwControlKeyState = 0 ;
b [ 2 ]. Event . KeyEvent . uChar . AsciiChar = '\r' ;
b [ 2 ]. Event . KeyEvent . wVirtualKeyCode = VK_RETURN ;
b [ 2 ]. Event . KeyEvent . wRepeatCount = 1 ;
b [ 2 ]. Event . KeyEvent . wVirtualScanCode = 0x1c ;
b [ 3 ]. EventType = KEY_EVENT ;
b [ 3 ]. Event . KeyEvent . bKeyDown = FALSE ;
b [ 3 ]. Event . KeyEvent . dwControlKeyState = 0 ;
b [ 3 ]. Event . KeyEvent . uChar . AsciiChar = '\r' ;
b [ 3 ]. Event . KeyEvent . wVirtualKeyCode = VK_RETURN ;
b [ 3 ]. Event . KeyEvent . wVirtualScanCode = 0x1c ;
b [ 3 ]. Event . KeyEvent . wRepeatCount = 1 ;
DWORD numb ;
WriteConsoleInput ( hStdIn , b , 4 , & numb );
cliThread -> join ();
#endif
delete cliThread ;
}
OpenSSLCrypto :: threadsCleanup ();
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
// 0 - normal shutdown
// 1 - shutdown at error
2008-10-14 11:57:03 -05:00
// 2 - restart command used, this code can be used by restarter for restart Trinityd
2010-08-23 18:15:01 +02:00
2014-07-04 15:22:06 +02:00
return World :: GetExitCode ();
}
void WorldUpdateLoop ()
{
uint32 realCurrTime = 0 ;
uint32 realPrevTime = getMSTime ();
uint32 prevSleepTime = 0 ; // used for balanced full tick time length near WORLD_SLEEP_CONST
///- While we have not World::m_stopEvent, update the world
while ( ! World :: IsStopped ())
{
++ World :: m_worldLoopCounter ;
realCurrTime = getMSTime ();
uint32 diff = getMSTimeDiff ( realPrevTime , realCurrTime );
sWorld -> Update ( diff );
realPrevTime = realCurrTime ;
// diff (D0) include time of previous sleep (d0) + tick time (t0)
// we want that next d1 + t1 == WORLD_SLEEP_CONST
// we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement
// d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0
if ( diff <= WORLD_SLEEP_CONST + prevSleepTime )
{
prevSleepTime = WORLD_SLEEP_CONST + prevSleepTime - diff ;
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( prevSleepTime ));
}
else
prevSleepTime = 0 ;
#ifdef _WIN32
if ( m_ServiceStatus == 0 )
World :: StopNow ( SHUTDOWN_EXIT_CODE );
while ( m_ServiceStatus == 2 )
Sleep ( 1000 );
#endif
}
}
/// Print out the usage string for this program on the console.
void usage ( const char * prog )
{
printf ( "Usage: \n " );
printf ( " %s [<options>] \n " , prog );
printf ( " -c config_file use config_file as configuration file \n " );
#ifdef _WIN32
printf ( " Running as service functions: \n " );
printf ( " --service run as service \n " );
printf ( " -s install install service \n " );
printf ( " -s uninstall uninstall service \n " );
#endif
}
void SignalHandler ( const boost :: system :: error_code & error , int signalNumber )
{
if ( ! error )
{
switch ( signalNumber )
{
case SIGINT :
case SIGTERM :
World :: StopNow ( SHUTDOWN_EXIT_CODE );
break ;
}
}
}
2014-07-06 23:04:38 +02:00
void FreezeDetectorHandler ( const boost :: system :: error_code & error )
2014-07-04 15:22:06 +02:00
{
2014-07-06 23:04:38 +02:00
if ( ! error )
2014-07-04 15:22:06 +02:00
{
uint32 curtime = getMSTime ();
2014-07-06 23:04:38 +02:00
2014-07-04 15:22:06 +02:00
uint32 worldLoopCounter = World :: m_worldLoopCounter ;
2014-07-06 23:04:38 +02:00
if ( _worldLoopCounter != worldLoopCounter )
2014-07-04 15:22:06 +02:00
{
2014-07-06 23:04:38 +02:00
_lastChangeMsTime = curtime ;
_worldLoopCounter = worldLoopCounter ;
2014-07-04 15:22:06 +02:00
}
// possible freeze
2014-07-08 21:17:24 +02:00
else if ( getMSTimeDiff ( _lastChangeMsTime , curtime ) > _maxCoreStuckTimeInMs )
2014-07-04 15:22:06 +02:00
{
TC_LOG_ERROR ( "server.worldserver" , "World Thread hangs, kicking out server!" );
ASSERT ( false );
}
2014-07-06 23:04:38 +02:00
_freezeCheckTimer . expires_from_now ( boost :: posix_time :: seconds ( 1 ));
_freezeCheckTimer . async_wait ( FreezeDetectorHandler );
2014-07-04 15:22:06 +02:00
}
}
AsyncAcceptor < RASession >* StartRaSocketAcceptor ( boost :: asio :: io_service & ioService )
{
uint16 raPort = uint16 ( sConfigMgr -> GetIntDefault ( "Ra.Port" , 3443 ));
std :: string raListener = sConfigMgr -> GetStringDefault ( "Ra.IP" , "0.0.0.0" );
return new AsyncAcceptor < RASession > ( ioService , raListener , raPort );
}
/// Initialize connection to the databases
bool StartDB ()
{
MySQL :: Library_Init ();
std :: string dbString ;
uint8 asyncThreads , synchThreads ;
dbString = sConfigMgr -> GetStringDefault ( "WorldDatabaseInfo" , "" );
if ( dbString . empty ())
{
TC_LOG_ERROR ( "server.worldserver" , "World database not specified in configuration file" );
return false ;
}
asyncThreads = uint8 ( sConfigMgr -> GetIntDefault ( "WorldDatabase.WorkerThreads" , 1 ));
if ( asyncThreads < 1 || asyncThreads > 32 )
{
TC_LOG_ERROR ( "server.worldserver" , "World database: invalid number of worker threads specified. "
"Please pick a value between 1 and 32." );
return false ;
}
synchThreads = uint8 ( sConfigMgr -> GetIntDefault ( "WorldDatabase.SynchThreads" , 1 ));
///- Initialize the world database
if ( ! WorldDatabase . Open ( dbString , asyncThreads , synchThreads ))
{
TC_LOG_ERROR ( "server.worldserver" , "Cannot connect to world database %s" , dbString . c_str ());
return false ;
}
///- Get character database info from configuration file
dbString = sConfigMgr -> GetStringDefault ( "CharacterDatabaseInfo" , "" );
if ( dbString . empty ())
{
TC_LOG_ERROR ( "server.worldserver" , "Character database not specified in configuration file" );
return false ;
}
asyncThreads = uint8 ( sConfigMgr -> GetIntDefault ( "CharacterDatabase.WorkerThreads" , 1 ));
if ( asyncThreads < 1 || asyncThreads > 32 )
{
TC_LOG_ERROR ( "server.worldserver" , "Character database: invalid number of worker threads specified. "
"Please pick a value between 1 and 32." );
return false ;
}
synchThreads = uint8 ( sConfigMgr -> GetIntDefault ( "CharacterDatabase.SynchThreads" , 2 ));
///- Initialize the Character database
if ( ! CharacterDatabase . Open ( dbString , asyncThreads , synchThreads ))
{
TC_LOG_ERROR ( "server.worldserver" , "Cannot connect to Character database %s" , dbString . c_str ());
return false ;
}
///- Get login database info from configuration file
dbString = sConfigMgr -> GetStringDefault ( "LoginDatabaseInfo" , "" );
if ( dbString . empty ())
{
TC_LOG_ERROR ( "server.worldserver" , "Login database not specified in configuration file" );
return false ;
}
asyncThreads = uint8 ( sConfigMgr -> GetIntDefault ( "LoginDatabase.WorkerThreads" , 1 ));
if ( asyncThreads < 1 || asyncThreads > 32 )
{
TC_LOG_ERROR ( "server.worldserver" , "Login database: invalid number of worker threads specified. "
"Please pick a value between 1 and 32." );
return false ;
}
synchThreads = uint8 ( sConfigMgr -> GetIntDefault ( "LoginDatabase.SynchThreads" , 1 ));
///- Initialise the login database
if ( ! LoginDatabase . Open ( dbString , asyncThreads , synchThreads ))
{
TC_LOG_ERROR ( "server.worldserver" , "Cannot connect to login database %s" , dbString . c_str ());
return false ;
}
///- Get the realm Id from the configuration file
realmID = sConfigMgr -> GetIntDefault ( "RealmID" , 0 );
if ( ! realmID )
{
TC_LOG_ERROR ( "server.worldserver" , "Realm ID not defined in configuration file" );
return false ;
}
TC_LOG_INFO ( "server.worldserver" , "Realm running as realm ID %d" , realmID );
///- Clean the database before starting
ClearOnlineAccounts ();
///- Insert version info into DB
WorldDatabase . PExecute ( "UPDATE version SET core_version = '%s', core_revision = '%s'" , _FULLVERSION , _HASH ); // One-time query
sWorld -> LoadDBVersion ();
TC_LOG_INFO ( "server.worldserver" , "Using World DB: %s" , sWorld -> GetDBVersion ());
return true ;
}
void StopDB ()
{
CharacterDatabase . Close ();
WorldDatabase . Close ();
LoginDatabase . Close ();
MySQL :: Library_End ();
}
/// Clear 'online' status for all accounts with characters in this realm
void ClearOnlineAccounts ()
{
// Reset online status for all accounts with characters on the current realm
LoginDatabase . DirectPExecute ( "UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = %d)" , realmID );
// Reset online status for all characters
CharacterDatabase . DirectExecute ( "UPDATE characters SET online = 0 WHERE online <> 0" );
// Battleground instance ids reset at server restart
CharacterDatabase . DirectExecute ( "UPDATE character_battleground_data SET instanceId = 0" );
2008-10-02 16:23:55 -05:00
}
2009-10-17 15:51:44 -07:00
2008-10-02 16:23:55 -05:00
/// @}