diff --git a/include/TConsole.h b/include/TConsole.h index 25ff258..618c586 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -27,6 +27,7 @@ private: void Command_Kick(const std::string& cmd); void Command_Say(const std::string& cmd); void Command_List(const std::string& cmd); + void Command_Status(const std::string& cmd); Commandline mCommandline; std::vector mCachedLuaHistory; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 7e2cadf..8a87c7d 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -82,6 +82,29 @@ public: void SetNetwork(TNetwork* Network) { mNetwork = Network; } void SetServer(TServer* Server) { mServer = Server; } + size_t GetResultsToCheckSize() { + std::unique_lock Lock(mResultsToCheckMutex); + return mResultsToCheck.size(); + } + size_t GetLuaStateCount() { + std::unique_lock Lock(mLuaStatesMutex); + return mLuaStates.size(); + } + size_t GetTimedEventsCount() { + std::unique_lock Lock(mTimedEventsMutex); + return mTimedEvents.size(); + } + size_t GetRegisteredEventHandlerCount() { + std::unique_lock Lock(mLuaEventsMutex); + size_t LuaEventsCount = 0; + for (const auto& State : mLuaEvents) { + for (const auto& Events : State.second) { + LuaEventsCount += Events.second.size(); + } + } + return LuaEventsCount - GetLuaStateCount(); + } + static void WaitForAll(std::vector>& Results); void ReportErrors(const std::vector>& Results); bool HasState(TLuaStateId StateId); diff --git a/include/TScopedTimer.h b/include/TScopedTimer.h index 3eea0ee..600e69a 100644 --- a/include/TScopedTimer.h +++ b/include/TScopedTimer.h @@ -10,6 +10,12 @@ public: TScopedTimer(const std::string& Name); TScopedTimer(std::function OnDestroy); ~TScopedTimer(); + auto GetElapsedTime() const { + auto EndTime = std::chrono::high_resolution_clock::now(); + auto Delta = EndTime - mStartTime; + size_t TimeDelta = Delta / std::chrono::milliseconds(1); + return TimeDelta; + } std::function OnDestroy { nullptr }; diff --git a/include/TServer.h b/include/TServer.h index dded2d9..2e51e12 100644 --- a/include/TServer.h +++ b/include/TServer.h @@ -29,6 +29,8 @@ public: static void HandleEvent(TClient& c, const std::string& Data); RWMutex& GetClientMutex() const { return mClientsMutex; } + + const TScopedTimer UptimeTimer; private: TClientSet mClients; mutable RWMutex mClientsMutex; diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 5cca537..0bca9a8 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -149,7 +149,8 @@ void TConsole::Command_Help(const std::string&) { kick [reason] kicks specified player with an optional reason list lists all players and info about them say sends the message to all players in chat - lua [state id] switches to lua, optionally into a specific state id's lua)"; + lua [state id] switches to lua, optionally into a specific state id's lua + status how the server is doing and what it's up to)"; Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString)); } @@ -215,6 +216,52 @@ void TConsole::Command_List(const std::string&) { } } +void TConsole::Command_Status(const std::string&) { + std::stringstream Status; + + size_t CarCount = 0; + size_t ConnectedCount = 0; + size_t GuestCount = 0; + size_t SyncedCount = 0; + size_t SyncingCount = 0; + size_t MissedPacketQueueSum = 0; + int LargestSecondsSinceLastPing = 0; + mLuaEngine->Server().ForEachClient([&](std::weak_ptr Client) -> bool { + if (!Client.expired()) { + auto Locked = Client.lock(); + CarCount += Locked->GetCarCount(); + ConnectedCount += Locked->IsConnected() ? 1 : 0; + GuestCount += Locked->IsGuest() ? 1 : 0; + SyncedCount += Locked->IsSynced() ? 1 : 0; + SyncingCount += Locked->IsSyncing() ? 1 : 0; + MissedPacketQueueSum += Locked->MissedPacketQueueSize(); + if (Locked->SecondsSinceLastPing() < LargestSecondsSinceLastPing) { + LargestSecondsSinceLastPing = Locked->SecondsSinceLastPing(); + } + } + return true; + }); + + auto ElapsedTime = mLuaEngine->Server().UptimeTimer.GetElapsedTime(); + + Status << "BeamMP-Server Status:\n" + << "\tTotal Players: " << mLuaEngine->Server().ClientCount() << "\n" + << "\tSyncing Players: " << SyncingCount << "\n" + << "\tSynced Players: " << SyncedCount << "\n" + << "\tConnected Players: " << ConnectedCount << "\n" + << "\tGuests: " << GuestCount << "\n" + << "\tCars: " << CarCount << "\n" + << "\tUptime: " << ElapsedTime << "ms (~" << size_t(ElapsedTime / 1000.0 / 60.0 / 60.0) << "h) \n" + << "\tLua:\n" + << "\t\tQueued results to check: " << mLuaEngine->GetResultsToCheckSize() << "\n" + << "\t\tStates: " << mLuaEngine->GetLuaStateCount() << "\n" + << "\t\tEvent timers: " << mLuaEngine->GetTimedEventsCount() << "\n" + << "\t\tEvent handlers: " << mLuaEngine->GetRegisteredEventHandlerCount() << "\n" + << ""; + + Application::Console().WriteRaw(Status.str()); +} + void TConsole::RunAsCommand(const std::string& cmd, bool IgnoreNotACommand) { auto FutureIsNonNil = [](const std::shared_ptr& Future) { @@ -308,6 +355,9 @@ TConsole::TConsole() { } else if (StringStartsWith(cmd, "list")) { RunAsCommand(cmd, true); Command_List(cmd); + } else if (StringStartsWith(cmd, "status")) { + RunAsCommand(cmd, true); + Command_Status(cmd); } else if (!cmd.empty()) { RunAsCommand(cmd); }