Add statuses, status messages

This commit is contained in:
Lion Kortlepel 2021-12-06 13:17:54 +01:00
parent 0f74eca2ee
commit a1335e8c7d
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
13 changed files with 126 additions and 24 deletions

View File

@ -97,15 +97,15 @@ public:
using SystemStatusMap = std::unordered_map<std::string /* system name */, Status /* status */>; using SystemStatusMap = std::unordered_map<std::string /* system name */, Status /* status */>;
static const SystemStatusMap& GetSubsystemStatuses() { static const SystemStatusMap& GetSubsystemStatuses() {
std::unique_lock Lock(mSystemStatusMapMutex);
return mSystemStatusMap; return mSystemStatusMap;
} }
static void SetSubsystemStatus(const std::string& Subsystem, Status status) { static void SetSubsystemStatus(const std::string& Subsystem, Status status);
mSystemStatusMap[Subsystem] = status;
}
private: private:
static inline SystemStatusMap mSystemStatusMap {}; static inline SystemStatusMap mSystemStatusMap {};
static inline std::mutex mSystemStatusMapMutex {};
static inline std::string mPPS; static inline std::string mPPS;
static inline std::unique_ptr<TConsole> mConsole; static inline std::unique_ptr<TConsole> mConsole;
static inline std::mutex mShutdownHandlersMutex {}; static inline std::mutex mShutdownHandlersMutex {};

View File

@ -73,7 +73,24 @@ bool Application::IsOutdated(const Version& Current, const Version& Newest) {
} }
} }
void Application::SetSubsystemStatus(const std::string& Subsystem, Status status) {
switch (status) {
case Status::Good:
beammp_trace("Subsystem '" + Subsystem + "': Good");
break;
case Status::Bad:
beammp_trace("Subsystem '" + Subsystem + "': Bad");
break;
case Status::Starting:
beammp_trace("Subsystem '" + Subsystem + "': Starting");
break;
}
std::unique_lock Lock(mSystemStatusMapMutex);
mSystemStatusMap[Subsystem] = status;
}
void Application::CheckForUpdates() { void Application::CheckForUpdates() {
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
// checks current version against latest version // checks current version against latest version
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" }; std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s"); auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
@ -87,12 +104,14 @@ void Application::CheckForUpdates() {
} else { } else {
beammp_info("Server up-to-date!"); beammp_info("Server up-to-date!");
} }
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good);
} else { } else {
beammp_warn("Unable to fetch version from backend."); beammp_warn("Unable to fetch version from backend.");
beammp_trace("got " + Response); beammp_trace("got " + Response);
auto Lock = Sentry.CreateExclusiveContext(); auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("get-response", { { "response", Response } }); Sentry.SetContext("get-response", { { "response", Response } });
Sentry.LogError("failed to get server version", _file_basename, _line); Sentry.LogError("failed to get server version", _file_basename, _line);
Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad);
} }
} }

View File

@ -264,6 +264,7 @@ void Http::Server::SetupEnvironment() {
} }
Http::Server::THttpServerInstance::THttpServerInstance() { Http::Server::THttpServerInstance::THttpServerInstance() {
Application::SetSubsystemStatus("HTTPServer", Application::Status::Starting);
mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this); mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this);
mThread.detach(); mThread.detach();
} }
@ -279,5 +280,6 @@ void Http::Server::THttpServerInstance::operator()() {
res.set_content("0", "text/plain"); res.set_content("0", "text/plain");
res.status = 200; res.status = 200;
}); });
Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
HttpLibServerInstance.listen("0.0.0.0", Application::Settings.HTTPServerPort); HttpLibServerInstance.listen("0.0.0.0", Application::Settings.HTTPServerPort);
} }

View File

