mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-18 08:28:32 +01:00
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
This commit is contained in:
@@ -22,17 +22,30 @@
|
||||
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
|
||||
#include "Common.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "AsyncAcceptor.h"
|
||||
#include "RASession.h"
|
||||
#include "Configuration/Config.h"
|
||||
#include "OpenSSLCrypto.h"
|
||||
#include "ProcessPriority.h"
|
||||
#include "BigNumber.h"
|
||||
#include "RealmList.h"
|
||||
#include "World.h"
|
||||
#include "MapManager.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "WorldSocketMgr.h"
|
||||
#include "OutdoorPvP/OutdoorPvPMgr.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "TCSoap.h"
|
||||
#include "CliRunnable.h"
|
||||
#include "SystemConfig.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "Master.h"
|
||||
|
||||
#ifndef _TRINITY_CORE_CONFIG
|
||||
# define _TRINITY_CORE_CONFIG "worldserver.conf"
|
||||
#endif
|
||||
#define TRINITY_CORE_CONFIG "worldserver.conf"
|
||||
#define WORLD_SLEEP_CONST 50
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "ServiceWin32.h"
|
||||
@@ -48,31 +61,26 @@ char serviceDescription[] = "TrinityCore World of Warcraft emulator world servic
|
||||
int m_ServiceStatus = -1;
|
||||
#endif
|
||||
|
||||
boost::asio::io_service _ioService;
|
||||
WorldDatabaseWorkerPool WorldDatabase; ///< Accessor to the world database
|
||||
CharacterDatabaseWorkerPool CharacterDatabase; ///< Accessor to the character database
|
||||
LoginDatabaseWorkerPool LoginDatabase; ///< Accessor to the realm/login database
|
||||
|
||||
uint32 realmID; ///< Id of the realm
|
||||
|
||||
/// Print out the usage string for this program on the console.
|
||||
void usage(const char* prog)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" %s [<options>]\n", prog);
|
||||
printf(" -c config_file use config_file as configuration file\n");
|
||||
#ifdef _WIN32
|
||||
printf(" Running as service functions:\n");
|
||||
printf(" --service run as service\n");
|
||||
printf(" -s install install service\n");
|
||||
printf(" -s uninstall uninstall service\n");
|
||||
#endif
|
||||
}
|
||||
void usage(const char* prog);
|
||||
void SignalHandler(const boost::system::error_code& error, int signalNumber);
|
||||
void FreezeDetectorThread(uint32 delayTime);
|
||||
AsyncAcceptor<RASession>* StartRaSocketAcceptor(boost::asio::io_service& ioService);
|
||||
bool StartDB();
|
||||
void StopDB();
|
||||
void WorldUpdateLoop();
|
||||
void ClearOnlineAccounts();
|
||||
|
||||
/// Launch the Trinity server
|
||||
extern int main(int argc, char** argv)
|
||||
{
|
||||
///- Command line parsing to get the configuration file name
|
||||
char const* cfg_file = _TRINITY_CORE_CONFIG;
|
||||
char const* cfg_file = TRINITY_CORE_CONFIG;
|
||||
int c = 1;
|
||||
while (c < argc)
|
||||
{
|
||||
@@ -131,21 +139,442 @@ extern int main(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon)", _FULLVERSION);
|
||||
TC_LOG_INFO("server.worldserver", "<Ctrl-C> to stop.\n");
|
||||
TC_LOG_INFO("server.worldserver", " ______ __");
|
||||
TC_LOG_INFO("server.worldserver", "/\\__ _\\ __ __/\\ \\__");
|
||||
TC_LOG_INFO("server.worldserver", "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\, _\\ __ __");
|
||||
TC_LOG_INFO("server.worldserver", " \\ \\ \\/\\`'__\\/\\ \\ /' _ `\\/\\ \\ \\ \\/ /\\ \\/\\ \\");
|
||||
TC_LOG_INFO("server.worldserver", " \\ \\ \\ \\ \\/ \\ \\ \\/\\ \\/\\ \\ \\ \\ \\ \\_\\ \\ \\_\\ \\");
|
||||
TC_LOG_INFO("server.worldserver", " \\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\_\\ \\_\\ \\__\\\\/`____ \\");
|
||||
TC_LOG_INFO("server.worldserver", " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\");
|
||||
TC_LOG_INFO("server.worldserver", " C O R E /\\___/");
|
||||
TC_LOG_INFO("server.worldserver", "http://TrinityCore.org \\/__/\n");
|
||||
TC_LOG_INFO("server.worldserver", "Using configuration file %s.", cfg_file);
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||
TC_LOG_INFO("server.worldserver", "Using Boost version: %s", BOOST_LIB_VERSION);
|
||||
TC_LOG_INFO("server.worldserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
|
||||
|
||||
///- 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();
|
||||
OpenSSLCrypto::threadsSetup();
|
||||
BigNumber seed1;
|
||||
seed1.SetRand(16 * 8);
|
||||
|
||||
/// worldserver PID file creation
|
||||
std::string pidFile = sConfigMgr->GetStringDefault("PidFile", "");
|
||||
if (!pidFile.empty())
|
||||
{
|
||||
if (uint32 pid = CreatePIDFile(pidFile))
|
||||
TC_LOG_INFO("server.worldserver", "Daemon PID: %u\n", pid);
|
||||
else
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Cannot create PID file %s.\n", pidFile.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set signal handlers (this must be done before starting io_service threads, because otherwise they would unblock and exit)
|
||||
boost::asio::signal_set signals(_ioService, SIGINT, SIGTERM);
|
||||
signals.async_wait(SignalHandler);
|
||||
|
||||
// Start the Boost based thread pool
|
||||
int numThreads = sConfigMgr->GetIntDefault("ThreadPool", 1);
|
||||
std::vector<std::thread> threadPool;
|
||||
|
||||
if (numThreads < 1)
|
||||
numThreads = 1;
|
||||
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
threadPool.push_back(std::thread(boost::bind(&boost::asio::io_service::run, &_ioService)));
|
||||
|
||||
// Set process priority according to configuration settings
|
||||
SetProcessPriority("server.worldserver");
|
||||
|
||||
// Start the databases
|
||||
if (!StartDB())
|
||||
return 1;
|
||||
|
||||
// Set server offline (not connectable)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_INVALID, realmID);
|
||||
|
||||
// Initialize the World
|
||||
sWorld->SetInitialWorldSettings();
|
||||
|
||||
// Launch CliRunnable thread
|
||||
std::thread* cliThread = nullptr;
|
||||
#ifdef _WIN32
|
||||
if (sConfigMgr->GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
||||
#else
|
||||
if (sConfigMgr->GetBoolDefault("Console.Enable", true))
|
||||
#endif
|
||||
{
|
||||
cliThread = new std::thread(CliThread);
|
||||
}
|
||||
|
||||
// Start the Remote Access port (acceptor) if enabled
|
||||
AsyncAcceptor<RASession>* raAcceptor = nullptr;
|
||||
if (sConfigMgr->GetBoolDefault("Ra.Enable", false))
|
||||
raAcceptor = StartRaSocketAcceptor(_ioService);
|
||||
|
||||
// Start soap serving thread if enabled
|
||||
std::thread* soapThread = nullptr;
|
||||
if (sConfigMgr->GetBoolDefault("SOAP.Enabled", false))
|
||||
{
|
||||
soapThread = new std::thread(TCSoapThread, sConfigMgr->GetStringDefault("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetIntDefault("SOAP.Port", 7878)));
|
||||
}
|
||||
|
||||
// Start up freeze catcher thread
|
||||
std::thread* freezeDetectorThread = nullptr;
|
||||
if (uint32 freezeDelay = sConfigMgr->GetIntDefault("MaxCoreStuckTime", 0))
|
||||
freezeDetectorThread = new std::thread(FreezeDetectorThread, freezeDelay);
|
||||
|
||||
// Launch the worldserver listener socket
|
||||
uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));
|
||||
std::string bindIp = sConfigMgr->GetStringDefault("BindIP", "0.0.0.0");
|
||||
|
||||
if (sWorldSocketMgr->StartNetwork(worldPort, bindIp.c_str()) == -1)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Failed to start network");
|
||||
return ERROR_EXIT_CODE;
|
||||
}
|
||||
|
||||
// Set server online (allow connecting now)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_INVALID, realmID);
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", _FULLVERSION);
|
||||
|
||||
sScriptMgr->OnStartup();
|
||||
|
||||
WorldUpdateLoop();
|
||||
|
||||
// Shutdown starts here
|
||||
|
||||
_ioService.stop();
|
||||
|
||||
for (auto& thread : threadPool)
|
||||
{
|
||||
thread.join();
|
||||
}
|
||||
|
||||
sScriptMgr->OnShutdown();
|
||||
|
||||
sWorld->KickAll(); // save and kick all players
|
||||
sWorld->UpdateSessions(1); // real players unload required UpdateSessions call
|
||||
|
||||
// unload battleground templates before different singletons destroyed
|
||||
sBattlegroundMgr->DeleteAllBattlegrounds();
|
||||
|
||||
sWorldSocketMgr->StopNetwork();
|
||||
|
||||
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
|
||||
sObjectAccessor->UnloadAll(); // unload 'i_player2corpse' storage and remove from world
|
||||
sScriptMgr->Unload();
|
||||
sOutdoorPvPMgr->Die();
|
||||
|
||||
// set server offline
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realmID);
|
||||
|
||||
// Clean up threads if any
|
||||
if (soapThread != nullptr)
|
||||
{
|
||||
soapThread->join();
|
||||
delete soapThread;
|
||||
}
|
||||
|
||||
if (raAcceptor != nullptr)
|
||||
delete raAcceptor;
|
||||
|
||||
///- Clean database before leaving
|
||||
ClearOnlineAccounts();
|
||||
|
||||
StopDB();
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "Halting process...");
|
||||
|
||||
if (cliThread != nullptr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
// this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API)
|
||||
//_exit(1);
|
||||
// send keyboard input to safely unblock the CLI thread
|
||||
INPUT_RECORD b[4];
|
||||
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
b[0].EventType = KEY_EVENT;
|
||||
b[0].Event.KeyEvent.bKeyDown = TRUE;
|
||||
b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
|
||||
b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
|
||||
b[0].Event.KeyEvent.wRepeatCount = 1;
|
||||
|
||||
b[1].EventType = KEY_EVENT;
|
||||
b[1].Event.KeyEvent.bKeyDown = FALSE;
|
||||
b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
|
||||
b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
|
||||
b[1].Event.KeyEvent.wRepeatCount = 1;
|
||||
|
||||
b[2].EventType = KEY_EVENT;
|
||||
b[2].Event.KeyEvent.bKeyDown = TRUE;
|
||||
b[2].Event.KeyEvent.dwControlKeyState = 0;
|
||||
b[2].Event.KeyEvent.uChar.AsciiChar = '\r';
|
||||
b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||
b[2].Event.KeyEvent.wRepeatCount = 1;
|
||||
b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
|
||||
|
||||
b[3].EventType = KEY_EVENT;
|
||||
b[3].Event.KeyEvent.bKeyDown = FALSE;
|
||||
b[3].Event.KeyEvent.dwControlKeyState = 0;
|
||||
b[3].Event.KeyEvent.uChar.AsciiChar = '\r';
|
||||
b[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||
b[3].Event.KeyEvent.wVirtualScanCode = 0x1c;
|
||||
b[3].Event.KeyEvent.wRepeatCount = 1;
|
||||
DWORD numb;
|
||||
WriteConsoleInput(hStdIn, b, 4, &numb);
|
||||
|
||||
cliThread->join();
|
||||
|
||||
#endif
|
||||
|
||||
delete cliThread;
|
||||
}
|
||||
|
||||
delete freezeDetectorThread;
|
||||
|
||||
OpenSSLCrypto::threadsCleanup();
|
||||
|
||||
// at sMaster return function exist with codes
|
||||
// 0 - normal shutdown
|
||||
// 1 - shutdown at error
|
||||
// 2 - restart command used, this code can be used by restarter for restart Trinityd
|
||||
|
||||
return ret;
|
||||
return World::GetExitCode();
|
||||
}
|
||||
|
||||
|
||||
void WorldUpdateLoop()
|
||||
{
|
||||
uint32 realCurrTime = 0;
|
||||
uint32 realPrevTime = getMSTime();
|
||||
|
||||
uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST
|
||||
|
||||
///- While we have not World::m_stopEvent, update the world
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
++World::m_worldLoopCounter;
|
||||
realCurrTime = getMSTime();
|
||||
|
||||
uint32 diff = getMSTimeDiff(realPrevTime, realCurrTime);
|
||||
|
||||
sWorld->Update(diff);
|
||||
realPrevTime = realCurrTime;
|
||||
|
||||
// diff (D0) include time of previous sleep (d0) + tick time (t0)
|
||||
// we want that next d1 + t1 == WORLD_SLEEP_CONST
|
||||
// we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement
|
||||
// d1 = WORLD_SLEEP_CONST - t0 = WORLD_SLEEP_CONST - (D0 - d0) = WORLD_SLEEP_CONST + d0 - D0
|
||||
if (diff <= WORLD_SLEEP_CONST + prevSleepTime)
|
||||
{
|
||||
prevSleepTime = WORLD_SLEEP_CONST + prevSleepTime - diff;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(prevSleepTime));
|
||||
}
|
||||
else
|
||||
prevSleepTime = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_ServiceStatus == 0)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
|
||||
while (m_ServiceStatus == 2)
|
||||
Sleep(1000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// Print out the usage string for this program on the console.
|
||||
void usage(const char* prog)
|
||||
{
|
||||
printf("Usage:\n");
|
||||
printf(" %s [<options>]\n", prog);
|
||||
printf(" -c config_file use config_file as configuration file\n");
|
||||
#ifdef _WIN32
|
||||
printf(" Running as service functions:\n");
|
||||
printf(" --service run as service\n");
|
||||
printf(" -s install install service\n");
|
||||
printf(" -s uninstall uninstall service\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void SignalHandler(const boost::system::error_code& error, int signalNumber)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
switch (signalNumber)
|
||||
{
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FreezeDetectorThread(uint32 delayTime)
|
||||
{
|
||||
if (!delayTime)
|
||||
return;
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", delayTime / 1000);
|
||||
uint32 loops = 0;
|
||||
uint32 lastChange = 0;
|
||||
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
uint32 curtime = getMSTime();
|
||||
// normal work
|
||||
uint32 worldLoopCounter = World::m_worldLoopCounter;
|
||||
if (loops != worldLoopCounter)
|
||||
{
|
||||
lastChange = curtime;
|
||||
loops = worldLoopCounter;
|
||||
}
|
||||
// possible freeze
|
||||
else if (getMSTimeDiff(lastChange, curtime) > delayTime)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
|
||||
ASSERT(false);
|
||||
}
|
||||
}
|
||||
TC_LOG_INFO("server.worldserver", "Anti-freeze thread exiting without problems.");
|
||||
}
|
||||
|
||||
AsyncAcceptor<RASession>* StartRaSocketAcceptor(boost::asio::io_service& ioService)
|
||||
{
|
||||
uint16 raPort = uint16(sConfigMgr->GetIntDefault("Ra.Port", 3443));
|
||||
std::string raListener = sConfigMgr->GetStringDefault("Ra.IP", "0.0.0.0");
|
||||
|
||||
return new AsyncAcceptor<RASession>(ioService, raListener, raPort);
|
||||
}
|
||||
|
||||
/// Initialize connection to the databases
|
||||
bool StartDB()
|
||||
{
|
||||
MySQL::Library_Init();
|
||||
|
||||
std::string dbString;
|
||||
uint8 asyncThreads, synchThreads;
|
||||
|
||||
dbString = sConfigMgr->GetStringDefault("WorldDatabaseInfo", "");
|
||||
if (dbString.empty())
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "World database not specified in configuration file");
|
||||
return false;
|
||||
}
|
||||
|
||||
asyncThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.WorkerThreads", 1));
|
||||
if (asyncThreads < 1 || asyncThreads > 32)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "World database: invalid number of worker threads specified. "
|
||||
"Please pick a value between 1 and 32.");
|
||||
return false;
|
||||
}
|
||||
|
||||
synchThreads = uint8(sConfigMgr->GetIntDefault("WorldDatabase.SynchThreads", 1));
|
||||
///- Initialize the world database
|
||||
if (!WorldDatabase.Open(dbString, asyncThreads, synchThreads))
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Cannot connect to world database %s", dbString.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
///- Get character database info from configuration file
|
||||
dbString = sConfigMgr->GetStringDefault("CharacterDatabaseInfo", "");
|
||||
if (dbString.empty())
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Character database not specified in configuration file");
|
||||
return false;
|
||||
}
|
||||
|
||||
asyncThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.WorkerThreads", 1));
|
||||
if (asyncThreads < 1 || asyncThreads > 32)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Character database: invalid number of worker threads specified. "
|
||||
"Please pick a value between 1 and 32.");
|
||||
return false;
|
||||
}
|
||||
|
||||
synchThreads = uint8(sConfigMgr->GetIntDefault("CharacterDatabase.SynchThreads", 2));
|
||||
|
||||
///- Initialize the Character database
|
||||
if (!CharacterDatabase.Open(dbString, asyncThreads, synchThreads))
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Cannot connect to Character database %s", dbString.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
///- Get login database info from configuration file
|
||||
dbString = sConfigMgr->GetStringDefault("LoginDatabaseInfo", "");
|
||||
if (dbString.empty())
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Login database not specified in configuration file");
|
||||
return false;
|
||||
}
|
||||
|
||||
asyncThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.WorkerThreads", 1));
|
||||
if (asyncThreads < 1 || asyncThreads > 32)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Login database: invalid number of worker threads specified. "
|
||||
"Please pick a value between 1 and 32.");
|
||||
return false;
|
||||
}
|
||||
|
||||
synchThreads = uint8(sConfigMgr->GetIntDefault("LoginDatabase.SynchThreads", 1));
|
||||
///- Initialise the login database
|
||||
if (!LoginDatabase.Open(dbString, asyncThreads, synchThreads))
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Cannot connect to login database %s", dbString.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
///- Get the realm Id from the configuration file
|
||||
realmID = sConfigMgr->GetIntDefault("RealmID", 0);
|
||||
if (!realmID)
|
||||
{
|
||||
TC_LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
|
||||
return false;
|
||||
}
|
||||
TC_LOG_INFO("server.worldserver", "Realm running as realm ID %d", realmID);
|
||||
|
||||
///- Clean the database before starting
|
||||
ClearOnlineAccounts();
|
||||
|
||||
///- Insert version info into DB
|
||||
WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", _FULLVERSION, _HASH); // One-time query
|
||||
|
||||
sWorld->LoadDBVersion();
|
||||
|
||||
TC_LOG_INFO("server.worldserver", "Using World DB: %s", sWorld->GetDBVersion());
|
||||
return true;
|
||||
}
|
||||
|
||||
void StopDB()
|
||||
{
|
||||
CharacterDatabase.Close();
|
||||
WorldDatabase.Close();
|
||||
LoginDatabase.Close();
|
||||
|
||||
MySQL::Library_End();
|
||||
}
|
||||
|
||||
/// Clear 'online' status for all accounts with characters in this realm
|
||||
void ClearOnlineAccounts()
|
||||
{
|
||||
// Reset online status for all accounts with characters on the current realm
|
||||
LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online > 0 AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = %d)", realmID);
|
||||
|
||||
// Reset online status for all characters
|
||||
CharacterDatabase.DirectExecute("UPDATE characters SET online = 0 WHERE online <> 0");
|
||||
|
||||
// Battleground instance ids reset at server restart
|
||||
CharacterDatabase.DirectExecute("UPDATE character_battleground_data SET instanceId = 0");
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
Reference in New Issue
Block a user