summaryrefslogtreecommitdiffstats
path: root/src/Root.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Root.cpp')
-rw-r--r--src/Root.cpp370
1 files changed, 170 insertions, 200 deletions
diff --git a/src/Root.cpp b/src/Root.cpp
index 4b40fd983..4291c5779 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -46,7 +46,10 @@
-cRoot * cRoot::s_Root = nullptr;
+extern bool g_RunAsService;
+decltype(cRoot::s_Root) cRoot::s_Root;
+decltype(cRoot::s_NextState) cRoot::s_NextState;
+decltype(cRoot::s_StopEvent) cRoot::s_StopEvent;
@@ -65,7 +68,7 @@ cRoot::cRoot(void) :
{
Temporary::RegisterAllBlockHandlers(m_BlockTypeRegistry);
s_Root = this;
- m_InputThreadRunFlag.clear();
+ TransitionNextState(NextState::Run);
}
@@ -81,85 +84,28 @@ cRoot::~cRoot()
-void cRoot::InputThread(cRoot & a_Params)
+bool cRoot::Run(cSettingsRepositoryInterface & a_OverridesRepo)
{
- cLogCommandOutputCallback Output;
- AString Command;
-
- while (a_Params.m_InputThreadRunFlag.test_and_set() && std::cin.good())
- {
- #ifndef _WIN32
- timeval Timeout{ 0, 0 };
- Timeout.tv_usec = 100 * 1000; // 100 msec
-
- fd_set ReadSet;
- FD_ZERO(&ReadSet);
- FD_SET(STDIN_FILENO, &ReadSet);
-
- if (select(STDIN_FILENO + 1, &ReadSet, nullptr, nullptr, &Timeout) <= 0)
- {
- // Don't call getline because there's nothing to read
- continue;
- }
- #endif
-
- std::getline(std::cin, Command);
-
- if (!a_Params.m_InputThreadRunFlag.test_and_set())
- {
- // Already shutting down, can't execute commands
- break;
- }
-
- if (!Command.empty())
- {
- // Execute and clear command string when submitted
- a_Params.ExecuteConsoleCommand(TrimString(Command), Output);
- }
- }
-
- // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running
- // Ignore this when running as a service, cin was never opened in that case
- if (!std::cin.good() && !m_RunAsService)
- {
- // Stop the server:
- a_Params.QueueExecuteConsoleCommand("stop");
- }
-}
-
-
-
-
-
-void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
-{
- #ifdef _WIN32
- HMENU ConsoleMenu = GetSystemMenu(GetConsoleWindow(), FALSE);
- EnableMenuItem(ConsoleMenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling
- #endif
-
- auto consoleLogListener = MakeConsoleListener(m_RunAsService);
+ auto consoleLogListener = MakeConsoleListener(g_RunAsService);
auto consoleAttachment = cLogger::GetInstance().AttachListener(std::move(consoleLogListener));
cLogger::cAttachment fileAttachment;
- if (!a_OverridesRepo->HasValue("Server","DisableLogFile"))
+ if (!a_OverridesRepo.HasValue("Server","DisableLogFile"))
{
auto fileLogListenerRet = MakeFileListener();
if (!fileLogListenerRet.first)
{
- m_TerminateEventRaised = true;
- LOGERROR("Failed to open log file, aborting");
- return;
+ throw std::runtime_error("failed to open log file");
}
fileAttachment = cLogger::GetInstance().AttachListener(std::move(fileLogListenerRet.second));
}
LOG("--- Started Log ---");
- #ifdef BUILD_ID
- LOG("Cuberite " BUILD_SERIES_NAME " build id: " BUILD_ID);
- LOG("from commit id: " BUILD_COMMIT_ID " built at: " BUILD_DATETIME);
- #endif
+#ifdef BUILD_ID
+ LOG("Cuberite " BUILD_SERIES_NAME " (id: " BUILD_ID ")");
+ LOG("from commit " BUILD_COMMIT_ID " built at: " BUILD_DATETIME);
+#endif
cDeadlockDetect dd;
auto BeginTime = std::chrono::steady_clock::now();
@@ -172,9 +118,9 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
LOG("Reading server config...");
m_SettingsFilename = "settings.ini";
- if (a_OverridesRepo->HasValue("Server","ConfigFile"))
+ if (a_OverridesRepo.HasValue("Server","ConfigFile"))
{
- m_SettingsFilename = a_OverridesRepo->GetValue("Server","ConfigFile");
+ m_SettingsFilename = a_OverridesRepo.GetValue("Server","ConfigFile");
}
auto IniFile = std::make_unique<cIniFile>();
@@ -187,7 +133,7 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
IniFile->AddHeaderComment(" Most of the settings here can be configured using the webadmin interface, if enabled in webadmin.ini");
}
- auto settingsRepo = std::make_unique<cOverridesSettingsRepository>(std::move(IniFile), std::move(a_OverridesRepo));
+ auto settingsRepo = std::make_unique<cOverridesSettingsRepository>(std::move(IniFile), a_OverridesRepo);
LOG("Starting server...");
@@ -200,8 +146,7 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
if (!m_Server->InitServer(*settingsRepo, ShouldAuthenticate))
{
settingsRepo->Flush();
- LOGERROR("Failure starting server, aborting...");
- return;
+ throw std::runtime_error("failure starting server");
}
m_WebAdmin = new cWebAdmin();
@@ -245,32 +190,13 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
{
m_WebAdmin->Start();
- LOGD("Starting InputThread...");
- try
- {
- m_InputThreadRunFlag.test_and_set();
- m_InputThread = std::thread(InputThread, std::ref(*this));
- }
- 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());
- }
-
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
m_StartTime = std::chrono::steady_clock::now();
- #ifdef _WIN32
- EnableMenuItem(ConsoleMenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
- #endif
-
- for (;;)
- {
- m_StopEvent.Wait();
-
- break;
- }
+ HandleInput();
+ s_StopEvent.Wait();
// Stop the server:
m_WebAdmin->Stop();
@@ -278,13 +204,6 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
LOG("Shutting down server...");
m_Server->Shutdown();
} // if (m_Server->Start()
- else
- {
- cRoot::m_TerminateEventRaised = true;
- #ifdef _WIN32
- EnableMenuItem(ConsoleMenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
- #endif
- }
delete m_MojangAPI; m_MojangAPI = nullptr;
@@ -313,75 +232,28 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
LOG("Cleaning up...");
delete m_Server; m_Server = nullptr;
- m_InputThreadRunFlag.clear();
- #ifdef _WIN32
- DWORD Length;
- INPUT_RECORD Record
- {
- KEY_EVENT,
- {
- {
- TRUE,
- 1,
- VK_RETURN,
- static_cast<WORD>(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
- m_InputThread.join();
- #endif
-
- if (m_TerminateEventRaised)
- {
- LOG("Shutdown successful!");
- }
- else
- {
- LOG("Shutdown successful - restarting...");
- }
+ LOG("Shutdown successful!");
LOG("--- Stopped Log ---");
+
+ return s_NextState == NextState::Restart;
}
-void cRoot::StopServer()
+void cRoot::Stop()
{
- // Kick all players from the server with custom disconnect message
+ TransitionNextState(NextState::Stop);
+}
- bool SentDisconnect = false;
- cRoot::Get()->ForEachPlayer([&](cPlayer & a_Player)
- {
- a_Player.GetClientHandlePtr()->Kick(m_Server->GetShutdownMessage());
- SentDisconnect = true;
- return false;
- }
- );
- if (SentDisconnect)
- {
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
- m_TerminateEventRaised = true;
- m_StopEvent.Set();
- m_InputThreadRunFlag.clear();
+
+
+
+
+void cRoot::Restart()
+{
+ TransitionNextState(NextState::Restart);
}
@@ -607,58 +479,39 @@ bool cRoot::ForEachWorld(cWorldListCallback a_Callback)
-void cRoot::TickCommands(void)
-{
- // Execute any pending commands:
- cCommandQueue PendingCommands;
- {
- cCSLock Lock(m_CSPendingCommands);
- std::swap(PendingCommands, m_PendingCommands);
- }
- for (cCommandQueue::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr)
- {
- ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output));
- }
-}
-
-
-
-
-
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
- // Put the command into a queue (Alleviates FS #363):
- cCSLock Lock(m_CSPendingCommands);
- m_PendingCommands.emplace_back(a_Cmd, &a_Output);
-}
-
-
-
-
-
-void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
-{
- // Put the command into a queue (Alleviates FS #363):
- cCSLock Lock(m_CSPendingCommands);
- m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback));
-}
-
-
+ const auto KickPlayers = [this]
+ {
+ // Kick all players from the server with custom disconnect message
+ bool SentDisconnect = false;
+ cRoot::Get()->ForEachPlayer(
+ [&](cPlayer & a_Player)
+ {
+ a_Player.GetClientHandlePtr()->Kick(m_Server->GetShutdownMessage());
+ SentDisconnect = true;
+ return false;
+ }
+ );
+ if (SentDisconnect)
+ {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ }
+ };
-void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
-{
// Some commands are built-in:
if (a_Cmd == "stop")
{
- StopServer();
+ KickPlayers();
+ cRoot::Stop();
return;
}
else if (a_Cmd == "restart")
{
- m_StopEvent.Set();
- m_InputThreadRunFlag.clear();
+ KickPlayers();
+ cRoot::Restart();
return;
}
@@ -670,6 +523,16 @@ void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback
+void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
+{
+ // Put the command into a queue (Alleviates FS #363):
+ QueueExecuteConsoleCommand(a_Cmd, *new cLogCommandDeleteSelfOutputCallback);
+}
+
+
+
+
+
void cRoot::KickUser(int a_ClientID, const AString & a_Reason)
{
m_Server->KickUser(a_ClientID, a_Reason);
@@ -1068,3 +931,110 @@ AStringVector cRoot::GetPlayerTabCompletionMultiWorld(const AString & a_Text)
);
return Results;
}
+
+
+
+
+
+void cRoot::HandleInput()
+{
+ if (g_RunAsService)
+ {
+ // Ignore input when running as a service, cin was never opened in that case:
+ return;
+ }
+
+ cLogCommandOutputCallback Output;
+ AString Command;
+
+ while (s_NextState == NextState::Run)
+ {
+#ifndef _WIN32
+ timeval Timeout{ 0, 0 };
+ Timeout.tv_usec = 100 * 1000; // 100 msec
+
+ fd_set ReadSet;
+ FD_ZERO(&ReadSet);
+ FD_SET(STDIN_FILENO, &ReadSet);
+
+ if (select(STDIN_FILENO + 1, &ReadSet, nullptr, nullptr, &Timeout) <= 0)
+ {
+ // Don't call getline because there's nothing to read
+ continue;
+ }
+#endif
+
+ if (!std::getline(std::cin, Command))
+ {
+ cRoot::Stop();
+ return;
+ }
+
+ if (s_NextState != NextState::Run)
+ {
+ // Already shutting down, can't execute commands
+ break;
+ }
+
+ if (!Command.empty())
+ {
+ // Execute and clear command string when submitted
+ QueueExecuteConsoleCommand(TrimString(Command), Output);
+ }
+ }
+}
+
+
+
+
+
+void cRoot::TransitionNextState(NextState a_NextState)
+{
+ {
+ auto Current = s_NextState.load();
+ do
+ {
+ // Stopping is final, so stops override restarts:
+ if (Current == NextState::Stop)
+ {
+ return;
+ }
+ }
+ while (!s_NextState.compare_exchange_strong(Current, a_NextState));
+ }
+
+ if (s_NextState == NextState::Run)
+ {
+ return;
+ }
+
+ s_StopEvent.Set();
+
+#ifdef WIN32
+ if (g_RunAsService)
+ {
+ return;
+ }
+
+ DWORD Length;
+ INPUT_RECORD Record
+ {
+ KEY_EVENT,
+ {
+ {
+ TRUE,
+ 1,
+ VK_RETURN,
+ static_cast<WORD>(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 apart from CancelIoEx, but xoft wants Windows XP support
+ // Only thing I can think of for now.
+ VERIFY(WriteConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &Record, 1, &Length) == TRUE);
+#endif
+}