54#include <openssl/opensslv.h>
55#include <openssl/crypto.h>
56#include <boost/asio/signal_set.hpp>
57#include <boost/dll/runtime_symbol_info.hpp>
58#include <boost/filesystem/operations.hpp>
59#include <boost/program_options.hpp>
60#include <google/protobuf/stubs/common.h>
66using namespace boost::program_options;
69#ifndef _TRINITY_CORE_CONFIG
70 #define _TRINITY_CORE_CONFIG "worldserver.conf"
73#ifndef _TRINITY_CORE_CONFIG_DIR
74 #define _TRINITY_CORE_CONFIG_DIR "worldserver.conf.d"
77#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
91#include <boost/dll/shared_library.hpp>
101 static void Start(std::shared_ptr<FreezeDetector>
const& freezeDetector)
103 freezeDetector->_timer.expires_after(5s);
104 freezeDetector->_timer.async_wait([freezeDetectorRef = std::weak_ptr(freezeDetector)](boost::system::error_code
const& error)
mutable
106 Handler(std::move(freezeDetectorRef), error);
110 static void Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code
const& error);
119void SignalHandler(boost::system::error_code
const& error,
int signalNumber);
127variables_map
GetConsoleArguments(
int argc,
char** argv, fs::path& configFile, fs::path& configDir, std::string& winServiceAction);
140 std::string winServiceAction;
144 if (vm.count(
"help") || vm.count(
"version"))
149 GOOGLE_PROTOBUF_VERIFY_VERSION;
155 if (winServiceAction ==
"install")
157 if (winServiceAction ==
"uninstall")
159 if (winServiceAction ==
"run")
163 boost::system::error_code dllError;
164 auto winmm =
Trinity::make_unique_ptr_with_deleter(
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::basic_signal_set<Trinity::Asio::IoContext::Executor> signals(*ioContext, SIGINT, SIGTERM);
272#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
273 signals.add(SIGBREAK);
278 int numThreads =
sConfigMgr->GetIntDefault(
"ThreadPool", 1);
282 std::unique_ptr<Trinity::ThreadPool> threadPool = std::make_unique<Trinity::ThreadPool>(numThreads);
284 for (
int i = 0; i < numThreads; ++i)
285 threadPool->PostWork([ioContext]() { ioContext->run(); });
298 if (vm.count(
"update-databases-only"))
305 auto sRealmListHandle = Trinity::make_unique_ptr_with_deleter<&RealmList::Close>(
sRealmList);
311 TC_LOG_ERROR(
"server.worldserver",
"Realm ID not defined in configuration file");
317 TC_LOG_INFO(
"server.worldserver",
"Realm running as realm ID {}", realmId);
322 std::shared_ptr<Realm const> realm =
sRealmList->GetCurrentRealm();
329 sMetric->Initialize(realm->Name, *ioContext, []()
331 TC_METRIC_VALUE(
"online_players", sWorld->GetPlayerCount());
332 TC_METRIC_VALUE(
"db_queue_login", uint64(LoginDatabase.QueueSize()));
333 TC_METRIC_VALUE(
"db_queue_character", uint64(CharacterDatabase.QueueSize()));
334 TC_METRIC_VALUE(
"db_queue_world", uint64(WorldDatabase.QueueSize()));
347 auto scriptReloadMgrHandle = Trinity::make_unique_ptr_with_deleter<&ScriptReloadMgr::Unload>(
sScriptReloadMgr);
350 auto sScriptMgrHandle = Trinity::make_unique_ptr_with_deleter<&ScriptMgr::Unload>(
sScriptMgr);
354 if (!
sWorld->SetInitialWorldSettings())
357 auto instanceLockMgrHandle = Trinity::make_unique_ptr_with_deleter<&InstanceLockMgr::Unload>(&
sInstanceLockMgr);
359 auto terrainMgrHandle = Trinity::make_unique_ptr_with_deleter<&TerrainMgr::UnloadAll>(&
sTerrainMgr);
361 auto outdoorPvpMgrHandle = Trinity::make_unique_ptr_with_deleter<&OutdoorPvPMgr::Die>(
sOutdoorPvPMgr);
364 auto mapManagementHandle = Trinity::make_unique_ptr_with_deleter<&MapManager::UnloadAll>(
sMapMgr);
367 auto battlegroundMgrHandle = Trinity::make_unique_ptr_with_deleter<&BattlegroundMgr::DeleteAllBattlegrounds>(
sBattlegroundMgr);
370 std::unique_ptr<Trinity::Net::AsyncAcceptor> raAcceptor;
371 if (
sConfigMgr->GetBoolDefault(
"Ra.Enable",
false))
375 std::unique_ptr<std::thread, ShutdownTCSoapThread> soapThread;
376 if (
sConfigMgr->GetBoolDefault(
"SOAP.Enabled",
false))
379 soapThread.reset(soap);
386 std::string worldListener =
sConfigMgr->GetStringDefault(
"BindIP",
"0.0.0.0");
388 int networkThreads =
sConfigMgr->GetIntDefault(
"Network.Threads", 1);
390 if (networkThreads <= 0)
392 TC_LOG_ERROR(
"server.worldserver",
"Network.Threads must be greater than 0");
397 if (!
sWorldSocketMgr.StartNetwork(*ioContext, worldListener, worldPort, networkThreads))
399 TC_LOG_ERROR(
"server.worldserver",
"Failed to initialize network");
407 sWorld->UpdateSessions(1);
419 std::shared_ptr<FreezeDetector> freezeDetector;
420 if (
int coreStuckTime =
sConfigMgr->GetIntDefault(
"MaxCoreStuckTime", 60))
422 freezeDetector = std::make_shared<FreezeDetector>(*ioContext, coreStuckTime * 1000);
424 TC_LOG_INFO(
"server.worldserver",
"Starting up anti-freeze thread ({} seconds max stuck time)...", coreStuckTime);
432 std::unique_ptr<std::thread, ShutdownCLIThread> cliThread;
436 if (
sConfigMgr->GetBoolDefault(
"Console.Enable",
true))
439 cliThread.reset(
new std::thread(
CliThread));
448 sLog->SetSynchronous();
455 TC_LOG_INFO(
"server.worldserver",
"Halting process...");
472 if (cliThread !=
nullptr)
476 if (!CancelSynchronousIo(cliThread->native_handle()))
479 DWORD errorCode = GetLastError();
482 if (errorCode != ERROR_NOT_FOUND)
485 DWORD numCharsWritten = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
486 nullptr, errorCode, 0, (LPTSTR)&errorBuffer, 0,
nullptr);
487 if (!numCharsWritten)
488 errorBuffer =
"Unknown error";
490 TC_LOG_DEBUG(
"server.worldserver",
"Error cancelling I/O of CliThread, error code {}, detail: {}",
uint32(errorCode), errorBuffer);
493 LocalFree((LPSTR)errorBuffer);
497 HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
498 b[0].EventType = KEY_EVENT;
499 b[0].Event.KeyEvent.bKeyDown = TRUE;
500 b[0].Event.KeyEvent.uChar.AsciiChar =
'X';
501 b[0].Event.KeyEvent.wVirtualKeyCode =
'X';
502 b[0].Event.KeyEvent.wRepeatCount = 1;
504 b[1].EventType = KEY_EVENT;
505 b[1].Event.KeyEvent.bKeyDown = FALSE;
506 b[1].Event.KeyEvent.uChar.AsciiChar =
'X';
507 b[1].Event.KeyEvent.wVirtualKeyCode =
'X';
508 b[1].Event.KeyEvent.wRepeatCount = 1;
510 b[2].EventType = KEY_EVENT;
511 b[2].Event.KeyEvent.bKeyDown = TRUE;
512 b[2].Event.KeyEvent.dwControlKeyState = 0;
513 b[2].Event.KeyEvent.uChar.AsciiChar =
'\r';
514 b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
515 b[2].Event.KeyEvent.wRepeatCount = 1;
516 b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
518 b[3].EventType = KEY_EVENT;
519 b[3].Event.KeyEvent.bKeyDown = FALSE;
520 b[3].Event.KeyEvent.dwControlKeyState = 0;
521 b[3].Event.KeyEvent.uChar.AsciiChar =
'\r';
522 b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
523 b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
524 b[3].Event.KeyEvent.wRepeatCount = 1;
526 WriteConsoleInput(hStdIn, b, 4, &numb);
542 uint32 halfMaxCoreStuckTime = maxCoreStuckTime / 2;
543 if (!halfMaxCoreStuckTime)
544 halfMaxCoreStuckTime = std::numeric_limits<uint32>::max();
558 if (diff < minUpdateDiff)
560 uint32 sleepTime = minUpdateDiff - diff;
561 if (sleepTime >= halfMaxCoreStuckTime)
562 TC_LOG_ERROR(
"server.worldserver",
"WorldUpdateLoop() waiting for {} ms with MaxCoreStuckTime set to {} ms", sleepTime, maxCoreStuckTime);
569 realPrevTime = realCurrTime;
596 if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
601 if (freezeDetector->_worldLoopCounter != worldLoopCounter)
603 freezeDetector->_lastChangeMsTime = curtime;
604 freezeDetector->_worldLoopCounter = worldLoopCounter;
610 if (msTimeDiff > freezeDetector->_maxCoreStuckTimeInMs)
612 TC_LOG_ERROR(
"server.worldserver",
"World Thread hangs for {} ms, forcing a crash!", msTimeDiff);
613 ABORT_MSG(
"World Thread hangs for %u ms, forcing a crash!", msTimeDiff);
617 freezeDetector->_timer.expires_after(1s);
618 freezeDetector->_timer.async_wait([freezeDetectorRef = std::move(freezeDetectorRef)](boost::system::error_code
const& error)
mutable
620 Handler(std::move(freezeDetectorRef), error);
629 std::string raListener =
sConfigMgr->GetStringDefault(
"Ra.IP",
"0.0.0.0");
631 std::unique_ptr<Trinity::Net::AsyncAcceptor> acceptor = std::make_unique<Trinity::Net::AsyncAcceptor>(ioContext, raListener, raPort);
632 if (!acceptor->Bind())
634 TC_LOG_ERROR(
"server.worldserver",
"Failed to bind RA socket acceptor");
639 acceptor->AsyncAccept(
640 [&] {
return &ioContext; },
685 LoginDatabase.DirectPExecute(
"UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = {})", realmId);
688 CharacterDatabase.DirectExecute(
"UPDATE characters SET online = 0 WHERE online <> 0");
691 CharacterDatabase.DirectExecute(
"UPDATE character_battleground_data SET instanceId = 0");
694variables_map
GetConsoleArguments(
int argc,
char** argv, fs::path& configFile, fs::path& configDir, [[maybe_unused]] std::string& winServiceAction)
696 options_description all(
"Allowed options");
698 (
"help,h",
"print usage message")
699 (
"version,v",
"print version build info")
701 "use <arg> as configuration file")
703 "use <arg> as directory with additional config files")
704 (
"update-databases-only,u",
"updates databases only")
707 options_description win(
"Windows platform specific options");
709 (
"service,s", value<std::string>(&winServiceAction)->default_value(
""),
"Windows service options: [install | uninstall]")
717 store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), vm);
720 catch (std::exception& e)
722 std::cerr << e.what() <<
"\n";
725 if (vm.count(
"help"))
727 std::cout << all <<
"\n";
729 else if (vm.count(
"version"))
737#if TRINITY_PLATFORM == TRINITY_PLATFORM_WINDOWS
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__, message__,...)
#define TC_LOG_ERROR(filterType__, message__,...)
#define TC_LOG_INFO(filterType__, message__,...)
#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
std::thread * CreateSoapThread(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)
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)
static void ShutdownEncryption()
static void ShutdownEncryption()
Manages all sockets connected to peers and network threads.
void StopNetwork() override
Stops all network threads, It will wait for all running threads .
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_NETWORK_API void ScanLocalNetworks()
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()
std::unique_ptr< T, Impl::stateful_unique_ptr_deleter< Ptr, Del > > make_unique_ptr_with_deleter(Ptr ptr, Del deleter)
void AbortHandler(int sigval) noexcept
void operator()(std::thread *cliThread) const
void operator()(std::thread *thread) const
bool StartDB()
Initialize connection to the databases.
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)
void ClearOnlineAccounts(uint32 realmId)
Clear 'online' status for all accounts with characters in this realm.
void SignalHandler(boost::system::error_code const &error, int signalNumber)
#define _TRINITY_CORE_CONFIG
TCHAR serviceDescription[]
#define _TRINITY_CORE_CONFIG_DIR