mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-16 10:30:27 +00:00
feat(Core): replace ACE network with Boost.Asio (#6574)
This commit is contained in:
@@ -4,24 +4,49 @@
|
||||
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
||||
*/
|
||||
|
||||
/// \addtogroup Trinityd Trinity Daemon
|
||||
/// @{
|
||||
/// \file
|
||||
|
||||
#include "ACSoap.h"
|
||||
#include "AppenderDB.h"
|
||||
#include "AsyncAcceptor.h"
|
||||
#include "AsyncAuctionListing.h"
|
||||
#include "AvgDiffTracker.h"
|
||||
#include "Banner.h"
|
||||
#include "BattlegroundMgr.h"
|
||||
#include "BigNumber.h"
|
||||
#include "CliRunnable.h"
|
||||
#include "Common.h"
|
||||
#include "Config.h"
|
||||
#include "Configuration/Config.h"
|
||||
#include "Database/DatabaseEnv.h"
|
||||
#include "Log.h"
|
||||
#include "Master.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "DeadlineTimer.h"
|
||||
#include "GitRevision.h"
|
||||
#include "IoContext.h"
|
||||
#include "MapManager.h"
|
||||
#include "MySQLThreading.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "OpenSSLCrypto.h"
|
||||
#include "OutdoorPvPMgr.h"
|
||||
#include "ProcessPriority.h"
|
||||
#include "RASession.h"
|
||||
#include "RealmList.h"
|
||||
#include "Resolver.h"
|
||||
#include "ScriptLoader.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SecretMgr.h"
|
||||
#include "SharedDefines.h"
|
||||
#include <ace/Version.h>
|
||||
#include <boost/version.hpp>
|
||||
#include "World.h"
|
||||
#include "WorldSocket.h"
|
||||
#include "WorldSocketMgr.h"
|
||||
#include <boost/asio/signal_set.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <csignal>
|
||||
#include <iostream>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
#ifndef _ACORE_CORE_CONFIG
|
||||
#define _ACORE_CORE_CONFIG "worldserver.conf"
|
||||
#ifdef ELUNA
|
||||
#include "LuaEngine.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -38,6 +63,44 @@ char serviceDescription[] = "AzerothCore World of Warcraft emulator world servic
|
||||
int m_ServiceStatus = -1;
|
||||
#endif
|
||||
|
||||
#ifndef _ACORE_CORE_CONFIG
|
||||
#define _ACORE_CORE_CONFIG "worldserver.conf"
|
||||
#endif
|
||||
|
||||
#define WORLD_SLEEP_CONST 10
|
||||
|
||||
class FreezeDetector
|
||||
{
|
||||
public:
|
||||
FreezeDetector(Acore::Asio::IoContext& ioContext, uint32 maxCoreStuckTime)
|
||||
: _timer(ioContext), _worldLoopCounter(0), _lastChangeMsTime(getMSTime()), _maxCoreStuckTimeInMs(maxCoreStuckTime) { }
|
||||
|
||||
static void Start(std::shared_ptr<FreezeDetector> const& freezeDetector)
|
||||
{
|
||||
freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(5));
|
||||
freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, std::weak_ptr<FreezeDetector>(freezeDetector), std::placeholders::_1));
|
||||
}
|
||||
|
||||
static void Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error);
|
||||
|
||||
private:
|
||||
Acore::Asio::DeadlineTimer _timer;
|
||||
uint32 _worldLoopCounter;
|
||||
uint32 _lastChangeMsTime;
|
||||
uint32 _maxCoreStuckTimeInMs;
|
||||
};
|
||||
|
||||
void SignalHandler(boost::system::error_code const& error, int signalNumber);
|
||||
void ClearOnlineAccounts();
|
||||
bool StartDB();
|
||||
void StopDB();
|
||||
bool LoadRealmInfo(Acore::Asio::IoContext& ioContext);
|
||||
AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext);
|
||||
void ShutdownCLIThread(std::thread* cliThread);
|
||||
void AuctionListingRunnable();
|
||||
void ShutdownAuctionListingThread(std::thread* thread);
|
||||
void WorldUpdateLoop();
|
||||
|
||||
/// Print out the usage string for this program on the console.
|
||||
void usage(const char* prog)
|
||||
{
|
||||
@@ -52,10 +115,11 @@ void usage(const char* prog)
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Launch the Trinity server
|
||||
extern int main(int argc, char** argv)
|
||||
/// Launch the Azeroth server
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Acore::Impl::CurrentServerProcessHolder::_type = SERVER_PROCESS_WORLDSERVER;
|
||||
signal(SIGABRT, &Acore::AbortHandler);
|
||||
|
||||
///- Command line parsing to get the configuration file name
|
||||
std::string configFile = sConfigMgr->GetConfigPath() + std::string(_ACORE_CORE_CONFIG);
|
||||
@@ -124,6 +188,9 @@ extern int main(int argc, char** argv)
|
||||
// Loading modules configs
|
||||
sConfigMgr->LoadModulesConfigs();
|
||||
|
||||
std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
|
||||
|
||||
// Init all logs
|
||||
sLog->RegisterAppender<AppenderDB>();
|
||||
sLog->Initialize();
|
||||
|
||||
@@ -134,23 +201,541 @@ extern int main(int argc, char** argv)
|
||||
},
|
||||
[]()
|
||||
{
|
||||
LOG_INFO("server.worldserver", "> Using configuration file %s.", sConfigMgr->GetFilename().c_str());
|
||||
LOG_INFO("server.worldserver", "> Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||
LOG_INFO("server.worldserver", "> Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
|
||||
LOG_INFO("server.worldserver", "> Using ACE version: %s\n", ACE_VERSION);
|
||||
LOG_INFO("server.worldserver", "> Using configuration file: %s", sConfigMgr->GetFilename().c_str());
|
||||
LOG_INFO("server.worldserver", "> Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
|
||||
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();
|
||||
|
||||
std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
|
||||
|
||||
// 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->GetOption<std::string>("PidFile", "");
|
||||
if (!pidFile.empty())
|
||||
{
|
||||
if (uint32 pid = CreatePIDFile(pidFile))
|
||||
LOG_ERROR("server", "Daemon PID: %u\n", pid); // outError for red color in console
|
||||
else
|
||||
{
|
||||
LOG_ERROR("server", "Cannot create PID file %s (possible error: permission)\n", pidFile.c_str());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set signal handlers (this must be done before starting IoContext threads, because otherwise they would unblock and exit)
|
||||
boost::asio::signal_set signals(*ioContext, SIGINT, SIGTERM);
|
||||
#if AC_PLATFORM == AC_PLATFORM_WINDOWS
|
||||
signals.add(SIGBREAK);
|
||||
#endif
|
||||
signals.async_wait(SignalHandler);
|
||||
|
||||
// Start the Boost based thread pool
|
||||
int numThreads = sConfigMgr->GetOption<int32>("ThreadPool", 1);
|
||||
std::shared_ptr<std::vector<std::thread>> threadPool(new std::vector<std::thread>(), [ioContext](std::vector<std::thread>* del)
|
||||
{
|
||||
ioContext->stop();
|
||||
for (std::thread& thr : *del)
|
||||
thr.join();
|
||||
|
||||
delete del;
|
||||
});
|
||||
|
||||
if (numThreads < 1)
|
||||
{
|
||||
numThreads = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
threadPool->push_back(std::thread([ioContext]()
|
||||
{
|
||||
ioContext->run();
|
||||
}));
|
||||
}
|
||||
|
||||
// Set process priority according to configuration settings
|
||||
SetProcessPriority("server.worldserver", sConfigMgr->GetOption<int32>(CONFIG_PROCESSOR_AFFINITY, 0), sConfigMgr->GetOption<bool>(CONFIG_HIGH_PRIORITY, false));
|
||||
|
||||
// Start the databases
|
||||
if (!StartDB())
|
||||
return 1;
|
||||
|
||||
std::shared_ptr<void> dbHandle(nullptr, [](void*) { StopDB(); });
|
||||
|
||||
// set server offline (not connectable)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = (flag & ~%u) | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
|
||||
|
||||
LoadRealmInfo(*ioContext);
|
||||
|
||||
// Loading modules configs
|
||||
sConfigMgr->PrintLoadedModulesConfigs();
|
||||
|
||||
sScriptMgr->SetScriptLoader(AddScripts);
|
||||
std::shared_ptr<void> sScriptMgrHandle(nullptr, [](void*)
|
||||
{
|
||||
sScriptMgr->Unload();
|
||||
//sScriptReloadMgr->Unload();
|
||||
});
|
||||
|
||||
///- Initialize the World
|
||||
sSecretMgr->Initialize();
|
||||
sWorld->SetInitialWorldSettings();
|
||||
|
||||
std::shared_ptr<void> mapManagementHandle(nullptr, [](void*)
|
||||
{
|
||||
// unload battleground templates before different singletons destroyed
|
||||
sBattlegroundMgr->DeleteAllBattlegrounds();
|
||||
|
||||
sOutdoorPvPMgr->Die(); // unload it before MapManager
|
||||
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
|
||||
|
||||
#ifdef ELUNA
|
||||
Eluna::Uninitialize();
|
||||
#endif
|
||||
});
|
||||
|
||||
// Start the Remote Access port (acceptor) if enabled
|
||||
std::unique_ptr<AsyncAcceptor> raAcceptor;
|
||||
if (sConfigMgr->GetOption<bool>("Ra.Enable", false))
|
||||
{
|
||||
raAcceptor.reset(StartRaSocketAcceptor(*ioContext));
|
||||
}
|
||||
|
||||
// Start soap serving thread if enabled
|
||||
std::shared_ptr<std::thread> soapThread;
|
||||
if (sConfigMgr->GetOption<bool>("SOAP.Enabled", false))
|
||||
{
|
||||
soapThread.reset(new std::thread(ACSoapThread, sConfigMgr->GetOption<std::string>("SOAP.IP", "127.0.0.1"), uint16(sConfigMgr->GetOption<int32>("SOAP.Port", 7878))),
|
||||
[](std::thread* thr)
|
||||
{
|
||||
thr->join();
|
||||
delete thr;
|
||||
});
|
||||
}
|
||||
|
||||
// Launch the worldserver listener socket
|
||||
uint16 worldPort = uint16(sWorld->getIntConfig(CONFIG_PORT_WORLD));
|
||||
std::string worldListener = sConfigMgr->GetOption<std::string>("BindIP", "0.0.0.0");
|
||||
|
||||
int networkThreads = sConfigMgr->GetOption<int32>("Network.Threads", 1);
|
||||
|
||||
if (networkThreads <= 0)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Network.Threads must be greater than 0");
|
||||
World::StopNow(ERROR_EXIT_CODE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!sWorldSocketMgr.StartWorldNetwork(*ioContext, worldListener, worldPort, networkThreads))
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Failed to initialize network");
|
||||
World::StopNow(ERROR_EXIT_CODE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> sWorldSocketMgrHandle(nullptr, [](void*)
|
||||
{
|
||||
sWorld->KickAll(); // save and kick all players
|
||||
sWorld->UpdateSessions(1); // real players unload required UpdateSessions call
|
||||
|
||||
sWorldSocketMgr.StopNetwork();
|
||||
|
||||
///- Clean database before leaving
|
||||
ClearOnlineAccounts();
|
||||
});
|
||||
|
||||
// Set server online (allow connecting now)
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag & ~%u, population = 0 WHERE id = '%u'", REALM_FLAG_VERSION_MISMATCH, realm.Id.Realm);
|
||||
realm.PopulationLevel = 0.0f;
|
||||
realm.Flags = RealmFlags(realm.Flags & ~uint32(REALM_FLAG_VERSION_MISMATCH));
|
||||
|
||||
// Start the freeze check callback cycle in 5 seconds (cycle itself is 1 sec)
|
||||
std::shared_ptr<FreezeDetector> freezeDetector;
|
||||
if (int32 coreStuckTime = sConfigMgr->GetOption<int32>("MaxCoreStuckTime", 60))
|
||||
{
|
||||
freezeDetector = std::make_shared<FreezeDetector>(*ioContext, coreStuckTime * 1000);
|
||||
FreezeDetector::Start(freezeDetector);
|
||||
LOG_INFO("server.worldserver", "Starting up anti-freeze thread (%u seconds max stuck time)...", coreStuckTime);
|
||||
}
|
||||
|
||||
LOG_INFO("server.worldserver", "%s (worldserver-daemon) ready...", GitRevision::GetFullVersion());
|
||||
|
||||
sScriptMgr->OnStartup();
|
||||
|
||||
// Launch CliRunnable thread
|
||||
std::shared_ptr<std::thread> cliThread;
|
||||
#ifdef _WIN32
|
||||
if (sConfigMgr->GetOption<bool>("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
|
||||
#else
|
||||
if (sConfigMgr->GetOption<bool>("Console.Enable", true))
|
||||
#endif
|
||||
{
|
||||
cliThread.reset(new std::thread(CliThread), &ShutdownCLIThread);
|
||||
}
|
||||
|
||||
// Launch CliRunnable thread
|
||||
std::shared_ptr<std::thread> auctionLisingThread;
|
||||
auctionLisingThread.reset(new std::thread(AuctionListingRunnable),
|
||||
[](std::thread* thr)
|
||||
{
|
||||
thr->join();
|
||||
delete thr;
|
||||
});
|
||||
|
||||
WorldUpdateLoop();
|
||||
|
||||
// Shutdown starts here
|
||||
threadPool.reset();
|
||||
|
||||
sScriptMgr->OnShutdown();
|
||||
|
||||
// set server offline
|
||||
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
|
||||
|
||||
LOG_INFO("server.worldserver", "Halting process...");
|
||||
|
||||
// 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
|
||||
// 2 - restart command used, this code can be used by restarter for restart Warheadd
|
||||
|
||||
return ret;
|
||||
return World::GetExitCode();
|
||||
}
|
||||
|
||||
/// @}
|
||||
/// Initialize connection to the databases
|
||||
bool StartDB()
|
||||
{
|
||||
MySQL::Library_Init();
|
||||
|
||||
// Load databases
|
||||
DatabaseLoader loader("server.worldserver");
|
||||
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)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Realm ID not defined in configuration file");
|
||||
return false;
|
||||
}
|
||||
else if (realm.Id.Realm > 255)
|
||||
{
|
||||
/*
|
||||
* Due to the client only being able to read a realm.Id.Realm
|
||||
* with a size of uint8 we can "only" store up to 255 realms
|
||||
* anything further the client will behave anormaly
|
||||
*/
|
||||
LOG_ERROR("server.worldserver", "Realm ID must range from 1 to 255");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO("server.loading", "Loading world information...");
|
||||
LOG_INFO("server.loading", "> RealmID: %u", realm.Id.Realm);
|
||||
|
||||
///- 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();
|
||||
sWorld->LoadDBRevision();
|
||||
|
||||
LOG_INFO("server.loading", "> Version DB world: %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
|
||||
// pussywizard: tc query would set online=0 even if logged in on another realm >_>
|
||||
LoginDatabase.DirectPExecute("UPDATE account SET online = 0 WHERE online = %u", realm.Id.Realm);
|
||||
|
||||
// Reset online status for all characters
|
||||
CharacterDatabase.DirectExecute("UPDATE characters SET online = 0 WHERE online <> 0");
|
||||
}
|
||||
|
||||
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();
|
||||
LPCSTR 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";
|
||||
|
||||
LOG_DEBUG("server.worldserver", "Error cancelling I/O of CliThread, error code %u, detail: %s", uint32(errorCode), errorBuffer);
|
||||
|
||||
if (!formatReturnCode)
|
||||
LocalFree((LPSTR)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 WorldUpdateLoop()
|
||||
{
|
||||
uint32 realCurrTime = 0;
|
||||
uint32 realPrevTime = getMSTime();
|
||||
|
||||
LoginDatabase.WarnAboutSyncQueries(true);
|
||||
CharacterDatabase.WarnAboutSyncQueries(true);
|
||||
WorldDatabase.WarnAboutSyncQueries(true);
|
||||
|
||||
///- 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;
|
||||
|
||||
uint32 executionTimeDiff = getMSTimeDiff(realCurrTime, getMSTime());
|
||||
devDiffTracker.Update(executionTimeDiff);
|
||||
avgDiffTracker.Update(executionTimeDiff > WORLD_SLEEP_CONST ? executionTimeDiff : WORLD_SLEEP_CONST);
|
||||
|
||||
// we know exactly how long it took to update the world, if the update took less than WORLD_SLEEP_CONST, sleep for WORLD_SLEEP_CONST - world update time
|
||||
if (executionTimeDiff < WORLD_SLEEP_CONST)
|
||||
{
|
||||
std::this_thread::sleep_for(Milliseconds(WORLD_SLEEP_CONST - executionTimeDiff));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (m_ServiceStatus == 0)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
|
||||
while (m_ServiceStatus == 2)
|
||||
Sleep(1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
LoginDatabase.WarnAboutSyncQueries(false);
|
||||
CharacterDatabase.WarnAboutSyncQueries(false);
|
||||
WorldDatabase.WarnAboutSyncQueries(false);
|
||||
}
|
||||
|
||||
void SignalHandler(boost::system::error_code const& error, int /*signalNumber*/)
|
||||
{
|
||||
if (!error)
|
||||
World::StopNow(SHUTDOWN_EXIT_CODE);
|
||||
}
|
||||
|
||||
void FreezeDetector::Handler(std::weak_ptr<FreezeDetector> freezeDetectorRef, boost::system::error_code const& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
if (std::shared_ptr<FreezeDetector> freezeDetector = freezeDetectorRef.lock())
|
||||
{
|
||||
uint32 curtime = getMSTime();
|
||||
|
||||
uint32 worldLoopCounter = World::m_worldLoopCounter;
|
||||
if (freezeDetector->_worldLoopCounter != worldLoopCounter)
|
||||
{
|
||||
freezeDetector->_lastChangeMsTime = curtime;
|
||||
freezeDetector->_worldLoopCounter = worldLoopCounter;
|
||||
}
|
||||
// possible freeze
|
||||
else if (getMSTimeDiff(freezeDetector->_lastChangeMsTime, curtime) > freezeDetector->_maxCoreStuckTimeInMs)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "World Thread hangs, kicking out server!");
|
||||
ABORT();
|
||||
}
|
||||
|
||||
freezeDetector->_timer.expires_from_now(boost::posix_time::seconds(1));
|
||||
freezeDetector->_timer.async_wait(std::bind(&FreezeDetector::Handler, freezeDetectorRef, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsyncAcceptor* StartRaSocketAcceptor(Acore::Asio::IoContext& ioContext)
|
||||
{
|
||||
uint16 raPort = uint16(sConfigMgr->GetOption<int32>("Ra.Port", 3443));
|
||||
std::string raListener = sConfigMgr->GetOption<std::string>("Ra.IP", "0.0.0.0");
|
||||
|
||||
AsyncAcceptor* acceptor = new AsyncAcceptor(ioContext, raListener, raPort);
|
||||
if (!acceptor->Bind())
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Failed to bind RA socket acceptor");
|
||||
delete acceptor;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
acceptor->AsyncAccept<RASession>();
|
||||
return acceptor;
|
||||
}
|
||||
|
||||
bool LoadRealmInfo(Acore::Asio::IoContext& ioContext)
|
||||
{
|
||||
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;
|
||||
|
||||
Acore::Asio::Resolver resolver(ioContext);
|
||||
|
||||
Field* fields = result->Fetch();
|
||||
realm.Name = fields[1].GetString();
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> externalAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[2].GetString(), "");
|
||||
if (!externalAddress)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[2].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
realm.ExternalAddress = std::make_unique<boost::asio::ip::address>(externalAddress->address());
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> localAddress = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[3].GetString(), "");
|
||||
if (!localAddress)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[3].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
realm.LocalAddress = std::make_unique<boost::asio::ip::address>(localAddress->address());
|
||||
|
||||
Optional<boost::asio::ip::tcp::endpoint> localSubmask = resolver.Resolve(boost::asio::ip::tcp::v4(), fields[4].GetString(), "");
|
||||
if (!localSubmask)
|
||||
{
|
||||
LOG_ERROR("server.worldserver", "Could not resolve address %s", fields[4].GetString().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
realm.LocalSubnetMask = std::make_unique<boost::asio::ip::address>(localSubmask->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;
|
||||
}
|
||||
|
||||
void AuctionListingRunnable()
|
||||
{
|
||||
LOG_INFO("server", "Starting up Auction House Listing thread...");
|
||||
|
||||
while (!World::IsStopped())
|
||||
{
|
||||
if (AsyncAuctionListingMgr::IsAuctionListingAllowed())
|
||||
{
|
||||
uint32 diff = AsyncAuctionListingMgr::GetDiff();
|
||||
AsyncAuctionListingMgr::ResetDiff();
|
||||
|
||||
if (AsyncAuctionListingMgr::GetTempList().size() || AsyncAuctionListingMgr::GetList().size())
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetLock());
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(AsyncAuctionListingMgr::GetTempLock());
|
||||
|
||||
for (auto const& delayEvent : AsyncAuctionListingMgr::GetTempList())
|
||||
AsyncAuctionListingMgr::GetList().emplace_back(delayEvent);
|
||||
|
||||
AsyncAuctionListingMgr::GetTempList().clear();
|
||||
}
|
||||
|
||||
for (auto& itr : AsyncAuctionListingMgr::GetList())
|
||||
{
|
||||
if (itr._msTimer <= diff)
|
||||
itr._msTimer = 0;
|
||||
else
|
||||
itr._msTimer -= diff;
|
||||
}
|
||||
|
||||
for (std::list<AuctionListItemsDelayEvent>::iterator itr = AsyncAuctionListingMgr::GetList().begin(); itr != AsyncAuctionListingMgr::GetList().end(); ++itr)
|
||||
{
|
||||
if ((*itr)._msTimer != 0)
|
||||
continue;
|
||||
|
||||
if ((*itr).Execute())
|
||||
AsyncAuctionListingMgr::GetList().erase(itr);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
LOG_INFO("server", "Auction House Listing thread exiting without problems.");
|
||||
}
|
||||
|
||||
void ShutdownAuctionListingThread(std::thread* thread)
|
||||
{
|
||||
if (thread != nullptr)
|
||||
{
|
||||
thread->join();
|
||||
delete thread;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user