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"
2022-05-06 12:21:59 +02:00
#include "AuthenticationPackets.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"
2018-07-22 13:22:35 +02:00
#include "InstanceLockMgr.h"
2018-01-06 01:21:59 +01:00
#include "IoContext.h"
2023-07-15 00:45:16 +02:00
#include "IpNetwork.h"
2017-05-12 18:49:51 +02:00
#include "MapManager.h"
#include "Metric.h"
#include "MySQLThreading.h"
#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"
2019-08-10 21:34:51 +02:00
#include "SecretMgr.h"
2014-07-04 15:22:06 +02:00
#include "TCSoap.h"
2022-07-23 19:13:33 +02:00
#include "TerrainMgr.h"
2022-10-08 20:08:29 +02:00
#include "ThreadPool.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"
2022-02-13 00:49:10 +01:00
#include "Util.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>
2022-06-14 23:18:42 +02:00
#include <boost/dll/runtime_symbol_info.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
2023-06-09 21:36:03 +03:00
#ifndef _TRINITY_CORE_CONFIG_DIR
#define _TRINITY_CORE_CONFIG_DIR "worldserver.conf.d"
#endif
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 ;
2022-01-03 11:26:23 +01:00
#include <boost/dll/shared_library.hpp>
#include <timeapi.h>
2008-10-02 16:23:55 -05:00
#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 ();
2023-06-09 21:36:03 +03:00
variables_map GetConsoleArguments ( int argc , char ** argv , fs :: path & configFile , fs :: path & configDir , std :: string & winServiceAction );
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 );
2023-06-09 21:36:03 +03:00
auto configDir = fs :: absolute ( _TRINITY_CORE_CONFIG_DIR );
std :: string winServiceAction ;
2009-10-17 15:51:44 -07:00
2023-06-09 21:36:03 +03:00
auto vm = GetConsoleArguments ( argc , argv , configFile , configDir , winServiceAction );
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
2023-06-09 21:36:03 +03:00
if ( winServiceAction == "install" )
2015-06-10 00:21:55 +02:00
return WinServiceInstall () ? 0 : 1 ;
2023-06-09 21:36:03 +03:00
if ( winServiceAction == "uninstall" )
2015-06-10 00:21:55 +02:00
return WinServiceUninstall () ? 0 : 1 ;
2023-06-09 21:36:03 +03:00
if ( winServiceAction == "run" )
return WinServiceRun () ? 0 : 1 ;
2022-01-03 11:26:23 +01:00
Optional < UINT > newTimerResolution ;
boost :: system :: error_code dllError ;
std :: shared_ptr < boost :: dll :: shared_library > winmm ( new boost :: dll :: shared_library ( "winmm.dll" , dllError , boost :: dll :: load_mode :: search_system_folders ), [ & ]( boost :: dll :: shared_library * lib )
{
try
{
if ( newTimerResolution )
lib -> get < decltype ( timeEndPeriod ) > ( "timeEndPeriod" )( * newTimerResolution );
}
catch ( std :: exception const & )
{
// ignore
}
delete lib ;
});
if ( winmm -> is_loaded ())
{
try
{
auto timeGetDevCapsPtr = winmm -> get < decltype ( timeGetDevCaps ) > ( "timeGetDevCaps" );
// setup timer resolution
TIMECAPS timeResolutionLimits ;
if ( timeGetDevCapsPtr ( & timeResolutionLimits , sizeof ( TIMECAPS )) == TIMERR_NOERROR )
{
auto timeBeginPeriodPtr = winmm -> get < decltype ( timeBeginPeriod ) > ( "timeBeginPeriod" );
newTimerResolution = std :: min ( std :: max ( timeResolutionLimits . wPeriodMin , 1u ), timeResolutionLimits . wPeriodMax );
timeBeginPeriodPtr ( * newTimerResolution );
}
}
catch ( std :: exception const & e )
{
printf ( "Failed to initialize timer resolution: %s \n " , e . what ());
}
}
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
2023-06-09 21:36:03 +03:00
std :: vector < std :: string > loadedConfigFiles ;
std :: vector < std :: string > configDirErrors ;
bool additionalConfigFileLoadSuccess = sConfigMgr -> LoadAdditionalDir ( configDir . generic_string (), true , loadedConfigFiles , configDirErrors );
for ( std :: string const & loadedConfigFile : loadedConfigFiles )
printf ( "Loaded additional config file %s \n " , loadedConfigFile . c_str ());
if ( ! additionalConfigFileLoadSuccess )
{
for ( std :: string const & configDirError : configDirErrors )
printf ( "Error in additional config files: %s \n " , configDirError . c_str ());
return 1 ;
}
2021-08-19 22:26:16 +02:00
std :: vector < std :: string > overriddenKeys = sConfigMgr -> OverrideWithEnvVariablesIfAny ();
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 )
{
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "{}" , text );
2016-07-17 17:24:53 +02:00
},
[]()
{
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "Using configuration file {}." , sConfigMgr -> GetFilename ());
TC_LOG_INFO ( "server.worldserver" , "Using SSL version: {} (library: {})" , OPENSSL_VERSION_TEXT , OpenSSL_version ( OPENSSL_VERSION ));
TC_LOG_INFO ( "server.worldserver" , "Using Boost version: {}.{}.{}" , BOOST_VERSION / 100000 , BOOST_VERSION / 100 % 1000 , BOOST_VERSION % 100 );
2016-07-17 17:24:53 +02:00
}
);
2014-07-04 15:22:06 +02:00
2021-08-19 22:26:16 +02:00
for ( std :: string const & key : overriddenKeys )
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "Configuration field '{}' was overridden with environment variable." , key );
2021-08-19 22:26:16 +02:00
2022-06-14 23:18:42 +02:00
OpenSSLCrypto :: threadsSetup ( boost :: dll :: program_location (). remove_filename ());
2014-07-04 15:22:06 +02:00
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 ))
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "Daemon PID: {} \n " , pid );
2014-07-04 15:22:06 +02:00
else
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "server.worldserver" , "Cannot create PID file {}. \n " , pidFile );
2014-07-04 15:22:06 +02:00
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 );
if ( numThreads < 1 )
numThreads = 1 ;
2022-10-08 20:08:29 +02:00
std :: shared_ptr < Trinity :: ThreadPool > threadPool = std :: make_shared < Trinity :: ThreadPool > ( numThreads );
2014-07-04 15:22:06 +02:00
for ( int i = 0 ; i < numThreads ; ++ i )
2022-10-08 20:08:29 +02:00
threadPool -> PostWork ([ ioContext ]() { ioContext -> run (); });
2014-07-04 15:22:06 +02:00
2022-10-13 12:53:54 +02:00
std :: shared_ptr < void > ioContextStopHandle ( nullptr , [ ioContext ]( void * ) { ioContext -> stop (); });
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
2020-06-22 09:53:10 +02:00
std :: shared_ptr < void > dbHandle ( nullptr , []( void * ) { StopDB (); });
2019-03-17 21:34:13 +01:00
if ( vm . count ( "update-databases-only" ))
return 0 ;
2023-07-15 00:45:16 +02:00
Trinity :: Net :: ScanLocalNetworks ();
2014-07-04 15:22:06 +02:00
// Set server offline (not connectable)
2023-01-08 21:16:53 +01:00
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag | {} WHERE id = '{}'" , 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 ());
2021-08-06 19:46:37 +02:00
TC_METRIC_VALUE ( "db_queue_login" , uint64 ( LoginDatabase . QueueSize ()));
TC_METRIC_VALUE ( "db_queue_character" , uint64 ( CharacterDatabase . QueueSize ()));
TC_METRIC_VALUE ( "db_queue_world" , uint64 ( WorldDatabase . QueueSize ()));
2016-06-04 11:26:57 +01:00
});
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
2019-08-10 21:34:51 +02:00
sSecretMgr -> Initialize ( SECRET_OWNER_WORLDSERVER );
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 ();
sOutdoorPvPMgr -> Die (); // unload it before MapManager
sMapMgr -> UnloadAll (); // unload all grids (including locked in memory)
2022-07-23 19:13:33 +02:00
sTerrainMgr . UnloadAll ();
2018-07-22 13:22:35 +02:00
sInstanceLockMgr . Unload ();
2017-05-04 20:41:22 +02:00
});
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 ();
});
2014-07-04 15:22:06 +02:00
// Set server online (allow connecting now)
2023-01-08 21:16:53 +01:00
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag & ~{}, population = 0 WHERE id = '{}'" , 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 ;
2019-10-26 19:47:48 +02:00
if ( int coreStuckTime = sConfigMgr -> GetIntDefault ( "MaxCoreStuckTime" , 60 ))
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 );
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "Starting up anti-freeze thread ({} 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
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "{} (worldserver-daemon) ready..." , GitRevision :: GetFullVersion ());
2014-08-11 17:28:10 +02:00
2019-05-26 23:01:50 +02:00
// 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 );
}
2014-07-04 15:22:06 +02:00
WorldUpdateLoop ();
// Shutdown starts here
2022-05-06 12:21:59 +02:00
WorldPackets :: Auth :: ConnectTo :: ShutdownEncryption ();
2022-07-31 21:39:48 +02:00
WorldPackets :: Auth :: EnterEncryptedMode :: ShutdownEncryption ();
2022-05-06 12:21:59 +02:00
2022-10-13 12:53:54 +02:00
ioContextStopHandle . reset ();
2022-10-08 20:08:29 +02:00
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
2023-01-08 21:16:53 +01:00
LoginDatabase . DirectPExecute ( "UPDATE realmlist SET flag = flag | {} WHERE id = '{}'" , 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 )
{
2020-08-04 15:42:08 +02:00
LPCSTR errorBuffer ;
2017-08-25 23:51:46 +02:00
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" ;
2023-01-08 21:16:53 +01:00
TC_LOG_DEBUG ( "server.worldserver" , "Error cancelling I/O of CliThread, error code {}, detail: {}" , uint32 ( errorCode ), errorBuffer );
2017-08-25 23:51:46 +02:00
if ( numCharsWritten )
2020-08-04 15:42:08 +02:00
LocalFree (( LPSTR ) errorBuffer );
2017-08-25 23:51:46 +02:00
// 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 ()
{
2022-04-16 23:14:42 +02:00
uint32 minUpdateDiff = uint32 ( sConfigMgr -> GetIntDefault ( "MinWorldUpdateTime" , 1 ));
2014-07-04 15:22:06 +02:00
uint32 realCurrTime = 0 ;
uint32 realPrevTime = getMSTime ();
2022-09-11 18:45:14 +02:00
uint32 maxCoreStuckTime = uint32 ( sConfigMgr -> GetIntDefault ( "MaxCoreStuckTime" , 60 )) * 1000 ;
uint32 halfMaxCoreStuckTime = maxCoreStuckTime / 2 ;
if ( ! halfMaxCoreStuckTime )
halfMaxCoreStuckTime = std :: numeric_limits < uint32 >:: max ();
2020-07-30 19:42:27 +00:00
LoginDatabase . WarnAboutSyncQueries ( true );
CharacterDatabase . WarnAboutSyncQueries ( true );
WorldDatabase . WarnAboutSyncQueries ( true );
HotfixDatabase . WarnAboutSyncQueries ( true );
2014-07-04 15:22:06 +02:00
///- While we have not World::m_stopEvent, update the world
while ( ! World :: IsStopped ())
{
++ World :: m_worldLoopCounter ;
realCurrTime = getMSTime ();
uint32 diff = getMSTimeDiff ( realPrevTime , realCurrTime );
2022-04-16 23:14:42 +02:00
if ( diff < minUpdateDiff )
2022-01-03 11:26:23 +01:00
{
2022-09-11 18:45:14 +02:00
uint32 sleepTime = minUpdateDiff - diff ;
if ( sleepTime >= halfMaxCoreStuckTime )
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "server.worldserver" , "WorldUpdateLoop() waiting for {} ms with MaxCoreStuckTime set to {} ms" , sleepTime , maxCoreStuckTime );
2022-01-03 11:26:23 +01:00
// sleep until enough time passes that we can update all timers
2022-09-11 18:45:14 +02:00
std :: this_thread :: sleep_for ( Milliseconds ( sleepTime ));
2022-01-03 11:26:23 +01:00
continue ;
}
2014-07-04 15:22:06 +02:00
sWorld -> Update ( diff );
realPrevTime = realCurrTime ;
#ifdef _WIN32
if ( m_ServiceStatus == 0 )
World :: StopNow ( SHUTDOWN_EXIT_CODE );
while ( m_ServiceStatus == 2 )
Sleep ( 1000 );
#endif
}
2020-07-30 19:42:27 +00:00
LoginDatabase . WarnAboutSyncQueries ( false );
CharacterDatabase . WarnAboutSyncQueries ( false );
WorldDatabase . WarnAboutSyncQueries ( false );
HotfixDatabase . WarnAboutSyncQueries ( false );
2014-07-04 15:22:06 +02:00
}
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
2022-09-11 18:45:14 +02:00
else
2017-05-04 20:41:22 +02:00
{
2022-09-11 18:45:14 +02:00
uint32 msTimeDiff = getMSTimeDiff ( freezeDetector -> _lastChangeMsTime , curtime );
if ( msTimeDiff > freezeDetector -> _maxCoreStuckTimeInMs )
{
2023-01-08 21:16:53 +01:00
TC_LOG_ERROR ( "server.worldserver" , "World Thread hangs for {} ms, forcing a crash!" , msTimeDiff );
2022-09-11 18:45:14 +02:00
ABORT_MSG ( "World Thread hangs for %u ms, forcing a crash!" , msTimeDiff );
}
2017-05-04 20:41:22 +02:00
}
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
{
2023-08-03 11:18:11 +02:00
realm = * realmListRealm ;
2017-05-13 11:49:09 +02:00
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
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "Realm running as realm ID {}" , realm . Id . Realm );
2014-07-04 15:22:06 +02:00
///- Clean the database before starting
ClearOnlineAccounts ();
///- Insert version info into DB
2023-01-08 21:16:53 +01:00
WorldDatabase . PExecute ( "UPDATE version SET core_version = '{}', core_revision = '{}'" , GitRevision :: GetFullVersion (), GitRevision :: GetHash ()); // One-time query
2014-07-04 15:22:06 +02:00
sWorld -> LoadDBVersion ();
2023-01-08 21:16:53 +01:00
TC_LOG_INFO ( "server.worldserver" , "Using World DB: {}" , sWorld -> GetDBVersion ());
2014-07-04 15:22:06 +02:00
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
2023-01-08 21:16:53 +01:00
LoginDatabase . DirectPExecute ( "UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = {})" , 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
2023-06-09 21:36:03 +03:00
variables_map GetConsoleArguments ( int argc , char ** argv , fs :: path & configFile , fs :: path & configDir , [[maybe_unused]] std :: string & winServiceAction )
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" )
2023-06-09 21:36:03 +03:00
( "config-dir,cd" , value < fs :: path > ( & configDir ) -> default_value ( fs :: absolute ( _TRINITY_CORE_CONFIG_DIR )),
"use <arg> as directory with additional config files" )
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 ()
2023-06-09 21:36:03 +03:00
( "service,s" , value < std :: string > ( & winServiceAction ) -> default_value ( "" ), "Windows service options: [install | uninstall]" )
2014-07-15 17:46:09 +02:00
;
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 ;
}