53#include <openssl/opensslv.h>
54#include <openssl/crypto.h>
55#include <boost/asio/signal_set.hpp>
56#include <boost/dll/runtime_symbol_info.hpp>
57#include <boost/filesystem/operations.hpp>
58#include <boost/program_options.hpp>
62using namespace boost::program_options;
65#ifndef _TRINITY_CORE_CONFIG
66 #define _TRINITY_CORE_CONFIG "worldserver.conf"
69#ifndef _TRINITY_CORE_CONFIG_DIR
70 #define _TRINITY_CORE_CONFIG_DIR "worldserver.conf.d"
73#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
87#include <boost/dll/shared_library.hpp>
97 static void Start(std::shared_ptr<FreezeDetector>
const& freezeDetector)
99 freezeDetector->_timer.expires_after(5s);
100 freezeDetector->_timer.async_wait([freezeDetectorRef = std::weak_ptr(freezeDetector)](boost::system::error_code
const& error)
mutable
102 Handler(std::move(freezeDetectorRef), error);
106 static void Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code
const& error);
115void SignalHandler(boost::system::error_code
const& error,
int signalNumber);
123variables_map
GetConsoleArguments(
int argc,
char** argv, fs::path& configFile, fs::path& configDir, std::string& winServiceAction);
137 std::string winServiceAction;
141 if (vm.count(
"help") || vm.count(
"version"))
146 if (winServiceAction ==
"install")
148 if (winServiceAction ==
"uninstall")
150 if (winServiceAction ==
"run")
154 boost::system::error_code dllError;
155 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)
159 if (newTimerResolution)
160 lib->get<
decltype(timeEndPeriod)>(
"timeEndPeriod")(*newTimerResolution);
162 catch (std::exception
const&)
170 if (winmm->is_loaded())
174 auto timeGetDevCapsPtr = winmm->get<
decltype(timeGetDevCaps)>(
"timeGetDevCaps");
176 TIMECAPS timeResolutionLimits;
177 if (timeGetDevCapsPtr(&timeResolutionLimits,
sizeof(TIMECAPS)) == TIMERR_NOERROR)
179 auto timeBeginPeriodPtr = winmm->get<
decltype(timeBeginPeriod)>(
"timeBeginPeriod");
180 newTimerResolution = std::min(std::max(timeResolutionLimits.wPeriodMin, 1u), timeResolutionLimits.wPeriodMax);
181 timeBeginPeriodPtr(*newTimerResolution);
184 catch (std::exception
const& e)
186 printf(
"Failed to initialize timer resolution: %s\n", e.what());
192 std::string configError;
193 if (!
sConfigMgr->LoadInitial(configFile.generic_string(),
194 std::vector<std::string>(argv, argv + argc),
197 printf(
"Error in config file: %s\n", configError.c_str());
201 std::vector<std::string> loadedConfigFiles;
202 std::vector<std::string> configDirErrors;
203 bool additionalConfigFileLoadSuccess =
sConfigMgr->LoadAdditionalDir(configDir.generic_string(),
true, loadedConfigFiles, configDirErrors);
204 for (std::string
const& loadedConfigFile : loadedConfigFiles)
205 printf(
"Loaded additional config file %s\n", loadedConfigFile.c_str());
207 if (!additionalConfigFileLoadSuccess)
209 for (std::string
const& configDirError : configDirErrors)
210 printf(
"Error in additional config files: %s\n", configDirError.c_str());
215 std::vector<std::string> overriddenKeys =
sConfigMgr->OverrideWithEnvVariablesIfAny();
217 std::shared_ptr<Trinity::Asio::IoContext> ioContext = std::make_shared<Trinity::Asio::IoContext>();
221 sLog->Initialize(
sConfigMgr->GetBoolDefault(
"Log.Async.Enable",
false) ? ioContext.get() :
nullptr);
231 TC_LOG_INFO(
"server.worldserver",
"Using SSL version: {} (library: {})", OPENSSL_VERSION_TEXT, OpenSSL_version(OPENSSL_VERSION));
232 TC_LOG_INFO(
"server.worldserver",
"Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
236 for (std::string
const& key : overriddenKeys)
237 TC_LOG_INFO(
"server.worldserver",
"Configuration field '{}' was overridden with environment variable.", key);
249 std::string pidFile =
sConfigMgr->GetStringDefault(
"PidFile",
"");
250 if (!pidFile.empty())
253 TC_LOG_INFO(
"server.worldserver",
"Daemon PID: {}\n", pid);
256 TC_LOG_ERROR(
"server.worldserver",
"Cannot create PID file {}.\n", pidFile);
262 boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM);
263#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
264 signals.add(SIGBREAK);
269 int numThreads =
sConfigMgr->GetIntDefault(
"ThreadPool", 1);
273 std::shared_ptr<Trinity::ThreadPool> threadPool = std::make_shared<Trinity::ThreadPool>(numThreads);
275 for (
int i = 0; i < numThreads; ++i)
276 threadPool->PostWork([ioContext]() { ioContext->run(); });
278 std::shared_ptr<void> ioContextStopHandle(
nullptr, [ioContext](
void*) { ioContext->stop(); });
287 std::shared_ptr<void> dbHandle(
nullptr, [](
void*) {
StopDB(); });
289 if (vm.count(
"update-databases-only"))
299 TC_METRIC_VALUE(
"online_players", sWorld->GetPlayerCount());
300 TC_METRIC_VALUE(
"db_queue_login", uint64(LoginDatabase.QueueSize()));
301 TC_METRIC_VALUE(
"db_queue_character", uint64(CharacterDatabase.QueueSize()));
302 TC_METRIC_VALUE(
"db_queue_world", uint64(WorldDatabase.QueueSize()));
307 std::shared_ptr<void> sMetricHandle(
nullptr, [](
void*)
314 std::shared_ptr<void> sScriptMgrHandle(
nullptr, [](
void*)
322 sWorld->SetInitialWorldSettings();
324 std::shared_ptr<void> mapManagementHandle(
nullptr, [](
void*)
335 std::unique_ptr<Trinity::Net::AsyncAcceptor> raAcceptor;
336 if (
sConfigMgr->GetBoolDefault(
"Ra.Enable",
false))
340 std::shared_ptr<std::thread> soapThread;
341 if (
sConfigMgr->GetBoolDefault(
"SOAP.Enabled",
false))
353 std::string worldListener =
sConfigMgr->GetStringDefault(
"BindIP",
"0.0.0.0");
355 int networkThreads =
sConfigMgr->GetIntDefault(
"Network.Threads", 1);
357 if (networkThreads <= 0)
359 TC_LOG_ERROR(
"server.worldserver",
"Network.Threads must be greater than 0");
364 if (!
sWorldSocketMgr.StartWorldNetwork(*ioContext, worldListener, worldPort, networkThreads))
366 TC_LOG_ERROR(
"server.worldserver",
"Failed to initialize network");
371 std::shared_ptr<void> sWorldSocketMgrHandle(
nullptr, [](
void*)
374 sWorld->UpdateSessions(1);
388 std::shared_ptr<FreezeDetector> freezeDetector;
389 if (
int coreStuckTime =
sConfigMgr->GetIntDefault(
"MaxCoreStuckTime", 60))
391 freezeDetector = std::make_shared<FreezeDetector>(*ioContext, coreStuckTime * 1000);
393 TC_LOG_INFO(
"server.worldserver",
"Starting up anti-freeze thread ({} seconds max stuck time)...", coreStuckTime);
401 std::shared_ptr<std::thread> cliThread;
405 if (
sConfigMgr->GetBoolDefault(
"Console.Enable",
true))
414 ioContextStopHandle.reset();
418 sLog->SetSynchronous();
425 TC_LOG_INFO(
"server.worldserver",
"Halting process...");
436 if (cliThread !=
nullptr)
440 if (!CancelSynchronousIo(cliThread->native_handle()))
443 DWORD errorCode = GetLastError();
446 DWORD formatReturnCode = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
447 nullptr, errorCode, 0, (LPTSTR)&errorBuffer, 0,
nullptr);
448 if (!formatReturnCode)
449 errorBuffer =
"Unknown error";
451 TC_LOG_DEBUG(
"server.worldserver",
"Error cancelling I/O of CliThread, error code {}, detail: {}",
uint32(errorCode), errorBuffer);
453 if (!formatReturnCode)
454 LocalFree((LPSTR)errorBuffer);
458 HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
459 b[0].EventType = KEY_EVENT;
460 b[0].Event.KeyEvent.bKeyDown = TRUE;
461 b[0].Event.KeyEvent.uChar.AsciiChar =
'X';
462 b[0].Event.KeyEvent.wVirtualKeyCode =
'X';
463 b[0].Event.KeyEvent.wRepeatCount = 1;
465 b[1].EventType = KEY_EVENT;
466 b[1].Event.KeyEvent.bKeyDown = FALSE;
467 b[1].Event.KeyEvent.uChar.AsciiChar =
'X';
468 b[1].Event.KeyEvent.wVirtualKeyCode =
'X';
469 b[1].Event.KeyEvent.wRepeatCount = 1;
471 b[2].EventType = KEY_EVENT;
472 b[2].Event.KeyEvent.bKeyDown = TRUE;
473 b[2].Event.KeyEvent.dwControlKeyState = 0;
474 b[2].Event.KeyEvent.uChar.AsciiChar =
'\r';
475 b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
476 b[2].Event.KeyEvent.wRepeatCount = 1;
477 b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
479 b[3].EventType = KEY_EVENT;
480 b[3].Event.KeyEvent.bKeyDown = FALSE;
481 b[3].Event.KeyEvent.dwControlKeyState = 0;
482 b[3].Event.KeyEvent.uChar.AsciiChar =
'\r';
483 b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
484 b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
485 b[3].Event.KeyEvent.wRepeatCount = 1;
487 WriteConsoleInput(hStdIn, b, 4, &numb);
502 uint32 halfMaxCoreStuckTime = maxCoreStuckTime / 2;
503 if (!halfMaxCoreStuckTime)
504 halfMaxCoreStuckTime = std::numeric_limits<uint32>::max();
517 if (diff < minUpdateDiff)
519 uint32 sleepTime = minUpdateDiff - diff;
520 if (sleepTime >= halfMaxCoreStuckTime)
521 TC_LOG_ERROR(
"server.worldserver",
"WorldUpdateLoop() waiting for {} ms with MaxCoreStuckTime set to {} ms", sleepTime, maxCoreStuckTime);
528 realPrevTime = realCurrTime;
554 if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
559 if (freezeDetector->_worldLoopCounter != worldLoopCounter)
561 freezeDetector->_lastChangeMsTime = curtime;
562 freezeDetector->_worldLoopCounter = worldLoopCounter;
568 if (msTimeDiff > freezeDetector->_maxCoreStuckTimeInMs)
570 TC_LOG_ERROR(
"server.worldserver",
"World Thread hangs for {} ms, forcing a crash!", msTimeDiff);
571 ABORT_MSG(
"World Thread hangs for %u ms, forcing a crash!", msTimeDiff);
575 freezeDetector->_timer.expires_after(1s);
576 freezeDetector->_timer.async_wait([freezeDetectorRef = std::move(freezeDetectorRef)](boost::system::error_code
const& error)
mutable
578 Handler(std::move(freezeDetectorRef), error);
587 std::string raListener =
sConfigMgr->GetStringDefault(
"Ra.IP",
"0.0.0.0");
589 std::unique_ptr<Trinity::Net::AsyncAcceptor> acceptor = std::make_unique<Trinity::Net::AsyncAcceptor>(ioContext, raListener, raPort);
590 if (!acceptor->Bind())
592 TC_LOG_ERROR(
"server.worldserver",
"Failed to bind RA socket acceptor");
598 std::make_shared<RASession>(std::move(sock))->Start();
606 QueryResult result =
LoginDatabase.PQuery(
"SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE id = {}",
realm.
Id.
Realm);
612 Field* fields = result->Fetch();
615 if (!externalAddress)
617 TC_LOG_ERROR(
"server.worldserver",
"Could not resolve address {}", fields[2].GetString());
626 TC_LOG_ERROR(
"server.worldserver",
"Could not resolve address {}", fields[3].GetString());
630 realm.
LocalAddress = std::make_unique<boost::asio::ip::address>(localAddress->address());
635 TC_LOG_ERROR(
"server.worldserver",
"Could not resolve address {}", fields[4].GetString());
670 TC_LOG_ERROR(
"server.worldserver",
"Realm ID not defined in configuration file");
701 LoginDatabase.DirectPExecute(
"UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = {})",
realm.
Id.
Realm);
704 CharacterDatabase.DirectExecute(
"UPDATE characters SET online = 0 WHERE online <> 0");
707 CharacterDatabase.DirectExecute(
"UPDATE character_battleground_data SET instanceId = 0");
710variables_map
GetConsoleArguments(
int argc,
char** argv, fs::path& configFile, fs::path& configDir, [[maybe_unused]] std::string& winServiceAction)
712 options_description all(
"Allowed options");
714 (
"help,h",
"print usage message")
715 (
"version,v",
"print version build info")
717 "use <arg> as configuration file")
719 "use <arg> as directory with additional config files")
720 (
"update-databases-only,u",
"updates databases only")
723 options_description win(
"Windows platform specific options");
725 (
"service,s", value<std::string>(&winServiceAction)->default_value(
""),
"Windows service options: [install | uninstall]")
733 store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), vm);
736 catch (std::exception& e) {
737 std::cerr << e.what() <<
"\n";
740 if (vm.count(
"help")) {
741 std::cout << all <<
"\n";
743 else if (vm.count(
"version"))
751#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
#define TC_LOG_DEBUG(filterType__,...)
#define TC_LOG_ERROR(filterType__,...)
#define TC_LOG_INFO(filterType__,...)
#define TC_METRIC_EVENT(category, title, description)
std::optional< T > Optional
Optional helper class to wrap optional values within.
void SetProcessPriority(std::string const &logChannel, uint32 affinity, bool highPriority)
#define CONFIG_HIGH_PRIORITY
#define CONFIG_PROCESSOR_AFFINITY
@ SERVER_PROCESS_WORLDSERVER
void TCSoapThread(const std::string &host, uint16 port)
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
uint32 CreatePIDFile(std::string const &filename)
create PID file
void SetRand(int32 numbits)
DatabaseLoader & AddDatabase(DatabaseWorkerPool< T > &pool, std::string const &name)
Class used to access individual fields of database query result.
std::string GetString() const
uint32 _maxCoreStuckTimeInMs
static void Start(std::shared_ptr< FreezeDetector > const &freezeDetector)
static void Handler(std::weak_ptr< FreezeDetector > freezeDetectorRef, boost::system::error_code const &error)
Trinity::Asio::DeadlineTimer _timer
FreezeDetector(Trinity::Asio::IoContext &ioContext, uint32 maxCoreStuckTime)
Optional< boost::asio::ip::tcp::endpoint > Resolve(boost::asio::ip::tcp const &protocol, std::string const &host, std::string const &service)
static uint8 GetExitCode()
static std::atomic< uint32 > m_worldLoopCounter
static void StopNow(uint8 exitcode)
void CliThread()
Thread start
TC_COMMON_API char const * GetFullVersion()
TC_COMMON_API char const * GetHash()
TC_DATABASE_API void Library_Init()
TC_DATABASE_API void Library_End()
TC_COMMON_API void threadsSetup(boost::filesystem::path const &providerModulePath)
Needs to be called before threads using openssl are spawned.
TC_COMMON_API void threadsCleanup()
Needs to be called after threads using openssl are despawned.
TC_COMMON_API void Show(char const *applicationName, void(*log)(char const *text), void(*logExtraInfo)())
TC_COMMON_API void Init()
boost::asio::basic_stream_socket< boost::asio::ip::tcp, boost::asio::io_context::executor_type > IoContextTcpSocket
TC_COMMON_API void Init(_TCHAR *serviceLongName, _TCHAR *serviceName, _TCHAR *serviceDescription, int(*entryPoint)(int argc, char **argv), int *status)
TC_COMMON_API int32 Install()
TC_COMMON_API int32 Uninstall()
TC_COMMON_API int32 Run()
TC_COMMON_API void VerifyOsVersion()
void AbortHandler(int sigval)
AccountTypes AllowedSecurityLevel
std::unique_ptr< boost::asio::ip::address > LocalSubnetMask
std::unique_ptr< boost::asio::ip::address > LocalAddress
std::unique_ptr< boost::asio::ip::address > ExternalAddress
static ServerProcessTypes _type
bool StartDB()
Initialize connection to the databases.
void ClearOnlineAccounts()
Clear 'online' status for all accounts with characters in this realm.
int main(int argc, char **argv)
Launch the Trinity server.
variables_map GetConsoleArguments(int argc, char **argv, fs::path &configFile, fs::path &configDir, std::string &winServiceAction)
std::unique_ptr< Trinity::Net::AsyncAcceptor > StartRaSocketAcceptor(Trinity::Asio::IoContext &ioContext)
bool LoadRealmInfo(Trinity::Asio::IoContext &ioContext)
void ShutdownCLIThread(std::thread *cliThread)
void SignalHandler(boost::system::error_code const &error, int signalNumber)
#define _TRINITY_CORE_CONFIG
TCHAR serviceDescription[]
#define _TRINITY_CORE_CONFIG_DIR