58#include <openssl/opensslv.h>
59#include <openssl/crypto.h>
60#include <boost/asio/signal_set.hpp>
61#include <boost/dll/runtime_symbol_info.hpp>
62#include <boost/filesystem/operations.hpp>
63#include <boost/program_options.hpp>
64#include <google/protobuf/stubs/common.h>
70using namespace boost::program_options;
73#ifndef _TRINITY_CORE_CONFIG
74 #define _TRINITY_CORE_CONFIG "worldserver.conf"
77#ifndef _TRINITY_CORE_CONFIG_DIR
78 #define _TRINITY_CORE_CONFIG_DIR "worldserver.conf.d"
94#include <boost/dll/shared_library.hpp>
104 static void Start(std::shared_ptr<FreezeDetector>
const& freezeDetector)
106 freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(5));
107 freezeDetector->_timer.async_wait([freezeDetectorRef = std::weak_ptr(freezeDetector)](boost::system::error_code
const& error)
mutable
109 Handler(std::move(freezeDetectorRef), error);
113 static void Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code
const& error);
122void SignalHandler(boost::system::error_code
const& error,
int signalNumber);
130variables_map
GetConsoleArguments(
int argc,
char** argv, fs::path& configFile, fs::path& configDir, std::string& winServiceAction);
133extern int main(
int argc,
char** argv)
143 std::string winServiceAction;
147 if (vm.count(
"help") || vm.count(
"version"))
150 GOOGLE_PROTOBUF_VERIFY_VERSION;
152 std::shared_ptr<void> protobufHandle(
nullptr, [](
void*) { google::protobuf::ShutdownProtobufLibrary(); });
155 if (winServiceAction ==
"install")
156 return WinServiceInstall() ? 0 : 1;
157 if (winServiceAction ==
"uninstall")
158 return WinServiceUninstall() ? 0 : 1;
159 if (winServiceAction ==
"run")
160 return WinServiceRun() ? 0 : 1;
163 boost::system::error_code dllError;
164 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)
168 if (newTimerResolution)
169 lib->get<
decltype(timeEndPeriod)>(
"timeEndPeriod")(*newTimerResolution);
171 catch (std::exception
const&)
179 if (winmm->is_loaded())
183 auto timeGetDevCapsPtr = winmm->get<
decltype(timeGetDevCaps)>(
"timeGetDevCaps");
185 TIMECAPS timeResolutionLimits;
186 if (timeGetDevCapsPtr(&timeResolutionLimits,
sizeof(TIMECAPS)) == TIMERR_NOERROR)
188 auto timeBeginPeriodPtr = winmm->get<
decltype(timeBeginPeriod)>(
"timeBeginPeriod");
189 newTimerResolution = std::min(std::max(timeResolutionLimits.wPeriodMin, 1u), timeResolutionLimits.wPeriodMax);
190 timeBeginPeriodPtr(*newTimerResolution);
193 catch (std::exception
const& e)
195 printf(
"Failed to initialize timer resolution: %s\n", e.what());
201 std::string configError;
202 if (!
sConfigMgr->LoadInitial(configFile.generic_string(),
203 std::vector<std::string>(argv, argv + argc),
206 printf(
"Error in config file: %s\n", configError.c_str());
210 std::vector<std::string> loadedConfigFiles;
211 std::vector<std::string> configDirErrors;
212 bool additionalConfigFileLoadSuccess =
sConfigMgr->LoadAdditionalDir(configDir.generic_string(),
true, loadedConfigFiles, configDirErrors);
213 for (std::string
const& loadedConfigFile : loadedConfigFiles)
214 printf(
"Loaded additional config file %s\n", loadedConfigFile.c_str());
216 if (!additionalConfigFileLoadSuccess)
218 for (std::string
const& configDirError : configDirErrors)
219 printf(
"Error in additional config files: %s\n", configDirError.c_str());
224 std::vector<std::string> overriddenKeys =
sConfigMgr->OverrideWithEnvVariablesIfAny();
226 std::shared_ptr<Trinity::Asio::IoContext> ioContext = std::make_shared<Trinity::Asio::IoContext>();
230 sLog->Initialize(
sConfigMgr->GetBoolDefault(
"Log.Async.Enable",
false) ? ioContext.get() :
nullptr);
240 TC_LOG_INFO(
"server.worldserver",
"Using SSL version: {} (library: {})", OPENSSL_VERSION_TEXT, OpenSSL_version(OPENSSL_VERSION));
241 TC_LOG_INFO(
"server.worldserver",
"Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
245 for (std::string
const& key : overriddenKeys)
246 TC_LOG_INFO(
"server.worldserver",
"Configuration field '{}' was overridden with environment variable.", key);
258 std::string pidFile =
sConfigMgr->GetStringDefault(
"PidFile",
"");
259 if (!pidFile.empty())
262 TC_LOG_INFO(
"server.worldserver",
"Daemon PID: {}\n", pid);
265 TC_LOG_ERROR(
"server.worldserver",
"Cannot create PID file {}.\n", pidFile);
271 boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM);
272#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
273 signals.add(SIGBREAK);
278 int numThreads =
sConfigMgr->GetIntDefault(
"ThreadPool", 1);
282 std::shared_ptr<Trinity::ThreadPool> threadPool = std::make_shared<Trinity::ThreadPool>(numThreads);
284 for (
int i = 0; i < numThreads; ++i)
285 threadPool->PostWork([ioContext]() { ioContext->run(); });
287 std::shared_ptr<void> ioContextStopHandle(
nullptr, [ioContext](
void*) { ioContext->stop(); });
296 std::shared_ptr<void> dbHandle(
nullptr, [](
void*) {
StopDB(); });
298 if (vm.count(
"update-databases-only"))
308 std::shared_ptr<void> sRealmListHandle(
nullptr, [](
void*) {
sRealmList->Close(); });
314 TC_METRIC_VALUE(
"online_players", sWorld->GetPlayerCount());
315 TC_METRIC_VALUE(
"db_queue_login", uint64(LoginDatabase.QueueSize()));
316 TC_METRIC_VALUE(
"db_queue_character", uint64(CharacterDatabase.QueueSize()));
317 TC_METRIC_VALUE(
"db_queue_world", uint64(WorldDatabase.QueueSize()));
322 std::shared_ptr<void> sMetricHandle(
nullptr, [](
void*)
329 std::shared_ptr<void> sScriptMgrHandle(
nullptr, [](
void*)
337 sWorld->SetInitialWorldSettings();
339 std::shared_ptr<void> mapManagementHandle(
nullptr, [](
void*)
351 std::unique_ptr<AsyncAcceptor> raAcceptor;
352 if (
sConfigMgr->GetBoolDefault(
"Ra.Enable",
false))
356 std::shared_ptr<std::thread> soapThread;
357 if (
sConfigMgr->GetBoolDefault(
"SOAP.Enabled",
false))
370 std::string worldListener =
sConfigMgr->GetStringDefault(
"BindIP",
"0.0.0.0");
372 int networkThreads =
sConfigMgr->GetIntDefault(
"Network.Threads", 1);
374 if (networkThreads <= 0)
376 TC_LOG_ERROR(
"server.worldserver",
"Network.Threads must be greater than 0");
381 if (!
sWorldSocketMgr.StartWorldNetwork(*ioContext, worldListener, worldPort, instancePort, networkThreads))
383 TC_LOG_ERROR(
"server.worldserver",
"Failed to initialize network");
388 std::shared_ptr<void> sWorldSocketMgrHandle(
nullptr, [](
void*)
391 sWorld->UpdateSessions(1);
405 std::shared_ptr<FreezeDetector> freezeDetector;
406 if (
int coreStuckTime =
sConfigMgr->GetIntDefault(
"MaxCoreStuckTime", 60))
408 freezeDetector = std::make_shared<FreezeDetector>(*ioContext, coreStuckTime * 1000);
410 TC_LOG_INFO(
"server.worldserver",
"Starting up anti-freeze thread ({} seconds max stuck time)...", coreStuckTime);
418 std::shared_ptr<std::thread> cliThread;
422 if (
sConfigMgr->GetBoolDefault(
"Console.Enable",
true))
434 ioContextStopHandle.reset();
438 sLog->SetSynchronous();
445 TC_LOG_INFO(
"server.worldserver",
"Halting process...");
456 if (cliThread !=
nullptr)
460 if (!CancelSynchronousIo(cliThread->native_handle()))
463 DWORD errorCode = GetLastError();
466 if (errorCode != ERROR_NOT_FOUND)
469 DWORD numCharsWritten = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
470 nullptr, errorCode, 0, (LPTSTR)&errorBuffer, 0,
nullptr);
471 if (!numCharsWritten)
472 errorBuffer =
"Unknown error";
474 TC_LOG_DEBUG(
"server.worldserver",
"Error cancelling I/O of CliThread, error code {}, detail: {}",
uint32(errorCode), errorBuffer);
477 LocalFree((LPSTR)errorBuffer);
481 HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
482 b[0].EventType = KEY_EVENT;
483 b[0].Event.KeyEvent.bKeyDown = TRUE;
484 b[0].Event.KeyEvent.uChar.AsciiChar =
'X';
485 b[0].Event.KeyEvent.wVirtualKeyCode =
'X';
486 b[0].Event.KeyEvent.wRepeatCount = 1;
488 b[1].EventType = KEY_EVENT;
489 b[1].Event.KeyEvent.bKeyDown = FALSE;
490 b[1].Event.KeyEvent.uChar.AsciiChar =
'X';
491 b[1].Event.KeyEvent.wVirtualKeyCode =
'X';
492 b[1].Event.KeyEvent.wRepeatCount = 1;
494 b[2].EventType = KEY_EVENT;
495 b[2].Event.KeyEvent.bKeyDown = TRUE;
496 b[2].Event.KeyEvent.dwControlKeyState = 0;
497 b[2].Event.KeyEvent.uChar.AsciiChar =
'\r';
498 b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
499 b[2].Event.KeyEvent.wRepeatCount = 1;
500 b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
502 b[3].EventType = KEY_EVENT;
503 b[3].Event.KeyEvent.bKeyDown = FALSE;
504 b[3].Event.KeyEvent.dwControlKeyState = 0;
505 b[3].Event.KeyEvent.uChar.AsciiChar =
'\r';
506 b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
507 b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
508 b[3].Event.KeyEvent.wRepeatCount = 1;
510 WriteConsoleInput(hStdIn, b, 4, &numb);
526 uint32 halfMaxCoreStuckTime = maxCoreStuckTime / 2;
527 if (!halfMaxCoreStuckTime)
528 halfMaxCoreStuckTime = std::numeric_limits<uint32>::max();
542 if (diff < minUpdateDiff)
544 uint32 sleepTime = minUpdateDiff - diff;
545 if (sleepTime >= halfMaxCoreStuckTime)
546 TC_LOG_ERROR(
"server.worldserver",
"WorldUpdateLoop() waiting for {} ms with MaxCoreStuckTime set to {} ms", sleepTime, maxCoreStuckTime);
553 realPrevTime = realCurrTime;
580 if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
585 if (freezeDetector->_worldLoopCounter != worldLoopCounter)
587 freezeDetector->_lastChangeMsTime = curtime;
588 freezeDetector->_worldLoopCounter = worldLoopCounter;
594 if (msTimeDiff > freezeDetector->_maxCoreStuckTimeInMs)
596 TC_LOG_ERROR(
"server.worldserver",
"World Thread hangs for {} ms, forcing a crash!", msTimeDiff);
597 ABORT_MSG(
"World Thread hangs for %u ms, forcing a crash!", msTimeDiff);
601 freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(1));
602 freezeDetector->_timer.async_wait([freezeDetectorRef = std::move(freezeDetectorRef)](boost::system::error_code
const& error)
mutable
604 Handler(std::move(freezeDetectorRef), error);
613 std::string raListener =
sConfigMgr->GetStringDefault(
"Ra.IP",
"0.0.0.0");
616 if (!acceptor->
Bind())
618 TC_LOG_ERROR(
"server.worldserver",
"Failed to bind RA socket acceptor");
631 realm = *realmListRealm;
658 TC_LOG_ERROR(
"server.worldserver",
"Realm ID not defined in configuration file");
690 LoginDatabase.DirectPExecute(
"UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = {})",
realm.
Id.
Realm);
693 CharacterDatabase.DirectExecute(
"UPDATE characters SET online = 0 WHERE online <> 0");
696 CharacterDatabase.DirectExecute(
"UPDATE character_battleground_data SET instanceId = 0");
701variables_map
GetConsoleArguments(
int argc,
char** argv, fs::path& configFile, fs::path& configDir, [[maybe_unused]] std::string& winServiceAction)
703 options_description all(
"Allowed options");
705 (
"help,h",
"print usage message")
706 (
"version,v",
"print version build info")
708 "use <arg> as configuration file")
710 "use <arg> as directory with additional config files")
711 (
"update-databases-only,u",
"updates databases only")
714 options_description win(
"Windows platform specific options");
716 (
"service,s", value<std::string>(&winServiceAction)->default_value(
""),
"Windows service options: [install | uninstall]")
724 store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), vm);
727 catch (std::exception& e)
729 std::cerr << e.what() <<
"\n";
732 if (vm.count(
"help"))
734 std::cout << all <<
"\n";
736 else if (vm.count(
"version"))
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
DatabaseWorkerPool< HotfixDatabaseConnection > HotfixDatabase
Accessor to the hotfix 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
@ SECRET_OWNER_WORLDSERVER
void TCSoapThread(const std::string &host, uint16 port)
uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
uint32 CreatePIDFile(std::string const &filename)
create PID file
char serviceDescription[]
void SignalHandler(std::weak_ptr< Trinity::Asio::IoContext > ioContextRef, boost::system::error_code const &error, int signalNumber)
int main(int argc, char **argv)
variables_map GetConsoleArguments(int argc, char **argv, fs::path &configFile, fs::path &configDir, std::string &winServiceAction)
bool StartDB()
Initialize connection to the database.
void StopDB()
Close the connection to the database.
void SetRand(int32 numbits)
DatabaseLoader & AddDatabase(DatabaseWorkerPool< T > &pool, std::string const &name)
uint32 _maxCoreStuckTimeInMs
static void Start(std::shared_ptr< FreezeDetector > const &freezeDetector)
Trinity::Asio::DeadlineTimer _timer
FreezeDetector(Trinity::Asio::IoContext &ioContext, uint32 maxCoreStuckTime)
static void ShutdownEncryption()
static void ShutdownEncryption()
static uint8 GetExitCode()
static std::atomic< uint32 > m_worldLoopCounter
static void StopNow(uint8 exitcode)
void ClearOnlineAccounts()
Clear 'online' status for all accounts with characters in this realm.
void CliThread()
Thread start
AsyncAcceptor * StartRaSocketAcceptor(Trinity::Asio::IoContext &ioContext)
void ShutdownCLIThread(std::thread *cliThread)
static void Handler(std::weak_ptr< FreezeDetector > freezeDetectorRef, boost::system::error_code const &error)
#define _TRINITY_CORE_CONFIG
#define _TRINITY_CORE_CONFIG_DIR
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()
TC_COMMON_API void ScanLocalNetworks()
TC_COMMON_API void VerifyOsVersion()
void AbortHandler(int sigval)
Battlenet::RealmHandle Id