@ -31,6 +31,7 @@ static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
TConfig::TConfig(const std::string& ConfigFileName) TConfig::TConfig(const std::string& ConfigFileName)
: mConfigFileName(ConfigFileName) { : mConfigFileName(ConfigFileName) {
Application::SetSubsystemStatus("Config", Application::Status::Starting);
if (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName)) { if (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName)) {
beammp_info("No config file found! Generating one..."); beammp_info("No config file found! Generating one...");
CreateConfigFile(mConfigFileName); CreateConfigFile(mConfigFileName);
@ -115,6 +116,7 @@ void TConfig::CreateConfigFile(std::string_view name) {
ofs.close(); ofs.close();
} else { } else {
beammp_error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work."); beammp_error("Couldn't create " + std::string(name) + ". Check permissions, try again, and contact support if it continues not to work.");
Application::SetSubsystemStatus("Config", Application::Status::Bad);
mFailed = true; mFailed = true;
} }
} }
@ -151,6 +153,7 @@ void TConfig::ParseFromFile(std::string_view name) {
} catch (const std::exception& err) { } catch (const std::exception& err) {
beammp_error("Error parsing config file value: " + std::string(err.what())); beammp_error("Error parsing config file value: " + std::string(err.what()));
mFailed = true; mFailed = true;
Application::SetSubsystemStatus("Config", Application::Status::Bad);
return; return;
} }
PrintDebug(); PrintDebug();
@ -161,7 +164,13 @@ void TConfig::ParseFromFile(std::string_view name) {
// all good so far, let's check if there's a key // all good so far, let's check if there's a key
if (Application::Settings.Key.empty()) { if (Application::Settings.Key.empty()) {
beammp_error("No AuthKey specified in the \"" + std::string(mConfigFileName) + "\" file. Please get an AuthKey, enter it into the config file, and restart this server."); beammp_error("No AuthKey specified in the \"" + std::string(mConfigFileName) + "\" file. Please get an AuthKey, enter it into the config file, and restart this server.");
Application::SetSubsystemStatus("Config", Application::Status::Bad);
mFailed = true; mFailed = true;
return;
}
Application::SetSubsystemStatus("Config", Application::Status::Good);
if (Application::Settings.Key.size() != 36) {
beammp_warn("AuthKey specified is the wrong length and likely isn't valid.");
} }
} }

View File

