diff --git a/Changelog.md b/Changelog.md index 6ee3ff8..aa104b3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,4 @@ + # v3.1.0 - ADDED Tab autocomplete in console, smart tab autocomplete (understands lua tables and types) in the lua console @@ -9,6 +10,7 @@ - FIXED issue with client->server events which contain ':' - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink - FIXED onInit not being called on hot-reload +- FIXED incorrect timing calculation of Lua EventTimer loop # v3.0.2 diff --git a/include/Common.h b/include/Common.h index 805739c..c77ffd1 100644 --- a/include/Common.h +++ b/include/Common.h @@ -12,6 +12,7 @@ extern TSentry Sentry; #include #include #include +#include #include #include @@ -96,6 +97,8 @@ public: static void CheckForUpdates(); static std::array VersionStrToInts(const std::string& str); static bool IsOutdated(const Version& Current, const Version& Newest); + static bool IsShuttingDown(); + static void SleepSafeSeconds(size_t Seconds); static void InitializeConsole() { if (!mConsole) { @@ -121,10 +124,14 @@ public: static void SetSubsystemStatus(const std::string& Subsystem, Status status); private: + static void SetShutdown(bool Val); + static inline SystemStatusMap mSystemStatusMap {}; static inline std::mutex mSystemStatusMapMutex {}; static inline std::string mPPS; static inline std::unique_ptr mConsole; + static inline std::shared_mutex mShutdownMtx {}; + static inline bool mShutdown { false }; static inline std::mutex mShutdownHandlersMutex {}; static inline std::deque mShutdownHandlers {}; diff --git a/include/THeartbeatThread.h b/include/THeartbeatThread.h index 1f0ab14..1063be6 100644 --- a/include/THeartbeatThread.h +++ b/include/THeartbeatThread.h @@ -15,7 +15,6 @@ private: std::string GenerateCall(); std::string GetPlayers(); - bool mShutdown = false; TResourceManager& mResourceManager; TServer& mServer; }; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index f71b9df..2041687 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -187,7 +187,7 @@ private: class StateThreadData : IThreaded { public: - StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine); + StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine); StateThreadData(const StateThreadData&) = delete; ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } [[nodiscard]] std::shared_ptr EnqueueScript(const TLuaChunk& Script); @@ -218,7 +218,6 @@ private: sol::table Lua_FS_ListDirectories(const std::string& Path); std::string mName; - std::atomic_bool& mShutdown; TLuaStateId mStateId; lua_State* mState; std::thread mThread; @@ -247,8 +246,7 @@ private: TNetwork* mNetwork; TServer* mServer; - std::atomic_bool mShutdown { false }; - fs::path mResourceServerPath; + const fs::path mResourceServerPath; std::vector> mLuaPlugins; std::unordered_map> mLuaStates; std::recursive_mutex mLuaStatesMutex; diff --git a/include/TNetwork.h b/include/TNetwork.h index 87e4235..6394306 100644 --- a/include/TNetwork.h +++ b/include/TNetwork.h @@ -32,7 +32,6 @@ private: TServer& mServer; TPPSMonitor& mPPSMonitor; SOCKET mUDPSock {}; - bool mShutdown { false }; TResourceManager& mResourceManager; std::thread mUDPThread; std::thread mTCPThread; diff --git a/include/TPPSMonitor.h b/include/TPPSMonitor.h index 508dfc5..0718f10 100644 --- a/include/TPPSMonitor.h +++ b/include/TPPSMonitor.h @@ -22,6 +22,5 @@ private: TServer& mServer; std::optional> mNetwork { std::nullopt }; - bool mShutdown { false }; int mInternalPPS { 0 }; -}; \ No newline at end of file +}; diff --git a/include/TPluginMonitor.h b/include/TPluginMonitor.h index a617987..2ed77bc 100644 --- a/include/TPluginMonitor.h +++ b/include/TPluginMonitor.h @@ -18,6 +18,5 @@ public: private: std::shared_ptr mEngine; fs::path mPath; - std::atomic_bool mShutdown { false }; std::unordered_map mFileTimes; }; diff --git a/src/Common.cpp b/src/Common.cpp index 11fe8f6..728bbf7 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -25,6 +25,7 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) { } void Application::GracefullyShutdown() { + SetShutdown(true); static bool AlreadyShuttingDown = false; static uint8_t ShutdownAttempts = 0; if (AlreadyShuttingDown) { @@ -93,6 +94,22 @@ bool Application::IsOutdated(const Version& Current, const Version& Newest) { } } +bool Application::IsShuttingDown() { + std::shared_lock Lock(mShutdownMtx); + return mShutdown; +} + +void Application::SleepSafeSeconds(size_t Seconds) { + // Sleeps for 500 ms, checks if a shutdown occurred, and so forth + for (size_t i = 0; i < Seconds * 2; ++i) { + if (Application::IsShuttingDown()) { + return; + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + } +} + TEST_CASE("Application::IsOutdated (version check)") { SUBCASE("Same version") { CHECK(!Application::IsOutdated({ 1, 2, 3 }, { 1, 2, 3 })); @@ -158,6 +175,11 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status mSystemStatusMap[Subsystem] = status; } +void Application::SetShutdown(bool Val) { + std::unique_lock Lock(mShutdownMtx); + mShutdown = Val; +} + TEST_CASE("Application::SetSubsystemStatus") { Application::SetSubsystemStatus("Test", Application::Status::Good); auto Map = Application::GetSubsystemStatuses(); diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 5d3d437..edd0ed2 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -20,7 +20,7 @@ void THeartbeatThread::operator()() { static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now(); bool isAuth = false; size_t UpdateReminderCounter = 0; - while (!mShutdown) { + while (!Application::IsShuttingDown()) { ++UpdateReminderCounter; Body = GenerateCall(); // a hot-change occurs when a setting has changed, to update the backend of that change. @@ -164,7 +164,6 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("Heartbeat", Application::Status::ShuttingDown); if (mThread.joinable()) { - mShutdown = true; mThread.join(); } Application::SetSubsystemStatus("Heartbeat", Application::Status::Shutdown); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 050f416..a4e2d52 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -15,20 +15,18 @@ TLuaEngine* LuaAPI::MP::Engine; -TLuaEngine::TLuaEngine() { +TLuaEngine::TLuaEngine() + : mResourceServerPath(fs::path(Application::Settings.Resource) / "Server") { Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting); LuaAPI::MP::Engine = this; if (!fs::exists(Application::Settings.Resource)) { fs::create_directory(Application::Settings.Resource); } - fs::path Path = fs::path(Application::Settings.Resource) / "Server"; - if (!fs::exists(Path)) { - fs::create_directory(Path); + if (!fs::exists(mResourceServerPath)) { + fs::create_directory(mResourceServerPath); } - mResourceServerPath = Path; Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("LuaEngine", Application::Status::ShuttingDown); - mShutdown = true; if (mThread.joinable()) { mThread.join(); } @@ -59,7 +57,7 @@ void TLuaEngine::operator()() { auto ResultCheckThread = std::thread([&] { RegisterThread("ResultCheckThread"); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::unique_lock Lock(mResultsToCheckMutex); mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20)); if (!mResultsToCheck.empty()) { @@ -79,10 +77,7 @@ void TLuaEngine::operator()() { }); // event loop auto Before = std::chrono::high_resolution_clock::now(); - while (!mShutdown) { - if (mLuaStates.size() == 0) { - std::this_thread::sleep_for(std::chrono::seconds(100)); - } + while (!Application::IsShuttingDown()) { { // Timed Events Scope std::unique_lock Lock(mTimedEventsMutex); for (auto& Timer : mTimedEvents) { @@ -108,12 +103,18 @@ void TLuaEngine::operator()() { } } } - const auto Expected = std::chrono::milliseconds(10); - if (auto Diff = std::chrono::high_resolution_clock::now() - Before; - Diff < Expected) { - std::this_thread::sleep_for(Expected - Diff); + if (mLuaStates.size() == 0) { + beammp_trace("No Lua states, event loop running extremely sparsely"); + Application::SleepSafeSeconds(10); } else { - beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind"); + constexpr double NsFactor = 1000000.0; + constexpr double Expected = 10.0; // ms + const auto Diff = (std::chrono::high_resolution_clock::now() - Before).count() / NsFactor; + if (Diff < Expected) { + std::this_thread::sleep_for(std::chrono::nanoseconds(size_t((Expected - Diff) * NsFactor))); + } else { + beammp_tracef("Event loop cannot keep up! Running {}ms behind", Diff); + } } Before = std::chrono::high_resolution_clock::now(); } @@ -326,7 +327,7 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, std::unique_lock Lock(mLuaStatesMutex); if (mLuaStates.find(StateId) == mLuaStates.end()) { beammp_debug("Creating lua state for state id \"" + StateId + "\""); - auto DataPtr = std::make_unique(Name, mShutdown, StateId, *this); + auto DataPtr = std::make_unique(Name, StateId, *this); mLuaStates[StateId] = std::move(DataPtr); RegisterEvent("onInit", StateId, "onInit"); if (!DontCallOnInit) { @@ -614,9 +615,8 @@ sol::table TLuaEngine::StateThreadData::Lua_JsonDecode(const std::string& str) { return table; } -TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine) +TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine) : mName(Name) - , mShutdown(Shutdown) , mStateId(StateId) , mState(luaL_newstate()) , mEngine(&Engine) { @@ -819,7 +819,7 @@ void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, co void TLuaEngine::StateThreadData::operator()() { RegisterThread("Lua:" + mStateId); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { { // StateExecuteQueue Scope std::unique_lock Lock(mStateExecuteQueueMutex); if (!mStateExecuteQueue.empty()) { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 6c654cd..be9b773 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -25,7 +25,6 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("UDPNetwork", Application::Status::ShuttingDown); if (mUDPThread.joinable()) { - mShutdown = true; mUDPThread.detach(); } Application::SetSubsystemStatus("UDPNetwork", Application::Status::Shutdown); @@ -33,7 +32,6 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("TCPNetwork", Application::Status::ShuttingDown); if (mTCPThread.joinable()) { - mShutdown = true; mTCPThread.detach(); } Application::SetSubsystemStatus("TCPNetwork", Application::Status::Shutdown); @@ -68,7 +66,7 @@ void TNetwork::UDPServerMain() { 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) { + while (!Application::IsShuttingDown()) { try { sockaddr_in client {}; std::string Data = UDPRcvFromClient(client); // Receives any data from Socket @@ -152,7 +150,7 @@ void TNetwork::TCPServerMain() { beammp_info("Vehicle event network online"); do { try { - if (mShutdown) { + if (Application::IsShuttingDown()) { beammp_debug("shutdown during TCP wait for accept loop"); break; } @@ -239,16 +237,16 @@ void TNetwork::HandleDownload(SOCKET TCPSock) { }); } -static int get_ip_str(const struct sockaddr *sa, char *strBuf, size_t strBufSize) { - switch(sa->sa_family) { - case AF_INET: - inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), strBuf, strBufSize); - break; - case AF_INET6: - inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), strBuf, strBufSize); - break; - default: - return 1; +static int get_ip_str(const struct sockaddr* sa, char* strBuf, size_t strBufSize) { + switch (sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), strBuf, strBufSize); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), strBuf, strBufSize); + break; + default: + return 1; } return 0; } diff --git a/src/TPPSMonitor.cpp b/src/TPPSMonitor.cpp index b5b4951..3edd17d 100644 --- a/src/TPPSMonitor.cpp +++ b/src/TPPSMonitor.cpp @@ -10,7 +10,6 @@ TPPSMonitor::TPPSMonitor(TServer& Server) Application::SetSubsystemStatus("PPSMonitor", Application::Status::ShuttingDown); if (mThread.joinable()) { beammp_debug("shutting down PPSMonitor"); - mShutdown = true; mThread.join(); beammp_debug("shut down PPSMonitor"); } @@ -27,7 +26,7 @@ void TPPSMonitor::operator()() { beammp_debug("PPSMonitor starting"); Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good); std::vector> TimedOutClients; - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::this_thread::sleep_for(std::chrono::seconds(1)); int C = 0, V = 0; if (mServer.ClientCount() == 0) { diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index 7c84fec..9553f5d 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -17,7 +17,6 @@ TPluginMonitor::TPluginMonitor(const fs::path& Path, std::shared_ptr } Application::RegisterShutdownHandler([this] { - mShutdown = true; if (mThread.joinable()) { mThread.join(); } @@ -30,7 +29,7 @@ void TPluginMonitor::operator()() { RegisterThread("PluginMonitor"); beammp_info("PluginMonitor started"); Application::SetSubsystemStatus("PluginMonitor", Application::Status::Good); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::vector ToRemove; for (const auto& Pair : mFileTimes) { try { @@ -61,7 +60,7 @@ void TPluginMonitor::operator()() { } catch (const std::exception& e) { ToRemove.push_back(Pair.first); } - for (size_t i = 0; i < 3 && !mShutdown; ++i) { + for (size_t i = 0; i < 3 && !Application::IsShuttingDown(); ++i) { std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/src/main.cpp b/src/main.cpp index f3e23f4..4650570 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,7 +69,7 @@ int main(int argc, char** argv) { Sentry.LogException(e, _file_basename, _line); MainRet = -1; } - return MainRet; + std::exit(MainRet); } int BeamMPServerMain(MainArguments Arguments) {