mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-30 21:57:01 +01:00
Core can now be built in dynamic mode, enabling the hotswap system to be used. Hotswap works fine on Windows. Didnt work for me on Debian, the rebuild and reload process happens but the old version of the module remains for some reason. Cant figure it out right now
637 lines
22 KiB
C++
637 lines
22 KiB
C++
/*
|
|
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/// \addtogroup Trinityd Trinity Daemon
|
|
/// @{
|
|
/// \file
|
|
|
|
#include "Common.h"
|
|
#include "Commands.h"
|
|
#include "ZmqContext.h"
|
|
#include "DatabaseEnv.h"
|
|
#include "AsyncAcceptor.h"
|
|
#include "RASession.h"
|
|
#include "Configuration/Config.h"
|
|
#include "OpenSSLCrypto.h"
|
|
#include "ProcessPriority.h"
|
|
#include "BigNumber.h"
|
|
#include "World.h"
|
|
#include "MapManager.h"
|
|
#include "InstanceSaveMgr.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "ScriptMgr.h"
|
|
#include "ScriptReloadMgr.h"
|
|
#include "ScriptLoader.h"
|
|
#include "OutdoorPvP/OutdoorPvPMgr.h"
|
|
#include "BattlegroundMgr.h"
|
|
#include "TCSoap.h"
|
|
#include "CliRunnable.h"
|
|
#include "GitRevision.h"
|
|
#include "WorldSocket.h"
|
|
#include "WorldSocketMgr.h"
|
|
#include "BattlenetServerManager.h"
|
|
#include "DatabaseLoader.h"
|
|
#include "AppenderDB.h"
|
|
#include <openssl/opensslv.h>
|
|
#include <openssl/crypto.h>
|
|
#include <boost/asio/io_service.hpp>
|
|
#include <boost/asio/deadline_timer.hpp>
|
|
#include <boost/filesystem/path.hpp>
|
|
#include <boost/program_options.hpp>
|
|
|
|
using namespace boost::program_options;
|
|
namespace fs = boost::filesystem;
|
|
|
|
#ifndef _TRINITY_CORE_CONFIG
|
|
#define _TRINITY_CORE_CONFIG "worldserver.conf"
|
|
#endif
|
|
|
|
#define WORLD_SLEEP_CONST 50
|
|
|
|
#ifdef _WIN32
|
|
#include "ServiceWin32.h"
|
|
char serviceName[] = "worldserver";
|
|
char serviceLongName[] = "TrinityCore world service";
|
|
char serviceDescription[] = "TrinityCore World of Warcraft emulator world service";
|
|
/*
|
|
* -1 - not in service mode
|
|
* 0 - stopped
|
|
* 1 - running
|
|
* 2 - paused
|
|
*/
|
|
int m_ServiceStatus = -1;
|
|
#endif
|
|
|
|
boost::asio::io_service _ioService;
|
|
boost::asio::deadline_timer _freezeCheckTimer(_ioService);
|
|
uint32 _worldLoopCounter(0);
|
|
uint32 _lastChangeMsTime(0);
|
|
uint32 _maxCoreStuckTimeInMs(0);
|
|
|
|
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);
|
|
bool StartDB();
|
|
void StopDB();
|
|
void WorldUpdateLoop();
|
|
void ClearOnlineAccounts();
|
|
void ShutdownCLIThread(std::thread* cliThread);
|
|
void ShutdownThreadPool(std::vector<std::thread>& threadPool);
|
|
bool LoadRealmInfo();
|
|
variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& cfg_service);
|
|
|
|
/// Launch the Trinity server
|
|
extern int main(int argc, char** argv)
|
|
{
|
|
signal(SIGABRT, &Trinity::AbortHandler);
|
|
|
|
auto configFile = fs::absolute(_TRINITY_CORE_CONFIG);
|
|
std::string configService;
|
|
|
|
auto vm = GetConsoleArguments(argc, argv, configFile, configService);
|
|
// exit if help or version is enabled
|
|
if (vm.count("help") || vm.count("version"))
|
|
return 0;
|
|
|
|
#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
|
|
|
|
std::string configError;
|
|
if (!sConfigMgr->LoadInitial(configFile.generic_string(),
|
|
std::vector<std::string>(argv, argv + argc),
|
|
configError))
|
|
{
|
|
printf("Error in config file: %s\n", configError.c_str());
|
|
return 1;
|
|
}
|
|
|
|
sLog->RegisterAppender<AppenderDB>();
|
|
// If logs are supposed to be handled async then we need to pass the io_service into the Log singleton
|
|
sLog->Initialize(sConfigMgr->GetBoolDefault("Log.Async.Enable", false) ? &_ioService : nullptr);
|
|
|
|
TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon)", GitRevision::GetFullVersion());
|
|
TC_LOG_INFO("server.worldserver", "<Ctrl-C> to stop.\n");
|
|
TC_LOG_INFO("server.worldserver", " ______ __");
|
|
TC_LOG_INFO("server.worldserver", "/\\__ _\\ __ __/\\ \\__");
|
|
TC_LOG_INFO("server.worldserver", "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\, _\\ __ __");
|
|
TC_LOG_INFO("server.worldserver", " \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\");
|
|
TC_LOG_INFO("server.worldserver", " \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\");
|
|
TC_LOG_INFO("server.worldserver", " \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\");
|
|
TC_LOG_INFO("server.worldserver", " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\");
|
|
TC_LOG_INFO("server.worldserver", " C O R E /\\___/");
|
|
TC_LOG_INFO("server.worldserver", "http://TrinityCore.org \\/__/\n");
|
|
TC_LOG_INFO("server.worldserver", "Using configuration file %s.", sConfigMgr->GetFilename().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);
|
|
|
|
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())
|
|
{
|
|
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);
|
|
#if PLATFORM == PLATFORM_WINDOWS
|
|
signals.add(SIGBREAK);
|
|
#endif
|
|
signals.async_wait(SignalHandler);
|
|
|
|
// Start the Boost based thread pool
|
|
int numThreads = sConfigMgr->GetIntDefault("ThreadPool", 1);
|
|
std::vector<std::thread> threadPool;
|
|
|
|
if (numThreads < 1)
|
|
numThreads = 1;
|
|
|
|
for (int i = 0; i < numThreads; ++i)
|
|
threadPool.push_back(std::thread(boost::bind(&boost::asio::io_service::run, &_ioService)));
|
|
|
|
// Set process priority according to configuration settings
|
|
SetProcessPriority("server.worldserver");
|
|
|
|
// Start the databases
|
|
if (!StartDB())
|
|
{
|
|
ShutdownThreadPool(threadPool);
|
|
return 1;
|
|
}
|
|
|
|
// Set server offline (not connectable)
|
|
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
|
|
|
LoadRealmInfo();
|
|
|
|
// Initialize the World
|
|
sScriptMgr->SetScriptLoader(AddScripts);
|
|
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)));
|
|
}
|
|
|
|
// Launch the worldserver listener socket
|
|
uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));
|
|
std::string worldListener = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
|
|
|
|
int networkThreads = sConfigMgr->GetIntDefault("Network.Threads", 1);
|
|
|
|
if (networkThreads <= 0)
|
|
{
|
|
TC_LOG_ERROR("server.worldserver", "Network.Threads must be greater than 0");
|
|
return false;
|
|
}
|
|
|
|
sWorldSocketMgr.StartNetwork(_ioService, worldListener, worldPort, networkThreads);
|
|
|
|
// Set server online (allow connecting now)
|
|
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
|
realm.PopulationLevel = 0.0f;
|
|
realm.Flags = RealmFlags(realm.Flags & ~uint32(REALM_FLAG_OFFLINE));
|
|
|
|
// Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec)
|
|
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)...", coreStuckTime);
|
|
}
|
|
|
|
sIpcContext->Initialize();
|
|
TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", GitRevision::GetFullVersion());
|
|
|
|
sBattlenetServer.InitializeConnection();
|
|
|
|
|
|
sScriptMgr->OnStartup();
|
|
|
|
WorldUpdateLoop();
|
|
|
|
// Shutdown starts here
|
|
ShutdownThreadPool(threadPool);
|
|
|
|
sLog->SetSynchronous();
|
|
|
|
sScriptMgr->OnShutdown();
|
|
|
|
sIpcContext->Close();
|
|
|
|
sBattlenetServer.CloseConnection();
|
|
|
|
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();
|
|
|
|
sInstanceSaveMgr->Unload();
|
|
sOutdoorPvPMgr->Die();
|
|
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
|
|
sScriptMgr->Unload();
|
|
sScriptReloadMgr->Unload();
|
|
|
|
// set server offline
|
|
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
|
|
|
// Clean up threads if any
|
|
if (soapThread != nullptr)
|
|
{
|
|
soapThread->join();
|
|
delete soapThread;
|
|
}
|
|
|
|
delete raAcceptor;
|
|
|
|
///- Clean database before leaving
|
|
ClearOnlineAccounts();
|
|
|
|
StopDB();
|
|
|
|
TC_LOG_INFO("server.worldserver", "Halting process...");
|
|
|
|
ShutdownCLIThread(cliThread);
|
|
|
|
OpenSSLCrypto::threadsCleanup();
|
|
|
|
// 0 - normal shutdown
|
|
// 1 - shutdown at error
|
|
// 2 - restart command used, this code can be used by restarter for restart Trinityd
|
|
|
|
return World::GetExitCode();
|
|
}
|
|
|
|
void ShutdownCLIThread(std::thread* cliThread)
|
|
{
|
|
if (cliThread != nullptr)
|
|
{
|
|
#ifdef _WIN32
|
|
// First try to cancel any I/O in the CLI thread
|
|
if (!CancelSynchronousIo(cliThread->native_handle()))
|
|
{
|
|
// if CancelSynchronousIo() fails, print the error and try with old way
|
|
DWORD errorCode = GetLastError();
|
|
LPSTR errorBuffer;
|
|
|
|
DWORD formatReturnCode = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
nullptr, errorCode, 0, (LPTSTR)&errorBuffer, 0, nullptr);
|
|
if (!formatReturnCode)
|
|
errorBuffer = "Unknown error";
|
|
|
|
TC_LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code %u, detail: %s",
|
|
uint32(errorCode), errorBuffer);
|
|
LocalFree(errorBuffer);
|
|
|
|
// 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);
|
|
}
|
|
#endif
|
|
cliThread->join();
|
|
delete cliThread;
|
|
}
|
|
}
|
|
|
|
void ShutdownThreadPool(std::vector<std::thread>& threadPool)
|
|
{
|
|
sScriptMgr->OnNetworkStop();
|
|
|
|
_ioService.stop();
|
|
|
|
for (auto& thread : threadPool)
|
|
{
|
|
thread.join();
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
void SignalHandler(const boost::system::error_code& error, int /*signalNumber*/)
|
|
{
|
|
if (!error)
|
|
World::StopNow(SHUTDOWN_EXIT_CODE);
|
|
}
|
|
|
|
void FreezeDetectorHandler(const boost::system::error_code& error)
|
|
{
|
|
if (!error)
|
|
{
|
|
uint32 curtime = getMSTime();
|
|
|
|
uint32 worldLoopCounter = World::m_worldLoopCounter;
|
|
if (_worldLoopCounter != worldLoopCounter)
|
|
{
|
|
_lastChangeMsTime = curtime;
|
|
_worldLoopCounter = worldLoopCounter;
|
|
}
|
|
// possible freeze
|
|
else if (getMSTimeDiff(_lastChangeMsTime, curtime) > _maxCoreStuckTimeInMs)
|
|
{
|
|
TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
|
|
ABORT();
|
|
}
|
|
|
|
_freezeCheckTimer.expires_from_now(boost::posix_time::seconds(1));
|
|
_freezeCheckTimer.async_wait(FreezeDetectorHandler);
|
|
}
|
|
}
|
|
|
|
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");
|
|
|
|
AsyncAcceptor* acceptor = new AsyncAcceptor(ioService, raListener, raPort);
|
|
acceptor->AsyncAccept<RASession>();
|
|
return acceptor;
|
|
}
|
|
|
|
bool LoadRealmInfo()
|
|
{
|
|
boost::asio::ip::tcp::resolver resolver(_ioService);
|
|
boost::asio::ip::tcp::resolver::iterator end;
|
|
|
|
QueryResult result = LoginDatabase.PQuery("SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE id = %u", realm.Id.Realm);
|
|
if (!result)
|
|
return false;
|
|
|
|
Field* fields = result->Fetch();
|
|
realm.Name = fields[1].GetString();
|
|
boost::asio::ip::tcp::resolver::query externalAddressQuery(ip::tcp::v4(), fields[2].GetString(), "");
|
|
|
|
boost::system::error_code ec;
|
|
boost::asio::ip::tcp::resolver::iterator endPoint = resolver.resolve(externalAddressQuery, ec);
|
|
if (endPoint == end || ec)
|
|
{
|
|
TC_LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[2].GetString().c_str());
|
|
return false;
|
|
}
|
|
|
|
realm.ExternalAddress = (*endPoint).endpoint().address();
|
|
|
|
boost::asio::ip::tcp::resolver::query localAddressQuery(ip::tcp::v4(), fields[3].GetString(), "");
|
|
endPoint = resolver.resolve(localAddressQuery, ec);
|
|
if (endPoint == end || ec)
|
|
{
|
|
TC_LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[3].GetString().c_str());
|
|
return false;
|
|
}
|
|
|
|
realm.LocalAddress = (*endPoint).endpoint().address();
|
|
|
|
boost::asio::ip::tcp::resolver::query localSubmaskQuery(ip::tcp::v4(), fields[4].GetString(), "");
|
|
endPoint = resolver.resolve(localSubmaskQuery, ec);
|
|
if (endPoint == end || ec)
|
|
{
|
|
TC_LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[4].GetString().c_str());
|
|
return false;
|
|
}
|
|
|
|
realm.LocalSubnetMask = (*endPoint).endpoint().address();
|
|
|
|
realm.Port = fields[5].GetUInt16();
|
|
realm.Type = fields[6].GetUInt8();
|
|
realm.Flags = RealmFlags(fields[7].GetUInt8());
|
|
realm.Timezone = fields[8].GetUInt8();
|
|
realm.AllowedSecurityLevel = AccountTypes(fields[9].GetUInt8());
|
|
realm.PopulationLevel = fields[10].GetFloat();
|
|
realm.Build = fields[11].GetUInt32();
|
|
return true;
|
|
}
|
|
|
|
/// Initialize connection to the databases
|
|
bool StartDB()
|
|
{
|
|
MySQL::Library_Init();
|
|
|
|
// Load databases
|
|
DatabaseLoader loader("server.worldserver", DatabaseLoader::DATABASE_NONE);
|
|
loader
|
|
.AddDatabase(LoginDatabase, "Login")
|
|
.AddDatabase(CharacterDatabase, "Character")
|
|
.AddDatabase(WorldDatabase, "World");
|
|
|
|
if (!loader.Load())
|
|
return false;
|
|
|
|
///- Get the realm Id from the configuration file
|
|
realm.Id.Realm = sConfigMgr->GetIntDefault("RealmID", 0);
|
|
if (!realm.Id.Realm)
|
|
{
|
|
TC_LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
|
|
return false;
|
|
}
|
|
|
|
QueryResult realmIdQuery = LoginDatabase.PQuery("SELECT `Region`,`Battlegroup` FROM `realmlist` WHERE `id`=%u", realm.Id.Realm);
|
|
if (!realmIdQuery)
|
|
{
|
|
TC_LOG_ERROR("server.worldserver", "Realm id %u not defined in realmlist table", realm.Id.Realm);
|
|
return false;
|
|
}
|
|
|
|
realm.Id.Region = (*realmIdQuery)[0].GetUInt8();
|
|
realm.Id.Site = (*realmIdQuery)[1].GetUInt8();
|
|
|
|
TC_LOG_INFO("server.worldserver", "Realm running as realm ID %u region %u battlegroup %u", realm.Id.Realm, uint32(realm.Id.Region), uint32(realm.Id.Site));
|
|
|
|
///- Clean the database before starting
|
|
ClearOnlineAccounts();
|
|
|
|
///- Insert version info into DB
|
|
WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", GitRevision::GetFullVersion(), GitRevision::GetHash()); // 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)", realm.Id.Realm);
|
|
|
|
// 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");
|
|
}
|
|
|
|
/// @}
|
|
|
|
variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService)
|
|
{
|
|
// Silences warning about configService not be used if the OS is not Windows
|
|
(void)configService;
|
|
|
|
options_description all("Allowed options");
|
|
all.add_options()
|
|
("help,h", "print usage message")
|
|
("version,v", "print version build info")
|
|
("config,c", value<fs::path>(&configFile)->default_value(fs::absolute(_TRINITY_CORE_CONFIG)),
|
|
"use <arg> as configuration file")
|
|
;
|
|
#ifdef _WIN32
|
|
options_description win("Windows platform specific options");
|
|
win.add_options()
|
|
("service,s", value<std::string>(&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";
|
|
}
|
|
else if (vm.count("version"))
|
|
{
|
|
std::cout << GitRevision::GetFullVersion() << "\n";
|
|
}
|
|
|
|
return vm;
|
|
}
|