@ -247,6 +247,34 @@ void TConsole::Command_Status(const std::string&) {
return true; return true;
}); });
size_t SystemsStarting = 0;
size_t SystemsGood = 0;
size_t SystemsBad = 0;
std::string SystemsBadList {};
std::string SystemsGoodList {};
std::string SystemsStartingList {};
auto Statuses = Application::GetSubsystemStatuses();
for (const auto& NameStatusPair : Statuses) {
switch (NameStatusPair.second) {
case Application::Status::Good:
SystemsGood++;
SystemsGoodList += NameStatusPair.first + ", ";
break;
case Application::Status::Bad:
SystemsBad++;
SystemsBadList += NameStatusPair.first + ", ";
break;
case Application::Status::Starting:
SystemsStarting++;
SystemsStartingList += NameStatusPair.first + ", ";
break;
}
}
// remove ", " at the end
SystemsBadList = SystemsBadList.substr(0, SystemsBadList.size() - 2);
SystemsGoodList = SystemsGoodList.substr(0, SystemsGoodList.size() - 2);
SystemsStartingList = SystemsStartingList.substr(0, SystemsStartingList.size() - 2);
auto ElapsedTime = mLuaEngine->Server().UptimeTimer.GetElapsedTime(); auto ElapsedTime = mLuaEngine->Server().UptimeTimer.GetElapsedTime();
Status << "BeamMP-Server Status:\n" Status << "BeamMP-Server Status:\n"
@ -262,6 +290,11 @@ void TConsole::Command_Status(const std::string&) {
<< "\t\tStates: " << mLuaEngine->GetLuaStateCount() << "\n" << "\t\tStates: " << mLuaEngine->GetLuaStateCount() << "\n"
<< "\t\tEvent timers: " << mLuaEngine->GetTimedEventsCount() << "\n" << "\t\tEvent timers: " << mLuaEngine->GetTimedEventsCount() << "\n"
<< "\t\tEvent handlers: " << mLuaEngine->GetRegisteredEventHandlerCount() << "\n" << "\t\tEvent handlers: " << mLuaEngine->GetRegisteredEventHandlerCount() << "\n"
<< "\tSubsystems:\n"
<< "\t\tGood/Starting/Bad: " << SystemsGood << "/" << SystemsStarting << "/" << SystemsBad << "\n"
<< "\t\tGood: [ " << SystemsGoodList << " ]\n"
<< "\t\tStarting: [ " << SystemsStartingList << " ]\n"
<< "\t\tBad: [ " << SystemsBadList << " ]\n"
<< ""; << "";
Application::Console().WriteRaw(Status.str()); Application::Console().WriteRaw(Status.str());

View File

@ -6,7 +6,6 @@
#include <sstream> #include <sstream>
void THeartbeatThread::operator()() { void THeartbeatThread::operator()() {
return;/*
RegisterThread("Heartbeat"); RegisterThread("Heartbeat");
std::string Body; std::string Body;
std::string T; std::string T;
@ -55,20 +54,28 @@ void THeartbeatThread::operator()() {
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
beammp_trace("got " + T + " from backend"); beammp_trace("got " + T + " from backend");
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
beammp_warn("Backend system refused server! Server will not show in the public server list."); beammp_warn("Backend system refused server! Server will not show in the public server list.");
Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
isAuth = false; isAuth = false;
SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode); SentryReportError(Application::GetBackup2Hostname() + Target, ResponseCode);
} else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
} }
} else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
} }
} else {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Good);
} }
if (!isAuth) { if (!isAuth) {
@ -81,9 +88,8 @@ void THeartbeatThread::operator()() {
} }
} }
//SocketIO::Get().SetAuthenticated(isAuth); // SocketIO::Get().SetAuthenticated(isAuth);
} }
*/
} }
std::string THeartbeatThread::GenerateCall() { std::string THeartbeatThread::GenerateCall() {
@ -108,6 +114,7 @@ std::string THeartbeatThread::GenerateCall() {
THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server) THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server)
: mResourceManager(ResourceManager) : mResourceManager(ResourceManager)
, mServer(Server) { , mServer(Server) {
Application::SetSubsystemStatus("Heartbeat", Application::Status::Starting);
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) { if (mThread.joinable()) {
mShutdown = true; mShutdown = true;

View File

@ -16,6 +16,7 @@ TLuaEngine* LuaAPI::MP::Engine;
TLuaEngine::TLuaEngine() TLuaEngine::TLuaEngine()
: mPluginMonitor(fs::path(Application::Settings.Resource) / "Server", *this, mShutdown) { : mPluginMonitor(fs::path(Application::Settings.Resource) / "Server", *this, mShutdown) {
Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
LuaAPI::MP::Engine = this; LuaAPI::MP::Engine = this;
if (!fs::exists(Application::Settings.Resource)) { if (!fs::exists(Application::Settings.Resource)) {
fs::create_directory(Application::Settings.Resource); fs::create_directory(Application::Settings.Resource);
@ -36,6 +37,7 @@ TLuaEngine::TLuaEngine()
void TLuaEngine::operator()() { void TLuaEngine::operator()() {
RegisterThread("LuaEngine"); RegisterThread("LuaEngine");
Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
// lua engine main thread // lua engine main thread
CollectAndInitPlugins(); CollectAndInitPlugins();
// now call all onInit's // now call all onInit's

View File

@ -11,6 +11,8 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
: mServer(Server) : mServer(Server)
, mPPSMonitor(PPSMonitor) , mPPSMonitor(PPSMonitor)
, mResourceManager(ResourceManager) { , mResourceManager(ResourceManager) {
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Starting);
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Starting);
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
beammp_debug("Kicking all players due to shutdown"); beammp_debug("Kicking all players due to shutdown");
Server.ForEachClient([&](std::weak_ptr<TClient> client) -> bool { Server.ForEachClient([&](std::weak_ptr<TClient> client) -> bool {
@ -56,10 +58,10 @@ void TNetwork::UDPServerMain() {
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) { if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
beammp_error("bind() failed: " + GetPlatformAgnosticErrorString()); beammp_error("bind() failed: " + GetPlatformAgnosticErrorString());
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(5));
exit(-1); exit(-1); // TODO: Wtf.
// return; // return;
} }
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good);
beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ") beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients")); + std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
while (!mShutdown) { while (!mShutdown) {
@ -123,7 +125,7 @@ void TNetwork::TCPServerMain() {
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) { if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
beammp_error("bind() failed: " + GetPlatformAgnosticErrorString()); beammp_error("bind() failed: " + GetPlatformAgnosticErrorString());
std::this_thread::sleep_for(std::chrono::seconds(5)); std::this_thread::sleep_for(std::chrono::seconds(5));
exit(-1); exit(-1); // TODO: Wtf.
} }
if (Listener == -1) { if (Listener == -1) {
beammp_error("Invalid listening socket"); beammp_error("Invalid listening socket");
@ -134,6 +136,7 @@ void TNetwork::TCPServerMain() {
// FIXME leak Listener // FIXME leak Listener
return; return;
} }
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
beammp_info(("Vehicle event network online")); beammp_info(("Vehicle event network online"));
do { do {
try { try {
@ -211,7 +214,7 @@ void TNetwork::Authentication(const TConnection& ClientConnection) {
beammp_trace("This thread is ip " + std::string(str)); beammp_trace("This thread is ip " + std::string(str));
Client->SetIdentifier("ip", str); Client->SetIdentifier("ip", str);
std::string Rc; //TODO: figure out why this is not default constructed std::string Rc; // TODO: figure out why this is not default constructed
beammp_info("Identifying new ClientConnection..."); beammp_info("Identifying new ClientConnection...");
Rc = TCPRcv(*Client); Rc = TCPRcv(*Client);

View File

@ -4,6 +4,7 @@
TPPSMonitor::TPPSMonitor(TServer& Server) TPPSMonitor::TPPSMonitor(TServer& Server)
: mServer(Server) { : mServer(Server) {
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Starting);
Application::SetPPS("-"); Application::SetPPS("-");
Application::RegisterShutdownHandler([&] { Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) { if (mThread.joinable()) {
@ -22,6 +23,7 @@ void TPPSMonitor::operator()() {
std::this_thread::sleep_for(std::chrono::milliseconds(1)); std::this_thread::sleep_for(std::chrono::milliseconds(1));
} }
beammp_debug("PPSMonitor starting"); beammp_debug("PPSMonitor starting");
Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
std::vector<std::shared_ptr<TClient>> TimedOutClients; std::vector<std::shared_ptr<TClient>> TimedOutClients;
while (!mShutdown) { while (!mShutdown) {
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));

View File

@ -6,6 +6,7 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
TResourceManager::TResourceManager() { TResourceManager::TResourceManager() {
Application::SetSubsystemStatus("ResourceManager", Application::Status::Starting);
std::string Path = Application::Settings.Resource + "/Client"; std::string Path = Application::Settings.Resource + "/Client";
if (!fs::exists(Path)) if (!fs::exists(Path))
fs::create_directories(Path); fs::create_directories(Path);
@ -13,11 +14,11 @@ TResourceManager::TResourceManager() {
std::string File(entry.path().string()); std::string File(entry.path().string());
if (auto pos = File.find(".zip"); pos != std::string::npos) { if (auto pos = File.find(".zip"); pos != std::string::npos) {
if (File.length() - pos == 4) { if (File.length() - pos == 4) {
std::replace(File.begin(), File.end(),'\\','/'); std::replace(File.begin(), File.end(), '\\', '/');
mFileList += File + ';'; mFileList += File + ';';
if(auto i = File.find_last_of('/'); i != std::string::npos){ if (auto i = File.find_last_of('/'); i != std::string::npos) {
++i; ++i;
File = File.substr(i,pos-i); File = File.substr(i, pos - i);
} }
mTrimmedList += "/" + fs::path(File).filename().string() + ';'; mTrimmedList += "/" + fs::path(File).filename().string() + ';';
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';'; mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
@ -29,4 +30,6 @@ TResourceManager::TResourceManager() {
if (mModsLoaded) if (mModsLoaded)
beammp_info("Loaded " + std::to_string(mModsLoaded) + " Mods"); beammp_info("Loaded " + std::to_string(mModsLoaded) + " Mods");
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
} }

View File

@ -55,6 +55,7 @@ void TSentry::SetupUser() {
if (!mValid) { if (!mValid) {
return; return;
} }
Application::SetSubsystemStatus("Sentry", Application::Status::Good);
sentry_value_t user = sentry_value_new_object(); sentry_value_t user = sentry_value_new_object();
if (Application::Settings.Key.size() == 36) { if (Application::Settings.Key.size() == 36) {
sentry_value_set_by_key(user, "id", sentry_value_new_string(Application::Settings.Key.c_str())); sentry_value_set_by_key(user, "id", sentry_value_new_string(Application::Settings.Key.c_str()));

View File

@ -17,6 +17,7 @@ namespace json = rapidjson;
TServer::TServer(const std::vector<std::string_view>& Arguments) { TServer::TServer(const std::vector<std::string_view>& Arguments) {
beammp_info("BeamMP Server v" + Application::ServerVersionString()); beammp_info("BeamMP Server v" + Application::ServerVersionString());
Application::SetSubsystemStatus("Server", Application::Status::Starting);
if (Arguments.size() > 1) { if (Arguments.size() > 1) {
Application::Settings.CustomIP = Arguments[0]; Application::Settings.CustomIP = Arguments[0];
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.'); size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
@ -28,6 +29,7 @@ TServer::TServer(const std::vector<std::string_view>& Arguments) {
beammp_info("server started with custom IP"); beammp_info("server started with custom IP");
} }
} }
Application::SetSubsystemStatus("Server", Application::Status::Good);
} }
void TServer::RemoveClient(const std::weak_ptr<TClient>& WeakClientPtr) { void TServer::RemoveClient(const std::weak_ptr<TClient>& WeakClientPtr) {

View File

@ -78,6 +78,7 @@ int main(int argc, char** argv) {
int BeamMPServerMain(MainArguments Arguments) { int BeamMPServerMain(MainArguments Arguments) {
setlocale(LC_ALL, "C"); setlocale(LC_ALL, "C");
Application::InitializeConsole(); Application::InitializeConsole();
Application::SetSubsystemStatus("Main", Application::Status::Starting);
SetupSignalHandlers(); SetupSignalHandlers();
@ -161,17 +162,35 @@ int BeamMPServerMain(MainArguments Arguments) {
Http::Server::THttpServerInstance HttpServerInstance {}; Http::Server::THttpServerInstance HttpServerInstance {};
} }
Application::SetSubsystemStatus("Main", Application::Status::Good);
RegisterThread("Main(Waiting)"); RegisterThread("Main(Waiting)");
bool FullyStarted = false;
while (!Shutdown) {
if (!FullyStarted) {
FullyStarted = true;
bool WithErrors = false;
std::string SystemsBadList {};
auto Statuses = Application::GetSubsystemStatuses(); auto Statuses = Application::GetSubsystemStatuses();
for (const auto& NameStatusPair : Statuses) { for (const auto& NameStatusPair : Statuses) {
if (NameStatusPair.second != Application::Status::Good) { if (NameStatusPair.second == Application::Status::Starting) {
beammp_info("not good: " + NameStatusPair.first); FullyStarted = false;
} else if (NameStatusPair.second == Application::Status::Bad) {
SystemsBadList += NameStatusPair.first + ", ";
WithErrors = true;
} }
} }
// remove ", "
while (!Shutdown) { SystemsBadList = SystemsBadList.substr(0, SystemsBadList.size() - 2);
std::this_thread::sleep_for(std::chrono::milliseconds(50)); if (FullyStarted) {
if (!WithErrors) {
beammp_info("ALL SYSTEMS STARTED SUCCESSFULLY, EVERYTHING IS OKAY");
} else {
beammp_error("STARTUP NOT SUCCESSFUL, SYSTEMS " + SystemsBadList + " HAD ERRORS. THIS MAY OR MAY NOT CAUSE ISSUES.");
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
beammp_info("Shutdown."); beammp_info("Shutdown.");
return 0; return 0;