diff --git a/include/Common.h b/include/Common.h
index ed34b7f..373f0a2 100644
--- a/include/Common.h
+++ b/include/Common.h
@@ -97,15 +97,15 @@ public:
using SystemStatusMap = std::unordered_map;
static const SystemStatusMap& GetSubsystemStatuses() {
+ std::unique_lock Lock(mSystemStatusMapMutex);
return mSystemStatusMap;
}
-
- static void SetSubsystemStatus(const std::string& Subsystem, Status status) {
- mSystemStatusMap[Subsystem] = status;
- }
+
+ static void SetSubsystemStatus(const std::string& Subsystem, Status status);
private:
static inline SystemStatusMap mSystemStatusMap {};
+ static inline std::mutex mSystemStatusMapMutex {};
static inline std::string mPPS;
static inline std::unique_ptr mConsole;
static inline std::mutex mShutdownHandlersMutex {};
diff --git a/src/Common.cpp b/src/Common.cpp
index 26f7619..620c609 100644
--- a/src/Common.cpp
+++ b/src/Common.cpp
@@ -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() {
+ Application::SetSubsystemStatus("UpdateCheck", Application::Status::Starting);
// checks current version against latest version
std::regex VersionRegex { R"(\d+\.\d+\.\d+\n*)" };
auto Response = Http::GET(GetBackendHostname(), 443, "/v/s");
@@ -87,12 +104,14 @@ void Application::CheckForUpdates() {
} else {
beammp_info("Server up-to-date!");
}
+ Application::SetSubsystemStatus("UpdateCheck", Application::Status::Good);
} else {
beammp_warn("Unable to fetch version from backend.");
beammp_trace("got " + Response);
auto Lock = Sentry.CreateExclusiveContext();
Sentry.SetContext("get-response", { { "response", Response } });
Sentry.LogError("failed to get server version", _file_basename, _line);
+ Application::SetSubsystemStatus("UpdateCheck", Application::Status::Bad);
}
}
diff --git a/src/Http.cpp b/src/Http.cpp
index 813a26d..3f53517 100644
--- a/src/Http.cpp
+++ b/src/Http.cpp
@@ -264,6 +264,7 @@ void Http::Server::SetupEnvironment() {
}
Http::Server::THttpServerInstance::THttpServerInstance() {
+ Application::SetSubsystemStatus("HTTPServer", Application::Status::Starting);
mThread = std::thread(&Http::Server::THttpServerInstance::operator(), this);
mThread.detach();
}
@@ -279,5 +280,6 @@ void Http::Server::THttpServerInstance::operator()() {
res.set_content("0", "text/plain");
res.status = 200;
});
+ Application::SetSubsystemStatus("HTTPServer", Application::Status::Good);
HttpLibServerInstance.listen("0.0.0.0", Application::Settings.HTTPServerPort);
}
diff --git a/src/TConfig.cpp b/src/TConfig.cpp
index 9817fc6..db46739 100644
--- a/src/TConfig.cpp
+++ b/src/TConfig.cpp
@@ -31,6 +31,7 @@ static constexpr std::string_view StrHTTPServerPort = "HTTPServerPort";
TConfig::TConfig(const std::string& ConfigFileName)
: mConfigFileName(ConfigFileName) {
+ Application::SetSubsystemStatus("Config", Application::Status::Starting);
if (!fs::exists(mConfigFileName) || !fs::is_regular_file(mConfigFileName)) {
beammp_info("No config file found! Generating one...");
CreateConfigFile(mConfigFileName);
@@ -115,6 +116,7 @@ void TConfig::CreateConfigFile(std::string_view name) {
ofs.close();
} else {
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;
}
}
@@ -151,6 +153,7 @@ void TConfig::ParseFromFile(std::string_view name) {
} catch (const std::exception& err) {
beammp_error("Error parsing config file value: " + std::string(err.what()));
mFailed = true;
+ Application::SetSubsystemStatus("Config", Application::Status::Bad);
return;
}
PrintDebug();
@@ -161,7 +164,13 @@ void TConfig::ParseFromFile(std::string_view name) {
// all good so far, let's check if there's a key
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.");
+ Application::SetSubsystemStatus("Config", Application::Status::Bad);
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.");
}
}
diff --git a/src/TConsole.cpp b/src/TConsole.cpp
index 9acf574..e947d65 100644
--- a/src/TConsole.cpp
+++ b/src/TConsole.cpp
@@ -247,6 +247,34 @@ void TConsole::Command_Status(const std::string&) {
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();
Status << "BeamMP-Server Status:\n"
@@ -262,6 +290,11 @@ void TConsole::Command_Status(const std::string&) {
<< "\t\tStates: " << mLuaEngine->GetLuaStateCount() << "\n"
<< "\t\tEvent timers: " << mLuaEngine->GetTimedEventsCount() << "\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());
diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp
index ac7a293..4f7ef55 100644
--- a/src/THeartbeatThread.cpp
+++ b/src/THeartbeatThread.cpp
@@ -6,7 +6,6 @@
#include
void THeartbeatThread::operator()() {
- return;/*
RegisterThread("Heartbeat");
std::string Body;
std::string T;
@@ -55,20 +54,28 @@ void THeartbeatThread::operator()() {
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
beammp_trace("got " + T + " from backend");
+ Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
SentryReportError(Application::GetBackendHostname() + Target, ResponseCode);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) {
SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode);
+ Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode);
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.");
-
+ Application::SetSubsystemStatus("Heartbeat", Application::Status::Bad);
isAuth = false;
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) {
@@ -81,9 +88,8 @@ void THeartbeatThread::operator()() {
}
}
- //SocketIO::Get().SetAuthenticated(isAuth);
+ // SocketIO::Get().SetAuthenticated(isAuth);
}
- */
}
std::string THeartbeatThread::GenerateCall() {
@@ -108,6 +114,7 @@ std::string THeartbeatThread::GenerateCall() {
THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& Server)
: mResourceManager(ResourceManager)
, mServer(Server) {
+ Application::SetSubsystemStatus("Heartbeat", Application::Status::Starting);
Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) {
mShutdown = true;
diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp
index 97d2ed7..4deafc6 100644
--- a/src/TLuaEngine.cpp
+++ b/src/TLuaEngine.cpp
@@ -16,6 +16,7 @@ TLuaEngine* LuaAPI::MP::Engine;
TLuaEngine::TLuaEngine()
: mPluginMonitor(fs::path(Application::Settings.Resource) / "Server", *this, mShutdown) {
+ Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
LuaAPI::MP::Engine = this;
if (!fs::exists(Application::Settings.Resource)) {
fs::create_directory(Application::Settings.Resource);
@@ -36,6 +37,7 @@ TLuaEngine::TLuaEngine()
void TLuaEngine::operator()() {
RegisterThread("LuaEngine");
+ Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
// lua engine main thread
CollectAndInitPlugins();
// now call all onInit's
diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp
index 914bf85..2534052 100644
--- a/src/TNetwork.cpp
+++ b/src/TNetwork.cpp
@@ -11,6 +11,8 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
: mServer(Server)
, mPPSMonitor(PPSMonitor)
, mResourceManager(ResourceManager) {
+ Application::SetSubsystemStatus("TCPNetwork", Application::Status::Starting);
+ Application::SetSubsystemStatus("UDPNetwork", Application::Status::Starting);
Application::RegisterShutdownHandler([&] {
beammp_debug("Kicking all players due to shutdown");
Server.ForEachClient([&](std::weak_ptr client) -> bool {
@@ -56,10 +58,10 @@ void TNetwork::UDPServerMain() {
if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
beammp_error("bind() failed: " + GetPlatformAgnosticErrorString());
std::this_thread::sleep_for(std::chrono::seconds(5));
- exit(-1);
+ exit(-1); // TODO: Wtf.
// 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 ")
+ std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
while (!mShutdown) {
@@ -123,7 +125,7 @@ void TNetwork::TCPServerMain() {
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) != 0) {
beammp_error("bind() failed: " + GetPlatformAgnosticErrorString());
std::this_thread::sleep_for(std::chrono::seconds(5));
- exit(-1);
+ exit(-1); // TODO: Wtf.
}
if (Listener == -1) {
beammp_error("Invalid listening socket");
@@ -134,6 +136,7 @@ void TNetwork::TCPServerMain() {
// FIXME leak Listener
return;
}
+ Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
beammp_info(("Vehicle event network online"));
do {
try {
@@ -211,7 +214,7 @@ void TNetwork::Authentication(const TConnection& ClientConnection) {
beammp_trace("This thread is ip " + std::string(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...");
Rc = TCPRcv(*Client);
diff --git a/src/TPPSMonitor.cpp b/src/TPPSMonitor.cpp
index 9ea80dc..5d28187 100644
--- a/src/TPPSMonitor.cpp
+++ b/src/TPPSMonitor.cpp
@@ -4,6 +4,7 @@
TPPSMonitor::TPPSMonitor(TServer& Server)
: mServer(Server) {
+ Application::SetSubsystemStatus("PPSMonitor", Application::Status::Starting);
Application::SetPPS("-");
Application::RegisterShutdownHandler([&] {
if (mThread.joinable()) {
@@ -22,6 +23,7 @@ void TPPSMonitor::operator()() {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
beammp_debug("PPSMonitor starting");
+ Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good);
std::vector> TimedOutClients;
while (!mShutdown) {
std::this_thread::sleep_for(std::chrono::seconds(1));
diff --git a/src/TResourceManager.cpp b/src/TResourceManager.cpp
index 96bc42a..afb0e6b 100644
--- a/src/TResourceManager.cpp
+++ b/src/TResourceManager.cpp
@@ -6,6 +6,7 @@
namespace fs = std::filesystem;
TResourceManager::TResourceManager() {
+ Application::SetSubsystemStatus("ResourceManager", Application::Status::Starting);
std::string Path = Application::Settings.Resource + "/Client";
if (!fs::exists(Path))
fs::create_directories(Path);
@@ -13,11 +14,11 @@ TResourceManager::TResourceManager() {
std::string File(entry.path().string());
if (auto pos = File.find(".zip"); pos != std::string::npos) {
if (File.length() - pos == 4) {
- std::replace(File.begin(), File.end(),'\\','/');
+ std::replace(File.begin(), File.end(), '\\', '/');
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;
- File = File.substr(i,pos-i);
+ File = File.substr(i, pos - i);
}
mTrimmedList += "/" + fs::path(File).filename().string() + ';';
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
@@ -29,4 +30,6 @@ TResourceManager::TResourceManager() {
if (mModsLoaded)
beammp_info("Loaded " + std::to_string(mModsLoaded) + " Mods");
+
+ Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
}
diff --git a/src/TSentry.cpp b/src/TSentry.cpp
index 447392f..da05c10 100644
--- a/src/TSentry.cpp
+++ b/src/TSentry.cpp
@@ -55,6 +55,7 @@ void TSentry::SetupUser() {
if (!mValid) {
return;
}
+ Application::SetSubsystemStatus("Sentry", Application::Status::Good);
sentry_value_t user = sentry_value_new_object();
if (Application::Settings.Key.size() == 36) {
sentry_value_set_by_key(user, "id", sentry_value_new_string(Application::Settings.Key.c_str()));
diff --git a/src/TServer.cpp b/src/TServer.cpp
index 69d6bb9..0f1ad32 100644
--- a/src/TServer.cpp
+++ b/src/TServer.cpp
@@ -17,6 +17,7 @@ namespace json = rapidjson;
TServer::TServer(const std::vector& Arguments) {
beammp_info("BeamMP Server v" + Application::ServerVersionString());
+ Application::SetSubsystemStatus("Server", Application::Status::Starting);
if (Arguments.size() > 1) {
Application::Settings.CustomIP = Arguments[0];
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
@@ -28,6 +29,7 @@ TServer::TServer(const std::vector& Arguments) {
beammp_info("server started with custom IP");
}
}
+ Application::SetSubsystemStatus("Server", Application::Status::Good);
}
void TServer::RemoveClient(const std::weak_ptr& WeakClientPtr) {
diff --git a/src/main.cpp b/src/main.cpp
index ca29d12..0d3ae5b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -78,6 +78,7 @@ int main(int argc, char** argv) {
int BeamMPServerMain(MainArguments Arguments) {
setlocale(LC_ALL, "C");
Application::InitializeConsole();
+ Application::SetSubsystemStatus("Main", Application::Status::Starting);
SetupSignalHandlers();
@@ -148,7 +149,7 @@ int BeamMPServerMain(MainArguments Arguments) {
beammp_trace("Running in debug mode on a debug build");
Sentry.SetupUser();
Sentry.PrintWelcome();
- TResourceManager ResourceManager;
+ TResourceManager ResourceManager;
TPPSMonitor PPSMonitor(Server);
THeartbeatThread Heartbeat(ResourceManager, Server);
TNetwork Network(Server, PPSMonitor, ResourceManager);
@@ -161,17 +162,35 @@ int BeamMPServerMain(MainArguments Arguments) {
Http::Server::THttpServerInstance HttpServerInstance {};
}
+ Application::SetSubsystemStatus("Main", Application::Status::Good);
RegisterThread("Main(Waiting)");
- auto Statuses = Application::GetSubsystemStatuses();
- for (const auto& NameStatusPair : Statuses) {
- if (NameStatusPair.second != Application::Status::Good) {
- beammp_info("not good: " + NameStatusPair.first);
- }
- }
-
+ bool FullyStarted = false;
while (!Shutdown) {
- std::this_thread::sleep_for(std::chrono::milliseconds(50));
+ if (!FullyStarted) {
+ FullyStarted = true;
+ bool WithErrors = false;
+ std::string SystemsBadList {};
+ auto Statuses = Application::GetSubsystemStatuses();
+ for (const auto& NameStatusPair : Statuses) {
+ if (NameStatusPair.second == Application::Status::Starting) {
+ FullyStarted = false;
+ } else if (NameStatusPair.second == Application::Status::Bad) {
+ SystemsBadList += NameStatusPair.first + ", ";
+ WithErrors = true;
+ }
+ }
+ // remove ", "
+ SystemsBadList = SystemsBadList.substr(0, SystemsBadList.size() - 2);
+ 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.");
return 0;