diff --git a/deps/cpp-httplib b/deps/cpp-httplib index 301faa0..b324921 160000 --- a/deps/cpp-httplib +++ b/deps/cpp-httplib @@ -1 +1 @@ -Subproject commit 301faa074c4a0fa1dbe470dfb4f77912caa1c57f +Subproject commit b324921c1aeff2976544128e4bb2a0979a4aa595 diff --git a/include/TConsole.h b/include/TConsole.h index c393842..6edcbe2 100644 --- a/include/TConsole.h +++ b/include/TConsole.h @@ -21,6 +21,7 @@ private: void RunAsCommand(const std::string& cmd, bool IgnoreNotACommand = false); void ChangeToLuaConsole(const std::string& LuaStateId); void ChangeToRegularConsole(); + void HandleLuaInternalCommand(const std::string& cmd); void Command_Lua(const std::string& cmd); void Command_Help(const std::string& cmd); diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 1673c61..897a653 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -32,8 +32,8 @@ static constexpr size_t TLuaArgTypes_Bool = 3; class TLuaPlugin; struct TLuaResult { - std::atomic_bool Ready; - std::atomic_bool Error; + bool Ready; + bool Error; std::string ErrorMessage; sol::object Result { sol::lua_nil }; TLuaStateId StateId; @@ -89,6 +89,7 @@ public: std::unique_lock Lock(mResultsToCheckMutex); return mResultsToCheck.size(); } + size_t GetLuaStateCount() { std::unique_lock Lock(mLuaStatesMutex); return mLuaStates.size(); @@ -152,6 +153,12 @@ public: static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; + // Debugging functions (slow) + std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); + std::queue>> Debug_GetStateExecuteQueueForState(TLuaStateId StateId); + std::queue, std::vector>> Debug_GetStateFunctionQueueForState(TLuaStateId StateId); + std::vector Debug_GetResultsToCheckForState(TLuaStateId StateId); + private: void CollectAndInitPlugins(); void InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config); @@ -170,6 +177,10 @@ private: void operator()() override; sol::state_view State() { return sol::state_view(mState); } + // Debug functions, slow + std::queue>> Debug_GetStateExecuteQueue(); + std::queue, std::vector>> Debug_GetStateFunctionQueue(); + private: sol::table Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs); sol::table Lua_TriggerLocalEvent(const std::string& EventName, sol::variadic_args EventArgs); diff --git a/src/TConsole.cpp b/src/TConsole.cpp index 79df8d2..83d73cf 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -107,10 +107,10 @@ void TConsole::ChangeToLuaConsole(const std::string& LuaStateId) { mStateId = LuaStateId; mIsLuaConsole = true; if (mStateId != mDefaultStateId) { - Application::Console().WriteRaw("Entered Lua console for state '" + mStateId + "'. To exit, type `exit()`"); + Application::Console().WriteRaw("Attached to Lua state '" + mStateId + "'. For help, type `:help`. To detach, type `:detach`"); mCommandline.set_prompt("lua @" + LuaStateId + "> "); } else { - Application::Console().WriteRaw("Entered Lua console. To exit, type `exit()`"); + Application::Console().WriteRaw("Attached to Lua. For help, type `:help`. To detach, type `:detach`"); mCommandline.set_prompt("lua> "); } mCachedRegularHistory = mCommandline.history(); @@ -122,9 +122,9 @@ void TConsole::ChangeToRegularConsole() { if (mIsLuaConsole) { mIsLuaConsole = false; if (mStateId != mDefaultStateId) { - Application::Console().WriteRaw("Left Lua console for state '" + mStateId + "'."); + Application::Console().WriteRaw("Detached from Lua state '" + mStateId + "'."); } else { - Application::Console().WriteRaw("Left Lua console."); + Application::Console().WriteRaw("Detached from Lua."); } mCachedLuaHistory = mCommandline.history(); mCommandline.set_history(mCachedRegularHistory); @@ -364,6 +364,58 @@ void TConsole::RunAsCommand(const std::string& cmd, bool IgnoreNotACommand) { } } +void TConsole::HandleLuaInternalCommand(const std::string& cmd) { + if (cmd == "detach") { + ChangeToRegularConsole(); + } else if (cmd == "queued") { + auto QueuedFunctions = LuaAPI::MP::Engine->Debug_GetStateFunctionQueueForState(mStateId); + Application::Console().WriteRaw("Pending functions in State '" + mStateId + "'"); + std::unordered_map FunctionsCount; + std::vector FunctionsInOrder; + while (!QueuedFunctions.empty()) { + auto Tuple = QueuedFunctions.front(); + QueuedFunctions.pop(); + FunctionsInOrder.push_back(std::get<0>(Tuple)); + FunctionsCount[std::get<0>(Tuple)] += 1; + } + std::set Uniques; + for (const auto& Function : FunctionsInOrder) { + if (Uniques.count(Function) == 0) { + Uniques.insert(Function); + if (FunctionsCount.at(Function) > 1) { + Application::Console().WriteRaw(" " + Function + " (" + std::to_string(FunctionsCount.at(Function)) + "x)"); + } else { + Application::Console().WriteRaw(" " + Function); + } + } + } + Application::Console().WriteRaw("Executed functions waiting to be checked in State '" + mStateId + "'"); + for (const auto& Function : LuaAPI::MP::Engine->Debug_GetResultsToCheckForState(mStateId)) { + Application::Console().WriteRaw(" '" + Function.Function + "' (Ready? " + (Function.Ready ? "Yes" : "No") + ", Error? " + (Function.Error ? "Yes: '" + Function.ErrorMessage + "'" : "No") + ")"); + } + } else if (cmd == "events") { + auto Events = LuaAPI::MP::Engine->Debug_GetEventsForState(mStateId); + Application::Console().WriteRaw("Registered Events + Handlers for State '" + mStateId + "'"); + for (const auto& EventHandlerPair : Events) { + Application::Console().WriteRaw(" Event '" + EventHandlerPair.first + "'"); + for (const auto& Handler : EventHandlerPair.second) { + Application::Console().WriteRaw(" " + Handler); + } + } + } else if (cmd == "help") { + Application::Console().WriteRaw(R"(BeamMP Lua Debugger + All commands must be prefixed with a `:`. Non-prefixed commands are interpreted as Lua. + +Commands + :detach detaches (exits) from this Lua console + :help displays this help + :events shows a list of currently registered events + :queued shows a list of all pending and queued functions)"); + } else { + beammp_error("internal command '" + cmd + "' is not known"); + } +} + TConsole::TConsole() { mCommandline.enable_history(); mCommandline.set_history_limit(20); @@ -381,8 +433,8 @@ TConsole::TConsole() { if (mIsLuaConsole) { if (!mLuaEngine) { beammp_info("Lua not started yet, please try again in a second"); - } else if (cmd == "exit()") { - ChangeToRegularConsole(); + } else if (!cmd.empty() && cmd.at(0) == ':') { + HandleLuaInternalCommand(cmd.substr(1)); } else { auto Future = mLuaEngine->EnqueueScript(mStateId, { std::make_shared(cmd), "", "" }); while (!Future->Ready) { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 89742e2..a81a1b0 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -149,6 +149,50 @@ TLuaStateId TLuaEngine::GetStateIDForPlugin(const fs::path& PluginPath) { return ""; } +std::unordered_map /* handlers */> TLuaEngine::Debug_GetEventsForState(TLuaStateId StateId) { + std::unordered_map> Result; + std::unique_lock Lock(mLuaEventsMutex); + for (const auto& EventNameToEventMap : mLuaEvents) { + for (const auto& IdSetOfHandlersPair : EventNameToEventMap.second) { + if (IdSetOfHandlersPair.first == StateId) { + for (const auto& Handler : IdSetOfHandlersPair.second) { + Result[EventNameToEventMap.first].push_back(Handler); + } + } + } + } + return Result; +} + +std::queue>> TLuaEngine::Debug_GetStateExecuteQueueForState(TLuaStateId StateId) { + std::queue>> Result; + std::unique_lock Lock(mLuaStatesMutex); + Result = mLuaStates.at(StateId)->Debug_GetStateExecuteQueue(); + return Result; +} + +std::queue, std::vector>> TLuaEngine::Debug_GetStateFunctionQueueForState(TLuaStateId StateId) { + std::queue, std::vector>> Result; + std::unique_lock Lock(mLuaStatesMutex); + Result = mLuaStates.at(StateId)->Debug_GetStateFunctionQueue(); + return Result; +} + +std::vector TLuaEngine::Debug_GetResultsToCheckForState(TLuaStateId StateId) { + std::unique_lock Lock(mResultsToCheckMutex); + auto ResultsToCheckCopy = mResultsToCheck; + Lock.unlock(); + std::vector Result; + while (!ResultsToCheckCopy.empty()) { + auto ResultToCheck = std::move(ResultsToCheckCopy.front()); + ResultsToCheckCopy.pop(); + if (ResultToCheck->StateId == StateId) { + Result.push_back(*ResultToCheck); + } + } + return Result; +} + void TLuaEngine::WaitForAll(std::vector>& Results, const std::optional& Max) { for (const auto& Result : Results) { bool Cancelled = false; @@ -671,6 +715,16 @@ void TLuaEngine::StateThreadData::operator()() { } } +std::queue>> TLuaEngine::StateThreadData::Debug_GetStateExecuteQueue() { + std::unique_lock Lock(mStateExecuteQueueMutex); + return mStateExecuteQueue; +} + +std::queue, std::vector>> TLuaEngine::StateThreadData::Debug_GetStateFunctionQueue() { + std::unique_lock Lock(mStateFunctionQueueMutex); + return mStateFunctionQueue; +} + void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS) { std::unique_lock Lock(mTimedEventsMutex); TimedEvent Event {