Fixed and improved restarting
Restarts are now an actual, close-as-possible to application exit+reopen.
This commit is contained in:
parent
b5ed23d2a6
commit
4315a11393
@ -17,6 +17,7 @@ echo "Building..."
|
|||||||
make -j 2;
|
make -j 2;
|
||||||
make -j 2 test ARGS="-V";
|
make -j 2 test ARGS="-V";
|
||||||
cd MCServer/;
|
cd MCServer/;
|
||||||
if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ]
|
if [ "$TRAVIS_MCSERVER_BUILD_TYPE" != "COVERAGE" ]; then
|
||||||
then echo stop | $MCSERVER_PATH;
|
echo restart | $MCSERVER_PATH;
|
||||||
|
echo stop | $MCSERVER_PATH;
|
||||||
fi
|
fi
|
||||||
|
@ -18,8 +18,36 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
cNetworkSingleton::cNetworkSingleton(void):
|
cNetworkSingleton::cNetworkSingleton() :
|
||||||
m_HasTerminated(false)
|
m_HasTerminated(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cNetworkSingleton::~cNetworkSingleton()
|
||||||
|
{
|
||||||
|
// Check that Terminate has been called already:
|
||||||
|
ASSERT(m_HasTerminated);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cNetworkSingleton & cNetworkSingleton::Get(void)
|
||||||
|
{
|
||||||
|
static cNetworkSingleton Instance;
|
||||||
|
return Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cNetworkSingleton::Initialise(void)
|
||||||
{
|
{
|
||||||
// Windows: initialize networking:
|
// Windows: initialize networking:
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -64,26 +92,7 @@ cNetworkSingleton::cNetworkSingleton(void):
|
|||||||
|
|
||||||
// Create the event loop thread:
|
// Create the event loop thread:
|
||||||
m_EventLoopThread = std::thread(RunEventLoop, this);
|
m_EventLoopThread = std::thread(RunEventLoop, this);
|
||||||
}
|
m_HasTerminated = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cNetworkSingleton::~cNetworkSingleton()
|
|
||||||
{
|
|
||||||
// Check that Terminate has been called already:
|
|
||||||
ASSERT(m_HasTerminated);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cNetworkSingleton & cNetworkSingleton::Get(void)
|
|
||||||
{
|
|
||||||
static cNetworkSingleton Instance;
|
|
||||||
return Instance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,13 +44,18 @@ typedef std::vector<cIPLookupPtr> cIPLookupPtrs;
|
|||||||
class cNetworkSingleton
|
class cNetworkSingleton
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
cNetworkSingleton();
|
||||||
~cNetworkSingleton();
|
~cNetworkSingleton();
|
||||||
|
|
||||||
/** Returns the singleton instance of this class */
|
/** Returns the singleton instance of this class */
|
||||||
static cNetworkSingleton & Get(void);
|
static cNetworkSingleton & Get(void);
|
||||||
|
|
||||||
|
/** Initialises all network-related threads.
|
||||||
|
To be called on first run or after app restart. */
|
||||||
|
void Initialise(void);
|
||||||
|
|
||||||
/** Terminates all network-related threads.
|
/** Terminates all network-related threads.
|
||||||
To be used only on app shutdown.
|
To be used only on app shutdown or restart.
|
||||||
MSVC runtime requires that the LibEvent networking be shut down before the main() function is exitted; this is the way to do it. */
|
MSVC runtime requires that the LibEvent networking be shut down before the main() function is exitted; this is the way to do it. */
|
||||||
void Terminate(void);
|
void Terminate(void);
|
||||||
|
|
||||||
@ -122,10 +127,6 @@ protected:
|
|||||||
/** The thread in which the main LibEvent loop runs. */
|
/** The thread in which the main LibEvent loop runs. */
|
||||||
std::thread m_EventLoopThread;
|
std::thread m_EventLoopThread;
|
||||||
|
|
||||||
|
|
||||||
/** Initializes the LibEvent internals. */
|
|
||||||
cNetworkSingleton(void);
|
|
||||||
|
|
||||||
/** Converts LibEvent-generated log events into log messages in MCS log. */
|
/** Converts LibEvent-generated log events into log messages in MCS log. */
|
||||||
static void LogCallback(int a_Severity, const char * a_Msg);
|
static void LogCallback(int a_Severity, const char * a_Msg);
|
||||||
|
|
||||||
|
319
src/Root.cpp
319
src/Root.cpp
@ -26,10 +26,10 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <conio.h>
|
|
||||||
#include <psapi.h>
|
#include <psapi.h>
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <signal.h>
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#endif
|
#endif
|
||||||
@ -39,7 +39,6 @@
|
|||||||
|
|
||||||
|
|
||||||
cRoot * cRoot::s_Root = nullptr;
|
cRoot * cRoot::s_Root = nullptr;
|
||||||
bool cRoot::m_ShouldStop = false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -53,10 +52,10 @@ cRoot::cRoot(void) :
|
|||||||
m_FurnaceRecipe(nullptr),
|
m_FurnaceRecipe(nullptr),
|
||||||
m_WebAdmin(nullptr),
|
m_WebAdmin(nullptr),
|
||||||
m_PluginManager(nullptr),
|
m_PluginManager(nullptr),
|
||||||
m_MojangAPI(nullptr),
|
m_MojangAPI(nullptr)
|
||||||
m_bRestart(false)
|
|
||||||
{
|
{
|
||||||
s_Root = this;
|
s_Root = this;
|
||||||
|
m_InputThreadRunFlag.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -76,24 +75,22 @@ void cRoot::InputThread(cRoot & a_Params)
|
|||||||
{
|
{
|
||||||
cLogCommandOutputCallback Output;
|
cLogCommandOutputCallback Output;
|
||||||
|
|
||||||
while (!cRoot::m_ShouldStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good())
|
while (a_Params.m_InputThreadRunFlag.test_and_set() && std::cin.good())
|
||||||
{
|
{
|
||||||
AString Command;
|
AString Command;
|
||||||
std::getline(std::cin, Command);
|
std::getline(std::cin, Command);
|
||||||
if (!Command.empty())
|
if (!Command.empty())
|
||||||
{
|
{
|
||||||
|
// Execute and clear command string when submitted
|
||||||
a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
|
a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_TerminateEventRaised || !std::cin.good())
|
// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running
|
||||||
|
if (!std::cin.good())
|
||||||
{
|
{
|
||||||
// We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running
|
|
||||||
// Stop the server:
|
// Stop the server:
|
||||||
if (!m_RunAsService) // Dont kill if running as a service
|
a_Params.QueueExecuteConsoleCommand("stop");
|
||||||
{
|
|
||||||
a_Params.m_ShouldStop = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,12 +98,12 @@ void cRoot::InputThread(cRoot & a_Params)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
|
void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
HWND hwnd = GetConsoleWindow();
|
HWND hwnd = GetConsoleWindow();
|
||||||
HMENU hmenu = GetSystemMenu(hwnd, FALSE);
|
HMENU hmenu = GetSystemMenu(hwnd, FALSE);
|
||||||
EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling
|
EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cLogger::cListener * consoleLogListener = MakeConsoleListener(m_RunAsService);
|
cLogger::cListener * consoleLogListener = MakeConsoleListener(m_RunAsService);
|
||||||
@ -127,156 +124,197 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
cDeadlockDetect dd;
|
cDeadlockDetect dd;
|
||||||
|
auto BeginTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
m_ShouldStop = false;
|
LoadGlobalSettings();
|
||||||
while (!m_ShouldStop)
|
|
||||||
|
LOG("Creating new server instance...");
|
||||||
|
m_Server = new cServer();
|
||||||
|
|
||||||
|
LOG("Reading server config...");
|
||||||
|
|
||||||
|
auto IniFile = cpp14::make_unique<cIniFile>();
|
||||||
|
if (!IniFile->ReadFile("settings.ini"))
|
||||||
{
|
{
|
||||||
auto BeginTime = std::chrono::steady_clock::now();
|
LOGWARN("Regenerating settings.ini, all settings will be reset");
|
||||||
m_bRestart = false;
|
IniFile->AddHeaderComment(" This is the main server configuration");
|
||||||
|
IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
|
||||||
LoadGlobalSettings();
|
IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
|
||||||
|
}
|
||||||
LOG("Creating new server instance...");
|
auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(a_OverridesRepo));
|
||||||
m_Server = new cServer();
|
|
||||||
|
|
||||||
LOG("Reading server config...");
|
|
||||||
|
|
||||||
auto IniFile = cpp14::make_unique<cIniFile>();
|
|
||||||
if (!IniFile->ReadFile("settings.ini"))
|
|
||||||
{
|
|
||||||
LOGWARN("Regenerating settings.ini, all settings will be reset");
|
|
||||||
IniFile->AddHeaderComment(" This is the main server configuration");
|
|
||||||
IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
|
|
||||||
IniFile->AddHeaderComment(" See: http://wiki.mc-server.org/doku.php?id=configure:settings.ini for further configuration help");
|
|
||||||
}
|
|
||||||
auto settingsRepo = cpp14::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(overridesRepo));
|
|
||||||
|
|
||||||
LOG("Starting server...");
|
|
||||||
m_MojangAPI = new cMojangAPI;
|
|
||||||
bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true);
|
|
||||||
m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
|
|
||||||
if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate))
|
|
||||||
{
|
|
||||||
settingsRepo->Flush();
|
|
||||||
LOGERROR("Failure starting server, aborting...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_WebAdmin = new cWebAdmin();
|
|
||||||
m_WebAdmin->Init();
|
|
||||||
|
|
||||||
LOGD("Loading settings...");
|
|
||||||
m_RankManager.reset(new cRankManager());
|
|
||||||
m_RankManager->Initialize(*m_MojangAPI);
|
|
||||||
m_CraftingRecipes = new cCraftingRecipes;
|
|
||||||
m_FurnaceRecipe = new cFurnaceRecipe();
|
|
||||||
|
|
||||||
LOGD("Loading worlds...");
|
|
||||||
LoadWorlds(*settingsRepo);
|
|
||||||
|
|
||||||
LOGD("Loading plugin manager...");
|
|
||||||
m_PluginManager = new cPluginManager();
|
|
||||||
m_PluginManager->ReloadPluginsNow(*settingsRepo);
|
|
||||||
|
|
||||||
LOGD("Loading MonsterConfig...");
|
|
||||||
m_MonsterConfig = new cMonsterConfig;
|
|
||||||
|
|
||||||
// This sets stuff in motion
|
|
||||||
LOGD("Starting Authenticator...");
|
|
||||||
m_Authenticator.Start(*settingsRepo);
|
|
||||||
|
|
||||||
LOGD("Starting worlds...");
|
|
||||||
StartWorlds();
|
|
||||||
|
|
||||||
if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true))
|
|
||||||
{
|
|
||||||
LOGD("Starting deadlock detector...");
|
|
||||||
dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
LOG("Starting server...");
|
||||||
|
m_MojangAPI = new cMojangAPI;
|
||||||
|
bool ShouldAuthenticate = settingsRepo->GetValueSetB("Authentication", "Authenticate", true);
|
||||||
|
m_MojangAPI->Start(*settingsRepo, ShouldAuthenticate); // Mojang API needs to be started before plugins, so that plugins may use it for DB upgrades on server init
|
||||||
|
if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate))
|
||||||
|
{
|
||||||
settingsRepo->Flush();
|
settingsRepo->Flush();
|
||||||
|
LOGERROR("Failure starting server, aborting...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LOGD("Finalising startup...");
|
m_WebAdmin = new cWebAdmin();
|
||||||
if (m_Server->Start())
|
m_WebAdmin->Init();
|
||||||
{
|
|
||||||
m_WebAdmin->Start();
|
|
||||||
|
|
||||||
#if !defined(ANDROID_NDK)
|
LOGD("Loading settings...");
|
||||||
|
m_RankManager.reset(new cRankManager());
|
||||||
|
m_RankManager->Initialize(*m_MojangAPI);
|
||||||
|
m_CraftingRecipes = new cCraftingRecipes();
|
||||||
|
m_FurnaceRecipe = new cFurnaceRecipe();
|
||||||
|
|
||||||
|
LOGD("Loading worlds...");
|
||||||
|
LoadWorlds(*settingsRepo);
|
||||||
|
|
||||||
|
LOGD("Loading plugin manager...");
|
||||||
|
m_PluginManager = new cPluginManager();
|
||||||
|
m_PluginManager->ReloadPluginsNow(*settingsRepo);
|
||||||
|
|
||||||
|
LOGD("Loading MonsterConfig...");
|
||||||
|
m_MonsterConfig = new cMonsterConfig;
|
||||||
|
|
||||||
|
// This sets stuff in motion
|
||||||
|
LOGD("Starting Authenticator...");
|
||||||
|
m_Authenticator.Start(*settingsRepo);
|
||||||
|
|
||||||
|
LOGD("Starting worlds...");
|
||||||
|
StartWorlds();
|
||||||
|
|
||||||
|
if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true))
|
||||||
|
{
|
||||||
|
LOGD("Starting deadlock detector...");
|
||||||
|
dd.Start(settingsRepo->GetValueSetI("DeadlockDetect", "IntervalSec", 20));
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsRepo->Flush();
|
||||||
|
|
||||||
|
LOGD("Finalising startup...");
|
||||||
|
if (m_Server->Start())
|
||||||
|
{
|
||||||
|
m_WebAdmin->Start();
|
||||||
|
|
||||||
|
#if !defined(ANDROID_NDK)
|
||||||
LOGD("Starting InputThread...");
|
LOGD("Starting InputThread...");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
m_InputThreadRunFlag.test_and_set();
|
||||||
m_InputThread = std::thread(InputThread, std::ref(*this));
|
m_InputThread = std::thread(InputThread, std::ref(*this));
|
||||||
m_InputThread.detach();
|
|
||||||
}
|
}
|
||||||
catch (std::system_error & a_Exception)
|
catch (std::system_error & a_Exception)
|
||||||
{
|
{
|
||||||
LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what());
|
LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
|
LOG("Startup complete, took %ldms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count()));
|
||||||
|
|
||||||
// Save the current time
|
// Save the current time
|
||||||
m_StartTime = std::chrono::steady_clock::now();
|
m_StartTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
|
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (!m_ShouldStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads
|
for (;;)
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_TerminateEventRaised)
|
|
||||||
{
|
|
||||||
m_ShouldStop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop the server:
|
|
||||||
m_WebAdmin->Stop();
|
|
||||||
|
|
||||||
LOG("Shutting down server...");
|
|
||||||
m_Server->Shutdown();
|
|
||||||
} // if (m_Server->Start())
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
m_ShouldStop = true;
|
m_StopEvent.Wait();
|
||||||
|
|
||||||
|
if (m_TerminateEventRaised && m_RunAsService)
|
||||||
|
{
|
||||||
|
// Dont kill if running as a service
|
||||||
|
m_TerminateEventRaised = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delete m_MojangAPI; m_MojangAPI = nullptr;
|
// Stop the server:
|
||||||
|
m_WebAdmin->Stop();
|
||||||
|
|
||||||
LOGD("Shutting down deadlock detector...");
|
LOG("Shutting down server...");
|
||||||
dd.Stop();
|
m_Server->Shutdown();
|
||||||
|
} // if (m_Server->Start()
|
||||||
|
|
||||||
LOGD("Stopping world threads...");
|
delete m_MojangAPI; m_MojangAPI = nullptr;
|
||||||
StopWorlds();
|
|
||||||
|
|
||||||
LOGD("Stopping authenticator...");
|
LOGD("Shutting down deadlock detector...");
|
||||||
m_Authenticator.Stop();
|
dd.Stop();
|
||||||
|
|
||||||
LOGD("Freeing MonsterConfig...");
|
LOGD("Stopping world threads...");
|
||||||
delete m_MonsterConfig; m_MonsterConfig = nullptr;
|
StopWorlds();
|
||||||
delete m_WebAdmin; m_WebAdmin = nullptr;
|
|
||||||
|
|
||||||
LOGD("Unloading recipes...");
|
LOGD("Stopping authenticator...");
|
||||||
delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr;
|
m_Authenticator.Stop();
|
||||||
delete m_CraftingRecipes; m_CraftingRecipes = nullptr;
|
|
||||||
|
|
||||||
LOG("Unloading worlds...");
|
LOGD("Freeing MonsterConfig...");
|
||||||
UnloadWorlds();
|
delete m_MonsterConfig; m_MonsterConfig = nullptr;
|
||||||
|
delete m_WebAdmin; m_WebAdmin = nullptr;
|
||||||
|
|
||||||
LOGD("Stopping plugin manager...");
|
LOGD("Unloading recipes...");
|
||||||
delete m_PluginManager; m_PluginManager = nullptr;
|
delete m_FurnaceRecipe; m_FurnaceRecipe = nullptr;
|
||||||
|
delete m_CraftingRecipes; m_CraftingRecipes = nullptr;
|
||||||
|
|
||||||
cItemHandler::Deinit();
|
LOG("Unloading worlds...");
|
||||||
|
UnloadWorlds();
|
||||||
|
|
||||||
LOG("Cleaning up...");
|
LOGD("Stopping plugin manager...");
|
||||||
delete m_Server; m_Server = nullptr;
|
delete m_PluginManager; m_PluginManager = nullptr;
|
||||||
|
|
||||||
|
cItemHandler::Deinit();
|
||||||
|
|
||||||
|
LOG("Cleaning up...");
|
||||||
|
delete m_Server; m_Server = nullptr;
|
||||||
|
|
||||||
|
m_InputThreadRunFlag.clear();
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD Length;
|
||||||
|
INPUT_RECORD Record
|
||||||
|
{
|
||||||
|
static_cast<WORD>(KEY_EVENT),
|
||||||
|
{
|
||||||
|
{
|
||||||
|
TRUE,
|
||||||
|
1,
|
||||||
|
VK_RETURN,
|
||||||
|
MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC),
|
||||||
|
{ { VK_RETURN } },
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Can't kill the input thread since it breaks cin (getline doesn't block / receive input on restart)
|
||||||
|
// Apparently no way to unblock getline
|
||||||
|
// Only thing I can think of for now
|
||||||
|
if (WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &Record, 1, &Length) == 0)
|
||||||
|
{
|
||||||
|
LOGWARN("Couldn't notify the input thread; the server will hang before shutdown!");
|
||||||
|
m_TerminateEventRaised = true;
|
||||||
|
m_InputThread.detach();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_InputThread.join();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (pthread_kill(m_InputThread.native_handle(), SIGKILL) != 0)
|
||||||
|
{
|
||||||
|
LOGWARN("Couldn't notify the input thread; the server will hang before shutdown!");
|
||||||
|
m_TerminateEventRaised = true;
|
||||||
|
m_InputThread.detach();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (m_TerminateEventRaised)
|
||||||
|
{
|
||||||
LOG("Shutdown successful!");
|
LOG("Shutdown successful!");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG("Shutdown successful - restarting...");
|
||||||
|
}
|
||||||
LOG("--- Stopped Log ---");
|
LOG("--- Stopped Log ---");
|
||||||
|
|
||||||
cLogger::GetInstance().DetachListener(consoleLogListener);
|
cLogger::GetInstance().DetachListener(consoleLogListener);
|
||||||
@ -475,19 +513,9 @@ void cRoot::TickCommands(void)
|
|||||||
|
|
||||||
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
|
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
|
||||||
{
|
{
|
||||||
// Some commands are built-in:
|
|
||||||
if (a_Cmd == "stop")
|
|
||||||
{
|
|
||||||
m_ShouldStop = true;
|
|
||||||
}
|
|
||||||
else if (a_Cmd == "restart")
|
|
||||||
{
|
|
||||||
m_bRestart = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put the command into a queue (Alleviates FS #363):
|
// Put the command into a queue (Alleviates FS #363):
|
||||||
cCSLock Lock(m_CSPendingCommands);
|
cCSLock Lock(m_CSPendingCommands);
|
||||||
m_PendingCommands.push_back(cCommand(a_Cmd, &a_Output));
|
m_PendingCommands.emplace_back(a_Cmd, &a_Output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -507,15 +535,18 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
|
|||||||
|
|
||||||
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
|
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
|
||||||
{
|
{
|
||||||
// cRoot handles stopping and restarting due to our access to controlling variables
|
// Some commands are built-in:
|
||||||
if (a_Cmd == "stop")
|
if (a_Cmd == "stop")
|
||||||
{
|
{
|
||||||
m_ShouldStop = true;
|
m_TerminateEventRaised = true;
|
||||||
|
m_StopEvent.Set();
|
||||||
|
m_InputThreadRunFlag.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (a_Cmd == "restart")
|
else if (a_Cmd == "restart")
|
||||||
{
|
{
|
||||||
m_bRestart = true;
|
m_StopEvent.Set();
|
||||||
|
m_InputThreadRunFlag.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
src/Root.h
10
src/Root.h
@ -7,6 +7,7 @@
|
|||||||
#include "Defines.h"
|
#include "Defines.h"
|
||||||
#include "RankManager.h"
|
#include "RankManager.h"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -48,13 +49,12 @@ public:
|
|||||||
|
|
||||||
static bool m_TerminateEventRaised;
|
static bool m_TerminateEventRaised;
|
||||||
static bool m_RunAsService;
|
static bool m_RunAsService;
|
||||||
static bool m_ShouldStop;
|
|
||||||
|
|
||||||
|
|
||||||
cRoot(void);
|
cRoot(void);
|
||||||
~cRoot();
|
~cRoot();
|
||||||
|
|
||||||
void Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo);
|
void Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo);
|
||||||
|
|
||||||
// tolua_begin
|
// tolua_begin
|
||||||
cServer * GetServer(void) { return m_Server; }
|
cServer * GetServer(void) { return m_Server; }
|
||||||
@ -200,6 +200,8 @@ private:
|
|||||||
cCommandQueue m_PendingCommands;
|
cCommandQueue m_PendingCommands;
|
||||||
|
|
||||||
std::thread m_InputThread;
|
std::thread m_InputThread;
|
||||||
|
cEvent m_StopEvent;
|
||||||
|
std::atomic_flag m_InputThreadRunFlag;
|
||||||
|
|
||||||
cServer * m_Server;
|
cServer * m_Server;
|
||||||
cMonsterConfig * m_MonsterConfig;
|
cMonsterConfig * m_MonsterConfig;
|
||||||
@ -213,9 +215,7 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<cRankManager> m_RankManager;
|
std::unique_ptr<cRankManager> m_RankManager;
|
||||||
|
|
||||||
cHTTPServer m_HTTPServer;
|
cHTTPServer m_HTTPServer;
|
||||||
|
|
||||||
bool m_bRestart;
|
|
||||||
|
|
||||||
|
|
||||||
void LoadGlobalSettings();
|
void LoadGlobalSettings();
|
||||||
|
65
src/main.cpp
65
src/main.cpp
@ -22,9 +22,6 @@
|
|||||||
/** If something has told the server to stop; checked periodically in cRoot */
|
/** If something has told the server to stop; checked periodically in cRoot */
|
||||||
bool cRoot::m_TerminateEventRaised = false;
|
bool cRoot::m_TerminateEventRaised = false;
|
||||||
|
|
||||||
/** Set to true when the server terminates, so our CTRL handler can then tell the OS to close the console. */
|
|
||||||
static bool g_ServerTerminated = false;
|
|
||||||
|
|
||||||
/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
|
/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
|
||||||
bool g_ShouldLogCommIn;
|
bool g_ShouldLogCommIn;
|
||||||
|
|
||||||
@ -72,7 +69,7 @@ Synchronize this with Server.cpp to enable the "dumpmem" console command. */
|
|||||||
void NonCtrlHandler(int a_Signal)
|
void NonCtrlHandler(int a_Signal)
|
||||||
{
|
{
|
||||||
LOGD("Terminate event raised from std::signal");
|
LOGD("Terminate event raised from std::signal");
|
||||||
cRoot::m_TerminateEventRaised = true;
|
cRoot::Get()->QueueExecuteConsoleCommand("stop");
|
||||||
|
|
||||||
switch (a_Signal)
|
switch (a_Signal)
|
||||||
{
|
{
|
||||||
@ -189,13 +186,11 @@ LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_Except
|
|||||||
// Handle CTRL events in windows, including console window close
|
// Handle CTRL events in windows, including console window close
|
||||||
BOOL CtrlHandler(DWORD fdwCtrlType)
|
BOOL CtrlHandler(DWORD fdwCtrlType)
|
||||||
{
|
{
|
||||||
cRoot::m_TerminateEventRaised = true;
|
cRoot::Get()->QueueExecuteConsoleCommand("stop");
|
||||||
LOGD("Terminate event raised from the Windows CtrlHandler");
|
LOGD("Terminate event raised from the Windows CtrlHandler");
|
||||||
|
|
||||||
while (!g_ServerTerminated)
|
std::this_thread::sleep_for(std::chrono::seconds(10)); // Delay as much as possible to try to get the server to shut down cleanly - 10 seconds given by Windows
|
||||||
{
|
// Returning from main() automatically aborts this handler thread
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Delay as much as possible to try to get the server to shut down cleanly
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -206,29 +201,22 @@ BOOL CtrlHandler(DWORD fdwCtrlType)
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// universalMain - Main startup logic for both standard running and as a service
|
// UniversalMain - Main startup logic for both standard running and as a service
|
||||||
|
|
||||||
void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
|
void UniversalMain(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
|
||||||
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
|
|
||||||
{
|
|
||||||
LOGERROR("Could not install the Windows CTRL handler!");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Initialize logging subsystem:
|
// Initialize logging subsystem:
|
||||||
cLogger::InitiateMultithreading();
|
cLogger::InitiateMultithreading();
|
||||||
|
|
||||||
// Initialize LibEvent:
|
// Initialize LibEvent:
|
||||||
cNetworkSingleton::Get();
|
cNetworkSingleton::Get().Initialise();
|
||||||
|
|
||||||
#if !defined(ANDROID_NDK)
|
#if !defined(ANDROID_NDK)
|
||||||
try
|
try
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
cRoot Root;
|
cRoot Root;
|
||||||
Root.Start(std::move(overridesRepo));
|
Root.Start(std::move(a_OverridesRepo));
|
||||||
}
|
}
|
||||||
#if !defined(ANDROID_NDK)
|
#if !defined(ANDROID_NDK)
|
||||||
catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
@ -241,8 +229,6 @@ void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
g_ServerTerminated = true;
|
|
||||||
|
|
||||||
// Shutdown all of LibEvent:
|
// Shutdown all of LibEvent:
|
||||||
cNetworkSingleton::Get().Terminate();
|
cNetworkSingleton::Get().Terminate();
|
||||||
}
|
}
|
||||||
@ -259,8 +245,11 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
|
|||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(lpParam);
|
UNREFERENCED_PARAMETER(lpParam);
|
||||||
|
|
||||||
// Do the normal startup
|
while (!cRoot::m_TerminateEventRaised)
|
||||||
universalMain(cpp14::make_unique<cMemorySettingsRepository>());
|
{
|
||||||
|
// Do the normal startup
|
||||||
|
UniversalMain(cpp14::make_unique<cMemorySettingsRepository>());
|
||||||
|
}
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -274,8 +263,7 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
|
|||||||
|
|
||||||
void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode)
|
void serviceSetState(DWORD acceptedControls, DWORD newState, DWORD exitCode)
|
||||||
{
|
{
|
||||||
SERVICE_STATUS serviceStatus;
|
SERVICE_STATUS serviceStatus = {};
|
||||||
ZeroMemory(&serviceStatus, sizeof(SERVICE_STATUS));
|
|
||||||
serviceStatus.dwCheckPoint = 0;
|
serviceStatus.dwCheckPoint = 0;
|
||||||
serviceStatus.dwControlsAccepted = acceptedControls;
|
serviceStatus.dwControlsAccepted = acceptedControls;
|
||||||
serviceStatus.dwCurrentState = newState;
|
serviceStatus.dwCurrentState = newState;
|
||||||
@ -302,11 +290,10 @@ void WINAPI serviceCtrlHandler(DWORD CtrlCode)
|
|||||||
{
|
{
|
||||||
case SERVICE_CONTROL_STOP:
|
case SERVICE_CONTROL_STOP:
|
||||||
{
|
{
|
||||||
cRoot::m_ShouldStop = true;
|
cRoot::Get()->QueueExecuteConsoleCommand("stop");
|
||||||
serviceSetState(0, SERVICE_STOP_PENDING, 0);
|
serviceSetState(0, SERVICE_STOP_PENDING, 0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
@ -365,7 +352,7 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv)
|
std::unique_ptr<cMemorySettingsRepository> ParseArguments(int argc, char **argv)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -484,7 +471,13 @@ int main(int argc, char **argv)
|
|||||||
#endif // SIGABRT_COMPAT
|
#endif // SIGABRT_COMPAT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto argsRepo = parseArguments(argc, argv);
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE))
|
||||||
|
{
|
||||||
|
LOGERROR("Could not install the Windows CTRL handler!");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Attempt to run as a service
|
// Attempt to run as a service
|
||||||
if (cRoot::m_RunAsService)
|
if (cRoot::m_RunAsService)
|
||||||
@ -522,13 +515,19 @@ int main(int argc, char **argv)
|
|||||||
close(STDOUT_FILENO);
|
close(STDOUT_FILENO);
|
||||||
close(STDERR_FILENO);
|
close(STDERR_FILENO);
|
||||||
|
|
||||||
universalMain(std::move(argsRepo));
|
while (!cRoot::m_TerminateEventRaised)
|
||||||
|
{
|
||||||
|
UniversalMain(std::move(ParseArguments(argc, argv)));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Not running as a service, do normal startup
|
while (!cRoot::m_TerminateEventRaised)
|
||||||
universalMain(std::move(argsRepo));
|
{
|
||||||
|
// Not running as a service, do normal startup
|
||||||
|
UniversalMain(std::move(ParseArguments(argc, argv)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
|
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
|
||||||
|
@ -118,6 +118,7 @@ static void DoTest(void)
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
cNetworkSingleton::Get().Initialise();
|
||||||
DoTest();
|
DoTest();
|
||||||
|
|
||||||
cNetworkSingleton::Get().Terminate();
|
cNetworkSingleton::Get().Terminate();
|
||||||
|
Loading…
Reference in New Issue
Block a user