2010-10-07 15:35:36 +02:00
/*
2020-01-02 06:44:10 +01:00
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
2010-10-07 15:35:36 +02:00
*
* 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
2008-10-02 16:23:55 -05:00
#include "Common.h"
2017-05-12 18:49:51 +02:00
#include "AppenderDB.h"
2014-07-04 15:22:06 +02:00
#include "AsyncAcceptor.h"
2017-05-12 18:49:51 +02:00
#include "Banner.h"
#include "BattlegroundMgr.h"
2014-07-04 15:22:06 +02:00
#include "BigNumber.h"
2017-05-12 18:49:51 +02:00
#include "CliRunnable.h"
#include "Configuration/Config.h"
#include "DatabaseEnv.h"
#include "DatabaseLoader.h"
2019-04-14 00:13:23 +02:00
#include "DeadlineTimer.h"
2017-05-12 18:49:51 +02:00
#include "GitRevision.h"
2014-07-28 23:32:53 +02:00
#include "InstanceSaveMgr.h"
2018-01-06 01:21:59 +01:00
#include "IoContext.h"
2017-05-12 18:49:51 +02:00
#include "MapManager.h"
#include "Metric.h"
#include "MySQLThreading.h"
2014-07-04 15:22:06 +02:00
#include "ObjectAccessor.h"
2017-05-12 18:49:51 +02:00
#include "OpenSSLCrypto.h"
#include "OutdoorPvP/OutdoorPvPMgr.h"
#include "ProcessPriority.h"
#include "RASession.h"
#include "RealmList.h"
#include "ScriptLoader.h"
2014-07-04 15:22:06 +02:00
#include "ScriptMgr.h"
2015-11-04 21:20:07 +01:00
#include "ScriptReloadMgr.h"
2014-07-04 15:22:06 +02:00
#include "TCSoap.h"
2017-05-12 18:49:51 +02:00
#include "World.h"
2014-07-07 22:03:41 +02:00
#include "WorldSocket.h"
2014-09-09 19:19:25 +02:00
#include "WorldSocketMgr.h"
2014-10-17 22:48:06 +02:00
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
2017-05-13 11:49:09 +02:00
#include <boost/asio/signal_set.hpp>
2017-05-12 18:49:51 +02:00
#include <boost/filesystem/operations.hpp>
2014-10-17 22:48:06 +02:00
#include <boost/program_options.hpp>
2016-03-28 17:12:57 +02:00
#include <google/protobuf/stubs/common.h>
2017-05-22 00:02:06 +02:00
#include <iostream>
2017-05-13 11:49:09 +02:00
#include <csignal>
2009-10-17 15:51:44 -07:00
2021-05-24 00:08:03 +02:00
#include "Hacks/boost_program_options_with_filesystem_path.h"
2014-07-15 17:46:09 +02:00
using namespace boost :: program_options ;
2016-04-18 20:29:42 +02:00
namespace fs = boost :: filesystem ;
2014-07-15 17:46:09 +02:00
2014-07-13 15:55:46 -05:00
#ifndef _TRINITY_CORE_CONFIG
#define _TRINITY_CORE_CONFIG "worldserver.conf"
#endif
2014-07-04 15:22:06 +02:00
#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
2017-05-04 20:41:22 +02:00
class FreezeDetector
{
public :
2018-01-06 01:21:59 +01:00
FreezeDetector ( Trinity :: Asio :: IoContext & ioContext , uint32 maxCoreStuckTime )
2018-05-12 13:59:34 +02:00
: _timer ( ioContext ), _worldLoopCounter ( 0 ), _lastChangeMsTime ( getMSTime ()), _maxCoreStuckTimeInMs ( maxCoreStuckTime ) { }
2017-05-04 20:41:22 +02:00
static void Start ( std :: shared_ptr < FreezeDetector > const & freezeDetector )
{
freezeDetector -> _timer . expires_from_now ( boost :: posix_time :: seconds ( 5 ));
freezeDetector -> _timer . async_wait ( std :: bind ( & FreezeDetector :: Handler , std :: weak_ptr < FreezeDetector > ( freezeDetector ), std :: placeholders :: _1 ));
}
static void Handler ( std :: weak_ptr < FreezeDetector > freezeDetectorRef , boost :: system :: error_code const & error );
private :
2019-04-14 00:13:23 +02:00
Trinity :: Asio :: DeadlineTimer _timer ;
2017-05-04 20:41:22 +02:00
uint32 _worldLoopCounter ;
uint32 _lastChangeMsTime ;
uint32 _maxCoreStuckTimeInMs ;
};
void SignalHandler ( boost :: system :: error_code const & error , int signalNumber );
2018-01-06 01:21:59 +01:00
AsyncAcceptor * StartRaSocketAcceptor ( Trinity :: Asio :: IoContext & ioContext );
2014-07-04 15:22:06 +02:00
bool StartDB ();
void StopDB ();
void WorldUpdateLoop ();
void ClearOnlineAccounts ();
2014-10-03 21:21:17 +02:00
void ShutdownCLIThread ( std :: thread * cliThread );
2017-05-13 11:49:09 +02:00
bool LoadRealmInfo ();
2016-04-18 20:29:42 +02:00
variables_map GetConsoleArguments ( int argc , char ** argv , fs :: path & configFile , std :: string & cfg_service );
2009-10-17 15:51:44 -07:00
2015-03-11 21:07:08 +01:00
/// Launch the Trinity server
extern int main ( int argc , char ** argv )
2008-10-02 16:23:55 -05:00
{
2016-03-03 19:58:28 +01:00
signal ( SIGABRT , & Trinity :: AbortHandler );
2016-03-03 17:29:48 +01:00
2016-04-18 20:29:42 +02:00
auto configFile = fs :: absolute ( _TRINITY_CORE_CONFIG );
2014-07-15 17:46:09 +02:00
std :: string configService ;
2009-10-17 15:51:44 -07:00
2014-07-15 17:46:09 +02:00
auto vm = GetConsoleArguments ( argc , argv , configFile , configService );
2015-07-04 00:45:52 +01:00
// exit if help or version is enabled
if ( vm . count ( "help" ) || vm . count ( "version" ))
2014-07-15 17:46:09 +02:00
return 0 ;
2013-07-28 16:59:07 +01:00
2016-03-28 17:12:57 +02:00
GOOGLE_PROTOBUF_VERIFY_VERSION ;
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > protobufHandle ( nullptr , []( void * ) { google :: protobuf :: ShutdownProtobufLibrary (); });
2014-07-16 19:27:15 +02:00
#ifdef _WIN32
if ( configService . compare ( "install" ) == 0 )
2015-06-10 00:21:55 +02:00
return WinServiceInstall () ? 0 : 1 ;
2014-07-16 19:27:15 +02:00
else if ( configService . compare ( "uninstall" ) == 0 )
2015-06-10 00:21:55 +02:00
return WinServiceUninstall () ? 0 : 1 ;
2014-07-16 19:27:15 +02:00
else if ( configService . compare ( "run" ) == 0 )
2015-06-10 00:21:55 +02:00
return WinServiceRun () ? 0 : 0 ;
2014-07-16 19:27:15 +02:00
#endif
2009-10-17 15:51:44 -07:00
2014-07-29 01:47:00 +01:00
std :: string configError ;
2016-04-18 20:29:42 +02:00
if ( ! sConfigMgr -> LoadInitial ( configFile . generic_string (),
std :: vector < std :: string > ( argv , argv + argc ),
configError ))
2008-10-02 16:23:55 -05:00
{
2014-07-29 01:47:00 +01:00
printf ( "Error in config file: %s \n " , configError . c_str ());
2008-10-02 16:23:55 -05:00
return 1 ;
}
2013-05-13 18:10:20 +02:00
2018-01-06 01:21:59 +01:00
std :: shared_ptr < Trinity :: Asio :: IoContext > ioContext = std :: make_shared < Trinity :: Asio :: IoContext > ();
2017-05-04 20:41:22 +02:00
2015-08-16 00:12:18 +02:00
sLog -> RegisterAppender < AppenderDB > ();
2018-01-06 01:21:59 +01:00
// If logs are supposed to be handled async then we need to pass the IoContext into the Log singleton
sLog -> Initialize ( sConfigMgr -> GetBoolDefault ( "Log.Async.Enable" , false ) ? ioContext . get () : nullptr );
2014-07-08 20:55:25 +02:00
2016-07-17 17:24:53 +02:00
Trinity :: Banner :: Show ( "worldserver-daemon" ,
[]( char const * text )
{
TC_LOG_INFO ( "server.worldserver" , "%s" , text );
},
[]()
{
TC_LOG_INFO ( "server.worldserver" , "Using configuration file %s." , sConfigMgr -> GetFilename (). c_str ());
TC_LOG_INFO ( "server.worldserver" , "Using SSL version: %s (library: %s)" , OPENSSL_VERSION_TEXT , SSLeay_version ( SSLEAY_VERSION ));
TC_LOG_INFO ( "server.worldserver" , "Using Boost version: %i.%i.%i" , BOOST_VERSION / 100000 , BOOST_VERSION / 100 % 1000 , BOOST_VERSION % 100 );
}
);
2014-07-04 15:22:06 +02:00
OpenSSLCrypto :: threadsSetup ();
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > opensslHandle ( nullptr , []( void * ) { OpenSSLCrypto :: threadsCleanup (); });
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 ;
}
}
2018-01-06 01:21:59 +01:00
// Set signal handlers (this must be done before starting IoContext threads, because otherwise they would unblock and exit)
boost :: asio :: signal_set signals ( * ioContext , SIGINT , SIGTERM );
2017-03-04 18:44:13 +01:00
#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
2014-07-23 19:04:07 +02:00
signals . add ( SIGBREAK );
#endif
2014-07-04 15:22:06 +02:00
signals . async_wait ( SignalHandler );
// Start the Boost based thread pool
int numThreads = sConfigMgr -> GetIntDefault ( "ThreadPool" , 1 );
2018-01-06 01:21:59 +01:00
std :: shared_ptr < std :: vector < std :: thread >> threadPool ( new std :: vector < std :: thread > (), [ ioContext ]( std :: vector < std :: thread >* del )
2017-05-04 20:41:22 +02:00
{
2018-01-06 01:21:59 +01:00
ioContext -> stop ();
2017-05-04 20:41:22 +02:00
for ( std :: thread & thr : * del )
thr . join ();
delete del ;
});
2014-07-04 15:22:06 +02:00
if ( numThreads < 1 )
numThreads = 1 ;
for ( int i = 0 ; i < numThreads ; ++ i )
2018-01-06 01:21:59 +01:00
threadPool -> push_back ( std :: thread ([ ioContext ]() { ioContext -> run (); }));
2014-07-04 15:22:06 +02:00
// Set process priority according to configuration settings
2017-05-11 22:29:51 +02:00
SetProcessPriority ( "server.worldserver" , sConfigMgr -> GetIntDefault ( CONFIG_PROCESSOR_AFFINITY , 0 ), sConfigMgr -> GetBoolDefault ( CONFIG_HIGH_PRIORITY , false ));
2014-07-04 15:22:06 +02:00
// Start the databases
if ( ! StartDB ())
return 1 ;
2017-05-04 20:41:22 +02:00
2019-03-17 21:34:13 +01:00
if ( vm . count ( "update-databases-only" ))
return 0 ;
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > dbHandle ( nullptr , []( void * ) { StopDB (); });
2014-07-04 15:22:06 +02:00
// Set server offline (not connectable)
2015-10-06 00:30:47 +02:00
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag | %u WHERE id = '%d'" , REALM_FLAG_OFFLINE , realm . Id . Realm );
2014-07-04 15:22:06 +02:00
2018-01-06 01:21:59 +01:00
sRealmList -> Initialize ( * ioContext , sConfigMgr -> GetIntDefault ( "RealmsStateUpdateDelay" , 10 ));
2016-03-28 17:12:57 +02:00
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > sRealmListHandle ( nullptr , []( void * ) { sRealmList -> Close (); });
2014-11-09 00:37:33 +01:00
2017-05-13 11:49:09 +02:00
LoadRealmInfo ();
2017-05-04 20:41:22 +02:00
2018-01-06 01:21:59 +01:00
sMetric -> Initialize ( realm . Name , * ioContext , []()
2016-06-04 11:26:57 +01:00
{
TC_METRIC_VALUE ( "online_players" , sWorld -> GetPlayerCount ());
});
TC_METRIC_EVENT ( "events" , "Worldserver started" , "" );
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > sMetricHandle ( nullptr , []( void * )
{
TC_METRIC_EVENT ( "events" , "Worldserver shutdown" , "" );
2017-12-17 14:35:18 +01:00
sMetric -> Unload ();
2017-05-04 20:41:22 +02:00
});
2016-03-16 15:34:25 +01:00
sScriptMgr -> SetScriptLoader ( AddScripts );
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > sScriptMgrHandle ( nullptr , []( void * )
{
sScriptMgr -> Unload ();
sScriptReloadMgr -> Unload ();
});
// Initialize the World
2014-07-04 15:22:06 +02:00
sWorld -> SetInitialWorldSettings ();
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > mapManagementHandle ( nullptr , []( void * )
2014-07-04 15:22:06 +02:00
{
2017-05-04 20:41:22 +02:00
// unload battleground templates before different singletons destroyed
sBattlegroundMgr -> DeleteAllBattlegrounds ();
sInstanceSaveMgr -> Unload ();
sOutdoorPvPMgr -> Die (); // unload it before MapManager
sMapMgr -> UnloadAll (); // unload all grids (including locked in memory)
});
2014-07-04 15:22:06 +02:00
// Start the Remote Access port (acceptor) if enabled
2017-05-04 20:41:22 +02:00
std :: unique_ptr < AsyncAcceptor > raAcceptor ;
2014-07-04 15:22:06 +02:00
if ( sConfigMgr -> GetBoolDefault ( "Ra.Enable" , false ))
2018-01-06 01:21:59 +01:00
raAcceptor . reset ( StartRaSocketAcceptor ( * ioContext ));
2014-07-04 15:22:06 +02:00
2019-01-18 22:57:23 +01:00
// Start soap serving thread if enabled
std :: shared_ptr < std :: thread > soapThread ;
if ( sConfigMgr -> GetBoolDefault ( "SOAP.Enabled" , false ))
{
soapThread . reset ( new std :: thread ( TCSoapThread , sConfigMgr -> GetStringDefault ( "SOAP.IP" , "127.0.0.1" ), uint16 ( sConfigMgr -> GetIntDefault ( "SOAP.Port" , 7878 ))),
[]( std :: thread * thr )
{
thr -> join ();
delete thr ;
});
}
2014-07-04 15:22:06 +02:00
// Launch the worldserver listener socket
uint16 worldPort = uint16 ( sWorld -> getIntConfig ( CONFIG_PORT_WORLD ));
2017-05-21 23:18:43 +02:00
uint16 instancePort = uint16 ( sWorld -> getIntConfig ( CONFIG_PORT_INSTANCE ));
2014-07-06 01:26:29 +02:00
std :: string worldListener = sConfigMgr -> GetStringDefault ( "BindIP" , "0.0.0.0" );
2014-07-04 15:22:06 +02:00
2016-03-28 02:02:29 +01:00
int networkThreads = sConfigMgr -> GetIntDefault ( "Network.Threads" , 1 );
if ( networkThreads <= 0 )
{
2016-03-31 03:19:13 +01:00
TC_LOG_ERROR ( "server.worldserver" , "Network.Threads must be greater than 0" );
2019-01-18 22:57:23 +01:00
World :: StopNow ( ERROR_EXIT_CODE );
2017-05-04 20:41:22 +02:00
return 1 ;
}
2018-01-06 01:21:59 +01:00
if ( ! sWorldSocketMgr . StartWorldNetwork ( * ioContext , worldListener , worldPort , instancePort , networkThreads ))
2017-05-04 20:41:22 +02:00
{
TC_LOG_ERROR ( "server.worldserver" , "Failed to initialize network" );
2019-01-18 22:57:23 +01:00
World :: StopNow ( ERROR_EXIT_CODE );
2017-05-04 20:41:22 +02:00
return 1 ;
2016-03-28 02:02:29 +01:00
}
2017-05-04 20:41:22 +02:00
std :: shared_ptr < void > sWorldSocketMgrHandle ( nullptr , []( void * )
{
sWorld -> KickAll (); // save and kick all players
sWorld -> UpdateSessions ( 1 ); // real players unload required UpdateSessions call
sWorldSocketMgr . StopNetwork ();
///- Clean database before leaving
ClearOnlineAccounts ();
});
// Launch CliRunnable thread
std :: shared_ptr < std :: thread > cliThread ;
#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 . reset ( new std :: thread ( CliThread ), & ShutdownCLIThread );
}
2009-10-17 15:51:44 -07:00
2014-07-04 15:22:06 +02:00
// Set server online (allow connecting now)
2015-10-06 00:30:47 +02:00
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'" , REALM_FLAG_OFFLINE , realm . Id . Realm );
2014-11-09 00:37:33 +01:00
realm . PopulationLevel = 0.0f ;
2015-10-06 00:30:47 +02:00
realm . Flags = RealmFlags ( realm . Flags & ~ uint32 ( REALM_FLAG_OFFLINE ));
2014-07-04 15:22:06 +02:00
2014-07-06 23:04:38 +02:00
// Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec)
2017-05-04 20:41:22 +02:00
std :: shared_ptr < FreezeDetector > freezeDetector ;
2014-07-08 21:17:24 +02:00
if ( int coreStuckTime = sConfigMgr -> GetIntDefault ( "MaxCoreStuckTime" , 0 ))
2014-07-06 23:04:38 +02:00
{
2018-01-06 01:21:59 +01:00
freezeDetector = std :: make_shared < FreezeDetector > ( * ioContext , coreStuckTime * 1000 );
2017-05-13 19:37:33 +02:00
FreezeDetector :: Start ( freezeDetector );
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-08-11 17:28:10 +02:00
sScriptMgr -> OnStartup ();
2020-09-05 10:28:02 +02:00
2020-09-05 10:25:09 +02:00
TC_LOG_INFO ( "server.worldserver" , "%s (worldserver-daemon) ready..." , GitRevision :: GetFullVersion ());
2014-08-11 17:28:10 +02:00
2014-07-04 15:22:06 +02:00
WorldUpdateLoop ();
// Shutdown starts here
2017-05-04 20:41:22 +02:00
threadPool . reset ();
2014-07-04 15:22:06 +02:00
2016-02-23 23:41:18 +01:00
sLog -> SetSynchronous ();
2014-07-04 15:22:06 +02:00
sScriptMgr -> OnShutdown ();
// set server offline
2015-10-06 00:30:47 +02:00
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag | %u WHERE id = '%d'" , REALM_FLAG_OFFLINE , realm . Id . Realm );
2016-06-04 11:26:57 +01:00
2014-07-04 15:22:06 +02:00
TC_LOG_INFO ( "server.worldserver" , "Halting process..." );
2014-10-03 21:21:17 +02:00
// 0 - normal shutdown
// 1 - shutdown at error
// 2 - restart command used, this code can be used by restarter for restart Trinityd
return World :: GetExitCode ();
}
void ShutdownCLIThread ( std :: thread * cliThread )
{
2014-07-04 15:22:06 +02:00
if ( cliThread != nullptr )
{
#ifdef _WIN32
2014-10-03 21:21:17 +02:00
// First try to cancel any I/O in the CLI thread
2014-09-28 13:49:44 +02:00
if ( ! CancelSynchronousIo ( cliThread -> native_handle ()))
{
2014-10-03 21:21:17 +02:00
// if CancelSynchronousIo() fails, print the error and try with old way
2014-09-28 13:49:44 +02:00
DWORD errorCode = GetLastError ();
2017-08-25 23:51:46 +02:00
// if CancelSynchronousIo fails with ERROR_NOT_FOUND then there was nothing to cancel, proceed with shutdown
if ( errorCode != ERROR_NOT_FOUND )
{
LPSTR errorBuffer ;
DWORD numCharsWritten = FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS ,
nullptr , errorCode , 0 , ( LPTSTR ) & errorBuffer , 0 , nullptr );
if ( ! numCharsWritten )
errorBuffer = "Unknown error" ;
TC_LOG_DEBUG ( "server.worldserver" , "Error cancelling I/O of CliThread, error code %u, detail: %s" , uint32 ( errorCode ), errorBuffer );
if ( numCharsWritten )
LocalFree ( errorBuffer );
// 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 );
}
2014-09-28 13:49:44 +02:00
}
2014-07-04 15:22:06 +02:00
#endif
2014-07-17 19:55:55 +02:00
cliThread -> join ();
2014-07-04 15:22:06 +02:00
delete cliThread ;
}
}
void WorldUpdateLoop ()
{
uint32 realCurrTime = 0 ;
uint32 realPrevTime = getMSTime ();
///- 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 ;
2016-12-31 20:50:39 +01:00
uint32 executionTimeDiff = getMSTimeDiff ( realCurrTime , getMSTime ());
2014-07-04 15:22:06 +02:00
2016-12-31 20:50:39 +01:00
// we know exactly how long it took to update the world, if the update took less than WORLD_SLEEP_CONST, sleep for WORLD_SLEEP_CONST - world update time
if ( executionTimeDiff < WORLD_SLEEP_CONST )
std :: this_thread :: sleep_for ( std :: chrono :: milliseconds ( WORLD_SLEEP_CONST - executionTimeDiff ));
2014-07-04 15:22:06 +02:00
#ifdef _WIN32
if ( m_ServiceStatus == 0 )
World :: StopNow ( SHUTDOWN_EXIT_CODE );
while ( m_ServiceStatus == 2 )
Sleep ( 1000 );
#endif
}
}
2017-05-04 20:41:22 +02:00
void SignalHandler ( boost :: system :: error_code const & error , int /*signalNumber*/ )
2014-07-04 15:22:06 +02:00
{
if ( ! error )
2014-07-23 19:04:07 +02:00
World :: StopNow ( SHUTDOWN_EXIT_CODE );
2014-07-04 15:22:06 +02:00
}
2017-05-04 20:41:22 +02:00
void FreezeDetector :: Handler ( std :: weak_ptr < FreezeDetector > freezeDetectorRef , boost :: system :: error_code const & 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
{
2017-05-04 20:41:22 +02:00
if ( std :: shared_ptr < FreezeDetector > freezeDetector = freezeDetectorRef . lock ())
2014-07-04 15:22:06 +02:00
{
2017-05-04 20:41:22 +02:00
uint32 curtime = getMSTime ();
uint32 worldLoopCounter = World :: m_worldLoopCounter ;
if ( freezeDetector -> _worldLoopCounter != worldLoopCounter )
{
freezeDetector -> _lastChangeMsTime = curtime ;
freezeDetector -> _worldLoopCounter = worldLoopCounter ;
}
// possible freeze
else if ( getMSTimeDiff ( freezeDetector -> _lastChangeMsTime , curtime ) > freezeDetector -> _maxCoreStuckTimeInMs )
{
TC_LOG_ERROR ( "server.worldserver" , "World Thread hangs, kicking out server!" );
ABORT ();
}
freezeDetector -> _timer . expires_from_now ( boost :: posix_time :: seconds ( 1 ));
freezeDetector -> _timer . async_wait ( std :: bind ( & FreezeDetector :: Handler , freezeDetectorRef , std :: placeholders :: _1 ));
2014-07-04 15:22:06 +02:00
}
}
}
2018-01-06 01:21:59 +01:00
AsyncAcceptor * StartRaSocketAcceptor ( Trinity :: Asio :: IoContext & ioContext )
2014-07-04 15:22:06 +02:00
{
uint16 raPort = uint16 ( sConfigMgr -> GetIntDefault ( "Ra.Port" , 3443 ));
std :: string raListener = sConfigMgr -> GetStringDefault ( "Ra.IP" , "0.0.0.0" );
2018-01-06 01:21:59 +01:00
AsyncAcceptor * acceptor = new AsyncAcceptor ( ioContext , raListener , raPort );
2017-05-04 20:41:22 +02:00
if ( ! acceptor -> Bind ())
{
TC_LOG_ERROR ( "server.worldserver" , "Failed to bind RA socket acceptor" );
delete acceptor ;
return nullptr ;
}
2014-09-09 19:19:25 +02:00
acceptor -> AsyncAccept < RASession > ();
return acceptor ;
2014-07-04 15:22:06 +02:00
}
2017-05-13 11:49:09 +02:00
bool LoadRealmInfo ()
2014-11-09 00:37:33 +01:00
{
2017-05-13 11:49:09 +02:00
if ( Realm const * realmListRealm = sRealmList -> GetRealm ( realm . Id ))
2014-11-09 00:37:33 +01:00
{
2017-05-13 11:49:09 +02:00
realm . Id = realmListRealm -> Id ;
realm . Build = realmListRealm -> Build ;
2020-06-23 08:54:12 +02:00
realm . ExternalAddress = std :: make_unique < boost :: asio :: ip :: address > ( * realmListRealm -> ExternalAddress );
realm . LocalAddress = std :: make_unique < boost :: asio :: ip :: address > ( * realmListRealm -> LocalAddress );
realm . LocalSubnetMask = std :: make_unique < boost :: asio :: ip :: address > ( * realmListRealm -> LocalSubnetMask );
2017-05-13 11:49:09 +02:00
realm . Port = realmListRealm -> Port ;
realm . Name = realmListRealm -> Name ;
2017-05-13 17:25:21 +02:00
realm . NormalizedName = realmListRealm -> NormalizedName ;
2017-05-13 11:49:09 +02:00
realm . Type = realmListRealm -> Type ;
realm . Flags = realmListRealm -> Flags ;
realm . Timezone = realmListRealm -> Timezone ;
realm . AllowedSecurityLevel = realmListRealm -> AllowedSecurityLevel ;
realm . PopulationLevel = realmListRealm -> PopulationLevel ;
return true ;
2014-11-09 00:37:33 +01:00
}
2017-05-13 11:49:09 +02:00
return false ;
2014-11-09 00:37:33 +01:00
}
2014-07-04 15:22:06 +02:00
/// Initialize connection to the databases
bool StartDB ()
{
MySQL :: Library_Init ();
2015-03-21 00:25:21 +01:00
// Load databases
2015-03-21 21:06:03 +01:00
DatabaseLoader loader ( "server.worldserver" , DatabaseLoader :: DATABASE_NONE );
2015-03-21 00:25:21 +01:00
loader
2016-03-03 01:27:02 +01:00
. AddDatabase ( LoginDatabase , "Login" )
2015-03-21 00:25:21 +01:00
. AddDatabase ( CharacterDatabase , "Character" )
2016-03-03 01:27:02 +01:00
. AddDatabase ( WorldDatabase , "World" )
. AddDatabase ( HotfixDatabase , "Hotfix" );
2014-07-04 15:22:06 +02:00
2015-03-21 00:25:21 +01:00
if ( ! loader . Load ())
2014-07-04 15:22:06 +02:00
return false ;
///- Get the realm Id from the configuration file
2015-10-06 00:30:47 +02:00
realm . Id . Realm = sConfigMgr -> GetIntDefault ( "RealmID" , 0 );
if ( ! realm . Id . Realm )
2014-07-04 15:22:06 +02:00
{
TC_LOG_ERROR ( "server.worldserver" , "Realm ID not defined in configuration file" );
return false ;
}
2014-10-17 22:48:06 +02:00
2015-10-06 00:30:47 +02:00
TC_LOG_INFO ( "server.worldserver" , "Realm running as realm ID %u" , realm . Id . Realm );
2014-07-04 15:22:06 +02:00
///- Clean the database before starting
ClearOnlineAccounts ();
///- Insert version info into DB
2015-08-16 23:27:25 +02:00
WorldDatabase . PExecute ( "UPDATE version SET core_version = '%s', core_revision = '%s'" , GitRevision :: GetFullVersion (), GitRevision :: GetHash ()); // One-time query
2014-07-04 15:22:06 +02:00
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
2015-10-06 00:30:47 +02:00
LoginDatabase . DirectPExecute ( "UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = %d)" , realm . Id . Realm );
2014-07-04 15:22:06 +02:00
// 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
/// @}
2014-07-15 17:46:09 +02:00
2016-04-18 20:29:42 +02:00
variables_map GetConsoleArguments ( int argc , char ** argv , fs :: path & configFile , std :: string & configService )
2014-07-15 17:46:09 +02:00
{
2014-07-23 16:13:30 -05:00
// Silences warning about configService not be used if the OS is not Windows
( void ) configService ;
2014-07-28 23:32:53 +02:00
2014-07-15 17:46:09 +02:00
options_description all ( "Allowed options" );
all . add_options ()
( "help,h" , "print usage message" )
2015-07-04 00:45:52 +01:00
( "version,v" , "print version build info" )
2016-04-18 20:29:42 +02:00
( "config,c" , value < fs :: path > ( & configFile ) -> default_value ( fs :: absolute ( _TRINITY_CORE_CONFIG )),
"use <arg> as configuration file" )
2019-03-17 21:34:13 +01:00
( "update-databases-only,u" , "updates databases only" )
2014-07-15 17:46:09 +02:00
;
#ifdef _WIN32
options_description win ( "Windows platform specific options" );
win . add_options ()
( "service,s" , value < std :: string > ( & configService ) -> default_value ( "" ), "Windows service options: [install | uninstall]" )
;
all . add ( win );
#endif
variables_map vm ;
try
{
store ( command_line_parser ( argc , argv ). options ( all ). allow_unregistered (). run (), vm );
notify ( vm );
}
2015-06-06 13:03:36 +02:00
catch ( std :: exception & e )
{
2014-07-15 17:46:09 +02:00
std :: cerr << e . what () << " \n " ;
}
2015-06-06 13:03:36 +02:00
if ( vm . count ( "help" ))
{
2014-07-15 17:46:09 +02:00
std :: cout << all << " \n " ;
}
2015-07-04 00:45:52 +01:00
else if ( vm . count ( "version" ))
{
2015-08-16 23:27:25 +02:00
std :: cout << GitRevision :: GetFullVersion () << " \n " ;
2015-07-04 00:45:52 +01:00
}
2014-07-15 17:46:09 +02:00
return vm ;
}