diff --git a/include/LuaAPI.h b/include/LuaAPI.h index f7530d0..b25c740 100644 --- a/include/LuaAPI.h +++ b/include/LuaAPI.h @@ -22,7 +22,7 @@ namespace MP { bool IsPlayerConnected(int ID); void Sleep(size_t Ms); void PrintRaw(sol::variadic_args); - std::string JsonSerialize(sol::table object); + std::string JsonSerialize(const sol::table& object); } namespace FS { std::pair CreateDirectory(const std::string& Path); diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 720d8ea..fb1353e 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -190,6 +190,7 @@ private: std::string Lua_GetPlayerName(int ID); sol::table Lua_GetPlayerVehicles(int ID); sol::table Lua_HttpCreateConnection(const std::string& host, uint16_t port); + sol::table Lua_JsonDeserialize(const std::string& str); int Lua_GetPlayerIDByName(const std::string& Name); std::string mName; diff --git a/src/LuaAPI.cpp b/src/LuaAPI.cpp index 5e60a16..53f28ad 100644 --- a/src/LuaAPI.cpp +++ b/src/LuaAPI.cpp @@ -354,7 +354,11 @@ std::string LuaAPI::FS::ConcatPaths(sol::variadic_args Args) { return Result; } -static void JsonSerializeRecursive(nlohmann::json& json, sol::object left, sol::object right) { +static void JsonSerializeRecursive(nlohmann::json& json, const sol::object& left, const sol::object& right, size_t depth = 0) { + if (depth > 100) { + beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path"); + return; + } std::string key; bool is_array = false; switch (left.get_type()) { @@ -384,31 +388,31 @@ static void JsonSerializeRecursive(nlohmann::json& json, sol::object left, sol:: return; case sol::type::poly: beammp_lua_warn("unsure what to do with poly type in JsonSerialize, ignoring"); - break; + return; case sol::type::boolean: value = right.as(); break; case sol::type::lightuserdata: beammp_lua_warn("unsure what to do with lightuserdata in JsonSerialize, ignoring"); - break; + return; case sol::type::userdata: beammp_lua_warn("unsure what to do with userdata in JsonSerialize, ignoring"); - break; + return; case sol::type::thread: beammp_lua_warn("unsure what to do with thread in JsonSerialize, ignoring"); - break; + return; case sol::type::string: value = right.as(); break; case sol::type::number: - value = right.as(); + value = right.as(); break; case sol::type::function: beammp_lua_warn("unsure what to do with function in JsonSerialize, ignoring"); - break; + return; case sol::type::table: for (const auto& pair : right.as()) { - JsonSerializeRecursive(value, pair.first, pair.second); + JsonSerializeRecursive(value, pair.first, pair.second, depth + 1); } break; } @@ -419,7 +423,7 @@ static void JsonSerializeRecursive(nlohmann::json& json, sol::object left, sol:: } } -std::string LuaAPI::MP::JsonSerialize(sol::table object) { +std::string LuaAPI::MP::JsonSerialize(const sol::table& object) { nlohmann::json json; // table for (const auto& entry : object) { diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 3f1ec58..5212e0e 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -473,6 +474,80 @@ sol::table TLuaEngine::StateThreadData::Lua_HttpCreateConnection(const std::stri return table; } +template +static void AddToTable(sol::table& table, const std::string& left, const T& value) { + if (left.empty()) { + table[table.size() + 1] = value; + } else { + table[left] = value; + } +} + +static void JsonDeserializeRecursive(sol::state_view& StateView, sol::table& table, const std::string& left, const nlohmann::json& right) { + switch (right.type()) { + case nlohmann::detail::value_t::null: + return; + case nlohmann::detail::value_t::object: { + auto value = table.create(); + value.clear(); + for (const auto& entry : right.items()) { + JsonDeserializeRecursive(StateView, value, entry.key(), entry.value()); + } + AddToTable(table, left, value); + break; + } + case nlohmann::detail::value_t::array: { + auto value = table.create(); + value.clear(); + for (const auto& entry : right.items()) { + JsonDeserializeRecursive(StateView, value, "", entry.value()); + } + AddToTable(table, left, value); + break; + } + case nlohmann::detail::value_t::string: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::boolean: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::number_integer: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::number_unsigned: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::number_float: + AddToTable(table, left, right.get()); + break; + case nlohmann::detail::value_t::binary: + beammp_lua_error("JsonDeserialize can't handle binary blob in json, ignoring"); + return; + case nlohmann::detail::value_t::discarded: + return; + } +} + +sol::table TLuaEngine::StateThreadData::Lua_JsonDeserialize(const std::string& str) { + sol::state_view StateView(mState); + auto table = StateView.create_table(); + // TODO: try / catch + nlohmann::json json = nlohmann::json::parse(str); + if (json.is_object()) { + for (const auto& entry : json.items()) { + JsonDeserializeRecursive(StateView, table, entry.key(), entry.value()); + } + } else if (json.is_array()) { + for (const auto& entry : json) { + JsonDeserializeRecursive(StateView, table, "", entry); + } + } else { + beammp_lua_error("JsonDeserialize expected array or object json, instead got " + std::string(json.type_name())); + return sol::nil; + } + return table; +} + TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine) : mName(Name) , mShutdown(Shutdown) @@ -557,12 +632,14 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi }); MPTable.set_function("Set", &LuaAPI::MP::Set); MPTable.set_function("JsonSerialize", &LuaAPI::MP::JsonSerialize); - + MPTable.set_function("JsonDeserialize", [this](const std::string& str) { + return Lua_JsonDeserialize(str); + }); + auto HttpTable = StateView.create_named_table("Http"); HttpTable.set_function("CreateConnection", [this](const std::string& host, uint16_t port) { return Lua_HttpCreateConnection(host, port); }); - MPTable.create_named("Settings", "Debug", 0,