From d81087b5af826c6621656e6ab96858c2bd3a3701 Mon Sep 17 00:00:00 2001 From: Neptnium <46964341+Neptnium@users.noreply.github.com> Date: Thu, 18 Apr 2024 18:43:04 +0200 Subject: [PATCH] fix TriggerGlobalEvent not passing event arguments correctly (closes #106) --- include/TLuaEngine.h | 38 ++++++++++++++++++--------- src/TLuaEngine.cpp | 62 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 23 deletions(-) diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index b4c14b0..b5865bc 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -38,17 +39,29 @@ #define SOL_ALL_SAFETIES_ON 1 #include +struct JsonString { + std::string value; +}; + +// value used to keep nils in a table or array, across serialization boundaries like +// JsonEncode, so that the nil stays at the same index and isn't treated like a special +// value (e.g. one that can be ignored or discarded). +const inline std::string BEAMMP_INTERNAL_NIL = "BEAMMP_SERVER_INTERNAL_NIL_VALUE"; + using TLuaStateId = std::string; namespace fs = std::filesystem; /** * std::variant means, that TLuaArgTypes may be one of the Types listed as template args */ -using TLuaArgTypes = std::variant>; -static constexpr size_t TLuaArgTypes_String = 0; -static constexpr size_t TLuaArgTypes_Int = 1; -static constexpr size_t TLuaArgTypes_VariadicArgs = 2; -static constexpr size_t TLuaArgTypes_Bool = 3; -static constexpr size_t TLuaArgTypes_StringStringMap = 4; +using TLuaValue = std::variant, float>; +enum TLuaType { + String = 0, + Int = 1, + Json = 2, + Bool = 3, + StringStringMap = 4, + Float = 5, +}; class TLuaPlugin; @@ -96,7 +109,7 @@ public: struct QueuedFunction { std::string FunctionName; std::shared_ptr Result; - std::vector Args; + std::vector Args; std::string EventName; // optional, may be empty }; @@ -149,7 +162,7 @@ public: void ReportErrors(const std::vector>& Results); bool HasState(TLuaStateId StateId); [[nodiscard]] std::shared_ptr EnqueueScript(TLuaStateId StateID, const TLuaChunk& Script); - [[nodiscard]] std::shared_ptr EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector& Args); + [[nodiscard]] std::shared_ptr EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector& Args); void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false); void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName); /** @@ -169,7 +182,7 @@ public: } std::vector> Results; - std::vector Arguments { TLuaArgTypes { std::forward(Args) }... }; + std::vector Arguments { TLuaValue { std::forward(Args) }... }; for (const auto& Event : mLuaEvents.at(EventName)) { for (const auto& Function : Event.second) { @@ -188,7 +201,7 @@ public: return {}; } std::vector> Results; - std::vector Arguments { TLuaArgTypes { std::forward(Args) }... }; + std::vector Arguments { TLuaValue { std::forward(Args) }... }; const auto Handlers = GetEventHandlersForState(EventName, StateId); for (const auto& Handler : Handlers) { Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments)); @@ -225,8 +238,8 @@ private: StateThreadData(const StateThreadData&) = delete; virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } [[nodiscard]] std::shared_ptr EnqueueScript(const TLuaChunk& Script); - [[nodiscard]] std::shared_ptr EnqueueFunctionCall(const std::string& FunctionName, const std::vector& Args); - [[nodiscard]] std::shared_ptr EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector& Args, const std::string& EventName, CallStrategy Strategy); + [[nodiscard]] std::shared_ptr EnqueueFunctionCall(const std::string& FunctionName, const std::vector& Args); + [[nodiscard]] std::shared_ptr EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector& Args, const std::string& EventName, CallStrategy Strategy); void RegisterEvent(const std::string& EventName, const std::string& FunctionName); void AddPath(const fs::path& Path); // to be added to path and cpath void operator()() override; @@ -268,6 +281,7 @@ private: std::recursive_mutex mPathsMutex; std::mt19937 mMersenneTwister; std::uniform_real_distribution mUniformRealDistribution01; + std::vector JsonStringToArray(JsonString Str); }; struct TimedEvent { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 5dff14e..35e6f60 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -350,7 +350,7 @@ std::shared_ptr TLuaEngine::EnqueueScript(TLuaStateId StateID, const return mLuaStates.at(StateID)->EnqueueScript(Script); } -std::shared_ptr TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector& Args) { +std::shared_ptr TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector& Args) { std::unique_lock Lock(mLuaStatesMutex); return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName, Args); } @@ -430,13 +430,50 @@ std::set TLuaEngine::GetEventHandlersForState(const std::string& Ev return mLuaEvents[EventName][StateId]; } +std::vector TLuaEngine::StateThreadData::JsonStringToArray(JsonString Str) { + auto LocalTable = Lua_JsonDecode(Str.value).as>(); + for (auto& value : LocalTable) { + if (value.is() && value.as() == BEAMMP_INTERNAL_NIL) { + value = sol::object {}; + } + } + return LocalTable; +} + sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) { - auto Return = mEngine->TriggerEvent(EventName, mStateId, EventArgs); + auto Table = mStateView.create_table(); + for (const sol::stack_proxy& Arg : EventArgs) { + switch (Arg.get_type()) { + case sol::type::none: + case sol::type::userdata: + case sol::type::lightuserdata: + case sol::type::thread: + case sol::type::function: + case sol::type::poly: + Table.add(BEAMMP_INTERNAL_NIL); + beammp_warnf("Passed a value of type '{}' to TriggerGlobalEvent(\"{}\", ...). This type can not be serialized, and cannot be passed between states. It will arrive as in handlers.", sol::type_name(EventArgs.lua_state(), Arg.get_type()), EventName); + break; + case sol::type::lua_nil: + Table.add(BEAMMP_INTERNAL_NIL); + break; + case sol::type::string: + case sol::type::number: + case sol::type::boolean: + case sol::type::table: + Table.add(Arg); + break; + } + } + JsonString Str { LuaAPI::MP::JsonEncode(Table) }; + beammp_debugf("json: {}", Str.value); + auto Return = mEngine->TriggerEvent(EventName, mStateId, Str); auto MyHandlers = mEngine->GetEventHandlersForState(EventName, mStateId); + + sol::variadic_results LocalArgs = JsonStringToArray(Str); for (const auto& Handler : MyHandlers) { auto Fn = mStateView[Handler]; if (Fn.valid()) { - auto LuaResult = Fn(EventArgs); + auto LuaResult = Fn(LocalArgs); auto Result = std::make_shared(); if (LuaResult.valid()) { Result->Error = false; @@ -658,6 +695,7 @@ static void AddToTable(sol::table& table, const std::string& left, const T& valu static void JsonDecodeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) { switch (right.type()) { case nlohmann::detail::value_t::null: + AddToTable(table, left, sol::lua_nil_t {}); return; case nlohmann::detail::value_t::object: { auto value = table.create(); @@ -894,7 +932,7 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueScript(const TLu return Result; } -std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector& Args, const std::string& EventName, CallStrategy Strategy) { +std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector& Args, const std::string& EventName, CallStrategy Strategy) { // TODO: Document all this decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end(); if (Strategy == CallStrategy::BestEffort) { @@ -916,7 +954,7 @@ std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCallFrom } } -std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector& Args) { +std::shared_ptr TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector& Args) { auto Result = std::make_shared(); Result->StateId = mStateId; Result->Function = FunctionName; @@ -1001,19 +1039,21 @@ void TLuaEngine::StateThreadData::operator()() { continue; } switch (Arg.index()) { - case TLuaArgTypes_String: + case TLuaType::String: LuaArgs.push_back(sol::make_object(StateView, std::get(Arg))); break; - case TLuaArgTypes_Int: + case TLuaType::Int: LuaArgs.push_back(sol::make_object(StateView, std::get(Arg))); break; - case TLuaArgTypes_VariadicArgs: - LuaArgs.push_back(sol::make_object(StateView, std::get(Arg))); + case TLuaType::Json: { + auto LocalArgs = JsonStringToArray(std::get(Arg)); + LuaArgs.insert(LuaArgs.end(), LocalArgs.begin(), LocalArgs.end()); break; - case TLuaArgTypes_Bool: + } + case TLuaType::Bool: LuaArgs.push_back(sol::make_object(StateView, std::get(Arg))); break; - case TLuaArgTypes_StringStringMap: { + case TLuaType::StringStringMap: { auto Map = std::get>(Arg); auto Table = StateView.create_table(); for (const auto& [k, v] : Map) {