diff options
-rw-r--r-- | src/server/authserver/Main.cpp | 160 | ||||
-rw-r--r-- | src/server/shared/Threading/ProcessPriority.h | 100 | ||||
-rw-r--r-- | src/server/worldserver/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/server/worldserver/Main.cpp | 487 | ||||
-rw-r--r-- | src/server/worldserver/Master.cpp | 479 | ||||
-rw-r--r-- | src/server/worldserver/Master.h | 57 | ||||
-rw-r--r-- | src/server/worldserver/WorldThread/WorldRunnable.cpp | 103 | ||||
-rw-r--r-- | src/server/worldserver/WorldThread/WorldRunnable.h | 32 | ||||
-rw-r--r-- | src/server/worldserver/worldserver.conf.dist | 23 |
9 files changed, 616 insertions, 828 deletions
diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 4e39ae0aca1..c7f71edbd0c 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -24,28 +24,24 @@ * authentication server */ -#include <openssl/opensslv.h> -#include <openssl/crypto.h> -#include <iostream> #include <cstdlib> #include <boost/date_time/posix_time/posix_time.hpp> +#include <iostream> +#include <openssl/opensslv.h> +#include <openssl/crypto.h> +#include "AsyncAcceptor.h" +#include "AuthSession.h" #include "Common.h" -#include "Database/DatabaseEnv.h" #include "Configuration/Config.h" +#include "Database/DatabaseEnv.h" #include "Log.h" +#include "ProcessPriority.h" +#include "RealmList.h" #include "SystemConfig.h" #include "Util.h" -#include "RealmList.h" -#include "AsyncAcceptor.h" -#include "AuthSession.h" - -#ifdef __linux__ -#include <sched.h> -#include <sys/resource.h> -#define PROCESS_HIGH_PRIORITY -15 // [-20, 19], default is 0 -#endif +using boost::asio::ip::tcp; #ifndef _TRINITY_REALM_CONFIG # define _TRINITY_REALM_CONFIG "authserver.conf" @@ -53,51 +49,15 @@ bool StartDB(); void StopDB(); -void SetProcessPriority(); +void SignalHandler(const boost::system::error_code& error, int signalNumber); +void KeepDatabaseAliveHandler(const boost::system::error_code& error); +void usage(const char* prog); boost::asio::io_service _ioService; boost::asio::deadline_timer _dbPingTimer(_ioService); uint32 _dbPingInterval; +LoginDatabaseWorkerPool LoginDatabase; -LoginDatabaseWorkerPool LoginDatabase; // Accessor to the authserver database - -using boost::asio::ip::tcp; - - -void SignalHandler(const boost::system::error_code& error, int signalNumber) -{ - if (!error) - { - switch (signalNumber) - { - case SIGINT: - case SIGTERM: - _ioService.stop(); - break; - } - } -} - -void KeepDatabaseAliveHandler(const boost::system::error_code& error) -{ - if (!error) - { - TC_LOG_INFO("server.authserver", "Ping MySQL to keep connection alive"); - LoginDatabase.KeepAlive(); - - _dbPingTimer.expires_from_now(boost::posix_time::minutes(_dbPingInterval)); - } -} - -/// Print out the usage string for this program on the console. -void usage(const char* prog) -{ - TC_LOG_INFO("server.authserver", "Usage: \n %s [<options>]\n" - " -c config_file use config_file as configuration file\n\r", - prog); -} - -/// Launch the auth server int main(int argc, char** argv) { // Command line parsing to get the configuration file name @@ -129,7 +89,6 @@ int main(int argc, char** argv) TC_LOG_INFO("server.authserver", "%s (authserver)", _FULLVERSION); TC_LOG_INFO("server.authserver", "<Ctrl-C> to stop.\n"); TC_LOG_INFO("server.authserver", "Using configuration file %s.", configFile); - TC_LOG_INFO("server.authserver", "%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); // authserver PID file creation @@ -158,31 +117,30 @@ int main(int argc, char** argv) return 1; } - // Launch the listening network socket - + // Start the listening port (acceptor) for auth connections int32 port = sConfigMgr->GetIntDefault("RealmServerPort", 3724); if (port < 0 || port > 0xFFFF) { TC_LOG_ERROR("server.authserver", "Specified port out of allowed range (1-65535)"); return 1; } - + std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - AsyncAcceptor<AuthSession> authServer(_ioService, bindIp, port); // Set signal handlers boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM); signals.async_wait(SignalHandler); - SetProcessPriority(); + // Set process priority according to configuration settings + SetProcessPriority("server.authserver"); + // Enabled a timed callback for handling the database keep alive ping _dbPingInterval = sConfigMgr->GetIntDefault("MaxPingTime", 30); - _dbPingTimer.expires_from_now(boost::posix_time::seconds(_dbPingInterval)); _dbPingTimer.async_wait(KeepDatabaseAliveHandler); - // Start the io service + // Start the io service worker loop _ioService.run(); // Close the Database Pool and library @@ -238,73 +196,35 @@ void StopDB() MySQL::Library_End(); } -void SetProcessPriority() +void SignalHandler(const boost::system::error_code& error, int signalNumber) { -#if defined(_WIN32) || defined(__linux__) - - ///- Handle affinity for multiple processors and process priority - uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0); - bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false); - -#ifdef _WIN32 // Windows - - HANDLE hProcess = GetCurrentProcess(); - if (affinity > 0) + if (!error) { - ULONG_PTR appAff; - ULONG_PTR sysAff; - - if (GetProcessAffinityMask(hProcess, &appAff, &sysAff)) + switch (signalNumber) { - // remove non accessible processors - ULONG_PTR currentAffinity = affinity & appAff; - - if (!currentAffinity) - TC_LOG_ERROR("server.authserver", "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the authserver. Accessible processors bitmask (hex): %x", affinity, appAff); - else if (SetProcessAffinityMask(hProcess, currentAffinity)) - TC_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %x", currentAffinity); - else - TC_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x", currentAffinity); + case SIGINT: + case SIGTERM: + _ioService.stop(); + break; } } +} - if (highPriority) - { - if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) - TC_LOG_INFO("server.authserver", "authserver process priority class set to HIGH"); - else - TC_LOG_ERROR("server.authserver", "Can't set authserver process priority class."); - } - -#else // Linux - - if (affinity > 0) +void KeepDatabaseAliveHandler(const boost::system::error_code& error) +{ + if (!error) { - cpu_set_t mask; - CPU_ZERO(&mask); - - for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i) - if (affinity & (1 << i)) - CPU_SET(i, &mask); - - if (sched_setaffinity(0, sizeof(mask), &mask)) - TC_LOG_ERROR("server.authserver", "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno)); - else - { - CPU_ZERO(&mask); - sched_getaffinity(0, sizeof(mask), &mask); - TC_LOG_INFO("server.authserver", "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask)); - } - } + TC_LOG_INFO("server.authserver", "Ping MySQL to keep connection alive"); + LoginDatabase.KeepAlive(); - if (highPriority) - { - if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY)) - TC_LOG_ERROR("server.authserver", "Can't set authserver process priority class, error: %s", strerror(errno)); - else - TC_LOG_INFO("server.authserver", "authserver process priority class set to %i", getpriority(PRIO_PROCESS, 0)); + _dbPingTimer.expires_from_now(boost::posix_time::minutes(_dbPingInterval)); } +} -#endif -#endif +/// Print out the usage string for this program on the console. +void usage(const char* prog) +{ + TC_LOG_INFO("server.authserver", "Usage: \n %s [<options>]\n" + " -c config_file use config_file as configuration file\n\r", + prog); } diff --git a/src/server/shared/Threading/ProcessPriority.h b/src/server/shared/Threading/ProcessPriority.h new file mode 100644 index 00000000000..cd116ccbbc8 --- /dev/null +++ b/src/server/shared/Threading/ProcessPriority.h @@ -0,0 +1,100 @@ +/* +* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> +* +* 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/>. +*/ + +#ifndef _PROCESSPRIO_H +#define _PROCESSPRIO_H + +#include "Configuration/Config.h" + +#ifdef __linux__ +#include <sched.h> +#include <sys/resource.h> +#define PROCESS_HIGH_PRIORITY -15 // [-20, 19], default is 0 +#endif + +void SetProcessPriority(const std::string logChannel) +{ +#if defined(_WIN32) || defined(__linux__) + + ///- Handle affinity for multiple processors and process priority + uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0); + bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false); + +#ifdef _WIN32 // Windows + + HANDLE hProcess = GetCurrentProcess(); + if (affinity > 0) + { + ULONG_PTR appAff; + ULONG_PTR sysAff; + + if (GetProcessAffinityMask(hProcess, &appAff, &sysAff)) + { + // remove non accessible processors + ULONG_PTR currentAffinity = affinity & appAff; + + if (!currentAffinity) + TC_LOG_ERROR(logChannel, "Processors marked in UseProcessors bitmask (hex) %x are not accessible. Accessible processors bitmask (hex): %x", affinity, appAff); + else if (SetProcessAffinityMask(hProcess, currentAffinity)) + TC_LOG_INFO(logChannel, "Using processors (bitmask, hex): %x", currentAffinity); + else + TC_LOG_ERROR(logChannel, "Can't set used processors (hex): %x", currentAffinity); + } + } + + if (highPriority) + { + if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) + TC_LOG_INFO(logChannel, "Process priority class set to HIGH"); + else + TC_LOG_ERROR(logChannel, "Can't set process priority class."); + } + +#else // Linux + + if (affinity > 0) + { + cpu_set_t mask; + CPU_ZERO(&mask); + + for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i) + if (affinity & (1 << i)) + CPU_SET(i, &mask); + + if (sched_setaffinity(0, sizeof(mask), &mask)) + TC_LOG_ERROR(logChannel, "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno)); + else + { + CPU_ZERO(&mask); + sched_getaffinity(0, sizeof(mask), &mask); + TC_LOG_INFO(logChannel, "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask)); + } + } + + if (highPriority) + { + if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY)) + TC_LOG_ERROR(logChannel, "Can't set process priority class, error: %s", strerror(errno)); + else + TC_LOG_INFO(logChannel, "Process priority class set to %i", getpriority(PRIO_PROCESS, 0)); + } + +#endif +#endif +} + +#endif diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt index 9d2e859a7de..b7a158ab3ce 100644 --- a/src/server/worldserver/CMakeLists.txt +++ b/src/server/worldserver/CMakeLists.txt @@ -11,7 +11,6 @@ file(GLOB_RECURSE sources_CommandLine CommandLine/*.cpp CommandLine/*.h) file(GLOB_RECURSE sources_RemoteAccess RemoteAccess/*.cpp RemoteAccess/*.h) file(GLOB_RECURSE sources_TCSoap TCSoap/*.cpp TCSoap/*.h) -file(GLOB_RECURSE sources_WorldThread WorldThread/*.cpp WorldThread/*.h) file(GLOB sources_localdir *.cpp *.h) if (USE_COREPCH) @@ -24,7 +23,6 @@ set(worldserver_SRCS ${sources_CommandLine} ${sources_RemoteAccess} ${sources_TCSoap} - ${sources_WorldThread} ${sources_localdir} ) @@ -138,7 +136,6 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/CommandLine ${CMAKE_CURRENT_SOURCE_DIR}/RemoteAccess ${CMAKE_CURRENT_SOURCE_DIR}/TCSoap - ${CMAKE_CURRENT_SOURCE_DIR}/WorldThread ${ACE_INCLUDE_DIR} ${MYSQL_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR} diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index bcc058c7fb3..0fc2c1a7221 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -22,17 +22,30 @@ #include <openssl/opensslv.h> #include <openssl/crypto.h> +#include <boost/asio/io_service.hpp> #include "Common.h" -#include "Database/DatabaseEnv.h" +#include "DatabaseEnv.h" +#include "AsyncAcceptor.h" +#include "RASession.h" #include "Configuration/Config.h" +#include "OpenSSLCrypto.h" +#include "ProcessPriority.h" +#include "BigNumber.h" +#include "RealmList.h" +#include "World.h" +#include "MapManager.h" +#include "ObjectAccessor.h" +#include "ScriptMgr.h" +#include "WorldSocketMgr.h" +#include "OutdoorPvP/OutdoorPvPMgr.h" +#include "BattlegroundMgr.h" +#include "TCSoap.h" +#include "CliRunnable.h" +#include "SystemConfig.h" -#include "Log.h" -#include "Master.h" - -#ifndef _TRINITY_CORE_CONFIG -# define _TRINITY_CORE_CONFIG "worldserver.conf" -#endif +#define TRINITY_CORE_CONFIG "worldserver.conf" +#define WORLD_SLEEP_CONST 50 #ifdef _WIN32 #include "ServiceWin32.h" @@ -48,31 +61,26 @@ char serviceDescription[] = "TrinityCore World of Warcraft emulator world servic int m_ServiceStatus = -1; #endif +boost::asio::io_service _ioService; WorldDatabaseWorkerPool WorldDatabase; ///< Accessor to the world database CharacterDatabaseWorkerPool CharacterDatabase; ///< Accessor to the character database LoginDatabaseWorkerPool LoginDatabase; ///< Accessor to the realm/login database - uint32 realmID; ///< Id of the realm -/// Print out the usage string for this program on the console. -void usage(const char* prog) -{ - printf("Usage:\n"); - printf(" %s [<options>]\n", prog); - printf(" -c config_file use config_file as configuration file\n"); -#ifdef _WIN32 - printf(" Running as service functions:\n"); - printf(" --service run as service\n"); - printf(" -s install install service\n"); - printf(" -s uninstall uninstall service\n"); -#endif -} +void usage(const char* prog); +void SignalHandler(const boost::system::error_code& error, int signalNumber); +void FreezeDetectorThread(uint32 delayTime); +AsyncAcceptor<RASession>* StartRaSocketAcceptor(boost::asio::io_service& ioService); +bool StartDB(); +void StopDB(); +void WorldUpdateLoop(); +void ClearOnlineAccounts(); /// Launch the Trinity server extern int main(int argc, char** argv) { ///- Command line parsing to get the configuration file name - char const* cfg_file = _TRINITY_CORE_CONFIG; + char const* cfg_file = TRINITY_CORE_CONFIG; int c = 1; while (c < argc) { @@ -131,21 +139,442 @@ extern int main(int argc, char** argv) return 1; } + TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon)", _FULLVERSION); + TC_LOG_INFO("server.worldserver", "<Ctrl-C> to stop.\n"); + TC_LOG_INFO("server.worldserver", " ______ __"); + TC_LOG_INFO("server.worldserver", "/\\__ _\\ __ __/\\ \\__"); + TC_LOG_INFO("server.worldserver", "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\, _\\ __ __"); + TC_LOG_INFO("server.worldserver", " \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\"); + TC_LOG_INFO("server.worldserver", " \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\"); + TC_LOG_INFO("server.worldserver", " \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\"); + TC_LOG_INFO("server.worldserver", " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\"); + TC_LOG_INFO("server.worldserver", " C O R E /\\___/"); + TC_LOG_INFO("server.worldserver", "http://TrinityCore.org \\/__/\n"); TC_LOG_INFO("server.worldserver", "Using configuration file %s.", cfg_file); - 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: %s", BOOST_LIB_VERSION); + TC_LOG_INFO("server.worldserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); + + OpenSSLCrypto::threadsSetup(); + BigNumber seed1; + seed1.SetRand(16 * 8); + + /// 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; + } + } + + // Set signal handlers (this must be done before starting io_service threads, because otherwise they would unblock and exit) + boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM); + signals.async_wait(SignalHandler); + + // Start the Boost based thread pool + int numThreads = sConfigMgr->GetIntDefault("ThreadPool", 1); + std::vector<std::thread> threadPool; + + if (numThreads < 1) + numThreads = 1; + + for (int i = 0; i < numThreads; ++i) + threadPool.push_back(std::thread(boost::bind(&boost::asio::io_service::run, &_ioService))); + + // Set process priority according to configuration settings + SetProcessPriority("server.worldserver"); + + // Start the databases + if (!StartDB()) + return 1; + + // Set server offline (not connectable) + LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_INVALID, realmID); + + // Initialize the World + sWorld->SetInitialWorldSettings(); + + // Launch CliRunnable thread + std::thread* cliThread = nullptr; +#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 = new std::thread(CliThread); + } + + // Start the Remote Access port (acceptor) if enabled + AsyncAcceptor<RASession>* raAcceptor = nullptr; + if (sConfigMgr->GetBoolDefault("Ra.Enable", false)) + raAcceptor = StartRaSocketAcceptor(_ioService); + + // Start soap serving thread if enabled + std::thread* soapThread = nullptr; + if (sConfigMgr->GetBoolDefault("SOAP.Enabled", false)) + { + soapThread = new std::thread(TCSoapThread, sConfigMgr->GetStringDefault("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetIntDefault("SOAP.Port", 7878))); + } + + // Start up freeze catcher thread + std::thread* freezeDetectorThread = nullptr; + if (uint32 freezeDelay = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0)) + freezeDetectorThread = new std::thread(FreezeDetectorThread, freezeDelay); + + // Launch the worldserver listener socket + uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD)); + std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); + + if (sWorldSocketMgr->StartNetwork(worldPort, bindIp.c_str()) == -1) + { + TC_LOG_ERROR("server.worldserver", "Failed to start network"); + return ERROR_EXIT_CODE; + } - ///- and run the 'Master' - /// @todo Why do we need this 'Master'? Can't all of this be in the Main as for Realmd? - int ret = sMaster->Run(); + // Set server online (allow connecting now) + LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); + + TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", _FULLVERSION); + + sScriptMgr->OnStartup(); + + WorldUpdateLoop(); + + // Shutdown starts here + + _ioService.stop(); + + for (auto& thread : threadPool) + { + thread.join(); + } + + sScriptMgr->OnShutdown(); + + sWorld->KickAll(); // save and kick all players + sWorld->UpdateSessions(1); // real players unload required UpdateSessions call + + // unload battleground templates before different singletons destroyed + sBattlegroundMgr->DeleteAllBattlegrounds(); + + sWorldSocketMgr->StopNetwork(); + + sMapMgr->UnloadAll(); // unload all grids (including locked in memory) + sObjectAccessor->UnloadAll(); // unload 'i_player2corpse' storage and remove from world + sScriptMgr->Unload(); + sOutdoorPvPMgr->Die(); + + // set server offline + LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realmID); + + // Clean up threads if any + if (soapThread != nullptr) + { + soapThread->join(); + delete soapThread; + } + + if (raAcceptor != nullptr) + delete raAcceptor; + + ///- Clean database before leaving + ClearOnlineAccounts(); + + StopDB(); + + TC_LOG_INFO("server.worldserver", "Halting process..."); + + if (cliThread != nullptr) + { +#ifdef _WIN32 + + // this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API) + //_exit(1); + // 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); + + cliThread->join(); + +#endif + + delete cliThread; + } + + delete freezeDetectorThread; + + OpenSSLCrypto::threadsCleanup(); - // at sMaster return function exist with codes // 0 - normal shutdown // 1 - shutdown at error // 2 - restart command used, this code can be used by restarter for restart Trinityd - return ret; + return World::GetExitCode(); +} + + +void WorldUpdateLoop() +{ + uint32 realCurrTime = 0; + uint32 realPrevTime = getMSTime(); + + uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST + + ///- 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; + + // diff (D0) include time of previous sleep (d0) + tick time (t0) + // we want that next d1 + t1 == WORLD_SLEEP_CONST + // we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement + // d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0 + if (diff <= WORLD_SLEEP_CONST + prevSleepTime) + { + prevSleepTime = WORLD_SLEEP_CONST + prevSleepTime - diff; + + std::this_thread::sleep_for(std::chrono::milliseconds(prevSleepTime)); + } + else + prevSleepTime = 0; + +#ifdef _WIN32 + if (m_ServiceStatus == 0) + World::StopNow(SHUTDOWN_EXIT_CODE); + + while (m_ServiceStatus == 2) + Sleep(1000); +#endif + } +} + +/// Print out the usage string for this program on the console. +void usage(const char* prog) +{ + printf("Usage:\n"); + printf(" %s [<options>]\n", prog); + printf(" -c config_file use config_file as configuration file\n"); +#ifdef _WIN32 + printf(" Running as service functions:\n"); + printf(" --service run as service\n"); + printf(" -s install install service\n"); + printf(" -s uninstall uninstall service\n"); +#endif +} + +void SignalHandler(const boost::system::error_code& error, int signalNumber) +{ + if (!error) + { + switch (signalNumber) + { + case SIGINT: + case SIGTERM: + World::StopNow(SHUTDOWN_EXIT_CODE); + break; + } + } +} + +void FreezeDetectorThread(uint32 delayTime) +{ + if (!delayTime) + return; + + TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", delayTime / 1000); + uint32 loops = 0; + uint32 lastChange = 0; + + while (!World::IsStopped()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + uint32 curtime = getMSTime(); + // normal work + uint32 worldLoopCounter = World::m_worldLoopCounter; + if (loops != worldLoopCounter) + { + lastChange = curtime; + loops = worldLoopCounter; + } + // possible freeze + else if (getMSTimeDiff(lastChange, curtime) > delayTime) + { + TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!"); + ASSERT(false); + } + } + TC_LOG_INFO("server.worldserver", "Anti-freeze thread exiting without problems."); +} + +AsyncAcceptor<RASession>* StartRaSocketAcceptor(boost::asio::io_service& ioService) +{ + uint16 raPort = uint16(sConfigMgr->GetIntDefault("Ra.Port", 3443)); + std::string raListener = sConfigMgr->GetStringDefault("Ra.IP", "0.0.0.0"); + + return new AsyncAcceptor<RASession>(ioService, raListener, raPort); +} + +/// Initialize connection to the databases +bool StartDB() +{ + MySQL::Library_Init(); + + std::string dbString; + uint8 asyncThreads, synchThreads; + + dbString = sConfigMgr->GetStringDefault("WorldDatabaseInfo", ""); + if (dbString.empty()) + { + TC_LOG_ERROR("server.worldserver", "World database not specified in configuration file"); + return false; + } + + asyncThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.WorkerThreads", 1)); + if (asyncThreads < 1 || asyncThreads > 32) + { + TC_LOG_ERROR("server.worldserver", "World database: invalid number of worker threads specified. " + "Please pick a value between 1 and 32."); + return false; + } + + synchThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.SynchThreads", 1)); + ///- Initialize the world database + if (!WorldDatabase.Open(dbString, asyncThreads, synchThreads)) + { + TC_LOG_ERROR("server.worldserver", "Cannot connect to world database %s", dbString.c_str()); + return false; + } + + ///- Get character database info from configuration file + dbString = sConfigMgr->GetStringDefault("CharacterDatabaseInfo", ""); + if (dbString.empty()) + { + TC_LOG_ERROR("server.worldserver", "Character database not specified in configuration file"); + return false; + } + + asyncThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.WorkerThreads", 1)); + if (asyncThreads < 1 || asyncThreads > 32) + { + TC_LOG_ERROR("server.worldserver", "Character database: invalid number of worker threads specified. " + "Please pick a value between 1 and 32."); + return false; + } + + synchThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.SynchThreads", 2)); + + ///- Initialize the Character database + if (!CharacterDatabase.Open(dbString, asyncThreads, synchThreads)) + { + TC_LOG_ERROR("server.worldserver", "Cannot connect to Character database %s", dbString.c_str()); + return false; + } + + ///- Get login database info from configuration file + dbString = sConfigMgr->GetStringDefault("LoginDatabaseInfo", ""); + if (dbString.empty()) + { + TC_LOG_ERROR("server.worldserver", "Login database not specified in configuration file"); + return false; + } + + asyncThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1)); + if (asyncThreads < 1 || asyncThreads > 32) + { + TC_LOG_ERROR("server.worldserver", "Login database: invalid number of worker threads specified. " + "Please pick a value between 1 and 32."); + return false; + } + + synchThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1)); + ///- Initialise the login database + if (!LoginDatabase.Open(dbString, asyncThreads, synchThreads)) + { + TC_LOG_ERROR("server.worldserver", "Cannot connect to login database %s", dbString.c_str()); + return false; + } + + ///- Get the realm Id from the configuration file + realmID = sConfigMgr->GetIntDefault("RealmID", 0); + if (!realmID) + { + TC_LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file"); + return false; + } + TC_LOG_INFO("server.worldserver", "Realm running as realm ID %d", realmID); + + ///- Clean the database before starting + ClearOnlineAccounts(); + + ///- Insert version info into DB + WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", _FULLVERSION, _HASH); // One-time query + + 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 + LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = %d)", realmID); + + // 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"); } /// @} diff --git a/src/server/worldserver/Master.cpp b/src/server/worldserver/Master.cpp deleted file mode 100644 index c529e6b6478..00000000000 --- a/src/server/worldserver/Master.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * 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/>. - */ - -/** \file - \ingroup Trinityd -*/ - -#include <thread> - -#include "Master.h" - -#include "SystemConfig.h" -#include "World.h" -#include "WorldRunnable.h" -#include "WorldSocket.h" -#include "WorldSocketMgr.h" -#include "Configuration/Config.h" -#include "Database/DatabaseEnv.h" -#include "Database/DatabaseWorkerPool.h" -#include "CliRunnable.h" -#include "Log.h" -#include "TCSoap.h" -#include "Timer.h" -#include "Util.h" -#include "RealmList.h" -#include "BigNumber.h" -#include "OpenSSLCrypto.h" -#include "AsyncAcceptor.h" - -#ifdef _WIN32 -#include "ServiceWin32.h" -extern int m_ServiceStatus; -#endif - -#ifdef __linux__ -#include <sched.h> -#include <sys/resource.h> -#define PROCESS_HIGH_PRIORITY -15 // [-20, 19], default is 0 -#endif - -boost::asio::io_service _ioService; - -void SignalHandler(const boost::system::error_code& error, int signalNumber) -{ - if (!error) - { - switch (signalNumber) - { - case SIGINT: - case SIGTERM: - _ioService.stop(); - break; - } - } -} - - -void FreezeDetectorThread(uint32 delayTime) -{ - if (!delayTime) - return; - - TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", delayTime / 1000); - uint32 loops = 0; - uint32 lastChange = 0; - - while (!World::IsStopped()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - uint32 curtime = getMSTime(); - // normal work - uint32 worldLoopCounter = World::m_worldLoopCounter; - if (loops != worldLoopCounter) - { - lastChange = curtime; - loops = worldLoopCounter; - } - // possible freeze - else if (getMSTimeDiff(lastChange, curtime) > delayTime) - { - TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!"); - ASSERT(false); - } - } - TC_LOG_INFO("server.worldserver", "Anti-freeze thread exiting without problems."); -} - -/// Main function -int Master::Run() -{ - OpenSSLCrypto::threadsSetup(); - BigNumber seed1; - seed1.SetRand(16 * 8); - - TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon)", _FULLVERSION); - TC_LOG_INFO("server.worldserver", "<Ctrl-C> to stop.\n"); - - TC_LOG_INFO("server.worldserver", " ______ __"); - TC_LOG_INFO("server.worldserver", "/\\__ _\\ __ __/\\ \\__"); - TC_LOG_INFO("server.worldserver", "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\, _\\ __ __"); - TC_LOG_INFO("server.worldserver", " \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\"); - TC_LOG_INFO("server.worldserver", " \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\"); - TC_LOG_INFO("server.worldserver", " \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\"); - TC_LOG_INFO("server.worldserver", " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\"); - TC_LOG_INFO("server.worldserver", " C O R E /\\___/"); - TC_LOG_INFO("server.worldserver", "http://TrinityCore.org \\/__/\n"); - - /// 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; - } - } - - ///- Start the databases - if (!_StartDB()) - return 1; - - // set server offline (not connectable) - LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_INVALID, realmID); - - ///- Initialize the World - sWorld->SetInitialWorldSettings(); - - // Set signal handlers - boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM); - signals.async_wait(SignalHandler); - - ///- Launch WorldRunnable thread - std::thread worldThread(WorldThread, std::ref(_ioService)); - - std::thread* cliThread = nullptr; - -#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 - { - ///- Launch CliRunnable thread - cliThread = new std::thread(CliThread); - } - - AsyncAcceptor<RASession>* raAcceptor = nullptr; - - if (sConfigMgr->GetBoolDefault("Ra.Enable", false)) - raAcceptor = StartRaSocketAcceptor(_ioService); - -#if defined(_WIN32) || defined(__linux__) - - ///- Handle affinity for multiple processors and process priority - uint32 affinity = sConfigMgr->GetIntDefault("UseProcessors", 0); - bool highPriority = sConfigMgr->GetBoolDefault("ProcessPriority", false); - -#ifdef _WIN32 // Windows - - HANDLE hProcess = GetCurrentProcess(); - - if (affinity > 0) - { - ULONG_PTR appAff; - ULONG_PTR sysAff; - - if (GetProcessAffinityMask(hProcess, &appAff, &sysAff)) - { - ULONG_PTR currentAffinity = affinity & appAff; // remove non accessible processors - - if (!currentAffinity) - TC_LOG_ERROR("server.worldserver", "Processors marked in UseProcessors bitmask (hex) %x are not accessible for the worldserver. Accessible processors bitmask (hex): %x", affinity, appAff); - else if (SetProcessAffinityMask(hProcess, currentAffinity)) - TC_LOG_INFO("server.worldserver", "Using processors (bitmask, hex): %x", currentAffinity); - else - TC_LOG_ERROR("server.worldserver", "Can't set used processors (hex): %x", currentAffinity); - } - } - - if (highPriority) - { - if (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS)) - TC_LOG_INFO("server.worldserver", "worldserver process priority class set to HIGH"); - else - TC_LOG_ERROR("server.worldserver", "Can't set worldserver process priority class."); - } - -#else // Linux - - if (affinity > 0) - { - cpu_set_t mask; - CPU_ZERO(&mask); - - for (unsigned int i = 0; i < sizeof(affinity) * 8; ++i) - if (affinity & (1 << i)) - CPU_SET(i, &mask); - - if (sched_setaffinity(0, sizeof(mask), &mask)) - TC_LOG_ERROR("server.worldserver", "Can't set used processors (hex): %x, error: %s", affinity, strerror(errno)); - else - { - CPU_ZERO(&mask); - sched_getaffinity(0, sizeof(mask), &mask); - TC_LOG_INFO("server.worldserver", "Using processors (bitmask, hex): %lx", *(__cpu_mask*)(&mask)); - } - } - - if (highPriority) - { - if (setpriority(PRIO_PROCESS, 0, PROCESS_HIGH_PRIORITY)) - TC_LOG_ERROR("server.worldserver", "Can't set worldserver process priority class, error: %s", strerror(errno)); - else - TC_LOG_INFO("server.worldserver", "worldserver process priority class set to %i", getpriority(PRIO_PROCESS, 0)); - } - -#endif -#endif - - //Start soap serving thread - std::thread* soapThread = nullptr; - - if (sConfigMgr->GetBoolDefault("SOAP.Enabled", false)) - { - soapThread = new std::thread(TCSoapThread, sConfigMgr->GetStringDefault("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetIntDefault("SOAP.Port", 7878))); - } - - std::thread* freezeDetectorThread = nullptr; - - ///- Start up freeze catcher thread - if (uint32 freezeDelay = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0)) - freezeDetectorThread = new std::thread(FreezeDetectorThread, freezeDelay); - - ///- Launch the world listener socket - uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD)); - std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); - - if (sWorldSocketMgr->StartNetwork(worldPort, bindIp.c_str()) == -1) - { - TC_LOG_ERROR("server.worldserver", "Failed to start network"); - World::StopNow(ERROR_EXIT_CODE); - // go down and shutdown the server - } - - // set server online (allow connecting now) - LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); - - TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", _FULLVERSION); - - _ioService.run(); - - // when the main thread closes the singletons get unloaded - // since worldrunnable uses them, it will crash if unloaded after master - worldThread.join(); - - if (soapThread != nullptr) - { - soapThread->join(); - delete soapThread; - } - - if (raAcceptor != nullptr) - delete raAcceptor; - - // set server offline - LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realmID); - - ///- Clean database before leaving - ClearOnlineAccounts(); - - _StopDB(); - - TC_LOG_INFO("server.worldserver", "Halting process..."); - - if (cliThread != nullptr) - { - #ifdef _WIN32 - - // this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API) - //_exit(1); - // 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); - - cliThread->join(); - - #endif - - delete cliThread; - } - - delete freezeDetectorThread; - - // for some unknown reason, unloading scripts here and not in worldrunnable - // fixes a memory leak related to detaching threads from the module - //UnloadScriptingModule(); - - OpenSSLCrypto::threadsCleanup(); - // Exit the process with specified return value - return World::GetExitCode(); -} - -/// Initialize connection to the databases -bool Master::_StartDB() -{ - MySQL::Library_Init(); - - std::string dbString; - uint8 asyncThreads, synchThreads; - - dbString = sConfigMgr->GetStringDefault("WorldDatabaseInfo", ""); - if (dbString.empty()) - { - TC_LOG_ERROR("server.worldserver", "World database not specified in configuration file"); - return false; - } - - asyncThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.WorkerThreads", 1)); - if (asyncThreads < 1 || asyncThreads > 32) - { - TC_LOG_ERROR("server.worldserver", "World database: invalid number of worker threads specified. " - "Please pick a value between 1 and 32."); - return false; - } - - synchThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.SynchThreads", 1)); - ///- Initialize the world database - if (!WorldDatabase.Open(dbString, asyncThreads, synchThreads)) - { - TC_LOG_ERROR("server.worldserver", "Cannot connect to world database %s", dbString.c_str()); - return false; - } - - ///- Get character database info from configuration file - dbString = sConfigMgr->GetStringDefault("CharacterDatabaseInfo", ""); - if (dbString.empty()) - { - TC_LOG_ERROR("server.worldserver", "Character database not specified in configuration file"); - return false; - } - - asyncThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.WorkerThreads", 1)); - if (asyncThreads < 1 || asyncThreads > 32) - { - TC_LOG_ERROR("server.worldserver", "Character database: invalid number of worker threads specified. " - "Please pick a value between 1 and 32."); - return false; - } - - synchThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.SynchThreads", 2)); - - ///- Initialize the Character database - if (!CharacterDatabase.Open(dbString, asyncThreads, synchThreads)) - { - TC_LOG_ERROR("server.worldserver", "Cannot connect to Character database %s", dbString.c_str()); - return false; - } - - ///- Get login database info from configuration file - dbString = sConfigMgr->GetStringDefault("LoginDatabaseInfo", ""); - if (dbString.empty()) - { - TC_LOG_ERROR("server.worldserver", "Login database not specified in configuration file"); - return false; - } - - asyncThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1)); - if (asyncThreads < 1 || asyncThreads > 32) - { - TC_LOG_ERROR("server.worldserver", "Login database: invalid number of worker threads specified. " - "Please pick a value between 1 and 32."); - return false; - } - - synchThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1)); - ///- Initialise the login database - if (!LoginDatabase.Open(dbString, asyncThreads, synchThreads)) - { - TC_LOG_ERROR("server.worldserver", "Cannot connect to login database %s", dbString.c_str()); - return false; - } - - ///- Get the realm Id from the configuration file - realmID = sConfigMgr->GetIntDefault("RealmID", 0); - if (!realmID) - { - TC_LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file"); - return false; - } - TC_LOG_INFO("server.worldserver", "Realm running as realm ID %d", realmID); - - ///- Clean the database before starting - ClearOnlineAccounts(); - - ///- Insert version info into DB - WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", _FULLVERSION, _HASH); // One-time query - - sWorld->LoadDBVersion(); - - TC_LOG_INFO("server.worldserver", "Using World DB: %s", sWorld->GetDBVersion()); - return true; -} - -void Master::_StopDB() -{ - CharacterDatabase.Close(); - WorldDatabase.Close(); - LoginDatabase.Close(); - - MySQL::Library_End(); -} - -/// Clear 'online' status for all accounts with characters in this realm -void Master::ClearOnlineAccounts() -{ - // Reset online status for all accounts with characters on the current realm - LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = %d)", realmID); - - // 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"); -} - -AsyncAcceptor<RASession>* Master::StartRaSocketAcceptor(boost::asio::io_service& ioService) -{ - uint16 raPort = uint16(sConfigMgr->GetIntDefault("Ra.Port", 3443)); - std::string raListener = sConfigMgr->GetStringDefault("Ra.IP", "0.0.0.0"); - - return new AsyncAcceptor<RASession>(ioService, raListener, raPort); -} diff --git a/src/server/worldserver/Master.h b/src/server/worldserver/Master.h deleted file mode 100644 index 86059e6417a..00000000000 --- a/src/server/worldserver/Master.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * 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/>. - */ - -/// \addtogroup Trinityd -/// @{ -/// \file - -#ifndef _MASTER_H -#define _MASTER_H - -#include <boost/asio/io_service.hpp> -#include "Common.h" -#include "RASession.h" - -template<typename T> -class AsyncAcceptor; - -/// Start the server -class Master -{ - public: - static Master* instance() - { - static Master* instance = new Master(); - return instance; - } - - int Run(); - - private: - bool _StartDB(); - void _StopDB(); - - void ClearOnlineAccounts(); - AsyncAcceptor<RASession>* StartRaSocketAcceptor(boost::asio::io_service& ioService); -}; - -#define sMaster Master::instance() - -#endif - -/// @} diff --git a/src/server/worldserver/WorldThread/WorldRunnable.cpp b/src/server/worldserver/WorldThread/WorldRunnable.cpp deleted file mode 100644 index 85c3e7a74b9..00000000000 --- a/src/server/worldserver/WorldThread/WorldRunnable.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * 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/>. - */ - -/** \file - \ingroup Trinityd -*/ - -#include <thread> - -#include "Common.h" -#include "ObjectAccessor.h" -#include "World.h" -#include "WorldSocketMgr.h" -#include "Database/DatabaseEnv.h" -#include "ScriptMgr.h" -#include "BattlegroundMgr.h" -#include "MapManager.h" -#include "Timer.h" -#include "WorldRunnable.h" -#include "OutdoorPvPMgr.h" - -#define WORLD_SLEEP_CONST 50 - -#ifdef _WIN32 -#include "ServiceWin32.h" -extern int m_ServiceStatus; -#endif - -/// Heartbeat for the World -void WorldThread(boost::asio::io_service& ioService) -{ - uint32 realCurrTime = 0; - uint32 realPrevTime = getMSTime(); - - uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST - - sScriptMgr->OnStartup(); - - ///- 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; - - // diff (D0) include time of previous sleep (d0) + tick time (t0) - // we want that next d1 + t1 == WORLD_SLEEP_CONST - // we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement - // d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0 - if (diff <= WORLD_SLEEP_CONST+prevSleepTime) - { - prevSleepTime = WORLD_SLEEP_CONST+prevSleepTime-diff; - - std::this_thread::sleep_for(std::chrono::milliseconds(prevSleepTime)); - } - else - prevSleepTime = 0; - - #ifdef _WIN32 - if (m_ServiceStatus == 0) - World::StopNow(SHUTDOWN_EXIT_CODE); - - while (m_ServiceStatus == 2) - Sleep(1000); - #endif - } - - ioService.stop(); - - sScriptMgr->OnShutdown(); - - sWorld->KickAll(); // save and kick all players - sWorld->UpdateSessions( 1 ); // real players unload required UpdateSessions call - - // unload battleground templates before different singletons destroyed - sBattlegroundMgr->DeleteAllBattlegrounds(); - - sWorldSocketMgr->StopNetwork(); - - sMapMgr->UnloadAll(); // unload all grids (including locked in memory) - sObjectAccessor->UnloadAll(); // unload 'i_player2corpse' storage and remove from world - sScriptMgr->Unload(); - sOutdoorPvPMgr->Die(); -} diff --git a/src/server/worldserver/WorldThread/WorldRunnable.h b/src/server/worldserver/WorldThread/WorldRunnable.h deleted file mode 100644 index c6d00d269e7..00000000000 --- a/src/server/worldserver/WorldThread/WorldRunnable.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> - * - * 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/>. - */ - -/// \addtogroup Trinityd -/// @{ -/// \file - -#ifndef __WORLDRUNNABLE_H -#define __WORLDRUNNABLE_H - -#include <boost/asio/io_service.hpp> - -void WorldThread(boost::asio::io_service& ioService); - -#endif - -/// @} diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 90f330bac42..c4e3ad832a7 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -144,6 +144,14 @@ WorldServerPort = 8085 BindIP = "0.0.0.0" # +# ThreadPool +# Description: Number of threads to be used for the global thread pool +# The thread pool is currently used for the signal handler and remote access +# Default: 1 + +ThreadPool = 1 + +# ################################################################################################### ################################################################################################### @@ -161,7 +169,8 @@ UseProcessors = 0 # # ProcessPriority # Description: Process priority setting for Windows and Linux based systems. -# Details: On Linux, a nice value of -15 is used. (requires superuser). On Windows, process is set to HIGH class. +# Details: On Linux, a nice value of -15 is used. (requires superuser). +# On Windows, process is set to HIGH class. # Default: 0 - (Normal) # 1 - (High) @@ -1069,7 +1078,8 @@ Account.PasswordChangeSecurity = 0 # # BirthdayTime -# Description: Set to date of project's birth in UNIX time. By default the date when TrinityCore was started (Thu Oct 2, 2008) +# Description: Set to date of project's birth in UNIX time. By default the date when +# TrinityCore was started (Thu Oct 2, 2008) # Default: 1222964635 # # @@ -2627,8 +2637,10 @@ UI.ShowQuestLevelsInDialogs = 0 # 1 - Prefix Timestamp to the text # 2 - Prefix Log Level to the text # 4 - Prefix Log Filter type to the text -# 8 - Append timestamp to the log file name. Format: YYYY-MM-DD_HH-MM-SS (Only used with Type = 2) -# 16 - Make a backup of existing file before overwrite (Only used with Mode = w) +# 8 - Append timestamp to the log file name. Format: YYYY-MM-DD_HH-MM-SS +# (Only used with Type = 2) +# 16 - Make a backup of existing file before overwrite +# (Only used with Mode = w) # # Colors (read as optional1 if Type = Console) # Format: "fatal error warn info debug trace" @@ -2744,7 +2756,8 @@ Log.Async.Enable = 0 # # Allow.IP.Based.Action.Logging -# Description: Logs actions, e.g. account login and logout to name a few, based on IP of current session. +# Description: Logs actions, e.g. account login and logout to name a few, based on IP of +# current session. # Default: 0 - (Disabled) # 1 - (Enabled) |