diff --git a/doc/lua_api.txt b/doc/lua_api.txt index bd465bbed..21cbcb822 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1458,6 +1458,12 @@ minetest.get_content_id(name) -> integer ^ Gets the internal content ID of name minetest.get_name_from_content_id(content_id) -> string ^ Gets the name of the content with that content ID +minetest.parse_json(string[, nullvalue]) -> something +^ Convert a string containing JSON data into the Lua equivalent +^ nullvalue: returned in place of the JSON null; defaults to nil +^ On success returns a table, a string, a number, a boolean or nullvalue +^ On failure outputs an error message and returns nil +^ Example: parse_json("[10, {\"a\":false}]") -> {[1] = 10, [2] = {a = false}} minetest.serialize(table) -> string ^ Convert a table containing tables, strings, numbers, booleans and nils into string form readable by minetest.deserialize diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index 014e689dc..d4bc093b0 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -174,6 +174,8 @@ engine.gettext(string) -> string fgettext(string, ...) -> string ^ call engine.gettext(string), replace "$1"..."$9" with the given ^ extra arguments, call engine.formspec_escape and return the result +engine.parse_json(string[, nullvalue]) -> something +^ see minetest.parse_json (lua_api.txt) dump(obj, dumped={}) ^ Return object serialized as a string string:split(separator) diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 2e26adb76..3a3a70860 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" #include "serverobject.h" #include "mapgen.h" +#include "json/json.h" struct EnumString es_TileAnimationType[] = { @@ -998,3 +999,84 @@ bool read_schematic(lua_State *L, int index, DecoSchematic *dschem, Server *serv return true; } + +/******************************************************************************/ +// Returns depth of json value tree +static int push_json_value_getdepth(const Json::Value &value) +{ + if (!value.isArray() && !value.isObject()) + return 1; + + int maxdepth = 0; + for (Json::Value::const_iterator it = value.begin(); + it != value.end(); ++it) { + int elemdepth = push_json_value_getdepth(*it); + if (elemdepth > maxdepth) + maxdepth = elemdepth; + } + return maxdepth + 1; +} +// Recursive function to convert JSON --> Lua table +static bool push_json_value_helper(lua_State *L, const Json::Value &value, + int nullindex) +{ + switch(value.type()) { + case Json::nullValue: + default: + lua_pushvalue(L, nullindex); + break; + case Json::intValue: + lua_pushinteger(L, value.asInt()); + break; + case Json::uintValue: + lua_pushinteger(L, value.asUInt()); + break; + case Json::realValue: + lua_pushnumber(L, value.asDouble()); + break; + case Json::stringValue: + { + const char *str = value.asCString(); + lua_pushstring(L, str ? str : ""); + } + break; + case Json::booleanValue: + lua_pushboolean(L, value.asInt()); + break; + case Json::arrayValue: + lua_newtable(L); + for (Json::Value::const_iterator it = value.begin(); + it != value.end(); ++it) { + push_json_value_helper(L, *it, nullindex); + lua_rawseti(L, -2, it.index() + 1); + } + break; + case Json::objectValue: + lua_newtable(L); + for (Json::Value::const_iterator it = value.begin(); + it != value.end(); ++it) { + const char *str = it.memberName(); + lua_pushstring(L, str ? str : ""); + push_json_value_helper(L, *it, nullindex); + lua_rawset(L, -3); + } + break; + } + return true; +} +// converts JSON --> Lua table; returns false if lua stack limit exceeded +// nullindex: Lua stack index of value to use in place of JSON null +bool push_json_value(lua_State *L, const Json::Value &value, int nullindex) +{ + if(nullindex < 0) + nullindex = lua_gettop(L) + 1 + nullindex; + + int depth = push_json_value_getdepth(value); + + // The maximum number of Lua stack slots used at each recursion level + // of push_json_value_helper is 2, so make sure there a depth * 2 slots + if (lua_checkstack(L, depth * 2)) + return push_json_value_helper(L, value, nullindex); + else + return false; +} diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 6d1dfe1d5..27019e29e 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -39,6 +39,8 @@ extern "C" { #include "irrlichttypes_bloated.h" #include "util/string.h" +namespace Json { class Value; } + struct MapNode; class INodeDefManager; struct PointedThing; @@ -145,6 +147,10 @@ bool read_schematic (lua_State *L, int index, void luaentity_get (lua_State *L,u16 id); +bool push_json_value (lua_State *L, + const Json::Value &value, + int nullindex); + extern struct EnumString es_TileAnimationType[]; #endif /* C_CONTENT_H_ */ diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 0e4de9eee..30fa56c42 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" #include "settings.h" #include "main.h" //required for g_settings, g_settings_path +#include "json/json.h" // debug(...) // Writes a line to dstream @@ -138,6 +139,45 @@ int ModApiUtil::l_setting_save(lua_State *L) return 0; } +// parse_json(str[, nullvalue]) +int ModApiUtil::l_parse_json(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + const char *jsonstr = luaL_checkstring(L, 1); + + // Use passed nullvalue or default to nil + int nullindex = 2; + if (lua_isnone(L, nullindex)) { + lua_pushnil(L); + nullindex = lua_gettop(L); + } + + Json::Value root; + + { + Json::Reader reader; + std::istringstream stream(jsonstr); + + if (!reader.parse(stream, root)) { + errorstream << "Failed to parse json data " + << reader.getFormattedErrorMessages(); + errorstream << "data: \"" << jsonstr << "\"" + << std::endl; + lua_pushnil(L); + return 1; + } + } + + if (!push_json_value(L, root, nullindex)) { + errorstream << "Failed to parse json data, " + << "depth exceeds lua stack limit" << std::endl; + errorstream << "data: \"" << jsonstr << "\"" << std::endl; + lua_pushnil(L); + } + return 1; +} + // get_dig_params(groups, tool_capabilities[, time_from_last_punch]) int ModApiUtil::l_get_dig_params(lua_State *L) { @@ -191,6 +231,8 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(setting_getbool); API_FCT(setting_save); + API_FCT(parse_json); + API_FCT(get_dig_params); API_FCT(get_hit_params); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index b102e315b..71c55b342 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -59,6 +59,9 @@ private: // setting_save() static int l_setting_save(lua_State *L); + // parse_json(str[, nullvalue]) + static int l_parse_json(lua_State *L); + // get_dig_params(groups, tool_capabilities[, time_from_last_punch]) static int l_get_dig_params(lua_State *L);