fix TriggerGlobalEvent not passing event arguments correctly (#307)

This is supposed to close #106 (The code is by @lionkor from
b068a9b48f)
This commit is contained in:
Lion 2024-05-08 11:47:23 +02:00 committed by GitHub
commit 586510041d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 23 deletions

View File

@ -30,6 +30,7 @@
#include <lua.hpp> #include <lua.hpp>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <nlohmann/json.hpp>
#include <queue> #include <queue>
#include <random> #include <random>
#include <set> #include <set>
@ -40,17 +41,29 @@
#define SOL_ALL_SAFETIES_ON 1 #define SOL_ALL_SAFETIES_ON 1
#include <sol/sol.hpp> #include <sol/sol.hpp>
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; using TLuaStateId = std::string;
namespace fs = std::filesystem; namespace fs = std::filesystem;
/** /**
* std::variant means, that TLuaArgTypes may be one of the Types listed as template args * std::variant means, that TLuaArgTypes may be one of the Types listed as template args
*/ */
using TLuaArgTypes = std::variant<std::string, int, sol::variadic_args, bool, std::unordered_map<std::string, std::string>>; using TLuaValue = std::variant<std::string, int, JsonString, bool, std::unordered_map<std::string, std::string>, float>;
static constexpr size_t TLuaArgTypes_String = 0; enum TLuaType {
static constexpr size_t TLuaArgTypes_Int = 1; String = 0,
static constexpr size_t TLuaArgTypes_VariadicArgs = 2; Int = 1,
static constexpr size_t TLuaArgTypes_Bool = 3; Json = 2,
static constexpr size_t TLuaArgTypes_StringStringMap = 4; Bool = 3,
StringStringMap = 4,
Float = 5,
};
class TLuaPlugin; class TLuaPlugin;
@ -98,7 +111,7 @@ public:
struct QueuedFunction { struct QueuedFunction {
std::string FunctionName; std::string FunctionName;
std::shared_ptr<TLuaResult> Result; std::shared_ptr<TLuaResult> Result;
std::vector<TLuaArgTypes> Args; std::vector<TLuaValue> Args;
std::string EventName; // optional, may be empty std::string EventName; // optional, may be empty
}; };
@ -151,7 +164,7 @@ public:
void ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results); void ReportErrors(const std::vector<std::shared_ptr<TLuaResult>>& Results);
bool HasState(TLuaStateId StateId); bool HasState(TLuaStateId StateId);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(TLuaStateId StateID, const TLuaChunk& Script); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(TLuaStateId StateID, const TLuaChunk& Script);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaValue>& Args);
void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false); void EnsureStateExists(TLuaStateId StateId, const std::string& Name, bool DontCallOnInit = false);
void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName); void RegisterEvent(const std::string& EventName, TLuaStateId StateId, const std::string& FunctionName);
/** /**
@ -171,7 +184,7 @@ public:
} }
std::vector<std::shared_ptr<TLuaResult>> Results; std::vector<std::shared_ptr<TLuaResult>> Results;
std::vector<TLuaArgTypes> Arguments { TLuaArgTypes { std::forward<ArgsT>(Args) }... }; std::vector<TLuaValue> Arguments { TLuaValue { std::forward<ArgsT>(Args) }... };
for (const auto& Event : mLuaEvents.at(EventName)) { for (const auto& Event : mLuaEvents.at(EventName)) {
for (const auto& Function : Event.second) { for (const auto& Function : Event.second) {
@ -190,7 +203,7 @@ public:
return {}; return {};
} }
std::vector<std::shared_ptr<TLuaResult>> Results; std::vector<std::shared_ptr<TLuaResult>> Results;
std::vector<TLuaArgTypes> Arguments { TLuaArgTypes { std::forward<ArgsT>(Args) }... }; std::vector<TLuaValue> Arguments { TLuaValue { std::forward<ArgsT>(Args) }... };
const auto Handlers = GetEventHandlersForState(EventName, StateId); const auto Handlers = GetEventHandlersForState(EventName, StateId);
for (const auto& Handler : Handlers) { for (const auto& Handler : Handlers) {
Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments)); Results.push_back(EnqueueFunctionCall(StateId, Handler, Arguments));
@ -227,8 +240,8 @@ private:
StateThreadData(const StateThreadData&) = delete; StateThreadData(const StateThreadData&) = delete;
virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } virtual ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); }
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueScript(const TLuaChunk& Script);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaValue>& Args);
[[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy); [[nodiscard]] std::shared_ptr<TLuaResult> EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaValue>& Args, const std::string& EventName, CallStrategy Strategy);
void RegisterEvent(const std::string& EventName, const std::string& FunctionName); void RegisterEvent(const std::string& EventName, const std::string& FunctionName);
void AddPath(const fs::path& Path); // to be added to path and cpath void AddPath(const fs::path& Path); // to be added to path and cpath
void operator()() override; void operator()() override;
@ -273,6 +286,7 @@ private:
std::recursive_mutex mPathsMutex; std::recursive_mutex mPathsMutex;
std::mt19937 mMersenneTwister; std::mt19937 mMersenneTwister;
std::uniform_real_distribution<double> mUniformRealDistribution01; std::uniform_real_distribution<double> mUniformRealDistribution01;
std::vector<sol::object> JsonStringToArray(JsonString Str);
}; };
struct TimedEvent { struct TimedEvent {

View File

@ -353,7 +353,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::EnqueueScript(TLuaStateId StateID, const
return mLuaStates.at(StateID)->EnqueueScript(Script); return mLuaStates.at(StateID)->EnqueueScript(Script);
} }
std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) { std::shared_ptr<TLuaResult> TLuaEngine::EnqueueFunctionCall(TLuaStateId StateID, const std::string& FunctionName, const std::vector<TLuaValue>& Args) {
std::unique_lock Lock(mLuaStatesMutex); std::unique_lock Lock(mLuaStatesMutex);
return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName, Args); return mLuaStates.at(StateID)->EnqueueFunctionCall(FunctionName, Args);
} }
@ -446,13 +446,50 @@ std::set<std::string> TLuaEngine::GetEventHandlersForState(const std::string& Ev
return mLuaEvents[EventName][StateId]; return mLuaEvents[EventName][StateId];
} }
std::vector<sol::object> TLuaEngine::StateThreadData::JsonStringToArray(JsonString Str) {
auto LocalTable = Lua_JsonDecode(Str.value).as<std::vector<sol::object>>();
for (auto& value : LocalTable) {
if (value.is<std::string>() && value.as<std::string>() == BEAMMP_INTERNAL_NIL) {
value = sol::object {};
}
}
return LocalTable;
}
sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string& EventName, sol::variadic_args EventArgs) { 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 <nil> 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); auto MyHandlers = mEngine->GetEventHandlersForState(EventName, mStateId);
sol::variadic_results LocalArgs = JsonStringToArray(Str);
for (const auto& Handler : MyHandlers) { for (const auto& Handler : MyHandlers) {
auto Fn = mStateView[Handler]; auto Fn = mStateView[Handler];
if (Fn.valid()) { if (Fn.valid()) {
auto LuaResult = Fn(EventArgs); auto LuaResult = Fn(LocalArgs);
auto Result = std::make_shared<TLuaResult>(); auto Result = std::make_shared<TLuaResult>();
if (LuaResult.valid()) { if (LuaResult.valid()) {
Result->Error = false; Result->Error = false;
@ -674,6 +711,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) { static void JsonDecodeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) {
switch (right.type()) { switch (right.type()) {
case nlohmann::detail::value_t::null: case nlohmann::detail::value_t::null:
AddToTable(table, left, sol::lua_nil_t {});
return; return;
case nlohmann::detail::value_t::object: { case nlohmann::detail::value_t::object: {
auto value = table.create(); auto value = table.create();
@ -968,7 +1006,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueScript(const TLu
return Result; return Result;
} }
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args, const std::string& EventName, CallStrategy Strategy) { std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFromCustomEvent(const std::string& FunctionName, const std::vector<TLuaValue>& Args, const std::string& EventName, CallStrategy Strategy) {
// TODO: Document all this // TODO: Document all this
decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end(); decltype(mStateFunctionQueue)::iterator Iter = mStateFunctionQueue.end();
if (Strategy == CallStrategy::BestEffort) { if (Strategy == CallStrategy::BestEffort) {
@ -990,7 +1028,7 @@ std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCallFrom
} }
} }
std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaArgTypes>& Args) { std::shared_ptr<TLuaResult> TLuaEngine::StateThreadData::EnqueueFunctionCall(const std::string& FunctionName, const std::vector<TLuaValue>& Args) {
auto Result = std::make_shared<TLuaResult>(); auto Result = std::make_shared<TLuaResult>();
Result->StateId = mStateId; Result->StateId = mStateId;
Result->Function = FunctionName; Result->Function = FunctionName;
@ -1076,19 +1114,21 @@ void TLuaEngine::StateThreadData::operator()() {
continue; continue;
} }
switch (Arg.index()) { switch (Arg.index()) {
case TLuaArgTypes_String: case TLuaType::String:
LuaArgs.push_back(sol::make_object(StateView, std::get<std::string>(Arg))); LuaArgs.push_back(sol::make_object(StateView, std::get<std::string>(Arg)));
break; break;
case TLuaArgTypes_Int: case TLuaType::Int:
LuaArgs.push_back(sol::make_object(StateView, std::get<int>(Arg))); LuaArgs.push_back(sol::make_object(StateView, std::get<int>(Arg)));
break; break;
case TLuaArgTypes_VariadicArgs: case TLuaType::Json: {
LuaArgs.push_back(sol::make_object(StateView, std::get<sol::variadic_args>(Arg))); auto LocalArgs = JsonStringToArray(std::get<JsonString>(Arg));
LuaArgs.insert(LuaArgs.end(), LocalArgs.begin(), LocalArgs.end());
break; break;
case TLuaArgTypes_Bool: }
case TLuaType::Bool:
LuaArgs.push_back(sol::make_object(StateView, std::get<bool>(Arg))); LuaArgs.push_back(sol::make_object(StateView, std::get<bool>(Arg)));
break; break;
case TLuaArgTypes_StringStringMap: { case TLuaType::StringStringMap: {
auto Map = std::get<std::unordered_map<std::string, std::string>>(Arg); auto Map = std::get<std::unordered_map<std::string, std::string>>(Arg);
auto Table = StateView.create_table(); auto Table = StateView.create_table();
for (const auto& [k, v] : Map) { for (const auto& [k, v] : Map) {