diff options
Diffstat (limited to 'src/script')
55 files changed, 4146 insertions, 1297 deletions
diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 491c05a1e..5ef672ca9 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -11,9 +11,10 @@ set(common_SCRIPT_SRCS PARENT_SCOPE) # Used by client only -set(minetest_SCRIPT_SRCS +set(client_SCRIPT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp - ${minetest_SCRIPT_COMMON_SRCS} - ${minetest_SCRIPT_CPP_API_SRCS} - ${minetest_SCRIPT_LUA_API_SRCS} + ${client_SCRIPT_COMMON_SRCS} + ${client_SCRIPT_CPP_API_SRCS} + ${client_SCRIPT_LUA_API_SRCS} PARENT_SCOPE) + diff --git a/src/script/common/CMakeLists.txt b/src/script/common/CMakeLists.txt index 27e2fb4d5..4a8e6bab5 100644 --- a/src/script/common/CMakeLists.txt +++ b/src/script/common/CMakeLists.txt @@ -1,4 +1,3 @@ -# Used by server and client set(common_SCRIPT_COMMON_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/c_content.cpp ${CMAKE_CURRENT_SOURCE_DIR}/c_converter.cpp @@ -6,6 +5,6 @@ set(common_SCRIPT_COMMON_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/c_internal.cpp PARENT_SCOPE) -# Used by client only -set(minetest_SCRIPT_COMMON_SRCS +set(client_SCRIPT_COMMON_SRCS PARENT_SCOPE) + diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index ff9aee8ed..3754fc2ff 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -162,18 +162,13 @@ void read_object_properties(lua_State *L, int index, lua_pop(L, 1); lua_getfield(L, -1, "colors"); - if(lua_istable(L, -1)){ - prop->colors.clear(); + if (lua_istable(L, -1)) { int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - if(lua_isstring(L, -1)) - prop->colors.push_back(readARGB8(L, -1)); - else - prop->colors.push_back(video::SColor(255, 255, 255, 255)); - // removes value, keeps key for next iteration - lua_pop(L, 1); + prop->colors.clear(); + for (lua_pushnil(L); lua_next(L, table); lua_pop(L, 1)) { + video::SColor color(255, 255, 255, 255); + read_color(L, -1, &color); + prop->colors.push_back(color); } } lua_pop(L, 1); @@ -205,17 +200,78 @@ void read_object_properties(lua_State *L, int index, } /******************************************************************************/ -TileDef read_tiledef(lua_State *L, int index) +void push_object_properties(lua_State *L, ObjectProperties *prop) +{ + lua_newtable(L); + lua_pushnumber(L, prop->hp_max); + lua_setfield(L, -2, "hp_max"); + lua_pushboolean(L, prop->physical); + lua_setfield(L, -2, "physical"); + lua_pushboolean(L, prop->collideWithObjects); + lua_setfield(L, -2, "collide_with_objects"); + lua_pushnumber(L, prop->weight); + lua_setfield(L, -2, "weight"); + push_aabb3f(L, prop->collisionbox); + lua_setfield(L, -2, "collisionbox"); + lua_pushlstring(L, prop->visual.c_str(), prop->visual.size()); + lua_setfield(L, -2, "visual"); + lua_pushlstring(L, prop->mesh.c_str(), prop->mesh.size()); + lua_setfield(L, -2, "mesh"); + push_v2f(L, prop->visual_size); + lua_setfield(L, -2, "visual_size"); + + lua_newtable(L); + u16 i = 1; + for (std::vector<std::string>::iterator it = prop->textures.begin(); + it != prop->textures.end(); ++it) { + lua_pushlstring(L, it->c_str(), it->size()); + lua_rawseti(L, -2, i); + } + lua_setfield(L, -2, "textures"); + + lua_newtable(L); + i = 1; + for (std::vector<video::SColor>::iterator it = prop->colors.begin(); + it != prop->colors.end(); ++it) { + push_ARGB8(L, *it); + lua_rawseti(L, -2, i); + } + lua_setfield(L, -2, "colors"); + + push_v2s16(L, prop->spritediv); + lua_setfield(L, -2, "spritediv"); + push_v2s16(L, prop->initial_sprite_basepos); + lua_setfield(L, -2, "initial_sprite_basepos"); + lua_pushboolean(L, prop->is_visible); + lua_setfield(L, -2, "is_visible"); + lua_pushboolean(L, prop->makes_footstep_sound); + lua_setfield(L, -2, "makes_footstep_sound"); + lua_pushnumber(L, prop->automatic_rotate); + lua_setfield(L, -2, "automatic_rotate"); + lua_pushnumber(L, prop->stepheight / BS); + lua_setfield(L, -2, "stepheight"); + if (prop->automatic_face_movement_dir) + lua_pushnumber(L, prop->automatic_face_movement_dir_offset); + else + lua_pushboolean(L, false); + lua_setfield(L, -2, "automatic_face_movement_dir"); +} + +/******************************************************************************/ +TileDef read_tiledef(lua_State *L, int index, u8 drawtype) { if(index < 0) index = lua_gettop(L) + 1 + index; TileDef tiledef; - + bool default_tiling = (drawtype == NDT_PLANTLIKE || drawtype == NDT_FIRELIKE) + ? false : true; // key at index -2 and value at index if(lua_isstring(L, index)){ // "default_lava.png" tiledef.name = lua_tostring(L, index); + tiledef.tileable_vertical = default_tiling; + tiledef.tileable_horizontal = default_tiling; } else if(lua_istable(L, index)) { @@ -224,20 +280,24 @@ TileDef read_tiledef(lua_State *L, int index) getstringfield(L, index, "name", tiledef.name); getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. tiledef.backface_culling = getboolfield_default( - L, index, "backface_culling", true); + L, index, "backface_culling", true); + tiledef.tileable_horizontal = getboolfield_default( + L, index, "tileable_horizontal", default_tiling); + tiledef.tileable_vertical = getboolfield_default( + L, index, "tileable_vertical", default_tiling); // animation = {} lua_getfield(L, index, "animation"); if(lua_istable(L, -1)){ // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} tiledef.animation.type = (TileAnimationType) - getenumfield(L, -1, "type", es_TileAnimationType, - TAT_NONE); + getenumfield(L, -1, "type", es_TileAnimationType, + TAT_NONE); tiledef.animation.aspect_w = - getintfield_default(L, -1, "aspect_w", 16); + getintfield_default(L, -1, "aspect_w", 16); tiledef.animation.aspect_h = - getintfield_default(L, -1, "aspect_h", 16); + getintfield_default(L, -1, "aspect_h", 16); tiledef.animation.length = - getfloatfield_default(L, -1, "length", 1.0); + getfloatfield_default(L, -1, "length", 1.0); } lua_pop(L, 1); } @@ -300,7 +360,7 @@ ContentFeatures read_content_features(lua_State *L, int index) int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value - f.tiledef[i] = read_tiledef(L, -1); + f.tiledef[i] = read_tiledef(L, -1, f.drawtype); // removes value, keeps key for next iteration lua_pop(L, 1); i++; @@ -335,7 +395,7 @@ ContentFeatures read_content_features(lua_State *L, int index) int i = 0; while(lua_next(L, table) != 0){ // Read tiledef from value - f.tiledef_special[i] = read_tiledef(L, -1); + f.tiledef_special[i] = read_tiledef(L, -1, f.drawtype); // removes value, keeps key for next iteration lua_pop(L, 1); i++; @@ -357,8 +417,7 @@ ContentFeatures read_content_features(lua_State *L, int index) /* Other stuff */ lua_getfield(L, index, "post_effect_color"); - if(!lua_isnil(L, -1)) - f.post_effect_color = readARGB8(L, -1); + read_color(L, -1, &f.post_effect_color); lua_pop(L, 1); f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", @@ -546,22 +605,23 @@ NodeBox read_nodebox(lua_State *L, int index) MapNode readnode(lua_State *L, int index, INodeDefManager *ndef) { lua_getfield(L, index, "name"); - const char *name = luaL_checkstring(L, -1); + if (!lua_isstring(L, -1)) + throw LuaError("Node name is not set or is not a string!"); + const char *name = lua_tostring(L, -1); lua_pop(L, 1); - u8 param1; + + u8 param1 = 0; lua_getfield(L, index, "param1"); - if(lua_isnil(L, -1)) - param1 = 0; - else + if (!lua_isnil(L, -1)) param1 = lua_tonumber(L, -1); lua_pop(L, 1); - u8 param2; + + u8 param2 = 0; lua_getfield(L, index, "param2"); - if(lua_isnil(L, -1)) - param2 = 0; - else + if (!lua_isnil(L, -1)) param2 = lua_tonumber(L, -1); lua_pop(L, 1); + return MapNode(ndef, name, param1, param2); } @@ -901,6 +961,12 @@ u32 read_flags_table(lua_State *L, int table, FlagDesc *flagdesc, u32 *flagmask) return flags; } +void push_flags_string(lua_State *L, FlagDesc *flagdesc, u32 flags, u32 flagmask) +{ + std::string flagstring = writeFlagString(flags, flagdesc, flagmask); + lua_pushlstring(L, flagstring.c_str(), flagstring.size()); +} + /******************************************************************************/ /* Lua Stored data! */ /******************************************************************************/ @@ -926,14 +992,23 @@ void read_groups(lua_State *L, int index, } /******************************************************************************/ +void push_groups(lua_State *L, const std::map<std::string, int> &groups) +{ + lua_newtable(L); + std::map<std::string, int>::const_iterator it; + for (it = groups.begin(); it != groups.end(); ++it) { + lua_pushnumber(L, it->second); + lua_setfield(L, -2, it->first.c_str()); + } +} + +/******************************************************************************/ void push_items(lua_State *L, const std::vector<ItemStack> &items) { - // Create and fill table lua_createtable(L, items.size(), 0); - std::vector<ItemStack>::const_iterator iter = items.begin(); - for (u32 i = 0; iter != items.end(); iter++) { - LuaItemStack::create(L, *iter); - lua_rawseti(L, -2, ++i); + for (u32 i = 0; i != items.size(); i++) { + LuaItemStack::create(L, items[i]); + lua_rawseti(L, -2, i + 1); } } @@ -982,14 +1057,16 @@ bool read_noiseparams(lua_State *L, int index, NoiseParams *np) if (!lua_istable(L, index)) return false; - np->offset = getfloatfield_default(L, index, "offset", 0.0); - np->scale = getfloatfield_default(L, index, "scale", 0.0); - np->persist = getfloatfield_default(L, index, "persist", 0.0); - np->lacunarity = getfloatfield_default(L, index, "lacunarity", 2.0); - np->seed = getintfield_default(L, index, "seed", 0); - np->octaves = getintfield_default(L, index, "octaves", 0); + getfloatfield(L, index, "offset", np->offset); + getfloatfield(L, index, "scale", np->scale); + getfloatfield(L, index, "persist", np->persist); + getfloatfield(L, index, "persistence", np->persist); + getfloatfield(L, index, "lacunarity", np->lacunarity); + getintfield(L, index, "seed", np->seed); + getintfield(L, index, "octaves", np->octaves); - u32 flags = 0, flagmask = 0; + u32 flags = 0; + u32 flagmask = 0; np->flags = getflagsfield(L, index, "flags", flagdesc_noiseparams, &flags, &flagmask) ? flags : NOISE_FLAG_DEFAULTS; @@ -1000,6 +1077,30 @@ bool read_noiseparams(lua_State *L, int index, NoiseParams *np) return true; } +void push_noiseparams(lua_State *L, NoiseParams *np) +{ + lua_newtable(L); + lua_pushnumber(L, np->offset); + lua_setfield(L, -2, "offset"); + lua_pushnumber(L, np->scale); + lua_setfield(L, -2, "scale"); + lua_pushnumber(L, np->persist); + lua_setfield(L, -2, "persistence"); + lua_pushnumber(L, np->lacunarity); + lua_setfield(L, -2, "lacunarity"); + lua_pushnumber(L, np->seed); + lua_setfield(L, -2, "seed"); + lua_pushnumber(L, np->octaves); + lua_setfield(L, -2, "octaves"); + + push_flags_string(L, flagdesc_noiseparams, np->flags, + np->flags); + lua_setfield(L, -2, "flags"); + + push_v3f(L, np->spread); + lua_setfield(L, -2, "spread"); +} + /******************************************************************************/ // Returns depth of json value tree static int push_json_value_getdepth(const Json::Value &value) diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 241b1ca76..46416ad8e 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -62,59 +62,57 @@ struct NoiseParams; class Schematic; -ContentFeatures read_content_features (lua_State *L, int index); -TileDef read_tiledef (lua_State *L, int index); -void read_soundspec (lua_State *L, int index, - SimpleSoundSpec &spec); -NodeBox read_nodebox (lua_State *L, int index); +ContentFeatures read_content_features (lua_State *L, int index); +TileDef read_tiledef (lua_State *L, int index, + u8 drawtype); +void read_soundspec (lua_State *L, int index, + SimpleSoundSpec &spec); +NodeBox read_nodebox (lua_State *L, int index); -void read_server_sound_params (lua_State *L, int index, - ServerSoundParams ¶ms); +void read_server_sound_params (lua_State *L, int index, + ServerSoundParams ¶ms); -void push_dig_params (lua_State *L,const DigParams ¶ms); -void push_hit_params (lua_State *L,const HitParams ¶ms); +void push_dig_params (lua_State *L, + const DigParams ¶ms); +void push_hit_params (lua_State *L, + const HitParams ¶ms); -ItemStack read_item (lua_State *L, int index, Server* srv); +ItemStack read_item (lua_State *L, int index, Server *srv); -ToolCapabilities read_tool_capabilities (lua_State *L, - int table); +ToolCapabilities read_tool_capabilities (lua_State *L, int table); void push_tool_capabilities (lua_State *L, const ToolCapabilities &prop); -ItemDefinition read_item_definition (lua_State *L, - int index, +ItemDefinition read_item_definition (lua_State *L, int index, ItemDefinition default_def); -void read_object_properties (lua_State *L, - int index, +void read_object_properties (lua_State *L, int index, + ObjectProperties *prop); +void push_object_properties (lua_State *L, ObjectProperties *prop); void push_inventory_list (lua_State *L, Inventory *inv, const char *name); -void read_inventory_list (lua_State *L, - int tableindex, - Inventory *inv, - const char *name, - Server* srv, - int forcesize=-1); +void read_inventory_list (lua_State *L, int tableindex, + Inventory *inv, const char *name, + Server *srv, int forcesize=-1); -MapNode readnode (lua_State *L, - int index, +MapNode readnode (lua_State *L, int index, INodeDefManager *ndef); -void pushnode (lua_State *L, - const MapNode &n, +void pushnode (lua_State *L, const MapNode &n, INodeDefManager *ndef); NodeBox read_nodebox (lua_State *L, int index); -void read_groups (lua_State *L, - int index, +void read_groups (lua_State *L, int index, std::map<std::string, int> &result); +void push_groups (lua_State *L, + const std::map<std::string, int> &groups); + //TODO rename to "read_enum_field" -int getenumfield (lua_State *L, - int table, +int getenumfield (lua_State *L, int table, const char *fieldname, const EnumString *spec, int default_); @@ -128,6 +126,9 @@ bool read_flags (lua_State *L, int index, FlagDesc *flagdesc, u32 *flags, u32 *flagmask); +void push_flags_string (lua_State *L, FlagDesc *flagdesc, + u32 flags, u32 flagmask); + u32 read_flags_table (lua_State *L, int table, FlagDesc *flagdesc, u32 *flagmask); @@ -142,23 +143,21 @@ void read_soundspec (lua_State *L, int index, SimpleSoundSpec &spec); - bool string_to_enum (const EnumString *spec, int &result, const std::string &str); bool read_noiseparams (lua_State *L, int index, NoiseParams *np); +void push_noiseparams (lua_State *L, NoiseParams *np); void luaentity_get (lua_State *L,u16 id); bool push_json_value (lua_State *L, const Json::Value &value, int nullindex); -void read_json_value (lua_State *L, - Json::Value &root, - int index, - u8 recursion = 0); +void read_json_value (lua_State *L, Json::Value &root, + int index, u8 recursion = 0); extern struct EnumString es_TileAnimationType[]; diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 66eeec68e..f1d3cc421 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -23,9 +23,23 @@ extern "C" { } #include "util/numeric.h" +#include "util/string.h" #include "common/c_converter.h" #include "constants.h" + +#define CHECK_TYPE(index, name, type) do { \ + int t = lua_type(L, (index)); \ + if (t != (type)) { \ + throw LuaError(std::string("Invalid ") + (name) + \ + " (expected " + lua_typename(L, (type)) + \ + " got " + lua_typename(L, t) + ")."); \ + } \ + } while(0) +#define CHECK_POS_COORD(name) CHECK_TYPE(-1, "position coordinate '" name "'", LUA_TNUMBER) +#define CHECK_POS_TAB(index) CHECK_TYPE(index, "position", LUA_TTABLE) + + void push_v3f(lua_State *L, v3f p) { lua_newtable(L); @@ -49,7 +63,7 @@ void push_v2f(lua_State *L, v2f p) v2s16 read_v2s16(lua_State *L, int index) { v2s16 p; - luaL_checktype(L, index, LUA_TTABLE); + CHECK_POS_TAB(index); lua_getfield(L, index, "x"); p.X = lua_tonumber(L, -1); lua_pop(L, 1); @@ -59,10 +73,43 @@ v2s16 read_v2s16(lua_State *L, int index) return p; } +v2s16 check_v2s16(lua_State *L, int index) +{ + v2s16 p; + CHECK_POS_TAB(index); + lua_getfield(L, index, "x"); + CHECK_POS_COORD("x"); + p.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + CHECK_POS_COORD("y"); + p.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + return p; +} + +void push_v2s16(lua_State *L, v2s16 p) +{ + lua_newtable(L); + lua_pushnumber(L, p.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, p.Y); + lua_setfield(L, -2, "y"); +} + +void push_v2s32(lua_State *L, v2s32 p) +{ + lua_newtable(L); + lua_pushnumber(L, p.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, p.Y); + lua_setfield(L, -2, "y"); +} + v2s32 read_v2s32(lua_State *L, int index) { v2s32 p; - luaL_checktype(L, index, LUA_TTABLE); + CHECK_POS_TAB(index); lua_getfield(L, index, "x"); p.X = lua_tonumber(L, -1); lua_pop(L, 1); @@ -75,11 +122,26 @@ v2s32 read_v2s32(lua_State *L, int index) v2f read_v2f(lua_State *L, int index) { v2f p; - luaL_checktype(L, index, LUA_TTABLE); + CHECK_POS_TAB(index); + lua_getfield(L, index, "x"); + p.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + p.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + return p; +} + +v2f check_v2f(lua_State *L, int index) +{ + v2f p; + CHECK_POS_TAB(index); lua_getfield(L, index, "x"); + CHECK_POS_COORD("x"); p.X = lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, index, "y"); + CHECK_POS_COORD("y"); p.Y = lua_tonumber(L, -1); lua_pop(L, 1); return p; @@ -88,7 +150,7 @@ v2f read_v2f(lua_State *L, int index) v3f read_v3f(lua_State *L, int index) { v3f pos; - luaL_checktype(L, index, LUA_TTABLE); + CHECK_POS_TAB(index); lua_getfield(L, index, "x"); pos.X = lua_tonumber(L, -1); lua_pop(L, 1); @@ -104,19 +166,35 @@ v3f read_v3f(lua_State *L, int index) v3f check_v3f(lua_State *L, int index) { v3f pos; - luaL_checktype(L, index, LUA_TTABLE); + CHECK_POS_TAB(index); lua_getfield(L, index, "x"); - pos.X = luaL_checknumber(L, -1); + CHECK_POS_COORD("x"); + pos.X = lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, index, "y"); - pos.Y = luaL_checknumber(L, -1); + CHECK_POS_COORD("y"); + pos.Y = lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, index, "z"); - pos.Z = luaL_checknumber(L, -1); + CHECK_POS_COORD("z"); + pos.Z = lua_tonumber(L, -1); lua_pop(L, 1); return pos; } +void push_ARGB8(lua_State *L, video::SColor color) +{ + lua_newtable(L); + lua_pushnumber(L, color.getAlpha()); + lua_setfield(L, -2, "a"); + lua_pushnumber(L, color.getRed()); + lua_setfield(L, -2, "r"); + lua_pushnumber(L, color.getGreen()); + lua_setfield(L, -2, "g"); + lua_pushnumber(L, color.getBlue()); + lua_setfield(L, -2, "b"); +} + void pushFloatPos(lua_State *L, v3f p) { p /= BS; @@ -153,13 +231,31 @@ v3s16 check_v3s16(lua_State *L, int index) return floatToInt(pf, 1.0); } -video::SColor readARGB8(lua_State *L, int index) +bool read_color(lua_State *L, int index, video::SColor *color) +{ + if (lua_istable(L, index)) { + *color = read_ARGB8(L, index); + } else if (lua_isnumber(L, index)) { + color->set(lua_tonumber(L, index)); + } else if (lua_isstring(L, index)) { + video::SColor parsed_color; + if (!parseColorString(lua_tostring(L, index), parsed_color, true)) + return false; + + *color = parsed_color; + } else { + return false; + } + + return true; +} + +video::SColor read_ARGB8(lua_State *L, int index) { video::SColor color(0); - luaL_checktype(L, index, LUA_TTABLE); + CHECK_TYPE(index, "ARGB color", LUA_TTABLE); lua_getfield(L, index, "a"); - if(lua_isnumber(L, -1)) - color.setAlpha(lua_tonumber(L, -1)); + color.setAlpha(lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0xFF); lua_pop(L, 1); lua_getfield(L, index, "r"); color.setRed(lua_tonumber(L, -1)); @@ -199,6 +295,23 @@ aabb3f read_aabb3f(lua_State *L, int index, f32 scale) return box; } +void push_aabb3f(lua_State *L, aabb3f box) +{ + lua_newtable(L); + lua_pushnumber(L, box.MinEdge.X); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, box.MinEdge.Y); + lua_rawseti(L, -2, 2); + lua_pushnumber(L, box.MinEdge.Z); + lua_rawseti(L, -2, 3); + lua_pushnumber(L, box.MaxEdge.X); + lua_rawseti(L, -2, 4); + lua_pushnumber(L, box.MaxEdge.Y); + lua_rawseti(L, -2, 5); + lua_pushnumber(L, box.MaxEdge.Z); + lua_rawseti(L, -2, 6); +} + std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale) { std::vector<aabb3f> boxes; @@ -227,24 +340,28 @@ std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale) return boxes; } -bool read_stringlist(lua_State *L, int index, std::vector<const char *> &result) +size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result) { if (index < 0) index = lua_gettop(L) + 1 + index; + size_t num_strings = 0; + if (lua_istable(L, index)) { lua_pushnil(L); while (lua_next(L, index)) { - if (lua_isstring(L, -1)) - result.push_back(lua_tostring(L, -1)); + if (lua_isstring(L, -1)) { + result->push_back(lua_tostring(L, -1)); + num_strings++; + } lua_pop(L, 1); } } else if (lua_isstring(L, index)) { - result.push_back(lua_tostring(L, index)); - } else { - return false; + result->push_back(lua_tostring(L, index)); + num_strings++; } - return true; + + return num_strings; } /* @@ -281,6 +398,45 @@ bool getintfield(lua_State *L, int table, return got; } +bool getintfield(lua_State *L, int table, + const char *fieldname, u8 &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isnumber(L, -1)){ + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +bool getintfield(lua_State *L, int table, + const char *fieldname, u16 &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isnumber(L, -1)){ + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +bool getintfield(lua_State *L, int table, + const char *fieldname, u32 &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isnumber(L, -1)){ + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + bool getfloatfield(lua_State *L, int table, const char *fieldname, float &result) { @@ -307,24 +463,26 @@ bool getboolfield(lua_State *L, int table, return got; } -bool getstringlistfield(lua_State *L, int table, const char *fieldname, - std::vector<const char *> &result) +size_t getstringlistfield(lua_State *L, int table, const char *fieldname, + std::vector<std::string> *result) { lua_getfield(L, table, fieldname); - bool got = read_stringlist(L, -1, result); + size_t num_strings_read = read_stringlist(L, -1, result); lua_pop(L, 1); - return got; + return num_strings_read; } std::string checkstringfield(lua_State *L, int table, const char *fieldname) { lua_getfield(L, table, fieldname); - std::string s = luaL_checkstring(L, -1); + CHECK_TYPE(-1, std::string("field \"") + fieldname + '"', LUA_TSTRING); + size_t len; + const char *s = lua_tolstring(L, -1, &len); lua_pop(L, 1); - return s; + return std::string(s, len); } std::string getstringfield_default(lua_State *L, int table, @@ -387,3 +545,95 @@ void setboolfield(lua_State *L, int table, } +//// +//// Array table slices +//// + +size_t write_array_slice_float( + lua_State *L, + int table_index, + float *data, + v3u16 data_size, + v3u16 slice_offset, + v3u16 slice_size) +{ + v3u16 pmin, pmax(data_size); + + if (slice_offset.X > 0) { + slice_offset.X--; + pmin.X = slice_offset.X; + pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X); + } + + if (slice_offset.Y > 0) { + slice_offset.Y--; + pmin.Y = slice_offset.Y; + pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y); + } + + if (slice_offset.Z > 0) { + slice_offset.Z--; + pmin.Z = slice_offset.Z; + pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z); + } + + const u32 ystride = data_size.X; + const u32 zstride = data_size.X * data_size.Y; + + u32 elem_index = 1; + for (u32 z = pmin.Z; z != pmax.Z; z++) + for (u32 y = pmin.Y; y != pmax.Y; y++) + for (u32 x = pmin.X; x != pmax.X; x++) { + u32 i = z * zstride + y * ystride + x; + lua_pushnumber(L, data[i]); + lua_rawseti(L, table_index, elem_index); + elem_index++; + } + + return elem_index - 1; +} + + +size_t write_array_slice_u16( + lua_State *L, + int table_index, + u16 *data, + v3u16 data_size, + v3u16 slice_offset, + v3u16 slice_size) +{ + v3u16 pmin, pmax(data_size); + + if (slice_offset.X > 0) { + slice_offset.X--; + pmin.X = slice_offset.X; + pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X); + } + + if (slice_offset.Y > 0) { + slice_offset.Y--; + pmin.Y = slice_offset.Y; + pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y); + } + + if (slice_offset.Z > 0) { + slice_offset.Z--; + pmin.Z = slice_offset.Z; + pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z); + } + + const u32 ystride = data_size.X; + const u32 zstride = data_size.X * data_size.Y; + + u32 elem_index = 1; + for (u32 z = pmin.Z; z != pmax.Z; z++) + for (u32 y = pmin.Y; y != pmax.Y; y++) + for (u32 x = pmin.X; x != pmax.X; x++) { + u32 i = z * zstride + y * ystride + x; + lua_pushinteger(L, data[i]); + lua_rawseti(L, table_index, elem_index); + elem_index++; + } + + return elem_index - 1; +} diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index 3b7eb6f7d..18a045d2a 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -48,11 +48,17 @@ int getintfield_default (lua_State *L, int table, bool getstringfield(lua_State *L, int table, const char *fieldname, std::string &result); -bool getstringlistfield(lua_State *L, int table, +size_t getstringlistfield(lua_State *L, int table, const char *fieldname, - std::vector<const char *> &result); + std::vector<std::string> *result); bool getintfield(lua_State *L, int table, const char *fieldname, int &result); +bool getintfield(lua_State *L, int table, + const char *fieldname, u8 &result); +bool getintfield(lua_State *L, int table, + const char *fieldname, u16 &result); +bool getintfield(lua_State *L, int table, + const char *fieldname, u32 &result); void read_groups(lua_State *L, int index, std::map<std::string, int> &result); bool getboolfield(lua_State *L, int table, @@ -70,8 +76,9 @@ void setfloatfield(lua_State *L, int table, void setboolfield(lua_State *L, int table, const char *fieldname, bool value); - v3f checkFloatPos (lua_State *L, int index); +v2f check_v2f (lua_State *L, int index); +v2s16 check_v2s16 (lua_State *L, int index); v3f check_v3f (lua_State *L, int index); v3s16 check_v3s16 (lua_State *L, int index); @@ -79,23 +86,32 @@ v3f read_v3f (lua_State *L, int index); v2f read_v2f (lua_State *L, int index); v2s16 read_v2s16 (lua_State *L, int index); v2s32 read_v2s32 (lua_State *L, int index); -video::SColor readARGB8 (lua_State *L, int index); +video::SColor read_ARGB8 (lua_State *L, int index); +bool read_color (lua_State *L, int index, + video::SColor *color); + aabb3f read_aabb3f (lua_State *L, int index, f32 scale); v3s16 read_v3s16 (lua_State *L, int index); std::vector<aabb3f> read_aabb3f_vector (lua_State *L, int index, f32 scale); -bool read_stringlist (lua_State *L, int index, - std::vector<const char *> &result); +size_t read_stringlist (lua_State *L, int index, + std::vector<std::string> *result); +void push_v2s16 (lua_State *L, v2s16 p); +void push_v2s32 (lua_State *L, v2s32 p); void push_v3s16 (lua_State *L, v3s16 p); +void push_aabb3f (lua_State *L, aabb3f box); +void push_ARGB8 (lua_State *L, video::SColor color); void pushFloatPos (lua_State *L, v3f p); void push_v3f (lua_State *L, v3f p); void push_v2f (lua_State *L, v2f p); +void warn_if_field_exists(lua_State *L, int table, + const char *fieldname, + const std::string &message); - -void warn_if_field_exists (lua_State *L, - int table, - const char *fieldname, - const std::string &message); +size_t write_array_slice_float(lua_State *L, int table_index, float *data, + v3u16 data_size, v3u16 slice_offset, v3u16 slice_size); +size_t write_array_slice_u16(lua_State *L, int table_index, u16 *data, + v3u16 data_size, v3u16 slice_offset, v3u16 slice_size); #endif /* C_CONVERTER_H_ */ diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index f811dd5d3..2a10ce0f2 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_internal.h" #include "debug.h" #include "log.h" -#include "main.h" #include "settings.h" std::string script_get_backtrace(lua_State *L) @@ -64,19 +63,65 @@ int script_exception_wrapper(lua_State *L, lua_CFunction f) return f(L); // Call wrapped function and return result. } catch (const char *s) { // Catch and convert exceptions. lua_pushstring(L, s); - } catch (std::exception& e) { + } catch (std::exception &e) { lua_pushstring(L, e.what()); - } catch (...) { - lua_pushliteral(L, "caught (...)"); } return lua_error(L); // Rethrow as a Lua error. } -void script_error(lua_State *L) +/* + * Note that we can't get tracebacks for LUA_ERRMEM or LUA_ERRERR (without + * hacking Lua internals). For LUA_ERRMEM, this is because memory errors will + * not execute the the error handler, and by the time lua_pcall returns the + * execution stack will have already been unwound. For LUA_ERRERR, there was + * another error while trying to generate a backtrace from a LUA_ERRRUN. It is + * presumed there is an error with the internal Lua state and thus not possible + * to gather a coherent backtrace. Realistically, the best we can do here is + * print which C function performed the failing pcall. + */ +void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn) { - const char *s = lua_tostring(L, -1); - std::string str(s ? s : ""); - throw LuaError(str); + if (pcall_result == 0) + return; + + const char *err_type; + switch (pcall_result) { + case LUA_ERRRUN: + err_type = "Runtime"; + break; + case LUA_ERRMEM: + err_type = "OOM"; + break; + case LUA_ERRERR: + err_type = "Double fault"; + break; + default: + err_type = "Unknown"; + } + + if (!mod) + mod = "??"; + + if (!fxn) + fxn = "??"; + + const char *err_descr = lua_tostring(L, -1); + if (!err_descr) + err_descr = "<no description>"; + + char buf[256]; + snprintf(buf, sizeof(buf), "%s error from mod '%s' in callback %s(): ", + err_type, mod, fxn); + + std::string err_msg(buf); + err_msg += err_descr; + + if (pcall_result == LUA_ERRMEM) { + err_msg += "\nCurrent Lua memory usage: " + + itos(lua_gc(L, LUA_GCCOUNT, 0) >> 10) + " MB"; + } + + throw LuaError(err_msg); } // Push the list of callbacks (a lua table). @@ -85,9 +130,10 @@ void script_error(lua_State *L) // - runs the callbacks // - replaces the table and arguments with the return value, // computed depending on mode -void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode) +void script_run_callbacks_f(lua_State *L, int nargs, + RunCallbacksMode mode, const char *fxn) { - assert(lua_gettop(L) >= nargs + 1); + FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments"); // Insert error handler lua_pushcfunction(L, script_error_handler); @@ -107,14 +153,14 @@ void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode) // Stack now looks like this: // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n> - if (lua_pcall(L, nargs + 2, 1, errorhandler)) { - script_error(L); - } + int result = lua_pcall(L, nargs + 2, 1, errorhandler); + if (result != 0) + script_error(L, result, NULL, fxn); lua_remove(L, -2); // Remove error handler } -void log_deprecated(lua_State *L, std::string message) +void log_deprecated(lua_State *L, const std::string &message) { static bool configured = false; static bool dolog = false; @@ -125,8 +171,7 @@ void log_deprecated(lua_State *L, std::string message) std::string value = g_settings->get("deprecated_lua_api_handling"); if (value == "log") { dolog = true; - } - if (value == "error") { + } else if (value == "error") { dolog = true; doerror = true; } @@ -134,11 +179,10 @@ void log_deprecated(lua_State *L, std::string message) if (doerror) { if (L != NULL) { - script_error(L); + script_error(L, LUA_ERRRUN, NULL, NULL); } else { - /* As of april 2014 assert is not optimized to nop in release builds - * therefore this is correct. */ - assert("Can't do a scripterror for this deprecated message, so exit completely!"); + FATAL_ERROR("Can't do a scripterror for this deprecated message, " + "so exit completely!"); } } diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h index eb9181b09..ecb514c8f 100644 --- a/src/script/common/c_internal.h +++ b/src/script/common/c_internal.h @@ -34,6 +34,16 @@ extern "C" { #include "common/c_types.h" +#define PCALL_RESL(L, RES) do { \ + int result_ = (RES); \ + if (result_ != 0) { \ + script_error((L), result_, NULL, __FUNCTION__); \ + } \ +} while (0) + +#define script_run_callbacks(L, nargs, mode) \ + script_run_callbacks_f((L), (nargs), (mode), __FUNCTION__) + // What script_run_callbacks does with the return values of callbacks. // Regardless of the mode, if only one callback is defined, // its return value is the total return value. @@ -67,8 +77,9 @@ enum RunCallbacksMode std::string script_get_backtrace(lua_State *L); int script_error_handler(lua_State *L); int script_exception_wrapper(lua_State *L, lua_CFunction f); -void script_error(lua_State *L); -void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode); -void log_deprecated(lua_State *L, std::string message); +void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn); +void script_run_callbacks_f(lua_State *L, int nargs, + RunCallbacksMode mode, const char *fxn); +void log_deprecated(lua_State *L, const std::string &message); #endif /* C_INTERNAL_H_ */ diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt index c45020055..be4d0131e 100644 --- a/src/script/cpp_api/CMakeLists.txt +++ b/src/script/cpp_api/CMakeLists.txt @@ -1,5 +1,5 @@ -# Used by server and client set(common_SCRIPT_CPP_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_base.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_entity.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_env.cpp @@ -8,11 +8,11 @@ set(common_SCRIPT_CPP_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/s_node.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_nodemeta.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_player.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_security.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_server.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp PARENT_SCOPE) -# Used by client only -set(minetest_SCRIPT_CPP_API_SRCS +set(client_SCRIPT_CPP_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp PARENT_SCOPE) + diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp index de1ebc07b..c00b22f98 100644 --- a/src/script/cpp_api/s_async.cpp +++ b/src/script/cpp_api/s_async.cpp @@ -155,7 +155,7 @@ void AsyncEngine::step(lua_State *L, int errorhandler) lua_getfield(L, -1, "async_event_handler"); if (lua_isnil(L, -1)) { - assert("Async event handler does not exist!" == 0); + FATAL_ERROR("Async event handler does not exist!"); } luaL_checktype(L, -1, LUA_TFUNCTION); @@ -164,9 +164,7 @@ void AsyncEngine::step(lua_State *L, int errorhandler) lua_pushlstring(L, jobDone.serializedResult.data(), jobDone.serializedResult.size()); - if (lua_pcall(L, 2, 0, errorhandler)) { - script_error(L); - } + PCALL_RESL(L, lua_pcall(L, 2, 0, errorhandler)); } resultQueueMutex.Unlock(); lua_pop(L, 1); // Pop core @@ -237,7 +235,7 @@ AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher, /******************************************************************************/ AsyncWorkerThread::~AsyncWorkerThread() { - assert(IsRunning() == false); + sanity_check(IsRunning() == false); } /******************************************************************************/ @@ -257,7 +255,7 @@ void* AsyncWorkerThread::Thread() std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua"; if (!loadScript(script)) { errorstream - << "AsyncWorkderThread execution of async base environment failed!" + << "AsyncWorkerThread execution of async base environment failed!" << std::endl; abort(); } @@ -293,8 +291,9 @@ void* AsyncWorkerThread::Thread() toProcess.serializedParams.data(), toProcess.serializedParams.size()); - if (lua_pcall(L, 2, 1, m_errorhandler)) { - scriptError(); + int result = lua_pcall(L, 2, 1, m_errorhandler); + if (result) { + PCALL_RES(result); toProcess.serializedResult = ""; } else { // Fetch result diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 1f96373dc..dcfbac4bf 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -19,7 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_base.h" #include "cpp_api/s_internal.h" +#include "cpp_api/s_security.h" #include "lua_api/l_object.h" +#include "common/c_converter.h" #include "serverobject.h" #include "debug.h" #include "filesys.h" @@ -45,18 +47,18 @@ class ModNameStorer private: lua_State *L; public: - ModNameStorer(lua_State *L_, const std::string &modname): + ModNameStorer(lua_State *L_, const std::string &mod_name): L(L_) { - // Store current modname in registry - lua_pushstring(L, modname.c_str()); - lua_setfield(L, LUA_REGISTRYINDEX, "current_modname"); + // Store current mod name in registry + lua_pushstring(L, mod_name.c_str()); + lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); } ~ModNameStorer() { - // Clear current modname in registry + // Clear current mod name from registry lua_pushnil(L); - lua_setfield(L, LUA_REGISTRYINDEX, "current_modname"); + lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); } }; @@ -67,12 +69,12 @@ public: ScriptApiBase::ScriptApiBase() { - #ifdef SCRIPTAPI_LOCK_DEBUG +#ifdef SCRIPTAPI_LOCK_DEBUG m_locked = false; - #endif +#endif m_luastack = luaL_newstate(); - assert(m_luastack); + FATAL_ERROR_IF(!m_luastack, "luaL_newstate() failed"); luaL_openlibs(m_luastack); @@ -102,6 +104,11 @@ ScriptApiBase::ScriptApiBase() lua_pushstring(m_luastack, porting::getPlatformName()); lua_setglobal(m_luastack, "PLATFORM"); + // m_secure gets set to true inside + // ScriptApiSecurity::initializeSecurity(), if neccessary. + // Default to false otherwise + m_secure = false; + m_server = NULL; m_environment = NULL; m_guiengine = NULL; @@ -112,88 +119,136 @@ ScriptApiBase::~ScriptApiBase() lua_close(m_luastack); } -bool ScriptApiBase::loadMod(const std::string &scriptpath, - const std::string &modname) +bool ScriptApiBase::loadMod(const std::string &script_path, + const std::string &mod_name, std::string *error) { - ModNameStorer modnamestorer(getStack(), modname); - - if (!string_allowed(modname, MODNAME_ALLOWED_CHARS)) { - errorstream<<"Error loading mod \""<<modname - <<"\": modname does not follow naming conventions: " - <<"Only chararacters [a-z0-9_] are allowed."<<std::endl; - return false; - } + ModNameStorer mod_name_storer(getStack(), mod_name); - return loadScript(scriptpath); + return loadScript(script_path, error); } -bool ScriptApiBase::loadScript(const std::string &scriptpath) +bool ScriptApiBase::loadScript(const std::string &script_path, std::string *error) { - verbosestream<<"Loading and running script from "<<scriptpath<<std::endl; + verbosestream << "Loading and running script from " << script_path << std::endl; lua_State *L = getStack(); - int ret = luaL_loadfile(L, scriptpath.c_str()) || lua_pcall(L, 0, 0, m_errorhandler); - if (ret) { - errorstream << "========== ERROR FROM LUA ===========" << std::endl; - errorstream << "Failed to load and run script from " << std::endl; - errorstream << scriptpath << ":" << std::endl; - errorstream << std::endl; - errorstream << lua_tostring(L, -1) << std::endl; - errorstream << std::endl; - errorstream << "======= END OF ERROR FROM LUA ========" << std::endl; + bool ok; + if (m_secure) { + ok = ScriptApiSecurity::safeLoadFile(L, script_path.c_str()); + } else { + ok = !luaL_loadfile(L, script_path.c_str()); + } + ok = ok && !lua_pcall(L, 0, 0, m_errorhandler); + if (!ok) { + std::string error_msg = lua_tostring(L, -1); + if (error) + *error = error_msg; + errorstream << "========== ERROR FROM LUA ===========" << std::endl + << "Failed to load and run script from " << std::endl + << script_path << ":" << std::endl << std::endl + << error_msg << std::endl << std::endl + << "======= END OF ERROR FROM LUA ========" << std::endl; lua_pop(L, 1); // Pop error message from stack return false; } return true; } +// Push the list of callbacks (a lua table). +// Then push nargs arguments. +// Then call this function, which +// - runs the callbacks +// - replaces the table and arguments with the return value, +// computed depending on mode +void ScriptApiBase::runCallbacksRaw(int nargs, + RunCallbacksMode mode, const char *fxn) +{ + lua_State *L = getStack(); + FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments"); + + // Insert error handler + lua_pushcfunction(L, script_error_handler); + int errorhandler = lua_gettop(L) - nargs - 1; + lua_insert(L, errorhandler); + + // Insert run_callbacks between error handler and table + lua_getglobal(L, "core"); + lua_getfield(L, -1, "run_callbacks"); + lua_remove(L, -2); + lua_insert(L, errorhandler + 1); + + // Insert mode after table + lua_pushnumber(L, (int)mode); + lua_insert(L, errorhandler + 3); + + // Stack now looks like this: + // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n> + + int result = lua_pcall(L, nargs + 2, 1, errorhandler); + if (result != 0) + scriptError(result, fxn); + + lua_remove(L, -2); // Remove error handler +} + void ScriptApiBase::realityCheck() { int top = lua_gettop(m_luastack); - if(top >= 30){ - dstream<<"Stack is over 30:"<<std::endl; + if (top >= 30) { + dstream << "Stack is over 30:" << std::endl; stackDump(dstream); std::string traceback = script_get_backtrace(m_luastack); throw LuaError("Stack is over 30 (reality check)\n" + traceback); } } -void ScriptApiBase::scriptError() +void ScriptApiBase::scriptError(int result, const char *fxn) { - throw LuaError(lua_tostring(m_luastack, -1)); + script_error(getStack(), result, m_last_run_mod.c_str(), fxn); } void ScriptApiBase::stackDump(std::ostream &o) { - int i; int top = lua_gettop(m_luastack); - for (i = 1; i <= top; i++) { /* repeat for each level */ + for (int i = 1; i <= top; i++) { /* repeat for each level */ int t = lua_type(m_luastack, i); switch (t) { - case LUA_TSTRING: /* strings */ - o<<"\""<<lua_tostring(m_luastack, i)<<"\""; + o << "\"" << lua_tostring(m_luastack, i) << "\""; break; - case LUA_TBOOLEAN: /* booleans */ - o<<(lua_toboolean(m_luastack, i) ? "true" : "false"); + o << (lua_toboolean(m_luastack, i) ? "true" : "false"); break; - case LUA_TNUMBER: /* numbers */ { char buf[10]; snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i)); - o<<buf; - break; } - + o << buf; + break; + } default: /* other values */ - o<<lua_typename(m_luastack, t); + o << lua_typename(m_luastack, t); break; - } - o<<" "; + o << " "; } - o<<std::endl; + o << std::endl; +} + +void ScriptApiBase::setOriginDirect(const char *origin) +{ + m_last_run_mod = origin ? origin : "??"; +} + +void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn) +{ +#ifdef SCRIPTAPI_DEBUG + lua_State *L = getStack(); + + m_last_run_mod = lua_istable(L, index) ? + getstringfield_default(L, index, "mod_origin", "") : ""; + //printf(">>>> running %s for mod: %s\n", fxn, m_last_run_mod.c_str()); +#endif } void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) @@ -245,7 +300,7 @@ void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj) void ScriptApiBase::objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj) { - if(cobj == NULL || cobj->getId() == 0){ + if (cobj == NULL || cobj->getId() == 0) { ObjectRef::create(L, cobj); } else { objectrefGet(L, cobj->getId()); @@ -263,4 +318,3 @@ void ScriptApiBase::objectrefGet(lua_State *L, u16 id) lua_remove(L, -2); // object_refs lua_remove(L, -2); // core } - diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index 4ea3677a9..d653b5bac 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -34,6 +34,25 @@ extern "C" { #include "common/c_internal.h" #define SCRIPTAPI_LOCK_DEBUG +#define SCRIPTAPI_DEBUG + +#define SCRIPT_MOD_NAME_FIELD "current_mod_name" +// MUST be an invalid mod name so that mods can't +// use that name to bypass security! +#define BUILTIN_MOD_NAME "*builtin*" + +#define PCALL_RES(RES) do { \ + int result_ = (RES); \ + if (result_ != 0) { \ + scriptError(result_, __FUNCTION__); \ + } \ +} while (0) + +#define runCallbacks(nargs, mode) \ + runCallbacksRaw((nargs), (mode), __FUNCTION__) + +#define setOriginFromTable(index) \ + setOriginFromTableRaw(index, __FUNCTION__) class Server; class Environment; @@ -42,17 +61,26 @@ class ServerActiveObject; class ScriptApiBase { public: - ScriptApiBase(); virtual ~ScriptApiBase(); - bool loadMod(const std::string &scriptpath, const std::string &modname); - bool loadScript(const std::string &scriptpath); + bool loadMod(const std::string &script_path, const std::string &mod_name, + std::string *error=NULL); + bool loadScript(const std::string &script_path, std::string *error=NULL); + + void runCallbacksRaw(int nargs, + RunCallbacksMode mode, const char *fxn); /* object */ void addObjectReference(ServerActiveObject *cobj); void removeObjectReference(ServerActiveObject *cobj); + Server* getServer() { return m_server; } + + std::string getOrigin() { return m_last_run_mod; } + void setOriginDirect(const char *origin); + void setOriginFromTableRaw(int index, const char *fxn); + protected: friend class LuaABM; friend class InvRef; @@ -66,10 +94,9 @@ protected: { return m_luastack; } void realityCheck(); - void scriptError(); + void scriptError(int result, const char *fxn); void stackDump(std::ostream &o); - Server* getServer() { return m_server; } void setServer(Server* server) { m_server = server; } Environment* getEnv() { return m_environment; } @@ -82,8 +109,10 @@ protected: void objectrefGet(lua_State *L, u16 id); JMutex m_luastackmutex; + std::string m_last_run_mod; // Stack index of Lua error handler int m_errorhandler; + bool m_secure; #ifdef SCRIPTAPI_LOCK_DEBUG bool m_locked; #endif diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index b52bde18a..0d159846a 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -91,9 +91,9 @@ void ScriptApiEntity::luaentity_Activate(u16 id, lua_pushvalue(L, object); // self lua_pushlstring(L, staticdata.c_str(), staticdata.size()); lua_pushinteger(L, dtime_s); - // Call with 3 arguments, 0 results - if (lua_pcall(L, 3, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler)); } else { lua_pop(L, 1); } @@ -136,12 +136,12 @@ std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id) lua_pop(L, 2); // Pop entity and get_staticdata return ""; } - luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self - // Call with 1 arguments, 1 results - if (lua_pcall(L, 1, 1, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler)); + lua_remove(L, object); // Remove object size_t len = 0; @@ -209,9 +209,10 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime) luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self lua_pushnumber(L, dtime); // dtime - // Call with 2 arguments, 0 results - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + lua_pop(L, 1); // Pop object } @@ -241,9 +242,10 @@ void ScriptApiEntity::luaentity_Punch(u16 id, lua_pushnumber(L, time_from_last_punch); push_tool_capabilities(L, *toolcap); push_v3f(L, dir); - // Call with 5 arguments, 0 results - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); + lua_pop(L, 1); // Pop object } @@ -268,9 +270,10 @@ void ScriptApiEntity::luaentity_Rightclick(u16 id, luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self objectrefGetOrCreate(L, clicker); // Clicker reference - // Call with 2 arguments, 0 results - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + + setOriginFromTable(object); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); + lua_pop(L, 1); // Pop object } diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index c171bbf02..9c733773a 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -38,7 +38,7 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, push_v3s16(L, minp); push_v3s16(L, maxp); lua_pushnumber(L, blockseed); - script_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(3, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiEnv::environment_Step(float dtime) @@ -52,7 +52,7 @@ void ScriptApiEnv::environment_Step(float dtime) // Call callbacks lua_pushnumber(L, dtime); try { - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } catch (LuaError &e) { getServer()->setAsyncFatalError(e.what()); } @@ -73,7 +73,7 @@ void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type) objectrefGetOrCreate(L, player); // player lua_pushstring(L,type.c_str()); // event type try { - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); } catch (LuaError &e) { getServer()->setAsyncFatalError(e.what()); } diff --git a/src/script/cpp_api/s_internal.h b/src/script/cpp_api/s_internal.h index 10ee1a7de..9999a584a 100644 --- a/src/script/cpp_api/s_internal.h +++ b/src/script/cpp_api/s_internal.h @@ -61,3 +61,4 @@ bool* m_variable; StackUnroller stack_unroller(L); #endif /* S_INTERNAL_H_ */ + diff --git a/src/script/cpp_api/s_inventory.cpp b/src/script/cpp_api/s_inventory.cpp index 422faf150..019d1ccc0 100644 --- a/src/script/cpp_api/s_inventory.cpp +++ b/src/script/cpp_api/s_inventory.cpp @@ -48,8 +48,7 @@ int ScriptApiDetached::detached_inventory_AllowMove( lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler)); if(!lua_isnumber(L, -1)) throw LuaError("allow_move should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); @@ -77,8 +76,7 @@ int ScriptApiDetached::detached_inventory_AllowPut( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_put should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); @@ -106,8 +104,7 @@ int ScriptApiDetached::detached_inventory_AllowTake( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_take should return a number. name=" + name); int ret = luaL_checkinteger(L, -1); @@ -139,8 +136,7 @@ void ScriptApiDetached::detached_inventory_OnMove( lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler)); } // Report put items @@ -164,8 +160,7 @@ void ScriptApiDetached::detached_inventory_OnPut( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } // Report taken items @@ -189,8 +184,7 @@ void ScriptApiDetached::detached_inventory_OnTake( lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } // Retrieves core.detached_inventories[name][callbackname] @@ -215,6 +209,9 @@ bool ScriptApiDetached::getDetachedInventoryCallback( lua_pop(L, 1); return false; } + + setOriginFromTable(-1); + lua_getfield(L, -1, callbackname); lua_remove(L, -2); // Should be a function or nil diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp index e3a9ac7aa..4d4d416ec 100644 --- a/src/script/cpp_api/s_item.cpp +++ b/src/script/cpp_api/s_item.cpp @@ -42,8 +42,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, dropper); pushFloatPos(L, pos); - if (lua_pcall(L, 3, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -68,8 +67,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, placer); pushPointedThing(pointed); - if (lua_pcall(L, 3, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -94,8 +92,7 @@ bool ScriptApiItem::item_OnUse(ItemStack &item, LuaItemStack::create(L, item); objectrefGetOrCreate(L, user); pushPointedThing(pointed); - if (lua_pcall(L, 3, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler)); if(!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -116,7 +113,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, lua_getfield(L, -1, "on_craft"); LuaItemStack::create(L, item); objectrefGetOrCreate(L, user); - + // Push inventory list std::vector<ItemStack> items; for (u32 i = 0; i < old_craft_grid->getSize(); i++) { @@ -125,8 +122,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user, push_items(L, items); InvRef::create(L, craft_inv); - if (lua_pcall(L, 4, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -156,8 +152,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user, push_items(L, items); InvRef::create(L, craft_inv); - if (lua_pcall(L, 4, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler)); if (!lua_isnil(L, -1)) { try { item = read_item(L,-1, getServer()); @@ -198,6 +193,9 @@ bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname) lua_remove(L, -2); luaL_checktype(L, -1, LUA_TTABLE); } + + setOriginFromTable(-1); + lua_getfield(L, -1, callbackname); lua_remove(L, -2); // Remove item def // Should be a function or nil diff --git a/src/script/cpp_api/s_mainmenu.cpp b/src/script/cpp_api/s_mainmenu.cpp index ef8cea6c9..17ceff082 100644 --- a/src/script/cpp_api/s_mainmenu.cpp +++ b/src/script/cpp_api/s_mainmenu.cpp @@ -21,15 +21,21 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_internal.h" #include "common/c_converter.h" -void ScriptApiMainMenu::setMainMenuErrorMessage(std::string errormessage) +void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data) { SCRIPTAPI_PRECHECKHEADER lua_getglobal(L, "gamedata"); int gamedata_idx = lua_gettop(L); lua_pushstring(L, "errormessage"); - lua_pushstring(L, errormessage.c_str()); + if (!data->errormessage.empty()) { + lua_pushstring(L, data->errormessage.c_str()); + } else { + lua_pushnil(L); + } lua_settable(L, gamedata_idx); + setboolfield(L, gamedata_idx, "reconnect_requested", + data->reconnect_requested); lua_pop(L, 1); } @@ -49,11 +55,10 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text) // Call it lua_pushstring(L, text.c_str()); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } -void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string> fields) +void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields) { SCRIPTAPI_PRECHECKHEADER @@ -69,8 +74,8 @@ void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string> // Convert fields to a Lua table lua_newtable(L); - std::map<std::string, std::string>::const_iterator it; - for (it = fields.begin(); it != fields.end(); it++){ + StringMap::const_iterator it; + for (it = fields.begin(); it != fields.end(); ++it) { const std::string &name = it->first; const std::string &value = it->second; lua_pushstring(L, name.c_str()); @@ -79,7 +84,6 @@ void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string> } // Call it - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } diff --git a/src/script/cpp_api/s_mainmenu.h b/src/script/cpp_api/s_mainmenu.h index 53dcd37e9..8d5895817 100644 --- a/src/script/cpp_api/s_mainmenu.h +++ b/src/script/cpp_api/s_mainmenu.h @@ -21,17 +21,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #define S_MAINMENU_H_ #include "cpp_api/s_base.h" -#include <map> +#include "util/string.h" +#include "../guiMainMenu.h" -class ScriptApiMainMenu - : virtual public ScriptApiBase -{ +class ScriptApiMainMenu : virtual public ScriptApiBase { public: /** - * set gamedata.errormessage to inform lua of an error - * @param errormessage the error message + * Hand over MainMenuDataForScript to lua to inform lua of the content + * @param data the data */ - void setMainMenuErrorMessage(std::string errormessage); + void setMainMenuData(MainMenuDataForScript *data); /** * process events received from formspec @@ -43,7 +42,7 @@ public: * process field data recieved from formspec * @param fields data in field format */ - void handleMainMenuButtons(std::map<std::string, std::string> fields); + void handleMainMenuButtons(const StringMap &fields); }; #endif /* S_MAINMENU_H_ */ diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index e3d3fb58b..dac058b13 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -106,8 +106,7 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, pushnode(L, node, ndef); objectrefGetOrCreate(L, puncher); pushPointedThing(pointed); - if (lua_pcall(L, 4, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler)); return true; } @@ -126,8 +125,7 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node, push_v3s16(L, p); pushnode(L, node, ndef); objectrefGetOrCreate(L, digger); - if (lua_pcall(L, 3, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler)); return true; } @@ -143,8 +141,7 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) @@ -159,8 +156,7 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) @@ -176,8 +172,7 @@ void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) // Call function push_v3s16(L, p); pushnode(L, node, ndef); - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); } bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) @@ -193,14 +188,13 @@ bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) // Call function push_v3s16(L, p); lua_pushnumber(L,dtime); - if (lua_pcall(L, 2, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); return (bool) lua_isboolean(L, -1) && (bool) lua_toboolean(L, -1) == true; } void ScriptApiNode::node_on_receive_fields(v3s16 p, const std::string &formname, - const std::map<std::string, std::string> &fields, + const StringMap &fields, ServerActiveObject *sender) { SCRIPTAPI_PRECHECKHEADER @@ -220,8 +214,8 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p, push_v3s16(L, p); // pos lua_pushstring(L, formname.c_str()); // formname lua_newtable(L); // fields - std::map<std::string, std::string>::const_iterator it; - for (it = fields.begin(); it != fields.end(); it++){ + StringMap::const_iterator it; + for (it = fields.begin(); it != fields.end(); it++) { const std::string &name = it->first; const std::string &value = it->second; lua_pushstring(L, name.c_str()); @@ -229,8 +223,7 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p, lua_settable(L, -3); } objectrefGetOrCreate(L, sender); // player - if (lua_pcall(L, 4, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler)); } void ScriptApiNode::node_falling_update(v3s16 p) @@ -239,8 +232,7 @@ void ScriptApiNode::node_falling_update(v3s16 p) lua_getglobal(L, "nodeupdate"); push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } void ScriptApiNode::node_falling_update_single(v3s16 p) @@ -249,7 +241,5 @@ void ScriptApiNode::node_falling_update_single(v3s16 p) lua_getglobal(L, "nodeupdate_single"); push_v3s16(L, p); - if (lua_pcall(L, 1, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler)); } - diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h index b3a6c83b5..fe1180cb3 100644 --- a/src/script/cpp_api/s_node.h +++ b/src/script/cpp_api/s_node.h @@ -20,11 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef S_NODE_H_ #define S_NODE_H_ -#include <map> - #include "irr_v3d.h" #include "cpp_api/s_base.h" #include "cpp_api/s_nodemeta.h" +#include "util/string.h" struct MapNode; class ServerActiveObject; @@ -47,7 +46,7 @@ public: bool node_on_timer(v3s16 p, MapNode node, f32 dtime); void node_on_receive_fields(v3s16 p, const std::string &formname, - const std::map<std::string, std::string> &fields, + const StringMap &fields, ServerActiveObject *sender); void node_falling_update(v3s16 p); void node_falling_update_single(v3s16 p); diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp index 01eee337c..638750b0e 100644 --- a/src/script/cpp_api/s_nodemeta.cpp +++ b/src/script/cpp_api/s_nodemeta.cpp @@ -54,8 +54,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p, lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_move should" " return a number, guilty node: " + nodename); @@ -89,8 +88,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if(!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_put should" " return a number, guilty node: " + nodename); @@ -124,8 +122,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler)); if (!lua_isnumber(L, -1)) throw LuaError("allow_metadata_inventory_take should" " return a number, guilty node: " + nodename); @@ -162,8 +159,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p, lua_pushinteger(L, to_index + 1); // to_index lua_pushinteger(L, count); // count objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 7, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler)); } // Report put items @@ -191,8 +187,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } // Report taken items @@ -220,13 +215,14 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p, lua_pushinteger(L, index + 1); // index LuaItemStack::create(L, stack); // stack objectrefGetOrCreate(L, player); // player - if (lua_pcall(L, 5, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler)); } -ScriptApiNodemeta::ScriptApiNodemeta() { +ScriptApiNodemeta::ScriptApiNodemeta() +{ } -ScriptApiNodemeta::~ScriptApiNodemeta() { +ScriptApiNodemeta::~ScriptApiNodemeta() +{ } diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp index 81bfd4505..ef3c31cfd 100644 --- a/src/script/cpp_api/s_player.cpp +++ b/src/script/cpp_api/s_player.cpp @@ -19,6 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_player.h" #include "cpp_api/s_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" #include "util/string.h" void ScriptApiPlayer::on_newplayer(ServerActiveObject *player) @@ -30,7 +32,7 @@ void ScriptApiPlayer::on_newplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_newplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player) @@ -42,7 +44,47 @@ void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_dieplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player, + ServerActiveObject *hitter, + float time_from_last_punch, + const ToolCapabilities *toolcap, + v3f dir, + s16 damage) +{ + SCRIPTAPI_PRECHECKHEADER + // Get core.registered_on_punchplayers + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_punchplayers"); + // Call callbacks + objectrefGetOrCreate(L, player); + objectrefGetOrCreate(L, hitter); + lua_pushnumber(L, time_from_last_punch); + push_tool_capabilities(L, *toolcap); + push_v3f(L, dir); + lua_pushnumber(L, damage); + runCallbacks(6, RUN_CALLBACKS_MODE_OR); + return lua_toboolean(L, -1); +} + +s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player, + s16 hp_change) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_player_hpchange + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_player_hpchange"); + lua_remove(L, -2); + + objectrefGetOrCreate(L, player); + lua_pushnumber(L, hp_change); + PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); + hp_change = lua_tointeger(L, -1); + lua_pop(L, -1); + return hp_change; } bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player) @@ -54,12 +96,15 @@ bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_respawnplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_OR); + runCallbacks(1, RUN_CALLBACKS_MODE_OR); bool positioning_handled_by_some = lua_toboolean(L, -1); return positioning_handled_by_some; } -bool ScriptApiPlayer::on_prejoinplayer(std::string name, std::string ip, std::string &reason) +bool ScriptApiPlayer::on_prejoinplayer( + const std::string &name, + const std::string &ip, + std::string *reason) { SCRIPTAPI_PRECHECKHEADER @@ -68,9 +113,9 @@ bool ScriptApiPlayer::on_prejoinplayer(std::string name, std::string ip, std::st lua_getfield(L, -1, "registered_on_prejoinplayers"); lua_pushstring(L, name.c_str()); lua_pushstring(L, ip.c_str()); - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR); + runCallbacks(2, RUN_CALLBACKS_MODE_OR); if (lua_isstring(L, -1)) { - reason.assign(lua_tostring(L, -1)); + reason->assign(lua_tostring(L, -1)); return true; } return false; @@ -85,7 +130,7 @@ void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_joinplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player) @@ -97,7 +142,7 @@ void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player) lua_getfield(L, -1, "registered_on_leaveplayers"); // Call callbacks objectrefGetOrCreate(L, player); - script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_cheat(ServerActiveObject *player, @@ -113,12 +158,12 @@ void ScriptApiPlayer::on_cheat(ServerActiveObject *player, lua_newtable(L); lua_pushlstring(L, cheat_type.c_str(), cheat_type.size()); lua_setfield(L, -2, "type"); - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); } void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player, const std::string &formname, - const std::map<std::string, std::string> &fields) + const StringMap &fields) { SCRIPTAPI_PRECHECKHEADER @@ -132,17 +177,19 @@ void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player, lua_pushstring(L, formname.c_str()); // param 3 lua_newtable(L); - for(std::map<std::string, std::string>::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; + StringMap::const_iterator it; + for (it = fields.begin(); it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; lua_pushstring(L, name.c_str()); lua_pushlstring(L, value.c_str(), value.size()); lua_settable(L, -3); } - script_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC); + runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC); } -ScriptApiPlayer::~ScriptApiPlayer() { + +ScriptApiPlayer::~ScriptApiPlayer() +{ } diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h index c77d397c4..2e4dc2222 100644 --- a/src/script/cpp_api/s_player.h +++ b/src/script/cpp_api/s_player.h @@ -20,10 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef S_PLAYER_H_ #define S_PLAYER_H_ -#include <map> - #include "cpp_api/s_base.h" +#include "irr_v3d.h" +#include "util/string.h" +struct ToolCapabilities; class ScriptApiPlayer : virtual public ScriptApiBase @@ -34,14 +35,17 @@ public: void on_newplayer(ServerActiveObject *player); void on_dieplayer(ServerActiveObject *player); bool on_respawnplayer(ServerActiveObject *player); - bool on_prejoinplayer(std::string name, std::string ip, std::string &reason); + bool on_prejoinplayer(const std::string &name, const std::string &ip, + std::string *reason); void on_joinplayer(ServerActiveObject *player); void on_leaveplayer(ServerActiveObject *player); void on_cheat(ServerActiveObject *player, const std::string &cheat_type); - + bool on_punchplayer(ServerActiveObject *player, + ServerActiveObject *hitter, float time_from_last_punch, + const ToolCapabilities *toolcap, v3f dir, s16 damage); + s16 on_player_hpchange(ServerActiveObject *player, s16 hp_change); void on_playerReceiveFields(ServerActiveObject *player, - const std::string &formname, - const std::map<std::string, std::string> &fields); + const std::string &formname, const StringMap &fields); }; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp new file mode 100644 index 000000000..6a6d40307 --- /dev/null +++ b/src/script/cpp_api/s_security.cpp @@ -0,0 +1,604 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "cpp_api/s_security.h" + +#include "filesys.h" +#include "porting.h" +#include "server.h" +#include "settings.h" + +#include <cerrno> +#include <string> +#include <iostream> + + +#define SECURE_API(lib, name) \ + lua_pushcfunction(L, sl_##lib##_##name); \ + lua_setfield(L, -2, #name); + + +static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1) +{ + if (from < 0) from = lua_gettop(L) + from + 1; + if (to < 0) to = lua_gettop(L) + to + 1; + for (unsigned i = 0; i < (len / sizeof(list[0])); i++) { + lua_getfield(L, from, list[i]); + lua_setfield(L, to, list[i]); + } +} + +// Pushes the original version of a library function on the stack, from the old version +static inline void push_original(lua_State *L, const char *lib, const char *func) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + lua_getfield(L, -1, lib); + lua_remove(L, -2); // Remove globals_backup + lua_getfield(L, -1, func); + lua_remove(L, -2); // Remove lib +} + + +void ScriptApiSecurity::initializeSecurity() +{ + static const char *whitelist[] = { + "assert", + "core", + "collectgarbage", + "DIR_DELIM", + "error", + "getfenv", + "getmetatable", + "ipairs", + "next", + "pairs", + "pcall", + "print", + "rawequal", + "rawget", + "rawset", + "select", + "setfenv", + "setmetatable", + "tonumber", + "tostring", + "type", + "unpack", + "_VERSION", + "xpcall", + // Completely safe libraries + "coroutine", + "string", + "table", + "math", + }; + static const char *io_whitelist[] = { + "close", + "flush", + "read", + "type", + "write", + }; + static const char *os_whitelist[] = { + "clock", + "date", + "difftime", + "exit", + "getenv", + "setlocale", + "time", + "tmpname", + }; + static const char *debug_whitelist[] = { + "gethook", + "traceback", + "getinfo", + "getmetatable", + "setupvalue", + "setmetatable", + "upvalueid", + "upvaluejoin", + "sethook", + "debug", + "getupvalue", + "setlocal", + }; + static const char *package_whitelist[] = { + "config", + "cpath", + "path", + "searchpath", + }; + static const char *jit_whitelist[] = { + "arch", + "flush", + "off", + "on", + "opt", + "os", + "status", + "version", + "version_num", + }; + + m_secure = true; + + lua_State *L = getStack(); + + // Backup globals to the registry + lua_getglobal(L, "_G"); + lua_setfield(L, LUA_REGISTRYINDEX, "globals_backup"); + + // Replace the global environment with an empty one +#if LUA_VERSION_NUM <= 501 + int is_main = lua_pushthread(L); // Push the main thread + FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state " + "isn't the main Lua thread!"); +#endif + lua_newtable(L); // Create new environment + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_G"); // Set _G of new environment +#if LUA_VERSION_NUM >= 502 // Lua >= 5.2 + // Set the global environment + lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS); +#else // Lua <= 5.1 + // Set the environment of the main thread + FATAL_ERROR_IF(!lua_setfenv(L, -2), "Security: Unable to set " + "environment of the main Lua thread!"); + lua_pop(L, 1); // Pop thread +#endif + + // Get old globals + lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + int old_globals = lua_gettop(L); + + + // Copy safe base functions + lua_getglobal(L, "_G"); + copy_safe(L, whitelist, sizeof(whitelist)); + + // And replace unsafe ones + SECURE_API(g, dofile); + SECURE_API(g, load); + SECURE_API(g, loadfile); + SECURE_API(g, loadstring); + SECURE_API(g, require); + lua_pop(L, 1); + + + // Copy safe IO functions + lua_getfield(L, old_globals, "io"); + lua_newtable(L); + copy_safe(L, io_whitelist, sizeof(io_whitelist)); + + // And replace unsafe ones + SECURE_API(io, open); + SECURE_API(io, input); + SECURE_API(io, output); + SECURE_API(io, lines); + + lua_setglobal(L, "io"); + lua_pop(L, 1); // Pop old IO + + + // Copy safe OS functions + lua_getfield(L, old_globals, "os"); + lua_newtable(L); + copy_safe(L, os_whitelist, sizeof(os_whitelist)); + + // And replace unsafe ones + SECURE_API(os, remove); + SECURE_API(os, rename); + + lua_setglobal(L, "os"); + lua_pop(L, 1); // Pop old OS + + + // Copy safe debug functions + lua_getfield(L, old_globals, "debug"); + lua_newtable(L); + copy_safe(L, debug_whitelist, sizeof(debug_whitelist)); + lua_setglobal(L, "debug"); + lua_pop(L, 1); // Pop old debug + + + // Copy safe package fields + lua_getfield(L, old_globals, "package"); + lua_newtable(L); + copy_safe(L, package_whitelist, sizeof(package_whitelist)); + lua_setglobal(L, "package"); + lua_pop(L, 1); // Pop old package + + + // Copy safe jit functions, if they exist + lua_getfield(L, -1, "jit"); + if (!lua_isnil(L, -1)) { + lua_newtable(L); + copy_safe(L, jit_whitelist, sizeof(jit_whitelist)); + lua_setglobal(L, "jit"); + } + lua_pop(L, 1); // Pop old jit + + lua_pop(L, 1); // Pop globals_backup +} + + +bool ScriptApiSecurity::isSecure(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + bool secure = !lua_isnil(L, -1); + lua_pop(L, 1); + return secure; +} + + +#define CHECK_FILE_ERR(ret, fp) \ + if (ret) { \ + if (fp) std::fclose(fp); \ + lua_pushfstring(L, "%s: %s", path, strerror(errno)); \ + return false; \ + } + + +bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path) +{ + FILE *fp; + char *chunk_name; + if (path == NULL) { + fp = stdin; + chunk_name = const_cast<char *>("=stdin"); + } else { + fp = fopen(path, "rb"); + if (!fp) { + lua_pushfstring(L, "%s: %s", path, strerror(errno)); + return false; + } + chunk_name = new char[strlen(path) + 2]; + chunk_name[0] = '@'; + chunk_name[1] = '\0'; + strcat(chunk_name, path); + } + + size_t start = 0; + int c = std::getc(fp); + if (c == '#') { + // Skip the first line + while ((c = std::getc(fp)) != EOF && c != '\n'); + if (c == '\n') c = std::getc(fp); + start = std::ftell(fp); + } + + if (c == LUA_SIGNATURE[0]) { + lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); + return false; + } + + // Read the file + int ret = std::fseek(fp, 0, SEEK_END); + CHECK_FILE_ERR(ret, fp); + if (ret) { + std::fclose(fp); + lua_pushfstring(L, "%s: %s", path, strerror(errno)); + return false; + } + size_t size = std::ftell(fp) - start; + char *code = new char[size]; + ret = std::fseek(fp, start, SEEK_SET); + CHECK_FILE_ERR(ret, fp); + if (ret) { + std::fclose(fp); + lua_pushfstring(L, "%s: %s", path, strerror(errno)); + return false; + } + size_t num_read = std::fread(code, 1, size, fp); + if (path) { + std::fclose(fp); + } + if (num_read != size) { + lua_pushliteral(L, "Error reading file to load."); + return false; + } + + if (luaL_loadbuffer(L, code, size, chunk_name)) { + return false; + } + + if (path) { + delete [] chunk_name; + } + return true; +} + + +bool ScriptApiSecurity::checkPath(lua_State *L, const char *path) +{ + std::string str; // Transient + + std::string norel_path = fs::RemoveRelativePathComponents(path); + std::string abs_path = fs::AbsolutePath(norel_path); + + if (!abs_path.empty()) { + // Don't allow accessing the settings file + str = fs::AbsolutePath(g_settings_path); + if (str == abs_path) return false; + } + + // If we couldn't find the absolute path (path doesn't exist) then + // try removing the last components until it works (to allow + // non-existent files/folders for mkdir). + std::string cur_path = norel_path; + std::string removed; + while (abs_path.empty() && !cur_path.empty()) { + std::string tmp_rmed; + cur_path = fs::RemoveLastPathComponent(cur_path, &tmp_rmed); + removed = tmp_rmed + (removed.empty() ? "" : DIR_DELIM + removed); + abs_path = fs::AbsolutePath(cur_path); + } + if (abs_path.empty()) return false; + // Add the removed parts back so that you can't, eg, create a + // directory in worldmods if worldmods doesn't exist. + if (!removed.empty()) abs_path += DIR_DELIM + removed; + + // Get server from registry + lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi"); + ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); + lua_pop(L, 1); + const Server *server = script->getServer(); + + if (!server) return false; + + // Get mod name + lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + if (lua_isstring(L, -1)) { + std::string mod_name = lua_tostring(L, -1); + + // Builtin can access anything + if (mod_name == BUILTIN_MOD_NAME) { + return true; + } + + // Allow paths in mod path + const ModSpec *mod = server->getModSpec(mod_name); + if (mod) { + str = fs::AbsolutePath(mod->path); + if (!str.empty() && fs::PathStartsWith(abs_path, str)) { + return true; + } + } + } + lua_pop(L, 1); // Pop mod name + + str = fs::AbsolutePath(server->getWorldPath()); + if (str.empty()) return false; + // Don't allow access to world mods. We add to the absolute path + // of the world instead of getting the absolute paths directly + // because that won't work if they don't exist. + if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") || + fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) { + return false; + } + // Allow all other paths in world path + if (fs::PathStartsWith(abs_path, str)) { + return true; + } + + // Default to disallowing + return false; +} + + +int ScriptApiSecurity::sl_g_dofile(lua_State *L) +{ + int nret = sl_g_loadfile(L); + if (nret != 1) { + lua_error(L); + // code after this function isn't executed + } + int top_precall = lua_gettop(L); + lua_call(L, 0, LUA_MULTRET); + // Return number of arguments returned by the function, + // adjusting for the function being poped. + return lua_gettop(L) - (top_precall - 1); +} + + +int ScriptApiSecurity::sl_g_load(lua_State *L) +{ + size_t len; + const char *buf; + std::string code; + const char *chunk_name = "=(load)"; + + luaL_checktype(L, 1, LUA_TFUNCTION); + if (!lua_isnone(L, 2)) { + luaL_checktype(L, 2, LUA_TSTRING); + chunk_name = lua_tostring(L, 2); + } + + while (true) { + lua_pushvalue(L, 1); + lua_call(L, 0, 1); + int t = lua_type(L, -1); + if (t == LUA_TNIL) { + break; + } else if (t != LUA_TSTRING) { + lua_pushnil(L); + lua_pushliteral(L, "Loader didn't return a string"); + return 2; + } + buf = lua_tolstring(L, -1, &len); + code += std::string(buf, len); + lua_pop(L, 1); // Pop return value + } + if (code[0] == LUA_SIGNATURE[0]) { + lua_pushnil(L); + lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); + return 2; + } + if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) { + lua_pushnil(L); + lua_insert(L, lua_gettop(L) - 1); + return 2; + } + return 1; +} + + +int ScriptApiSecurity::sl_g_loadfile(lua_State *L) +{ + const char *path = NULL; + + if (lua_isstring(L, 1)) { + path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + if (!safeLoadFile(L, path)) { + lua_pushnil(L); + lua_insert(L, -2); + return 2; + } + + return 1; +} + + +int ScriptApiSecurity::sl_g_loadstring(lua_State *L) +{ + const char *chunk_name = "=(load)"; + + luaL_checktype(L, 1, LUA_TSTRING); + if (!lua_isnone(L, 2)) { + luaL_checktype(L, 2, LUA_TSTRING); + chunk_name = lua_tostring(L, 2); + } + + size_t size; + const char *code = lua_tolstring(L, 1, &size); + + if (size > 0 && code[0] == LUA_SIGNATURE[0]) { + lua_pushnil(L); + lua_pushliteral(L, "Bytecode prohibited when mod security is enabled."); + return 2; + } + if (luaL_loadbuffer(L, code, size, chunk_name)) { + lua_pushnil(L); + lua_insert(L, lua_gettop(L) - 1); + return 2; + } + return 1; +} + + +int ScriptApiSecurity::sl_g_require(lua_State *L) +{ + lua_pushliteral(L, "require() is disabled when mod security is on."); + return lua_error(L); +} + + +int ScriptApiSecurity::sl_io_open(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + + push_original(L, "io", "open"); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_call(L, 2, 2); + return 2; +} + + +int ScriptApiSecurity::sl_io_input(lua_State *L) +{ + if (lua_isstring(L, 1)) { + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + push_original(L, "io", "input"); + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + return 1; +} + + +int ScriptApiSecurity::sl_io_output(lua_State *L) +{ + if (lua_isstring(L, 1)) { + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + push_original(L, "io", "output"); + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + return 1; +} + + +int ScriptApiSecurity::sl_io_lines(lua_State *L) +{ + if (lua_isstring(L, 1)) { + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + } + + push_original(L, "io", "lines"); + lua_pushvalue(L, 1); + int top_precall = lua_gettop(L); + lua_call(L, 1, LUA_MULTRET); + // Return number of arguments returned by the function, + // adjusting for the function being poped. + return lua_gettop(L) - (top_precall - 1); +} + + +int ScriptApiSecurity::sl_os_rename(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + const char *path1 = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path1); + + luaL_checktype(L, 2, LUA_TSTRING); + const char *path2 = lua_tostring(L, 2); + CHECK_SECURE_PATH(L, path2); + + push_original(L, "os", "rename"); + lua_pushvalue(L, 1); + lua_pushvalue(L, 2); + lua_call(L, 2, 2); + return 2; +} + + +int ScriptApiSecurity::sl_os_remove(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TSTRING); + const char *path = lua_tostring(L, 1); + CHECK_SECURE_PATH(L, path); + + push_original(L, "os", "remove"); + lua_pushvalue(L, 1); + lua_call(L, 1, 2); + return 2; +} + diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h new file mode 100644 index 000000000..4a4389cf5 --- /dev/null +++ b/src/script/cpp_api/s_security.h @@ -0,0 +1,70 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef S_SECURITY_H +#define S_SECURITY_H + +#include "cpp_api/s_base.h" + + +#define CHECK_SECURE_PATH(L, path) \ + if (!ScriptApiSecurity::checkPath(L, path)) { \ + lua_pushstring(L, (std::string("Attempt to access external file ") + \ + path + " with mod security on.").c_str()); \ + lua_error(L); \ + } +#define CHECK_SECURE_PATH_OPTIONAL(L, path) \ + if (ScriptApiSecurity::isSecure(L)) { \ + CHECK_SECURE_PATH(L, path); \ + } + + +class ScriptApiSecurity : virtual public ScriptApiBase +{ +public: + // Sets up security on the ScriptApi's Lua state + void initializeSecurity(); + // Checks if the Lua state has been secured + static bool isSecure(lua_State *L); + // Loads a file as Lua code safely (doesn't allow bytecode). + static bool safeLoadFile(lua_State *L, const char *path); + // Checks if mods are allowed to read and write to the path + static bool checkPath(lua_State *L, const char *path); + +private: + // Syntax: "sl_" <Library name or 'g' (global)> '_' <Function name> + // (sl stands for Secure Lua) + + static int sl_g_dofile(lua_State *L); + static int sl_g_load(lua_State *L); + static int sl_g_loadfile(lua_State *L); + static int sl_g_loadstring(lua_State *L); + static int sl_g_require(lua_State *L); + + static int sl_io_open(lua_State *L); + static int sl_io_input(lua_State *L); + static int sl_io_output(lua_State *L); + static int sl_io_lines(lua_State *L); + + static int sl_os_rename(lua_State *L); + static int sl_os_remove(lua_State *L); +}; + +#endif + diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp index 21fe164aa..ec2f9c0af 100644 --- a/src/script/cpp_api/s_server.cpp +++ b/src/script/cpp_api/s_server.cpp @@ -32,8 +32,7 @@ bool ScriptApiServer::getAuth(const std::string &playername, if (lua_type(L, -1) != LUA_TFUNCTION) throw LuaError("Authentication handler missing get_auth"); lua_pushstring(L, playername.c_str()); - if (lua_pcall(L, 1, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler)); lua_remove(L, -2); // Remove auth handler // nil = login not allowed @@ -68,6 +67,9 @@ void ScriptApiServer::getAuthHandler() lua_pop(L, 1); lua_getfield(L, -1, "builtin_auth_handler"); } + + setOriginFromTable(-1); + lua_remove(L, -2); // Remove core if (lua_type(L, -1) != LUA_TTABLE) throw LuaError("Authentication handler table not valid"); @@ -104,8 +106,7 @@ void ScriptApiServer::createAuth(const std::string &playername, throw LuaError("Authentication handler missing create_auth"); lua_pushstring(L, playername.c_str()); lua_pushstring(L, password.c_str()); - if (lua_pcall(L, 2, 0, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler)); } bool ScriptApiServer::setPassword(const std::string &playername, @@ -120,8 +121,7 @@ bool ScriptApiServer::setPassword(const std::string &playername, throw LuaError("Authentication handler missing set_password"); lua_pushstring(L, playername.c_str()); lua_pushstring(L, password.c_str()); - if (lua_pcall(L, 2, 1, m_errorhandler)) - scriptError(); + PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler)); return lua_toboolean(L, -1); } @@ -136,7 +136,7 @@ bool ScriptApiServer::on_chat_message(const std::string &name, // Call callbacks lua_pushstring(L, name.c_str()); lua_pushstring(L, message.c_str()); - script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR_SC); + runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC); bool ate = lua_toboolean(L, -1); return ate; } @@ -149,6 +149,6 @@ void ScriptApiServer::on_shutdown() lua_getglobal(L, "core"); lua_getfield(L, -1, "registered_on_shutdown"); // Call callbacks - script_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST); + runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); } diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index 08960d2ad..2501ce6d6 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -1,5 +1,5 @@ -# Used by server and client set(common_SCRIPT_LUA_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/l_areastore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_base.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_craft.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp @@ -18,7 +18,7 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp PARENT_SCOPE) -# Used by client only -set(minetest_SCRIPT_LUA_API_SRCS +set(client_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp PARENT_SCOPE) + diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp new file mode 100644 index 000000000..1e9075119 --- /dev/null +++ b/src/script/lua_api/l_areastore.cpp @@ -0,0 +1,401 @@ +/* +Minetest +Copyright (C) 2015 est31 <mtest31@outlook.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "lua_api/l_areastore.h" +#include "lua_api/l_internal.h" +#include "common/c_converter.h" +#include "cpp_api/s_security.h" +#include "areastore.h" +#include "filesys.h" +#ifndef ANDROID + #include "cmake_config.h" +#endif +#include <fstream> + +static inline void get_data_and_border_flags(lua_State *L, u8 start_i, + bool *borders, bool *data) +{ + if (!lua_isboolean(L, start_i)) + return; + *borders = lua_toboolean(L, start_i); + if (!lua_isboolean(L, start_i + 1)) + return; + *data = lua_toboolean(L, start_i + 1); +} + +static void push_area(lua_State *L, const Area *a, + bool include_borders, bool include_data) +{ + if (!include_borders && !include_data) { + lua_pushboolean(L, true); + } + lua_newtable(L); + if (include_borders) { + push_v3s16(L, a->minedge); + lua_setfield(L, -2, "min"); + push_v3s16(L, a->maxedge); + lua_setfield(L, -2, "max"); + } + if (include_data) { + lua_pushlstring(L, a->data.c_str(), a->data.size()); + lua_setfield(L, -2, "data"); + } +} + +static inline void push_areas(lua_State *L, const std::vector<Area *> &areas, + bool borders, bool data) +{ + lua_newtable(L); + size_t cnt = areas.size(); + for (size_t i = 0; i < cnt; i++) { + lua_pushnumber(L, areas[i]->id); + push_area(L, areas[i], borders, data); + lua_settable(L, -3); + } +} + +// garbage collector +int LuaAreaStore::gc_object(lua_State *L) +{ + LuaAreaStore *o = *(LuaAreaStore **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// get_area(id, include_borders, include_data) +int LuaAreaStore::l_get_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + u32 id = luaL_checknumber(L, 2); + + bool include_borders = true; + bool include_data = false; + get_data_and_border_flags(L, 3, &include_borders, &include_data); + + const Area *res; + + res = ast->getArea(id); + push_area(L, res, include_borders, include_data); + + return 1; +} + +// get_areas_for_pos(pos, include_borders, include_data) +int LuaAreaStore::l_get_areas_for_pos(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + v3s16 pos = check_v3s16(L, 2); + + bool include_borders = true; + bool include_data = false; + get_data_and_border_flags(L, 3, &include_borders, &include_data); + + std::vector<Area *> res; + + ast->getAreasForPos(&res, pos); + push_areas(L, res, include_borders, include_data); + + return 1; +} + +// get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data) +int LuaAreaStore::l_get_areas_in_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + v3s16 minedge = check_v3s16(L, 2); + v3s16 maxedge = check_v3s16(L, 3); + + bool include_borders = true; + bool include_data = false; + bool accept_overlap = false; + if (lua_isboolean(L, 4)) { + accept_overlap = lua_toboolean(L, 4); + get_data_and_border_flags(L, 5, &include_borders, &include_data); + } + std::vector<Area *> res; + + ast->getAreasInArea(&res, minedge, maxedge, accept_overlap); + push_areas(L, res, include_borders, include_data); + + return 1; +} + +// insert_area(edge1, edge2, data) +int LuaAreaStore::l_insert_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + Area a; + + a.minedge = check_v3s16(L, 2); + a.maxedge = check_v3s16(L, 3); + + a.extremifyEdges(); + a.id = ast->getFreeId(a.minedge, a.maxedge); + + if (a.id == AREA_ID_INVALID) { + // couldn't get free id + lua_pushnil(L); + return 1; + } + + size_t d_len; + const char *data = luaL_checklstring(L, 4, &d_len); + + a.data = std::string(data, d_len); + + ast->insertArea(a); + + lua_pushnumber(L, a.id); + return 1; +} + +// reserve(count) +int LuaAreaStore::l_reserve(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + size_t count = luaL_checknumber(L, 2); + ast->reserve(count); + return 0; +} + +// remove_area(id) +int LuaAreaStore::l_remove_area(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + u32 id = luaL_checknumber(L, 2); + bool success = ast->removeArea(id); + + lua_pushboolean(L, success); + return 1; +} + +// set_cache_params(params) +int LuaAreaStore::l_set_cache_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + luaL_checktype(L, 2, LUA_TTABLE); + + bool enabled = getboolfield_default(L, 2, "enabled", true); + u8 block_radius = getintfield_default(L, 2, "block_radius", 64); + size_t limit = getintfield_default(L, 2, "block_radius", 1000); + + ast->setCacheParams(enabled, block_radius, limit); + + return 0; +} + +#if 0 +// to_string() +int LuaAreaStore::l_to_string(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + std::ostringstream os(std::ios_base::binary); + ast->serialize(os); + std::string str = os.str(); + + lua_pushlstring(L, str.c_str(), str.length()); + return 1; +} + +// to_file(filename) +int LuaAreaStore::l_to_file(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + const char *filename = luaL_checkstring(L, 2); + CHECK_SECURE_PATH_OPTIONAL(L, filename); + + std::ostringstream os(std::ios_base::binary); + ast->serialize(os); + + lua_pushboolean(L, fs::safeWriteToFile(filename, os.str())); + return 1; +} + +// from_string(str) +int LuaAreaStore::l_from_string(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + size_t len; + const char *str = luaL_checklstring(L, 2, &len); + + std::istringstream is(std::string(str, len), std::ios::binary); + bool success = ast->deserialize(is); + + lua_pushboolean(L, success); + return 1; +} + +// from_file(filename) +int LuaAreaStore::l_from_file(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = checkobject(L, 1); + AreaStore *ast = o->as; + + const char *filename = luaL_checkstring(L, 2); + CHECK_SECURE_PATH_OPTIONAL(L, filename); + + std::ifstream is(filename, std::ios::binary); + bool success = ast->deserialize(is); + + lua_pushboolean(L, success); + return 1; +} +#endif + +LuaAreaStore::LuaAreaStore() +{ +#if USE_SPATIAL + this->as = new SpatialAreaStore(); +#else + this->as = new VectorAreaStore(); +#endif +} + +LuaAreaStore::LuaAreaStore(const std::string &type) +{ +#if USE_SPATIAL + if (type == "LibSpatial") { + this->as = new SpatialAreaStore(); + } else +#endif + { + this->as = new VectorAreaStore(); + } +} + +LuaAreaStore::~LuaAreaStore() +{ + delete as; +} + +// LuaAreaStore() +// Creates an LuaAreaStore and leaves it on top of stack +int LuaAreaStore::create_object(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaAreaStore *o = (lua_isstring(L, 1)) ? + new LuaAreaStore(lua_tostring(L, 1)) : + new LuaAreaStore(); + + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaAreaStore *LuaAreaStore::checkobject(lua_State *L, int narg) +{ + NO_MAP_LOCK_REQUIRED; + + luaL_checktype(L, narg, LUA_TUSERDATA); + + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(LuaAreaStore **)ud; // unbox pointer +} + +void LuaAreaStore::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (AreaStore()) + lua_register(L, className, create_object); +} + +const char LuaAreaStore::className[] = "AreaStore"; +const luaL_reg LuaAreaStore::methods[] = { + luamethod(LuaAreaStore, get_area), + luamethod(LuaAreaStore, get_areas_for_pos), + luamethod(LuaAreaStore, get_areas_in_area), + luamethod(LuaAreaStore, insert_area), + luamethod(LuaAreaStore, reserve), + luamethod(LuaAreaStore, remove_area), + luamethod(LuaAreaStore, set_cache_params), + /* luamethod(LuaAreaStore, to_string), + luamethod(LuaAreaStore, to_file), + luamethod(LuaAreaStore, from_string), + luamethod(LuaAreaStore, from_file),*/ + {0,0} +}; diff --git a/src/script/lua_api/l_areastore.h b/src/script/lua_api/l_areastore.h new file mode 100644 index 000000000..a25529627 --- /dev/null +++ b/src/script/lua_api/l_areastore.h @@ -0,0 +1,70 @@ +/* +Minetest +Copyright (C) 2015 est31 <mtest31@outlook.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef L_AREASTORE_H_ +#define L_AREASTORE_H_ + +#include "lua_api/l_base.h" +#include "irr_v3d.h" +#include "areastore.h" + +/* + AreaStore + */ +class LuaAreaStore : public ModApiBase { +private: + + static const char className[]; + static const luaL_reg methods[]; + + static int gc_object(lua_State *L); + + static int l_get_area(lua_State *L); + + static int l_get_areas_for_pos(lua_State *L); + static int l_get_areas_in_area(lua_State *L); + static int l_insert_area(lua_State *L); + static int l_reserve(lua_State *L); + static int l_remove_area(lua_State *L); + + static int l_set_cache_params(lua_State *L); + + /* static int l_to_string(lua_State *L); + static int l_to_file(lua_State *L); + + static int l_from_string(lua_State *L); + static int l_from_file(lua_State *L); */ + +public: + AreaStore *as; + + LuaAreaStore(); + LuaAreaStore(const std::string &type); + ~LuaAreaStore(); + + // AreaStore() + // Creates a AreaStore and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaAreaStore *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +#endif /* L_AREASTORE_H_ */ diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index b8d673ee4..6ad3e4ba2 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -20,8 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_base.h" #include "lua_api/l_internal.h" #include "cpp_api/s_base.h" +#include <mods.h> +#include <server.h> -ScriptApiBase* ModApiBase::getScriptApiBase(lua_State *L) { +ScriptApiBase *ModApiBase::getScriptApiBase(lua_State *L) +{ // Get server from registry lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi"); ScriptApiBase *sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1); @@ -29,23 +32,42 @@ ScriptApiBase* ModApiBase::getScriptApiBase(lua_State *L) { return sapi_ptr; } -Server* ModApiBase::getServer(lua_State *L) { +Server *ModApiBase::getServer(lua_State *L) +{ return getScriptApiBase(L)->getServer(); } -Environment* ModApiBase::getEnv(lua_State *L) { +Environment *ModApiBase::getEnv(lua_State *L) +{ return getScriptApiBase(L)->getEnv(); } -GUIEngine* ModApiBase::getGuiEngine(lua_State *L) { +GUIEngine *ModApiBase::getGuiEngine(lua_State *L) +{ return getScriptApiBase(L)->getGuiEngine(); } -bool ModApiBase::registerFunction(lua_State *L, - const char *name, - lua_CFunction fct, - int top - ) { +std::string ModApiBase::getCurrentModPath(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + const char *current_mod_name = lua_tostring(L, -1); + if (!current_mod_name) + return "."; + + const ModSpec *mod = getServer(L)->getModSpec(current_mod_name); + if (!mod) + return "."; + + return mod->path; +} + + +bool ModApiBase::registerFunction( + lua_State *L, + const char *name, + lua_CFunction fct, + int top) +{ //TODO check presence first! lua_pushstring(L,name); diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index debbcd09b..641013dfd 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -35,11 +35,13 @@ class GUIEngine; class ModApiBase { -protected: +public: static ScriptApiBase* getScriptApiBase(lua_State *L); static Server* getServer(lua_State *L); static Environment* getEnv(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); + // When we are not loading the mod, this function returns "." + static std::string getCurrentModPath(lua_State *L); // Get an arbitrary subclass of ScriptApiBase // by using dynamic_cast<> on getScriptApiBase() diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp index 8f8efbfbc..391a0133d 100644 --- a/src/script/lua_api/l_craft.cpp +++ b/src/script/lua_api/l_craft.cpp @@ -173,7 +173,7 @@ int ModApiCraft::l_register_craft(lua_State *L) CraftDefinition *def = new CraftDefinitionShaped( output, width, recipe, replacements); - craftdef->registerCraft(def); + craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionShapeless @@ -205,7 +205,7 @@ int ModApiCraft::l_register_craft(lua_State *L) CraftDefinition *def = new CraftDefinitionShapeless( output, recipe, replacements); - craftdef->registerCraft(def); + craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionToolRepair @@ -216,7 +216,7 @@ int ModApiCraft::l_register_craft(lua_State *L) CraftDefinition *def = new CraftDefinitionToolRepair( additional_wear); - craftdef->registerCraft(def); + craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionCooking @@ -246,7 +246,7 @@ int ModApiCraft::l_register_craft(lua_State *L) CraftDefinition *def = new CraftDefinitionCooking( output, recipe, cooktime, replacements); - craftdef->registerCraft(def); + craftdef->registerCraft(def, getServer(L)); } /* CraftDefinitionFuel @@ -270,7 +270,7 @@ int ModApiCraft::l_register_craft(lua_State *L) CraftDefinition *def = new CraftDefinitionFuel( recipe, burntime, replacements); - craftdef->registerCraft(def); + craftdef->registerCraft(def, getServer(L)); } else { @@ -303,18 +303,23 @@ int ModApiCraft::l_get_craft_result(lua_State *L) ICraftDefManager *cdef = gdef->cdef(); CraftInput input(method, width, items); CraftOutput output; - bool got = cdef->getCraftResult(input, output, true, gdef); + std::vector<ItemStack> output_replacements; + bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef); lua_newtable(L); // output table - if(got){ + if (got) { ItemStack item; item.deSerialize(output.item, gdef->idef()); LuaItemStack::create(L, item); lua_setfield(L, -2, "item"); setintfield(L, -1, "time", output.time); + push_items(L, output_replacements); + lua_setfield(L, -2, "replacements"); } else { LuaItemStack::create(L, ItemStack()); lua_setfield(L, -2, "item"); setintfield(L, -1, "time", 0); + lua_newtable(L); + lua_setfield(L, -2, "replacements"); } lua_newtable(L); // decremented input table lua_pushstring(L, method_s.c_str()); @@ -326,56 +331,82 @@ int ModApiCraft::l_get_craft_result(lua_State *L) return 2; } + +static void push_craft_recipe(lua_State *L, IGameDef *gdef, + const CraftDefinition *recipe, + const CraftOutput &tmpout) +{ + CraftInput input = recipe->getInput(tmpout, gdef); + CraftOutput output = recipe->getOutput(input, gdef); + + lua_newtable(L); // items + std::vector<ItemStack>::const_iterator iter = input.items.begin(); + for (u16 j = 1; iter != input.items.end(); iter++, j++) { + if (iter->empty()) + continue; + lua_pushstring(L, iter->name.c_str()); + lua_rawseti(L, -2, j); + } + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", input.width); + switch (input.method) { + case CRAFT_METHOD_NORMAL: + lua_pushstring(L, "normal"); + break; + case CRAFT_METHOD_COOKING: + lua_pushstring(L, "cooking"); + break; + case CRAFT_METHOD_FUEL: + lua_pushstring(L, "fuel"); + break; + default: + lua_pushstring(L, "unknown"); + } + lua_setfield(L, -2, "type"); + lua_pushstring(L, output.item.c_str()); + lua_setfield(L, -2, "output"); +} + +static void push_craft_recipes(lua_State *L, IGameDef *gdef, + const std::vector<CraftDefinition*> &recipes, + const CraftOutput &output) +{ + lua_createtable(L, recipes.size(), 0); + + if (recipes.empty()) { + lua_pushnil(L); + return; + } + + std::vector<CraftDefinition*>::const_iterator it = recipes.begin(); + for (unsigned i = 0; it != recipes.end(); ++it) { + lua_newtable(L); + push_craft_recipe(L, gdef, *it, output); + lua_rawseti(L, -2, ++i); + } +} + + // get_craft_recipe(result item) int ModApiCraft::l_get_craft_recipe(lua_State *L) { NO_MAP_LOCK_REQUIRED; - int k = 1; - int input_i = 1; - std::string o_item = luaL_checkstring(L,input_i); + std::string item = luaL_checkstring(L, 1); + Server *server = getServer(L); + CraftOutput output(item, 0); + std::vector<CraftDefinition*> recipes = server->cdef() + ->getCraftRecipes(output, server, 1); - IGameDef *gdef = getServer(L); - ICraftDefManager *cdef = gdef->cdef(); - CraftInput input; - CraftOutput output(o_item,0); - bool got = cdef->getCraftRecipe(input, output, gdef); - lua_newtable(L); // output table - if(got){ - lua_newtable(L); - for(std::vector<ItemStack>::const_iterator - i = input.items.begin(); - i != input.items.end(); i++, k++) - { - if (i->empty()) - { - continue; - } - lua_pushinteger(L,k); - lua_pushstring(L,i->name.c_str()); - lua_settable(L, -3); - } - lua_setfield(L, -2, "items"); - setintfield(L, -1, "width", input.width); - switch (input.method) { - case CRAFT_METHOD_NORMAL: - lua_pushstring(L,"normal"); - break; - case CRAFT_METHOD_COOKING: - lua_pushstring(L,"cooking"); - break; - case CRAFT_METHOD_FUEL: - lua_pushstring(L,"fuel"); - break; - default: - lua_pushstring(L,"unknown"); - } - lua_setfield(L, -2, "type"); - } else { + lua_createtable(L, 1, 0); + + if (recipes.empty()) { lua_pushnil(L); lua_setfield(L, -2, "items"); setintfield(L, -1, "width", 0); + return 1; } + push_craft_recipe(L, server, recipes[0], output); return 1; } @@ -384,59 +415,13 @@ int ModApiCraft::l_get_all_craft_recipes(lua_State *L) { NO_MAP_LOCK_REQUIRED; - std::string o_item = luaL_checkstring(L,1); - IGameDef *gdef = getServer(L); - ICraftDefManager *cdef = gdef->cdef(); - CraftInput input; - CraftOutput output(o_item,0); - std::vector<CraftDefinition*> recipes_list; - recipes_list = cdef->getCraftRecipes(output, gdef); - if (recipes_list.empty()) { - lua_pushnil(L); - return 1; - } + std::string item = luaL_checkstring(L, 1); + Server *server = getServer(L); + CraftOutput output(item, 0); + std::vector<CraftDefinition*> recipes = server->cdef() + ->getCraftRecipes(output, server); - lua_createtable(L, recipes_list.size(), 0); - std::vector<CraftDefinition*>::const_iterator iter = recipes_list.begin(); - for (u16 i = 0; iter != recipes_list.end(); iter++) { - CraftOutput tmpout; - tmpout.item = ""; - tmpout.time = 0; - tmpout = (*iter)->getOutput(input, gdef); - std::string query = tmpout.item; - char *fmtpos, *fmt = &query[0]; - if (strtok_r(fmt, " ", &fmtpos) == output.item) { - input = (*iter)->getInput(output, gdef); - lua_newtable(L); - lua_newtable(L); // items - std::vector<ItemStack>::const_iterator iter = input.items.begin(); - for (u16 j = 1; iter != input.items.end(); iter++, j++) { - if (iter->empty()) - continue; - lua_pushstring(L, iter->name.c_str()); - lua_rawseti(L, -2, j); - } - lua_setfield(L, -2, "items"); - setintfield(L, -1, "width", input.width); - switch (input.method) { - case CRAFT_METHOD_NORMAL: - lua_pushstring(L, "normal"); - break; - case CRAFT_METHOD_COOKING: - lua_pushstring(L, "cooking"); - break; - case CRAFT_METHOD_FUEL: - lua_pushstring(L, "fuel"); - break; - default: - lua_pushstring(L, "unknown"); - } - lua_setfield(L, -2, "type"); - lua_pushstring(L, &tmpout.item[0]); - lua_setfield(L, -2, "output"); - lua_rawseti(L, -2, ++i); - } - } + push_craft_recipes(L, server, recipes, output); return 1; } diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index cd5d253ac..28afdd071 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -49,7 +49,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); - assert(lua_checkstack(L, 20)); + sanity_check(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); lua_pushcfunction(L, script_error_handler); @@ -65,9 +65,11 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, lua_pushnumber(L, m_id); lua_gettable(L, -2); if(lua_isnil(L, -1)) - assert(0); + FATAL_ERROR(""); lua_remove(L, -2); // Remove registered_abms + scriptIface->setOriginFromTable(-1); + // Call action luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "action"); @@ -77,8 +79,11 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, pushnode(L, n, env->getGameDef()->ndef()); lua_pushnumber(L, active_object_count); lua_pushnumber(L, active_object_count_wider); - if(lua_pcall(L, 4, 0, errorhandler)) - script_error(L); + + int result = lua_pcall(L, 4, 0, errorhandler); + if (result) + scriptIface->scriptError(result, "LuaABM::trigger"); + lua_pop(L, 1); // Pop error handler } @@ -334,6 +339,22 @@ int ModApiEnvMod::l_add_node_level(lua_State *L) return 1; } +// find_nodes_with_meta(pos1, pos2) +int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L) +{ + GET_ENV_PTR; + + std::vector<v3s16> positions = env->getMap().findNodesWithMetadata( + check_v3s16(L, 1), check_v3s16(L, 2)); + + lua_newtable(L); + for (size_t i = 0; i != positions.size(); i++) { + push_v3s16(L, positions[i]); + lua_rawseti(L, -2, i + 1); + } + + return 1; +} // get_meta(pos) int ModApiEnvMod::l_get_meta(lua_State *L) @@ -402,23 +423,11 @@ int ModApiEnvMod::l_add_item(lua_State *L) return 0; lua_pushvalue(L, 1); lua_pushstring(L, item.getItemString().c_str()); - if(lua_pcall(L, 2, 1, errorhandler)) - script_error(L); + + PCALL_RESL(L, lua_pcall(L, 2, 1, errorhandler)); + lua_remove(L, errorhandler); // Remove error handler return 1; - /*lua_pushvalue(L, 1); - lua_pushstring(L, "__builtin:item"); - lua_pushstring(L, item.getItemString().c_str()); - return l_add_entity(L);*/ - /*// Do it - ServerActiveObject *obj = createItemSAO(env, pos, item.getItemString()); - int objectid = env->addActiveObject(obj); - // If failed to add, return nothing (reads as nil) - if(objectid == 0) - return 0; - // Return ObjectRef - objectrefGetOrCreate(L, obj); - return 1;*/ } // get_player_by_name(name) @@ -451,10 +460,11 @@ int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L) // Do it v3f pos = checkFloatPos(L, 1); float radius = luaL_checknumber(L, 2) * BS; - std::set<u16> ids = env->getObjectsInsideRadius(pos, radius); + std::vector<u16> ids; + env->getObjectsInsideRadius(ids, pos, radius); ScriptApiBase *script = getScriptApiBase(L); lua_createtable(L, ids.size(), 0); - std::set<u16>::const_iterator iter = ids.begin(); + std::vector<u16>::const_iterator iter = ids.begin(); for(u32 i = 0; iter != ids.end(); iter++) { ServerActiveObject *obj = env->getActiveObject(*iter); // Insert object reference into table @@ -472,7 +482,7 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L) // Do it float timeofday_f = luaL_checknumber(L, 1); - assert(timeofday_f >= 0.0 && timeofday_f <= 1.0); + sanity_check(timeofday_f >= 0.0 && timeofday_f <= 1.0); int timeofday_mh = (int)(timeofday_f * 24000.0); // This should be set directly in the environment but currently // such changes aren't immediately sent to the clients, so call @@ -530,9 +540,8 @@ int ModApiEnvMod::l_find_node_near(lua_State *L) } for(int d=1; d<=radius; d++){ - std::list<v3s16> list; - getFacePositions(list, d); - for(std::list<v3s16>::iterator i = list.begin(); + std::vector<v3s16> list = FacePositionCache::getFacePositions(d); + for(std::vector<v3s16>::iterator i = list.begin(); i != list.end(); ++i){ v3s16 p = pos + (*i); content_t c = env->getMap().getNodeNoEx(p).getContent(); @@ -555,30 +564,92 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) v3s16 minp = read_v3s16(L, 1); v3s16 maxp = read_v3s16(L, 2); std::set<content_t> filter; - if(lua_istable(L, 3)){ + if(lua_istable(L, 3)) { int table = 3; lua_pushnil(L); - while(lua_next(L, table) != 0){ + while(lua_next(L, table) != 0) { // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TSTRING); ndef->getIds(lua_tostring(L, -1), filter); // removes value, keeps key for next iteration lua_pop(L, 1); } - } else if(lua_isstring(L, 3)){ + } else if(lua_isstring(L, 3)) { + ndef->getIds(lua_tostring(L, 3), filter); + } + + std::map<content_t, u16> individual_count; + + lua_newtable(L); + u64 i = 0; + for (s16 x = minp.X; x <= maxp.X; x++) + for (s16 y = minp.Y; y <= maxp.Y; y++) + for (s16 z = minp.Z; z <= maxp.Z; z++) { + v3s16 p(x, y, z); + content_t c = env->getMap().getNodeNoEx(p).getContent(); + if (filter.count(c) != 0) { + push_v3s16(L, p); + lua_rawseti(L, -2, ++i); + individual_count[c]++; + } + } + lua_newtable(L); + for (std::set<content_t>::iterator it = filter.begin(); + it != filter.end(); ++it) { + lua_pushnumber(L, individual_count[*it]); + lua_setfield(L, -2, ndef->get(*it).name.c_str()); + } + return 2; +} + +// find_nodes_in_area_under_air(minp, maxp, nodenames) -> list of positions +// nodenames: e.g. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L) +{ + /* Note: A similar but generalized (and therefore slower) version of this + * function could be created -- e.g. find_nodes_in_area_under -- which + * would accept a node name (or ID?) or list of names that the "above node" + * should be. + * TODO + */ + + GET_ENV_PTR; + + INodeDefManager *ndef = getServer(L)->ndef(); + v3s16 minp = read_v3s16(L, 1); + v3s16 maxp = read_v3s16(L, 2); + std::set<content_t> filter; + + if (lua_istable(L, 3)) { + int table = 3; + lua_pushnil(L); + while(lua_next(L, table) != 0) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + ndef->getIds(lua_tostring(L, -1), filter); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, 3)) { ndef->getIds(lua_tostring(L, 3), filter); } lua_newtable(L); u64 i = 0; - for(s16 x = minp.X; x <= maxp.X; x++) - for(s16 y = minp.Y; y <= maxp.Y; y++) - for(s16 z = minp.Z; z <= maxp.Z; z++) { + for (s16 x = minp.X; x <= maxp.X; x++) + for (s16 z = minp.Z; z <= maxp.Z; z++) { + s16 y = minp.Y; v3s16 p(x, y, z); content_t c = env->getMap().getNodeNoEx(p).getContent(); - if(filter.count(c) != 0) { - push_v3s16(L, p); - lua_rawseti(L, -2, ++i); + for (; y <= maxp.Y; y++) { + v3s16 psurf(x, y + 1, z); + content_t csurf = env->getMap().getNodeNoEx(psurf).getContent(); + if(c != CONTENT_AIR && csurf == CONTENT_AIR && + filter.count(c) != 0) { + push_v3s16(L, v3s16(x, y, z)); + lua_rawseti(L, -2, ++i); + } + c = csurf; } } return 1; @@ -702,10 +773,12 @@ int ModApiEnvMod::l_delete_area(lua_State *L) for (s16 y = bpmin.Y; y <= bpmax.Y; y++) for (s16 x = bpmin.X; x <= bpmax.X; x++) { v3s16 bp(x, y, z); - if (map.deleteBlock(bp)) + if (map.deleteBlock(bp)) { + env->setStaticForActiveObjectsInBlock(bp, false); event.modified_blocks.insert(bp); - else + } else { success = false; + } } map.dispatchEvent(&event); @@ -872,6 +945,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(set_node_level); API_FCT(add_node_level); API_FCT(add_entity); + API_FCT(find_nodes_with_meta); API_FCT(get_meta); API_FCT(get_node_timer); API_FCT(get_player_by_name); @@ -881,6 +955,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top) API_FCT(get_gametime); API_FCT(find_node_near); API_FCT(find_nodes_in_area); + API_FCT(find_nodes_in_area_under_air); API_FCT(delete_area); API_FCT(get_perlin); API_FCT(get_perlin_map); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index bfaea1c4d..0d4ca788e 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -64,7 +64,6 @@ private: // pos = {x=num, y=num, z=num} static int l_punch_node(lua_State *L); - // get_node_max_level(pos) // pos = {x=num, y=num, z=num} static int l_get_node_max_level(lua_State *L); @@ -81,6 +80,9 @@ private: // pos = {x=num, y=num, z=num} static int l_add_node_level(lua_State *L); + // find_nodes_with_meta(pos1, pos2) + static int l_find_nodes_with_meta(lua_State *L); + // get_meta(pos) static int l_get_meta(lua_State *L); @@ -119,6 +121,10 @@ private: // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_nodes_in_area(lua_State *L); + // find_surface_nodes_in_area(minp, maxp, nodenames) -> list of positions + // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" + static int l_find_nodes_in_area_under_air(lua_State *L); + // delete_area(p1, p2) -> true/false static int l_delete_area(lua_State *L); diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h index 5936ac046..1e40c5c4a 100644 --- a/src/script/lua_api/l_internal.h +++ b/src/script/lua_api/l_internal.h @@ -33,13 +33,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #define API_FCT(name) registerFunction(L, #name, l_##name,top) #define ASYNC_API_FCT(name) engine.registerFunction(#name, l_##name) -#if (defined(WIN32) || defined(_WIN32_WCE)) #define NO_MAP_LOCK_REQUIRED + +/* +#if (defined(WIN32) || defined(_WIN32_WCE)) + #define NO_MAP_LOCK_REQUIRED #else -#include "main.h" -#include "profiler.h" -#define NO_MAP_LOCK_REQUIRED \ - ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD) + #include "profiler.h" + #define NO_MAP_LOCK_REQUIRED \ + ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD) #endif +*/ #endif /* L_INTERNAL_H_ */ diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 2bed2a255..92311d6fc 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "emerge.h" #include "sound.h" #include "settings.h" -#include "main.h" // for g_settings #include "log.h" #include "EDriverTypes.h" @@ -90,7 +89,7 @@ int ModApiMainMenu::getBoolData(lua_State *L, std::string name,bool& valid) int ModApiMainMenu::l_update_formspec(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); if (engine->m_startgame) return 0; @@ -109,21 +108,25 @@ int ModApiMainMenu::l_update_formspec(lua_State *L) int ModApiMainMenu::l_start(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); //update c++ gamedata from lua table bool valid = false; + MainMenuData *data = engine->m_data; - engine->m_data->selected_world = getIntegerData(L, "selected_world",valid) -1; - engine->m_data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid); - engine->m_data->name = getTextData(L,"playername"); - engine->m_data->password = getTextData(L,"password"); - engine->m_data->address = getTextData(L,"address"); - engine->m_data->port = getTextData(L,"port"); - engine->m_data->serverdescription = getTextData(L,"serverdescription"); - engine->m_data->servername = getTextData(L,"servername"); + data->selected_world = getIntegerData(L, "selected_world",valid) -1; + data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid); + data->do_reconnect = getBoolData(L, "do_reconnect", valid); + if (!data->do_reconnect) { + data->name = getTextData(L,"playername"); + data->password = getTextData(L,"password"); + data->address = getTextData(L,"address"); + data->port = getTextData(L,"port"); + } + data->serverdescription = getTextData(L,"serverdescription"); + data->servername = getTextData(L,"servername"); //close menu next time engine->m_startgame = true; @@ -134,7 +137,7 @@ int ModApiMainMenu::l_start(lua_State *L) int ModApiMainMenu::l_close(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); engine->m_kill = true; return 0; @@ -144,7 +147,7 @@ int ModApiMainMenu::l_close(lua_State *L) int ModApiMainMenu::l_set_background(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); std::string backgroundlevel(luaL_checkstring(L, 1)); std::string texturename(luaL_checkstring(L, 2)); @@ -189,7 +192,7 @@ int ModApiMainMenu::l_set_background(lua_State *L) int ModApiMainMenu::l_set_clouds(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); bool value = lua_toboolean(L,1); @@ -209,9 +212,9 @@ int ModApiMainMenu::l_get_textlist_index(lua_State *L) int ModApiMainMenu::l_get_table_index(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); - std::wstring tablename(narrow_to_wide(luaL_checkstring(L, 1))); + std::string tablename(luaL_checkstring(L, 1)); GUITable *table = engine->m_menu->getTable(tablename); s32 selection = table ? table->getSelected() : 0; @@ -617,7 +620,7 @@ int ModApiMainMenu::l_delete_favorite(lua_State *L) int ModApiMainMenu::l_show_keys_menu(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); GUIKeyChangeMenu *kmenu = new GUIKeyChangeMenu( engine->m_device->getGUIEnvironment(), @@ -644,15 +647,12 @@ int ModApiMainMenu::l_create_world(lua_State *L) (gameidx < (int) games.size())) { // Create world if it doesn't exist - if(!initializeWorld(path, games[gameidx].id)){ + if (!loadGameConfAndInitWorld(path, games[gameidx])) { lua_pushstring(L, "Failed to initialize world"); - - } - else { - lua_pushnil(L); + } else { + lua_pushnil(L); } - } - else { + } else { lua_pushstring(L, "Invalid game index"); } return 1; @@ -692,7 +692,7 @@ int ModApiMainMenu::l_delete_world(lua_State *L) int ModApiMainMenu::l_set_topleft_text(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); std::string text = ""; @@ -758,30 +758,6 @@ int ModApiMainMenu::l_get_texturepath_share(lua_State *L) } /******************************************************************************/ -int ModApiMainMenu::l_get_dirlist(lua_State *L) -{ - const char *path = luaL_checkstring(L, 1); - bool dironly = lua_toboolean(L, 2); - - std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path); - - unsigned int index = 1; - lua_newtable(L); - int table = lua_gettop(L); - - for (unsigned int i=0;i< dirlist.size(); i++) { - if ((dirlist[i].dir) || (dironly == false)) { - lua_pushnumber(L,index); - lua_pushstring(L,dirlist[i].name.c_str()); - lua_settable(L, table); - index++; - } - } - - return 1; -} - -/******************************************************************************/ int ModApiMainMenu::l_create_dir(lua_State *L) { const char *path = luaL_checkstring(L, 1); @@ -843,7 +819,7 @@ int ModApiMainMenu::l_copy_dir(lua_State *L) int ModApiMainMenu::l_extract_zip(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine); const char *zipfile = luaL_checkstring(L, 1); const char *destination = luaL_checkstring(L, 2); @@ -860,7 +836,7 @@ int ModApiMainMenu::l_extract_zip(lua_State *L) return 1; } - assert(fs->getFileArchiveCount() > 0); + sanity_check(fs->getFileArchiveCount() > 0); /**********************************************************************/ /* WARNING this is not threadsafe!! */ @@ -931,7 +907,7 @@ int ModApiMainMenu::l_extract_zip(lua_State *L) int ModApiMainMenu::l_get_mainmenu_path(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); lua_pushstring(L,engine->getScriptDir().c_str()); return 1; @@ -963,7 +939,7 @@ bool ModApiMainMenu::isMinetestPath(std::string path) int ModApiMainMenu::l_show_file_open_dialog(lua_State *L) { GUIEngine* engine = getGuiEngine(L); - assert(engine != 0); + sanity_check(engine != NULL); const char *formname= luaL_checkstring(L, 1); const char *title = luaL_checkstring(L, 2); @@ -983,7 +959,7 @@ int ModApiMainMenu::l_show_file_open_dialog(lua_State *L) /******************************************************************************/ int ModApiMainMenu::l_get_version(lua_State *L) { - lua_pushstring(L, minetest_version_simple); + lua_pushstring(L, g_version_string); return 1; } @@ -1060,10 +1036,32 @@ int ModApiMainMenu::l_get_video_drivers(lua_State *L) } /******************************************************************************/ +int ModApiMainMenu::l_get_video_modes(lua_State *L) +{ + std::vector<core::vector3d<u32> > videomodes + = porting::getSupportedVideoModes(); + + lua_newtable(L); + for (u32 i = 0; i != videomodes.size(); i++) { + lua_newtable(L); + lua_pushnumber(L, videomodes[i].X); + lua_setfield(L, -2, "w"); + lua_pushnumber(L, videomodes[i].Y); + lua_setfield(L, -2, "h"); + lua_pushnumber(L, videomodes[i].Z); + lua_setfield(L, -2, "depth"); + + lua_rawseti(L, -2, i + 1); + } + + return 1; +} + +/******************************************************************************/ int ModApiMainMenu::l_gettext(lua_State *L) { std::wstring wtext = wstrgettext((std::string) luaL_checkstring(L, 1)); - lua_pushstring(L, wide_to_narrow(wtext).c_str()); + lua_pushstring(L, wide_to_utf8(wtext).c_str()); return 1; } @@ -1118,8 +1116,8 @@ int ModApiMainMenu::l_do_async_callback(lua_State *L) const char* serialized_param_raw = luaL_checklstring(L, 2, ¶m_length); - assert(serialized_func_raw != NULL); - assert(serialized_param_raw != NULL); + sanity_check(serialized_func_raw != NULL); + sanity_check(serialized_param_raw != NULL); std::string serialized_func = std::string(serialized_func_raw, func_length); std::string serialized_param = std::string(serialized_param_raw, param_length); @@ -1152,7 +1150,6 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(get_gamepath); API_FCT(get_texturepath); API_FCT(get_texturepath_share); - API_FCT(get_dirlist); API_FCT(create_dir); API_FCT(delete_dir); API_FCT(copy_dir); @@ -1167,6 +1164,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(sound_stop); API_FCT(gettext); API_FCT(get_video_drivers); + API_FCT(get_video_modes); API_FCT(get_screen_info); API_FCT(get_min_supp_proto); API_FCT(get_max_supp_proto); @@ -1185,7 +1183,6 @@ void ModApiMainMenu::InitializeAsync(AsyncEngine& engine) ASYNC_API_FCT(get_gamepath); ASYNC_API_FCT(get_texturepath); ASYNC_API_FCT(get_texturepath_share); - ASYNC_API_FCT(get_dirlist); ASYNC_API_FCT(create_dir); ASYNC_API_FCT(delete_dir); ASYNC_API_FCT(copy_dir); diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index 8b21a93aa..9c1fed272 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -137,6 +137,8 @@ private: static int l_get_video_drivers(lua_State *L); + static int l_get_video_modes(lua_State *L); + //version compatibility static int l_get_min_supp_proto(lua_State *L); diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index d470cef88..d30b68054 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "cpp_api/s_security.h" #include "util/serialize.h" #include "server.h" #include "environment.h" @@ -32,18 +33,17 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_schematic.h" #include "mapgen_v5.h" #include "mapgen_v7.h" +#include "filesys.h" #include "settings.h" -#include "main.h" #include "log.h" - struct EnumString ModApiMapgen::es_BiomeTerrainType[] = { - {BIOME_TYPE_NORMAL, "normal"}, - {BIOME_TYPE_LIQUID, "liquid"}, - {BIOME_TYPE_NETHER, "nether"}, - {BIOME_TYPE_AETHER, "aether"}, - {BIOME_TYPE_FLAT, "flat"}, + {BIOME_NORMAL, "normal"}, + {BIOME_LIQUID, "liquid"}, + {BIOME_NETHER, "nether"}, + {BIOME_AETHER, "aether"}, + {BIOME_FLAT, "flat"}, {0, NULL}, }; @@ -68,10 +68,10 @@ struct EnumString ModApiMapgen::es_MapgenObject[] = struct EnumString ModApiMapgen::es_OreType[] = { - {ORE_TYPE_SCATTER, "scatter"}, - {ORE_TYPE_SHEET, "sheet"}, - {ORE_TYPE_BLOB, "blob"}, - {ORE_TYPE_VEIN, "vein"}, + {ORE_SCATTER, "scatter"}, + {ORE_SHEET, "sheet"}, + {ORE_BLOB, "blob"}, + {ORE_VEIN, "vein"}, {0, NULL}, }; @@ -85,116 +85,238 @@ struct EnumString ModApiMapgen::es_Rotation[] = {0, NULL}, }; +struct EnumString ModApiMapgen::es_SchematicFormatType[] = +{ + {SCHEM_FMT_HANDLE, "handle"}, + {SCHEM_FMT_MTS, "mts"}, + {SCHEM_FMT_LUA, "lua"}, + {0, NULL}, +}; + +ObjDef *get_objdef(lua_State *L, int index, ObjDefManager *objmgr); + +Biome *get_or_load_biome(lua_State *L, int index, + BiomeManager *biomemgr); +Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef); +size_t get_biome_list(lua_State *L, int index, + BiomeManager *biomemgr, std::set<u8> *biome_id_list); + +Schematic *get_or_load_schematic(lua_State *L, int index, + SchematicManager *schemmgr, StringMap *replace_names); +Schematic *load_schematic(lua_State *L, int index, INodeDefManager *ndef, + StringMap *replace_names); +Schematic *load_schematic_from_def(lua_State *L, int index, + INodeDefManager *ndef, StringMap *replace_names); +bool read_schematic_def(lua_State *L, int index, + Schematic *schem, std::vector<std::string> *names); + +bool read_deco_simple(lua_State *L, DecoSimple *deco); +bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco); + /////////////////////////////////////////////////////////////////////////////// +ObjDef *get_objdef(lua_State *L, int index, ObjDefManager *objmgr) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; -bool read_schematic(lua_State *L, int index, Schematic *schem, - INodeDefManager *ndef, std::map<std::string, std::string> &replace_names) + // If a number, assume this is a handle to an object def + if (lua_isnumber(L, index)) + return objmgr->get(lua_tointeger(L, index)); + + // If a string, assume a name is given instead + if (lua_isstring(L, index)) + return objmgr->getByName(lua_tostring(L, index)); + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +Schematic *get_or_load_schematic(lua_State *L, int index, + SchematicManager *schemmgr, StringMap *replace_names) { + if (index < 0) + index = lua_gettop(L) + 1 + index; + + Schematic *schem = (Schematic *)get_objdef(L, index, schemmgr); + if (schem) + return schem; + + schem = load_schematic(L, index, schemmgr->getNodeDef(), + replace_names); + if (!schem) + return NULL; + + if (schemmgr->add(schem) == OBJDEF_INVALID_HANDLE) { + delete schem; + return NULL; + } + + return schem; +} + + +Schematic *load_schematic(lua_State *L, int index, INodeDefManager *ndef, + StringMap *replace_names) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + Schematic *schem = NULL; + + if (lua_istable(L, index)) { + schem = load_schematic_from_def(L, index, ndef, + replace_names); + if (!schem) { + delete schem; + return NULL; + } + } else if (lua_isnumber(L, index)) { + return NULL; + } else if (lua_isstring(L, index)) { + schem = SchematicManager::create(SCHEMATIC_NORMAL); + + std::string filepath = lua_tostring(L, index); + if (!fs::IsPathAbsolute(filepath)) + filepath = ModApiBase::getCurrentModPath(L) + DIR_DELIM + filepath; + + if (!schem->loadSchematicFromFile(filepath, ndef, + replace_names)) { + delete schem; + return NULL; + } + } + + return schem; +} + + +Schematic *load_schematic_from_def(lua_State *L, int index, + INodeDefManager *ndef, StringMap *replace_names) +{ + Schematic *schem = SchematicManager::create(SCHEMATIC_NORMAL); + + if (!read_schematic_def(L, index, schem, &schem->m_nodenames)) { + delete schem; + return NULL; + } + + size_t num_nodes = schem->m_nodenames.size(); + + schem->m_nnlistsizes.push_back(num_nodes); + + if (replace_names) { + for (size_t i = 0; i != num_nodes; i++) { + StringMap::iterator it = replace_names->find(schem->m_nodenames[i]); + if (it != replace_names->end()) + schem->m_nodenames[i] = it->second; + } + } + + if (ndef) + ndef->pendNodeResolve(schem); + + return schem; +} + + +bool read_schematic_def(lua_State *L, int index, + Schematic *schem, std::vector<std::string> *names) +{ + if (!lua_istable(L, index)) + return false; + //// Get schematic size lua_getfield(L, index, "size"); - v3s16 size = read_v3s16(L, -1); + v3s16 size = check_v3s16(L, -1); lua_pop(L, 1); + schem->size = size; + //// Get schematic data lua_getfield(L, index, "data"); luaL_checktype(L, -1, LUA_TTABLE); - int numnodes = size.X * size.Y * size.Z; - MapNode *schemdata = new MapNode[numnodes]; - int i = 0; + u32 numnodes = size.X * size.Y * size.Z; + schem->schemdata = new MapNode[numnodes]; - lua_pushnil(L); - while (lua_next(L, -2)) { - if (i >= numnodes) { - i++; - lua_pop(L, 1); + size_t names_base = names->size(); + std::map<std::string, content_t> name_id_map; + + u32 i = 0; + for (lua_pushnil(L); lua_next(L, -2); i++, lua_pop(L, 1)) { + if (i >= numnodes) continue; - } - // same as readnode, except param1 default is MTSCHEM_PROB_CONST - lua_getfield(L, -1, "name"); - std::string name = luaL_checkstring(L, -1); - lua_pop(L, 1); + //// Read name + std::string name; + if (!getstringfield(L, -1, "name", name)) + throw LuaError("Schematic data definition with missing name field"); + //// Read param1/prob u8 param1; - lua_getfield(L, -1, "param1"); - param1 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : MTSCHEM_PROB_ALWAYS; - lua_pop(L, 1); - - u8 param2; - lua_getfield(L, -1, "param2"); - param2 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : 0; - lua_pop(L, 1); - - std::map<std::string, std::string>::iterator it; - it = replace_names.find(name); - if (it != replace_names.end()) - name = it->second; + if (!getintfield(L, -1, "param1", param1) && + !getintfield(L, -1, "prob", param1)) + param1 = MTSCHEM_PROB_ALWAYS_OLD; + + //// Read param2 + u8 param2 = getintfield_default(L, -1, "param2", 0); + + //// Find or add new nodename-to-ID mapping + std::map<std::string, content_t>::iterator it = name_id_map.find(name); + content_t name_index; + if (it != name_id_map.end()) { + name_index = it->second; + } else { + name_index = names->size() - names_base; + name_id_map[name] = name_index; + names->push_back(name); + } - schemdata[i] = MapNode(ndef, name, param1, param2); + //// Perform probability/force_place fixup on param1 + param1 >>= 1; + if (getboolfield_default(L, -1, "force_place", false)) + param1 |= MTSCHEM_FORCE_PLACE; - i++; - lua_pop(L, 1); + //// Actually set the node in the schematic + schem->schemdata[i] = MapNode(name_index, param1, param2); } if (i != numnodes) { - errorstream << "read_schematic: incorrect number of " + errorstream << "read_schematic_def: incorrect number of " "nodes provided in raw schematic data (got " << i << ", expected " << numnodes << ")." << std::endl; - delete schemdata; return false; } //// Get Y-slice probability values (if present) - u8 *slice_probs = new u8[size.Y]; - for (i = 0; i != size.Y; i++) - slice_probs[i] = MTSCHEM_PROB_ALWAYS; + schem->slice_probs = new u8[size.Y]; + for (i = 0; i != (u32) size.Y; i++) + schem->slice_probs[i] = MTSCHEM_PROB_ALWAYS; lua_getfield(L, index, "yslice_prob"); if (lua_istable(L, -1)) { - lua_pushnil(L); - while (lua_next(L, -2)) { - if (getintfield(L, -1, "ypos", i) && i >= 0 && i < size.Y) { - slice_probs[i] = getintfield_default(L, -1, - "prob", MTSCHEM_PROB_ALWAYS); - } - lua_pop(L, 1); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + u16 ypos; + if (!getintfield(L, -1, "ypos", ypos) || (ypos >= size.Y) || + !getintfield(L, -1, "prob", schem->slice_probs[ypos])) + continue; + + schem->slice_probs[ypos] >>= 1; } } - // Here, we read the nodes directly from the INodeDefManager - there is no - // need for pending node resolutions so we'll mark this schematic as updated - schem->flags = SCHEM_CIDS_UPDATED; - - schem->size = size; - schem->schemdata = schemdata; - schem->slice_probs = slice_probs; return true; } -bool get_schematic(lua_State *L, int index, Schematic *schem, - INodeDefManager *ndef, std::map<std::string, std::string> &replace_names) +void read_schematic_replacements(lua_State *L, int index, StringMap *replace_names) { if (index < 0) index = lua_gettop(L) + 1 + index; - if (lua_istable(L, index)) { - return read_schematic(L, index, schem, ndef, replace_names); - } else if (lua_isstring(L, index)) { - const char *filename = lua_tostring(L, index); - return schem->loadSchematicFromFile(filename, ndef, replace_names); - } else { - return false; - } -} - - -void read_schematic_replacements(lua_State *L, - std::map<std::string, std::string> &replace_names, int index) -{ lua_pushnil(L); while (lua_next(L, index)) { std::string replace_from; @@ -213,11 +335,119 @@ void read_schematic_replacements(lua_State *L, replace_to = lua_tostring(L, -1); } - replace_names[replace_from] = replace_to; + replace_names->insert(std::make_pair(replace_from, replace_to)); lua_pop(L, 1); } } +/////////////////////////////////////////////////////////////////////////////// + +Biome *get_or_load_biome(lua_State *L, int index, BiomeManager *biomemgr) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + Biome *biome = (Biome *)get_objdef(L, index, biomemgr); + if (biome) + return biome; + + biome = read_biome_def(L, index, biomemgr->getNodeDef()); + if (!biome) + return NULL; + + if (biomemgr->add(biome) == OBJDEF_INVALID_HANDLE) { + delete biome; + return NULL; + } + + return biome; +} + + +Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef) +{ + if (!lua_istable(L, index)) + return NULL; + + BiomeType biometype = (BiomeType)getenumfield(L, index, "type", + ModApiMapgen::es_BiomeTerrainType, BIOME_NORMAL); + Biome *b = BiomeManager::create(biometype); + + b->name = getstringfield_default(L, index, "name", ""); + b->depth_top = getintfield_default(L, index, "depth_top", 0); + b->depth_filler = getintfield_default(L, index, "depth_filler", -31000); + b->depth_water_top = getintfield_default(L, index, "depth_water_top", 0); + b->y_min = getintfield_default(L, index, "y_min", -31000); + b->y_max = getintfield_default(L, index, "y_max", 31000); + b->heat_point = getfloatfield_default(L, index, "heat_point", 0.f); + b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.f); + b->flags = 0; //reserved + + std::vector<std::string> &nn = b->m_nodenames; + nn.push_back(getstringfield_default(L, index, "node_top", "")); + nn.push_back(getstringfield_default(L, index, "node_filler", "")); + nn.push_back(getstringfield_default(L, index, "node_stone", "")); + nn.push_back(getstringfield_default(L, index, "node_water_top", "")); + nn.push_back(getstringfield_default(L, index, "node_water", "")); + nn.push_back(getstringfield_default(L, index, "node_river_water", "")); + nn.push_back(getstringfield_default(L, index, "node_dust", "")); + ndef->pendNodeResolve(b); + + return b; +} + + +size_t get_biome_list(lua_State *L, int index, + BiomeManager *biomemgr, std::set<u8> *biome_id_list) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + if (lua_isnil(L, index)) + return 0; + + bool is_single = true; + if (lua_istable(L, index)) { + lua_getfield(L, index, "name"); + is_single = !lua_isnil(L, -1); + lua_pop(L, 1); + } + + if (is_single) { + Biome *biome = get_or_load_biome(L, index, biomemgr); + if (!biome) { + errorstream << "get_biome_list: failed to get biome '" + << (lua_isstring(L, index) ? lua_tostring(L, index) : "") + << "'." << std::endl; + return 1; + } + + biome_id_list->insert(biome->index); + return 0; + } + + // returns number of failed resolutions + size_t fail_count = 0; + size_t count = 0; + + for (lua_pushnil(L); lua_next(L, index); lua_pop(L, 1)) { + count++; + Biome *biome = get_or_load_biome(L, -1, biomemgr); + if (!biome) { + fail_count++; + errorstream << "get_biome_list: failed to get biome '" + << (lua_isstring(L, -1) ? lua_tostring(L, -1) : "") + << "'" << std::endl; + continue; + } + + biome_id_list->insert(biome->index); + } + + return fail_count; +} + +/////////////////////////////////////////////////////////////////////////////// // get_mapgen_object(objectname) // returns the requested object used during map generation @@ -239,92 +469,98 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L) size_t maplen = mg->csize.X * mg->csize.Z; switch (mgobj) { - case MGOBJ_VMANIP: { - MMVManip *vm = mg->vm; + case MGOBJ_VMANIP: { + MMVManip *vm = mg->vm; - // VoxelManip object - LuaVoxelManip *o = new LuaVoxelManip(vm, true); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, "VoxelManip"); - lua_setmetatable(L, -2); + // VoxelManip object + LuaVoxelManip *o = new LuaVoxelManip(vm, true); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, "VoxelManip"); + lua_setmetatable(L, -2); - // emerged min pos - push_v3s16(L, vm->m_area.MinEdge); + // emerged min pos + push_v3s16(L, vm->m_area.MinEdge); - // emerged max pos - push_v3s16(L, vm->m_area.MaxEdge); + // emerged max pos + push_v3s16(L, vm->m_area.MaxEdge); - return 3; + return 3; + } + case MGOBJ_HEIGHTMAP: { + if (!mg->heightmap) + return 0; + + lua_newtable(L); + for (size_t i = 0; i != maplen; i++) { + lua_pushinteger(L, mg->heightmap[i]); + lua_rawseti(L, -2, i + 1); } - case MGOBJ_HEIGHTMAP: { - if (!mg->heightmap) - return 0; - - lua_newtable(L); - for (size_t i = 0; i != maplen; i++) { - lua_pushinteger(L, mg->heightmap[i]); - lua_rawseti(L, -2, i + 1); - } - return 1; + return 1; + } + case MGOBJ_BIOMEMAP: { + if (!mg->biomemap) + return 0; + + lua_newtable(L); + for (size_t i = 0; i != maplen; i++) { + lua_pushinteger(L, mg->biomemap[i]); + lua_rawseti(L, -2, i + 1); } - case MGOBJ_BIOMEMAP: { - if (!mg->biomemap) - return 0; - lua_newtable(L); - for (size_t i = 0; i != maplen; i++) { - lua_pushinteger(L, mg->biomemap[i]); - lua_rawseti(L, -2, i + 1); - } - - return 1; + return 1; + } + case MGOBJ_HEATMAP: { + if (!mg->heatmap) + return 0; + + lua_newtable(L); + for (size_t i = 0; i != maplen; i++) { + lua_pushnumber(L, mg->heatmap[i]); + lua_rawseti(L, -2, i + 1); } - case MGOBJ_HEATMAP: { // Mapgen V7 specific objects - case MGOBJ_HUMIDMAP: - if (strcmp(emerge->params.mg_name.c_str(), "v7")) - return 0; - - MapgenV7 *mgv7 = (MapgenV7 *)mg; - float *arr = (mgobj == MGOBJ_HEATMAP) ? - mgv7->noise_heat->result : mgv7->noise_humidity->result; - if (!arr) - return 0; + return 1; + } - lua_newtable(L); - for (size_t i = 0; i != maplen; i++) { - lua_pushnumber(L, arr[i]); - lua_rawseti(L, -2, i + 1); - } + case MGOBJ_HUMIDMAP: { + if (!mg->humidmap) + return 0; - return 1; + lua_newtable(L); + for (size_t i = 0; i != maplen; i++) { + lua_pushnumber(L, mg->humidmap[i]); + lua_rawseti(L, -2, i + 1); } - case MGOBJ_GENNOTIFY: { - std::map<std::string, std::vector<v3s16> >event_map; - std::map<std::string, std::vector<v3s16> >::iterator it; - mg->gennotify.getEvents(event_map); + return 1; + } + case MGOBJ_GENNOTIFY: { + std::map<std::string, std::vector<v3s16> >event_map; + std::map<std::string, std::vector<v3s16> >::iterator it; - lua_newtable(L); - for (it = event_map.begin(); it != event_map.end(); ++it) { - lua_newtable(L); + mg->gennotify.getEvents(event_map); - for (size_t j = 0; j != it->second.size(); j++) { - push_v3s16(L, it->second[j]); - lua_rawseti(L, -2, j + 1); - } + lua_newtable(L); + for (it = event_map.begin(); it != event_map.end(); ++it) { + lua_newtable(L); - lua_setfield(L, -2, it->first.c_str()); + for (size_t j = 0; j != it->second.size(); j++) { + push_v3s16(L, it->second[j]); + lua_rawseti(L, -2, j + 1); } - return 1; + lua_setfield(L, -2, it->first.c_str()); } + + return 1; + } } return 0; } + int ModApiMapgen::l_get_mapgen_params(lua_State *L) { MapgenParams *params = &getServer(L)->getEmergeManager()->params; @@ -350,6 +586,7 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L) return 1; } + // set_mapgen_params(params) // set mapgen parameters int ModApiMapgen::l_set_mapgen_params(lua_State *L) @@ -389,6 +626,7 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L) return 0; } + // set_noiseparams(name, noiseparams, set_default) // set global config values for noise parameters int ModApiMapgen::l_set_noiseparams(lua_State *L) @@ -406,6 +644,21 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L) return 0; } + +// get_noiseparams(name) +int ModApiMapgen::l_get_noiseparams(lua_State *L) +{ + std::string name = luaL_checkstring(L, 1); + + NoiseParams np; + if (!g_settings->getNoiseParams(name, np)) + return 0; + + push_noiseparams(L, &np); + return 1; +} + + // set_gen_notify(flags, {deco_id_table}) int ModApiMapgen::l_set_gen_notify(lua_State *L) { @@ -421,7 +674,7 @@ int ModApiMapgen::l_set_gen_notify(lua_State *L) lua_pushnil(L); while (lua_next(L, 2)) { if (lua_isnumber(L, -1)) - emerge->gen_notify_on_deco_ids.insert(lua_tonumber(L, -1)); + emerge->gen_notify_on_deco_ids.insert((u32)lua_tonumber(L, -1)); lua_pop(L, 1); } } @@ -429,6 +682,26 @@ int ModApiMapgen::l_set_gen_notify(lua_State *L) return 0; } + +// get_gen_notify() +int ModApiMapgen::l_get_gen_notify(lua_State *L) +{ + EmergeManager *emerge = getServer(L)->getEmergeManager(); + push_flags_string(L, flagdesc_gennotify, emerge->gen_notify_on, + emerge->gen_notify_on); + + lua_newtable(L); + int i = 1; + for (std::set<u32>::iterator it = emerge->gen_notify_on_deco_ids.begin(); + it != emerge->gen_notify_on_deco_ids.end(); ++it) { + lua_pushnumber(L, *it); + lua_rawseti(L, -2, i); + i++; + } + return 2; +} + + // register_biome({lots of stuff}) int ModApiMapgen::l_register_biome(lua_State *L) { @@ -438,66 +711,20 @@ int ModApiMapgen::l_register_biome(lua_State *L) INodeDefManager *ndef = getServer(L)->getNodeDefManager(); BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; - enum BiomeType biometype = (BiomeType)getenumfield(L, index, "type", - es_BiomeTerrainType, BIOME_TYPE_NORMAL); - Biome *b = bmgr->create(biometype); - - b->name = getstringfield_default(L, index, "name", ""); - b->depth_top = getintfield_default(L, index, "depth_top", 1); - b->depth_filler = getintfield_default(L, index, "depth_filler", 3); - b->height_shore = getintfield_default(L, index, "height_shore", 3); - b->depth_water_top = getintfield_default(L, index, "depth_water_top", 0); - b->y_min = getintfield_default(L, index, "y_min", -31000); - b->y_max = getintfield_default(L, index, "y_max", 31000); - b->heat_point = getfloatfield_default(L, index, "heat_point", 0.f); - b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.f); - b->flags = 0; //reserved + Biome *biome = read_biome_def(L, index, ndef); + if (!biome) + return 0; - u32 id = bmgr->add(b); - if (id == (u32)-1) { - delete b; + ObjDefHandle handle = bmgr->add(biome); + if (handle == OBJDEF_INVALID_HANDLE) { + delete biome; return 0; } - NodeResolveInfo *nri = new NodeResolveInfo(b); - std::list<std::string> &nnames = nri->nodenames; - nnames.push_back(getstringfield_default(L, index, "node_top", "")); - nnames.push_back(getstringfield_default(L, index, "node_filler", "")); - nnames.push_back(getstringfield_default(L, index, "node_shore_top", "")); - nnames.push_back(getstringfield_default(L, index, "node_shore_filler", "")); - nnames.push_back(getstringfield_default(L, index, "node_underwater", "")); - nnames.push_back(getstringfield_default(L, index, "node_stone", "")); - nnames.push_back(getstringfield_default(L, index, "node_water_top", "")); - nnames.push_back(getstringfield_default(L, index, "node_water", "")); - nnames.push_back(getstringfield_default(L, index, "node_dust", "")); - ndef->pendNodeResolve(nri); - - verbosestream << "register_biome: " << b->name << std::endl; - - lua_pushinteger(L, id); + lua_pushinteger(L, handle); return 1; } -int ModApiMapgen::l_clear_registered_biomes(lua_State *L) -{ - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; - bmgr->clear(); - return 0; -} - -int ModApiMapgen::l_clear_registered_decorations(lua_State *L) -{ - DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr; - dmgr->clear(); - return 0; -} - -int ModApiMapgen::l_clear_registered_ores(lua_State *L) -{ - OreManager *omgr = getServer(L)->getEmergeManager()->oremgr; - omgr->clear(); - return 0; -} // register_decoration({lots of stuff}) int ModApiMapgen::l_register_decoration(lua_State *L) @@ -508,6 +735,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L) INodeDefManager *ndef = getServer(L)->getNodeDefManager(); DecorationManager *decomgr = getServer(L)->getEmergeManager()->decomgr; BiomeManager *biomemgr = getServer(L)->getEmergeManager()->biomemgr; + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; enum DecorationType decotype = (DecorationType)getenumfield(L, index, "deco_type", es_DecorationType, -1); @@ -515,7 +743,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L) Decoration *deco = decomgr->create(decotype); if (!deco) { errorstream << "register_decoration: decoration placement type " - << decotype << " not implemented"; + << decotype << " not implemented" << std::endl; return 0; } @@ -531,15 +759,11 @@ int ModApiMapgen::l_register_decoration(lua_State *L) return 0; } - NodeResolveInfo *nri = new NodeResolveInfo(deco); - //// Get node name(s) to place decoration on - std::vector<const char *> place_on_names; - getstringlistfield(L, index, "place_on", place_on_names); - nri->nodelistinfo.push_back(NodeListInfo(place_on_names.size())); - for (size_t i = 0; i != place_on_names.size(); i++) - nri->nodenames.push_back(place_on_names[i]); + size_t nread = getstringlistfield(L, index, "place_on", &deco->m_nodenames); + deco->m_nnlistsizes.push_back(nread); + //// Get decoration flags getflagsfield(L, index, "flags", flagdesc_deco, &deco->flags, NULL); //// Get NoiseParams to define how decoration is placed @@ -549,51 +773,45 @@ int ModApiMapgen::l_register_decoration(lua_State *L) lua_pop(L, 1); //// Get biomes associated with this decoration (if any) - std::vector<const char *> biome_list; - getstringlistfield(L, index, "biomes", biome_list); - for (size_t i = 0; i != biome_list.size(); i++) { - Biome *b = (Biome *)biomemgr->getByName(biome_list[i]); - if (!b) - continue; - - deco->biomes.insert(b->id); - } + lua_getfield(L, index, "biomes"); + if (get_biome_list(L, -1, biomemgr, &deco->biomes)) + errorstream << "register_decoration: couldn't get all biomes " << std::endl; + lua_pop(L, 1); //// Handle decoration type-specific parameters bool success = false; switch (decotype) { - case DECO_SIMPLE: - success = regDecoSimple(L, nri, (DecoSimple *)deco); - break; - case DECO_SCHEMATIC: - success = regDecoSchematic(L, ndef, (DecoSchematic *)deco); - break; - case DECO_LSYSTEM: - break; + case DECO_SIMPLE: + success = read_deco_simple(L, (DecoSimple *)deco); + break; + case DECO_SCHEMATIC: + success = read_deco_schematic(L, schemmgr, (DecoSchematic *)deco); + break; + case DECO_LSYSTEM: + break; } - ndef->pendNodeResolve(nri); - if (!success) { delete deco; return 0; } - u32 id = decomgr->add(deco); - if (id == (u32)-1) { + ndef->pendNodeResolve(deco); + + ObjDefHandle handle = decomgr->add(deco); + if (handle == OBJDEF_INVALID_HANDLE) { delete deco; return 0; } - verbosestream << "register_decoration: " << deco->name << std::endl; - - lua_pushinteger(L, id); + lua_pushinteger(L, handle); return 1; } -bool ModApiMapgen::regDecoSimple(lua_State *L, - NodeResolveInfo *nri, DecoSimple *deco) + +bool read_deco_simple(lua_State *L, DecoSimple *deco) { + size_t nnames; int index = 1; deco->deco_height = getintfield_default(L, index, "height", 1); @@ -606,60 +824,48 @@ bool ModApiMapgen::regDecoSimple(lua_State *L, return false; } - std::vector<const char *> deco_names; - getstringlistfield(L, index, "decoration", deco_names); - if (deco_names.size() == 0) { + nnames = getstringlistfield(L, index, "decoration", &deco->m_nodenames); + deco->m_nnlistsizes.push_back(nnames); + if (nnames == 0) { errorstream << "register_decoration: no decoration nodes " "defined" << std::endl; return false; } - nri->nodelistinfo.push_back(NodeListInfo(deco_names.size())); - for (size_t i = 0; i != deco_names.size(); i++) - nri->nodenames.push_back(deco_names[i]); - std::vector<const char *> spawnby_names; - getstringlistfield(L, index, "spawn_by", spawnby_names); - if (deco->nspawnby != -1 && spawnby_names.size() == 0) { + nnames = getstringlistfield(L, index, "spawn_by", &deco->m_nodenames); + deco->m_nnlistsizes.push_back(nnames); + if (nnames == 0 && deco->nspawnby != -1) { errorstream << "register_decoration: no spawn_by nodes defined," " but num_spawn_by specified" << std::endl; return false; } - nri->nodelistinfo.push_back(NodeListInfo(spawnby_names.size())); - for (size_t i = 0; i != spawnby_names.size(); i++) - nri->nodenames.push_back(spawnby_names[i]); return true; } -bool ModApiMapgen::regDecoSchematic(lua_State *L, INodeDefManager *ndef, - DecoSchematic *deco) + +bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco) { int index = 1; deco->rotation = (Rotation)getenumfield(L, index, "rotation", - es_Rotation, ROTATE_0); + ModApiMapgen::es_Rotation, ROTATE_0); - std::map<std::string, std::string> replace_names; + StringMap replace_names; lua_getfield(L, index, "replacements"); if (lua_istable(L, -1)) - read_schematic_replacements(L, replace_names, lua_gettop(L)); + read_schematic_replacements(L, -1, &replace_names); lua_pop(L, 1); - // TODO(hmmmm): get a ref from registered schematics - Schematic *schem = new Schematic; lua_getfield(L, index, "schematic"); - if (!get_schematic(L, -1, schem, ndef, replace_names)) { - lua_pop(L, 1); - delete schem; - return false; - } + Schematic *schem = get_or_load_schematic(L, -1, schemmgr, &replace_names); lua_pop(L, 1); deco->schematic = schem; - - return true; + return schem != NULL; } + // register_ore({lots of stuff}) int ModApiMapgen::l_register_ore(lua_State *L) { @@ -667,10 +873,11 @@ int ModApiMapgen::l_register_ore(lua_State *L) luaL_checktype(L, index, LUA_TTABLE); INodeDefManager *ndef = getServer(L)->getNodeDefManager(); + BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; OreManager *oremgr = getServer(L)->getEmergeManager()->oremgr; enum OreType oretype = (OreType)getenumfield(L, index, - "ore_type", es_OreType, ORE_TYPE_SCATTER); + "ore_type", es_OreType, ORE_SCATTER); Ore *ore = oremgr->create(oretype); if (!ore) { errorstream << "register_ore: ore_type " << oretype << " not implemented"; @@ -686,6 +893,7 @@ int ModApiMapgen::l_register_ore(lua_State *L) ore->noise = NULL; ore->flags = 0; + //// Get y_min/y_max warn_if_field_exists(L, index, "height_min", "Deprecated: new name is \"y_min\"."); warn_if_field_exists(L, index, "height_max", @@ -708,8 +916,16 @@ int ModApiMapgen::l_register_ore(lua_State *L) return 0; } + //// Get flags getflagsfield(L, index, "flags", flagdesc_ore, &ore->flags, NULL); + //// Get biomes associated with this decoration (if any) + lua_getfield(L, index, "biomes"); + if (get_biome_list(L, -1, bmgr, &ore->biomes)) + errorstream << "register_ore: couldn't get all biomes " << std::endl; + lua_pop(L, 1); + + //// Get noise parameters if needed lua_getfield(L, index, "noise_params"); if (read_noiseparams(L, -1, &ore->np)) { ore->flags |= OREFLAG_USE_NOISE; @@ -721,45 +937,152 @@ int ModApiMapgen::l_register_ore(lua_State *L) } lua_pop(L, 1); - if (oretype == ORE_TYPE_VEIN) { + if (oretype == ORE_VEIN) { OreVein *orevein = (OreVein *)ore; orevein->random_factor = getfloatfield_default(L, index, "random_factor", 1.f); } - u32 id = oremgr->add(ore); - if (id == (u32)-1) { + ObjDefHandle handle = oremgr->add(ore); + if (handle == OBJDEF_INVALID_HANDLE) { delete ore; return 0; } - NodeResolveInfo *nri = new NodeResolveInfo(ore); - nri->nodenames.push_back(getstringfield_default(L, index, "ore", "")); + ore->m_nodenames.push_back(getstringfield_default(L, index, "ore", "")); + + size_t nnames = getstringlistfield(L, index, "wherein", &ore->m_nodenames); + ore->m_nnlistsizes.push_back(nnames); + + ndef->pendNodeResolve(ore); + + lua_pushinteger(L, handle); + return 1; +} + - std::vector<const char *> wherein_names; - getstringlistfield(L, index, "wherein", wherein_names); - nri->nodelistinfo.push_back(NodeListInfo(wherein_names.size())); - for (size_t i = 0; i != wherein_names.size(); i++) - nri->nodenames.push_back(wherein_names[i]); +// register_schematic({schematic}, replacements={}) +int ModApiMapgen::l_register_schematic(lua_State *L) +{ + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + + StringMap replace_names; + if (lua_istable(L, 2)) + read_schematic_replacements(L, 2, &replace_names); - ndef->pendNodeResolve(nri); + Schematic *schem = load_schematic(L, 1, schemmgr->getNodeDef(), + &replace_names); + if (!schem) + return 0; - verbosestream << "register_ore: " << ore->name << std::endl; + ObjDefHandle handle = schemmgr->add(schem); + if (handle == OBJDEF_INVALID_HANDLE) { + delete schem; + return 0; + } - lua_pushinteger(L, id); + lua_pushinteger(L, handle); return 1; } -// create_schematic(p1, p2, probability_list, filename) + +// clear_registered_biomes() +int ModApiMapgen::l_clear_registered_biomes(lua_State *L) +{ + BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + bmgr->clear(); + return 0; +} + + +// clear_registered_decorations() +int ModApiMapgen::l_clear_registered_decorations(lua_State *L) +{ + DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr; + dmgr->clear(); + return 0; +} + + +// clear_registered_ores() +int ModApiMapgen::l_clear_registered_ores(lua_State *L) +{ + OreManager *omgr = getServer(L)->getEmergeManager()->oremgr; + omgr->clear(); + return 0; +} + + +// clear_registered_schematics() +int ModApiMapgen::l_clear_registered_schematics(lua_State *L) +{ + SchematicManager *smgr = getServer(L)->getEmergeManager()->schemmgr; + smgr->clear(); + return 0; +} + + +// generate_ores(vm, p1, p2, [ore_id]) +int ModApiMapgen::l_generate_ores(lua_State *L) +{ + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + Mapgen mg; + mg.seed = emerge->params.seed; + mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; + mg.ndef = getServer(L)->getNodeDefManager(); + + v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : + mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE; + v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : + mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE; + sortBoxVerticies(pmin, pmax); + + u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); + + emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax); + + return 0; +} + + +// generate_decorations(vm, p1, p2, [deco_id]) +int ModApiMapgen::l_generate_decorations(lua_State *L) +{ + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + Mapgen mg; + mg.seed = emerge->params.seed; + mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; + mg.ndef = getServer(L)->getNodeDefManager(); + + v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : + mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE; + v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : + mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE; + sortBoxVerticies(pmin, pmax); + + u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed); + + emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); + + return 0; +} + + +// create_schematic(p1, p2, probability_list, filename, y_slice_prob_list) int ModApiMapgen::l_create_schematic(lua_State *L) { - Schematic schem; + INodeDefManager *ndef = getServer(L)->getNodeDefManager(); + + const char *filename = luaL_checkstring(L, 4); + CHECK_SECURE_PATH_OPTIONAL(L, filename); Map *map = &(getEnv(L)->getMap()); - INodeDefManager *ndef = getServer(L)->getNodeDefManager(); + Schematic schem; - v3s16 p1 = read_v3s16(L, 1); - v3s16 p2 = read_v3s16(L, 2); + v3s16 p1 = check_v3s16(L, 1); + v3s16 p2 = check_v3s16(L, 2); sortBoxVerticies(p1, p2); std::vector<std::pair<v3s16, u8> > prob_list; @@ -768,7 +1091,7 @@ int ModApiMapgen::l_create_schematic(lua_State *L) while (lua_next(L, 3)) { if (lua_istable(L, -1)) { lua_getfield(L, -1, "pos"); - v3s16 pos = read_v3s16(L, -1); + v3s16 pos = check_v3s16(L, -1); lua_pop(L, 1); u8 prob = getintfield_default(L, -1, "prob", MTSCHEM_PROB_ALWAYS); @@ -793,8 +1116,6 @@ int ModApiMapgen::l_create_schematic(lua_State *L) } } - const char *filename = luaL_checkstring(L, 4); - if (!schem.getSchematicFromMap(map, p1, p2)) { errorstream << "create_schematic: failed to get schematic " "from map" << std::endl; @@ -807,60 +1128,25 @@ int ModApiMapgen::l_create_schematic(lua_State *L) actionstream << "create_schematic: saved schematic file '" << filename << "'." << std::endl; + lua_pushboolean(L, true); return 1; } -// generate_ores(vm, [ore_id]) -int ModApiMapgen::l_generate_ores(lua_State *L) -{ - EmergeManager *emerge = getServer(L)->getEmergeManager(); - - Mapgen mg; - mg.seed = emerge->params.seed; - mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; - mg.ndef = getServer(L)->getNodeDefManager(); - - u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed); - - emerge->oremgr->placeAllOres(&mg, blockseed, - mg.vm->m_area.MinEdge, mg.vm->m_area.MaxEdge); - - return 0; -} - -// generate_decorations(vm, [deco_id]) -int ModApiMapgen::l_generate_decorations(lua_State *L) -{ - EmergeManager *emerge = getServer(L)->getEmergeManager(); - - Mapgen mg; - mg.seed = emerge->params.seed; - mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; - mg.ndef = getServer(L)->getNodeDefManager(); - - u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed); - - emerge->decomgr->placeAllDecos(&mg, blockseed, - mg.vm->m_area.MinEdge, mg.vm->m_area.MaxEdge); - - return 0; -} // place_schematic(p, schematic, rotation, replacement) int ModApiMapgen::l_place_schematic(lua_State *L) { - Schematic schem; - Map *map = &(getEnv(L)->getMap()); - INodeDefManager *ndef = getServer(L)->getNodeDefManager(); + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; //// Read position - v3s16 p = read_v3s16(L, 1); + v3s16 p = check_v3s16(L, 1); //// Read rotation int rot = ROTATE_0; - if (lua_isstring(L, 3)) - string_to_enum(es_Rotation, rot, std::string(lua_tostring(L, 3))); + const char *enumstr = lua_tostring(L, 3); + if (enumstr) + string_to_enum(es_Rotation, rot, std::string(enumstr)); //// Read force placement bool force_placement = true; @@ -868,21 +1154,73 @@ int ModApiMapgen::l_place_schematic(lua_State *L) force_placement = lua_toboolean(L, 5); //// Read node replacements - std::map<std::string, std::string> replace_names; + StringMap replace_names; if (lua_istable(L, 4)) - read_schematic_replacements(L, replace_names, 4); + read_schematic_replacements(L, 4, &replace_names); //// Read schematic - if (!get_schematic(L, 2, &schem, ndef, replace_names)) { + Schematic *schem = get_or_load_schematic(L, 2, schemmgr, &replace_names); + if (!schem) { errorstream << "place_schematic: failed to get schematic" << std::endl; return 0; } - schem.placeStructure(map, p, 0, (Rotation)rot, force_placement, ndef); + schem->placeStructure(map, p, 0, (Rotation)rot, force_placement); + + lua_pushboolean(L, true); + return 1; +} + +// serialize_schematic(schematic, format, options={...}) +int ModApiMapgen::l_serialize_schematic(lua_State *L) +{ + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + + //// Read options + bool use_comments = getboolfield_default(L, 3, "lua_use_comments", false); + u32 indent_spaces = getintfield_default(L, 3, "lua_num_indent_spaces", 0); + + //// Get schematic + bool was_loaded = false; + Schematic *schem = (Schematic *)get_objdef(L, 1, schemmgr); + if (!schem) { + schem = load_schematic(L, 1, NULL, NULL); + was_loaded = true; + } + if (!schem) { + errorstream << "serialize_schematic: failed to get schematic" << std::endl; + return 0; + } + + //// Read format of definition to save as + int schem_format = SCHEM_FMT_MTS; + const char *enumstr = lua_tostring(L, 2); + if (enumstr) + string_to_enum(es_SchematicFormatType, schem_format, std::string(enumstr)); + + //// Serialize to binary string + std::ostringstream os(std::ios_base::binary); + switch (schem_format) { + case SCHEM_FMT_MTS: + schem->serializeToMts(&os, schem->m_nodenames); + break; + case SCHEM_FMT_LUA: + schem->serializeToLua(&os, schem->m_nodenames, + use_comments, indent_spaces); + break; + default: + return 0; + } + + if (was_loaded) + delete schem; + std::string ser = os.str(); + lua_pushlstring(L, ser.c_str(), ser.length()); return 1; } + void ModApiMapgen::Initialize(lua_State *L, int top) { API_FCT(get_mapgen_object); @@ -890,19 +1228,23 @@ void ModApiMapgen::Initialize(lua_State *L, int top) API_FCT(get_mapgen_params); API_FCT(set_mapgen_params); API_FCT(set_noiseparams); + API_FCT(get_noiseparams); API_FCT(set_gen_notify); + API_FCT(get_gen_notify); API_FCT(register_biome); API_FCT(register_decoration); API_FCT(register_ore); + API_FCT(register_schematic); API_FCT(clear_registered_biomes); API_FCT(clear_registered_decorations); API_FCT(clear_registered_ores); + API_FCT(clear_registered_schematics); API_FCT(generate_ores); API_FCT(generate_decorations); - API_FCT(create_schematic); API_FCT(place_schematic); + API_FCT(serialize_schematic); } diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h index e17d1b85a..7440d1285 100644 --- a/src/script/lua_api/l_mapgen.h +++ b/src/script/lua_api/l_mapgen.h @@ -22,11 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_base.h" -class INodeDefManager; -struct NodeResolveInfo; -class DecoSimple; -class DecoSchematic; - class ModApiMapgen : public ModApiBase { private: // get_mapgen_object(objectname) @@ -44,9 +39,15 @@ private: // set_noiseparam_defaults(name, noiseparams, set_default) static int l_set_noiseparams(lua_State *L); + // get_noiseparam_defaults(name) + static int l_get_noiseparams(lua_State *L); + // set_gen_notify(flagstring) static int l_set_gen_notify(lua_State *L); + // set_gen_notify(flagstring) + static int l_get_gen_notify(lua_State *L); + // register_biome({lots of stuff}) static int l_register_biome(lua_State *L); @@ -56,16 +57,22 @@ private: // register_ore({lots of stuff}) static int l_register_ore(lua_State *L); + // register_schematic({schematic}, replacements={}) + static int l_register_schematic(lua_State *L); + // clear_registered_biomes() static int l_clear_registered_biomes(lua_State *L); // clear_registered_decorations() static int l_clear_registered_decorations(lua_State *L); - // generate_ores(vm) + // clear_registered_schematics() + static int l_clear_registered_schematics(lua_State *L); + + // generate_ores(vm, p1, p2) static int l_generate_ores(lua_State *L); - // generate_decorations(vm) + // generate_decorations(vm, p1, p2) static int l_generate_decorations(lua_State *L); // clear_registered_ores @@ -77,19 +84,19 @@ private: // place_schematic(p, schematic, rotation, replacement) static int l_place_schematic(lua_State *L); - static bool regDecoSimple(lua_State *L, - NodeResolveInfo *nri, DecoSimple *deco); - static bool regDecoSchematic(lua_State *L, - INodeDefManager *ndef, DecoSchematic *deco); + // serialize_schematic(schematic, format, options={...}) + static int l_serialize_schematic(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); static struct EnumString es_BiomeTerrainType[]; static struct EnumString es_DecorationType[]; static struct EnumString es_MapgenObject[]; static struct EnumString es_OreType[]; static struct EnumString es_Rotation[]; - -public: - static void Initialize(lua_State *L, int top); + static struct EnumString es_SchematicFormatType[]; + static struct EnumString es_NodeResolveMethod[]; }; #endif /* L_MAPGEN_H_ */ diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 4f20e56f9..6cdbe5c68 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -63,9 +63,10 @@ void NodeMetaRef::reportMetadataChange(NodeMetaRef *ref) ref->m_env->getMap().dispatchEvent(&event); // Set the block to be saved MapBlock *block = ref->m_env->getMap().getBlockNoCreateNoEx(blockpos); - if(block) + if (block) { block->raiseModified(MOD_STATE_WRITE_NEEDED, - "NodeMetaRef::reportMetadataChange"); + MOD_REASON_REPORT_META_CHANGE); + } } // Exported functions @@ -189,32 +190,34 @@ int NodeMetaRef::l_to_table(lua_State *L) NodeMetaRef *ref = checkobject(L, 1); NodeMetadata *meta = getmeta(ref, true); - if(meta == NULL){ + if (meta == NULL) { lua_pushnil(L); return 1; } lua_newtable(L); + // fields lua_newtable(L); { - std::map<std::string, std::string> fields = meta->getStrings(); - for(std::map<std::string, std::string>::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; + StringMap fields = meta->getStrings(); + for (StringMap::const_iterator + it = fields.begin(); it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; lua_pushlstring(L, name.c_str(), name.size()); lua_pushlstring(L, value.c_str(), value.size()); lua_settable(L, -3); } } lua_setfield(L, -2, "fields"); + // inventory lua_newtable(L); Inventory *inv = meta->getInventory(); - if(inv){ - std::vector<const InventoryList*> lists = inv->getLists(); - for(std::vector<const InventoryList*>::const_iterator - i = lists.begin(); i != lists.end(); i++){ + if (inv) { + std::vector<const InventoryList *> lists = inv->getLists(); + for(std::vector<const InventoryList *>::const_iterator + i = lists.begin(); i != lists.end(); i++) { push_inventory_list(L, inv, (*i)->getName().c_str()); lua_setfield(L, -2, (*i)->getName().c_str()); } diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 5a82b6485..c8dc2d2dc 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -23,12 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" -// garbage collector -int LuaPerlinNoise::gc_object(lua_State *L) +/////////////////////////////////////// +/* + LuaPerlinNoise +*/ + +LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) : + np(*params) +{ +} + + +LuaPerlinNoise::~LuaPerlinNoise() { - LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); - delete o; - return 0; } @@ -36,7 +43,7 @@ int LuaPerlinNoise::l_get2d(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaPerlinNoise *o = checkobject(L, 1); - v2f p = read_v2f(L, 2); + v2f p = check_v2f(L, 2); lua_Number val = NoisePerlin2D(&o->np, p.X, p.Y, 0); lua_pushnumber(L, val); return 1; @@ -47,26 +54,13 @@ int LuaPerlinNoise::l_get3d(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaPerlinNoise *o = checkobject(L, 1); - v3f p = read_v3f(L, 2); + v3f p = check_v3f(L, 2); lua_Number val = NoisePerlin3D(&o->np, p.X, p.Y, p.Z, 0); lua_pushnumber(L, val); return 1; } -LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) : - np(*params) -{ -} - - -LuaPerlinNoise::~LuaPerlinNoise() -{ -} - - -// LuaPerlinNoise(seed, octaves, persistence, scale) -// Creates an LuaPerlinNoise and leaves it on top of stack int LuaPerlinNoise::create_object(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -91,14 +85,22 @@ int LuaPerlinNoise::create_object(lua_State *L) } -LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg) +int LuaPerlinNoise::gc_object(lua_State *L) +{ + LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPerlinNoise *LuaPerlinNoise::checkobject(lua_State *L, int narg) { NO_MAP_LOCK_REQUIRED; luaL_checktype(L, narg, LUA_TUSERDATA); void *ud = luaL_checkudata(L, narg, className); if (!ud) luaL_typerror(L, narg, className); - return *(LuaPerlinNoise**)ud; // unbox pointer + return *(LuaPerlinNoise **)ud; } @@ -111,7 +113,7 @@ void LuaPerlinNoise::Register(lua_State *L) lua_pushliteral(L, "__metatable"); lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() + lua_settable(L, metatable); lua_pushliteral(L, "__index"); lua_pushvalue(L, methodtable); @@ -121,12 +123,11 @@ void LuaPerlinNoise::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); - lua_pop(L, 1); // drop metatable + lua_pop(L, 1); - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); - // Can be created from Lua (PerlinNoise(seed, octaves, persistence) lua_register(L, className, create_object); } @@ -138,16 +139,26 @@ const luaL_reg LuaPerlinNoise::methods[] = { {0,0} }; - +/////////////////////////////////////// /* - PerlinNoiseMap - */ + LuaPerlinNoiseMap +*/ -int LuaPerlinNoiseMap::gc_object(lua_State *L) +LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size) { - LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); - delete o; - return 0; + m_is3d = size.Z > 1; + np = *params; + try { + noise = new Noise(&np, seed, size.X, size.Y, size.Z); + } catch (InvalidNoiseParamsException &e) { + throw LuaError(e.what()); + } +} + + +LuaPerlinNoiseMap::~LuaPerlinNoiseMap() +{ + delete noise; } @@ -157,15 +168,15 @@ int LuaPerlinNoiseMap::l_get2dMap(lua_State *L) size_t i = 0; LuaPerlinNoiseMap *o = checkobject(L, 1); - v2f p = read_v2f(L, 2); + v2f p = check_v2f(L, 2); Noise *n = o->noise; n->perlinMap2D(p.X, p.Y); lua_newtable(L); - for (int y = 0; y != n->sy; y++) { + for (u32 y = 0; y != n->sy; y++) { lua_newtable(L); - for (int x = 0; x != n->sx; x++) { + for (u32 x = 0; x != n->sx; x++) { lua_pushnumber(L, n->result[i++]); lua_rawseti(L, -2, x + 1); } @@ -180,14 +191,19 @@ int LuaPerlinNoiseMap::l_get2dMap_flat(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaPerlinNoiseMap *o = checkobject(L, 1); - v2f p = read_v2f(L, 2); + v2f p = check_v2f(L, 2); + bool use_buffer = lua_istable(L, 3); Noise *n = o->noise; n->perlinMap2D(p.X, p.Y); size_t maplen = n->sx * n->sy; - lua_newtable(L); + if (use_buffer) + lua_pushvalue(L, 3); + else + lua_newtable(L); + for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, n->result[i]); lua_rawseti(L, -2, i + 1); @@ -202,7 +218,7 @@ int LuaPerlinNoiseMap::l_get3dMap(lua_State *L) size_t i = 0; LuaPerlinNoiseMap *o = checkobject(L, 1); - v3f p = read_v3f(L, 2); + v3f p = check_v3f(L, 2); if (!o->m_is3d) return 0; @@ -211,11 +227,11 @@ int LuaPerlinNoiseMap::l_get3dMap(lua_State *L) n->perlinMap3D(p.X, p.Y, p.Z); lua_newtable(L); - for (int z = 0; z != n->sz; z++) { + for (u32 z = 0; z != n->sz; z++) { lua_newtable(L); - for (int y = 0; y != n->sy; y++) { + for (u32 y = 0; y != n->sy; y++) { lua_newtable(L); - for (int x = 0; x != n->sx; x++) { + for (u32 x = 0; x != n->sx; x++) { lua_pushnumber(L, n->result[i++]); lua_rawseti(L, -2, x + 1); } @@ -232,7 +248,8 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaPerlinNoiseMap *o = checkobject(L, 1); - v3f p = read_v3f(L, 2); + v3f p = check_v3f(L, 2); + bool use_buffer = lua_istable(L, 3); if (!o->m_is3d) return 0; @@ -242,7 +259,11 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L) size_t maplen = n->sx * n->sy * n->sz; - lua_newtable(L); + if (use_buffer) + lua_pushvalue(L, 3); + else + lua_newtable(L); + for (size_t i = 0; i != maplen; i++) { lua_pushnumber(L, n->result[i]); lua_rawseti(L, -2, i + 1); @@ -251,26 +272,61 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L) } -LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size) +int LuaPerlinNoiseMap::l_calc2dMap(lua_State *L) { - m_is3d = size.Z > 1; - np = *params; - try { - noise = new Noise(&np, seed, size.X, size.Y, size.Z); - } catch (InvalidNoiseParamsException &e) { - throw LuaError(e.what()); - } + NO_MAP_LOCK_REQUIRED; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v2f p = check_v2f(L, 2); + + Noise *n = o->noise; + n->perlinMap2D(p.X, p.Y); + + return 0; } +int LuaPerlinNoiseMap::l_calc3dMap(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; -LuaPerlinNoiseMap::~LuaPerlinNoiseMap() + LuaPerlinNoiseMap *o = checkobject(L, 1); + v3f p = check_v3f(L, 2); + + if (!o->m_is3d) + return 0; + + Noise *n = o->noise; + n->perlinMap3D(p.X, p.Y, p.Z); + + return 0; +} + + +int LuaPerlinNoiseMap::l_getMapSlice(lua_State *L) { - delete noise; + NO_MAP_LOCK_REQUIRED; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v3s16 slice_offset = read_v3s16(L, 2); + v3s16 slice_size = read_v3s16(L, 3); + bool use_buffer = lua_istable(L, 4); + + Noise *n = o->noise; + + if (use_buffer) + lua_pushvalue(L, 3); + else + lua_newtable(L); + + write_array_slice_float(L, lua_gettop(L), n->result, + v3u16(n->sx, n->sy, n->sz), + v3u16(slice_offset.X, slice_offset.Y, slice_offset.Z), + v3u16(slice_size.X, slice_size.Y, slice_size.Z)); + + return 1; } -// LuaPerlinNoiseMap(np, size) -// Creates an LuaPerlinNoiseMap and leaves it on top of stack int LuaPerlinNoiseMap::create_object(lua_State *L) { NoiseParams np; @@ -286,6 +342,14 @@ int LuaPerlinNoiseMap::create_object(lua_State *L) } +int LuaPerlinNoiseMap::gc_object(lua_State *L) +{ + LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); @@ -294,7 +358,7 @@ LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg) if (!ud) luaL_typerror(L, narg, className); - return *(LuaPerlinNoiseMap **)ud; // unbox pointer + return *(LuaPerlinNoiseMap **)ud; } @@ -307,7 +371,7 @@ void LuaPerlinNoiseMap::Register(lua_State *L) lua_pushliteral(L, "__metatable"); lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() + lua_settable(L, metatable); lua_pushliteral(L, "__index"); lua_pushvalue(L, methodtable); @@ -317,12 +381,11 @@ void LuaPerlinNoiseMap::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); - lua_pop(L, 1); // drop metatable + lua_pop(L, 1); - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); - // Can be created from Lua (PerlinNoiseMap(np, size) lua_register(L, className, create_object); } @@ -331,37 +394,31 @@ const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap"; const luaL_reg LuaPerlinNoiseMap::methods[] = { luamethod(LuaPerlinNoiseMap, get2dMap), luamethod(LuaPerlinNoiseMap, get2dMap_flat), + luamethod(LuaPerlinNoiseMap, calc2dMap), luamethod(LuaPerlinNoiseMap, get3dMap), luamethod(LuaPerlinNoiseMap, get3dMap_flat), + luamethod(LuaPerlinNoiseMap, calc3dMap), + luamethod(LuaPerlinNoiseMap, getMapSlice), {0,0} }; +/////////////////////////////////////// /* LuaPseudoRandom */ -// garbage collector -int LuaPseudoRandom::gc_object(lua_State *L) -{ - LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1)); - delete o; - return 0; -} - - -// next(self, min=0, max=32767) -> get next value int LuaPseudoRandom::l_next(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaPseudoRandom *o = checkobject(L, 1); int min = 0; int max = 32767; - lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist - if(!lua_isnil(L, 2)) + lua_settop(L, 3); + if (lua_isnumber(L, 2)) min = luaL_checkinteger(L, 2); - if(!lua_isnil(L, 3)) + if (lua_isnumber(L, 3)) max = luaL_checkinteger(L, 3); - if(max < min){ + if (max < min) { errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl; throw LuaError("PseudoRandom.next(): max < min"); } @@ -378,34 +435,107 @@ int LuaPseudoRandom::l_next(lua_State *L) } -LuaPseudoRandom::LuaPseudoRandom(int seed): - m_pseudo(seed) +int LuaPseudoRandom::create_object(lua_State *L) { + int seed = luaL_checknumber(L, 1); + LuaPseudoRandom *o = new LuaPseudoRandom(seed); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; } -LuaPseudoRandom::~LuaPseudoRandom() +int LuaPseudoRandom::gc_object(lua_State *L) { + LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1)); + delete o; + return 0; } -const PseudoRandom& LuaPseudoRandom::getItem() const +LuaPseudoRandom *LuaPseudoRandom::checkobject(lua_State *L, int narg) { - return m_pseudo; + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaPseudoRandom **)ud; } -PseudoRandom& LuaPseudoRandom::getItem() + +void LuaPseudoRandom::Register(lua_State *L) { - return m_pseudo; + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); } -// LuaPseudoRandom(seed) -// Creates an LuaPseudoRandom and leaves it on top of stack -int LuaPseudoRandom::create_object(lua_State *L) +const char LuaPseudoRandom::className[] = "PseudoRandom"; +const luaL_reg LuaPseudoRandom::methods[] = { + luamethod(LuaPseudoRandom, next), + {0,0} +}; + +/////////////////////////////////////// +/* + LuaPcgRandom +*/ + +int LuaPcgRandom::l_next(lua_State *L) { - int seed = luaL_checknumber(L, 1); - LuaPseudoRandom *o = new LuaPseudoRandom(seed); + NO_MAP_LOCK_REQUIRED; + + LuaPcgRandom *o = checkobject(L, 1); + u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN; + u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX; + + lua_pushinteger(L, o->m_rnd.range(min, max)); + return 1; +} + + +int LuaPcgRandom::l_rand_normal_dist(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPcgRandom *o = checkobject(L, 1); + u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN; + u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX; + int num_trials = lua_isnumber(L, 4) ? lua_tointeger(L, 4) : 6; + + lua_pushinteger(L, o->m_rnd.randNormalDist(min, max, num_trials)); + return 1; +} + + +int LuaPcgRandom::create_object(lua_State *L) +{ + lua_Integer seed = luaL_checknumber(L, 1); + LuaPcgRandom *o = lua_isnumber(L, 2) ? + new LuaPcgRandom(seed, lua_tointeger(L, 2)) : + new LuaPcgRandom(seed); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); lua_setmetatable(L, -2); @@ -413,17 +543,25 @@ int LuaPseudoRandom::create_object(lua_State *L) } -LuaPseudoRandom* LuaPseudoRandom::checkobject(lua_State *L, int narg) +int LuaPcgRandom::gc_object(lua_State *L) +{ + LuaPcgRandom *o = *(LuaPcgRandom **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPcgRandom *LuaPcgRandom::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); void *ud = luaL_checkudata(L, narg, className); if (!ud) luaL_typerror(L, narg, className); - return *(LuaPseudoRandom**)ud; // unbox pointer + return *(LuaPcgRandom **)ud; } -void LuaPseudoRandom::Register(lua_State *L) +void LuaPcgRandom::Register(lua_State *L) { lua_newtable(L); int methodtable = lua_gettop(L); @@ -432,7 +570,7 @@ void LuaPseudoRandom::Register(lua_State *L) lua_pushliteral(L, "__metatable"); lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() + lua_settable(L, metatable); lua_pushliteral(L, "__index"); lua_pushvalue(L, methodtable); @@ -442,18 +580,18 @@ void LuaPseudoRandom::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); - lua_pop(L, 1); // drop metatable + lua_pop(L, 1); - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); - // Can be created from Lua (LuaPseudoRandom(seed)) lua_register(L, className, create_object); } -const char LuaPseudoRandom::className[] = "PseudoRandom"; -const luaL_reg LuaPseudoRandom::methods[] = { - luamethod(LuaPseudoRandom, next), +const char LuaPcgRandom::className[] = "PcgRandom"; +const luaL_reg LuaPcgRandom::methods[] = { + luamethod(LuaPcgRandom, next), + luamethod(LuaPcgRandom, rand_normal_dist), {0,0} }; diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h index 3e22ac7a0..e958c5a23 100644 --- a/src/script/lua_api/l_noise.h +++ b/src/script/lua_api/l_noise.h @@ -64,6 +64,9 @@ class LuaPerlinNoiseMap : public ModApiBase { static const char className[]; static const luaL_reg methods[]; + // Exported functions + + // garbage collector static int gc_object(lua_State *L); static int l_get2dMap(lua_State *L); @@ -71,6 +74,10 @@ class LuaPerlinNoiseMap : public ModApiBase { static int l_get3dMap(lua_State *L); static int l_get3dMap_flat(lua_State *L); + static int l_calc2dMap(lua_State *L); + static int l_calc3dMap(lua_State *L); + static int l_getMapSlice(lua_State *L); + public: LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size); @@ -104,18 +111,51 @@ private: static int l_next(lua_State *L); public: - LuaPseudoRandom(int seed); - - ~LuaPseudoRandom(); - - const PseudoRandom& getItem() const; - PseudoRandom& getItem(); + LuaPseudoRandom(int seed) : + m_pseudo(seed) {} // LuaPseudoRandom(seed) // Creates an LuaPseudoRandom and leaves it on top of stack static int create_object(lua_State *L); - static LuaPseudoRandom* checkobject(lua_State *L, int narg); + static LuaPseudoRandom *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + LuaPcgRandom +*/ +class LuaPcgRandom : public ModApiBase { +private: + PcgRandom m_rnd; + + static const char className[]; + static const luaL_reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // next(self, min=-2147483648, max=2147483647) -> get next value + static int l_next(lua_State *L); + + // rand_normal_dist(self, min=-2147483648, max=2147483647, num_trials=6) -> + // get next normally distributed random value + static int l_rand_normal_dist(lua_State *L); + +public: + LuaPcgRandom(u64 seed) : + m_rnd(seed) {} + LuaPcgRandom(u64 seed, u64 seq) : + m_rnd(seed, seq) {} + + // LuaPcgRandom(seed) + // Creates an LuaPcgRandom and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPcgRandom *checkobject(lua_State *L, int narg); static void Register(lua_State *L); }; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 4286840fe..3ac8eeefb 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -26,11 +26,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "tool.h" #include "serverobject.h" -#include "content_object.h" #include "content_sao.h" #include "server.h" #include "hud.h" +#include "scripting_game.h" +#define GET_ENV_PTR ServerEnvironment* env = \ + dynamic_cast<ServerEnvironment*>(getEnv(L)); \ + if (env == NULL) return 0 struct EnumString es_HudElementType[] = { @@ -65,6 +68,7 @@ struct EnumString es_HudBuiltinElement[] = {HUD_FLAG_CROSSHAIR_VISIBLE, "crosshair"}, {HUD_FLAG_WIELDITEM_VISIBLE, "wielditem"}, {HUD_FLAG_BREATHBAR_VISIBLE, "breathbar"}, + {HUD_FLAG_MINIMAP_VISIBLE, "minimap"}, {0, NULL}, }; @@ -77,7 +81,7 @@ ObjectRef* ObjectRef::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); void *ud = luaL_checkudata(L, narg, className); - if(!ud) luaL_typerror(L, narg, className); + if (!ud) luaL_typerror(L, narg, className); return *(ObjectRef**)ud; // unbox pointer } @@ -90,9 +94,9 @@ ServerActiveObject* ObjectRef::getobject(ObjectRef *ref) LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) { ServerActiveObject *obj = getobject(ref); - if(obj == NULL) + if (obj == NULL) return NULL; - if(obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) + if (obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) return NULL; return (LuaEntitySAO*)obj; } @@ -100,9 +104,9 @@ LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) { ServerActiveObject *obj = getobject(ref); - if(obj == NULL) + if (obj == NULL) return NULL; - if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) + if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) return NULL; return (PlayerSAO*)obj; } @@ -110,7 +114,7 @@ PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) Player* ObjectRef::getplayer(ObjectRef *ref) { PlayerSAO *playersao = getplayersao(ref); - if(playersao == NULL) + if (playersao == NULL) return NULL; return playersao->getPlayer(); } @@ -129,9 +133,22 @@ int ObjectRef::gc_object(lua_State *L) { int ObjectRef::l_remove(lua_State *L) { NO_MAP_LOCK_REQUIRED; + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) + return 0; + if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) + return 0; + + std::set<int> child_ids = co->getAttachmentChildIds(); + std::set<int>::iterator it; + for (it = child_ids.begin(); it != child_ids.end(); ++it) { + ServerActiveObject *child = env->getActiveObject(*it); + child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0)); + } + verbosestream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl; co->m_removed = true; return 0; @@ -144,7 +161,7 @@ int ObjectRef::l_getpos(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; v3f pos = co->getBasePosition() / BS; lua_newtable(L); lua_pushnumber(L, pos.X); @@ -163,7 +180,7 @@ int ObjectRef::l_setpos(lua_State *L) ObjectRef *ref = checkobject(L, 1); //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // pos v3f pos = checkFloatPos(L, 2); // Do it @@ -178,7 +195,7 @@ int ObjectRef::l_moveto(lua_State *L) ObjectRef *ref = checkobject(L, 1); //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // pos v3f pos = checkFloatPos(L, 2); // continuous @@ -196,20 +213,36 @@ int ObjectRef::l_punch(lua_State *L) ObjectRef *puncher_ref = checkobject(L, 2); ServerActiveObject *co = getobject(ref); ServerActiveObject *puncher = getobject(puncher_ref); - if(co == NULL) return 0; - if(puncher == NULL) return 0; + if (co == NULL) return 0; + if (puncher == NULL) return 0; v3f dir; - if(lua_type(L, 5) != LUA_TTABLE) + if (lua_type(L, 5) != LUA_TTABLE) dir = co->getBasePosition() - puncher->getBasePosition(); else dir = read_v3f(L, 5); float time_from_last_punch = 1000000; - if(lua_isnumber(L, 3)) + if (lua_isnumber(L, 3)) time_from_last_punch = lua_tonumber(L, 3); ToolCapabilities toolcap = read_tool_capabilities(L, 4); dir.normalize(); + + s16 src_original_hp = co->getHP(); + s16 dst_origin_hp = puncher->getHP(); + // Do it co->punch(dir, &toolcap, puncher, time_from_last_punch); + + // If the punched is a player, and its HP changed + if (src_original_hp != co->getHP() && + co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co); + } + + // If the puncher is a player, and its HP changed + if (dst_origin_hp != puncher->getHP() && + puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)puncher); + } return 0; } @@ -221,8 +254,8 @@ int ObjectRef::l_right_click(lua_State *L) ObjectRef *ref2 = checkobject(L, 2); ServerActiveObject *co = getobject(ref); ServerActiveObject *co2 = getobject(ref2); - if(co == NULL) return 0; - if(co2 == NULL) return 0; + if (co == NULL) return 0; + if (co2 == NULL) return 0; // Do it co->rightClick(co2); return 0; @@ -237,12 +270,15 @@ int ObjectRef::l_set_hp(lua_State *L) ObjectRef *ref = checkobject(L, 1); luaL_checknumber(L, 2); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; int hp = lua_tonumber(L, 2); /*infostream<<"ObjectRef::l_set_hp(): id="<<co->getId() <<" hp="<<hp<<std::endl;*/ // Do it co->setHP(hp); + if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) + getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co); + // Return return 0; } @@ -255,7 +291,7 @@ int ObjectRef::l_get_hp(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL){ + if (co == NULL) { // Default hp is 1 lua_pushnumber(L, 1); return 1; @@ -274,10 +310,10 @@ int ObjectRef::l_get_inventory(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it InventoryLocation loc = co->getInventoryLocation(); - if(getServer(L)->getInventory(loc) != NULL) + if (getServer(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -290,7 +326,7 @@ int ObjectRef::l_get_wield_list(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it lua_pushstring(L, co->getWieldList().c_str()); return 1; @@ -302,7 +338,7 @@ int ObjectRef::l_get_wield_index(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it lua_pushinteger(L, co->getWieldIndex() + 1); return 1; @@ -314,7 +350,7 @@ int ObjectRef::l_get_wielded_item(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL){ + if (co == NULL) { // Empty ItemStack LuaItemStack::create(L, ItemStack()); return 1; @@ -330,10 +366,13 @@ int ObjectRef::l_set_wielded_item(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it ItemStack item = read_item(L, 2, getServer(L)); bool success = co->setWieldedItem(item); + if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + getServer(L)->SendInventory(((PlayerSAO*)co)); + } lua_pushboolean(L, success); return 1; } @@ -344,7 +383,7 @@ int ObjectRef::l_set_armor_groups(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it ItemGroupList groups; read_groups(L, 2, groups); @@ -352,13 +391,27 @@ int ObjectRef::l_set_armor_groups(lua_State *L) return 0; } +// get_armor_groups(self) +int ObjectRef::l_get_armor_groups(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if (co == NULL) + return 0; + // Do it + ItemGroupList groups = co->getArmorGroups(); + push_groups(L, groups); + return 1; +} + // set_physics_override(self, physics_override_speed, physics_override_jump, // physics_override_gravity, sneak, sneak_glitch) int ObjectRef::l_set_physics_override(lua_State *L) { ObjectRef *ref = checkobject(L, 1); PlayerSAO *co = (PlayerSAO *) getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it if (lua_istable(L, 2)) { co->m_physics_override_speed = getfloatfield_default(L, 2, "speed", co->m_physics_override_speed); @@ -369,15 +422,15 @@ int ObjectRef::l_set_physics_override(lua_State *L) co->m_physics_override_sent = false; } else { // old, non-table format - if(!lua_isnil(L, 2)){ + if (!lua_isnil(L, 2)) { co->m_physics_override_speed = lua_tonumber(L, 2); co->m_physics_override_sent = false; } - if(!lua_isnil(L, 3)){ + if (!lua_isnil(L, 3)) { co->m_physics_override_jump = lua_tonumber(L, 3); co->m_physics_override_sent = false; } - if(!lua_isnil(L, 4)){ + if (!lua_isnil(L, 4)) { co->m_physics_override_gravity = lua_tonumber(L, 4); co->m_physics_override_sent = false; } @@ -385,27 +438,74 @@ int ObjectRef::l_set_physics_override(lua_State *L) return 0; } -// set_animation(self, frame_range, frame_speed, frame_blend) +// get_physics_override(self) +int ObjectRef::l_get_physics_override(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *co = (PlayerSAO *)getobject(ref); + if (co == NULL) + return 0; + // Do it + lua_newtable(L); + lua_pushnumber(L, co->m_physics_override_speed); + lua_setfield(L, -2, "speed"); + lua_pushnumber(L, co->m_physics_override_jump); + lua_setfield(L, -2, "jump"); + lua_pushnumber(L, co->m_physics_override_gravity); + lua_setfield(L, -2, "gravity"); + lua_pushboolean(L, co->m_physics_override_sneak); + lua_setfield(L, -2, "sneak"); + lua_pushboolean(L, co->m_physics_override_sneak_glitch); + lua_setfield(L, -2, "sneak_glitch"); + return 1; +} + +// set_animation(self, frame_range, frame_speed, frame_blend, frame_loop) int ObjectRef::l_set_animation(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it v2f frames = v2f(1, 1); - if(!lua_isnil(L, 2)) + if (!lua_isnil(L, 2)) frames = read_v2f(L, 2); float frame_speed = 15; - if(!lua_isnil(L, 3)) + if (!lua_isnil(L, 3)) frame_speed = lua_tonumber(L, 3); float frame_blend = 0; - if(!lua_isnil(L, 4)) + if (!lua_isnil(L, 4)) frame_blend = lua_tonumber(L, 4); - co->setAnimation(frames, frame_speed, frame_blend); + bool frame_loop = true; + if (lua_isboolean(L, 5)) + frame_loop = lua_toboolean(L, 5); + co->setAnimation(frames, frame_speed, frame_blend, frame_loop); return 0; } +// get_animation(self) +int ObjectRef::l_get_animation(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if (co == NULL) + return 0; + // Do it + v2f frames = v2f(1,1); + float frame_speed = 15; + float frame_blend = 0; + bool frame_loop = true; + co->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop); + + push_v2f(L, frames); + lua_pushnumber(L, frame_speed); + lua_pushnumber(L, frame_blend); + lua_pushboolean(L, frame_loop); + return 4; +} + // set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed) int ObjectRef::l_set_local_animation(lua_State *L) { @@ -417,11 +517,11 @@ int ObjectRef::l_set_local_animation(lua_State *L) // Do it v2s32 frames[4]; for (int i=0;i<4;i++) { - if(!lua_isnil(L, 2+1)) + if (!lua_isnil(L, 2+1)) frames[i] = read_v2s32(L, 2+i); } float frame_speed = 30; - if(!lua_isnil(L, 6)) + if (!lua_isnil(L, 6)) frame_speed = lua_tonumber(L, 6); if (!getServer(L)->setLocalPlayerAnimations(player, frames, frame_speed)) @@ -431,6 +531,27 @@ int ObjectRef::l_set_local_animation(lua_State *L) return 0; } +// get_local_animation(self) +int ObjectRef::l_get_local_animation(lua_State *L) +{ + //NO_MAP_LOCK_REQUIRED + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + + v2s32 frames[4]; + float frame_speed; + player->getLocalAnimations(frames, &frame_speed); + + for (int i = 0; i < 4; i++) { + push_v2s32(L, frames[i]); + } + + lua_pushnumber(L, frame_speed); + return 5; +} + // set_eye_offset(self, v3f first pv, v3f third pv) int ObjectRef::l_set_eye_offset(lua_State *L) { @@ -443,9 +564,9 @@ int ObjectRef::l_set_eye_offset(lua_State *L) v3f offset_first = v3f(0, 0, 0); v3f offset_third = v3f(0, 0, 0); - if(!lua_isnil(L, 2)) + if (!lua_isnil(L, 2)) offset_first = read_v3f(L, 2); - if(!lua_isnil(L, 3)) + if (!lua_isnil(L, 3)) offset_third = read_v3f(L, 3); // Prevent abuse of offset values (keep player always visible) @@ -461,60 +582,154 @@ int ObjectRef::l_set_eye_offset(lua_State *L) return 0; } +// get_eye_offset(self) +int ObjectRef::l_get_eye_offset(lua_State *L) +{ + //NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + // Do it + push_v3f(L, player->eye_offset_first); + push_v3f(L, player->eye_offset_third); + return 2; +} + // set_bone_position(self, std::string bone, v3f position, v3f rotation) int ObjectRef::l_set_bone_position(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it std::string bone = ""; - if(!lua_isnil(L, 2)) + if (!lua_isnil(L, 2)) bone = lua_tostring(L, 2); v3f position = v3f(0, 0, 0); - if(!lua_isnil(L, 3)) + if (!lua_isnil(L, 3)) position = read_v3f(L, 3); v3f rotation = v3f(0, 0, 0); - if(!lua_isnil(L, 4)) + if (!lua_isnil(L, 4)) rotation = read_v3f(L, 4); co->setBonePosition(bone, position, rotation); return 0; } +// get_bone_position(self, bone) +int ObjectRef::l_get_bone_position(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if (co == NULL) + return 0; + // Do it + std::string bone = ""; + if (!lua_isnil(L, 2)) + bone = lua_tostring(L, 2); + + v3f position = v3f(0, 0, 0); + v3f rotation = v3f(0, 0, 0); + co->getBonePosition(bone, &position, &rotation); + + push_v3f(L, position); + push_v3f(L, rotation); + return 2; +} + // set_attach(self, parent, bone, position, rotation) int ObjectRef::l_set_attach(lua_State *L) { NO_MAP_LOCK_REQUIRED; + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); ObjectRef *parent_ref = checkobject(L, 2); ServerActiveObject *co = getobject(ref); ServerActiveObject *parent = getobject(parent_ref); - if(co == NULL) return 0; - if(parent == NULL) return 0; + if (co == NULL) + return 0; + if (parent == NULL) + return 0; // Do it + int parent_id = 0; std::string bone = ""; - if(!lua_isnil(L, 3)) - bone = lua_tostring(L, 3); v3f position = v3f(0, 0, 0); - if(!lua_isnil(L, 4)) - position = read_v3f(L, 4); v3f rotation = v3f(0, 0, 0); - if(!lua_isnil(L, 5)) + co->getAttachment(&parent_id, &bone, &position, &rotation); + if (parent_id) { + ServerActiveObject *old_parent = env->getActiveObject(parent_id); + old_parent->removeAttachmentChild(co->getId()); + } + + bone = ""; + if (!lua_isnil(L, 3)) + bone = lua_tostring(L, 3); + position = v3f(0, 0, 0); + if (!lua_isnil(L, 4)) + position = read_v3f(L, 4); + rotation = v3f(0, 0, 0); + if (!lua_isnil(L, 5)) rotation = read_v3f(L, 5); co->setAttachment(parent->getId(), bone, position, rotation); + parent->addAttachmentChild(co->getId()); return 0; } +// get_attach(self) +int ObjectRef::l_get_attach(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + GET_ENV_PTR; + + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if (co == NULL) + return 0; + + // Do it + int parent_id = 0; + std::string bone = ""; + v3f position = v3f(0, 0, 0); + v3f rotation = v3f(0, 0, 0); + co->getAttachment(&parent_id, &bone, &position, &rotation); + if (!parent_id) + return 0; + ServerActiveObject *parent = env->getActiveObject(parent_id); + + getScriptApiBase(L)->objectrefGetOrCreate(L, parent); + lua_pushlstring(L, bone.c_str(), bone.size()); + push_v3f(L, position); + push_v3f(L, rotation); + return 4; +} + // set_detach(self) int ObjectRef::l_set_detach(lua_State *L) { NO_MAP_LOCK_REQUIRED; + GET_ENV_PTR; + ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) + return 0; + + int parent_id = 0; + std::string bone = ""; + v3f position; + v3f rotation; + co->getAttachment(&parent_id, &bone, &position, &rotation); + ServerActiveObject *parent = NULL; + if (parent_id) + parent = env->getActiveObject(parent_id); + // Do it co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0)); + if (parent != NULL) + parent->removeAttachmentChild(co->getId()); return 0; } @@ -524,15 +739,40 @@ int ObjectRef::l_set_properties(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; ObjectProperties *prop = co->accessObjectProperties(); - if(!prop) + if (!prop) return 0; read_object_properties(L, 2, prop); co->notifyObjectPropertiesModified(); return 0; } +// get_properties(self) +int ObjectRef::l_get_properties(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if (co == NULL) + return 0; + ObjectProperties *prop = co->accessObjectProperties(); + if (!prop) + return 0; + push_object_properties(L, prop); + return 1; +} + +// is_player(self) +int ObjectRef::l_is_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + lua_pushboolean(L, (player != NULL)); + return 1; +} + /* LuaEntitySAO-only */ // setvelocity(self, {x=num, y=num, z=num}) @@ -541,7 +781,7 @@ int ObjectRef::l_setvelocity(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; v3f pos = checkFloatPos(L, 2); // Do it co->setVelocity(pos); @@ -554,7 +794,7 @@ int ObjectRef::l_getvelocity(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it v3f v = co->getVelocity(); pushFloatPos(L, v); @@ -567,7 +807,7 @@ int ObjectRef::l_setacceleration(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // pos v3f pos = checkFloatPos(L, 2); // Do it @@ -581,7 +821,7 @@ int ObjectRef::l_getacceleration(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it v3f v = co->getAcceleration(); pushFloatPos(L, v); @@ -594,7 +834,7 @@ int ObjectRef::l_setyaw(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; float yaw = luaL_checknumber(L, 2) * core::RADTODEG; // Do it co->setYaw(yaw); @@ -607,7 +847,7 @@ int ObjectRef::l_getyaw(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it float yaw = co->getYaw() * core::DEGTORAD; lua_pushnumber(L, yaw); @@ -620,7 +860,7 @@ int ObjectRef::l_settexturemod(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it std::string mod = luaL_checkstring(L, 2); co->setTextureMod(mod); @@ -634,19 +874,19 @@ int ObjectRef::l_setsprite(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it v2s16 p(0,0); - if(!lua_isnil(L, 2)) + if (!lua_isnil(L, 2)) p = read_v2s16(L, 2); int num_frames = 1; - if(!lua_isnil(L, 3)) + if (!lua_isnil(L, 3)) num_frames = lua_tonumber(L, 3); float framelength = 0.2; - if(!lua_isnil(L, 4)) + if (!lua_isnil(L, 4)) framelength = lua_tonumber(L, 4); bool select_horiz_by_yawpitch = false; - if(!lua_isnil(L, 5)) + if (!lua_isnil(L, 5)) select_horiz_by_yawpitch = lua_toboolean(L, 5); co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch); return 0; @@ -660,7 +900,7 @@ int ObjectRef::l_get_entity_name(lua_State *L) ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); log_deprecated(L,"Deprecated call to \"get_entity_name"); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it std::string name = co->getName(); lua_pushstring(L, name.c_str()); @@ -673,7 +913,7 @@ int ObjectRef::l_get_luaentity(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it luaentity_get(L, co->getId()); return 1; @@ -681,16 +921,6 @@ int ObjectRef::l_get_luaentity(lua_State *L) /* Player-only */ -// is_player(self) -int ObjectRef::l_is_player(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - lua_pushboolean(L, (player != NULL)); - return 1; -} - // is_player_connected(self) int ObjectRef::l_is_player_connected(lua_State *L) { @@ -707,7 +937,7 @@ int ObjectRef::l_get_player_name(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL){ + if (player == NULL) { lua_pushlstring(L, "", 0); return 1; } @@ -716,13 +946,28 @@ int ObjectRef::l_get_player_name(lua_State *L) return 1; } +// get_player_velocity(self) +int ObjectRef::l_get_player_velocity(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) { + lua_pushnil(L); + return 1; + } + // Do it + push_v3f(L, player->getSpeed() / BS); + return 1; +} + // get_look_dir(self) int ObjectRef::l_get_look_dir(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL) return 0; + if (player == NULL) return 0; // Do it float pitch = player->getRadPitch(); float yaw = player->getRadYaw(); @@ -737,7 +982,7 @@ int ObjectRef::l_get_look_pitch(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL) return 0; + if (player == NULL) return 0; // Do it lua_pushnumber(L, player->getRadPitch()); return 1; @@ -749,7 +994,7 @@ int ObjectRef::l_get_look_yaw(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL) return 0; + if (player == NULL) return 0; // Do it lua_pushnumber(L, player->getRadYaw()); return 1; @@ -761,7 +1006,7 @@ int ObjectRef::l_set_look_pitch(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; float pitch = luaL_checknumber(L, 2) * core::RADTODEG; // Do it co->setPitch(pitch); @@ -774,7 +1019,7 @@ int ObjectRef::l_set_look_yaw(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; float yaw = luaL_checknumber(L, 2) * core::RADTODEG; // Do it co->setYaw(yaw); @@ -787,11 +1032,15 @@ int ObjectRef::l_set_breath(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; u16 breath = luaL_checknumber(L, 2); // Do it co->setBreath(breath); - co->m_breath_not_sent = true; + + // If the object is a player sent the breath to client + if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) + getServer(L)->SendPlayerBreath(((PlayerSAO*)co)->getPeerID()); + return 0; } @@ -801,7 +1050,7 @@ int ObjectRef::l_get_breath(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); - if(co == NULL) return 0; + if (co == NULL) return 0; // Do it u16 breath = co->getBreath(); lua_pushinteger (L, breath); @@ -814,7 +1063,7 @@ int ObjectRef::l_set_inventory_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL) return 0; + if (player == NULL) return 0; std::string formspec = luaL_checkstring(L, 2); player->inventory_formspec = formspec; @@ -829,7 +1078,7 @@ int ObjectRef::l_get_inventory_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL) return 0; + if (player == NULL) return 0; std::string formspec = player->inventory_formspec; lua_pushlstring(L, formspec.c_str(), formspec.size()); @@ -842,7 +1091,7 @@ int ObjectRef::l_get_player_control(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL){ + if (player == NULL) { lua_pushlstring(L, "", 0); return 1; } @@ -876,7 +1125,7 @@ int ObjectRef::l_get_player_control_bits(lua_State *L) NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); - if(player == NULL){ + if (player == NULL) { lua_pushlstring(L, "", 0); return 1; } @@ -1136,6 +1385,8 @@ int ObjectRef::l_hud_get_flags(lua_State *L) lua_setfield(L, -2, "wielditem"); lua_pushboolean(L, player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE); lua_setfield(L, -2, "breathbar"); + lua_pushboolean(L, player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE); + lua_setfield(L, -2, "minimap"); return 1; } @@ -1157,6 +1408,20 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L) return 1; } +// hud_get_hotbar_itemcount(self) +int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + + s32 hotbar_itemcount = getServer(L)->hudGetHotbarItemcount(player); + + lua_pushnumber(L, hotbar_itemcount); + return 1; +} + // hud_set_hotbar_image(self, name) int ObjectRef::l_hud_set_hotbar_image(lua_State *L) { @@ -1171,6 +1436,19 @@ int ObjectRef::l_hud_set_hotbar_image(lua_State *L) return 1; } +// hud_get_hotbar_image(self) +int ObjectRef::l_hud_get_hotbar_image(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + + std::string name = getServer(L)->hudGetHotbarImage(player); + lua_pushlstring(L, name.c_str(), name.size()); + return 1; +} + // hud_set_hotbar_selected_image(self, name) int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L) { @@ -1185,6 +1463,19 @@ int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L) return 1; } +// hud_get_hotbar_selected_image(self) +int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + + std::string name = getServer(L)->hudGetHotbarSelectedImage(player); + lua_pushlstring(L, name.c_str(), name.size()); + return 1; +} + // set_sky(self, bgcolor, type, list) int ObjectRef::l_set_sky(lua_State *L) { @@ -1194,8 +1485,7 @@ int ObjectRef::l_set_sky(lua_State *L) return 0; video::SColor bgcolor(255,255,255,255); - if (!lua_isnil(L, 2)) - bgcolor = readARGB8(L, 2); + read_color(L, 2, &bgcolor); std::string type = luaL_checkstring(L, 3); @@ -1224,6 +1514,33 @@ int ObjectRef::l_set_sky(lua_State *L) return 1; } +// get_sky(self) +int ObjectRef::l_get_sky(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + video::SColor bgcolor(255, 255, 255, 255); + std::string type; + std::vector<std::string> params; + + player->getSky(&bgcolor, &type, ¶ms); + type = type == "" ? "regular" : type; + + push_ARGB8(L, bgcolor); + lua_pushlstring(L, type.c_str(), type.size()); + lua_newtable(L); + s16 i = 1; + for (std::vector<std::string>::iterator it = params.begin(); + it != params.end(); ++it) { + lua_pushlstring(L, it->c_str(), it->size()); + lua_rawseti(L, -2, i); + i++; + } + return 3; +} + // override_day_night_ratio(self, brightness=0...1) int ObjectRef::l_override_day_night_ratio(lua_State *L) { @@ -1234,7 +1551,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L) bool do_override = false; float ratio = 0.0f; - if (!lua_isnil(L, 2)){ + if (!lua_isnil(L, 2)) { do_override = true; ratio = luaL_checknumber(L, 2); } @@ -1246,6 +1563,65 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L) return 1; } +// get_day_night_ratio(self) +int ObjectRef::l_get_day_night_ratio(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + + bool do_override; + float ratio; + player->getDayNightRatio(&do_override, &ratio); + + if (do_override) + lua_pushnumber(L, ratio); + else + lua_pushnil(L); + + return 1; +} + +// set_nametag_attributes(self, attributes) +int ObjectRef::l_set_nametag_attributes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = getplayersao(ref); + if (playersao == NULL) + return 0; + + lua_getfield(L, 2, "color"); + if (!lua_isnil(L, -1)) { + video::SColor color = playersao->getNametagColor(); + if (!read_color(L, -1, &color)) + return 0; + playersao->setNametagColor(color); + } + + lua_pushboolean(L, true); + return 1; +} + +// get_nametag_attributes(self) +int ObjectRef::l_get_nametag_attributes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *playersao = getplayersao(ref); + if (playersao == NULL) + return 0; + + video::SColor color = playersao->getNametagColor(); + + lua_newtable(L); + push_ARGB8(L, color); + lua_setfield(L, -2, "color"); + + return 1; +} + ObjectRef::ObjectRef(ServerActiveObject *object): m_object(object) { @@ -1254,7 +1630,7 @@ ObjectRef::ObjectRef(ServerActiveObject *object): ObjectRef::~ObjectRef() { - /*if(m_object) + /*if (m_object) infostream<<"ObjectRef destructing for id=" <<m_object->getId()<<std::endl; else @@ -1323,12 +1699,16 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, get_wielded_item), luamethod(ObjectRef, set_wielded_item), luamethod(ObjectRef, set_armor_groups), - luamethod(ObjectRef, set_physics_override), + luamethod(ObjectRef, get_armor_groups), luamethod(ObjectRef, set_animation), + luamethod(ObjectRef, get_animation), luamethod(ObjectRef, set_bone_position), + luamethod(ObjectRef, get_bone_position), luamethod(ObjectRef, set_attach), + luamethod(ObjectRef, get_attach), luamethod(ObjectRef, set_detach), luamethod(ObjectRef, set_properties), + luamethod(ObjectRef, get_properties), // LuaEntitySAO-only luamethod(ObjectRef, setvelocity), luamethod(ObjectRef, getvelocity), @@ -1344,6 +1724,7 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, is_player), luamethod(ObjectRef, is_player_connected), luamethod(ObjectRef, get_player_name), + luamethod(ObjectRef, get_player_velocity), luamethod(ObjectRef, get_look_dir), luamethod(ObjectRef, get_look_pitch), luamethod(ObjectRef, get_look_yaw), @@ -1355,6 +1736,8 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, get_player_control), luamethod(ObjectRef, get_player_control_bits), + luamethod(ObjectRef, set_physics_override), + luamethod(ObjectRef, get_physics_override), luamethod(ObjectRef, hud_add), luamethod(ObjectRef, hud_remove), luamethod(ObjectRef, hud_change), @@ -1362,11 +1745,20 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, hud_set_flags), luamethod(ObjectRef, hud_get_flags), luamethod(ObjectRef, hud_set_hotbar_itemcount), + luamethod(ObjectRef, hud_get_hotbar_itemcount), luamethod(ObjectRef, hud_set_hotbar_image), + luamethod(ObjectRef, hud_get_hotbar_image), luamethod(ObjectRef, hud_set_hotbar_selected_image), + luamethod(ObjectRef, hud_get_hotbar_selected_image), luamethod(ObjectRef, set_sky), + luamethod(ObjectRef, get_sky), luamethod(ObjectRef, override_day_night_ratio), + luamethod(ObjectRef, get_day_night_ratio), luamethod(ObjectRef, set_local_animation), + luamethod(ObjectRef, get_local_animation), luamethod(ObjectRef, set_eye_offset), + luamethod(ObjectRef, get_eye_offset), + luamethod(ObjectRef, set_nametag_attributes), + luamethod(ObjectRef, get_nametag_attributes), {0,0} }; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index d51ca379f..a4457cc05 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -101,25 +101,46 @@ private: // set_armor_groups(self, groups) static int l_set_armor_groups(lua_State *L); + // get_armor_groups(self) + static int l_get_armor_groups(lua_State *L); + // set_physics_override(self, physics_override_speed, physics_override_jump, // physics_override_gravity, sneak, sneak_glitch) static int l_set_physics_override(lua_State *L); - // set_animation(self, frame_range, frame_speed, frame_blend) + // get_physics_override(self) + static int l_get_physics_override(lua_State *L); + + // set_animation(self, frame_range, frame_speed, frame_blend, frame_loop) static int l_set_animation(lua_State *L); + // get_animation(self) + static int l_get_animation(lua_State *L); + // set_bone_position(self, std::string bone, v3f position, v3f rotation) static int l_set_bone_position(lua_State *L); + // get_bone_position(self, bone) + static int l_get_bone_position(lua_State *L); + // set_attach(self, parent, bone, position, rotation) static int l_set_attach(lua_State *L); + // get_attach(self) + static int l_get_attach(lua_State *L); + // set_detach(self) static int l_set_detach(lua_State *L); // set_properties(self, properties) static int l_set_properties(lua_State *L); + // get_properties(self) + static int l_get_properties(lua_State *L); + + // is_player(self) + static int l_is_player(lua_State *L); + /* LuaEntitySAO-only */ // setvelocity(self, {x=num, y=num, z=num}) @@ -156,15 +177,15 @@ private: /* Player-only */ - // is_player(self) - static int l_is_player(lua_State *L); - // is_player_connected(self) static int l_is_player_connected(lua_State *L); // get_player_name(self) static int l_get_player_name(lua_State *L); + // get_player_velocity(self) + static int l_get_player_velocity(lua_State *L); + // get_look_dir(self) static int l_get_look_dir(lua_State *L); @@ -222,24 +243,51 @@ private: // hud_set_hotbar_itemcount(self, hotbar_itemcount) static int l_hud_set_hotbar_itemcount(lua_State *L); + // hud_get_hotbar_itemcount(self) + static int l_hud_get_hotbar_itemcount(lua_State *L); + // hud_set_hotbar_image(self, name) static int l_hud_set_hotbar_image(lua_State *L); + // hud_get_hotbar_image(self) + static int l_hud_get_hotbar_image(lua_State *L); + // hud_set_hotbar_selected_image(self, name) static int l_hud_set_hotbar_selected_image(lua_State *L); + // hud_get_hotbar_selected_image(self) + static int l_hud_get_hotbar_selected_image(lua_State *L); + // set_sky(self, type, list) static int l_set_sky(lua_State *L); - // override_day_night_ratio(self, type, list) + // get_sky(self, type, list) + static int l_get_sky(lua_State *L); + + // override_day_night_ratio(self, type) static int l_override_day_night_ratio(lua_State *L); + // get_day_night_ratio(self) + static int l_get_day_night_ratio(lua_State *L); + // set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed) static int l_set_local_animation(lua_State *L); + // get_local_animation(self) + static int l_get_local_animation(lua_State *L); + // set_eye_offset(self, v3f first pv, v3f third pv) static int l_set_eye_offset(lua_State *L); + // get_eye_offset(self) + static int l_get_eye_offset(lua_State *L); + + // set_nametag_attributes(self, attributes) + static int l_set_nametag_attributes(lua_State *L); + + // get_nametag_attributes(self) + static int l_get_nametag_attributes(lua_State *L); + public: ObjectRef(ServerActiveObject *object); diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 6769f5c23..2532b2b08 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -34,17 +34,20 @@ int ModApiParticles::l_add_particle(lua_State *L) { // Get parameters v3f pos, vel, acc; - pos= vel= acc= v3f(0, 0, 0); + pos = vel = acc = v3f(0, 0, 0); + float expirationtime, size; - expirationtime= size= 1; + expirationtime = size = 1; + bool collisiondetection, vertical; - collisiondetection= vertical= false; + collisiondetection = vertical = false; + std::string texture = ""; - const char *playername = ""; + std::string playername = ""; if (lua_gettop(L) > 1) // deprecated { - log_deprecated(L,"Deprecated add_particle call with individual parameters instead of definition"); + log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); pos = check_v3f(L, 1); vel = check_v3f(L, 2); acc = check_v3f(L, 3); @@ -57,44 +60,44 @@ int ModApiParticles::l_add_particle(lua_State *L) } else if (lua_istable(L, 1)) { - int table = lua_gettop(L); - lua_pushnil(L); - while (lua_next(L, table) != 0) - { - const char *key = lua_tostring(L, -2); - if(strcmp(key,"pos")==0){ - pos=check_v3f(L, -1); - }else if(strcmp(key,"vel")==0){ - vel=check_v3f(L, -1); - }else if(strcmp(key,"acc")==0){ - acc=check_v3f(L, -1); - }else if(strcmp(key,"expirationtime")==0){ - expirationtime=luaL_checknumber(L, -1); - }else if(strcmp(key,"size")==0){ - size=luaL_checknumber(L, -1); - }else if(strcmp(key,"collisiondetection")==0){ - collisiondetection=lua_toboolean(L, -1); - }else if(strcmp(key,"vertical")==0){ - vertical=lua_toboolean(L, -1); - }else if(strcmp(key,"texture")==0){ - texture=luaL_checkstring(L, -1); - }else if(strcmp(key,"playername")==0){ - playername=luaL_checkstring(L, -1); - } - lua_pop(L, 1); + lua_getfield(L, 1, "pos"); + pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f(); + lua_pop(L, 1); + + lua_getfield(L, 1, "vel"); + if (lua_istable(L, -1)) { + vel = check_v3f(L, -1); + log_deprecated(L, "The use of vel is deprecated. " + "Use velocity instead"); } + lua_pop(L, 1); + + lua_getfield(L, 1, "velocity"); + vel = lua_istable(L, -1) ? check_v3f(L, -1) : vel; + lua_pop(L, 1); + + lua_getfield(L, 1, "acc"); + if (lua_istable(L, -1)) { + acc = check_v3f(L, -1); + log_deprecated(L, "The use of acc is deprecated. " + "Use acceleration instead"); + } + lua_pop(L, 1); + + lua_getfield(L, 1, "acceleration"); + acc = lua_istable(L, -1) ? check_v3f(L, -1) : acc; + lua_pop(L, 1); + + expirationtime = getfloatfield_default(L, 1, "expirationtime", 1); + size = getfloatfield_default(L, 1, "size", 1); + collisiondetection = getboolfield_default(L, 1, + "collisiondetection", collisiondetection); + vertical = getboolfield_default(L, 1, "vertical", vertical); + texture = getstringfield_default(L, 1, "texture", ""); + playername = getstringfield_default(L, 1, "playername", ""); } - if (strcmp(playername, "")==0) // spawn for all players - { - getServer(L)->spawnParticleAll(pos, vel, acc, + getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, collisiondetection, vertical, texture); - } - else - { - getServer(L)->spawnParticle(playername, - pos, vel, acc, expirationtime, - size, collisiondetection, vertical, texture); - } return 1; } @@ -125,7 +128,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) bool collisiondetection, vertical; collisiondetection= vertical= false; std::string texture = ""; - const char *playername = ""; + std::string playername = ""; if (lua_gettop(L) > 1) //deprecated { @@ -149,74 +152,55 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) } else if (lua_istable(L, 1)) { - int table = lua_gettop(L); - lua_pushnil(L); - while (lua_next(L, table) != 0) - { - const char *key = lua_tostring(L, -2); - if(strcmp(key,"amount")==0){ - amount=luaL_checknumber(L, -1); - }else if(strcmp(key,"time")==0){ - time=luaL_checknumber(L, -1); - }else if(strcmp(key,"minpos")==0){ - minpos=check_v3f(L, -1); - }else if(strcmp(key,"maxpos")==0){ - maxpos=check_v3f(L, -1); - }else if(strcmp(key,"minvel")==0){ - minvel=check_v3f(L, -1); - }else if(strcmp(key,"maxvel")==0){ - maxvel=check_v3f(L, -1); - }else if(strcmp(key,"minacc")==0){ - minacc=check_v3f(L, -1); - }else if(strcmp(key,"maxacc")==0){ - maxacc=check_v3f(L, -1); - }else if(strcmp(key,"minexptime")==0){ - minexptime=luaL_checknumber(L, -1); - }else if(strcmp(key,"maxexptime")==0){ - maxexptime=luaL_checknumber(L, -1); - }else if(strcmp(key,"minsize")==0){ - minsize=luaL_checknumber(L, -1); - }else if(strcmp(key,"maxsize")==0){ - maxsize=luaL_checknumber(L, -1); - }else if(strcmp(key,"collisiondetection")==0){ - collisiondetection=lua_toboolean(L, -1); - }else if(strcmp(key,"vertical")==0){ - vertical=lua_toboolean(L, -1); - }else if(strcmp(key,"texture")==0){ - texture=luaL_checkstring(L, -1); - }else if(strcmp(key,"playername")==0){ - playername=luaL_checkstring(L, -1); - } - lua_pop(L, 1); - } - } - if (strcmp(playername, "")==0) //spawn for all players - { - u32 id = getServer(L)->addParticleSpawnerAll( amount, time, - minpos, maxpos, - minvel, maxvel, - minacc, maxacc, - minexptime, maxexptime, - minsize, maxsize, - collisiondetection, - vertical, - texture); - lua_pushnumber(L, id); - } - else - { - u32 id = getServer(L)->addParticleSpawner(playername, - amount, time, - minpos, maxpos, - minvel, maxvel, - minacc, maxacc, - minexptime, maxexptime, - minsize, maxsize, - collisiondetection, - vertical, - texture); - lua_pushnumber(L, id); + amount = getintfield_default(L, 1, "amount", amount); + time = getfloatfield_default(L, 1, "time", time); + + lua_getfield(L, 1, "minpos"); + minpos = lua_istable(L, -1) ? check_v3f(L, -1) : minpos; + lua_pop(L, 1); + + lua_getfield(L, 1, "maxpos"); + maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : maxpos; + lua_pop(L, 1); + + lua_getfield(L, 1, "minvel"); + minvel = lua_istable(L, -1) ? check_v3f(L, -1) : minvel; + lua_pop(L, 1); + + lua_getfield(L, 1, "maxvel"); + maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : maxvel; + lua_pop(L, 1); + + lua_getfield(L, 1, "minacc"); + minacc = lua_istable(L, -1) ? check_v3f(L, -1) : minacc; + lua_pop(L, 1); + + lua_getfield(L, 1, "maxacc"); + maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : maxacc; + lua_pop(L, 1); + + minexptime = getfloatfield_default(L, 1, "minexptime", minexptime); + maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime); + minsize = getfloatfield_default(L, 1, "minsize", minsize); + maxsize = getfloatfield_default(L, 1, "maxsize", maxsize); + collisiondetection = getboolfield_default(L, 1, + "collisiondetection", collisiondetection); + vertical = getboolfield_default(L, 1, "vertical", vertical); + texture = getstringfield_default(L, 1, "texture", ""); + playername = getstringfield_default(L, 1, "playername", ""); } + + u32 id = getServer(L)->addParticleSpawner(amount, time, + minpos, maxpos, + minvel, maxvel, + minacc, maxacc, + minexptime, maxexptime, + minsize, maxsize, + collisiondetection, + vertical, + texture, playername); + lua_pushnumber(L, id); + return 1; } @@ -226,16 +210,12 @@ int ModApiParticles::l_delete_particlespawner(lua_State *L) { // Get parameters u32 id = luaL_checknumber(L, 1); - - if (lua_gettop(L) == 2) // only delete for one player - { - const char *playername = luaL_checkstring(L, 2); - getServer(L)->deleteParticleSpawner(playername, id); - } - else // delete for all players - { - getServer(L)->deleteParticleSpawnerAll(id); + std::string playername = ""; + if (lua_gettop(L) == 2) { + playername = luaL_checkstring(L, 2); } + + getServer(L)->deleteParticleSpawner(playername, id); return 1; } diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 8d7f6512e..73eca9d60 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "cpp_api/s_base.h" #include "server.h" #include "environment.h" #include "player.h" @@ -29,7 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc., // request_shutdown() int ModApiServer::l_request_shutdown(lua_State *L) { - getServer(L)->requestShutdown(); + const char *msg = lua_tolstring(L, 1, NULL); + bool reconnect = lua_toboolean(L, 2); + getServer(L)->requestShutdown(msg ? msg : "", reconnect); return 0; } @@ -306,7 +309,7 @@ int ModApiServer::l_kick_player(lua_State *L) lua_pushboolean(L, false); // No such player return 1; } - getServer(L)->DenyAccess(player->peer_id, narrow_to_wide(message)); + getServer(L)->DenyAccess_Legacy(player->peer_id, utf8_to_wide(message)); lua_pushboolean(L, true); return 1; } @@ -342,7 +345,7 @@ int ModApiServer::l_show_formspec(lua_State *L) int ModApiServer::l_get_current_modname(lua_State *L) { NO_MAP_LOCK_REQUIRED; - lua_getfield(L, LUA_REGISTRYINDEX, "current_modname"); + lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); return 1; } @@ -367,33 +370,18 @@ int ModApiServer::l_get_modnames(lua_State *L) NO_MAP_LOCK_REQUIRED; // Get a list of mods - std::list<std::string> mods_unsorted, mods_sorted; - getServer(L)->getModNames(mods_unsorted); + std::vector<std::string> modlist; + getServer(L)->getModNames(modlist); // Take unsorted items from mods_unsorted and sort them into // mods_sorted; not great performance but the number of mods on a // server will likely be small. - for(std::list<std::string>::iterator i = mods_unsorted.begin(); - i != mods_unsorted.end(); ++i) { - bool added = false; - for(std::list<std::string>::iterator x = mods_sorted.begin(); - x != mods_sorted.end(); ++x) { - // I doubt anybody using Minetest will be using - // anything not ASCII based :) - if(i->compare(*x) <= 0) { - mods_sorted.insert(x, *i); - added = true; - break; - } - } - if(!added) - mods_sorted.push_back(*i); - } + std::sort(modlist.begin(), modlist.end()); // Package them up for Lua - lua_createtable(L, mods_sorted.size(), 0); - std::list<std::string>::iterator iter = mods_sorted.begin(); - for (u16 i = 0; iter != mods_sorted.end(); iter++) { + lua_createtable(L, modlist.size(), 0); + std::vector<std::string>::iterator iter = modlist.begin(); + for (u16 i = 0; iter != modlist.end(); iter++) { lua_pushstring(L, iter->c_str()); lua_rawseti(L, -2, ++i); } @@ -450,6 +438,31 @@ int ModApiServer::l_notify_authentication_modified(lua_State *L) return 0; } +// get_last_run_mod() +int ModApiServer::l_get_last_run_mod(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + const char *current_mod = lua_tostring(L, -1); + if (current_mod == NULL || current_mod[0] == '\0') { + lua_pop(L, 1); + lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str()); + } + return 1; +} + +// set_last_run_mod(modname) +int ModApiServer::l_set_last_run_mod(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; +#ifdef SCRIPTAPI_DEBUG + const char *mod = lua_tostring(L, 1); + getScriptApiBase(L)->setOriginDirect(mod); + //printf(">>>> last mod set from Lua: %s\n", mod); +#endif + return 0; +} + #ifndef NDEBUG // cause_error(type_of_error) int ModApiServer::l_cause_error(lua_State *L) @@ -507,6 +520,8 @@ void ModApiServer::Initialize(lua_State *L, int top) API_FCT(unban_player_or_ip); API_FCT(notify_authentication_modified); + API_FCT(get_last_run_mod); + API_FCT(set_last_run_mod); #ifndef NDEBUG API_FCT(cause_error); #endif diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index fd85a8975..df31f325f 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class ModApiServer : public ModApiBase { private: - // request_shutdown() + // request_shutdown([message], [reconnect]) static int l_request_shutdown(lua_State *L); // get_server_status() @@ -88,6 +88,12 @@ private: // notify_authentication_modified(name) static int l_notify_authentication_modified(lua_State *L); + // get_last_run_mod() + static int l_get_last_run_mod(lua_State *L); + + // set_last_run_mod(modname) + static int l_set_last_run_mod(lua_State *L); + #ifndef NDEBUG // cause_error(type_of_error) static int l_cause_error(lua_State *L); diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index 9c88a3e05..35b82b435 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_settings.h" #include "lua_api/l_internal.h" +#include "cpp_api/s_security.h" #include "settings.h" #include "log.h" @@ -188,6 +189,7 @@ int LuaSettings::create_object(lua_State* L) { NO_MAP_LOCK_REQUIRED; const char* filename = luaL_checkstring(L, 1); + CHECK_SECURE_PATH_OPTIONAL(L, filename); LuaSettings* o = new LuaSettings(filename); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index eb6c1835d..12146e80a 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -24,13 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_async.h" #include "serialization.h" #include "json/json.h" +#include "cpp_api/s_security.h" +#include "areastore.h" #include "debug.h" #include "porting.h" #include "log.h" #include "tool.h" #include "filesys.h" #include "settings.h" -#include "main.h" //required for g_settings, g_settings_path +#include "util/auth.h" +#include <algorithm> // debug(...) // Writes a line to dstream @@ -92,12 +95,19 @@ int ModApiUtil::l_log(lua_State *L) return 0; } +#define CHECK_SECURE_SETTING(L, name) \ + if (name.compare(0, 7, "secure.") == 0) {\ + lua_pushliteral(L, "Attempt to set secure setting.");\ + lua_error(L);\ + } + // setting_set(name, value) int ModApiUtil::l_setting_set(lua_State *L) { NO_MAP_LOCK_REQUIRED; - const char *name = luaL_checkstring(L, 1); - const char *value = luaL_checkstring(L, 2); + std::string name = luaL_checkstring(L, 1); + std::string value = luaL_checkstring(L, 2); + CHECK_SECURE_SETTING(L, name); g_settings->set(name, value); return 0; } @@ -120,8 +130,9 @@ int ModApiUtil::l_setting_get(lua_State *L) int ModApiUtil::l_setting_setbool(lua_State *L) { NO_MAP_LOCK_REQUIRED; - const char *name = luaL_checkstring(L, 1); + std::string name = luaL_checkstring(L, 1); bool value = lua_toboolean(L, 2); + CHECK_SECURE_SETTING(L, name); g_settings->setBool(name, value); return 0; } @@ -256,8 +267,7 @@ int ModApiUtil::l_get_password_hash(lua_State *L) NO_MAP_LOCK_REQUIRED; std::string name = luaL_checkstring(L, 1); std::string raw_password = luaL_checkstring(L, 2); - std::string hash = translatePassword(name, - narrow_to_wide(raw_password)); + std::string hash = translatePassword(name, raw_password); lua_pushstring(L, hash.c_str()); return 1; } @@ -308,7 +318,7 @@ int ModApiUtil::l_compress(lua_State *L) int ModApiUtil::l_decompress(lua_State *L) { size_t size; - const char * data = luaL_checklstring(L, 1, &size); + const char *data = luaL_checklstring(L, 1, &size); std::istringstream is(std::string(data, size)); std::ostringstream os; @@ -320,6 +330,64 @@ int ModApiUtil::l_decompress(lua_State *L) return 1; } +// mkdir(path) +int ModApiUtil::l_mkdir(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *path = luaL_checkstring(L, 1); + CHECK_SECURE_PATH_OPTIONAL(L, path); + lua_pushboolean(L, fs::CreateAllDirs(path)); + return 1; +} + +// get_dir_list(path, is_dir) +int ModApiUtil::l_get_dir_list(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *path = luaL_checkstring(L, 1); + short is_dir = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : -1; + + CHECK_SECURE_PATH_OPTIONAL(L, path); + + std::vector<fs::DirListNode> list = fs::GetDirListing(path); + + int index = 0; + lua_newtable(L); + + for (size_t i = 0; i < list.size(); i++) { + if (is_dir == -1 || is_dir == list[i].dir) { + lua_pushstring(L, list[i].name.c_str()); + lua_rawseti(L, -2, ++index); + } + } + + return 1; +} + +int ModApiUtil::l_request_insecure_environment(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + if (!ScriptApiSecurity::isSecure(L)) { + lua_getglobal(L, "_G"); + return 1; + } + lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD); + if (!lua_isstring(L, -1)) { + lua_pushnil(L); + return 1; + } + const char *mod_name = lua_tostring(L, -1); + std::string trusted_mods = g_settings->get("secure.trusted_mods"); + std::vector<std::string> mod_list = str_split(trusted_mods, ','); + if (std::find(mod_list.begin(), mod_list.end(), mod_name) == mod_list.end()) { + lua_pushnil(L); + return 1; + } + lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup"); + return 1; +} + + void ModApiUtil::Initialize(lua_State *L, int top) { API_FCT(debug); @@ -345,6 +413,11 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(compress); API_FCT(decompress); + + API_FCT(mkdir); + API_FCT(get_dir_list); + + API_FCT(request_insecure_environment); } void ModApiUtil::InitializeAsync(AsyncEngine& engine) @@ -367,5 +440,8 @@ void ModApiUtil::InitializeAsync(AsyncEngine& engine) ASYNC_API_FCT(compress); ASYNC_API_FCT(decompress); + + ASYNC_API_FCT(mkdir); + ASYNC_API_FCT(get_dir_list); } diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index e82432381..e75aa28cb 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -78,7 +78,7 @@ private: // is_yes(arg) static int l_is_yes(lua_State *L); - // get_scriptdir() + // get_builtin_path() static int l_get_builtin_path(lua_State *L); // compress(data, method, ...) @@ -87,6 +87,15 @@ private: // decompress(data, method, ...) static int l_decompress(lua_State *L); + // mkdir(path) + static int l_mkdir(lua_State *L); + + // get_dir_list(path, is_dir) + static int l_get_dir_list(lua_State *L); + + // request_insecure_environment() + static int l_request_insecure_environment(lua_State *L); + public: static void Initialize(lua_State *L, int top); @@ -95,3 +104,4 @@ public: }; #endif /* L_UTIL_H_ */ + diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index de7612115..ac6c10303 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -46,8 +46,8 @@ int LuaVoxelManip::l_read_from_map(lua_State *L) LuaVoxelManip *o = checkobject(L, 1); MMVManip *vm = o->vm; - v3s16 bp1 = getNodeBlockPos(read_v3s16(L, 2)); - v3s16 bp2 = getNodeBlockPos(read_v3s16(L, 3)); + v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 2)); + v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 3)); sortBoxVerticies(bp1, bp2); vm->initialEmerge(bp1, bp2); @@ -63,12 +63,18 @@ int LuaVoxelManip::l_get_data(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaVoxelManip *o = checkobject(L, 1); + bool use_buffer = lua_istable(L, 2); + MMVManip *vm = o->vm; - int volume = vm->m_area.getVolume(); + u32 volume = vm->m_area.getVolume(); - lua_newtable(L); - for (int i = 0; i != volume; i++) { + if (use_buffer) + lua_pushvalue(L, 2); + else + lua_newtable(L); + + for (u32 i = 0; i != volume; i++) { lua_Integer cid = vm->m_data[i].getContent(); lua_pushinteger(L, cid); lua_rawseti(L, -2, i + 1); @@ -87,8 +93,8 @@ int LuaVoxelManip::l_set_data(lua_State *L) if (!lua_istable(L, 2)) return 0; - int volume = vm->m_area.getVolume(); - for (int i = 0; i != volume; i++) { + u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) { lua_rawgeti(L, 2, i + 1); content_t c = lua_tointeger(L, -1); @@ -116,7 +122,7 @@ int LuaVoxelManip::l_get_node_at(lua_State *L) GET_ENV_PTR; LuaVoxelManip *o = checkobject(L, 1); - v3s16 pos = read_v3s16(L, 2); + v3s16 pos = check_v3s16(L, 2); pushnode(L, o->vm->getNodeNoExNoEmerge(pos), env->getGameDef()->ndef()); return 1; @@ -128,7 +134,7 @@ int LuaVoxelManip::l_set_node_at(lua_State *L) GET_ENV_PTR; LuaVoxelManip *o = checkobject(L, 1); - v3s16 pos = read_v3s16(L, 2); + v3s16 pos = check_v3s16(L, 2); MapNode n = readnode(L, 3, env->getGameDef()->ndef()); o->vm->setNodeNoEmerge(pos, n); @@ -171,8 +177,8 @@ int LuaVoxelManip::l_calc_lighting(lua_State *L) v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE; v3s16 fpmin = vm->m_area.MinEdge; v3s16 fpmax = vm->m_area.MaxEdge; - v3s16 pmin = lua_istable(L, 2) ? read_v3s16(L, 2) : fpmin + yblock; - v3s16 pmax = lua_istable(L, 3) ? read_v3s16(L, 3) : fpmax - yblock; + v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : fpmin + yblock; + v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : fpmax - yblock; sortBoxVerticies(pmin, pmax); if (!vm->m_area.contains(VoxelArea(pmin, pmax))) @@ -206,8 +212,8 @@ int LuaVoxelManip::l_set_lighting(lua_State *L) MMVManip *vm = o->vm; v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE; - v3s16 pmin = lua_istable(L, 3) ? read_v3s16(L, 3) : vm->m_area.MinEdge + yblock; - v3s16 pmax = lua_istable(L, 4) ? read_v3s16(L, 4) : vm->m_area.MaxEdge - yblock; + v3s16 pmin = lua_istable(L, 3) ? check_v3s16(L, 3) : vm->m_area.MinEdge + yblock; + v3s16 pmax = lua_istable(L, 4) ? check_v3s16(L, 4) : vm->m_area.MaxEdge - yblock; sortBoxVerticies(pmin, pmax); if (!vm->m_area.contains(VoxelArea(pmin, pmax))) @@ -228,10 +234,10 @@ int LuaVoxelManip::l_get_light_data(lua_State *L) LuaVoxelManip *o = checkobject(L, 1); MMVManip *vm = o->vm; - int volume = vm->m_area.getVolume(); + u32 volume = vm->m_area.getVolume(); lua_newtable(L); - for (int i = 0; i != volume; i++) { + for (u32 i = 0; i != volume; i++) { lua_Integer light = vm->m_data[i].param1; lua_pushinteger(L, light); lua_rawseti(L, -2, i + 1); @@ -250,8 +256,8 @@ int LuaVoxelManip::l_set_light_data(lua_State *L) if (!lua_istable(L, 2)) return 0; - int volume = vm->m_area.getVolume(); - for (int i = 0; i != volume; i++) { + u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) { lua_rawgeti(L, 2, i + 1); u8 light = lua_tointeger(L, -1); @@ -270,10 +276,10 @@ int LuaVoxelManip::l_get_param2_data(lua_State *L) LuaVoxelManip *o = checkobject(L, 1); MMVManip *vm = o->vm; - int volume = vm->m_area.getVolume(); + u32 volume = vm->m_area.getVolume(); lua_newtable(L); - for (int i = 0; i != volume; i++) { + for (u32 i = 0; i != volume; i++) { lua_Integer param2 = vm->m_data[i].param2; lua_pushinteger(L, param2); lua_rawseti(L, -2, i + 1); @@ -292,8 +298,8 @@ int LuaVoxelManip::l_set_param2_data(lua_State *L) if (!lua_istable(L, 2)) return 0; - int volume = vm->m_area.getVolume(); - for (int i = 0; i != volume; i++) { + u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) { lua_rawgeti(L, 2, i + 1); u8 param2 = lua_tointeger(L, -1); @@ -402,7 +408,7 @@ int LuaVoxelManip::create_object(lua_State *L) Map *map = &(env->getMap()); LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ? - new LuaVoxelManip(map, read_v3s16(L, 1), read_v3s16(L, 2)) : + new LuaVoxelManip(map, check_v3s16(L, 1), check_v3s16(L, 2)) : new LuaVoxelManip(map); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index e716bc979..4f0350d41 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_game.h" #include "server.h" #include "log.h" +#include "settings.h" #include "cpp_api/s_internal.h" +#include "lua_api/l_areastore.h" #include "lua_api/l_base.h" #include "lua_api/l_craft.h" #include "lua_api/l_env.h" @@ -49,10 +51,12 @@ GameScripting::GameScripting(Server* server) // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() // once the environment has been created - //TODO add security - SCRIPTAPI_PRECHECKHEADER + if (g_settings->getBool("secure.enable_security")) { + initializeSecurity(); + } + lua_getglobal(L, "core"); int top = lua_gettop(L); @@ -88,10 +92,12 @@ void GameScripting::InitializeModApi(lua_State *L, int top) // Register reference classes (userdata) InvRef::Register(L); + LuaAreaStore::Register(L); LuaItemStack::Register(L); LuaPerlinNoise::Register(L); LuaPerlinNoiseMap::Register(L); LuaPseudoRandom::Register(L); + LuaPcgRandom::Register(L); LuaVoxelManip::Register(L); NodeMetaRef::Register(L); NodeTimerRef::Register(L); @@ -99,7 +105,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) LuaSettings::Register(L); } -void log_deprecated(std::string message) +void log_deprecated(const std::string &message) { - log_deprecated(NULL,message); + log_deprecated(NULL, message); } diff --git a/src/script/scripting_game.h b/src/script/scripting_game.h index 14dbd9170..970b3e80d 100644 --- a/src/script/scripting_game.h +++ b/src/script/scripting_game.h @@ -27,19 +27,21 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "cpp_api/s_node.h" #include "cpp_api/s_player.h" #include "cpp_api/s_server.h" +#include "cpp_api/s_security.h" /*****************************************************************************/ /* Scripting <-> Game Interface */ /*****************************************************************************/ -class GameScripting - : virtual public ScriptApiBase, - public ScriptApiDetached, - public ScriptApiEntity, - public ScriptApiEnv, - public ScriptApiNode, - public ScriptApiPlayer, - public ScriptApiServer +class GameScripting : + virtual public ScriptApiBase, + public ScriptApiDetached, + public ScriptApiEntity, + public ScriptApiEnv, + public ScriptApiNode, + public ScriptApiPlayer, + public ScriptApiServer, + public ScriptApiSecurity { public: GameScripting(Server* server); @@ -50,6 +52,6 @@ private: void InitializeModApi(lua_State *L, int top); }; -void log_deprecated(std::string message); +void log_deprecated(const std::string &message); #endif /* SCRIPTING_GAME_H_ */ diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp index 54b3133c5..c74c18edc 100644 --- a/src/script/scripting_mainmenu.cpp +++ b/src/script/scripting_mainmenu.cpp @@ -38,8 +38,6 @@ MainMenuScripting::MainMenuScripting(GUIEngine* guiengine) { setGuiEngine(guiengine); - //TODO add security - SCRIPTAPI_PRECHECKHEADER lua_getglobal(L, "core"); |