From 310f5e68467ee61b66796fdd88c7c9691d4bd2a0 Mon Sep 17 00:00:00 2001 From: leak Date: Wed, 2 Jul 2014 17:38:44 +0200 Subject: Some ground work for ASIO based RemoteAccess handling --- src/server/worldserver/Main.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 75d9ca5145d..bcc058c7fb3 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -22,7 +22,6 @@ #include #include -#include #include "Common.h" #include "Database/DatabaseEnv.h" @@ -135,7 +134,7 @@ extern int main(int argc, char** argv) 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 ACE version: %s", ACE_VERSION); + TC_LOG_INFO("server.worldserver", "Using Boost version: %s", BOOST_LIB_VERSION); ///- and run the 'Master' /// @todo Why do we need this 'Master'? Can't all of this be in the Main as for Realmd? -- cgit v1.2.3 From 021e18d152b68b9d2a0c5887bab7eb7b61ecd2ca Mon Sep 17 00:00:00 2001 From: leak Date: Fri, 4 Jul 2014 15:22:06 +0200 Subject: Refactored both world and auth main - Master/Worldrunable removed - World Update loop now running on main (which was doing nothing before) - Processpriority moved to shared - Added a preliminary thread pool for boost::asio::io_service --- src/server/authserver/Main.cpp | 160 ++----- src/server/shared/Threading/ProcessPriority.h | 100 +++++ src/server/worldserver/CMakeLists.txt | 3 - src/server/worldserver/Main.cpp | 487 +++++++++++++++++++-- src/server/worldserver/Master.cpp | 479 -------------------- src/server/worldserver/Master.h | 57 --- .../worldserver/WorldThread/WorldRunnable.cpp | 103 ----- src/server/worldserver/WorldThread/WorldRunnable.h | 32 -- src/server/worldserver/worldserver.conf.dist | 23 +- 9 files changed, 616 insertions(+), 828 deletions(-) create mode 100644 src/server/shared/Threading/ProcessPriority.h delete mode 100644 src/server/worldserver/Master.cpp delete mode 100644 src/server/worldserver/Master.h delete mode 100644 src/server/worldserver/WorldThread/WorldRunnable.cpp delete mode 100644 src/server/worldserver/WorldThread/WorldRunnable.h (limited to 'src/server/worldserver/Main.cpp') 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 -#include -#include #include #include +#include +#include +#include +#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 -#include -#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 []\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", " 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 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 []\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 +* +* 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 . +*/ + +#ifndef _PROCESSPRIO_H +#define _PROCESSPRIO_H + +#include "Configuration/Config.h" + +#ifdef __linux__ +#include +#include +#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 #include +#include #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 []\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* 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", " 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 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* 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 []\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* 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(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 - * Copyright (C) 2005-2009 MaNGOS - * - * 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 . - */ - -/** \file - \ingroup Trinityd -*/ - -#include - -#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 -#include -#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", " 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* 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* 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(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 - * Copyright (C) 2005-2009 MaNGOS - * - * 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 . - */ - -/// \addtogroup Trinityd -/// @{ -/// \file - -#ifndef _MASTER_H -#define _MASTER_H - -#include -#include "Common.h" -#include "RASession.h" - -template -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* 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 - * Copyright (C) 2005-2009 MaNGOS - * - * 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 . - */ - -/** \file - \ingroup Trinityd -*/ - -#include - -#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 - * Copyright (C) 2005-2009 MaNGOS - * - * 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 . - */ - -/// \addtogroup Trinityd -/// @{ -/// \file - -#ifndef __WORLDRUNNABLE_H -#define __WORLDRUNNABLE_H - -#include - -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 @@ -143,6 +143,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) -- cgit v1.2.3 From 7befb26625dd1eeb237e223296d9ed8817297025 Mon Sep 17 00:00:00 2001 From: leak Date: Sun, 6 Jul 2014 01:26:29 +0200 Subject: Some groundwork for replacing the ACE based WorldSocket handling --- src/server/game/Scripting/ScriptMgr.cpp | 10 +- src/server/game/Scripting/ScriptMgr.h | 24 +- src/server/game/Server/Protocol/ServerPktHeader.h | 59 ++ src/server/game/Server/WorldSession.cpp | 20 +- src/server/game/Server/WorldSession.h | 6 +- src/server/game/Server/WorldSocket.cpp | 1050 --------------------- src/server/game/Server/WorldSocket.h | 218 ----- src/server/game/Server/WorldSocketAcceptor.h | 67 -- src/server/game/Server/WorldSocketMgr.cpp | 360 ------- src/server/game/Server/WorldSocketMgr.h | 78 -- src/server/game/Server/WorldTcpSession.cpp | 407 ++++++++ src/server/game/Server/WorldTcpSession.h | 81 ++ src/server/worldserver/Main.cpp | 18 +- 13 files changed, 579 insertions(+), 1819 deletions(-) create mode 100644 src/server/game/Server/Protocol/ServerPktHeader.h delete mode 100644 src/server/game/Server/WorldSocket.cpp delete mode 100644 src/server/game/Server/WorldSocket.h delete mode 100644 src/server/game/Server/WorldSocketAcceptor.h delete mode 100644 src/server/game/Server/WorldSocketMgr.cpp delete mode 100644 src/server/game/Server/WorldSocketMgr.h create mode 100644 src/server/game/Server/WorldTcpSession.cpp create mode 100644 src/server/game/Server/WorldTcpSession.h (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 5821ae3eb3c..06f6094c511 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -400,35 +400,35 @@ void ScriptMgr::OnNetworkStop() FOREACH_SCRIPT(ServerScript)->OnNetworkStop(); } -void ScriptMgr::OnSocketOpen(WorldSocket* socket) +void ScriptMgr::OnSocketOpen(WorldTcpSession* socket) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnSocketOpen(socket); } -void ScriptMgr::OnSocketClose(WorldSocket* socket, bool wasNew) +void ScriptMgr::OnSocketClose(WorldTcpSession* socket, bool wasNew) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnSocketClose(socket, wasNew); } -void ScriptMgr::OnPacketReceive(WorldSocket* socket, WorldPacket packet) +void ScriptMgr::OnPacketReceive(WorldTcpSession* socket, WorldPacket packet) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnPacketReceive(socket, packet); } -void ScriptMgr::OnPacketSend(WorldSocket* socket, WorldPacket packet) +void ScriptMgr::OnPacketSend(WorldTcpSession* socket, WorldPacket packet) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnPacketSend(socket, packet); } -void ScriptMgr::OnUnknownPacketReceive(WorldSocket* socket, WorldPacket packet) +void ScriptMgr::OnUnknownPacketReceive(WorldTcpSession* socket, WorldPacket packet) { ASSERT(socket); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 0126c649019..530d99b9ad2 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -57,7 +57,7 @@ class Transport; class Unit; class Vehicle; class WorldPacket; -class WorldSocket; +class WorldTcpSession; class WorldObject; struct AchievementCriteriaData; @@ -214,30 +214,30 @@ class ServerScript : public ScriptObject public: - // Called when reactive socket I/O is started (WorldSocketMgr). + // Called when reactive socket I/O is started (WorldTcpSessionMgr). virtual void OnNetworkStart() { } // Called when reactive I/O is stopped. virtual void OnNetworkStop() { } // Called when a remote socket establishes a connection to the server. Do not store the socket object. - virtual void OnSocketOpen(WorldSocket* /*socket*/) { } + virtual void OnSocketOpen(WorldTcpSession* /*socket*/) { } // Called when a socket is closed. Do not store the socket object, and do not rely on the connection // being open; it is not. - virtual void OnSocketClose(WorldSocket* /*socket*/, bool /*wasNew*/) { } + virtual void OnSocketClose(WorldTcpSession* /*socket*/, bool /*wasNew*/) { } // Called when a packet is sent to a client. The packet object is a copy of the original packet, so reading // and modifying it is safe. - virtual void OnPacketSend(WorldSocket* /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnPacketSend(WorldTcpSession* /*socket*/, WorldPacket& /*packet*/) { } // Called when a (valid) packet is received by a client. The packet object is a copy of the original packet, so // reading and modifying it is safe. - virtual void OnPacketReceive(WorldSocket* /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnPacketReceive(WorldTcpSession* /*socket*/, WorldPacket& /*packet*/) { } // Called when an invalid (unknown opcode) packet is received by a client. The packet is a reference to the orignal // packet; not a copy. This allows you to actually handle unknown packets (for whatever purpose). - virtual void OnUnknownPacketReceive(WorldSocket* /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnUnknownPacketReceive(WorldTcpSession* /*socket*/, WorldPacket& /*packet*/) { } }; class WorldScript : public ScriptObject @@ -908,11 +908,11 @@ class ScriptMgr void OnNetworkStart(); void OnNetworkStop(); - void OnSocketOpen(WorldSocket* socket); - void OnSocketClose(WorldSocket* socket, bool wasNew); - void OnPacketReceive(WorldSocket* socket, WorldPacket packet); - void OnPacketSend(WorldSocket* socket, WorldPacket packet); - void OnUnknownPacketReceive(WorldSocket* socket, WorldPacket packet); + void OnSocketOpen(WorldTcpSession* socket); + void OnSocketClose(WorldTcpSession* socket, bool wasNew); + void OnPacketReceive(WorldTcpSession* socket, WorldPacket packet); + void OnPacketSend(WorldTcpSession* socket, WorldPacket packet); + void OnUnknownPacketReceive(WorldTcpSession* socket, WorldPacket packet); public: /* WorldScript */ diff --git a/src/server/game/Server/Protocol/ServerPktHeader.h b/src/server/game/Server/Protocol/ServerPktHeader.h new file mode 100644 index 00000000000..4b0dae9f0f3 --- /dev/null +++ b/src/server/game/Server/Protocol/ServerPktHeader.h @@ -0,0 +1,59 @@ +/* +* Copyright (C) 2008-2014 TrinityCore +* Copyright (C) 2005-2009 MaNGOS +* +* 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 . +*/ + +#ifndef __SERVERPKTHDR_H__ +#define __SERVERPKTHDR_H__ + +#include "Log.h" + +struct ServerPktHeader +{ + /** + * size is the length of the payload _plus_ the length of the opcode + */ + ServerPktHeader(uint32 size, uint16 cmd) : size(size) + { + uint8 headerIndex=0; + if (isLargePacket()) + { + TC_LOG_DEBUG("network", "initializing large server to client packet. Size: %u, cmd: %u", size, cmd); + header[headerIndex++] = 0x80 | (0xFF & (size >> 16)); + } + header[headerIndex++] = 0xFF &(size >> 8); + header[headerIndex++] = 0xFF & size; + + header[headerIndex++] = 0xFF & cmd; + header[headerIndex++] = 0xFF & (cmd >> 8); + } + + uint8 getHeaderLength() + { + // cmd = 2 bytes, size= 2||3bytes + return 2 + (isLargePacket() ? 3 : 2); + } + + bool isLargePacket() const + { + return size > 0x7FFF; + } + + const uint32 size; + uint8 header[5]; +}; + +#endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 444c06e41b8..4f3112403db 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -20,7 +20,7 @@ \ingroup u2w */ -#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it +#include "WorldTcpSession.h" #include "Config.h" #include "Common.h" #include "DatabaseEnv.h" @@ -97,7 +97,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): +WorldSession::WorldSession(uint32 id, WorldTcpSession* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): m_muteTime(mute_time), m_timeOutTime(0), AntiDOS(this), @@ -130,8 +130,7 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 if (sock) { - m_Address = sock->GetRemoteAddress(); - sock->AddReference(); + m_Address = sock->GetRemoteIpAddress(); ResetTimeOutTime(); LoginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId()); // One-time query } @@ -150,7 +149,6 @@ WorldSession::~WorldSession() if (m_Socket) { m_Socket->CloseSocket(); - m_Socket->RemoveReference(); m_Socket = NULL; } @@ -227,8 +225,7 @@ void WorldSession::SendPacket(WorldPacket const* packet) } #endif // !TRINITY_DEBUG - if (m_Socket->SendPacket(*packet) == -1) - m_Socket->CloseSocket(); + m_Socket->AsyncWrite(*packet); } /// Add an incoming packet to the queue @@ -281,9 +278,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) uint32 processedPackets = 0; time_t currentTime = time(NULL); - while (m_Socket && !m_Socket->IsClosed() && - !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && - _recvQueue.next(packet, updater)) + while (m_Socket && !_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket && _recvQueue.next(packet, updater)) { if (!AntiDOS.EvaluateOpcode(*packet, currentTime)) { @@ -402,7 +397,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) break; } - if (m_Socket && !m_Socket->IsClosed() && _warden) + if (m_Socket && m_Socket->IsOpen() && _warden) _warden->Update(); ProcessQueryCallbacks(); @@ -420,12 +415,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) _warden->Update(); ///- Cleanup socket pointer if need - if (m_Socket && m_Socket->IsClosed()) + if (m_Socket && !m_Socket->IsOpen()) { expireTime -= expireTime > diff ? diff : expireTime; if (expireTime < diff || forceExit) { - m_Socket->RemoveReference(); m_Socket = NULL; } } diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 4cb6ec2d7af..709650a8119 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -46,7 +46,7 @@ class SpellCastTargets; class Unit; class Warden; class WorldPacket; -class WorldSocket; +class WorldTcpSession; struct AreaTableEntry; struct AuctionEntry; struct DeclinedName; @@ -208,7 +208,7 @@ struct PacketCounter class WorldSession { public: - WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); + WorldSession(uint32 id, WorldTcpSession* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); ~WorldSession(); bool PlayerLoading() const { return m_playerLoading; } @@ -981,7 +981,7 @@ class WorldSession uint32 m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set) Player* _player; - WorldSocket* m_Socket; + WorldTcpSession* m_Socket; std::string m_Address; // Current Remote Address // std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session! diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp deleted file mode 100644 index d35ee80099d..00000000000 --- a/src/server/game/Server/WorldSocket.cpp +++ /dev/null @@ -1,1050 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - * - * 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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "WorldSocket.h" -#include "Common.h" -#include "Player.h" -#include "Util.h" -#include "World.h" -#include "WorldPacket.h" -#include "SharedDefines.h" -#include "ByteBuffer.h" -#include "Opcodes.h" -#include "DatabaseEnv.h" -#include "BigNumber.h" -#include "SHA1.h" -#include "WorldSession.h" -#include "WorldSocketMgr.h" -#include "Log.h" -#include "PacketLog.h" -#include "ScriptMgr.h" -#include "AccountMgr.h" - -#if defined(__GNUC__) -#pragma pack(1) -#else -#pragma pack(push, 1) -#endif - -struct ServerPktHeader -{ - /** - * size is the length of the payload _plus_ the length of the opcode - */ - ServerPktHeader(uint32 size, uint16 cmd) : size(size) - { - uint8 headerIndex=0; - if (isLargePacket()) - { - TC_LOG_DEBUG("network", "initializing large server to client packet. Size: %u, cmd: %u", size, cmd); - header[headerIndex++] = 0x80 | (0xFF & (size >> 16)); - } - header[headerIndex++] = 0xFF &(size >> 8); - header[headerIndex++] = 0xFF & size; - - header[headerIndex++] = 0xFF & cmd; - header[headerIndex++] = 0xFF & (cmd >> 8); - } - - uint8 getHeaderLength() - { - // cmd = 2 bytes, size= 2||3bytes - return 2 + (isLargePacket() ? 3 : 2); - } - - bool isLargePacket() const - { - return size > 0x7FFF; - } - - const uint32 size; - uint8 header[5]; -}; - -struct ClientPktHeader -{ - uint16 size; - uint32 cmd; -}; - -#if defined(__GNUC__) -#pragma pack() -#else -#pragma pack(pop) -#endif - -WorldSocket::WorldSocket (void): WorldHandler(), -m_LastPingTime(ACE_Time_Value::zero), m_OverSpeedPings(0), m_Session(0), -m_RecvWPct(0), m_RecvPct(), m_Header(sizeof (ClientPktHeader)), -m_OutBuffer(0), m_OutBufferSize(65536), m_OutActive(false), -m_Seed(static_cast (rand32())) -{ - reference_counting_policy().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED); - - msg_queue()->high_water_mark(8 * 1024 * 1024); - msg_queue()->low_water_mark(8 * 1024 * 1024); -} - -WorldSocket::~WorldSocket (void) -{ - delete m_RecvWPct; - - if (m_OutBuffer) - m_OutBuffer->release(); - - closing_ = true; - - peer().close(); -} - -bool WorldSocket::IsClosed (void) const -{ - return closing_; -} - -void WorldSocket::CloseSocket (void) -{ - { - ACE_GUARD (LockType, Guard, m_OutBufferLock); - - if (closing_) - return; - - closing_ = true; - peer().close_writer(); - } - - { - ACE_GUARD (LockType, Guard, m_SessionLock); - - m_Session = NULL; - } -} - -const std::string& WorldSocket::GetRemoteAddress (void) const -{ - return m_Address; -} - -int WorldSocket::SendPacket(WorldPacket const& pct) -{ - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); - - if (closing_) - return -1; - - // Dump outgoing packet - if (sPacketLog->CanLogPacket()) - sPacketLog->LogPacket(pct, SERVER_TO_CLIENT); - - WorldPacket const* pkt = &pct; - - - if (m_Session) - TC_LOG_TRACE("network.opcode", "S->C: %s %s", m_Session->GetPlayerInfo().c_str(), GetOpcodeNameForLogging(pkt->GetOpcode()).c_str()); - - sScriptMgr->OnPacketSend(this, *pkt); - - ServerPktHeader header(pkt->size()+2, pkt->GetOpcode()); - m_Crypt.EncryptSend ((uint8*)header.header, header.getHeaderLength()); - - if (m_OutBuffer->space() >= pkt->size() + header.getHeaderLength() && msg_queue()->is_empty()) - { - // Put the packet on the buffer. - if (m_OutBuffer->copy((char*) header.header, header.getHeaderLength()) == -1) - ACE_ASSERT (false); - - if (!pkt->empty()) - if (m_OutBuffer->copy((char*) pkt->contents(), pkt->size()) == -1) - ACE_ASSERT (false); - } - else - { - // Enqueue the packet. - ACE_Message_Block* mb; - - ACE_NEW_RETURN(mb, ACE_Message_Block(pkt->size() + header.getHeaderLength()), -1); - - mb->copy((char*) header.header, header.getHeaderLength()); - - if (!pkt->empty()) - mb->copy((const char*)pkt->contents(), pkt->size()); - - if (msg_queue()->enqueue_tail(mb, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::SendPacket enqueue_tail failed"); - mb->release(); - return -1; - } - } - - return 0; -} - -long WorldSocket::AddReference (void) -{ - return static_cast (add_reference()); -} - -long WorldSocket::RemoveReference (void) -{ - return static_cast (remove_reference()); -} - -int WorldSocket::open (void *a) -{ - ACE_UNUSED_ARG (a); - - // Prevent double call to this func. - if (m_OutBuffer) - return -1; - - // This will also prevent the socket from being Updated - // while we are initializing it. - m_OutActive = true; - - // Hook for the manager. - if (sWorldSocketMgr->OnSocketOpen(this) == -1) - return -1; - - // Allocate the buffer. - ACE_NEW_RETURN (m_OutBuffer, ACE_Message_Block (m_OutBufferSize), -1); - - // Store peer address. - ACE_INET_Addr remote_addr; - - if (peer().get_remote_addr(remote_addr) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::open: peer().get_remote_addr errno = %s", ACE_OS::strerror (errno)); - return -1; - } - - m_Address = remote_addr.get_host_addr(); - - if (HandleSendAuthSession() == -1) - return -1; - - // Register with ACE Reactor - if (reactor()->register_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::open: unable to register client handler errno = %s", ACE_OS::strerror (errno)); - return -1; - } - - // reactor takes care of the socket from now on - remove_reference(); - - return 0; -} - -int WorldSocket::close(u_long) -{ - shutdown(); - - closing_ = true; - - remove_reference(); - - return 0; -} - -int WorldSocket::handle_input(ACE_HANDLE) -{ - if (closing_) - return -1; - - switch (handle_input_missing_data()) - { - case -1 : - { - if ((errno == EWOULDBLOCK) || - (errno == EAGAIN)) - { - return Update(); // interesting line, isn't it ? - } - - TC_LOG_DEBUG("network", "WorldSocket::handle_input: Peer error closing connection errno = %s", ACE_OS::strerror (errno)); - - errno = ECONNRESET; - return -1; - } - case 0: - { - TC_LOG_DEBUG("network", "WorldSocket::handle_input: Peer has closed connection"); - - errno = ECONNRESET; - return -1; - } - case 1: - return 1; - default: - return Update(); // another interesting line ;) - } - - ACE_NOTREACHED(return -1); -} - -int WorldSocket::handle_output(ACE_HANDLE) -{ - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); - - if (closing_) - return -1; - - size_t send_len = m_OutBuffer->length(); - - if (send_len == 0) - return handle_output_queue(Guard); - -#ifdef MSG_NOSIGNAL - ssize_t n = peer().send (m_OutBuffer->rd_ptr(), send_len, MSG_NOSIGNAL); -#else - ssize_t n = peer().send (m_OutBuffer->rd_ptr(), send_len); -#endif // MSG_NOSIGNAL - - if (n == 0) - return -1; - else if (n == -1) - { - if (errno == EWOULDBLOCK || errno == EAGAIN) - return schedule_wakeup_output (Guard); - - return -1; - } - else if (n < (ssize_t)send_len) //now n > 0 - { - m_OutBuffer->rd_ptr (static_cast (n)); - - // move the data to the base of the buffer - m_OutBuffer->crunch(); - - return schedule_wakeup_output (Guard); - } - else //now n == send_len - { - m_OutBuffer->reset(); - - return handle_output_queue (Guard); - } - - ACE_NOTREACHED (return 0); -} - -int WorldSocket::handle_output_queue(GuardType& g) -{ - if (msg_queue()->is_empty()) - return cancel_wakeup_output(g); - - ACE_Message_Block* mblk; - - if (msg_queue()->dequeue_head(mblk, (ACE_Time_Value*)&ACE_Time_Value::zero) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::handle_output_queue dequeue_head"); - return -1; - } - - const size_t send_len = mblk->length(); - -#ifdef MSG_NOSIGNAL - ssize_t n = peer().send(mblk->rd_ptr(), send_len, MSG_NOSIGNAL); -#else - ssize_t n = peer().send(mblk->rd_ptr(), send_len); -#endif // MSG_NOSIGNAL - - if (n == 0) - { - mblk->release(); - - return -1; - } - else if (n == -1) - { - if (errno == EWOULDBLOCK || errno == EAGAIN) - { - msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero); - return schedule_wakeup_output (g); - } - - mblk->release(); - return -1; - } - else if (n < (ssize_t)send_len) //now n > 0 - { - mblk->rd_ptr(static_cast (n)); - - if (msg_queue()->enqueue_head(mblk, (ACE_Time_Value*) &ACE_Time_Value::zero) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::handle_output_queue enqueue_head"); - mblk->release(); - return -1; - } - - return schedule_wakeup_output (g); - } - else //now n == send_len - { - mblk->release(); - - return msg_queue()->is_empty() ? cancel_wakeup_output(g) : ACE_Event_Handler::WRITE_MASK; - } - - ACE_NOTREACHED(return -1); -} - -int WorldSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask) -{ - // Critical section - { - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1); - - closing_ = true; - - if (h == ACE_INVALID_HANDLE) - peer().close_writer(); - } - - // Critical section - { - ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); - - m_Session = NULL; - } - - reactor()->remove_handler(this, ACE_Event_Handler::DONT_CALL | ACE_Event_Handler::ALL_EVENTS_MASK); - return 0; -} - -int WorldSocket::Update (void) -{ - if (closing_) - return -1; - - if (m_OutActive) - return 0; - - { - ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, 0); - if (m_OutBuffer->length() == 0 && msg_queue()->is_empty()) - return 0; - } - - int ret; - do - ret = handle_output(get_handle()); - while (ret > 0); - - return ret; -} - -int WorldSocket::handle_input_header (void) -{ - ACE_ASSERT(m_RecvWPct == NULL); - - ACE_ASSERT(m_Header.length() == sizeof(ClientPktHeader)); - - m_Crypt.DecryptRecv ((uint8*)m_Header.rd_ptr(), sizeof(ClientPktHeader)); - - ClientPktHeader& header = *((ClientPktHeader*)m_Header.rd_ptr()); - - EndianConvertReverse(header.size); - EndianConvert(header.cmd); - - if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240)) - { - Player* _player = m_Session ? m_Session->GetPlayer() : NULL; - TC_LOG_ERROR("network", "WorldSocket::handle_input_header(): client (account: %u, char [GUID: %u, name: %s]) sent malformed packet (size: %d, cmd: %d)", - m_Session ? m_Session->GetAccountId() : 0, - _player ? _player->GetGUIDLow() : 0, - _player ? _player->GetName().c_str() : "", - header.size, header.cmd); - - errno = EINVAL; - return -1; - } - - header.size -= 4; - - ACE_NEW_RETURN(m_RecvWPct, WorldPacket ((uint16)header.cmd, header.size), -1); - - if (header.size > 0) - { - m_RecvWPct->resize(header.size); - m_RecvPct.base ((char*) m_RecvWPct->contents(), m_RecvWPct->size()); - } - else - { - ACE_ASSERT(m_RecvPct.space() == 0); - } - - return 0; -} - -int WorldSocket::handle_input_payload (void) -{ - // set errno properly here on error !!! - // now have a header and payload - - ACE_ASSERT (m_RecvPct.space() == 0); - ACE_ASSERT (m_Header.space() == 0); - ACE_ASSERT (m_RecvWPct != NULL); - - const int ret = ProcessIncoming (m_RecvWPct); - - m_RecvPct.base (NULL, 0); - m_RecvPct.reset(); - m_RecvWPct = NULL; - - m_Header.reset(); - - if (ret == -1) - errno = EINVAL; - - return ret; -} - -int WorldSocket::handle_input_missing_data (void) -{ - char buf [4096]; - - ACE_Data_Block db (sizeof (buf), - ACE_Message_Block::MB_DATA, - buf, - 0, - 0, - ACE_Message_Block::DONT_DELETE, - 0); - - ACE_Message_Block message_block(&db, - ACE_Message_Block::DONT_DELETE, - 0); - - const size_t recv_size = message_block.space(); - - const ssize_t n = peer().recv (message_block.wr_ptr(), - recv_size); - - if (n <= 0) - return int(n); - - message_block.wr_ptr (n); - - while (message_block.length() > 0) - { - if (m_Header.space() > 0) - { - //need to receive the header - const size_t to_header = (message_block.length() > m_Header.space() ? m_Header.space() : message_block.length()); - m_Header.copy (message_block.rd_ptr(), to_header); - message_block.rd_ptr (to_header); - - if (m_Header.space() > 0) - { - // Couldn't receive the whole header this time. - ACE_ASSERT (message_block.length() == 0); - errno = EWOULDBLOCK; - return -1; - } - - // We just received nice new header - if (handle_input_header() == -1) - { - ACE_ASSERT ((errno != EWOULDBLOCK) && (errno != EAGAIN)); - return -1; - } - } - - // Its possible on some error situations that this happens - // for example on closing when epoll receives more chunked data and stuff - // hope this is not hack, as proper m_RecvWPct is asserted around - if (!m_RecvWPct) - { - TC_LOG_ERROR("network", "Forcing close on input m_RecvWPct = NULL"); - errno = EINVAL; - return -1; - } - - // We have full read header, now check the data payload - if (m_RecvPct.space() > 0) - { - //need more data in the payload - const size_t to_data = (message_block.length() > m_RecvPct.space() ? m_RecvPct.space() : message_block.length()); - m_RecvPct.copy (message_block.rd_ptr(), to_data); - message_block.rd_ptr (to_data); - - if (m_RecvPct.space() > 0) - { - // Couldn't receive the whole data this time. - ACE_ASSERT (message_block.length() == 0); - errno = EWOULDBLOCK; - return -1; - } - } - - //just received fresh new payload - if (handle_input_payload() == -1) - { - ACE_ASSERT ((errno != EWOULDBLOCK) && (errno != EAGAIN)); - return -1; - } - } - - return size_t(n) == recv_size ? 1 : 2; -} - -int WorldSocket::cancel_wakeup_output(GuardType& g) -{ - if (!m_OutActive) - return 0; - - m_OutActive = false; - - g.release(); - - if (reactor()->cancel_wakeup - (this, ACE_Event_Handler::WRITE_MASK) == -1) - { - // would be good to store errno from reactor with errno guard - TC_LOG_ERROR("network", "WorldSocket::cancel_wakeup_output"); - return -1; - } - - return 0; -} - -int WorldSocket::schedule_wakeup_output(GuardType& g) -{ - if (m_OutActive) - return 0; - - m_OutActive = true; - - g.release(); - - if (reactor()->schedule_wakeup - (this, ACE_Event_Handler::WRITE_MASK) == -1) - { - TC_LOG_ERROR("network", "WorldSocket::schedule_wakeup_output"); - return -1; - } - - return 0; -} - -int WorldSocket::ProcessIncoming(WorldPacket* new_pct) -{ - ACE_ASSERT (new_pct); - - // manage memory ;) - ACE_Auto_Ptr aptr(new_pct); - - const ACE_UINT16 opcode = new_pct->GetOpcode(); - - if (closing_) - return -1; - - // Dump received packet. - if (sPacketLog->CanLogPacket()) - sPacketLog->LogPacket(*new_pct, CLIENT_TO_SERVER); - - std::string opcodeName = GetOpcodeNameForLogging(opcode); - if (m_Session) - TC_LOG_TRACE("network.opcode", "C->S: %s %s", m_Session->GetPlayerInfo().c_str(), opcodeName.c_str()); - - try - { - switch (opcode) - { - case CMSG_PING: - return HandlePing(*new_pct); - case CMSG_AUTH_SESSION: - if (m_Session) - { - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", m_Session->GetPlayerInfo().c_str()); - return -1; - } - - sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); - return HandleAuthSession(*new_pct); - case CMSG_KEEP_ALIVE: - TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); - sScriptMgr->OnPacketReceive(this, WorldPacket(*new_pct)); - return 0; - default: - { - ACE_GUARD_RETURN(LockType, Guard, m_SessionLock, -1); - if (!m_Session) - { - TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - return -1; - } - - // Our Idle timer will reset on any non PING opcodes. - // Catches people idling on the login screen and any lingering ingame connections. - m_Session->ResetTimeOutTime(); - - // OK, give the packet to WorldSession - aptr.release(); - // WARNING here we call it with locks held. - // Its possible to cause deadlock if QueuePacket calls back - m_Session->QueuePacket(new_pct); - return 0; - } - } - } - catch (ByteBufferException &) - { - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming ByteBufferException occured while parsing an instant handled packet %s from client %s, accountid=%i. Disconnected client.", - opcodeName.c_str(), GetRemoteAddress().c_str(), m_Session ? int32(m_Session->GetAccountId()) : -1); - new_pct->hexlike(); - return -1; - } - - ACE_NOTREACHED (return 0); -} - -int WorldSocket::HandleSendAuthSession() -{ - WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); - packet << uint32(1); // 1...31 - packet << uint32(m_Seed); - - BigNumber seed1; - seed1.SetRand(16 * 8); - packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds - - BigNumber seed2; - seed2.SetRand(16 * 8); - packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds - return SendPacket(packet); -} - -int WorldSocket::HandleAuthSession(WorldPacket& recvPacket) -{ - uint8 digest[20]; - uint32 clientSeed; - uint8 security; - uint32 id; - LocaleConstant locale; - std::string account; - SHA1Hash sha; - uint32 clientBuild; - uint32 unk2, unk3, unk5, unk6, unk7; - uint64 unk4; - WorldPacket packet, SendAddonPacked; - BigNumber k; - bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); - - if (sWorld->IsClosed()) - { - SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteAddress().c_str()); - return -1; - } - - // Read the content of the packet - recvPacket >> clientBuild; - recvPacket >> unk2; - recvPacket >> account; - recvPacket >> unk3; - recvPacket >> clientSeed; - recvPacket >> unk5 >> unk6 >> unk7; - recvPacket >> unk4; - recvPacket.read(digest, 20); - - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", - clientBuild, - unk2, - account.c_str(), - unk3, - clientSeed); - - // Get the account information from the realmd database - // 0 1 2 3 4 5 6 7 8 - // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ? - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); - - stmt->setString(0, account); - - PreparedQueryResult result = LoginDatabase.Query(stmt); - - // Stop if the account is not found - if (!result) - { - // We can not log here, as we do not know the account. Thus, no accountId. - SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); - return -1; - } - - Field* fields = result->Fetch(); - - uint8 expansion = fields[4].GetUInt8(); - uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); - if (expansion > world_expansion) - expansion = world_expansion; - - // For hook purposes, we get Remoteaddress at this point. - std::string address = GetRemoteAddress(); - - // As we don't know if attempted login process by ip works, we update last_attempt_ip right away - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); - - stmt->setString(0, address); - stmt->setString(1, account); - - LoginDatabase.Execute(stmt); - // This also allows to check for possible "hack" attempts on account - - // id has to be fetched at this point, so that first actual account response that fails can be logged - id = fields[0].GetUInt32(); - - ///- Re-check ip locking (same check as in realmd). - if (fields[3].GetUInt8() == 1) // if ip is locked - { - if (strcmp (fields[2].GetCString(), address.c_str())) - { - SendAuthResponseError(AUTH_FAILED); - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); - // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well - sScriptMgr->OnFailedAccountLogin(id); - return -1; - } - } - - k.SetHexStr(fields[1].GetCString()); - - int64 mutetime = fields[5].GetInt64(); - //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. - if (mutetime < 0) - { - mutetime = time(NULL) + llabs(mutetime); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN); - - stmt->setInt64(0, mutetime); - stmt->setUInt32(1, id); - - LoginDatabase.Execute(stmt); - } - - locale = LocaleConstant (fields[6].GetUInt8()); - if (locale >= TOTAL_LOCALES) - locale = LOCALE_enUS; - - uint32 recruiter = fields[7].GetUInt32(); - std::string os = fields[8].GetString(); - - // Must be done before WorldSession is created - if (wardenActive && os != "Win" && os != "OSX") - { - SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str()); - return -1; - } - - // Checks gmlevel per Realm - stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); - - stmt->setUInt32(0, id); - stmt->setInt32(1, int32(realmID)); - - result = LoginDatabase.Query(stmt); - - if (!result) - security = 0; - else - { - fields = result->Fetch(); - security = fields[0].GetUInt8(); - } - - // Re-check account ban (same check as in realmd) - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); - - stmt->setUInt32(0, id); - stmt->setString(1, address); - - PreparedQueryResult banresult = LoginDatabase.Query(stmt); - - if (banresult) // if account banned - { - SendAuthResponseError(AUTH_BANNED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); - sScriptMgr->OnFailedAccountLogin(id); - return -1; - } - - // Check locked state for server - AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); - TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); - if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) - { - SendAuthResponseError(AUTH_UNAVAILABLE); - TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); - sScriptMgr->OnFailedAccountLogin(id); - return -1; - } - - // Check that Key and account name are the same on client and server - uint32 t = 0; - uint32 seed = m_Seed; - - sha.UpdateData(account); - sha.UpdateData((uint8*)&t, 4); - sha.UpdateData((uint8*)&clientSeed, 4); - sha.UpdateData((uint8*)&seed, 4); - sha.UpdateBigNumbers(&k, NULL); - sha.Finalize(); - - if (memcmp(sha.GetDigest(), digest, 20)) - { - SendAuthResponseError(AUTH_FAILED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str()); - return -1; - } - - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", - account.c_str(), - address.c_str()); - - // Check if this user is by any chance a recruiter - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); - - stmt->setUInt32(0, id); - - result = LoginDatabase.Query(stmt); - - bool isRecruiter = false; - if (result) - isRecruiter = true; - - // Update the last_ip in the database as it was successful for login - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); - - stmt->setString(0, address); - stmt->setString(1, account); - - LoginDatabase.Execute(stmt); - - // NOTE ATM the socket is single-threaded, have this in mind ... - ACE_NEW_RETURN(m_Session, WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter), -1); - - m_Crypt.Init(&k); - - m_Session->LoadGlobalAccountData(); - m_Session->LoadTutorialsData(); - m_Session->ReadAddonsInfo(recvPacket); - m_Session->LoadPermissions(); - - // At this point, we can safely hook a successful login - sScriptMgr->OnAccountLogin(id); - - // Initialize Warden system only if it is enabled by config - if (wardenActive) - m_Session->InitWarden(&k, os); - - // Sleep this Network thread for - uint32 sleepTime = sWorld->getIntConfig(CONFIG_SESSION_ADD_DELAY); - ACE_OS::sleep(ACE_Time_Value(0, sleepTime)); - - sWorld->AddSession(m_Session); - return 0; -} - -int WorldSocket::HandlePing(WorldPacket& recvPacket) -{ - uint32 ping; - uint32 latency; - - // Get the ping packet content - recvPacket >> ping; - recvPacket >> latency; - - if (m_LastPingTime == ACE_Time_Value::zero) - m_LastPingTime = ACE_OS::gettimeofday(); // for 1st ping - else - { - ACE_Time_Value cur_time = ACE_OS::gettimeofday(); - ACE_Time_Value diff_time (cur_time); - diff_time -= m_LastPingTime; - m_LastPingTime = cur_time; - - if (diff_time < ACE_Time_Value (27)) - { - ++m_OverSpeedPings; - - uint32 max_count = sWorld->getIntConfig (CONFIG_MAX_OVERSPEED_PINGS); - - if (max_count && m_OverSpeedPings > max_count) - { - ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); - - if (m_Session && !m_Session->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING)) - { - TC_LOG_ERROR("network", "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)", - m_Session->GetPlayerInfo().c_str(), GetRemoteAddress().c_str()); - - return -1; - } - } - } - else - m_OverSpeedPings = 0; - } - - // critical section - { - ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1); - - if (m_Session) - { - m_Session->SetLatency (latency); - m_Session->ResetClientTimeDelay(); - } - else - { - TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, " - "but is not authenticated or got recently kicked, " - " address = %s", - GetRemoteAddress().c_str()); - return -1; - } - } - - WorldPacket packet(SMSG_PONG, 4); - packet << ping; - return SendPacket(packet); -} - -void WorldSocket::SendAuthResponseError(uint8 code) -{ - WorldPacket packet(SMSG_AUTH_RESPONSE, 1); - packet << uint8(code); - SendPacket(packet); -} diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h deleted file mode 100644 index 1f98be0152b..00000000000 --- a/src/server/game/Server/WorldSocket.h +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - * - * 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 . - */ - -/** \addtogroup u2w User to World Communication - * @{ - * \file WorldSocket.h - * \author Derex - */ - -#ifndef _WORLDSOCKET_H -#define _WORLDSOCKET_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined (ACE_LACKS_PRAGMA_ONCE) -#pragma once -#endif /* ACE_LACKS_PRAGMA_ONCE */ - -#include "Common.h" -#include "AuthCrypt.h" - -class ACE_Message_Block; -class WorldPacket; -class WorldSession; - -/// Handler that can communicate over stream sockets. -typedef ACE_Svc_Handler WorldHandler; - -/** - * WorldSocket. - * - * This class is responsible for the communication with - * remote clients. - * Most methods return -1 on failure. - * The class uses reference counting. - * - * For output the class uses one buffer (64K usually) and - * a queue where it stores packet if there is no place on - * the queue. The reason this is done, is because the server - * does really a lot of small-size writes to it, and it doesn't - * scale well to allocate memory for every. When something is - * written to the output buffer the socket is not immediately - * activated for output (again for the same reason), there - * is 10ms celling (thats why there is Update() method). - * This concept is similar to TCP_CORK, but TCP_CORK - * uses 200ms celling. As result overhead generated by - * sending packets from "producer" threads is minimal, - * and doing a lot of writes with small size is tolerated. - * - * The calls to Update() method are managed by WorldSocketMgr - * and ReactorRunnable. - * - * For input, the class uses one 4096 bytes buffer on stack - * to which it does recv() calls. And then received data is - * distributed where its needed. 4096 matches pretty well the - * traffic generated by client for now. - * - * The input/output do speculative reads/writes (AKA it tryes - * to read all data available in the kernel buffer or tryes to - * write everything available in userspace buffer), - * which is ok for using with Level and Edge Triggered IO - * notification. - * - */ -class WorldSocket : public WorldHandler -{ - public: - WorldSocket (void); - virtual ~WorldSocket (void); - - friend class WorldSocketMgr; - - /// Mutex type used for various synchronizations. - typedef ACE_Thread_Mutex LockType; - typedef ACE_Guard GuardType; - - /// Check if socket is closed. - bool IsClosed(void) const; - - /// Close the socket. - void CloseSocket(void); - - /// Get address of connected peer. - const std::string& GetRemoteAddress(void) const; - - /// Send A packet on the socket, this function is reentrant. - /// @param pct packet to send - /// @return -1 of failure - int SendPacket(const WorldPacket& pct); - - /// Add reference to this object. - long AddReference(void); - - /// Remove reference to this object. - long RemoveReference(void); - - /// things called by ACE framework. - - /// Called on open, the void* is the acceptor. - virtual int open(void *); - - /// Called on failures inside of the acceptor, don't call from your code. - virtual int close(u_long); - - /// Called when we can read from the socket. - virtual int handle_input(ACE_HANDLE = ACE_INVALID_HANDLE); - - /// Called when the socket can write. - virtual int handle_output(ACE_HANDLE = ACE_INVALID_HANDLE); - - /// Called when connection is closed or error happens. - virtual int handle_close(ACE_HANDLE = ACE_INVALID_HANDLE, - ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK); - - /// Called by WorldSocketMgr/ReactorRunnable. - int Update(void); - - private: - /// Helper functions for processing incoming data. - int handle_input_header(void); - int handle_input_payload(void); - int handle_input_missing_data(void); - - /// Help functions to mark/unmark the socket for output. - /// @param g the guard is for m_OutBufferLock, the function will release it - int cancel_wakeup_output(GuardType& g); - int schedule_wakeup_output(GuardType& g); - - /// Drain the queue if its not empty. - int handle_output_queue(GuardType& g); - - /// process one incoming packet. - /// @param new_pct received packet, note that you need to delete it. - int ProcessIncoming(WorldPacket* new_pct); - - /// Called by ProcessIncoming() on CMSG_AUTH_SESSION. - int HandleAuthSession(WorldPacket& recvPacket); - - /// Called by ProcessIncoming() on CMSG_PING. - int HandlePing(WorldPacket& recvPacket); - - int HandleSendAuthSession(); - - private: - void SendAuthResponseError(uint8); - /// Time in which the last ping was received - ACE_Time_Value m_LastPingTime; - - /// Keep track of over-speed pings, to prevent ping flood. - uint32 m_OverSpeedPings; - - /// Address of the remote peer - std::string m_Address; - - /// Class used for managing encryption of the headers - AuthCrypt m_Crypt; - - /// Mutex lock to protect m_Session - LockType m_SessionLock; - - /// Session to which received packets are routed - WorldSession* m_Session; - - /// here are stored the fragments of the received data - WorldPacket* m_RecvWPct; - - /// This block actually refers to m_RecvWPct contents, - /// which allows easy and safe writing to it. - /// It wont free memory when its deleted. m_RecvWPct takes care of freeing. - ACE_Message_Block m_RecvPct; - - /// Fragment of the received header. - ACE_Message_Block m_Header; - - /// Mutex for protecting output related data. - LockType m_OutBufferLock; - - /// Buffer used for writing output. - ACE_Message_Block* m_OutBuffer; - - /// Size of the m_OutBuffer. - size_t m_OutBufferSize; - - /// True if the socket is registered with the reactor for output - bool m_OutActive; - - uint32 m_Seed; - - WorldSocket(WorldSocket const& right) = delete; - WorldSocket& operator=(WorldSocket const& right) = delete; -}; - -#endif /* _WORLDSOCKET_H */ - -/// @} - diff --git a/src/server/game/Server/WorldSocketAcceptor.h b/src/server/game/Server/WorldSocketAcceptor.h deleted file mode 100644 index 0b07196a0cd..00000000000 --- a/src/server/game/Server/WorldSocketAcceptor.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore - * - * 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 . - */ - -/** \addtogroup u2w User to World Communication - * @{ - * \file WorldSocketMgr.h - */ - -#ifndef __WORLDSOCKETACCEPTOR_H_ -#define __WORLDSOCKETACCEPTOR_H_ - -#include "Common.h" - -#include -#include - -#include "WorldSocket.h" - -class WorldSocketAcceptor : public ACE_Acceptor -{ -public: - WorldSocketAcceptor(void) { } - virtual ~WorldSocketAcceptor(void) - { - if (reactor()) - reactor()->cancel_timer(this, 1); - } - -protected: - - virtual int handle_timeout(const ACE_Time_Value& /*current_time*/, const void* /*act = 0*/) - { - TC_LOG_DEBUG("misc", "Resuming acceptor"); - reactor()->cancel_timer(this, 1); - return reactor()->register_handler(this, ACE_Event_Handler::ACCEPT_MASK); - } - - virtual int handle_accept_error(void) - { -#if defined(ENFILE) && defined(EMFILE) - if (errno == ENFILE || errno == EMFILE) - { - TC_LOG_ERROR("misc", "Out of file descriptors, suspending incoming connections for 10 seconds"); - reactor()->remove_handler(this, ACE_Event_Handler::ACCEPT_MASK | ACE_Event_Handler::DONT_CALL); - reactor()->schedule_timer(this, NULL, ACE_Time_Value(10)); - } -#endif - return 0; - } -}; - -#endif /* __WORLDSOCKETACCEPTOR_H_ */ -/// @} diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp deleted file mode 100644 index 3c1db3551d0..00000000000 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore - * Copyright (C) 2005-2008 MaNGOS - * - * 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 . - */ - -/** \file WorldSocketMgr.cpp -* \ingroup u2w -* \author Derex -*/ - -#include "WorldSocketMgr.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "Log.h" -#include "Common.h" -#include "Config.h" -#include "DatabaseEnv.h" -#include "WorldSocket.h" -#include "WorldSocketAcceptor.h" -#include "ScriptMgr.h" - -/** -* This is a helper class to WorldSocketMgr, that manages -* network threads, and assigning connections from acceptor thread -* to other network threads -*/ -class ReactorRunnable : protected ACE_Task_Base -{ - public: - - ReactorRunnable() : - m_Reactor(0), - m_Connections(0), - m_ThreadId(-1) - { - ACE_Reactor_Impl* imp; - - #if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) - - imp = new ACE_Dev_Poll_Reactor(); - - imp->max_notify_iterations (128); - imp->restart (1); - - #else - - imp = new ACE_TP_Reactor(); - imp->max_notify_iterations (128); - - #endif - - m_Reactor = new ACE_Reactor (imp, 1); - } - - virtual ~ReactorRunnable() - { - Stop(); - Wait(); - - delete m_Reactor; - } - - void Stop() - { - m_Reactor->end_reactor_event_loop(); - } - - int Start() - { - if (m_ThreadId != -1) - return -1; - - return (m_ThreadId = activate()); - } - - void Wait() { ACE_Task_Base::wait(); } - - long Connections() - { - return static_cast (m_Connections.value()); - } - - int AddSocket (WorldSocket* sock) - { - std::lock_guard lock(newSocketsLock); - - ++m_Connections; - sock->AddReference(); - sock->reactor (m_Reactor); - m_NewSockets.insert (sock); - - sScriptMgr->OnSocketOpen(sock); - - return 0; - } - - ACE_Reactor* GetReactor() - { - return m_Reactor; - } - - protected: - - void AddNewSockets() - { - std::lock_guard lock(newSocketsLock); - - if (m_NewSockets.empty()) - return; - - for (SocketSet::const_iterator i = m_NewSockets.begin(); i != m_NewSockets.end(); ++i) - { - WorldSocket* sock = (*i); - - if (sock->IsClosed()) - { - sScriptMgr->OnSocketClose(sock, true); - - sock->RemoveReference(); - --m_Connections; - } - else - m_Sockets.insert (sock); - } - - m_NewSockets.clear(); - } - - virtual int svc() - { - TC_LOG_DEBUG("misc", "Network Thread Starting"); - - ACE_ASSERT (m_Reactor); - - SocketSet::iterator i, t; - - while (!m_Reactor->reactor_event_loop_done()) - { - // dont be too smart to move this outside the loop - // the run_reactor_event_loop will modify interval - ACE_Time_Value interval (0, 10000); - - if (m_Reactor->run_reactor_event_loop (interval) == -1) - break; - - AddNewSockets(); - - for (i = m_Sockets.begin(); i != m_Sockets.end();) - { - if ((*i)->Update() == -1) - { - t = i; - ++i; - - (*t)->CloseSocket(); - - sScriptMgr->OnSocketClose((*t), false); - - (*t)->RemoveReference(); - --m_Connections; - m_Sockets.erase (t); - } - else - ++i; - } - } - - TC_LOG_DEBUG("misc", "Network Thread exits"); - - return 0; - } - - private: - typedef ACE_Atomic_Op AtomicInt; - typedef std::set SocketSet; - - ACE_Reactor* m_Reactor; - AtomicInt m_Connections; - int m_ThreadId; - - SocketSet m_Sockets; - - SocketSet m_NewSockets; - std::mutex newSocketsLock; -}; - -WorldSocketMgr::WorldSocketMgr() : - m_NetThreads(0), - m_NetThreadsCount(0), - m_SockOutKBuff(-1), - m_SockOutUBuff(65536), - m_UseNoDelay(true), - m_Acceptor (0) { } - -WorldSocketMgr::~WorldSocketMgr() -{ - delete [] m_NetThreads; - delete m_Acceptor; -} - -int -WorldSocketMgr::StartReactiveIO (ACE_UINT16 port, const char* address) -{ - m_UseNoDelay = sConfigMgr->GetBoolDefault ("Network.TcpNodelay", true); - - int num_threads = sConfigMgr->GetIntDefault ("Network.Threads", 1); - - if (num_threads <= 0) - { - TC_LOG_ERROR("misc", "Network.Threads is wrong in your config file"); - return -1; - } - - m_NetThreadsCount = static_cast (num_threads + 1); - - m_NetThreads = new ReactorRunnable[m_NetThreadsCount]; - - TC_LOG_DEBUG("misc", "Max allowed socket connections %d", ACE::max_handles()); - - // -1 means use default - m_SockOutKBuff = sConfigMgr->GetIntDefault ("Network.OutKBuff", -1); - - m_SockOutUBuff = sConfigMgr->GetIntDefault ("Network.OutUBuff", 65536); - - if (m_SockOutUBuff <= 0) - { - TC_LOG_ERROR("misc", "Network.OutUBuff is wrong in your config file"); - return -1; - } - - m_Acceptor = new WorldSocketAcceptor; - - ACE_INET_Addr listen_addr (port, address); - - if (m_Acceptor->open(listen_addr, m_NetThreads[0].GetReactor(), ACE_NONBLOCK) == -1) - { - TC_LOG_ERROR("misc", "Failed to open acceptor, check if the port is free"); - return -1; - } - - for (size_t i = 0; i < m_NetThreadsCount; ++i) - m_NetThreads[i].Start(); - - return 0; -} - -int -WorldSocketMgr::StartNetwork (ACE_UINT16 port, const char* address) -{ - if (!sLog->ShouldLog("misc", LOG_LEVEL_DEBUG)) - ACE_Log_Msg::instance()->priority_mask (LM_ERROR, ACE_Log_Msg::PROCESS); - - if (StartReactiveIO(port, address) == -1) - return -1; - - sScriptMgr->OnNetworkStart(); - - return 0; -} - -void -WorldSocketMgr::StopNetwork() -{ - if (m_Acceptor) - { - m_Acceptor->close(); - } - - if (m_NetThreadsCount != 0) - { - for (size_t i = 0; i < m_NetThreadsCount; ++i) - m_NetThreads[i].Stop(); - } - - Wait(); - - sScriptMgr->OnNetworkStop(); -} - -void -WorldSocketMgr::Wait() -{ - if (m_NetThreadsCount != 0) - { - for (size_t i = 0; i < m_NetThreadsCount; ++i) - m_NetThreads[i].Wait(); - } -} - -int -WorldSocketMgr::OnSocketOpen (WorldSocket* sock) -{ - // set some options here - if (m_SockOutKBuff >= 0) - { - if (sock->peer().set_option (SOL_SOCKET, - SO_SNDBUF, - (void*) & m_SockOutKBuff, - sizeof (int)) == -1 && errno != ENOTSUP) - { - TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen set_option SO_SNDBUF"); - return -1; - } - } - - static const int ndoption = 1; - - // Set TCP_NODELAY. - if (m_UseNoDelay) - { - if (sock->peer().set_option (ACE_IPPROTO_TCP, - TCP_NODELAY, - (void*)&ndoption, - sizeof (int)) == -1) - { - TC_LOG_ERROR("misc", "WorldSocketMgr::OnSocketOpen: peer().set_option TCP_NODELAY errno = %s", ACE_OS::strerror (errno)); - return -1; - } - } - - sock->m_OutBufferSize = static_cast (m_SockOutUBuff); - - // we skip the Acceptor Thread - size_t min = 1; - - ACE_ASSERT (m_NetThreadsCount >= 1); - - for (size_t i = 1; i < m_NetThreadsCount; ++i) - if (m_NetThreads[i].Connections() < m_NetThreads[min].Connections()) - min = i; - - return m_NetThreads[min].AddSocket (sock); -} diff --git a/src/server/game/Server/WorldSocketMgr.h b/src/server/game/Server/WorldSocketMgr.h deleted file mode 100644 index 2c91a164ff9..00000000000 --- a/src/server/game/Server/WorldSocketMgr.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore - * Copyright (C) 2005-2009 MaNGOS - * - * 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 . - */ - -/** \addtogroup u2w User to World Communication - * @{ - * \file WorldSocketMgr.h - * \author Derex - */ - -#ifndef __WORLDSOCKETMGR_H -#define __WORLDSOCKETMGR_H - -#include -#include - -class WorldSocket; -class ReactorRunnable; -class ACE_Event_Handler; - -/// Manages all sockets connected to peers and network threads -class WorldSocketMgr -{ - friend class WorldSocket; - -public: - static WorldSocketMgr* instance() - { - static WorldSocketMgr* instance = new WorldSocketMgr(); - return instance; - } - - /// Start network, listen at address:port . - int StartNetwork(ACE_UINT16 port, const char* address); - - /// Stops all network threads, It will wait for all running threads . - void StopNetwork(); - - /// Wait untill all network threads have "joined" . - void Wait(); - -private: - int OnSocketOpen(WorldSocket* sock); - - int StartReactiveIO(ACE_UINT16 port, const char* address); - -private: - WorldSocketMgr(); - virtual ~WorldSocketMgr(); - - ReactorRunnable* m_NetThreads; - size_t m_NetThreadsCount; - - int m_SockOutKBuff; - int m_SockOutUBuff; - bool m_UseNoDelay; - - class WorldSocketAcceptor* m_Acceptor; -}; - -#define sWorldSocketMgr WorldSocketMgr::instance() - -#endif -/// @} diff --git a/src/server/game/Server/WorldTcpSession.cpp b/src/server/game/Server/WorldTcpSession.cpp new file mode 100644 index 00000000000..0efd7ab8042 --- /dev/null +++ b/src/server/game/Server/WorldTcpSession.cpp @@ -0,0 +1,407 @@ +/* +* Copyright (C) 2008-2014 TrinityCore +* Copyright (C) 2005-2009 MaNGOS +* +* 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 . +*/ + +#include +#include +#include "WorldTcpSession.h" +#include "ServerPktHeader.h" +#include "BigNumber.h" +#include "Opcodes.h" +#include "ScriptMgr.h" +#include "SHA1.h" + +using boost::asio::ip::tcp; +using boost::asio::streambuf; + +void WorldTcpSession::Start() +{ + AsyncReadHeader(); + HandleSendAuthSession(); +} + +void WorldTcpSession::HandleSendAuthSession() +{ + WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); + packet << uint32(1); // 1...31 + packet << uint32(_authSeed); + + BigNumber seed1; + seed1.SetRand(16 * 8); + packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds + + BigNumber seed2; + seed2.SetRand(16 * 8); + packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds + + AsyncWrite(packet); +} + + +void WorldTcpSession::AsyncReadHeader() +{ + auto self(shared_from_this()); + + _socket.async_read_some(boost::asio::buffer(_readBuffer, sizeof(ClientPktHeader)), [this, self](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes == sizeof(ClientPktHeader)) + { + ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; + + EndianConvertReverse(header->size); + EndianConvert(header->cmd); + + AsyncReadData(header->size - sizeof(ClientPktHeader)); + } + else + { + _socket.close(); + } + }); +} + +void WorldTcpSession::AsyncReadData(size_t dataSize) +{ + auto self(shared_from_this()); + + _socket.async_read_some(boost::asio::buffer(&_readBuffer[sizeof(ClientPktHeader)], dataSize), [this, self, dataSize](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes == dataSize) + { + ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; + + header->size -= sizeof(ClientPktHeader); + + uint16 opcode = (uint16)header->cmd; + + std::string opcodeName = GetOpcodeNameForLogging(opcode); + + WorldPacket* packet = new WorldPacket(opcode, header->size); + + packet->resize(header->size); + + std::memcpy(packet->contents(), &_readBuffer[sizeof(ClientPktHeader)], header->size); + + switch (opcode) + { + case CMSG_PING: + //return HandlePing(*new_pct); + break; + case CMSG_AUTH_SESSION: + if (_worldSession) + { + TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); + break; + } + + // sScriptMgr->OnPacketReceive(this, packet); + HandleAuthSession(*packet); + break; + case CMSG_KEEP_ALIVE: + TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); + //sScriptMgr->OnPacketReceive(this, packet); + break; + default: + { + //ACE_GUARD_RETURN(LockType, Guard, m_SessionLock, -1); + + if (!_worldSession) + { + TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + break; + } + + // Our Idle timer will reset on any non PING opcodes. + // Catches people idling on the login screen and any lingering ingame connections. + _worldSession->ResetTimeOutTime(); + + // WARNING here we call it with locks held. + // Its possible to cause deadlock if QueuePacket calls back + _worldSession->QueuePacket(packet); + + break; + } + } + + + AsyncReadHeader(); + } + else + { + _socket.close(); + } + }); +} + +void WorldTcpSession::AsyncWrite(WorldPacket const& packet) +{ + ServerPktHeader header(packet.size() + 2, packet.GetOpcode()); + _authCrypt.EncryptSend((uint8*)header.header, header.getHeaderLength()); + + std::memcpy(_writeBuffer, (char*)header.header, header.getHeaderLength()); + + std::memcpy(_writeBuffer + header.getHeaderLength(), (char const*)packet.contents(), packet.size()); + + boost::asio::async_write(_socket, boost::asio::buffer(_writeBuffer, header.getHeaderLength() + packet.size()), [this](boost::system::error_code error, std::size_t /*length*/) + { + if (error) + { + _socket.close(); + } + }); +} + +int WorldTcpSession::HandleAuthSession(WorldPacket& recvPacket) +{ + uint8 digest[20]; + uint32 clientSeed; + uint8 security; + uint32 id; + LocaleConstant locale; + std::string account; + SHA1Hash sha; + uint32 clientBuild; + uint32 unk2, unk3, unk5, unk6, unk7; + uint64 unk4; + WorldPacket packet, SendAddonPacked; + BigNumber k; + bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); + + if (sWorld->IsClosed()) + { + SendAuthResponseError(AUTH_REJECT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteIpAddress().c_str()); + return -1; + } + + // Read the content of the packet + recvPacket >> clientBuild; + recvPacket >> unk2; + recvPacket >> account; + recvPacket >> unk3; + recvPacket >> clientSeed; + recvPacket >> unk5 >> unk6 >> unk7; + recvPacket >> unk4; + recvPacket.read(digest, 20); + + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", + clientBuild, + unk2, + account.c_str(), + unk3, + clientSeed); + + // Get the account information from the realmd database + // 0 1 2 3 4 5 6 7 8 + // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ? + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); + + stmt->setString(0, account); + + PreparedQueryResult result = LoginDatabase.Query(stmt); + + // Stop if the account is not found + if (!result) + { + // We can not log here, as we do not know the account. Thus, no accountId. + SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); + return -1; + } + + Field* fields = result->Fetch(); + + uint8 expansion = fields[4].GetUInt8(); + uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); + if (expansion > world_expansion) + expansion = world_expansion; + + // For hook purposes, we get Remoteaddress at this point. + std::string address = GetRemoteIpAddress(); + + // As we don't know if attempted login process by ip works, we update last_attempt_ip right away + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); + + stmt->setString(0, address); + stmt->setString(1, account); + + LoginDatabase.Execute(stmt); + // This also allows to check for possible "hack" attempts on account + + // id has to be fetched at this point, so that first actual account response that fails can be logged + id = fields[0].GetUInt32(); + + ///- Re-check ip locking (same check as in realmd). + if (fields[3].GetUInt8() == 1) // if ip is locked + { + if (strcmp(fields[2].GetCString(), address.c_str())) + { + SendAuthResponseError(AUTH_FAILED); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); + // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well + sScriptMgr->OnFailedAccountLogin(id); + return -1; + } + } + + k.SetHexStr(fields[1].GetCString()); + + int64 mutetime = fields[5].GetInt64(); + //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. + if (mutetime < 0) + { + mutetime = time(NULL) + llabs(mutetime); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN); + + stmt->setInt64(0, mutetime); + stmt->setUInt32(1, id); + + LoginDatabase.Execute(stmt); + } + + locale = LocaleConstant(fields[6].GetUInt8()); + if (locale >= TOTAL_LOCALES) + locale = LOCALE_enUS; + + uint32 recruiter = fields[7].GetUInt32(); + std::string os = fields[8].GetString(); + + // Must be done before WorldSession is created + if (wardenActive && os != "Win" && os != "OSX") + { + SendAuthResponseError(AUTH_REJECT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str()); + return -1; + } + + // Checks gmlevel per Realm + stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); + + stmt->setUInt32(0, id); + stmt->setInt32(1, int32(realmID)); + + result = LoginDatabase.Query(stmt); + + if (!result) + security = 0; + else + { + fields = result->Fetch(); + security = fields[0].GetUInt8(); + } + + // Re-check account ban (same check as in realmd) + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); + + stmt->setUInt32(0, id); + stmt->setString(1, address); + + PreparedQueryResult banresult = LoginDatabase.Query(stmt); + + if (banresult) // if account banned + { + SendAuthResponseError(AUTH_BANNED); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); + sScriptMgr->OnFailedAccountLogin(id); + return -1; + } + + // Check locked state for server + AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); + TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); + if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) + { + SendAuthResponseError(AUTH_UNAVAILABLE); + TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); + sScriptMgr->OnFailedAccountLogin(id); + return -1; + } + + // Check that Key and account name are the same on client and server + uint32 t = 0; + + sha.UpdateData(account); + sha.UpdateData((uint8*)&t, 4); + sha.UpdateData((uint8*)&clientSeed, 4); + sha.UpdateData((uint8*)&_authSeed, 4); + sha.UpdateBigNumbers(&k, NULL); + sha.Finalize(); + + if (memcmp(sha.GetDigest(), digest, 20)) + { + SendAuthResponseError(AUTH_FAILED); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str()); + return -1; + } + + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", + account.c_str(), + address.c_str()); + + // Check if this user is by any chance a recruiter + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); + + stmt->setUInt32(0, id); + + result = LoginDatabase.Query(stmt); + + bool isRecruiter = false; + if (result) + isRecruiter = true; + + // Update the last_ip in the database as it was successful for login + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); + + stmt->setString(0, address); + stmt->setString(1, account); + + LoginDatabase.Execute(stmt); + + // NOTE ATM the socket is single-threaded, have this in mind ... + _worldSession = new WorldSession(id, this, AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); + + _authCrypt.Init(&k); + + _worldSession->LoadGlobalAccountData(); + _worldSession->LoadTutorialsData(); + _worldSession->ReadAddonsInfo(recvPacket); + _worldSession->LoadPermissions(); + + // At this point, we can safely hook a successful login + sScriptMgr->OnAccountLogin(id); + + // Initialize Warden system only if it is enabled by config + if (wardenActive) + _worldSession->InitWarden(&k, os); + + // Sleep this Network thread for + // uint32 sleepTime = sWorld->getIntConfig(CONFIG_SESSION_ADD_DELAY); + // ACE_OS::sleep(ACE_Time_Value(0, sleepTime)); + + sWorld->AddSession(_worldSession); + + return 0; +} + +void WorldTcpSession::SendAuthResponseError(uint8 code) +{ + WorldPacket packet(SMSG_AUTH_RESPONSE, 1); + packet << uint8(code); + + AsyncWrite(packet); +} diff --git a/src/server/game/Server/WorldTcpSession.h b/src/server/game/Server/WorldTcpSession.h new file mode 100644 index 00000000000..fde37a642e5 --- /dev/null +++ b/src/server/game/Server/WorldTcpSession.h @@ -0,0 +1,81 @@ +/* +* Copyright (C) 2008-2014 TrinityCore +* Copyright (C) 2005-2009 MaNGOS +* +* 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 . +*/ + +#ifndef __WORLDTCPSESSION_H__ +#define __WORLDTCPSESSION_H__ + +#include +#include +#include +#include "Common.h" +#include "AuthCrypt.h" +#include "Util.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +using boost::asio::ip::tcp; + +#pragma pack(push, 1) + +struct ClientPktHeader +{ + uint16 size; + uint32 cmd; +}; + +#pragma pack(pop) + +class WorldTcpSession : public std::enable_shared_from_this +{ +public: + WorldTcpSession(tcp::socket socket) : _socket(std::move(socket)), _authSeed(static_cast (rand32())) + { + + } + + void Start(); + + const std::string GetRemoteIpAddress() const { return _socket.remote_endpoint().address().to_string(); }; + unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); } + + void CloseSocket() { _socket.close(); }; + bool IsOpen() { return _socket.is_open(); }; + + void WorldTcpSession::AsyncWrite(WorldPacket const& packet); + +private: + + void WorldTcpSession::HandleSendAuthSession(); + int WorldTcpSession::HandleAuthSession(WorldPacket& recvPacket); + void WorldTcpSession::SendAuthResponseError(uint8 code); + + void WorldTcpSession::AsyncReadHeader(); + void WorldTcpSession::AsyncReadData(size_t dataSize); + + tcp::socket _socket; + + char _readBuffer[4096]; + char _writeBuffer[4096]; + + uint32 _authSeed; + AuthCrypt _authCrypt; + + WorldSession* _worldSession; +}; + +#endif diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 0fc2c1a7221..7ac60c631b3 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -37,12 +37,12 @@ #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 "WorldTcpSession.h" #define TRINITY_CORE_CONFIG "worldserver.conf" #define WORLD_SLEEP_CONST 50 @@ -155,8 +155,6 @@ extern int main(int argc, char** argv) 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", ""); @@ -228,21 +226,17 @@ extern int main(int argc, char** argv) // Launch the worldserver listener socket uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD)); - std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); + std::string worldListener = 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; - } + AsyncAcceptor worldAcceptor(_ioService, worldListener, worldPort); + + sScriptMgr->OnStartup(); // 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 @@ -262,8 +256,6 @@ extern int main(int argc, char** argv) // 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(); -- cgit v1.2.3 From 59c8ffe4b3fec5979926d991c17cae84b41e46e2 Mon Sep 17 00:00:00 2001 From: leak Date: Sun, 6 Jul 2014 23:04:38 +0200 Subject: Change the freeze detector thread to be a periodic callback running on the thread pool --- src/server/authserver/Main.cpp | 3 ++- src/server/worldserver/Main.cpp | 48 ++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 23 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index c7f71edbd0c..39ad4b60dfe 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -137,7 +137,7 @@ int main(int argc, char** argv) // 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.expires_from_now(boost::posix_time::minutes(_dbPingInterval)); _dbPingTimer.async_wait(KeepDatabaseAliveHandler); // Start the io service worker loop @@ -218,6 +218,7 @@ void KeepDatabaseAliveHandler(const boost::system::error_code& error) LoginDatabase.KeepAlive(); _dbPingTimer.expires_from_now(boost::posix_time::minutes(_dbPingInterval)); + _dbPingTimer.async_wait(KeepDatabaseAliveHandler); } } diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 7ac60c631b3..db49d2492c0 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "Common.h" #include "DatabaseEnv.h" @@ -62,6 +63,11 @@ int m_ServiceStatus = -1; #endif boost::asio::io_service _ioService; +boost::asio::deadline_timer _freezeCheckTimer(_ioService); +uint32 _worldLoopCounter(0); +uint32 _lastChangeMsTime(0); +uint32 _maxCoreStuckTime(0); + WorldDatabaseWorkerPool WorldDatabase; ///< Accessor to the world database CharacterDatabaseWorkerPool CharacterDatabase; ///< Accessor to the character database LoginDatabaseWorkerPool LoginDatabase; ///< Accessor to the realm/login database @@ -69,7 +75,7 @@ uint32 realmID; ///< Id of the realm void usage(const char* prog); void SignalHandler(const boost::system::error_code& error, int signalNumber); -void FreezeDetectorThread(uint32 delayTime); +void FreezeDetectorHandler(const boost::system::error_code& error); AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService); bool StartDB(); void StopDB(); @@ -219,11 +225,6 @@ extern int main(int argc, char** argv) 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 worldListener = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); @@ -235,6 +236,15 @@ extern int main(int argc, char** argv) // Set server online (allow connecting now) LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); + // Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec) + std::thread* freezeDetectorThread = nullptr; + if (_maxCoreStuckTime = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0)) + { + _freezeCheckTimer.expires_from_now(boost::posix_time::seconds(5)); + _freezeCheckTimer.async_wait(FreezeDetectorHandler); + TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", _maxCoreStuckTime); + } + TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", _FULLVERSION); WorldUpdateLoop(); @@ -408,34 +418,28 @@ void SignalHandler(const boost::system::error_code& error, int signalNumber) } } -void FreezeDetectorThread(uint32 delayTime) +void FreezeDetectorHandler(const boost::system::error_code& error) { - 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()) + if (!error) { - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); uint32 curtime = getMSTime(); - // normal work + uint32 worldLoopCounter = World::m_worldLoopCounter; - if (loops != worldLoopCounter) + if (_worldLoopCounter != worldLoopCounter) { - lastChange = curtime; - loops = worldLoopCounter; + _lastChangeMsTime = curtime; + _worldLoopCounter = worldLoopCounter; } // possible freeze - else if (getMSTimeDiff(lastChange, curtime) > delayTime) + else if (getMSTimeDiff(_lastChangeMsTime, curtime) > _maxCoreStuckTime) { TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!"); ASSERT(false); } + + _freezeCheckTimer.expires_from_now(boost::posix_time::seconds(1)); + _freezeCheckTimer.async_wait(FreezeDetectorHandler); } - TC_LOG_INFO("server.worldserver", "Anti-freeze thread exiting without problems."); } AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService) -- cgit v1.2.3 From eeb272040101f246d191e81c9eab9ee0c70462da Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 6 Jul 2014 19:05:13 -0500 Subject: Removed some dead code --- src/server/worldserver/Main.cpp | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index db49d2492c0..ff53c91e7b6 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -237,7 +237,6 @@ extern int main(int argc, char** argv) LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); // Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec) - std::thread* freezeDetectorThread = nullptr; if (_maxCoreStuckTime = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0)) { _freezeCheckTimer.expires_from_now(boost::posix_time::seconds(5)); @@ -337,8 +336,6 @@ extern int main(int argc, char** argv) delete cliThread; } - delete freezeDetectorThread; - OpenSSLCrypto::threadsCleanup(); // 0 - normal shutdown -- cgit v1.2.3 From c7d5660e992bdec5cdac6a9fab76ecb6447dcb49 Mon Sep 17 00:00:00 2001 From: leak Date: Mon, 7 Jul 2014 21:37:20 +0200 Subject: Readd support for TCP_NODELAY socket option --- src/server/shared/Networking/AsyncAcceptor.h | 6 ++++++ src/server/worldserver/Main.cpp | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h index c54471fd01a..fbb0313381e 100644 --- a/src/server/shared/Networking/AsyncAcceptor.h +++ b/src/server/shared/Networking/AsyncAcceptor.h @@ -33,6 +33,12 @@ public: AsyncAccept(); }; + AsyncAcceptor(boost::asio::io_service& ioService, std::string bindIp, int port, bool tcpNoDelay) : + AsyncAcceptor(ioService, bindIp, port) + { + _socket.set_option(boost::asio::ip::tcp::no_delay(tcpNoDelay)); + }; + private: void AsyncAcceptor::AsyncAccept() { diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index ff53c91e7b6..dae947b7324 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -228,8 +228,9 @@ extern int main(int argc, char** argv) // Launch the worldserver listener socket uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD)); std::string worldListener = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); + bool tcpNoDelay = sConfigMgr->GetBoolDefault("Network.TcpNodelay", true); - AsyncAcceptor worldAcceptor(_ioService, worldListener, worldPort); + AsyncAcceptor worldAcceptor(_ioService, worldListener, worldPort, tcpNoDelay); sScriptMgr->OnStartup(); -- cgit v1.2.3 From a738cd96dcf506530e79ecb1f53e09fd59511a70 Mon Sep 17 00:00:00 2001 From: leak Date: Mon, 7 Jul 2014 22:03:41 +0200 Subject: Renamed WorldTcpSession back to WorldSocket --- src/server/game/Scripting/ScriptMgr.cpp | 10 +- src/server/game/Scripting/ScriptMgr.h | 22 +- src/server/game/Server/WorldSession.cpp | 4 +- src/server/game/Server/WorldSession.h | 6 +- src/server/game/Server/WorldSocket.cpp | 475 +++++++++++++++++++++++++++++ src/server/game/Server/WorldSocket.h | 85 ++++++ src/server/game/Server/WorldTcpSession.cpp | 475 ----------------------------- src/server/game/Server/WorldTcpSession.h | 85 ------ src/server/worldserver/Main.cpp | 4 +- 9 files changed, 583 insertions(+), 583 deletions(-) create mode 100644 src/server/game/Server/WorldSocket.cpp create mode 100644 src/server/game/Server/WorldSocket.h delete mode 100644 src/server/game/Server/WorldTcpSession.cpp delete mode 100644 src/server/game/Server/WorldTcpSession.h (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index b45ed6b640c..1b91ed1690c 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -400,35 +400,35 @@ void ScriptMgr::OnNetworkStop() FOREACH_SCRIPT(ServerScript)->OnNetworkStop(); } -void ScriptMgr::OnSocketOpen(std::shared_ptr socket) +void ScriptMgr::OnSocketOpen(std::shared_ptr socket) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnSocketOpen(socket); } -void ScriptMgr::OnSocketClose(std::shared_ptr socket, bool wasNew) +void ScriptMgr::OnSocketClose(std::shared_ptr socket, bool wasNew) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnSocketClose(socket, wasNew); } -void ScriptMgr::OnPacketReceive(std::shared_ptr socket, WorldPacket packet) +void ScriptMgr::OnPacketReceive(std::shared_ptr socket, WorldPacket packet) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnPacketReceive(socket, packet); } -void ScriptMgr::OnPacketSend(std::shared_ptr socket, WorldPacket packet) +void ScriptMgr::OnPacketSend(std::shared_ptr socket, WorldPacket packet) { ASSERT(socket); FOREACH_SCRIPT(ServerScript)->OnPacketSend(socket, packet); } -void ScriptMgr::OnUnknownPacketReceive(std::shared_ptr socket, WorldPacket packet) +void ScriptMgr::OnUnknownPacketReceive(std::shared_ptr socket, WorldPacket packet) { ASSERT(socket); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 81c764cb1ca..260c43f9b3b 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -57,7 +57,7 @@ class Transport; class Unit; class Vehicle; class WorldPacket; -class WorldTcpSession; +class WorldSocket; class WorldObject; struct AchievementCriteriaData; @@ -221,23 +221,23 @@ class ServerScript : public ScriptObject virtual void OnNetworkStop() { } // Called when a remote socket establishes a connection to the server. Do not store the socket object. - virtual void OnSocketOpen(std::shared_ptr /*socket*/) { } + virtual void OnSocketOpen(std::shared_ptr /*socket*/) { } // Called when a socket is closed. Do not store the socket object, and do not rely on the connection // being open; it is not. - virtual void OnSocketClose(std::shared_ptr /*socket*/, bool /*wasNew*/) { } + virtual void OnSocketClose(std::shared_ptr /*socket*/, bool /*wasNew*/) { } // Called when a packet is sent to a client. The packet object is a copy of the original packet, so reading // and modifying it is safe. - virtual void OnPacketSend(std::shared_ptr /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnPacketSend(std::shared_ptr /*socket*/, WorldPacket& /*packet*/) { } // Called when a (valid) packet is received by a client. The packet object is a copy of the original packet, so // reading and modifying it is safe. - virtual void OnPacketReceive(std::shared_ptr /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnPacketReceive(std::shared_ptr /*socket*/, WorldPacket& /*packet*/) { } // Called when an invalid (unknown opcode) packet is received by a client. The packet is a reference to the orignal // packet; not a copy. This allows you to actually handle unknown packets (for whatever purpose). - virtual void OnUnknownPacketReceive(std::shared_ptr /*socket*/, WorldPacket& /*packet*/) { } + virtual void OnUnknownPacketReceive(std::shared_ptr /*socket*/, WorldPacket& /*packet*/) { } }; class WorldScript : public ScriptObject @@ -908,11 +908,11 @@ class ScriptMgr void OnNetworkStart(); void OnNetworkStop(); - void OnSocketOpen(std::shared_ptr socket); - void OnSocketClose(std::shared_ptr socket, bool wasNew); - void OnPacketReceive(std::shared_ptr socket, WorldPacket packet); - void OnPacketSend(std::shared_ptr socket, WorldPacket packet); - void OnUnknownPacketReceive(std::shared_ptr socket, WorldPacket packet); + void OnSocketOpen(std::shared_ptr socket); + void OnSocketClose(std::shared_ptr socket, bool wasNew); + void OnPacketReceive(std::shared_ptr socket, WorldPacket packet); + void OnPacketSend(std::shared_ptr socket, WorldPacket packet); + void OnUnknownPacketReceive(std::shared_ptr socket, WorldPacket packet); public: /* WorldScript */ diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 63bceb53f2c..f30991b385e 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -20,7 +20,7 @@ \ingroup u2w */ -#include "WorldTcpSession.h" +#include "WorldSocket.h" #include "Config.h" #include "Common.h" #include "DatabaseEnv.h" @@ -97,7 +97,7 @@ bool WorldSessionFilter::Process(WorldPacket* packet) } /// WorldSession constructor -WorldSession::WorldSession(uint32 id, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): +WorldSession::WorldSession(uint32 id, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter): m_muteTime(mute_time), m_timeOutTime(0), AntiDOS(this), diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index d7b2b9ca4d4..06ae5d4f635 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -46,7 +46,7 @@ class SpellCastTargets; class Unit; class Warden; class WorldPacket; -class WorldTcpSession; +class WorldSocket; struct AreaTableEntry; struct AuctionEntry; struct DeclinedName; @@ -208,7 +208,7 @@ struct PacketCounter class WorldSession { public: - WorldSession(uint32 id, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); + WorldSession(uint32 id, std::shared_ptr sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter); ~WorldSession(); bool PlayerLoading() const { return m_playerLoading; } @@ -981,7 +981,7 @@ class WorldSession uint32 m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set) Player* _player; - std::shared_ptr m_Socket; + std::shared_ptr m_Socket; std::string m_Address; // Current Remote Address // std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session! diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp new file mode 100644 index 00000000000..f024d3d98ca --- /dev/null +++ b/src/server/game/Server/WorldSocket.cpp @@ -0,0 +1,475 @@ +/* +* Copyright (C) 2008-2014 TrinityCore +* Copyright (C) 2005-2009 MaNGOS +* +* 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 . +*/ + +#include +#include +#include +#include "WorldSocket.h" +#include "ServerPktHeader.h" +#include "BigNumber.h" +#include "Opcodes.h" +#include "ScriptMgr.h" +#include "SHA1.h" + +using boost::asio::ip::tcp; +using boost::asio::streambuf; + +WorldSocket::WorldSocket(tcp::socket socket) + : _socket(std::move(socket)), _authSeed(static_cast(rand32())), _worldSession(nullptr), _OverSpeedPings(0) +{ +} + +void WorldSocket::Start() +{ + AsyncReadHeader(); + HandleSendAuthSession(); +} + +void WorldSocket::HandleSendAuthSession() +{ + WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); + packet << uint32(1); // 1...31 + packet << uint32(_authSeed); + + BigNumber seed1; + seed1.SetRand(16 * 8); + packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds + + BigNumber seed2; + seed2.SetRand(16 * 8); + packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds + + AsyncWrite(packet); +} + + +void WorldSocket::AsyncReadHeader() +{ + auto self(shared_from_this()); + _socket.async_read_some(boost::asio::buffer(_readBuffer, sizeof(ClientPktHeader)), [this, self](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes == sizeof(ClientPktHeader)) + { + ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; + + if (_worldSession) + _authCrypt.DecryptRecv((uint8*)header, sizeof(ClientPktHeader)); + + EndianConvertReverse(header->size); + EndianConvert(header->cmd); + + AsyncReadData(header->size - sizeof(header->cmd)); + } + else + { + _socket.close(); + } + }); +} + +void WorldSocket::AsyncReadData(size_t dataSize) +{ + auto self(shared_from_this()); + _socket.async_read_some(boost::asio::buffer(&_readBuffer[sizeof(ClientPktHeader)], dataSize), [this, dataSize, self](boost::system::error_code error, size_t transferedBytes) + { + if (!error && transferedBytes == dataSize) + { + ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; + + header->size -= sizeof(header->cmd); + + uint16 opcode = (uint16)header->cmd; + + std::string opcodeName = GetOpcodeNameForLogging(opcode); + + WorldPacket packet(opcode, header->size); + + if (header->size > 0) + { + packet.resize(header->size); + + std::memcpy(packet.contents(), &_readBuffer[sizeof(ClientPktHeader)], header->size); + } + + switch (opcode) + { + case CMSG_PING: + HandlePing(packet); + break; + case CMSG_AUTH_SESSION: + if (_worldSession) + { + TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); + break; + } + + sScriptMgr->OnPacketReceive(shared_from_this(), packet); + HandleAuthSession(packet); + break; + case CMSG_KEEP_ALIVE: + TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); + sScriptMgr->OnPacketReceive(shared_from_this(), packet); + break; + default: + { + if (!_worldSession) + { + TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); + break; + } + + // Our Idle timer will reset on any non PING opcodes. + // Catches people idling on the login screen and any lingering ingame connections. + _worldSession->ResetTimeOutTime(); + + // Copy the packet to the heap before enqueuing + _worldSession->QueuePacket(new WorldPacket(packet)); + break; + } + } + + AsyncReadHeader(); + } + else + { + _socket.close(); + } + }); +} + +void WorldSocket::AsyncWrite(WorldPacket const& packet) +{ + ServerPktHeader header(packet.size() + 2, packet.GetOpcode()); + _authCrypt.EncryptSend((uint8*)header.header, header.getHeaderLength()); + + auto data = new char[header.getHeaderLength() + packet.size()]; + std::memcpy(data, (char*)header.header, header.getHeaderLength()); + + if (!packet.empty()) + std::memcpy(data + header.getHeaderLength(), (char const*)packet.contents(), packet.size()); + + std::shared_ptr buffer(data, [=](char* _b) + { + delete[] _b; + }); + + boost::asio::async_write(_socket, boost::asio::buffer(buffer.get(), header.getHeaderLength() + packet.size()), [this, buffer](boost::system::error_code error, std::size_t /*length*/) + { + if (error) + { + _socket.close(); + } + }); +} + +void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) +{ + uint8 digest[20]; + uint32 clientSeed; + uint8 security; + uint32 id; + LocaleConstant locale; + std::string account; + SHA1Hash sha; + uint32 clientBuild; + uint32 unk2, unk3, unk5, unk6, unk7; + uint64 unk4; + WorldPacket packet, SendAddonPacked; + BigNumber k; + bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); + + if (sWorld->IsClosed()) + { + SendAuthResponseError(AUTH_REJECT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteIpAddress().c_str()); + return; + } + + // Read the content of the packet + recvPacket >> clientBuild; + recvPacket >> unk2; + recvPacket >> account; + recvPacket >> unk3; + recvPacket >> clientSeed; + recvPacket >> unk5 >> unk6 >> unk7; + recvPacket >> unk4; + recvPacket.read(digest, 20); + + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", + clientBuild, + unk2, + account.c_str(), + unk3, + clientSeed); + + // Get the account information from the realmd database + // 0 1 2 3 4 5 6 7 8 + // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ? + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); + + stmt->setString(0, account); + + PreparedQueryResult result = LoginDatabase.Query(stmt); + + // Stop if the account is not found + if (!result) + { + // We can not log here, as we do not know the account. Thus, no accountId. + SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); + return; + } + + Field* fields = result->Fetch(); + + uint8 expansion = fields[4].GetUInt8(); + uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); + if (expansion > world_expansion) + expansion = world_expansion; + + // For hook purposes, we get Remoteaddress at this point. + std::string address = GetRemoteIpAddress(); + + // As we don't know if attempted login process by ip works, we update last_attempt_ip right away + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); + + stmt->setString(0, address); + stmt->setString(1, account); + + LoginDatabase.Execute(stmt); + // This also allows to check for possible "hack" attempts on account + + // id has to be fetched at this point, so that first actual account response that fails can be logged + id = fields[0].GetUInt32(); + + ///- Re-check ip locking (same check as in realmd). + if (fields[3].GetUInt8() == 1) // if ip is locked + { + if (strcmp(fields[2].GetCString(), address.c_str())) + { + SendAuthResponseError(AUTH_FAILED); + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); + // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well + sScriptMgr->OnFailedAccountLogin(id); + return; + } + } + + k.SetHexStr(fields[1].GetCString()); + + int64 mutetime = fields[5].GetInt64(); + //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. + if (mutetime < 0) + { + mutetime = time(NULL) + llabs(mutetime); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN); + + stmt->setInt64(0, mutetime); + stmt->setUInt32(1, id); + + LoginDatabase.Execute(stmt); + } + + locale = LocaleConstant(fields[6].GetUInt8()); + if (locale >= TOTAL_LOCALES) + locale = LOCALE_enUS; + + uint32 recruiter = fields[7].GetUInt32(); + std::string os = fields[8].GetString(); + + // Must be done before WorldSession is created + if (wardenActive && os != "Win" && os != "OSX") + { + SendAuthResponseError(AUTH_REJECT); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str()); + return; + } + + // Checks gmlevel per Realm + stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); + + stmt->setUInt32(0, id); + stmt->setInt32(1, int32(realmID)); + + result = LoginDatabase.Query(stmt); + + if (!result) + security = 0; + else + { + fields = result->Fetch(); + security = fields[0].GetUInt8(); + } + + // Re-check account ban (same check as in realmd) + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); + + stmt->setUInt32(0, id); + stmt->setString(1, address); + + PreparedQueryResult banresult = LoginDatabase.Query(stmt); + + if (banresult) // if account banned + { + SendAuthResponseError(AUTH_BANNED); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); + sScriptMgr->OnFailedAccountLogin(id); + return; + } + + // Check locked state for server + AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); + TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); + if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) + { + SendAuthResponseError(AUTH_UNAVAILABLE); + TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); + sScriptMgr->OnFailedAccountLogin(id); + return; + } + + // Check that Key and account name are the same on client and server + uint32 t = 0; + + sha.UpdateData(account); + sha.UpdateData((uint8*)&t, 4); + sha.UpdateData((uint8*)&clientSeed, 4); + sha.UpdateData((uint8*)&_authSeed, 4); + sha.UpdateBigNumbers(&k, NULL); + sha.Finalize(); + + if (memcmp(sha.GetDigest(), digest, 20)) + { + SendAuthResponseError(AUTH_FAILED); + TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str()); + return; + } + + TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", + account.c_str(), + address.c_str()); + + // Check if this user is by any chance a recruiter + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); + + stmt->setUInt32(0, id); + + result = LoginDatabase.Query(stmt); + + bool isRecruiter = false; + if (result) + isRecruiter = true; + + // Update the last_ip in the database as it was successful for login + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); + + stmt->setString(0, address); + stmt->setString(1, account); + + LoginDatabase.Execute(stmt); + + // NOTE ATM the socket is single-threaded, have this in mind ... + _worldSession = new WorldSession(id, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); + + _authCrypt.Init(&k); + + _worldSession->LoadGlobalAccountData(); + _worldSession->LoadTutorialsData(); + _worldSession->ReadAddonsInfo(recvPacket); + _worldSession->LoadPermissions(); + + // At this point, we can safely hook a successful login + sScriptMgr->OnAccountLogin(id); + + // Initialize Warden system only if it is enabled by config + if (wardenActive) + _worldSession->InitWarden(&k, os); + + sWorld->AddSession(_worldSession); +} + +void WorldSocket::SendAuthResponseError(uint8 code) +{ + WorldPacket packet(SMSG_AUTH_RESPONSE, 1); + packet << uint8(code); + + AsyncWrite(packet); +} + +void WorldSocket::HandlePing(WorldPacket& recvPacket) +{ + uint32 ping; + uint32 latency; + + // Get the ping packet content + recvPacket >> ping; + recvPacket >> latency; + + if (_LastPingTime == steady_clock::time_point()) + { + _LastPingTime = steady_clock::now(); + } + else + { + steady_clock::time_point now = steady_clock::now(); + + steady_clock::duration diff = now - _LastPingTime; + + _LastPingTime = now; + + if (diff < seconds(27)) + { + ++_OverSpeedPings; + + uint32 maxAllowed = sWorld->getIntConfig(CONFIG_MAX_OVERSPEED_PINGS); + + if (maxAllowed && _OverSpeedPings > maxAllowed) + { + if (_worldSession && !_worldSession->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING)) + { + TC_LOG_ERROR("network", "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)", + _worldSession->GetPlayerInfo().c_str(), GetRemoteIpAddress().c_str()); + + _socket.close(); + return; + } + } + } + else + _OverSpeedPings = 0; + } + + if (_worldSession) + { + _worldSession->SetLatency(latency); + _worldSession->ResetClientTimeDelay(); + } + else + { + TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, but is not authenticated or got recently kicked, address = %s", + GetRemoteIpAddress().c_str()); + + _socket.close(); + return; + } + + WorldPacket packet(SMSG_PONG, 4); + packet << ping; + return AsyncWrite(packet); +} diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h new file mode 100644 index 00000000000..ddce699f7f1 --- /dev/null +++ b/src/server/game/Server/WorldSocket.h @@ -0,0 +1,85 @@ +/* +* Copyright (C) 2008-2014 TrinityCore +* Copyright (C) 2005-2009 MaNGOS +* +* 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 . +*/ + +#ifndef __WORLDTCPSESSION_H__ +#define __WORLDTCPSESSION_H__ + +#include +#include +#include +#include +#include "Common.h" +#include "AuthCrypt.h" +#include "Util.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +using boost::asio::ip::tcp; + +#pragma pack(push, 1) + +struct ClientPktHeader +{ + uint16 size; + uint32 cmd; +}; + +#pragma pack(pop) + +class WorldSocket : public std::enable_shared_from_this +{ +public: + WorldSocket(tcp::socket socket); + + WorldSocket(WorldSocket const& right) = delete; + WorldSocket& operator=(WorldSocket const& right) = delete; + + void Start(); + + const std::string GetRemoteIpAddress() const { return _socket.remote_endpoint().address().to_string(); }; + unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); } + + void CloseSocket() { _socket.close(); }; + bool IsOpen() { return _socket.is_open(); }; + + void AsyncWrite(WorldPacket const& packet); + +private: + void HandleSendAuthSession(); + void HandleAuthSession(WorldPacket& recvPacket); + void SendAuthResponseError(uint8 code); + + void HandlePing(WorldPacket& recvPacket); + + void AsyncReadHeader(); + void AsyncReadData(size_t dataSize); + + tcp::socket _socket; + + char _readBuffer[4096]; + + uint32 _authSeed; + AuthCrypt _authCrypt; + + std::chrono::steady_clock::time_point _LastPingTime; + uint32 _OverSpeedPings; + + WorldSession* _worldSession; +}; + +#endif diff --git a/src/server/game/Server/WorldTcpSession.cpp b/src/server/game/Server/WorldTcpSession.cpp deleted file mode 100644 index 89ecb31ae68..00000000000 --- a/src/server/game/Server/WorldTcpSession.cpp +++ /dev/null @@ -1,475 +0,0 @@ -/* -* Copyright (C) 2008-2014 TrinityCore -* Copyright (C) 2005-2009 MaNGOS -* -* 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 . -*/ - -#include -#include -#include -#include "WorldTcpSession.h" -#include "ServerPktHeader.h" -#include "BigNumber.h" -#include "Opcodes.h" -#include "ScriptMgr.h" -#include "SHA1.h" - -using boost::asio::ip::tcp; -using boost::asio::streambuf; - -WorldTcpSession::WorldTcpSession(tcp::socket socket) - : _socket(std::move(socket)), _authSeed(static_cast(rand32())), _worldSession(nullptr), _OverSpeedPings(0) -{ -} - -void WorldTcpSession::Start() -{ - AsyncReadHeader(); - HandleSendAuthSession(); -} - -void WorldTcpSession::HandleSendAuthSession() -{ - WorldPacket packet(SMSG_AUTH_CHALLENGE, 37); - packet << uint32(1); // 1...31 - packet << uint32(_authSeed); - - BigNumber seed1; - seed1.SetRand(16 * 8); - packet.append(seed1.AsByteArray(16).get(), 16); // new encryption seeds - - BigNumber seed2; - seed2.SetRand(16 * 8); - packet.append(seed2.AsByteArray(16).get(), 16); // new encryption seeds - - AsyncWrite(packet); -} - - -void WorldTcpSession::AsyncReadHeader() -{ - auto self(shared_from_this()); - _socket.async_read_some(boost::asio::buffer(_readBuffer, sizeof(ClientPktHeader)), [this, self](boost::system::error_code error, size_t transferedBytes) - { - if (!error && transferedBytes == sizeof(ClientPktHeader)) - { - ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; - - if (_worldSession) - _authCrypt.DecryptRecv((uint8*)header, sizeof(ClientPktHeader)); - - EndianConvertReverse(header->size); - EndianConvert(header->cmd); - - AsyncReadData(header->size - sizeof(header->cmd)); - } - else - { - _socket.close(); - } - }); -} - -void WorldTcpSession::AsyncReadData(size_t dataSize) -{ - auto self(shared_from_this()); - _socket.async_read_some(boost::asio::buffer(&_readBuffer[sizeof(ClientPktHeader)], dataSize), [this, dataSize, self](boost::system::error_code error, size_t transferedBytes) - { - if (!error && transferedBytes == dataSize) - { - ClientPktHeader* header = (ClientPktHeader*)&_readBuffer; - - header->size -= sizeof(header->cmd); - - uint16 opcode = (uint16)header->cmd; - - std::string opcodeName = GetOpcodeNameForLogging(opcode); - - WorldPacket packet(opcode, header->size); - - if (header->size > 0) - { - packet.resize(header->size); - - std::memcpy(packet.contents(), &_readBuffer[sizeof(ClientPktHeader)], header->size); - } - - switch (opcode) - { - case CMSG_PING: - HandlePing(packet); - break; - case CMSG_AUTH_SESSION: - if (_worldSession) - { - TC_LOG_ERROR("network", "WorldSocket::ProcessIncoming: received duplicate CMSG_AUTH_SESSION from %s", _worldSession->GetPlayerInfo().c_str()); - break; - } - - sScriptMgr->OnPacketReceive(shared_from_this(), packet); - HandleAuthSession(packet); - break; - case CMSG_KEEP_ALIVE: - TC_LOG_DEBUG("network", "%s", opcodeName.c_str()); - sScriptMgr->OnPacketReceive(shared_from_this(), packet); - break; - default: - { - if (!_worldSession) - { - TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - break; - } - - // Our Idle timer will reset on any non PING opcodes. - // Catches people idling on the login screen and any lingering ingame connections. - _worldSession->ResetTimeOutTime(); - - // Copy the packet to the heap before enqueuing - _worldSession->QueuePacket(new WorldPacket(packet)); - break; - } - } - - AsyncReadHeader(); - } - else - { - _socket.close(); - } - }); -} - -void WorldTcpSession::AsyncWrite(WorldPacket const& packet) -{ - ServerPktHeader header(packet.size() + 2, packet.GetOpcode()); - _authCrypt.EncryptSend((uint8*)header.header, header.getHeaderLength()); - - auto data = new char[header.getHeaderLength() + packet.size()]; - std::memcpy(data, (char*)header.header, header.getHeaderLength()); - - if (!packet.empty()) - std::memcpy(data + header.getHeaderLength(), (char const*)packet.contents(), packet.size()); - - std::shared_ptr buffer(data, [=](char* _b) - { - delete[] _b; - }); - - boost::asio::async_write(_socket, boost::asio::buffer(buffer.get(), header.getHeaderLength() + packet.size()), [this, buffer](boost::system::error_code error, std::size_t /*length*/) - { - if (error) - { - _socket.close(); - } - }); -} - -void WorldTcpSession::HandleAuthSession(WorldPacket& recvPacket) -{ - uint8 digest[20]; - uint32 clientSeed; - uint8 security; - uint32 id; - LocaleConstant locale; - std::string account; - SHA1Hash sha; - uint32 clientBuild; - uint32 unk2, unk3, unk5, unk6, unk7; - uint64 unk4; - WorldPacket packet, SendAddonPacked; - BigNumber k; - bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED); - - if (sWorld->IsClosed()) - { - SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: World closed, denying client (%s).", GetRemoteIpAddress().c_str()); - return; - } - - // Read the content of the packet - recvPacket >> clientBuild; - recvPacket >> unk2; - recvPacket >> account; - recvPacket >> unk3; - recvPacket >> clientSeed; - recvPacket >> unk5 >> unk6 >> unk7; - recvPacket >> unk4; - recvPacket.read(digest, 20); - - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u", - clientBuild, - unk2, - account.c_str(), - unk3, - clientSeed); - - // Get the account information from the realmd database - // 0 1 2 3 4 5 6 7 8 - // SELECT id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ? - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME); - - stmt->setString(0, account); - - PreparedQueryResult result = LoginDatabase.Query(stmt); - - // Stop if the account is not found - if (!result) - { - // We can not log here, as we do not know the account. Thus, no accountId. - SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account)."); - return; - } - - Field* fields = result->Fetch(); - - uint8 expansion = fields[4].GetUInt8(); - uint32 world_expansion = sWorld->getIntConfig(CONFIG_EXPANSION); - if (expansion > world_expansion) - expansion = world_expansion; - - // For hook purposes, we get Remoteaddress at this point. - std::string address = GetRemoteIpAddress(); - - // As we don't know if attempted login process by ip works, we update last_attempt_ip right away - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP); - - stmt->setString(0, address); - stmt->setString(1, account); - - LoginDatabase.Execute(stmt); - // This also allows to check for possible "hack" attempts on account - - // id has to be fetched at this point, so that first actual account response that fails can be logged - id = fields[0].GetUInt32(); - - ///- Re-check ip locking (same check as in realmd). - if (fields[3].GetUInt8() == 1) // if ip is locked - { - if (strcmp(fields[2].GetCString(), address.c_str())) - { - SendAuthResponseError(AUTH_FAILED); - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str()); - // We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well - sScriptMgr->OnFailedAccountLogin(id); - return; - } - } - - k.SetHexStr(fields[1].GetCString()); - - int64 mutetime = fields[5].GetInt64(); - //! Negative mutetime indicates amount of seconds to be muted effective on next login - which is now. - if (mutetime < 0) - { - mutetime = time(NULL) + llabs(mutetime); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN); - - stmt->setInt64(0, mutetime); - stmt->setUInt32(1, id); - - LoginDatabase.Execute(stmt); - } - - locale = LocaleConstant(fields[6].GetUInt8()); - if (locale >= TOTAL_LOCALES) - locale = LOCALE_enUS; - - uint32 recruiter = fields[7].GetUInt32(); - std::string os = fields[8].GetString(); - - // Must be done before WorldSession is created - if (wardenActive && os != "Win" && os != "OSX") - { - SendAuthResponseError(AUTH_REJECT); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str()); - return; - } - - // Checks gmlevel per Realm - stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); - - stmt->setUInt32(0, id); - stmt->setInt32(1, int32(realmID)); - - result = LoginDatabase.Query(stmt); - - if (!result) - security = 0; - else - { - fields = result->Fetch(); - security = fields[0].GetUInt8(); - } - - // Re-check account ban (same check as in realmd) - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS); - - stmt->setUInt32(0, id); - stmt->setString(1, address); - - PreparedQueryResult banresult = LoginDatabase.Query(stmt); - - if (banresult) // if account banned - { - SendAuthResponseError(AUTH_BANNED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned)."); - sScriptMgr->OnFailedAccountLogin(id); - return; - } - - // Check locked state for server - AccountTypes allowedAccountType = sWorld->GetPlayerSecurityLimit(); - TC_LOG_DEBUG("network", "Allowed Level: %u Player Level %u", allowedAccountType, AccountTypes(security)); - if (allowedAccountType > SEC_PLAYER && AccountTypes(security) < allowedAccountType) - { - SendAuthResponseError(AUTH_UNAVAILABLE); - TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough"); - sScriptMgr->OnFailedAccountLogin(id); - return; - } - - // Check that Key and account name are the same on client and server - uint32 t = 0; - - sha.UpdateData(account); - sha.UpdateData((uint8*)&t, 4); - sha.UpdateData((uint8*)&clientSeed, 4); - sha.UpdateData((uint8*)&_authSeed, 4); - sha.UpdateBigNumbers(&k, NULL); - sha.Finalize(); - - if (memcmp(sha.GetDigest(), digest, 20)) - { - SendAuthResponseError(AUTH_FAILED); - TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Authentication failed for account: %u ('%s') address: %s", id, account.c_str(), address.c_str()); - return; - } - - TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.", - account.c_str(), - address.c_str()); - - // Check if this user is by any chance a recruiter - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_RECRUITER); - - stmt->setUInt32(0, id); - - result = LoginDatabase.Query(stmt); - - bool isRecruiter = false; - if (result) - isRecruiter = true; - - // Update the last_ip in the database as it was successful for login - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP); - - stmt->setString(0, address); - stmt->setString(1, account); - - LoginDatabase.Execute(stmt); - - // NOTE ATM the socket is single-threaded, have this in mind ... - _worldSession = new WorldSession(id, shared_from_this(), AccountTypes(security), expansion, mutetime, locale, recruiter, isRecruiter); - - _authCrypt.Init(&k); - - _worldSession->LoadGlobalAccountData(); - _worldSession->LoadTutorialsData(); - _worldSession->ReadAddonsInfo(recvPacket); - _worldSession->LoadPermissions(); - - // At this point, we can safely hook a successful login - sScriptMgr->OnAccountLogin(id); - - // Initialize Warden system only if it is enabled by config - if (wardenActive) - _worldSession->InitWarden(&k, os); - - sWorld->AddSession(_worldSession); -} - -void WorldTcpSession::SendAuthResponseError(uint8 code) -{ - WorldPacket packet(SMSG_AUTH_RESPONSE, 1); - packet << uint8(code); - - AsyncWrite(packet); -} - -void WorldTcpSession::HandlePing(WorldPacket& recvPacket) -{ - uint32 ping; - uint32 latency; - - // Get the ping packet content - recvPacket >> ping; - recvPacket >> latency; - - if (_LastPingTime == steady_clock::time_point()) - { - _LastPingTime = steady_clock::now(); - } - else - { - steady_clock::time_point now = steady_clock::now(); - - steady_clock::duration diff = now - _LastPingTime; - - _LastPingTime = now; - - if (diff < seconds(27)) - { - ++_OverSpeedPings; - - uint32 maxAllowed = sWorld->getIntConfig(CONFIG_MAX_OVERSPEED_PINGS); - - if (maxAllowed && _OverSpeedPings > maxAllowed) - { - if (_worldSession && !_worldSession->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING)) - { - TC_LOG_ERROR("network", "WorldSocket::HandlePing: %s kicked for over-speed pings (address: %s)", - _worldSession->GetPlayerInfo().c_str(), GetRemoteIpAddress().c_str()); - - _socket.close(); - return; - } - } - } - else - _OverSpeedPings = 0; - } - - if (_worldSession) - { - _worldSession->SetLatency(latency); - _worldSession->ResetClientTimeDelay(); - } - else - { - TC_LOG_ERROR("network", "WorldSocket::HandlePing: peer sent CMSG_PING, but is not authenticated or got recently kicked, address = %s", - GetRemoteIpAddress().c_str()); - - _socket.close(); - return; - } - - WorldPacket packet(SMSG_PONG, 4); - packet << ping; - return AsyncWrite(packet); -} diff --git a/src/server/game/Server/WorldTcpSession.h b/src/server/game/Server/WorldTcpSession.h deleted file mode 100644 index 387768bfbef..00000000000 --- a/src/server/game/Server/WorldTcpSession.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -* Copyright (C) 2008-2014 TrinityCore -* Copyright (C) 2005-2009 MaNGOS -* -* 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 . -*/ - -#ifndef __WORLDTCPSESSION_H__ -#define __WORLDTCPSESSION_H__ - -#include -#include -#include -#include -#include "Common.h" -#include "AuthCrypt.h" -#include "Util.h" -#include "WorldPacket.h" -#include "WorldSession.h" - -using boost::asio::ip::tcp; - -#pragma pack(push, 1) - -struct ClientPktHeader -{ - uint16 size; - uint32 cmd; -}; - -#pragma pack(pop) - -class WorldTcpSession : public std::enable_shared_from_this -{ -public: - WorldTcpSession(tcp::socket socket); - - WorldTcpSession(WorldTcpSession const& right) = delete; - WorldTcpSession& operator=(WorldTcpSession const& right) = delete; - - void Start(); - - const std::string GetRemoteIpAddress() const { return _socket.remote_endpoint().address().to_string(); }; - unsigned short GetRemotePort() const { return _socket.remote_endpoint().port(); } - - void CloseSocket() { _socket.close(); }; - bool IsOpen() { return _socket.is_open(); }; - - void AsyncWrite(WorldPacket const& packet); - -private: - void HandleSendAuthSession(); - void HandleAuthSession(WorldPacket& recvPacket); - void SendAuthResponseError(uint8 code); - - void HandlePing(WorldPacket& recvPacket); - - void AsyncReadHeader(); - void AsyncReadData(size_t dataSize); - - tcp::socket _socket; - - char _readBuffer[4096]; - - uint32 _authSeed; - AuthCrypt _authCrypt; - - std::chrono::steady_clock::time_point _LastPingTime; - uint32 _OverSpeedPings; - - WorldSession* _worldSession; -}; - -#endif diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index dae947b7324..15a282b70ea 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -43,7 +43,7 @@ #include "TCSoap.h" #include "CliRunnable.h" #include "SystemConfig.h" -#include "WorldTcpSession.h" +#include "WorldSocket.h" #define TRINITY_CORE_CONFIG "worldserver.conf" #define WORLD_SLEEP_CONST 50 @@ -230,7 +230,7 @@ extern int main(int argc, char** argv) std::string worldListener = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0"); bool tcpNoDelay = sConfigMgr->GetBoolDefault("Network.TcpNodelay", true); - AsyncAcceptor worldAcceptor(_ioService, worldListener, worldPort, tcpNoDelay); + AsyncAcceptor worldAcceptor(_ioService, worldListener, worldPort, tcpNoDelay); sScriptMgr->OnStartup(); -- cgit v1.2.3 From d1594998f80762fa58f64cf123f9bf9cb77036e4 Mon Sep 17 00:00:00 2001 From: leak Date: Tue, 8 Jul 2014 20:55:25 +0200 Subject: Replaced the LogWorker thread with Boost ASIO --- src/server/shared/Database/DatabaseWorker.h | 1 + src/server/shared/Logging/Log.cpp | 17 +++++---- src/server/shared/Logging/Log.h | 16 ++++++-- src/server/shared/Logging/LogWorker.cpp | 56 ---------------------------- src/server/shared/Logging/LogWorker.h | 46 ----------------------- src/server/shared/Networking/AsyncAcceptor.h | 4 +- src/server/worldserver/Main.cpp | 6 +++ 7 files changed, 31 insertions(+), 115 deletions(-) delete mode 100644 src/server/shared/Logging/LogWorker.cpp delete mode 100644 src/server/shared/Logging/LogWorker.h (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/shared/Database/DatabaseWorker.h b/src/server/shared/Database/DatabaseWorker.h index 91aef2c7194..6f452c767f6 100644 --- a/src/server/shared/Database/DatabaseWorker.h +++ b/src/server/shared/Database/DatabaseWorker.h @@ -18,6 +18,7 @@ #ifndef _WORKERTHREAD_H #define _WORKERTHREAD_H +#include #include "ProducerConsumerQueue.h" class MySQLConnection; diff --git a/src/server/shared/Logging/Log.cpp b/src/server/shared/Logging/Log.cpp index 57d8797e61e..65cf930a634 100644 --- a/src/server/shared/Logging/Log.cpp +++ b/src/server/shared/Logging/Log.cpp @@ -29,7 +29,7 @@ #include #include -Log::Log() : worker(NULL) +Log::Log() : _ioService(nullptr), _strand(nullptr) { m_logsTimestamp = "_" + GetTimestampStr(); LoadFromConfig(); @@ -37,6 +37,7 @@ Log::Log() : worker(NULL) Log::~Log() { + delete _strand; Close(); } @@ -272,8 +273,13 @@ void Log::write(LogMessage* msg) const Logger const* logger = GetLoggerByType(msg->type); msg->text.append("\n"); - if (worker) - worker->Enqueue(new LogOperation(logger, msg)); + if (_ioService) + { + auto logOperation = std::shared_ptr(new LogOperation(logger, msg)); + + _ioService->post(_strand->wrap([logOperation](){ logOperation->call(); })); + + } else { logger->write(*msg); @@ -375,8 +381,6 @@ void Log::SetRealmId(uint32 id) void Log::Close() { - delete worker; - worker = NULL; loggers.clear(); for (AppenderMap::iterator it = appenders.begin(); it != appenders.end(); ++it) { @@ -390,9 +394,6 @@ void Log::LoadFromConfig() { Close(); - if (sConfigMgr->GetBoolDefault("Log.Async.Enable", false)) - worker = new LogWorker(); - AppenderId = 0; m_logsDir = sConfigMgr->GetStringDefault("LogsDir", ""); if (!m_logsDir.empty()) diff --git a/src/server/shared/Logging/Log.h b/src/server/shared/Logging/Log.h index 8d2fd33d886..e739c9eaf4e 100644 --- a/src/server/shared/Logging/Log.h +++ b/src/server/shared/Logging/Log.h @@ -22,8 +22,9 @@ #include "Define.h" #include "Appender.h" #include "Logger.h" -#include "LogWorker.h" #include +#include +#include #include #include @@ -39,9 +40,17 @@ class Log ~Log(); public: - static Log* instance() + + static Log* instance(boost::asio::io_service* ioService = nullptr) { static Log* instance = new Log(); + + if (ioService != nullptr) + { + instance->_ioService = ioService; + instance->_strand = new boost::asio::strand(*ioService); + } + return instance; } @@ -77,7 +86,8 @@ class Log std::string m_logsDir; std::string m_logsTimestamp; - LogWorker* worker; + boost::asio::io_service* _ioService; + boost::asio::strand* _strand; }; inline Logger const* Log::GetLoggerByType(std::string const& type) const diff --git a/src/server/shared/Logging/LogWorker.cpp b/src/server/shared/Logging/LogWorker.cpp deleted file mode 100644 index ab0f41bf105..00000000000 --- a/src/server/shared/Logging/LogWorker.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore - * - * 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 . - */ - -#include "LogWorker.h" -#include - -LogWorker::LogWorker() -{ - _cancelationToken = false; - _workerThread = std::thread(&LogWorker::WorkerThread, this); -} - -LogWorker::~LogWorker() -{ - _cancelationToken = true; - - _queue.Cancel(); - - _workerThread.join(); -} - -void LogWorker::Enqueue(LogOperation* op) -{ - return _queue.Push(op); -} - -void LogWorker::WorkerThread() -{ - while (1) - { - LogOperation* operation = nullptr; - - _queue.WaitAndPop(operation); - - if (_cancelationToken) - return; - - operation->call(); - - delete operation; - } -} diff --git a/src/server/shared/Logging/LogWorker.h b/src/server/shared/Logging/LogWorker.h deleted file mode 100644 index b2680b12c34..00000000000 --- a/src/server/shared/Logging/LogWorker.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore - * - * 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 . - */ - -#ifndef LOGWORKER_H -#define LOGWORKER_H - -#include -#include - -#include "LogOperation.h" -#include "ProducerConsumerQueue.h" - -class LogWorker -{ - public: - LogWorker(); - ~LogWorker(); - - void Enqueue(LogOperation *op); - - private: - ProducerConsumerQueue _queue; - - void WorkerThread(); - std::thread _workerThread; - - std::atomic_bool _cancelationToken; -}; - - - -#endif diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h index 1b65282527a..789bd9c3a74 100644 --- a/src/server/shared/Networking/AsyncAcceptor.h +++ b/src/server/shared/Networking/AsyncAcceptor.h @@ -37,9 +37,9 @@ public: _socket(ioService), _acceptor(ioService, tcp::endpoint(boost::asio::ip::address::from_string(bindIp), port)) { - AsyncAccept(); + _acceptor.set_option(boost::asio::ip::tcp::no_delay(tcpNoDelay)); - _socket.set_option(boost::asio::ip::tcp::no_delay(tcpNoDelay)); + AsyncAccept(); }; private: diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 15a282b70ea..05ae31e8780 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -145,6 +145,12 @@ extern int main(int argc, char** argv) return 1; } + if (sConfigMgr->GetBoolDefault("Log.Async.Enable", false)) + { + // If logs are supposed to be handled async then we need to pass the io_service into the Log singleton + Log::instance(&_ioService); + } + TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon)", _FULLVERSION); TC_LOG_INFO("server.worldserver", " to stop.\n"); TC_LOG_INFO("server.worldserver", " ______ __"); -- cgit v1.2.3 From 2f28f4ce75c407d929596aaa874a9f4d040a7da3 Mon Sep 17 00:00:00 2001 From: leak Date: Tue, 8 Jul 2014 21:17:24 +0200 Subject: Fixed FreezeDetectorHandler trigger time --- src/server/worldserver/Main.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 05ae31e8780..8df31c5022a 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -66,7 +66,7 @@ boost::asio::io_service _ioService; boost::asio::deadline_timer _freezeCheckTimer(_ioService); uint32 _worldLoopCounter(0); uint32 _lastChangeMsTime(0); -uint32 _maxCoreStuckTime(0); +uint32 _maxCoreStuckTimeInMs(0); WorldDatabaseWorkerPool WorldDatabase; ///< Accessor to the world database CharacterDatabaseWorkerPool CharacterDatabase; ///< Accessor to the character database @@ -244,11 +244,12 @@ extern int main(int argc, char** argv) LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID); // Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec) - if (_maxCoreStuckTime = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0)) + if (int coreStuckTime = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0)) { + _maxCoreStuckTimeInMs = coreStuckTime * 1000; _freezeCheckTimer.expires_from_now(boost::posix_time::seconds(5)); _freezeCheckTimer.async_wait(FreezeDetectorHandler); - TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", _maxCoreStuckTime); + TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", coreStuckTime); } TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", _FULLVERSION); @@ -435,7 +436,7 @@ void FreezeDetectorHandler(const boost::system::error_code& error) _worldLoopCounter = worldLoopCounter; } // possible freeze - else if (getMSTimeDiff(_lastChangeMsTime, curtime) > _maxCoreStuckTime) + else if (getMSTimeDiff(_lastChangeMsTime, curtime) > _maxCoreStuckTimeInMs) { TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!"); ASSERT(false); -- cgit v1.2.3 From 375363ed08069b6aa7a921e35fdd1cc7affc946e Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 9 Jul 2014 16:52:41 -0500 Subject: Fixed the first world connection being slow --- src/server/worldserver/Main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 8df31c5022a..0e7e7064031 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -102,7 +102,7 @@ extern int main(int argc, char** argv) cfg_file = argv[c]; } - #ifdef _WIN32 +#ifdef _WIN32 if (strcmp(argv[c], "-s") == 0) // Services { if (++c >= argc) @@ -134,7 +134,7 @@ extern int main(int argc, char** argv) if (strcmp(argv[c], "--service") == 0) WinServiceRun(); - #endif +#endif ++c; } @@ -168,6 +168,11 @@ extern int main(int argc, char** argv) OpenSSLCrypto::threadsSetup(); + // Seed the OpenSSL's PRNG here. + // That way it won't auto-seed when calling BigNumber::SetRand and slow down the first world login + BigNumber seed; + seed.SetRand(16 * 8); + /// worldserver PID file creation std::string pidFile = sConfigMgr->GetStringDefault("PidFile", ""); if (!pidFile.empty()) -- cgit v1.2.3 From a4f7211aedc767878fa2234c592ebf83c2e3fa10 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 13 Jul 2014 15:55:46 -0500 Subject: Fixed the worldserver not using the path to the config file as defined in cmake --- src/server/worldserver/Main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 0e7e7064031..967a31579fa 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -45,7 +45,10 @@ #include "SystemConfig.h" #include "WorldSocket.h" -#define TRINITY_CORE_CONFIG "worldserver.conf" +#ifndef _TRINITY_CORE_CONFIG + #define _TRINITY_CORE_CONFIG "worldserver.conf" +#endif + #define WORLD_SLEEP_CONST 50 #ifdef _WIN32 @@ -86,7 +89,7 @@ void ClearOnlineAccounts(); 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) { -- cgit v1.2.3 From 68398a559e2264b85d8765949989f44e39ce364d Mon Sep 17 00:00:00 2001 From: Chaplain Date: Tue, 15 Jul 2014 17:46:09 +0200 Subject: [Auth\Worldserver] Use boost to load console arguments. (Added a few style changes and cmake fix) Conflicts: src/server/worldserver/Main.cpp --- cmake/macros/ConfigureBoost.cmake | 2 +- src/server/authserver/Main.cpp | 60 ++++++++------ src/server/shared/Configuration/Config.cpp | 12 ++- src/server/shared/Configuration/Config.h | 10 +-- src/server/shared/Utilities/ServiceWin32.cpp | 6 +- src/server/worldserver/Main.cpp | 118 ++++++++++++--------------- 6 files changed, 101 insertions(+), 107 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/cmake/macros/ConfigureBoost.cmake b/cmake/macros/ConfigureBoost.cmake index a06b1949964..aa64414afb7 100644 --- a/cmake/macros/ConfigureBoost.cmake +++ b/cmake/macros/ConfigureBoost.cmake @@ -27,7 +27,7 @@ if(WIN32) add_definitions(-D_WIN32_WINNT=${ver}) endif() -find_package(Boost 1.49 REQUIRED system thread) +find_package(Boost 1.49 REQUIRED system thread program_options) add_definitions(-DBOOST_DATE_TIME_NO_LIB) add_definitions(-DBOOST_REGEX_NO_LIB) add_definitions(-DBOOST_CHRONO_NO_LIB) diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 1286e261a47..701f65c0c14 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include "Util.h" using boost::asio::ip::tcp; +using namespace boost::program_options; #ifndef _TRINITY_REALM_CONFIG # define _TRINITY_REALM_CONFIG "authserver.conf" @@ -51,7 +53,7 @@ bool StartDB(); void StopDB(); void SignalHandler(const boost::system::error_code& error, int signalNumber); void KeepDatabaseAliveHandler(const boost::system::error_code& error); -void usage(const char* prog); +variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile); boost::asio::io_service _ioService; boost::asio::deadline_timer _dbPingTimer(_ioService); @@ -60,36 +62,24 @@ LoginDatabaseWorkerPool LoginDatabase; int main(int argc, char** argv) { - // Command line parsing to get the configuration file name - char const* configFile = _TRINITY_REALM_CONFIG; - int count = 1; - while (count < argc) - { - if (strcmp(argv[count], "-c") == 0) - { - if (++count >= argc) - { - printf("Runtime-Error: -c option requires an input argument\n"); - usage(argv[0]); - return 1; - } - else - configFile = argv[count]; - } - ++count; - } + std::string configFile = _TRINITY_REALM_CONFIG; + auto vm = GetConsoleArguments(argc, argv, configFile); + // exit if help is enabled + if (vm.count("help")) + return 0; if (!sConfigMgr->LoadInitial(configFile)) { - printf("Invalid or missing configuration file : %s\n", configFile); + printf("Invalid or missing configuration file : %s\n", configFile.c_str()); printf("Verify that the file exists and has \'[authserver]\' written in the top of the file!\n"); return 1; } TC_LOG_INFO("server.authserver", "%s (authserver)", _FULLVERSION); TC_LOG_INFO("server.authserver", " 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)); + TC_LOG_INFO("server.authserver", "Using configuration file %s.", configFile.c_str()); + 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: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); // authserver PID file creation std::string pidFile = sConfigMgr->GetStringDefault("PidFile", ""); @@ -222,10 +212,26 @@ void KeepDatabaseAliveHandler(const boost::system::error_code& error) } } -/// Print out the usage string for this program on the console. -void usage(const char* prog) +variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile) { - TC_LOG_INFO("server.authserver", "Usage: \n %s []\n" - " -c config_file use config_file as configuration file\n\r", - prog); + options_description all("Allowed options"); + all.add_options() + ("help,h", "print usage message") + ("config,c", value(&configFile)->default_value(_TRINITY_REALM_CONFIG), "use as configuration file") + ; + variables_map variablesMap; + try + { + store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), variablesMap); + notify(variablesMap); + } + catch (std::exception& e) { + std::cerr << e.what() << "\n"; + } + + if (variablesMap.count("help")) { + std::cout << all << "\n"; + } + + return variablesMap; } diff --git a/src/server/shared/Configuration/Config.cpp b/src/server/shared/Configuration/Config.cpp index fe61cde5594..5cd7ef52f82 100644 --- a/src/server/shared/Configuration/Config.cpp +++ b/src/server/shared/Configuration/Config.cpp @@ -25,10 +25,8 @@ using namespace boost::property_tree; -bool ConfigMgr::LoadInitial(char const* file) +bool ConfigMgr::LoadInitial(std::string const& file) { - ASSERT(file); - std::lock_guard lock(_configLock); _filename = file; @@ -57,7 +55,7 @@ bool ConfigMgr::Reload() return LoadInitial(_filename.c_str()); } -std::string ConfigMgr::GetStringDefault(const char* name, const std::string& def) +std::string ConfigMgr::GetStringDefault(std::string const& name, const std::string& def) { std::string value = _config.get(ptree::path_type(name, '/'), def); @@ -66,7 +64,7 @@ std::string ConfigMgr::GetStringDefault(const char* name, const std::string& def return value; } -bool ConfigMgr::GetBoolDefault(const char* name, bool def) +bool ConfigMgr::GetBoolDefault(std::string const& name, bool def) { try { @@ -80,12 +78,12 @@ bool ConfigMgr::GetBoolDefault(const char* name, bool def) } } -int ConfigMgr::GetIntDefault(const char* name, int def) +int ConfigMgr::GetIntDefault(std::string const& name, int def) { return _config.get(ptree::path_type(name, '/'), def); } -float ConfigMgr::GetFloatDefault(const char* name, float def) +float ConfigMgr::GetFloatDefault(std::string const& name, float def) { return _config.get(ptree::path_type(name, '/'), def); } diff --git a/src/server/shared/Configuration/Config.h b/src/server/shared/Configuration/Config.h index d05a083d166..68daca5440f 100644 --- a/src/server/shared/Configuration/Config.h +++ b/src/server/shared/Configuration/Config.h @@ -31,7 +31,7 @@ class ConfigMgr public: /// Method used only for loading main configuration files (authserver.conf and worldserver.conf) - bool LoadInitial(char const* file); + bool LoadInitial(std::string const& file); static ConfigMgr* instance() { @@ -41,10 +41,10 @@ public: bool Reload(); - std::string GetStringDefault(const char* name, const std::string& def); - bool GetBoolDefault(const char* name, bool def); - int GetIntDefault(const char* name, int def); - float GetFloatDefault(const char* name, float def); + std::string GetStringDefault(std::string const& name, const std::string& def); + bool GetBoolDefault(std::string const& name, bool def); + int GetIntDefault(std::string const& name, int def); + float GetFloatDefault(std::string const& name, float def); std::string const& GetFilename(); std::list GetKeysByString(std::string const& name); diff --git a/src/server/shared/Utilities/ServiceWin32.cpp b/src/server/shared/Utilities/ServiceWin32.cpp index ec472b33f40..f4a0339d9e6 100644 --- a/src/server/shared/Utilities/ServiceWin32.cpp +++ b/src/server/shared/Utilities/ServiceWin32.cpp @@ -60,7 +60,7 @@ bool WinServiceInstall() if (GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0) { SC_HANDLE service; - std::strcat(path, " --service"); + std::strcat(path, " --service run"); service = CreateService(serviceControlManager, serviceName, // name of service serviceLongName, // service name to display @@ -119,6 +119,8 @@ bool WinServiceInstall() } CloseServiceHandle(serviceControlManager); } + + printf("Service installed\n"); return true; } @@ -143,6 +145,8 @@ bool WinServiceUninstall() CloseServiceHandle(serviceControlManager); } + + printf("Service uninstalled\n"); return true; } diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 967a31579fa..c28adbc7c21 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "Common.h" #include "DatabaseEnv.h" @@ -45,6 +46,8 @@ #include "SystemConfig.h" #include "WorldSocket.h" +using namespace boost::program_options; + #ifndef _TRINITY_CORE_CONFIG #define _TRINITY_CORE_CONFIG "worldserver.conf" #endif @@ -76,7 +79,6 @@ CharacterDatabaseWorkerPool CharacterDatabase; ///< Accessor to the LoginDatabaseWorkerPool LoginDatabase; ///< Accessor to the realm/login database uint32 realmID; ///< Id of the realm -void usage(const char* prog); void SignalHandler(const boost::system::error_code& error, int signalNumber); void FreezeDetectorHandler(const boost::system::error_code& error); AsyncAcceptor* StartRaSocketAcceptor(boost::asio::io_service& ioService); @@ -84,66 +86,32 @@ bool StartDB(); void StopDB(); void WorldUpdateLoop(); void ClearOnlineAccounts(); +variables_map GetConsoleArguments(int argc, char** argv, std::string& cfg_file, std::string& cfg_service); /// 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; - int c = 1; - while (c < argc) - { - if (!strcmp(argv[c], "-c")) - { - if (++c >= argc) - { - printf("Runtime-Error: -c option requires an input argument"); - usage(argv[0]); - return 1; - } - else - cfg_file = argv[c]; - } + std::string configFile = _TRINITY_CORE_CONFIG; + std::string configService; -#ifdef _WIN32 - if (strcmp(argv[c], "-s") == 0) // Services - { - if (++c >= argc) - { - printf("Runtime-Error: -s option requires an input argument"); - usage(argv[0]); - return 1; - } - - if (strcmp(argv[c], "install") == 0) - { - if (WinServiceInstall()) - printf("Installing service\n"); - return 1; - } - else if (strcmp(argv[c], "uninstall") == 0) - { - if (WinServiceUninstall()) - printf("Uninstalling service\n"); - return 1; - } - else - { - printf("Runtime-Error: unsupported option %s", argv[c]); - usage(argv[0]); - return 1; - } - } + auto vm = GetConsoleArguments(argc, argv, configFile, configService); + // exit if help is enabled + if (vm.count("help")) + return 0; - if (strcmp(argv[c], "--service") == 0) + if (!configService.empty()) + { + if (configService.compare("install") == 0) + return WinServiceInstall() == true ? 0 : 1; + else if (configService.compare("uninstall") == 0) + return WinServiceUninstall() == true ? 0 : 1; + else if (configService.compare("run") == 0) WinServiceRun(); -#endif - ++c; } - if (!sConfigMgr->LoadInitial(cfg_file)) + if (!sConfigMgr->LoadInitial(configFile)) { - printf("Invalid or missing configuration file : %s\n", cfg_file); + printf("Invalid or missing configuration file : %s\n", configFile.c_str()); printf("Verify that the file exists and has \'[worldserver]' written in the top of the file!\n"); return 1; } @@ -165,7 +133,7 @@ extern int main(int argc, char** argv) 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 configuration file %s.", configFile.c_str()); 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: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); @@ -403,20 +371,6 @@ void WorldUpdateLoop() } } -/// Print out the usage string for this program on the console. -void usage(const char* prog) -{ - printf("Usage:\n"); - printf(" %s []\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) @@ -587,3 +541,35 @@ void ClearOnlineAccounts() } /// @} + +variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService) +{ + options_description all("Allowed options"); + all.add_options() + ("help,h", "print usage message") + ("config,c", value(&configFile)->default_value(_TRINITY_CORE_CONFIG), "use as configuration file") + ; +#ifdef _WIN32 + options_description win("Windows platform specific options"); + win.add_options() + ("service,s", value(&configService)->default_value(""), "Windows service options: [install | uninstall]") + ; + + all.add(win); +#endif + variables_map vm; + try + { + store(command_line_parser(argc, argv).options(all).allow_unregistered().run(), vm); + notify(vm); + } + catch (std::exception& e) { + std::cerr << e.what() << "\n"; + } + + if (vm.count("help")) { + std::cout << all << "\n"; + } + + return vm; +} -- cgit v1.2.3 From 8c10ffa62d1d9f6630eca22e2f37ce10deb2cf39 Mon Sep 17 00:00:00 2001 From: leak Date: Wed, 16 Jul 2014 19:27:15 +0200 Subject: GCC compile fixes --- src/server/worldserver/Main.cpp | 17 ++++++++--------- src/tools/mmaps_generator/MapBuilder.cpp | 1 + src/tools/mmaps_generator/MapBuilder.h | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index c28adbc7c21..a7b430a4e97 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -99,15 +99,14 @@ extern int main(int argc, char** argv) if (vm.count("help")) return 0; - if (!configService.empty()) - { - if (configService.compare("install") == 0) - return WinServiceInstall() == true ? 0 : 1; - else if (configService.compare("uninstall") == 0) - return WinServiceUninstall() == true ? 0 : 1; - else if (configService.compare("run") == 0) - WinServiceRun(); - } +#ifdef _WIN32 + if (configService.compare("install") == 0) + return WinServiceInstall() == true ? 0 : 1; + else if (configService.compare("uninstall") == 0) + return WinServiceUninstall() == true ? 0 : 1; + else if (configService.compare("run") == 0) + WinServiceRun(); +#endif if (!sConfigMgr->LoadInitial(configFile)) { diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp index 3b1516b3d11..01a342249fd 100644 --- a/src/tools/mmaps_generator/MapBuilder.cpp +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -15,6 +15,7 @@ * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ +#include #include "PathCommon.h" #include "MapBuilder.h" diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h index 4cd36602a4b..944d03968b5 100644 --- a/src/tools/mmaps_generator/MapBuilder.h +++ b/src/tools/mmaps_generator/MapBuilder.h @@ -99,7 +99,7 @@ namespace MMAP // builds list of maps, then builds all of mmap tiles (based on the skip settings) void buildAllMaps(int threads); - void MapBuilder::WorkerThread(); + void WorkerThread(); private: // detect maps and tiles -- cgit v1.2.3 From fe1289ed1acaf856e1751966994d492c562d6ec2 Mon Sep 17 00:00:00 2001 From: jackpoz Date: Thu, 17 Jul 2014 19:55:55 +0200 Subject: Core/Misc: Fix abort() on shutdown on *nix Wait for cliThead to exit before deleting it to about std::abort() triggered on *nix systems. --- src/server/worldserver/Main.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/server/worldserver/Main.cpp') diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index a7b430a4e97..0c051eae37a 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -311,11 +311,8 @@ extern int main(int argc, char** argv) b[3].Event.KeyEvent.wRepeatCount = 1; DWORD numb; WriteConsoleInput(hStdIn, b, 4, &numb); - - cliThread->join(); - #endif - + cliThread->join(); delete cliThread; } -- cgit v1.2.3