X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fscriptapi.cpp;h=ec3d34676672decfdcca5db0d2a5f9b7665bd7f2;hb=318669327e84d8fc7498e32c5bacfad658d8ba87;hp=213fb47f9ae941a0a64d5223b721d60e3b5f5dfe;hpb=aba7134301e0fe49cb3a6b157d226e1405753237;p=oweals%2Fminetest.git diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 213fb47f9..ec3d34676 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -3,16 +3,16 @@ Minetest-c55 Copyright (C) 2011 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or +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 General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +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. */ @@ -37,16 +37,19 @@ extern "C" { #include "content_sao.h" // For LuaEntitySAO and PlayerSAO #include "itemdef.h" #include "nodedef.h" +#include "biome.h" #include "craftdef.h" #include "main.h" // For g_settings #include "settings.h" // For accessing g_settings #include "nodemetadata.h" #include "mapblock.h" // For getNodeBlockPos #include "content_nodemeta.h" -#include "utility.h" #include "tool.h" #include "daynightratio.h" #include "noise.h" // PseudoRandom for LuaPseudoRandom +#include "util/pointedthing.h" +#include "rollback.h" +#include "treegen.h" static void stackDump(lua_State *L, std::ostream &o) { @@ -382,6 +385,7 @@ struct EnumString es_DrawType[] = {NDT_PLANTLIKE, "plantlike"}, {NDT_FENCELIKE, "fencelike"}, {NDT_RAILLIKE, "raillike"}, + {NDT_NODEBOX, "nodebox"}, {0, NULL}, }; @@ -418,6 +422,31 @@ struct EnumString es_NodeBoxType[] = {0, NULL}, }; +struct EnumString es_CraftMethod[] = +{ + {CRAFT_METHOD_NORMAL, "normal"}, + {CRAFT_METHOD_COOKING, "cooking"}, + {CRAFT_METHOD_FUEL, "fuel"}, + {0, NULL}, +}; + +struct EnumString es_TileAnimationType[] = +{ + {TAT_NONE, "none"}, + {TAT_VERTICAL_FRAMES, "vertical_frames"}, + {0, NULL}, +}; + +struct EnumString es_BiomeTerrainType[] = +{ + {BIOME_TERRAIN_NORMAL, "normal"}, + {BIOME_TERRAIN_LIQUID, "liquid"}, + {BIOME_TERRAIN_NETHER, "nether"}, + {BIOME_TERRAIN_AETHER, "aether"}, + {BIOME_TERRAIN_FLAT, "flat"}, + {0, NULL}, +}; + /* C struct <-> Lua table converter functions */ @@ -580,52 +609,89 @@ static video::SColor readARGB8(lua_State *L, int index) return color; } -static core::aabbox3d read_aabbox3df32(lua_State *L, int index, f32 scale) +static aabb3f read_aabb3f(lua_State *L, int index, f32 scale) { - core::aabbox3d box; - if(lua_istable(L, -1)){ - lua_rawgeti(L, -1, 1); + aabb3f box; + if(lua_istable(L, index)){ + lua_rawgeti(L, index, 1); box.MinEdge.X = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 2); + lua_rawgeti(L, index, 2); box.MinEdge.Y = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 3); + lua_rawgeti(L, index, 3); box.MinEdge.Z = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 4); + lua_rawgeti(L, index, 4); box.MaxEdge.X = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 5); + lua_rawgeti(L, index, 5); box.MaxEdge.Y = lua_tonumber(L, -1) * scale; lua_pop(L, 1); - lua_rawgeti(L, -1, 6); + lua_rawgeti(L, index, 6); box.MaxEdge.Z = lua_tonumber(L, -1) * scale; lua_pop(L, 1); } return box; } -#if 0 -/* - MaterialProperties -*/ +static std::vector read_aabb3f_vector(lua_State *L, int index, f32 scale) +{ + std::vector boxes; + if(lua_istable(L, index)){ + int n = lua_objlen(L, index); + // Check if it's a single box or a list of boxes + bool possibly_single_box = (n == 6); + for(int i = 1; i <= n && possibly_single_box; i++){ + lua_rawgeti(L, index, i); + if(!lua_isnumber(L, -1)) + possibly_single_box = false; + lua_pop(L, 1); + } + if(possibly_single_box){ + // Read a single box + boxes.push_back(read_aabb3f(L, index, scale)); + } else { + // Read a list of boxes + for(int i = 1; i <= n; i++){ + lua_rawgeti(L, index, i); + boxes.push_back(read_aabb3f(L, -1, scale)); + lua_pop(L, 1); + } + } + } + return boxes; +} -static MaterialProperties read_material_properties( - lua_State *L, int table) +static NodeBox read_nodebox(lua_State *L, int index) { - MaterialProperties prop; - prop.diggability = (Diggability)getenumfield(L, -1, "diggability", - es_Diggability, DIGGABLE_NORMAL); - getfloatfield(L, -1, "constant_time", prop.constant_time); - getfloatfield(L, -1, "weight", prop.weight); - getfloatfield(L, -1, "crackiness", prop.crackiness); - getfloatfield(L, -1, "crumbliness", prop.crumbliness); - getfloatfield(L, -1, "cuttability", prop.cuttability); - getfloatfield(L, -1, "flammability", prop.flammability); - return prop; + NodeBox nodebox; + if(lua_istable(L, -1)){ + nodebox.type = (NodeBoxType)getenumfield(L, index, "type", + es_NodeBoxType, NODEBOX_REGULAR); + + lua_getfield(L, index, "fixed"); + if(lua_istable(L, -1)) + nodebox.fixed = read_aabb3f_vector(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_top"); + if(lua_istable(L, -1)) + nodebox.wall_top = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_bottom"); + if(lua_istable(L, -1)) + nodebox.wall_bottom = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_side"); + if(lua_istable(L, -1)) + nodebox.wall_side = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + } + return nodebox; } -#endif /* Groups @@ -633,6 +699,8 @@ static MaterialProperties read_material_properties( static void read_groups(lua_State *L, int index, std::map &result) { + if (!lua_istable(L,index)) + return; result.clear(); lua_pushnil(L); if(index < 0) @@ -876,10 +944,12 @@ static void read_object_properties(lua_State *L, int index, lua_getfield(L, -1, "collisionbox"); if(lua_istable(L, -1)) - prop->collisionbox = read_aabbox3df32(L, -1, 1.0); + prop->collisionbox = read_aabb3f(L, -1, 1.0); lua_pop(L, 1); getstringfield(L, -1, "visual", prop->visual); + + getstringfield(L, -1, "mesh", prop->mesh); lua_getfield(L, -1, "visual_size"); if(lua_istable(L, -1)) @@ -902,6 +972,23 @@ static 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(); + 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); + } + } + lua_pop(L, 1); lua_getfield(L, -1, "spritediv"); if(lua_istable(L, -1)) @@ -912,7 +999,7 @@ static void read_object_properties(lua_State *L, int index, if(lua_istable(L, -1)) prop->initial_sprite_basepos = read_v2s16(L, -1); lua_pop(L, 1); - + getboolfield(L, -1, "is_visible", prop->is_visible); getboolfield(L, -1, "makes_footstep_sound", prop->makes_footstep_sound); getfloatfield(L, -1, "automatic_rotate", prop->automatic_rotate); @@ -922,13 +1009,14 @@ static void read_object_properties(lua_State *L, int index, ItemDefinition */ -static ItemDefinition read_item_definition(lua_State *L, int index) +static ItemDefinition read_item_definition(lua_State *L, int index, + ItemDefinition default_def = ItemDefinition()) { if(index < 0) index = lua_gettop(L) + 1 + index; // Read the item definition - ItemDefinition def; + ItemDefinition def = default_def; def.type = (ItemType)getenumfield(L, index, "type", es_ItemType, ITEM_NONE); @@ -955,7 +1043,7 @@ static ItemDefinition read_item_definition(lua_State *L, int index) warn_if_field_exists(L, index, "tool_digging_properties", "deprecated: use tool_capabilities"); - + lua_getfield(L, index, "tool_capabilities"); if(lua_istable(L, -1)){ def.tool_capabilities = new ToolCapabilities( @@ -973,9 +1061,59 @@ static ItemDefinition read_item_definition(lua_State *L, int index) read_groups(L, -1, def.groups); lua_pop(L, 1); + // Client shall immediately place this node when player places the item. + // Server will update the precise end result a moment later. + // "" = no prediction + getstringfield(L, index, "node_placement_prediction", + def.node_placement_prediction); + return def; } +/* + TileDef +*/ + +static TileDef read_tiledef(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + TileDef tiledef; + + // key at index -2 and value at index + if(lua_isstring(L, index)){ + // "default_lava.png" + tiledef.name = lua_tostring(L, index); + } + else if(lua_istable(L, index)) + { + // {name="default_lava.png", animation={}} + tiledef.name = ""; + getstringfield(L, index, "name", tiledef.name); + getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. + tiledef.backface_culling = getboolfield_default( + L, index, "backface_culling", true); + // 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); + tiledef.animation.aspect_w = + getintfield_default(L, -1, "aspect_w", 16); + tiledef.animation.aspect_h = + getintfield_default(L, -1, "aspect_h", 16); + tiledef.animation.length = + getfloatfield_default(L, -1, "length", 1.0); + } + lua_pop(L, 1); + } + + return tiledef; +} + /* ContentFeatures */ @@ -986,6 +1124,18 @@ static ContentFeatures read_content_features(lua_State *L, int index) index = lua_gettop(L) + 1 + index; ContentFeatures f; + + /* Cache existence of some callbacks */ + lua_getfield(L, index, "on_construct"); + if(!lua_isnil(L, -1)) f.has_on_construct = true; + lua_pop(L, 1); + lua_getfield(L, index, "on_destruct"); + if(!lua_isnil(L, -1)) f.has_on_destruct = true; + lua_pop(L, 1); + lua_getfield(L, index, "after_destruct"); + if(!lua_isnil(L, -1)) f.has_after_destruct = true; + lua_pop(L, 1); + /* Name */ getstringfield(L, index, "name", f.name); @@ -1000,17 +1150,22 @@ static ContentFeatures read_content_features(lua_State *L, int index) NDT_NORMAL); getfloatfield(L, index, "visual_scale", f.visual_scale); - lua_getfield(L, index, "tile_images"); + // tiles = {} + lua_getfield(L, index, "tiles"); + // If nil, try the deprecated name "tile_images" instead + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + warn_if_field_exists(L, index, "tile_images", + "Deprecated; new name is \"tiles\"."); + lua_getfield(L, index, "tile_images"); + } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - if(lua_isstring(L, -1)) - f.tname_tiles[i] = lua_tostring(L, -1); - else - f.tname_tiles[i] = ""; + // Read tiledef from value + f.tiledef[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; @@ -1021,29 +1176,31 @@ static ContentFeatures read_content_features(lua_State *L, int index) } // Copy last value to all remaining textures if(i >= 1){ - std::string lastname = f.tname_tiles[i-1]; + TileDef lasttile = f.tiledef[i-1]; while(i < 6){ - f.tname_tiles[i] = lastname; + f.tiledef[i] = lasttile; i++; } } } lua_pop(L, 1); - lua_getfield(L, index, "special_materials"); + // special_tiles = {} + lua_getfield(L, index, "special_tiles"); + // If nil, try the deprecated name "special_materials" instead + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + warn_if_field_exists(L, index, "special_materials", + "Deprecated; new name is \"special_tiles\"."); + lua_getfield(L, index, "special_materials"); + } if(lua_istable(L, -1)){ int table = lua_gettop(L); lua_pushnil(L); int i = 0; while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - int smtable = lua_gettop(L); - std::string tname = getstringfield_default( - L, smtable, "image", ""); - bool backface_culling = getboolfield_default( - L, smtable, "backface_culling", true); - MaterialSpec mspec(tname, backface_culling); - f.mspec_special[i] = mspec; + // Read tiledef from value + f.tiledef_special[i] = read_tiledef(L, -1); // removes value, keeps key for next iteration lua_pop(L, 1); i++; @@ -1058,7 +1215,7 @@ static ContentFeatures read_content_features(lua_State *L, int index) f.alpha = getintfield_default(L, index, "alpha", 255); /* Other stuff */ - + lua_getfield(L, index, "post_effect_color"); if(!lua_isnil(L, -1)) f.post_effect_color = readARGB8(L, -1); @@ -1082,7 +1239,7 @@ static ContentFeatures read_content_features(lua_State *L, int index) "deprecated: use 'drop' field"); warn_if_field_exists(L, index, "metadata_name", "deprecated: use on_add and metadata callbacks"); - + // True for all ground-like things like stone and mud, false for eg. trees getboolfield(L, index, "is_ground_content", f.is_ground_content); f.light_propagates = (f.param_type == CPT_LIGHT); @@ -1112,44 +1269,28 @@ static ContentFeatures read_content_features(lua_State *L, int index) // the slowest possible f.liquid_viscosity = getintfield_default(L, index, "liquid_viscosity", f.liquid_viscosity); + getboolfield(L, index, "liquid_renewable", f.liquid_renewable); // Amount of light the node emits f.light_source = getintfield_default(L, index, "light_source", f.light_source); f.damage_per_second = getintfield_default(L, index, "damage_per_second", f.damage_per_second); - - lua_getfield(L, index, "selection_box"); - if(lua_istable(L, -1)){ - f.selection_box.type = (NodeBoxType)getenumfield(L, -1, "type", - es_NodeBoxType, NODEBOX_REGULAR); - - lua_getfield(L, -1, "fixed"); - if(lua_istable(L, -1)) - f.selection_box.fixed = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, -1, "wall_top"); - if(lua_istable(L, -1)) - f.selection_box.wall_top = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, -1, "wall_bottom"); - if(lua_istable(L, -1)) - f.selection_box.wall_bottom = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - lua_getfield(L, -1, "wall_side"); - if(lua_istable(L, -1)) - f.selection_box.wall_side = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); - } + lua_getfield(L, index, "node_box"); + if(lua_istable(L, -1)) + f.node_box = read_nodebox(L, -1); lua_pop(L, 1); + lua_getfield(L, index, "selection_box"); + if(lua_istable(L, -1)) + f.selection_box = read_nodebox(L, -1); + lua_pop(L, 1); + // Set to true if paramtype used to be 'facedir_simple' getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); // Set to true if wall_mounted used to be set to true getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); - + // Sound table lua_getfield(L, index, "sounds"); if(lua_istable(L, -1)){ @@ -1173,6 +1314,9 @@ static ContentFeatures read_content_features(lua_State *L, int index) */ static ItemStack read_item(lua_State *L, int index); +static std::vector read_items(lua_State *L, int index); +// creates a table of ItemStacks +static void push_items(lua_State *L, const std::vector &items); static void inventory_set_list_from_lua(Inventory *inv, const char *name, lua_State *L, int tableindex, int forcesize=-1) @@ -1185,15 +1329,7 @@ static void inventory_set_list_from_lua(Inventory *inv, const char *name, return; } // Otherwise set list - std::vector items; - luaL_checktype(L, tableindex, LUA_TTABLE); - lua_pushnil(L); - while(lua_next(L, tableindex) != 0){ - // key at index -2 and value at index -1 - items.push_back(read_item(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } + std::vector items = read_items(L, tableindex); int listsize = (forcesize != -1) ? forcesize : items.size(); InventoryList *invlist = inv->addList(name, listsize); int index = 0; @@ -1218,21 +1354,10 @@ static void inventory_get_list_to_lua(Inventory *inv, const char *name, lua_pushnil(L); return; } - // Get the table insert function - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int table_insert = lua_gettop(L); - // Create and fill table - lua_newtable(L); - int table = lua_gettop(L); - for(u32 i=0; igetSize(); i++){ - ItemStack item = invlist->getItem(i); - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); - lua_pushstring(L, item.getItemString().c_str()); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } + std::vector items; + for(u32 i=0; igetSize(); i++) + items.push_back(invlist->getItem(i)); + push_items(L, items); } /* @@ -1254,7 +1379,7 @@ private: static const luaL_reg methods[]; // Exported functions - + // garbage collector static int gc_object(lua_State *L) { @@ -1507,7 +1632,7 @@ public: { return m_stack; } - + // LuaItemStack(itemstack or itemstring or table or nil) // Creates an LuaItemStack and leaves it on top of stack static int create_object(lua_State *L) @@ -1638,6 +1763,45 @@ static ItemStack read_item(lua_State *L, int index) } } +static std::vector read_items(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + std::vector items; + luaL_checktype(L, index, LUA_TTABLE); + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + items.push_back(read_item(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return items; +} + +// creates a table of ItemStacks +static void push_items(lua_State *L, const std::vector &items) +{ + // Get the table insert function + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + // Create and fill table + lua_newtable(L); + int table = lua_gettop(L); + for(u32 i=0; igetInventory(ref->m_loc); @@ -1677,9 +1841,9 @@ private: // Inform other things that the inventory has changed get_server(L)->setInventoryModified(ref->m_loc); } - + // Exported functions - + // garbage collector static int gc_object(lua_State *L) { InvRef *o = *(InvRef **)(lua_touserdata(L, 1)); @@ -1687,6 +1851,20 @@ private: return 0; } + // is_empty(self, listname) -> true/false + static int l_is_empty(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list && list->getUsedSlots() > 0){ + lua_pushboolean(L, false); + } else { + lua_pushboolean(L, true); + } + return 1; + } + // get_size(self, listname) static int l_get_size(lua_State *L) { @@ -1701,6 +1879,20 @@ private: return 1; } + // get_width(self, listname) + static int l_get_width(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushinteger(L, list->getWidth()); + } else { + lua_pushinteger(L, 0); + } + return 1; + } + // set_size(self, listname, size) static int l_set_size(lua_State *L) { @@ -1723,6 +1915,23 @@ private: return 0; } + // set_width(self, listname, size) + static int l_set_width(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int newwidth = luaL_checknumber(L, 3); + Inventory *inv = getinv(L, ref); + InventoryList *list = inv->getList(listname); + if(list){ + list->setWidth(newwidth); + } else { + return 0; + } + reportInventoryChange(L, ref); + return 0; + } + // get_stack(self, listname, i) -> itemstack static int l_get_stack(lua_State *L) { @@ -1851,6 +2060,43 @@ private: return 1; } + // get_location() -> location (like minetest.get_inventory(location)) + static int l_get_location(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const InventoryLocation &loc = ref->m_loc; + switch(loc.type){ + case InventoryLocation::PLAYER: + lua_newtable(L); + lua_pushstring(L, "player"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, loc.name.c_str()); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::NODEMETA: + lua_newtable(L); + lua_pushstring(L, "nodemeta"); + lua_setfield(L, -2, "type"); + push_v3s16(L, loc.p); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::DETACHED: + lua_newtable(L); + lua_pushstring(L, "detached"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, loc.name.c_str()); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::UNDEFINED: + case InventoryLocation::CURRENT_PLAYER: + break; + } + lua_newtable(L); + lua_pushstring(L, "undefined"); + lua_setfield(L, -2, "type"); + return 1; + } + public: InvRef(const InventoryLocation &loc): m_loc(loc) @@ -1913,8 +2159,11 @@ public: }; const char InvRef::className[] = "InvRef"; const luaL_reg InvRef::methods[] = { + method(InvRef, is_empty), method(InvRef, get_size), method(InvRef, set_size), + method(InvRef, get_width), + method(InvRef, set_width), method(InvRef, get_stack), method(InvRef, set_stack), method(InvRef, get_list), @@ -1923,6 +2172,7 @@ const luaL_reg InvRef::methods[] = { method(InvRef, room_for_item), method(InvRef, contains_item), method(InvRef, remove_item), + method(InvRef, get_location), {0,0} }; @@ -1946,7 +2196,7 @@ private: if(!ud) luaL_typerror(L, narg, className); return *(NodeMetaRef**)ud; // unbox pointer } - + static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create) { NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p); @@ -1960,6 +2210,7 @@ private: static void reportMetadataChange(NodeMetaRef *ref) { + // NOTE: This same code is in rollback_interface.cpp // Inform other things that the metadata has changed v3s16 blockpos = getNodeBlockPos(ref->m_p); MapEditEvent event; @@ -1972,9 +2223,9 @@ private: block->raiseModified(MOD_STATE_WRITE_NEEDED, "NodeMetaRef::reportMetadataChange"); } - + // Exported functions - + // garbage collector static int gc_object(lua_State *L) { NodeMetaRef *o = *(NodeMetaRef **)(lua_touserdata(L, 1)); @@ -2088,97 +2339,90 @@ private: return 1; } - // get_inventory_draw_spec(self) - static int l_get_inventory_draw_spec(lua_State *L) + // to_table(self) + static int l_to_table(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref, false); + NodeMetadata *meta = getmeta(ref, true); if(meta == NULL){ - lua_pushlstring(L, "", 0); + lua_pushnil(L); return 1; } - std::string str = meta->getString("formspec"); - lua_pushlstring(L, str.c_str(), str.size()); - return 1; - } - - // set_inventory_draw_spec(self, text) - static int l_set_inventory_draw_spec(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - size_t len = 0; - const char *s = lua_tolstring(L, 2, &len); - std::string str(s, len); - - NodeMetadata *meta = getmeta(ref, !str.empty()); - if(meta == NULL || str == meta->getString("formspec")) - return 0; - meta->setString("formspec",str); - reportMetadataChange(ref); - return 0; - } - - // get_form_spec(self) - static int l_get_form_spec(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - - NodeMetadata *meta = getmeta(ref, false); - if(meta == NULL){ - lua_pushlstring(L, "", 0); - return 1; + lua_newtable(L); + // fields + lua_newtable(L); + { + std::map fields = meta->getStrings(); + for(std::map::const_iterator + i = fields.begin(); i != fields.end(); i++){ + const std::string &name = i->first; + const std::string &value = i->second; + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } } - std::string str = meta->getString("formspec"); - lua_pushlstring(L, str.c_str(), str.size()); + lua_setfield(L, -2, "fields"); + // inventory + lua_newtable(L); + Inventory *inv = meta->getInventory(); + if(inv){ + std::vector lists = inv->getLists(); + for(std::vector::const_iterator + i = lists.begin(); i != lists.end(); i++){ + inventory_get_list_to_lua(inv, (*i)->getName().c_str(), L); + lua_setfield(L, -2, (*i)->getName().c_str()); + } + } + lua_setfield(L, -2, "inventory"); return 1; } - // set_form_spec(self, text) - static int l_set_form_spec(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - size_t len = 0; - const char *s = lua_tolstring(L, 2, &len); - std::string str(s, len); - - NodeMetadata *meta = getmeta(ref, !str.empty()); - if(meta == NULL || str == meta->getString("formspec")) - return 0; - meta->setString("formspec",str); - reportMetadataChange(ref); - return 0; - } - - // get_infotext(self) - static int l_get_infotext(lua_State *L) + // from_table(self, table) + static int l_from_table(lua_State *L) { NodeMetaRef *ref = checkobject(L, 1); + int base = 2; - NodeMetadata *meta = getmeta(ref, false); - if(meta == NULL){ - lua_pushlstring(L, "", 0); + if(lua_isnil(L, base)){ + // No metadata + ref->m_env->getMap().removeNodeMetadata(ref->m_p); + lua_pushboolean(L, true); return 1; } - std::string str = meta->getString("infotext"); - lua_pushlstring(L, str.c_str(), str.size()); - return 1; - } - - // set_infotext(self, text) - static int l_set_infotext(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - size_t len = 0; - const char *s = lua_tolstring(L, 2, &len); - std::string str(s, len); - NodeMetadata *meta = getmeta(ref, !str.empty()); - if(meta == NULL || str == meta->getString("infotext")) - return 0; - meta->setString("infotext",str); + // Has metadata; clear old one first + ref->m_env->getMap().removeNodeMetadata(ref->m_p); + // Create new metadata + NodeMetadata *meta = getmeta(ref, true); + // Set fields + lua_getfield(L, base, "fields"); + int fieldstable = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, fieldstable) != 0){ + // key at index -2 and value at index -1 + std::string name = lua_tostring(L, -2); + size_t cl; + const char *cs = lua_tolstring(L, -1, &cl); + std::string value(cs, cl); + meta->setString(name, value); + lua_pop(L, 1); // removes value, keeps key for next iteration + } + // Set inventory + Inventory *inv = meta->getInventory(); + lua_getfield(L, base, "inventory"); + int inventorytable = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, inventorytable) != 0){ + // key at index -2 and value at index -1 + std::string name = lua_tostring(L, -2); + inventory_set_list_from_lua(inv, name.c_str(), L, -1); + lua_pop(L, 1); // removes value, keeps key for next iteration + } reportMetadataChange(ref); - return 0; + lua_pushboolean(L, true); + return 1; } public: @@ -2240,12 +2484,8 @@ const luaL_reg NodeMetaRef::methods[] = { method(NodeMetaRef, get_float), method(NodeMetaRef, set_float), method(NodeMetaRef, get_inventory), - method(NodeMetaRef, get_inventory_draw_spec), - method(NodeMetaRef, set_inventory_draw_spec), - method(NodeMetaRef, get_form_spec), - method(NodeMetaRef, set_form_spec), - method(NodeMetaRef, get_infotext), - method(NodeMetaRef, set_infotext), + method(NodeMetaRef, to_table), + method(NodeMetaRef, from_table), {0,0} }; @@ -2268,7 +2508,7 @@ public: if(!ud) luaL_typerror(L, narg, className); return *(ObjectRef**)ud; // unbox pointer } - + static ServerActiveObject* getobject(ObjectRef *ref) { ServerActiveObject *co = ref->m_object; @@ -2284,7 +2524,7 @@ private: return NULL; return (LuaEntitySAO*)obj; } - + static PlayerSAO* getplayersao(ObjectRef *ref) { ServerActiveObject *obj = getobject(ref); @@ -2294,7 +2534,7 @@ private: return NULL; return (PlayerSAO*)obj; } - + static Player* getplayer(ObjectRef *ref) { PlayerSAO *playersao = getplayersao(ref); @@ -2302,9 +2542,9 @@ private: return NULL; return playersao->getPlayer(); } - + // Exported functions - + // garbage collector static int gc_object(lua_State *L) { ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1)); @@ -2323,7 +2563,7 @@ private: co->m_removed = true; return 0; } - + // getpos(self) // returns: {x=num, y=num, z=num} static int l_getpos(lua_State *L) @@ -2341,7 +2581,7 @@ private: lua_setfield(L, -2, "z"); return 1; } - + // setpos(self, pos) static int l_setpos(lua_State *L) { @@ -2355,7 +2595,7 @@ private: co->setPos(pos); return 0; } - + // moveto(self, pos, continuous=false) static int l_moveto(lua_State *L) { @@ -2372,7 +2612,7 @@ private: return 0; } - // punch(self, puncher, tool_capabilities, direction, time_from_last_punch) + // punch(self, puncher, time_from_last_punch, tool_capabilities, dir) static int l_punch(lua_State *L) { ObjectRef *ref = checkobject(L, 1); @@ -2381,13 +2621,18 @@ private: ServerActiveObject *puncher = getobject(puncher_ref); if(co == NULL) return 0; if(puncher == NULL) return 0; - ToolCapabilities toolcap = read_tool_capabilities(L, 3); - v3f dir = read_v3f(L, 4); + v3f dir; + 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, 5)) - time_from_last_punch = lua_tonumber(L, 5); + if(lua_isnumber(L, 3)) + time_from_last_punch = lua_tonumber(L, 3); + ToolCapabilities toolcap = read_tool_capabilities(L, 4); + dir.normalize(); // Do it - puncher->punch(dir, &toolcap, puncher, time_from_last_punch); + co->punch(dir, &toolcap, puncher, time_from_last_punch); return 0; } @@ -2430,7 +2675,11 @@ private: { ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if(co == NULL){ + // Default hp is 1 + lua_pushnumber(L, 1); + return 1; + } int hp = co->getHP(); /*infostream<<"ObjectRef::l_get_hp(): id="<getId() <<" hp="<getInventory(loc) != NULL) InvRef::create(L, loc); else - lua_pushnil(L); + lua_pushnil(L); // An object may have no inventory (nil) return 1; } @@ -2481,7 +2730,11 @@ private: { ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; + if(co == NULL){ + // Empty ItemStack + LuaItemStack::create(L, ItemStack()); + return 1; + } // Do it LuaItemStack::create(L, co->getWieldedItem()); return 1; @@ -2513,6 +2766,80 @@ private: return 0; } + // set_animation(self, frame_range, frame_speed, frame_blend) + static int l_set_animation(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + v2f frames = v2f(1, 1); + if(!lua_isnil(L, 2)) + frames = read_v2f(L, 2); + float frame_speed = 15; + if(!lua_isnil(L, 3)) + frame_speed = lua_tonumber(L, 3); + float frame_blend = 0; + if(!lua_isnil(L, 4)) + frame_blend = lua_tonumber(L, 4); + co->setAnimation(frames, frame_speed, frame_blend); + return 0; + } + + // set_bone_position(self, std::string bone, v3f position, v3f rotation) + static int l_set_bone_position(lua_State *L) + { + 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); + if(!lua_isnil(L, 3)) + position = read_v3f(L, 3); + v3f rotation = v3f(0, 0, 0); + if(!lua_isnil(L, 4)) + rotation = read_v3f(L, 4); + co->setBonePosition(bone, position, rotation); + return 0; + } + + // set_attach(self, parent, bone, position, rotation) + static int l_set_attach(lua_State *L) + { + 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; + // Do it + 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)) + rotation = read_v3f(L, 5); + co->setAttachment(parent->getId(), bone, position, rotation); + return 0; + } + + // set_detach(self) + static int l_set_detach(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0)); + return 0; + } + // set_properties(self, properties) static int l_set_properties(lua_State *L) { @@ -2540,7 +2867,7 @@ private: co->setVelocity(pos); return 0; } - + // getvelocity(self) static int l_getvelocity(lua_State *L) { @@ -2552,7 +2879,7 @@ private: pushFloatPos(L, v); return 1; } - + // setacceleration(self, {x=num, y=num, z=num}) static int l_setacceleration(lua_State *L) { @@ -2565,7 +2892,7 @@ private: co->setAcceleration(pos); return 0; } - + // getacceleration(self) static int l_getacceleration(lua_State *L) { @@ -2577,7 +2904,7 @@ private: pushFloatPos(L, v); return 1; } - + // setyaw(self, radians) static int l_setyaw(lua_State *L) { @@ -2589,7 +2916,7 @@ private: co->setYaw(yaw); return 0; } - + // getyaw(self) static int l_getyaw(lua_State *L) { @@ -2601,7 +2928,7 @@ private: lua_pushnumber(L, yaw); return 1; } - + // settexturemod(self, mod) static int l_settexturemod(lua_State *L) { @@ -2613,7 +2940,7 @@ private: co->setTextureMod(mod); return 0; } - + // setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, // select_horiz_by_yawpitch=false) static int l_setsprite(lua_State *L) @@ -2650,7 +2977,7 @@ private: lua_pushstring(L, name.c_str()); return 1; } - + // get_luaentity(self) static int l_get_luaentity(lua_State *L) { @@ -2661,23 +2988,32 @@ private: luaentity_get(L, co->getId()); return 1; } - + /* Player-only */ - + + // is_player(self) + static int l_is_player(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + lua_pushboolean(L, (player != NULL)); + return 1; + } + // get_player_name(self) static int l_get_player_name(lua_State *L) { ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL){ - lua_pushnil(L); + lua_pushlstring(L, "", 0); return 1; } // Do it lua_pushstring(L, player->getName()); return 1; } - + // get_look_dir(self) static int l_get_look_dir(lua_State *L) { @@ -2714,6 +3050,79 @@ private: return 1; } + // set_inventory_formspec(self, formspec) + static int l_set_inventory_formspec(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL) return 0; + std::string formspec = luaL_checkstring(L, 2); + + player->inventory_formspec = formspec; + get_server(L)->reportInventoryFormspecModified(player->getName()); + lua_pushboolean(L, true); + return 1; + } + + // get_inventory_formspec(self) -> formspec + static int l_get_inventory_formspec(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL) return 0; + + std::string formspec = player->inventory_formspec; + lua_pushlstring(L, formspec.c_str(), formspec.size()); + return 1; + } + + // get_player_control(self) + static int l_get_player_control(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL){ + lua_pushlstring(L, "", 0); + return 1; + } + // Do it + PlayerControl control = player->getPlayerControl(); + lua_newtable(L); + lua_pushboolean(L, control.up); + lua_setfield(L, -2, "up"); + lua_pushboolean(L, control.down); + lua_setfield(L, -2, "down"); + lua_pushboolean(L, control.left); + lua_setfield(L, -2, "left"); + lua_pushboolean(L, control.right); + lua_setfield(L, -2, "right"); + lua_pushboolean(L, control.jump); + lua_setfield(L, -2, "jump"); + lua_pushboolean(L, control.aux1); + lua_setfield(L, -2, "aux1"); + lua_pushboolean(L, control.sneak); + lua_setfield(L, -2, "sneak"); + lua_pushboolean(L, control.LMB); + lua_setfield(L, -2, "LMB"); + lua_pushboolean(L, control.RMB); + lua_setfield(L, -2, "RMB"); + return 1; + } + + // get_player_control_bits(self) + static int l_get_player_control_bits(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL){ + lua_pushlstring(L, "", 0); + return 1; + } + // Do it + lua_pushnumber(L, player->keyPressed); + return 1; + } + public: ObjectRef(ServerActiveObject *object): m_object(object) @@ -2746,7 +3155,7 @@ public: ObjectRef *o = checkobject(L, -1); o->m_object = NULL; } - + static void Register(lua_State *L) { lua_newtable(L); @@ -2792,6 +3201,10 @@ const luaL_reg ObjectRef::methods[] = { method(ObjectRef, get_wielded_item), method(ObjectRef, set_wielded_item), method(ObjectRef, set_armor_groups), + method(ObjectRef, set_animation), + method(ObjectRef, set_bone_position), + method(ObjectRef, set_attach), + method(ObjectRef, set_detach), method(ObjectRef, set_properties), // LuaEntitySAO-only method(ObjectRef, setvelocity), @@ -2805,18 +3218,23 @@ const luaL_reg ObjectRef::methods[] = { method(ObjectRef, get_entity_name), method(ObjectRef, get_luaentity), // Player-only + method(ObjectRef, is_player), method(ObjectRef, get_player_name), method(ObjectRef, get_look_dir), method(ObjectRef, get_look_pitch), method(ObjectRef, get_look_yaw), + method(ObjectRef, set_inventory_formspec), + method(ObjectRef, get_inventory_formspec), + method(ObjectRef, get_player_control), + method(ObjectRef, get_player_control_bits), {0,0} }; -// Creates a new anonymous reference if id=0 +// Creates a new anonymous reference if cobj=NULL or id=0 static void objectref_get_or_create(lua_State *L, ServerActiveObject *cobj) { - if(cobj->getId() == 0){ + if(cobj == NULL || cobj->getId() == 0){ ObjectRef::create(L, cobj); } else { objectref_get(L, cobj->getId()); @@ -2833,8 +3251,8 @@ class LuaPerlinNoise private: int seed; int octaves; - double persistence; - double scale; + float persistence; + float scale; static const char className[]; static const luaL_reg methods[]; @@ -2866,8 +3284,8 @@ private: } public: - LuaPerlinNoise(int a_seed, int a_octaves, double a_persistence, - double a_scale): + LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence, + float a_scale): seed(a_seed), octaves(a_octaves), persistence(a_persistence), @@ -2885,8 +3303,8 @@ public: { int seed = luaL_checkint(L, 1); int octaves = luaL_checkint(L, 2); - double persistence = luaL_checknumber(L, 3); - double scale = luaL_checknumber(L, 4); + float persistence = luaL_checknumber(L, 3); + float scale = luaL_checknumber(L, 4); LuaPerlinNoise *o = new LuaPerlinNoise(seed, octaves, persistence, scale); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); @@ -2938,107 +3356,248 @@ const luaL_reg LuaPerlinNoise::methods[] = { }; /* - EnvRef + NodeTimerRef */ -class EnvRef +class NodeTimerRef { private: + v3s16 m_p; ServerEnvironment *m_env; static const char className[]; static const luaL_reg methods[]; static int gc_object(lua_State *L) { - EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1)); + NodeTimerRef *o = *(NodeTimerRef **)(lua_touserdata(L, 1)); delete o; return 0; } - static EnvRef *checkobject(lua_State *L, int narg) + static NodeTimerRef *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 *(EnvRef**)ud; // unbox pointer + return *(NodeTimerRef**)ud; // unbox pointer } - - // Exported functions - // EnvRef:set_node(pos, node) - // pos = {x=num, y=num, z=num} - static int l_set_node(lua_State *L) + static int l_set(lua_State *L) { - //infostream<<"EnvRef::l_set_node()"<m_env; if(env == NULL) return 0; - // pos - v3s16 pos = read_v3s16(L, 2); - // content - MapNode n = readnode(L, 3, env->getGameDef()->ndef()); - // Do it - // Call destructor - MapNode n_old = env->getMap().getNodeNoEx(pos); - scriptapi_node_on_destruct(L, pos, n_old); - // Replace node - bool succeeded = env->getMap().addNodeWithEvent(pos, n); - lua_pushboolean(L, succeeded); - // Call constructor - if(succeeded) - scriptapi_node_on_construct(L, pos, n); - return 1; + f32 t = luaL_checknumber(L,2); + f32 e = luaL_checknumber(L,3); + env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e)); + return 0; } - static int l_add_node(lua_State *L) + static int l_start(lua_State *L) { - return l_set_node(L); + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + f32 t = luaL_checknumber(L,2); + env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0)); + return 0; } - // EnvRef:remove_node(pos) - // pos = {x=num, y=num, z=num} - static int l_remove_node(lua_State *L) + static int l_stop(lua_State *L) { - //infostream<<"EnvRef::l_remove_node()"<m_env; if(env == NULL) return 0; - // pos - v3s16 pos = read_v3s16(L, 2); - // Do it - // Call destructor - MapNode n = env->getMap().getNodeNoEx(pos); - scriptapi_node_on_destruct(L, pos, n); - // Replace with air - bool succeeded = env->getMap().removeNodeWithEvent(pos); - lua_pushboolean(L, succeeded); - // Air doesn't require constructor + env->getMap().removeNodeTimer(o->m_p); + return 0; + } + + static int l_is_started(lua_State *L) + { + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushboolean(L,(t.timeout != 0)); return 1; } - // EnvRef:get_node(pos) - // pos = {x=num, y=num, z=num} - static int l_get_node(lua_State *L) + static int l_get_timeout(lua_State *L) { - //infostream<<"EnvRef::l_get_node()"<m_env; if(env == NULL) return 0; - // pos - v3s16 pos = read_v3s16(L, 2); - // Do it - MapNode n = env->getMap().getNodeNoEx(pos); - // Return node - pushnode(L, n, env->getGameDef()->ndef()); + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushnumber(L,t.timeout); return 1; } - // EnvRef:get_node_or_nil(pos) - // pos = {x=num, y=num, z=num} - static int l_get_node_or_nil(lua_State *L) + static int l_get_elapsed(lua_State *L) { - //infostream<<"EnvRef::l_get_node()"<m_env; + if(env == NULL) return 0; + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushnumber(L,t.elapsed); + return 1; + } + +public: + NodeTimerRef(v3s16 p, ServerEnvironment *env): + m_p(p), + m_env(env) + { + } + + ~NodeTimerRef() + { + } + + // Creates an NodeTimerRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, v3s16 p, ServerEnvironment *env) + { + NodeTimerRef *o = new NodeTimerRef(p, env); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + } + + static void set_null(lua_State *L) + { + NodeTimerRef *o = checkobject(L, -1); + o->m_env = NULL; + } + + static void 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 + + // Cannot be created from Lua + //lua_register(L, className, create_object); + } +}; +const char NodeTimerRef::className[] = "NodeTimerRef"; +const luaL_reg NodeTimerRef::methods[] = { + method(NodeTimerRef, start), + method(NodeTimerRef, set), + method(NodeTimerRef, stop), + method(NodeTimerRef, is_started), + method(NodeTimerRef, get_timeout), + method(NodeTimerRef, get_elapsed), + {0,0} +}; + +/* + EnvRef +*/ + +class EnvRef +{ +private: + ServerEnvironment *m_env; + + static const char className[]; + static const luaL_reg methods[]; + + static int gc_object(lua_State *L) { + EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1)); + delete o; + return 0; + } + + static EnvRef *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 *(EnvRef**)ud; // unbox pointer + } + + // Exported functions + + // EnvRef:set_node(pos, node) + // pos = {x=num, y=num, z=num} + static int l_set_node(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + INodeDefManager *ndef = env->getGameDef()->ndef(); + // parameters + v3s16 pos = read_v3s16(L, 2); + MapNode n = readnode(L, 3, ndef); + // Do it + bool succeeded = env->setNode(pos, n); + lua_pushboolean(L, succeeded); + return 1; + } + + static int l_add_node(lua_State *L) + { + return l_set_node(L); + } + + // EnvRef:remove_node(pos) + // pos = {x=num, y=num, z=num} + static int l_remove_node(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + INodeDefManager *ndef = env->getGameDef()->ndef(); + // parameters + v3s16 pos = read_v3s16(L, 2); + // Do it + bool succeeded = env->removeNode(pos); + lua_pushboolean(L, succeeded); + return 1; + } + + // EnvRef:get_node(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // pos + v3s16 pos = read_v3s16(L, 2); + // Do it + MapNode n = env->getMap().getNodeNoEx(pos); + // Return node + pushnode(L, n, env->getGameDef()->ndef()); + return 1; + } + + // EnvRef:get_node_or_nil(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node_or_nil(lua_State *L) + { + EnvRef *o = checkobject(L, 1); ServerEnvironment *env = o->m_env; if(env == NULL) return 0; // pos @@ -3070,7 +3629,7 @@ private: if(lua_isnumber(L, 3)) time_of_day = 24000.0 * lua_tonumber(L, 3); time_of_day %= 24000; - u32 dnr = time_to_daynight_ratio(time_of_day); + u32 dnr = time_to_daynight_ratio(time_of_day, true); MapNode n = env->getMap().getNodeNoEx(pos); try{ MapNode n = env->getMap().getNode(pos); @@ -3084,6 +3643,107 @@ private: } } + // EnvRef:place_node(pos, node) + // pos = {x=num, y=num, z=num} + static int l_place_node(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + v3s16 pos = read_v3s16(L, 2); + MapNode n = readnode(L, 3, env->getGameDef()->ndef()); + + // Don't attempt to load non-loaded area as of now + MapNode n_old = env->getMap().getNodeNoEx(pos); + if(n_old.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Create item to place + INodeDefManager *ndef = get_server(L)->ndef(); + IItemDefManager *idef = get_server(L)->idef(); + ItemStack item(ndef->get(n).name, 1, 0, "", idef); + // Make pointed position + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos + v3s16(0,-1,0); + // Place it with a NULL placer (appears in Lua as a non-functional + // ObjectRef) + bool success = scriptapi_item_on_place(L, item, NULL, pointed); + lua_pushboolean(L, success); + return 1; + } + + // EnvRef:dig_node(pos) + // pos = {x=num, y=num, z=num} + static int l_dig_node(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + v3s16 pos = read_v3s16(L, 2); + + // Don't attempt to load non-loaded area as of now + MapNode n = env->getMap().getNodeNoEx(pos); + if(n.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Dig it out with a NULL digger (appears in Lua as a + // non-functional ObjectRef) + bool success = scriptapi_node_on_dig(L, pos, n, NULL); + lua_pushboolean(L, success); + return 1; + } + + // EnvRef:punch_node(pos) + // pos = {x=num, y=num, z=num} + static int l_punch_node(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + v3s16 pos = read_v3s16(L, 2); + + // Don't attempt to load non-loaded area as of now + MapNode n = env->getMap().getNodeNoEx(pos); + if(n.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Punch it with a NULL puncher (appears in Lua as a non-functional + // ObjectRef) + bool success = scriptapi_node_on_punch(L, pos, n, NULL); + lua_pushboolean(L, success); + return 1; + } + + // EnvRef:get_meta(pos) + static int l_get_meta(lua_State *L) + { + //infostream<<"EnvRef::l_get_meta()"<m_env; + if(env == NULL) return 0; + // Do it + v3s16 p = read_v3s16(L, 2); + NodeMetaRef::create(L, p, env); + return 1; + } + + // EnvRef:get_node_timer(pos) + static int l_get_node_timer(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + // Do it + v3s16 p = read_v3s16(L, 2); + NodeTimerRef::create(L, p, env); + return 1; + } + // EnvRef:add_entity(pos, entityname) -> ObjectRef or nil // pos = {x=num, y=num, z=num} static int l_add_entity(lua_State *L) @@ -3164,19 +3824,6 @@ private: return 0; } - // EnvRef:get_meta(pos) - static int l_get_meta(lua_State *L) - { - //infostream<<"EnvRef::l_get_meta()"<m_env; - if(env == NULL) return 0; - // Do it - v3s16 p = read_v3s16(L, 2); - NodeMetaRef::create(L, p, env); - return 1; - } - // EnvRef:get_player_by_name(name) static int l_get_player_by_name(lua_State *L) { @@ -3333,7 +3980,7 @@ private: lua_getglobal(L, "table"); lua_getfield(L, -1, "insert"); int table_insert = lua_gettop(L); - + lua_newtable(L); int table = lua_gettop(L); for(s16 x=minp.X; x<=maxp.X; x++) @@ -3363,8 +4010,8 @@ private: int seeddiff = luaL_checkint(L, 2); int octaves = luaL_checkint(L, 3); - double persistence = luaL_checknumber(L, 4); - double scale = luaL_checknumber(L, 5); + float persistence = luaL_checknumber(L, 4); + float scale = luaL_checknumber(L, 5); LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale); *(void **)(lua_newuserdata(L, sizeof(void *))) = n; @@ -3373,6 +4020,63 @@ private: return 1; } + // EnvRef:clear_objects() + // clear all objects in the environment + static int l_clear_objects(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + o->m_env->clearAllObjects(); + return 0; + } + + static int l_spawn_tree(lua_State *L) + { + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + v3s16 p0 = read_v3s16(L, 2); + + treegen::TreeDef tree_def; + std::string trunk,leaves,fruit; + INodeDefManager *ndef = env->getGameDef()->ndef(); + + if(lua_istable(L, 3)) + { + getstringfield(L, 3, "axiom", tree_def.initial_axiom); + getstringfield(L, 3, "rules_a", tree_def.rules_a); + getstringfield(L, 3, "rules_b", tree_def.rules_b); + getstringfield(L, 3, "rules_c", tree_def.rules_c); + getstringfield(L, 3, "rules_d", tree_def.rules_d); + getstringfield(L, 3, "trunk", trunk); + tree_def.trunknode=ndef->getId(trunk); + getstringfield(L, 3, "leaves", leaves); + tree_def.leavesnode=ndef->getId(leaves); + tree_def.leaves2_chance=0; + getstringfield(L, 3, "leaves2", leaves); + if (leaves !="") + { + tree_def.leaves2node=ndef->getId(leaves); + getintfield(L, 3, "leaves2_chance", tree_def.leaves2_chance); + } + getintfield(L, 3, "angle", tree_def.angle); + getintfield(L, 3, "iterations", tree_def.iterations); + getintfield(L, 3, "random_level", tree_def.iterations_random_level); + getstringfield(L, 3, "trunk_type", tree_def.trunk_type); + getboolfield(L, 3, "thin_branches", tree_def.thin_branches); + tree_def.fruit_chance=0; + getstringfield(L, 3, "fruit", fruit); + if (fruit != "") + { + tree_def.fruitnode=ndef->getId(fruit); + getintfield(L, 3, "fruit_chance",tree_def.fruit_chance); + } + } + else + return 0; + treegen::spawn_ltree (env, p0, ndef, tree_def); + return 1; + } + public: EnvRef(ServerEnvironment *env): m_env(env) @@ -3401,7 +4105,7 @@ public: EnvRef *o = checkobject(L, -1); o->m_env = NULL; } - + static void Register(lua_State *L) { lua_newtable(L); @@ -3438,11 +4142,15 @@ const luaL_reg EnvRef::methods[] = { method(EnvRef, get_node), method(EnvRef, get_node_or_nil), method(EnvRef, get_node_light), + method(EnvRef, place_node), + method(EnvRef, dig_node), + method(EnvRef, punch_node), method(EnvRef, add_entity), method(EnvRef, add_item), method(EnvRef, add_rat), method(EnvRef, add_firefly), method(EnvRef, get_meta), + method(EnvRef, get_node_timer), method(EnvRef, get_player_by_name), method(EnvRef, get_objects_inside_radius), method(EnvRef, set_timeofday), @@ -3450,6 +4158,10 @@ const luaL_reg EnvRef::methods[] = { method(EnvRef, find_node_near), method(EnvRef, find_nodes_in_area), method(EnvRef, get_perlin), + //method{EnvRef, get_perlin_map_2d}, + //method{EnvRef, get_perlin_map_3d}, + method(EnvRef, clear_objects), + method(EnvRef, spawn_tree), {0,0} }; @@ -3467,7 +4179,7 @@ private: static const luaL_reg methods[]; // Exported functions - + // garbage collector static int gc_object(lua_State *L) { @@ -3487,6 +4199,10 @@ private: min = luaL_checkinteger(L, 2); if(!lua_isnil(L, 3)) max = luaL_checkinteger(L, 3); + if(max < min){ + errorstream<<"PseudoRandom.next(): max="< 32767/5) throw LuaError(L, "PseudoRandom.next() max-min is not 32767 and is > 32768/5. This is disallowed due to the bad random distribution the implementation would otherwise make."); PseudoRandom &pseudo = o->m_pseudo; @@ -3514,7 +4230,7 @@ public: { return m_pseudo; } - + // LuaPseudoRandom(seed) // Creates an LuaPseudoRandom and leaves it on top of stack static int create_object(lua_State *L) @@ -3618,7 +4334,7 @@ public: u32 active_object_count, u32 active_object_count_wider) { lua_State *L = m_lua; - + realitycheck(L); assert(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); @@ -3634,7 +4350,7 @@ public: lua_gettable(L, registered_abms); if(lua_isnil(L, -1)) assert(0); - + // Call action luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, "action"); @@ -3725,10 +4441,102 @@ static int l_log(lua_State *L) return 0; } -// register_item_raw({lots of stuff}) -static int l_register_item_raw(lua_State *L) +// request_shutdown() +static int l_request_shutdown(lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); + get_server(L)->requestShutdown(); + return 0; +} + +// get_server_status() +static int l_get_server_status(lua_State *L) +{ + lua_pushstring(L, wide_to_narrow(get_server(L)->getStatusString()).c_str()); + return 1; +} + +// register_biome_groups({frequencies}) +static int l_register_biome_groups(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + int index = 1; + if (!lua_istable(L, index)) + throw LuaError(L, "register_biome_groups: parameter is not a table"); + + BiomeDefManager *bmgr = get_server(L)->getBiomeDef(); + if (!bmgr) { + verbosestream << "register_biome_groups: BiomeDefManager not active" << std::endl; + return 0; + } + + lua_pushnil(L); + for (int i = 1; lua_next(L, index) != 0; i++) { + bmgr->addBiomeGroup(lua_tonumber(L, -1)); + lua_pop(L, 1); + } + lua_pop(L, 1); + + return 0; +} + +// register_biome({lots of stuff}) +static int l_register_biome(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + int index = 1, groupid; + std::string nodename; + + IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager(); + BiomeDefManager *bmgr = get_server(L)->getBiomeDef(); + if (!bmgr) { + verbosestream << "register_biome: BiomeDefManager not active" << std::endl; + return 0; + } + + groupid = getintfield_default(L, index, "group_id", 0); + + enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index, + "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL); + Biome *b = bmgr->createBiome(terrain); + + b->name = getstringfield_default(L, index, "name", ""); + + if (getstringfield(L, index, "node_top", nodename)) + b->n_top = MapNode(ndef->getId(nodename)); + else + b->n_top = MapNode(CONTENT_IGNORE); + + if (getstringfield(L, index, "node_filler", nodename)) + b->n_filler = MapNode(ndef->getId(nodename)); + else + b->n_filler = b->n_top; + + b->ntopnodes = getintfield_default(L, index, "num_top_nodes", 0); + + b->height_min = getintfield_default(L, index, "height_min", 0); + b->height_max = getintfield_default(L, index, "height_max", 0); + b->heat_min = getfloatfield_default(L, index, "heat_min", 0.); + b->heat_max = getfloatfield_default(L, index, "heat_max", 0.); + b->humidity_min = getfloatfield_default(L, index, "humidity_min", 0.); + b->humidity_max = getfloatfield_default(L, index, "humidity_max", 0.); + + b->np = new NoiseParams; // should read an entire NoiseParams later on... + getfloatfield(L, index, "scale", b->np->scale); + getfloatfield(L, index, "offset", b->np->offset); + + b->groupid = (s8)groupid; + b->flags = 0; //reserved + + bmgr->addBiome(b); + + verbosestream << "register_biome: " << b->name << std::endl; + return 0; +} + +// register_item_raw({lots of stuff}) +static int l_register_item_raw(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); int table = 1; // Get the writable item and node definition managers from the server @@ -3738,9 +4546,10 @@ static int l_register_item_raw(lua_State *L) get_server(L)->getWritableNodeDefManager(); // Check if name is defined + std::string name; lua_getfield(L, table, "name"); if(lua_isstring(L, -1)){ - std::string name = lua_tostring(L, -1); + name = lua_tostring(L, -1); verbosestream<<"register_item_raw: "<registerItem(def); // Read the node definition (content features) and register it @@ -3771,9 +4595,9 @@ static int l_register_alias_raw(lua_State *L) // Get the writable item definition manager from the server IWritableItemDefManager *idef = get_server(L)->getWritableItemDefManager(); - + idef->registerAlias(name, convert_to); - + return 0; /* number of results */ } @@ -3882,7 +4706,7 @@ static int l_register_craft(lua_State *L) // Get the writable craft definition manager from the server IWritableCraftDefManager *craftdef = get_server(L)->getWritableCraftDefManager(); - + std::string type = getstringfield_default(L, table, "type", "shaped"); /* @@ -3976,8 +4800,17 @@ static int l_register_craft(lua_State *L) float cooktime = getfloatfield_default(L, table, "cooktime", 3.0); + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (cooking output=\"" + output + "\")"); + } + CraftDefinition *def = new CraftDefinitionCooking( - output, recipe, cooktime); + output, recipe, cooktime, replacements); craftdef->registerCraft(def); } /* @@ -3991,8 +4824,17 @@ static int l_register_craft(lua_State *L) float burntime = getfloatfield_default(L, table, "burntime", 1.0); + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (fuel recipe=\"" + recipe + "\")"); + } + CraftDefinition *def = new CraftDefinitionFuel( - recipe, burntime); + recipe, burntime, replacements); craftdef->registerCraft(def); } else @@ -4081,6 +4923,56 @@ static int l_get_player_privs(lua_State *L) return 1; } +// get_ban_list() +static int l_get_ban_list(lua_State *L) +{ + lua_pushstring(L, get_server(L)->getBanDescription("").c_str()); + return 1; +} + +// get_ban_description() +static int l_get_ban_description(lua_State *L) +{ + const char * ip_or_name = luaL_checkstring(L, 1); + lua_pushstring(L, get_server(L)->getBanDescription(std::string(ip_or_name)).c_str()); + return 1; +} + +// ban_player() +static int l_ban_player(lua_State *L) +{ + const char * name = luaL_checkstring(L, 1); + Player *player = get_env(L)->getPlayer(name); + if(player == NULL) + { + lua_pushboolean(L, false); // no such player + return 1; + } + try + { + Address addr = get_server(L)->getPeerAddress(get_env(L)->getPlayer(name)->peer_id); + std::string ip_str = addr.serializeString(); + get_server(L)->setIpBanned(ip_str, name); + } + catch(con::PeerNotFoundException) // unlikely + { + dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushboolean(L, false); // error + return 1; + } + lua_pushboolean(L, true); + return 1; +} + +// unban_player_or_ip() +static int l_unban_player_of_ip(lua_State *L) +{ + const char * ip_or_name = luaL_checkstring(L, 1); + get_server(L)->unsetIpBanned(ip_or_name); + lua_pushboolean(L, true); + return 1; +} + // get_inventory(location) static int l_get_inventory(lua_State *L) { @@ -4094,8 +4986,11 @@ static int l_get_inventory(lua_State *L) lua_getfield(L, 1, "pos"); v3s16 pos = check_v3s16(L, -1); loc.setNodeMeta(pos); + } else if(type == "detached"){ + std::string name = checkstringfield(L, 1, "name"); + loc.setDetached(name); } - + if(get_server(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else @@ -4103,6 +4998,36 @@ static int l_get_inventory(lua_State *L) return 1; } +// create_detached_inventory_raw(name) +static int l_create_detached_inventory_raw(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + if(get_server(L)->createDetachedInventory(name) != NULL){ + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + }else{ + lua_pushnil(L); + } + return 1; +} + +// show_formspec(playername,formname,formspec) +static int l_show_formspec(lua_State *L) +{ + const char *playername = luaL_checkstring(L, 1); + const char *formname = luaL_checkstring(L, 2); + const char *formspec = luaL_checkstring(L, 3); + + if(get_server(L)->showFormspec(playername,formspec,formname)) + { + lua_pushboolean(L, true); + }else{ + lua_pushboolean(L, false); + } + return 1; +} + // get_dig_params(groups, tool_capabilities[, time_from_last_punch]) static int l_get_dig_params(lua_State *L) { @@ -4157,6 +5082,60 @@ static int l_get_modpath(lua_State *L) return 1; } +// get_modnames() +// the returned list is sorted alphabetically for you +static int l_get_modnames(lua_State *L) +{ + // Get a list of mods + core::list mods_unsorted, mods_sorted; + get_server(L)->getModNames(mods_unsorted); + + // 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(core::list::Iterator i = mods_unsorted.begin(); + i != mods_unsorted.end(); i++) + { + bool added = false; + for(core::list::Iterator x = mods_sorted.begin(); + x != mods_unsorted.end(); x++) + { + // I doubt anybody using Minetest will be using + // anything not ASCII based :) + if((*i).compare(*x) <= 0) + { + mods_sorted.insert_before(x, *i); + added = true; + break; + } + } + if(!added) + mods_sorted.push_back(*i); + } + + // Get the table insertion function from Lua. + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int insertion_func = lua_gettop(L); + + // Package them up for Lua + lua_newtable(L); + int new_table = lua_gettop(L); + core::list::Iterator i = mods_sorted.begin(); + while(i != mods_sorted.end()) + { + lua_pushvalue(L, insertion_func); + lua_pushvalue(L, new_table); + lua_pushstring(L, (*i).c_str()); + if(lua_pcall(L, 2, 0, 0) != 0) + { + script_error(L, "error: %s", lua_tostring(L, -1)); + } + i++; + } + return 1; +} + // get_worldpath() static int l_get_worldpath(lua_State *L) { @@ -4213,29 +5192,189 @@ static int l_notify_authentication_modified(lua_State *L) return 0; } +// get_craft_result(input) +static int l_get_craft_result(lua_State *L) +{ + int input_i = 1; + std::string method_s = getstringfield_default(L, input_i, "method", "normal"); + enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method", + es_CraftMethod, CRAFT_METHOD_NORMAL); + int width = 1; + lua_getfield(L, input_i, "width"); + if(lua_isnumber(L, -1)) + width = luaL_checkinteger(L, -1); + lua_pop(L, 1); + lua_getfield(L, input_i, "items"); + std::vector items = read_items(L, -1); + lua_pop(L, 1); // items + + IGameDef *gdef = get_server(L); + ICraftDefManager *cdef = gdef->cdef(); + CraftInput input(method, width, items); + CraftOutput output; + bool got = cdef->getCraftResult(input, output, true, gdef); + lua_newtable(L); // output table + 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); + } else { + LuaItemStack::create(L, ItemStack()); + lua_setfield(L, -2, "item"); + setintfield(L, -1, "time", 0); + } + lua_newtable(L); // decremented input table + lua_pushstring(L, method_s.c_str()); + lua_setfield(L, -2, "method"); + lua_pushinteger(L, width); + lua_setfield(L, -2, "width"); + push_items(L, input.items); + lua_setfield(L, -2, "items"); + return 2; +} + +// get_craft_recipe(result item) +static int l_get_craft_recipe(lua_State *L) +{ + int k = 0; + char tmp[20]; + int input_i = 1; + std::string o_item = luaL_checkstring(L,input_i); + + IGameDef *gdef = get_server(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::const_iterator + i = input.items.begin(); + i != input.items.end(); i++, k++) + { + if (i->empty()) + { + continue; + } + sprintf(tmp,"%d",k); + lua_pushstring(L,tmp); + 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_pushnil(L); + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", 0); + } + return 1; +} + +// rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds +static int l_rollback_get_last_node_actor(lua_State *L) +{ + v3s16 p = read_v3s16(L, 1); + int range = luaL_checknumber(L, 2); + int seconds = luaL_checknumber(L, 3); + Server *server = get_server(L); + IRollbackManager *rollback = server->getRollbackManager(); + v3s16 act_p; + int act_seconds = 0; + std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds); + lua_pushstring(L, actor.c_str()); + push_v3s16(L, act_p); + lua_pushnumber(L, act_seconds); + return 3; +} + +// rollback_revert_actions_by(actor, seconds) -> bool, log messages +static int l_rollback_revert_actions_by(lua_State *L) +{ + std::string actor = luaL_checkstring(L, 1); + int seconds = luaL_checknumber(L, 2); + Server *server = get_server(L); + IRollbackManager *rollback = server->getRollbackManager(); + std::list actions = rollback->getRevertActions(actor, seconds); + std::list log; + bool success = server->rollbackRevertActions(actions, &log); + // Push boolean result + lua_pushboolean(L, success); + // Get the table insert function and push the log table + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + lua_newtable(L); + int table = lua_gettop(L); + for(std::list::const_iterator i = log.begin(); + i != log.end(); i++) + { + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + lua_pushstring(L, i->c_str()); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + lua_remove(L, -2); // Remove table + lua_remove(L, -2); // Remove insert + return 2; +} + static const struct luaL_Reg minetest_f [] = { {"debug", l_debug}, {"log", l_log}, + {"request_shutdown", l_request_shutdown}, + {"get_server_status", l_get_server_status}, {"register_item_raw", l_register_item_raw}, {"register_alias_raw", l_register_alias_raw}, {"register_craft", l_register_craft}, + {"register_biome", l_register_biome}, + {"register_biome_groups", l_register_biome_groups}, {"setting_set", l_setting_set}, {"setting_get", l_setting_get}, {"setting_getbool", l_setting_getbool}, {"chat_send_all", l_chat_send_all}, {"chat_send_player", l_chat_send_player}, {"get_player_privs", l_get_player_privs}, + {"get_ban_list", l_get_ban_list}, + {"get_ban_description", l_get_ban_description}, + {"ban_player", l_ban_player}, + {"unban_player_or_ip", l_unban_player_of_ip}, {"get_inventory", l_get_inventory}, + {"create_detached_inventory_raw", l_create_detached_inventory_raw}, + {"show_formspec", l_show_formspec}, {"get_dig_params", l_get_dig_params}, {"get_hit_params", l_get_hit_params}, {"get_current_modname", l_get_current_modname}, {"get_modpath", l_get_modpath}, + {"get_modnames", l_get_modnames}, {"get_worldpath", l_get_worldpath}, {"sound_play", l_sound_play}, {"sound_stop", l_sound_stop}, {"is_singleplayer", l_is_singleplayer}, {"get_password_hash", l_get_password_hash}, {"notify_authentication_modified", l_notify_authentication_modified}, + {"get_craft_result", l_get_craft_result}, + {"get_craft_recipe", l_get_craft_recipe}, + {"rollback_get_last_node_actor", l_rollback_get_last_node_actor}, + {"rollback_revert_actions_by", l_rollback_revert_actions_by}, {NULL, NULL} }; @@ -4258,12 +5397,11 @@ void scriptapi_export(lua_State *L, Server *server) lua_newtable(L); luaL_register(L, NULL, minetest_f); lua_setglobal(L, "minetest"); - + // Get the main minetest table lua_getglobal(L, "minetest"); // Add tables to minetest - lua_newtable(L); lua_setfield(L, -2, "object_refs"); lua_newtable(L); @@ -4273,6 +5411,7 @@ void scriptapi_export(lua_State *L, Server *server) LuaItemStack::Register(L); InvRef::Register(L); NodeMetaRef::Register(L); + NodeTimerRef::Register(L); ObjectRef::Register(L); EnvRef::Register(L); LuaPseudoRandom::Register(L); @@ -4291,7 +5430,7 @@ bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath, <<"Only chararacters [a-z0-9_] are allowed."<addActiveBlockModifier(abm); // removes value, keeps key for next iteration @@ -4430,7 +5569,7 @@ void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj) lua_getfield(L, -1, "object_refs"); luaL_checktype(L, -1, LUA_TTABLE); int objectstable = lua_gettop(L); - + // object_refs[id] = object lua_pushnumber(L, cobj->getId()); // Push id lua_pushvalue(L, object); // Copy object to top of stack @@ -4449,7 +5588,7 @@ void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj) lua_getfield(L, -1, "object_refs"); luaL_checktype(L, -1, LUA_TTABLE); int objectstable = lua_gettop(L); - + // Get object_refs[id] lua_pushnumber(L, cobj->getId()); // Push id lua_gettable(L, objectstable); @@ -4545,16 +5684,16 @@ static void scriptapi_run_callbacks(lua_State *L, int nargs, lua_replace(L, rv); else if(mode == RUN_CALLBACKS_MODE_AND || mode == RUN_CALLBACKS_MODE_AND_SC){ - if(lua_toboolean(L, rv) == true && - lua_toboolean(L, -1) == false) + if((bool)lua_toboolean(L, rv) == true && + (bool)lua_toboolean(L, -1) == false) lua_replace(L, rv); else lua_pop(L, 1); } else if(mode == RUN_CALLBACKS_MODE_OR || mode == RUN_CALLBACKS_MODE_OR_SC){ - if(lua_toboolean(L, rv) == false && - lua_toboolean(L, -1) == true) + if((bool)lua_toboolean(L, rv) == false && + (bool)lua_toboolean(L, -1) == true) lua_replace(L, rv); else lua_pop(L, 1); @@ -4565,10 +5704,10 @@ static void scriptapi_run_callbacks(lua_State *L, int nargs, // Handle short circuit modes if(mode == RUN_CALLBACKS_MODE_AND_SC && - lua_toboolean(L, rv) == false) + (bool)lua_toboolean(L, rv) == false) break; else if(mode == RUN_CALLBACKS_MODE_OR_SC && - lua_toboolean(L, rv) == true) + (bool)lua_toboolean(L, rv) == true) break; // value removed, keep key for next iteration @@ -4610,6 +5749,19 @@ bool scriptapi_on_chat_message(lua_State *L, const std::string &name, return ate; } +void scriptapi_on_shutdown(lua_State *L) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Get registered shutdown hooks + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_shutdown"); + // Call callbacks + scriptapi_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST); +} + void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player) { realitycheck(L); @@ -4682,21 +5834,6 @@ void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player) scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); } -void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - Inventory *inv = player->getInventory(); - assert(inv); - - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "creative_inventory"); - luaL_checktype(L, -1, LUA_TTABLE); - inventory_set_list_from_lua(inv, "main", L, -1, PLAYER_INVENTORY_SIZE); -} - static void get_auth_handler(lua_State *L) { lua_getglobal(L, "minetest"); @@ -4715,7 +5852,7 @@ bool scriptapi_get_auth(lua_State *L, const std::string &playername, realitycheck(L); assert(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); - + get_auth_handler(L); lua_getfield(L, -1, "get_auth"); if(lua_type(L, -1) != LUA_TFUNCTION) @@ -4723,12 +5860,12 @@ bool scriptapi_get_auth(lua_State *L, const std::string &playername, lua_pushstring(L, playername.c_str()); if(lua_pcall(L, 1, 1, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); - + // nil = login not allowed if(lua_isnil(L, -1)) return false; luaL_checktype(L, -1, LUA_TTABLE); - + std::string password; bool found = getstringfield(L, -1, "password", password); if(!found) @@ -4743,7 +5880,7 @@ bool scriptapi_get_auth(lua_State *L, const std::string &playername, if(dst_privs) read_privileges(L, -1, *dst_privs); lua_pop(L, 1); - + return true; } @@ -4753,7 +5890,7 @@ void scriptapi_create_auth(lua_State *L, const std::string &playername, realitycheck(L); assert(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); - + get_auth_handler(L); lua_getfield(L, -1, "create_auth"); if(lua_type(L, -1) != LUA_TFUNCTION) @@ -4770,7 +5907,7 @@ bool scriptapi_set_password(lua_State *L, const std::string &playername, realitycheck(L); assert(lua_checkstack(L, 20)); StackUnroller stack_unroller(L); - + get_auth_handler(L); lua_getfield(L, -1, "set_password"); if(lua_type(L, -1) != LUA_TFUNCTION) @@ -4782,6 +5919,40 @@ bool scriptapi_set_password(lua_State *L, const std::string &playername, return lua_toboolean(L, -1); } +/* + player +*/ + +void scriptapi_on_player_receive_fields(lua_State *L, + ServerActiveObject *player, + const std::string &formname, + const std::map &fields) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Get minetest.registered_on_chat_messages + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_player_receive_fields"); + // Call callbacks + // param 1 + objectref_get_or_create(L, player); + // param 2 + lua_pushstring(L, formname.c_str()); + // param 3 + lua_newtable(L); + for(std::map::const_iterator + i = fields.begin(); i != fields.end(); i++){ + const std::string &name = i->first; + const std::string &value = i->second; + lua_pushstring(L, name.c_str()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC); +} + /* item callbacks and node callbacks */ @@ -4790,6 +5961,8 @@ bool scriptapi_set_password(lua_State *L, const std::string &playername, // If that is nil or on error, return false and stack is unchanged // If that is a function, returns true and pushes the // function onto the stack +// If minetest.registered_items[name] doesn't exist, minetest.nodedef_default +// is tried instead so unknown items can still be manipulated to some degree static bool get_item_callback(lua_State *L, const char *name, const char *callbackname) { @@ -4802,11 +5975,17 @@ static bool get_item_callback(lua_State *L, // Should be a table if(lua_type(L, -1) != LUA_TTABLE) { + // Report error and clean up errorstream<<"Item \""<ndef(); + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "after_destruct")) + return; + + // Call function + push_v3s16(L, p); + pushnode(L, node, ndef); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_timer")) + return false; + + // Call function + push_v3s16(L, p); + lua_pushnumber(L,dtime); + if(lua_pcall(L, 2, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if((bool)lua_isboolean(L,-1) && (bool)lua_toboolean(L,-1) == true) + return true; + + return false; +} + void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, const std::string &formname, const std::map &fields, @@ -4983,7 +6204,7 @@ void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, StackUnroller stack_unroller(L); INodeDefManager *ndef = get_server(L)->ndef(); - + // If node doesn't exist, we don't know what callback to call MapNode node = get_env(L)->getMap().getNodeNoEx(p); if(node.getContent() == CONTENT_IGNORE) @@ -5014,7 +6235,134 @@ void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, script_error(L, "error: %s", lua_tostring(L, -1)); } -void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p, +/* + Node metadata inventory callbacks +*/ + +// Return number of accepted items to be moved +int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "allow_metadata_inventory_move")) + return count; + + // function(pos, from_list, from_index, to_list, to_index, count, player) + // pos + push_v3s16(L, p); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 7, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_metadata_inventory_move should return a number"); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be put +int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "allow_metadata_inventory_put")) + return stack.count; + + // Call function(pos, listname, index, stack, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_metadata_inventory_put should return a number"); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be taken +int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "allow_metadata_inventory_take")) + return stack.count; + + // Call function(pos, listname, index, count, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_metadata_inventory_take should return a number"); + return luaL_checkinteger(L, -1); +} + +// Report moved items +void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p, const std::string &from_list, int from_index, const std::string &to_list, int to_index, int count, ServerActiveObject *player) @@ -5036,18 +6384,26 @@ void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p, return; // function(pos, from_list, from_index, to_list, to_index, count, player) + // pos push_v3s16(L, p); + // from_list lua_pushstring(L, from_list.c_str()); + // from_index lua_pushinteger(L, from_index + 1); + // to_list lua_pushstring(L, to_list.c_str()); + // to_index lua_pushinteger(L, to_index + 1); + // count lua_pushinteger(L, count); + // player objectref_get_or_create(L, player); if(lua_pcall(L, 7, 0, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); } -ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p, +// Report put items +void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p, const std::string &listname, int index, ItemStack &stack, ServerActiveObject *player) { @@ -5060,26 +6416,31 @@ ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p, // If node doesn't exist, we don't know what callback to call MapNode node = get_env(L)->getMap().getNodeNoEx(p); if(node.getContent() == CONTENT_IGNORE) - return stack; + return; // Push callback function on stack if(!get_item_callback(L, ndef->get(node).name.c_str(), - "on_metadata_inventory_offer")) - return stack; + "on_metadata_inventory_put")) + return; // Call function(pos, listname, index, stack, player) + // pos push_v3s16(L, p); + // listname lua_pushstring(L, listname.c_str()); + // index lua_pushinteger(L, index + 1); + // stack LuaItemStack::create(L, stack); + // player objectref_get_or_create(L, player); - if(lua_pcall(L, 5, 1, 0)) + if(lua_pcall(L, 5, 0, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); - return read_item(L, -1); } -ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p, - const std::string &listname, int index, int count, +// Report taken items +void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, ServerActiveObject *player) { realitycheck(L); @@ -5091,22 +6452,276 @@ ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p, // If node doesn't exist, we don't know what callback to call MapNode node = get_env(L)->getMap().getNodeNoEx(p); if(node.getContent() == CONTENT_IGNORE) - return ItemStack(); + return; // Push callback function on stack if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_metadata_inventory_take")) - return ItemStack(); + return; - // Call function(pos, listname, index, count, player) + // Call function(pos, listname, index, stack, player) + // pos push_v3s16(L, p); + // listname lua_pushstring(L, listname.c_str()); + // index lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +/* + Detached inventory callbacks +*/ + +// Retrieves minetest.detached_inventories[name][callbackname] +// If that is nil or on error, return false and stack is unchanged +// If that is a function, returns true and pushes the +// function onto the stack +static bool get_detached_inventory_callback(lua_State *L, + const std::string &name, const char *callbackname) +{ + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "detached_inventories"); + lua_remove(L, -2); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, name.c_str()); + lua_remove(L, -2); + // Should be a table + if(lua_type(L, -1) != LUA_TTABLE) + { + errorstream<<"Item \""<hp_max = 10; - + + /* Read stuff */ + + prop->hp_max = getintfield_default(L, -1, "hp_max", 10); + + getboolfield(L, -1, "physical", prop->physical); + + getfloatfield(L, -1, "weight", prop->weight); + + lua_getfield(L, -1, "collisionbox"); + if(lua_istable(L, -1)) + prop->collisionbox = read_aabb3f(L, -1, 1.0); + lua_pop(L, 1); + + getstringfield(L, -1, "visual", prop->visual); + + getstringfield(L, -1, "mesh", prop->mesh); + // Deprecated: read object properties directly read_object_properties(L, -1, prop); - + // Read initial_properties lua_getfield(L, -1, "initial_properties"); read_object_properties(L, -1, prop);